// REQUIRED_ARGS: -O -fPIC // PERMUTE_ARGS: // only testing on SYSV-ABI, but backend code is identical across platforms // DISABLED: win32 win64 osx linux32 freebsd32 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 */ 0xe8, 0x00, 0x00, 0x00, 0x00, /* mov edi,eax */ 0x89, 0xc7, /* call 11 */ 0xe8, 0x00, 0x00, 0x00, 0x00, /* pop rbp */ 0x5d, /* ret */ 0xc3, ]), Code!(ubyte, 8)([ /* push rbp */ 0x55, /* mov rbp,rsp */ 0x48, 0x8b, 0xec, /* call 9 */ 0xe8, 0x00, 0x00, 0x00, 0x00, /* mov rdi,rax */ 0x48, 0x89, 0xc7, /* call 11 */ 0xe8, 0x00, 0x00, 0x00, 0x00, /* pop rbp */ 0x5d, /* ret */ 0xc3, ]), Code!(ubyte, 16)([ /* push rbp */ 0x55, /* mov rbp,rsp */ 0x48, 0x8b, 0xec, /* call 9 */ 0xe8, 0x00, 0x00, 0x00, 0x00, /* mov rdi,rax */ 0x48, 0x89, 0xc7, /* mov rsi,rdx */ 0x48, 0x89, 0xd6, /* call 14 */ 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 */ 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 */ 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 */ 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 */ 0x75, 0xf6, /* call 2c */ 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); }(); }