// PERMUTE_ARGS: -release -g version(Windows) {} else version(X86_64) { /* uncomment to enable tests! */ //version = Run_X86_64_Tests; } extern (C) int printf(const char*, ...); template tuple(A...) { alias A tuple; } alias byte B; alias short S; alias int I; alias long L; alias float F; alias double D; alias real R; // Single Type struct b { B a; } struct bb { B a,b; } struct bbb { B a,b,c; } struct bbbb { B a,b,c,d; } struct bbbbb { B a,b,c,d, e; } struct b6 { B a,b,c,d, e,f; } struct b7 { B a,b,c,d, e,f,g; } struct b8 { B a,b,c,d, e,f,g,h; } struct b9 { B a,b,c,d, e,f,g,h, i; } struct b10 { B a,b,c,d, e,f,g,h, i,j; } struct b11 { B a,b,c,d, e,f,g,h, i,j,k; } struct b12 { B a,b,c,d, e,f,g,h, i,j,k,l; } struct b13 { B a,b,c,d, e,f,g,h, i,j,k,l, m; } struct b14 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n; } struct b15 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o; } struct b16 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p; } struct b17 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q; } struct b18 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r; } struct b19 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r,s; } struct b20 { B a,b,c,d, e,f,g,h, i,j,k,l, m,n,o,p, q,r,s,t;} struct s { S a; } struct ss { S a,b; } struct sss { S a,b,c; } struct ssss { S a,b,c,d; } struct sssss { S a,b,c,d, e; } struct s6 { S a,b,c,d, e,f; } struct s7 { S a,b,c,d, e,f,g; } struct s8 { S a,b,c,d, e,f,g,h; } struct s9 { S a,b,c,d, e,f,g,h, i; } struct s10 { S a,b,c,d, e,f,g,h, i,j;} struct i { I a; } struct l { L a; } struct ii { I a,b; } struct ll { L a,b; } struct iii { I a,b,c; } struct lll { L a,b,c; } struct iiii { I a,b,c,d; } struct llll { L a,b,c,d; } struct iiiii { I a,b,c,d,e; } struct lllll { L a,b,c,d,e; } struct f { F a; } struct d { D a; } struct ff { F a,b; } struct dd { D a,b; } struct fff { F a,b,c; } struct ddd { D a,b,c; } struct ffff { F a,b,c,d; } struct dddd { D a,b,c,d; } struct fffff { F a,b,c,d,e; } struct ddddd { D a,b,c,d,e; } // Mixed Size struct js { I a; S b; } struct iss { I a; S b,c; } struct si { S a; I b; } struct ssi { S a,b; I c; } struct sis { S a; I b; S c; } struct ls { L a; S b; } struct lss { L a; S b,c; } struct sl { S a; L b; } struct ssl { S a,b; L c; } struct sls { S a; L b; S c; } struct li { L a; I b; } struct lii { L a; I b,c; } struct il { I a; L b; } struct iil { I a,b; L c; } struct ili { I a; L b; I c; } struct df { D a; F b; } struct dff { D a; F b,c; } struct fd { F a; D b; } struct ffd { F a,b; D c; } struct fdf { F a; D b; F c; } // Mixed Types struct fi { F a; I b; } struct fii { F a; I b,c; } struct jf { I a; F b; } struct iif { I a,b; F c; } struct ifi { I a; F b; I c; } struct ffi { F a,b; I c; } struct ffii { F a,b; I c,d; } struct iff { I a; F b,c; } struct iiff { I a,b; F c,d; } struct ifif { I a; F b; I c; F d;} struct di { D a; I b; } struct dii { D a; I b,c; } struct id { I a; D b; } struct iid { I a,b; D c; } struct idi { I a; D b; I c; } // Real ( long double ) struct r { R a; } struct rr { R a,b; } struct rb { R a; B b; } struct rf { R a; F b; } struct fr { F a; R b; } // Int Registers only alias tuple!( b,bb,bbb,bbbb,bbbbb, b6, b7, b8, b9, b10, b11,b12,b13,b14,b15, b16,b17,b18,b19,b20, s,ss,sss,ssss,sssss, s6, s7, s8, s9, s10, i,ii,iii,iiii,iiiii, l,ll,lll,llll,lllll, // js,iss,si,ssi, sis, ls,lss,sl,ssl, sls, li,lii,il,iil, ili, fi,fii,jf,iif, ifi, ffi,ffii,iff,iiff,ifif, // INT_END // SSE registers only f,ff,fff,ffff,fffff, d,dd,ddd,dddd,ddddd, // df,dff,fd,ffd, fdf, // SSE_END // Int and SSE di, dii,id, iid, idi, // MIX_END // --- ) ALL_T; enum INT_END = 65; enum SSE_END = 80; enum MIX_END = ALL_T.length; // x87 alias tuple!( r,rr,rb,rf,fr, // --- ) R_T; //"r","rr","rb","rf","fr", string[] ALL_S=[ "b","bb","bbb","bbbb","bbbbb", "b6", "b7", "b8", "b9", "b10", "b11","b12","b13","b14","b15", "b16","b17","b18","b19","b20", "s","ss","sss","ssss","sssss", "s6","s7","s8","s9" , "s10", "i","ii","iii","iiii","iiiii", "l","ll","lll","llll","lllll", // --- "js","iss","si","ssi", "sis", "ls","lss","sl","ssl", "sls", "li","lii","il","iil", "ili", "fi","fii","jf","iif", "ifi", "ffi","ffii","iff","iiff","ifif", // --- "f","ff","fff","ffff","fffff", "d","dd","ddd","dddd","ddddd", "df","dff","fd","ffd", "dfd", // --- "di","dii","id","iid","idi", ]; /* *********************************************************************** All ************************************************************************/ // test1 Struct passing and return int[MIX_END] results_1; T test1_out(T)( ) { T t; foreach( i, ref e; t.tupleof ) e = i+1; return t; } T test1_inout(T)( T t) { foreach( i, ref e; t.tupleof ) e += 10; return t; } void test1_call_out(T)( int n ) { T t1; foreach( i, ref e; t1.tupleof ) e = i+1; T t2 = test1_out!(T)(); if( t1 == t2 ) results_1[n] |= 1; } void test1_call_inout(T)( int n ) { T t1; foreach( i, ref e; t1.tupleof ) e = i+1; T t2 = test1_inout!(T)( t1 ); foreach( i, ref e; t1.tupleof ) e += 10; if( t1 == t2 ) results_1[n] |= 2; } void D_test1( ) { // Run Tests foreach( n, T; ALL_T ) { test1_call_out!(T)(n); test1_call_inout!(T)(n); } bool pass = true; foreach( i, r; results_1 ) { if( ~r & 1 ) { pass = false; printf( "Test1 out %s \tFail\n", ALL_S[i].ptr ); } if( ~r & 2 ) { pass = false; printf( "Test1 inout %s \tFail\n", ALL_S[i].ptr ); } } assert( pass ); results_1[0..5] = 0; foreach( n, T; R_T ) { test1_call_out!(T)(n); test1_call_inout!(T)(n); } } /************************************************************************/ // based on runnable/test23.d : test44() // Return Struct into an Array struct S1 { int i,j; static S1 foo(int x) { S1 s; s.i = x; return s; } } struct S2 { int i,j,k; static S2 foo(int x) { S2 s; s.i = x; return s; } } struct S3 { float i,j; static S3 foo(int x) { S3 s; s.i = x; return s; } } struct S4 { float i,j,k; static S4 foo(int x) { S4 s; s.i = x; return s; } } struct S5 { float i,j; int k; static S5 foo(float x) { S5 s; s.i = x; return s; } } void D_test2() { S1[] s1; S2[] s2; S3[] s3; S4[] s4; S5[] s5; s1 = s1 ~ S1.foo(6); s1 = s1 ~ S1.foo(1); s2 = s2 ~ S2.foo(6); s2 = s2 ~ S2.foo(1); s3 = s3 ~ S3.foo(6); s3 = s3 ~ S3.foo(1); s4 = s4 ~ S4.foo(6); s4 = s4 ~ S4.foo(1); s5 = s5 ~ S5.foo(6); s5 = s5 ~ S5.foo(1); assert( s1.length == 2 ); assert( s1[0].i == 6 ); assert( s1[1].i == 1 ); assert( s2.length == 2 ); assert( s2[0].i == 6 ); assert( s2[1].i == 1 ); /+ // These Fail on Mainline DMD64 ( Should pass! ) assert( s3.length == 2 ); assert( s3[0].i == 6 ); assert( s3[1].i == 1 ); assert( s4.length == 2 ); assert( s4[0].i == 6 ); assert( s4[1].i == 1 ); +/ assert( s5.length == 2 ); assert( s5[0].i == 6 ); assert( s5[1].i == 1 ); } /* *********************************************************************** X86_64 ************************************************************************/ version(Run_X86_64_Tests) { struct TEST { immutable int num; immutable string desc; bool[MIX_END] result; } /** * 0 = Should Fail * 1 = Should Pass */ immutable int[MIX_END] expected = [ 1,1,1,1,1, // b 1,1,1,1,1, // b6 1,1,1,1,1, // b11 1,0,0,0,0, // b16 1,1,1,1,1, // s 1,1,1,0,0, // s6 1,1,1,1,0, // i 1,1,0,0,0, // l 1,1,1,1,1, // si mix 1,1,1,1,0, // sl 1,1,1,1,0, // il 1,1,1,1,1, // int and float 1,1,1,1,1, // int and float // SSE regs only 1,1,1,1,0, // f 1,1,0,0,0, // d 1,1,1,1,0, // float and double // SSE + INT regs 1,1,1,1,0, // int and double ]; /** * Describes value expected in registers * * null means do not test * ( because value is passed on the stack ). */ immutable long[][] RegValue = [ /* 0 b */ [ 0x0000000000000001, ], /* 1 bb */ [ 0x0000000000000201, ], /* 2 bbb */ [ 0x0000000000030201, ], /* 3 bbbb */ [ 0x0000000004030201, ], /* 4 bbbbb */ [ 0x0000000504030201, ], /* 5 b6 */ [ 0x0000060504030201, ], /* 6 b7 */ [ 0x0007060504030201, ], /* 7 b8 */ [ 0x0807060504030201, ], /* 8 b9 */ [ 0x0807060504030201, 0x0000000000000009 ], /* 9 b10 */ [ 0x0807060504030201, 0x0000000000000a09 ], /* 10 b11 */ [ 0x0807060504030201, 0x00000000000b0a09 ], /* 11 b12 */ [ 0x0807060504030201, 0x000000000c0b0a09 ], /* 12 b13 */ [ 0x0807060504030201, 0x0000000d0c0b0a09 ], /* 13 b14 */ [ 0x0807060504030201, 0x00000e0d0c0b0a09 ], /* 14 b15 */ [ 0x0807060504030201, 0x000f0e0d0c0b0a09 ], /* 15 b16 */ [ 0x0807060504030201, 0x100f0e0d0c0b0a09 ], /* 16 b17 */ null, /* 17 b18 */ null, /* 18 b19 */ null, /* 19 b20 */ null, /* 20 s */ [ 0x0000000000000001, ], /* 21 ss */ [ 0x0000000000020001, ], /* 22 sss */ [ 0x0000000300020001, ], /* 23 ssss */ [ 0x0004000300020001, ], /* 24 sssss */ [ 0x0004000300020001, 0x0000000000000005 ], /* 25 s6 */ [ 0x0004000300020001, 0x0000000000060005 ], /* 26 s7 */ [ 0x0004000300020001, 0x0000000700060005 ], /* 27 s8 */ [ 0x0004000300020001, 0x0008000700060005 ], /* 28 s9 */ null, /* 29 s10 */ null, /* 30 i */ [ 0x0000000000000001, ], /* 31 ii */ [ 0x0000000200000001, ], /* 32 iii */ [ 0x0000000200000001, 0x0000000000000003 ], /* 33 iiii */ [ 0x0000000200000001, 0x0000000400000003 ], /* 34 iiiii */ null, /* 35 l */ [ 0x0000000000000001, ], /* 36 ll */ [ 0x0000000000000001, 0x0000000000000002 ], /* 37 lll */ null, /* 38 llll */ null, /* 39 lllll */ null, /* 40 js */ [ 0x0000000200000001, ], /* 41 iss */ [ 0x0003000200000001, ], /* 42 si */ [ 0x0000000200000001, ], /* 43 ssi */ [ 0x0000000300020001, ], /* 44 sis */ [ 0x0000000200000001, 0x0000000000000003 ], /* 45 ls */ [ 0x0000000000000001, 0x0000000000000002 ], /* 46 lss */ [ 0x0000000000000001, 0x0000000000030002 ], /* 47 sl */ [ 0x0000000000000001, 0x0000000000000002 ], /* 48 ssl */ [ 0x0000000000020001, 0x0000000000000003 ], /* 49 sls */ null, /* 50 li */ [ 0x0000000000000001, 0x0000000000000002 ], /* 51 lii */ [ 0x0000000000000001, 0x0000000300000002 ], /* 52 il */ [ 0x0000000000000001, 0x0000000000000002 ], /* 53 iil */ [ 0x0000000200000001, 0x0000000000000003 ], /* 54 ili */ null, /* 55 fi */ [ 0x000000023f800000, ], /* 56 fii */ [ 0x000000023f800000, 0x0000000000000003 ], /* 57 jf */ [ 0x4000000000000001, ], /* 58 iif */ [ 0x0000000200000001, 0x0000000040400000 ], /* 59 ifi */ [ 0x4000000000000001, 0x0000000000000003 ], /* 60 ffi */ [ 0x0000000000000003, 0x400000003f800000 ], /* 61 ffii */ [ 0x0000000400000003, 0x400000003f800000 ], /* 62 iff */ [ 0x4000000000000001, 0x0000000040400000 ], /* 63 iiff */ [ 0x0000000200000001, 0x4080000040400000 ], /* 64 ifif */ [ 0x4000000000000001, 0x4080000000000003 ], /* 65 f */ [ 0x000000003f800000, ], /* 66 ff */ [ 0x400000003f800000, ], /* 67 fff */ [ 0x400000003f800000, 0x0000000040400000 ], /* 68 ffff */ [ 0x400000003f800000, 0x4080000040400000 ], /* 69 fffff */ null, /* 70 d */ [ 0x3ff0000000000000, ], /* 71 dd */ [ 0x3ff0000000000000, 0x4000000000000000 ], /* 72 ddd */ null, /* 73 dddd */ null, /* 74 ddddd */ null, /* 75 df */ [ 0x3ff0000000000000, 0x0000000040000000 ], /* 76 dff */ [ 0x3ff0000000000000, 0x4040000040000000 ], /* 77 fd */ [ 0x000000003f800000, 0x4000000000000000 ], /* 78 ffd */ [ 0x400000003f800000, 0x4008000000000000 ], /* 79 fdf */ null, /* 80 di */ [ 0x3ff0000000000000, 0x0000000000000002 ], /* 81 dii */ [ 0x3ff0000000000000, 0x0000000300000002 ], /* 82 id */ [ 0x4000000000000000, 0x0000000000000001 ], /* 83 iid */ [ 0x4008000000000000, 0x0000000200000001 ], /* 84 idi */ null, ]; /* Have to do it this way for OSX: Issue 7354 */ __gshared long[2] dump; /** * Generate Register capture */ string gen_reg_capture( int n, string registers )( ) { if( RegValue[n] == null ) return "return;"; string[] REG = mixin(registers); // ["RDI","RSI"]; // Which type of compare static if(n < INT_END) enum MODE = 1; // Int else static if(n < SSE_END) enum MODE = 2; // Float else enum MODE = 3; // Mix /* Begin */ string code = "asm {\n"; final switch( MODE ) { case 1: code ~= "mov [dump], "~REG[0]~";\n"; REG = REG[1..$]; break; case 2: case 3: code ~= "movq [dump], XMM0;\n"; } if( RegValue[n].length == 2 ) final switch( MODE ) { case 1: case 3: code ~= "mov [dump+8], "~REG[0]~";\n"; break; case 2: code ~= "movq [dump+8], XMM1;\n"; } else { code ~= "xor R8, R8;\n"; code ~= "mov [dump+8], R8;\n"; } return code ~ "}\n"; } /** * Check the results */ bool check( TEST data ) { bool pass = true; foreach( i, e; expected ) { if( data.result[i] != (e & 1) ) { printf( "Test%d %s \tFail\n", data.num, ALL_S[i].ptr); pass = false; } } return pass; } /************************************************************************/ // test1 Return Struct in Registers // ( if RDI == 12 we have no hidden pointer ) TEST data1 = { 1, "RDI hidden pointer" }; T test1_asm( T, int n )( int i ) { asm { cmp EDI, 12; je L1; leave; ret; } L1: data1.result[n] = true; } void test1() { printf("\nRunning iasm Test 1 ( %s )\n", data1.desc.ptr); foreach( int n, T; ALL_T ) test1_asm!(T,n)(12); check( data1 ); } /************************************************************************/ // test2 Pass Struct in Registers // ( if RDI == 0 we have no stack pointer ) TEST data2 = { 2, "RDI struct pointer" }; T test2_asm( T, int n )( T t ) { asm { mov [dump], RDI; mov [dump+8], RSP; cmp EDI, 0; // TODO test RDI is a ptr to stack ? ? je L1; leave; ret; } L1: data2.result[n] = true; } T test2f_asm( T, int n )( T t, int x ) { asm { cmp EDI, 12; je L1; leave; ret; } L1: data2.result[n] = true; } void test2() { printf("\nRunning iasm Test 2 ( %s )\n", data2.desc.ptr); // Integer foreach( int n, T; ALL_T ) { T t = { 0 }; test2_asm!(T,n)( t ); } // float alternative test foreach( int n, T; ALL_T[INT_END..SSE_END] ) { enum n2 = n + INT_END; data2.result[n2] = false; test2f_asm!(T,n2)( T.init, 12 ); } check( data2 ); } /************************************************************************/ // test3 TEST data3 = { 3, "Check Return Register value" }; void test3_run( T, int n )( ) { test3_ret!T(); mixin( gen_reg_capture!(n,`["RAX","RDX"]`)() ); //dbg!(T,n)( ); enum len = RegValue[n].length; if( dump[0..len] == RegValue[n] ) data3.result[n] = true; } T test3_ret( T )( ) { T t; foreach( i, ref e; t.tupleof ) e = i+1; return t; } void test3() { printf("\nRunning iasm Test 3 ( %s )\n", data3.desc.ptr); foreach( int n, T; ALL_T ) test3_run!(T,n)( ); check( data3 ); } /************************************************************************/ // test4 TEST data4 = { 4, "Check Input Register value" }; void test4_run( T, int n )( T t ) { mixin( gen_reg_capture!(n,`["RDI","RSI"]`)() ); //dbg!(T,n)( ); enum len = RegValue[n].length; if( dump[0..len] == RegValue[n] ) data4.result[n] = true; } void dbg( T, int n )( ) { import std.stdio; writefln( "D %s\t[ %16x, %16x ]", T.stringof, dump[0], dump[1], ); writef( "C %s\t[ %16x", T.stringof, RegValue[n][0] ); if( RegValue[n].length == 2 ) writef( ", %16x", RegValue[n][1] ); writefln( " ]" ); } void test4() { printf("\nRunning iasm Test 4 ( %s )\n", data4.desc.ptr); foreach( int n, T; ALL_T ) { T t; foreach( i, ref e; t.tupleof ) e = i+1; test4_run!(T,n)( t ); } check( data4 ); } } // end version(Run_X86_64_Tests) /************************************************************************/ void main() { D_test1(); D_test2(); version(Run_X86_64_Tests) { test1(); test2(); test3(); test4(); } } /+ /** * C code to generate the table RegValue */ string c_generate_returns() { string value = " 1, 2, 3, 4, 5, 6, 7, 8, 9,10," "11,12,13,14,15,16,17,18,19,20,"; string code = "#include \"cgen.h\"\n"; // Generate return functions foreach( int n, T; ALL_T ) { auto Ts = T.stringof; auto len = T.tupleof.length; code ~= "struct "~Ts~" func_ret_"~Ts~"(void) { \n"; code ~= "struct "~Ts~" x = { "; code ~= value[0..len*3] ~ " };\n"; code ~= "return x;\n}\n"; } return code; } string c_generate_pass() { string value = " 1, 2, 3, 4, 5, 6, 7, 8, 9,10," "11,12,13,14,15,16,17,18,19,20,"; string code; // Generate return functions foreach( int n, T; ALL_T ) { auto Ts = T.stringof; auto len = T.tupleof.length; code ~= "void func_pass_"~Ts~"( struct "~Ts~" x ) {\n"; //////////////// // Which type of compare static if(n < INT_END) enum MODE = 1; // Int else static if(n < SSE_END) enum MODE = 2; // Float else enum MODE = 3; // Mix auto nn = n.stringof; /* Begin */ code ~= "asm(\n"; final switch( MODE ) { case 1: code ~= `"movq %rdi, reg\n" "movq %rsi, reg+8\n"`; break; case 2: code ~= `"movq %xmm0, reg\n" "movq %xmm1, reg+8\n"`; break; case 3: code ~= `"movq %xmm0, reg\n" "movq %rdi, reg+8\n"`; } code ~= "\n);\n"; code ~= "}\n"; //////////////// code ~= "void func_call_"~Ts~"( void ) {\n"; code ~= "struct "~Ts~" x = { "; code ~= value[0..len*3] ~ " };\n"; code ~= "func_pass_"~Ts~"( x );\n}\n"; } return code; } string c_generate_main() { string code = "void main() {\n"; foreach( int n, T; ALL_T ) { // Which type of compare static if(n < INT_END) enum MODE = 1; // Int else static if(n < SSE_END) enum MODE = 2; // Float else enum MODE = 3; // Mix auto nn = n.stringof; auto Ts = T.stringof; /* Begin */ code ~= `printf("/* %3d `~Ts~`\t*/ ", `~nn~`);`"\n"; if( !(expected[n] & 1) ) { code ~= `printf("null,\n");`"\n"; continue; } code ~= "asm(\n"; code ~= `"call func_ret_`~Ts~`\n"`"\n"; final switch( MODE ) { case 1: code ~= `"movq %rax, reg\n" "movq %rdx, reg+8\n"`; break; case 2: code ~= `"movq %xmm0, reg\n" "movq %xmm1, reg+8\n"`; break; case 3: code ~= `"movq %xmm0, reg\n" "movq %rax, reg+8\n"`; } code ~= "\n);\n"; code ~= `printf("[ 0x%016lx", reg.r1 );`"\n"; if( T.sizeof > 8 || MODE == 3 ) code ~= `printf(", 0x%016lx ],\n", reg.r2 );`"\n"; else code ~= `printf(", %015c ],\n", ' ' );`"\n"; } foreach( int n, T; ALL_T ) { // Which type of compare static if(n < INT_END) enum MODE = 1; // Int else static if(n < SSE_END) enum MODE = 2; // Float else enum MODE = 3; // Mix auto nn = n.stringof; auto Ts = T.stringof; /* Begin */ code ~= `printf("/* %3d `~Ts~`\t*/ ", `~nn~`);`"\n"; if( !(expected[n] & 1) ) { code ~= `printf("null,\n");`"\n"; continue; } code ~= "func_call_"~Ts~"();\n"; code ~= `printf("[ 0x%016lx", reg.r1 );`"\n"; if( T.sizeof > 8 || MODE == 3 ) code ~= `printf(", 0x%016lx ],\n", reg.r2 );`"\n"; else code ~= `printf(", %015c ],\n", ' ' );`"\n"; } return code ~ "}"; } pragma(msg, c_generate_returns() ); pragma(msg, c_generate_pass() ); pragma(msg, c_generate_main() ); // +/ /+ /** * Generate Functions that pass/return each Struct type * * ( Easier to look at objdump this way ) */ string d_generate_functions( ) { string code = "extern(C) {"; // pass foreach( s; ALL_T ) { string ss = s.stringof; code ~= "void func_in_"~ss~"( "~ss~" t ) { t.a = 12; }\n"; } // return foreach( s; ALL_T[0..10] ) { string ss = s.stringof; code ~= ` auto func_out_`~ss~`() { `~ss~` t; foreach( i, ref e; t.tupleof ) e = i+1; return t; }`; } // pass & return foreach( s; ALL_T[0..10] ) { string ss = s.stringof; code ~= ` auto func_inout_`~ss~`( `~ss~` t ) { foreach( i, ref e; t.tupleof ) e += 10; return t; }`; } return code ~ "\n} // extern(C)\n"; } //pragma( msg, d_generate_functions() ); mixin( d_generate_functions() ); // +/