mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
244 lines
8.8 KiB
D
244 lines
8.8 KiB
D
// REQUIRED_ARGS: -O -fPIC
|
|
// PERMUTE_ARGS:
|
|
// only testing on SYSV-ABI, but backend code is identical across platforms
|
|
// DISABLED: win32 win64 osx linux32 freebsd32 openbsd32
|
|
debug = PRINTF;
|
|
debug (PRINTF) import core.stdc.stdio;
|
|
|
|
// Run this after codegen changes:
|
|
// env DMD=generated/linux/release/64/dmd rdmd -fPIC -version=update test/runnable/test_cdstrpar.d
|
|
version (update)
|
|
{
|
|
import std.algorithm : canFind, find, splitter, until;
|
|
import std.array : appender, join;
|
|
import std.conv : to;
|
|
import std.exception : enforce;
|
|
import std.file : readText;
|
|
import std.format : formattedWrite;
|
|
import std.meta : AliasSeq;
|
|
import std.path : baseName, setExtension;
|
|
import std.process : environment, execute, pipeProcess, wait;
|
|
import std.range : dropOne;
|
|
import std.regex : ctRegex, matchFirst, replaceFirstInto;
|
|
import std.stdio : File, stdout, writeln;
|
|
import std.string : strip;
|
|
import std.traits : EnumMembers;
|
|
import std.typecons : tuple;
|
|
|
|
enum Arch
|
|
{
|
|
baseline, // doesn't affect argument passing
|
|
// avx,
|
|
// avx2,
|
|
}
|
|
|
|
enum sizes = [4, 8, 16, 32, 64];
|
|
|
|
enum asmRE = ctRegex!`^\s+[\da-z]+:((\s[\da-z]{2})*)(.*)$`;
|
|
|
|
void formatASM(Captures, Sink)(Captures cap, Sink sink)
|
|
{
|
|
formattedWrite(sink, " /* %-30s */ %-(0x%s,%| %)\n", cap[3].strip, cap[1].splitter);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
enum src = __FILE__;
|
|
auto dmd = environment.get("DMD", "dmd");
|
|
auto sink = appender!string();
|
|
foreach (arch; [EnumMembers!Arch])
|
|
{
|
|
auto args = [dmd, "-c", "-O", "-fPIC", "-mcpu=" ~ arch.to!string, __FILE__];
|
|
auto rc = execute(args);
|
|
enforce(rc.status == 0, rc.output);
|
|
formattedWrite(sink, "alias %sCases = AliasSeq!(\n", arch);
|
|
// Just add empty Code!(newtype, count)(null) elements when adding a new type
|
|
foreach (type; AliasSeq!(ubyte))
|
|
{
|
|
foreach (sz; sizes)
|
|
{
|
|
args = ["objdump", "--disassemble", "--disassembler-options=intel-mnemonic",
|
|
"--section=.text.testee_" ~ type.stringof ~ "_" ~ sz.to!string,
|
|
__FILE__.baseName.setExtension(".o")];
|
|
auto p = pipeProcess(args);
|
|
formattedWrite(sink, " Code!(%s, %s)([\n", type.stringof, sz);
|
|
foreach (line; p.stdout.byLine.find!(ln => ln.matchFirst(ctRegex!">:$"))
|
|
.dropOne.until!(ln => ln.canFind("...")))
|
|
{
|
|
replaceFirstInto!formatASM(sink, line, asmRE);
|
|
}
|
|
formattedWrite(sink, " ]),\n");
|
|
enforce(wait(p.pid) == 0, p.stderr.byLine.join("\n"));
|
|
}
|
|
}
|
|
formattedWrite(sink, ");\n\n");
|
|
}
|
|
{
|
|
auto content = src.readText;
|
|
auto f = File(src, "w");
|
|
auto orng = f.lockingTextWriter;
|
|
immutable string start = "// dfmt off";
|
|
immutable string end = "// dfmt on";
|
|
replaceFirstInto!((_, orng) => formattedWrite(orng, start ~ "\n%s" ~ end, sink.data))(orng,
|
|
content, ctRegex!(`^` ~ start ~ `[^$]*` ~ end ~ `$`, "m"));
|
|
}
|
|
}
|
|
}
|
|
else:
|
|
|
|
struct Struct(T, int N)
|
|
{
|
|
T[N] buf;
|
|
}
|
|
|
|
private void callee(S)(S s)
|
|
{
|
|
}
|
|
|
|
private T getRValue(T)()
|
|
{
|
|
return T.init;
|
|
}
|
|
|
|
template testee(T, int N)
|
|
{
|
|
pragma(mangle, "testee_" ~ T.stringof ~ "_" ~ N.stringof) void testee()
|
|
{
|
|
callee(getRValue!(Struct!(T, N)));
|
|
}
|
|
}
|
|
|
|
// holding the expected byte sequence
|
|
struct Code(T_, int N_)
|
|
{
|
|
alias T = T_;
|
|
alias N = N_;
|
|
ubyte[] code;
|
|
}
|
|
|
|
alias AliasSeq(Args...) = Args;
|
|
|
|
// dfmt off
|
|
alias baselineCases = AliasSeq!(
|
|
Code!(ubyte, 4)([
|
|
/* push rbp */ 0x55,
|
|
/* mov rbp,rsp */ 0x48, 0x8b, 0xec,
|
|
/* call 9 <testee_ubyte_4+0x9> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* mov edi,eax */ 0x89, 0xc7,
|
|
/* call 11 <testee_ubyte_4+0x11> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* pop rbp */ 0x5d,
|
|
/* ret */ 0xc3,
|
|
]),
|
|
Code!(ubyte, 8)([
|
|
/* push rbp */ 0x55,
|
|
/* mov rbp,rsp */ 0x48, 0x8b, 0xec,
|
|
/* call 9 <testee_ubyte_8+0x9> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* mov rdi,rax */ 0x48, 0x89, 0xc7,
|
|
/* call 11 <testee_ubyte_8+0x11> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* pop rbp */ 0x5d,
|
|
/* ret */ 0xc3,
|
|
]),
|
|
Code!(ubyte, 16)([
|
|
/* push rbp */ 0x55,
|
|
/* mov rbp,rsp */ 0x48, 0x8b, 0xec,
|
|
/* call 9 <testee_ubyte_16+0x9> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* mov rdi,rax */ 0x48, 0x89, 0xc7,
|
|
/* mov rsi,rdx */ 0x48, 0x89, 0xd6,
|
|
/* call 14 <testee_ubyte_16+0x14> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* pop rbp */ 0x5d,
|
|
/* ret */ 0xc3,
|
|
]),
|
|
Code!(ubyte, 32)([
|
|
/* push rbp */ 0x55,
|
|
/* mov rbp,rsp */ 0x48, 0x8b, 0xec,
|
|
/* sub rsp,0x20 */ 0x48, 0x83, 0xec, 0x20,
|
|
/* lea rdi,[rbp-0x20] */ 0x48, 0x8d, 0x7d, 0xe0,
|
|
/* call 11 <testee_ubyte_32+0x11> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* mov rsi,rax */ 0x48, 0x89, 0xc6,
|
|
/* push QWORD PTR [rsi+0x18] */ 0xff, 0x76, 0x18,
|
|
/* push QWORD PTR [rsi+0x10] */ 0xff, 0x76, 0x10,
|
|
/* push QWORD PTR [rsi+0x8] */ 0xff, 0x76, 0x08,
|
|
/* push QWORD PTR [rsi] */ 0xff, 0x36,
|
|
/* call 24 <testee_ubyte_32+0x24> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* add rsp,0x20 */ 0x48, 0x83, 0xc4, 0x20,
|
|
/* mov rsp,rbp */ 0x48, 0x8b, 0xe5,
|
|
/* pop rbp */ 0x5d,
|
|
/* ret */ 0xc3,
|
|
/* add BYTE PTR [rax],al */ 0x00, 0x00,
|
|
]),
|
|
Code!(ubyte, 64)([
|
|
/* push rbp */ 0x55,
|
|
/* mov rbp,rsp */ 0x48, 0x8b, 0xec,
|
|
/* sub rsp,0x40 */ 0x48, 0x83, 0xec, 0x40,
|
|
/* lea rdi,[rbp-0x40] */ 0x48, 0x8d, 0x7d, 0xc0,
|
|
/* call 11 <testee_ubyte_64+0x11> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* mov rsi,rax */ 0x48, 0x89, 0xc6,
|
|
/* mov ecx,0x8 */ 0xb9, 0x08, 0x00, 0x00, 0x00,
|
|
/* add rsi,0x38 */ 0x48, 0x83, 0xc6, 0x38,
|
|
/* push QWORD PTR [rsi] */ 0xff, 0x36,
|
|
/* sub rsi,0x8 */ 0x48, 0x83, 0xee, 0x08,
|
|
/* dec ecx */ 0xff, 0xc9,
|
|
/* jne 1d <testee_ubyte_64+0x1d> */ 0x75, 0xf6,
|
|
/* call 2c <testee_ubyte_64+0x2c> */ 0xe8, 0x00, 0x00, 0x00, 0x00,
|
|
/* add rsp,0x40 */ 0x48, 0x83, 0xc4, 0x40,
|
|
/* mov rsp,rbp */ 0x48, 0x8b, 0xe5,
|
|
/* pop rbp */ 0x5d,
|
|
/* ret */ 0xc3,
|
|
/* add BYTE PTR [rax],al */ 0x00, 0x00,
|
|
]),
|
|
);
|
|
|
|
// dfmt on
|
|
|
|
bool matches(const(ubyte)[] code, const(ubyte)[] exp)
|
|
{
|
|
assert(code.length == exp.length);
|
|
foreach (ref i; 0 .. code.length)
|
|
{
|
|
if (code[i] == exp[i])
|
|
continue;
|
|
// wildcard match for relative call displacement
|
|
if (i && exp.length - (i - 1) >= 5 && exp[i - 1 .. i + 4] == [0xe8, 0x00, 0x00, 0x00, 0x00])
|
|
{
|
|
i += 3;
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
alias testCases = AliasSeq!(baselineCases);
|
|
|
|
void main()
|
|
{
|
|
foreach (tc; testCases)
|
|
(){ // workaround Issue 7157
|
|
auto code = (cast(ubyte*)&testee!(tc.T, tc.N))[0 .. tc.code.length];
|
|
bool failure;
|
|
if (!code.matches(tc.code))
|
|
{
|
|
fprintf(stderr, "Expected code sequence for testee!(%s, %u) not found.",
|
|
tc.T.stringof.ptr, tc.N);
|
|
fprintf(stderr, "\n Expected:");
|
|
foreach (i, d; tc.code)
|
|
{
|
|
if (tc.code[i] != code[i])
|
|
fprintf(stderr, " \033[32m0x%02x\033[0m", d);
|
|
else
|
|
fprintf(stderr, " 0x%02x", d);
|
|
}
|
|
fprintf(stderr, "\n Actual:");
|
|
foreach (i, d; code)
|
|
{
|
|
if (tc.code[i] != code[i])
|
|
fprintf(stderr, " \033[31m0x%02x\033[0m", d);
|
|
else
|
|
fprintf(stderr, " 0x%02x", d);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
failure = true;
|
|
}
|
|
assert(!failure);
|
|
}();
|
|
}
|