Re: The X Macro using D
On Saturday, 22 July 2017 at 11:50:40 UTC, Stefan Koch wrote: tuple map and array are all pretty expensive. please profile. Well a bit more compile time isn't the end of the world, and by far not the only metric (e.g. readability and maintainability also rank high). You're slightly obsessed with the template compile-time topic ;). BTW, the term expensive is fairly subjective easily misunderstood. Sth. more specific would reduce the chance for confusion, e.g. "tuple map and array require longer to compile"
Re: The X Macro using D
On Friday, 21 July 2017 at 20:44:13 UTC, Enamex wrote: On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote: [...] How about this (if I'm not mistaken, this's only one template instantiation per tuple-type): [...] tuple map and array are all pretty expensive. please profile.
Re: The X Macro using D
On Friday, 21 July 2017 at 19:26:05 UTC, Johan Engelen wrote: On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote: Some time ago, I wrote about the X Macro in C: https://digitalmars.com/articles/b51.html I used it from time to time in C code. It's one of the things I actually like about the C preprocessor. But in translating the aged C code to D it was time to make X work in D. Here's the C code, followed by the D translation. This mechanism is used in LLVM in a number of places, where the list entries are used e.g. to populate tables and to define aggregate member fields. For example: https://github.com/llvm-mirror/compiler-rt/blob/master/lib/profile/InstrProfData.inc#L29-L51 And those in turn are generated from TableGen.
Re: The X Macro using D
On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote: On 7/20/2017 2:21 PM, Stefan Koch wrote: Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE. If you like, present the CTFE solution. Should be fun! How about this (if I'm not mistaken, this's only one template instantiation per tuple-type): ```d import std.typecons: tuple, Tuple; import std.algorithm: map; import std.array: array; enum regm_t { AX, BX, CX, DX, DI, SI, None } enum tym_t { uchar_, ushort_, ulong_ } enum Ydata = [ tuple("AH", 4, regm_t.AX, tym_t.uchar_), tuple("AL", 0, regm_t.AX, tym_t.uchar_), tuple("AX", 8, regm_t.AX, tym_t.ushort_), tuple("BH", 7, regm_t.BX, tym_t.uchar_), tuple("BL", 3, regm_t.BX, tym_t.uchar_), tuple("BP", 13, regm_t.None, tym_t.ushort_), tuple("BX", 11, regm_t.BX, tym_t.ushort_), tuple("CH", 5, regm_t.CX, tym_t.uchar_), tuple("CL", 1, regm_t.CX, tym_t.uchar_), tuple("CX", 9, regm_t.CX, tym_t.ushort_), tuple("DH", 6, regm_t.DX, tym_t.uchar_), tuple("DI", 15, regm_t.DI, tym_t.ushort_), tuple("DL", 2, regm_t.DX, tym_t.uchar_), tuple("DX", 10, regm_t.DX, tym_t.ushort_), tuple("EAX", 16, regm_t.AX, tym_t.ulong_), tuple("EBP", 21, regm_t.None, tym_t.ulong_), tuple("EBX", 19, regm_t.BX, tym_t.ulong_), tuple("ECX", 17, regm_t.CX, tym_t.ulong_), tuple("EDI", 23, regm_t.DI, tym_t.ulong_), tuple("EDX", 18, regm_t.DX, tym_t.ulong_), tuple("ESI", 22, regm_t.SI, tym_t.ulong_), tuple("ESP", 20, regm_t.None, tym_t.ulong_), tuple("SI", 14, regm_t.SI, tym_t.ushort_), tuple("SP", 12, regm_t.None, tym_t.ushort_), ]; static auto Y(size_t idx, T...)(Tuple!(T)[] ts) pure nothrow { // I thought to try something like assumeUnique here // but was thinking of the Rust semantics in doing so // Not sure if this leads to spurious allocations at // the points where Y is used. Is there someway to tell them, // even if the target type is const or immutable // that this returned array is brand new with no other references // around? return ts .map!(x => x[idx]) .array ; } enum { Xtab = 0, Xreg, Xmask, Xty, } // Register number to use in addressing mode private __gshared const(char)*[24] pseudotab = Y!Xtab(Ydata); // Register number to use in addressing mode __gshared ubyte[24] pseudoreg = Y!Xreg(Ydata); // Mask to use for registers affected __gshared regm_t[24] pseudomask = Y!Xmask(Ydata); // Table for type of pseudo register variable private __gshared const(tym_t)[24] pseudoty = Y!Xty(Ydata); ```
Re: The X Macro using D
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote: Some time ago, I wrote about the X Macro in C: https://digitalmars.com/articles/b51.html I used it from time to time in C code. It's one of the things I actually like about the C preprocessor. But in translating the aged C code to D it was time to make X work in D. Here's the C code, followed by the D translation. This mechanism is used in LLVM in a number of places, where the list entries are used e.g. to populate tables and to define aggregate member fields. For example: https://github.com/llvm-mirror/compiler-rt/blob/master/lib/profile/InstrProfData.inc#L29-L51
Re: The X Macro using D
On 2017-07-21 14:27, Olivier FAURE wrote: Quick questions: isn't it possible to do private __gshared const(char)*[24] pseudotab = Y.map!(x => x.id); instead? That seems like the most obvious and easy to read option; and in contrast to the other solutions proposed, it's closer to the Rule of Least Power. Yes, that's basically what my solution is doing [1]. [1] http://forum.dlang.org/post/oksd27$1li9$1...@digitalmars.com -- /Jacob Carlborg
Re: The X Macro using D
On Friday, 21 July 2017 at 12:27:35 UTC, Olivier FAURE wrote: private __gshared const(char)*[24] pseudotab = Y.map!(x => x.id); I meant private __gshared static immutable string[Y.length] pseudotab = Y.map!(x => x.id); but you get my point. Also, upon trying it, it doesn't seem to work (at least the immutable part doesn't), but I don't really understand why. All the variables in the expression are known at compile time.
Re: The X Macro using D
On Friday, 21 July 2017 at 08:06:09 UTC, Stefan Koch wrote: My pleasure :) // ... mixin((){ // ... enum Y = [ // id reg mask ty X("AH", 4, mAX, TYuchar), X("AL", 0, mAX, TYuchar), X("AX", 8, mAX, TYushort), X("BH", 7, mBX, TYuchar), X("BL", 3, mBX, TYuchar), // ... X("ESI", 22, mSI, TYulong), X("ESP", 20, 0, TYulong), X("SI", 14, mSI, TYushort), X("SP", 12, 0, TYushort), ]; enum lns = itos(Y.length); string pseudotab = "\nprivate __gshared static immutable string[" ~ lns ~ "] pseudotab = ["; foreach(i, r; Y) { pseudotab ~= `"` ~ r.id ~ `", `; } pseudotab ~= "];\n"; }()); Quick questions: isn't it possible to do private __gshared const(char)*[24] pseudotab = Y.map!(x => x.id); instead? That seems like the most obvious and easy to read option; and in contrast to the other solutions proposed, it's closer to the Rule of Least Power.
Re: The X Macro using D
On Friday, 21 July 2017 at 11:19:47 UTC, Patrick Schluter wrote: In C there's no point in the X macro anymore since C99. Designated initializer allow to do it properly[1] now. enum COLORS { red, blue, green, max }; char *cstring[max] = {[red]="red", [blue]="blue", [green]="green" }; /* C99 */ I don't see how this allows data for different arrays to be written in interleaved form and then extracted by column to initialize the separate arrays, as Walter's does.
Re: The X Macro using D
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote: Some time ago, I wrote about the X Macro in C: https://digitalmars.com/articles/b51.html I used it from time to time in C code. It's one of the things I actually like about the C preprocessor. But in translating the aged C code to D it was time to make X work in D. Here's the C code, followed by the D translation. (I suppose it could be done with C++ templates, but I'll leave that to Andrei or Eric Niebler .) C Version // Macro trick to generate several parallel tables #define Y \ X("AH",4,mAX,TYuchar) \ X("AL",0,mAX,TYuchar) \ X("AX",8,mAX,TYushort) \ X("BH",7,mBX,TYuchar) \ X("BL",3,mBX,TYuchar) \ X("BP",13,0,TYushort) \ X("BX",11,mBX,TYushort) \ X("CH",5,mCX,TYuchar) \ X("CL",1,mCX,TYuchar) \ X("CX",9,mCX,TYushort) \ X("DH",6,mDX,TYuchar) \ X("DI",15,mDI,TYushort) \ X("DL",2,mDX,TYuchar) \ X("DX",10,mDX,TYushort) \ X("EAX",16,mAX,TYulong) \ X("EBP",21,0,TYulong) \ X("EBX",19,mBX,TYulong) \ X("ECX",17,mCX,TYulong) \ X("EDI",23,mDI,TYulong) \ X("EDX",18,mDX,TYulong) \ X("ESI",22,mSI,TYulong) \ X("ESP",20,0,TYulong) \ X("SI",14,mSI,TYushort) \ X("SP",12,0,TYushort) // Table for identifiers static const char *pseudotab[] = { #define X(id,reg,m,ty) id, Y #undef X }; // Register number to use in addressing mode unsigned char pseudoreg[] = { #define X(id,reg,m,ty) reg, Y #undef X }; // Mask to use for registers affected regm_t pseudomask[] = { #define X(id,reg,m,ty) m, Y #undef X }; // Table for type of pseudo register variable static unsigned char pseudoty[] = { #define X(id,reg,m,ty) mTYvolatile | ty, Y #undef X }; D Version /* 4 parallel tables using "X Macro" technique */ template Y(alias X) { enum Y = [ // id reg mask ty X!("AH", 4, mAX, TYuchar), X!("AL", 0, mAX, TYuchar), X!("AX", 8, mAX, TYushort), X!("BH", 7, mBX, TYuchar), X!("BL", 3, mBX, TYuchar), X!("BP", 13, 0, TYushort), X!("BX", 11, mBX, TYushort), X!("CH", 5, mCX, TYuchar), X!("CL", 1, mCX, TYuchar), X!("CX", 9, mCX, TYushort), X!("DH", 6, mDX, TYuchar), X!("DI", 15, mDI, TYushort), X!("DL", 2, mDX, TYuchar), X!("DX", 10, mDX, TYushort), X!("EAX", 16, mAX, TYulong), X!("EBP", 21, 0, TYulong), X!("EBX", 19, mBX, TYulong), X!("ECX", 17, mCX, TYulong), X!("EDI", 23, mDI, TYulong), X!("EDX", 18, mDX, TYulong), X!("ESI", 22, mSI, TYulong), X!("ESP", 20, 0, TYulong), X!("SI", 14, mSI, TYushort), X!("SP", 12, 0, TYushort), ]; } // Table for identifiers template Xtab(alias A, alias B, alias C, alias D) { enum Xtab = A; } private __gshared const(char)*[24] pseudotab = Y!Xtab; // Register number to use in addressing mode template Xreg(alias A, alias B, alias C, alias D) { enum Xreg = B; } __gshared ubyte[24] pseudoreg = Y!Xreg; // Mask to use for registers affected template Xmask(alias A, alias B, alias C, alias D) { enum Xmask = C; } __gshared regm_t[24] pseudomask = Y!Xmask; // Table for type of pseudo register variable template Xty(alias A, alias B, alias C, alias D) { enum Xty = mTYvolatile | D; } private __gshared const(tym_t)[24] pseudoty = Y!Xty; I wonder if you could use one of the SoA implementations (e.g from here: https://wiki.dlang.org/Transforming_slice_of_structs_into_struct_of_slices) to the same effect.
Re: The X Macro using D
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote: Some time ago, I wrote about the X Macro in C: https://digitalmars.com/articles/b51.html I used it from time to time in C code. It's one of the things I actually like about the C preprocessor. But in translating the aged C code to D it was time to make X work in D. Here's the C code, followed by the D translation. In C there's no point in the X macro anymore since C99. Designated initializer allow to do it properly[1] now. enum COLORS { red, blue, green, max }; char *cstring[max] = {[red]="red", [blue]="blue", [green]="green" }; /* C99 */ It works also with array indexes[2]. int a[3] = { [2]=1, [0]=3, [1]=2 }; /* C99 designated initializer */ int a[3] = { [2]=1, [0]=3, 2 }; /* C99 designated initializer */ C++ hasn't yet integrated. [1]: https://dlang.org/ctod.html#arrayenum [2]: https://dlang.org/ctod.html#arrayinit2
Re: The X Macro using D
On 2017-07-21 10:25, Stefan Koch wrote: and leaves bloat in the binary. Perhaps that should be fixed in the compiler ;) -- /Jacob Carlborg
Re: The X Macro using D
On Friday, 21 July 2017 at 08:12:55 UTC, Jacob Carlborg wrote: On 2017-07-21 00:02, Walter Bright wrote: On 7/20/2017 2:21 PM, Stefan Koch wrote: Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE. If you like, present the CTFE solution. Should be fun! Here's my solution. It sill uses templates for the implementation of "map": that uses more compile-time then mine :) and leaves bloat in the binary.
Re: The X Macro using D
On 2017-07-21 10:06, Stefan Koch wrote: My pleasure :) My approach, without string mixin: http://forum.dlang.org/post/oksd27$1li9$1...@digitalmars.com :) -- /Jacob Carlborg
Re: The X Macro using D
On 2017-07-21 00:02, Walter Bright wrote: On 7/20/2017 2:21 PM, Stefan Koch wrote: Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE. If you like, present the CTFE solution. Should be fun! Here's my solution. It sill uses templates for the implementation of "map": auto map(alias func)(const(Row)[] array) { alias R = typeof(func(Row.init)); R[] result; foreach (e ; array) result ~= func(e); return result; } struct Row { string id; int reg; int mask; int ty; } immutable Row[2] table = [ Row("AH", 4, mAX, TYuchar), Row("AL", 0, mAX, TYuchar) ]; alias regm_t = int; alias tym_t = int; enum mAX = 1; enum TYuchar = 1; enum mTYvolatile = 2; private __gshared const(char)*[table.length] pseudotab = table.map!(row => row.id); __gshared ubyte[table.length] pseudoreg = table.map!(row => row.reg); __gshared int[table.length] pseudomask = table.map!(row => row.mask); private __gshared const(tym_t)[table.length] pseudoty = table.map!(row => mTYvolatile | row.ty); I added some type aliases and enums to be able to run the code self contained without DMD. Some advantages: * Less use of templates * More readable since there's a specific type (Row) with named fields, no need to write the fields in comments * Easier to update when the length of the arrays are not hard coded -- /Jacob Carlborg
Re: The X Macro using D
On Thursday, 20 July 2017 at 22:02:32 UTC, Walter Bright wrote: On 7/20/2017 2:21 PM, Stefan Koch wrote: Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE. If you like, present the CTFE solution. Should be fun! My pleasure :) string itos(uint n) { char[] result = []; immutable len = 10; result.length = len; uint i = len - 1; while (n > 10) { result[i--] = cast(char) ('0' + (n % 10)); n /= 10; } result[i] = cast(char) ('0' + (n % 10)); return cast(string) result[i .. $]; } mixin((){ static struct X { string id; ubyte reg; uint mask; ubyte ty; } enum Y = [ // id reg mask ty X("AH", 4, mAX, TYuchar), X("AL", 0, mAX, TYuchar), X("AX", 8, mAX, TYushort), X("BH", 7, mBX, TYuchar), X("BL", 3, mBX, TYuchar), X("BP", 13, 0, TYushort), X("BX", 11, mBX, TYushort), X("CH", 5, mCX, TYuchar), X("CL", 1, mCX, TYuchar), X("CX", 9, mCX, TYushort), X("DH", 6, mDX, TYuchar), X("DI", 15, mDI, TYushort), X("DL", 2, mDX, TYuchar), X("DX", 10, mDX, TYushort), X("EAX", 16, mAX, TYulong), X("EBP", 21, 0, TYulong), X("EBX", 19, mBX, TYulong), X("ECX", 17, mCX, TYulong), X("EDI", 23, mDI, TYulong), X("EDX", 18, mDX, TYulong), X("ESI", 22, mSI, TYulong), X("ESP", 20, 0, TYulong), X("SI", 14, mSI, TYushort), X("SP", 12, 0, TYushort), ]; enum lns = itos(Y.length); string pseudotab = "\nprivate __gshared static immutable string[" ~ lns ~ "] pseudotab = ["; string pseudoreg = "\nprivate __gshared static immutable ubyte[" ~ lns ~ "] pseudoreg = ["; string pseudomask = "\nprivate __gshared static immutable regm_t[" ~ lns ~ "] pseudomask = ["; string pseudoty = "\nprivate __gshared static immutable ubyte[" ~ lns ~ "] pseudoty = ["; foreach(i, r; Y) { pseudotab ~= `"` ~ r.id ~ `", `; pseudoreg ~= itos(r.reg) ~ `, `; pseudomask ~= itos(r.mask) ~ `, `; pseudoty ~= itos(r.ty) ~ `, `; } pseudotab ~= "];\n"; pseudoreg ~= "];\n"; pseudomask ~= "];\n"; pseudoty ~= "];\n"; return pseudotab ~ pseudoreg ~ pseudomask ~ pseudoty; }());
Re: The X Macro using D
On 7/20/2017 2:21 PM, Stefan Koch wrote: Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE. If you like, present the CTFE solution. Should be fun!
Re: The X Macro using D
On Thursday, 20 July 2017 at 21:17:45 UTC, Walter Bright wrote: template Y(alias X) { enum Y = [ // id reg mask ty X!("AH", 4, mAX, TYuchar), X!("AL", 0, mAX, TYuchar), X!("AX", 8, mAX, TYushort), X!("BH", 7, mBX, TYuchar), X!("BL", 3, mBX, TYuchar), X!("BP", 13, 0, TYushort), X!("BX", 11, mBX, TYushort), X!("CH", 5, mCX, TYuchar), X!("CL", 1, mCX, TYuchar), X!("CX", 9, mCX, TYushort), X!("DH", 6, mDX, TYuchar), X!("DI", 15, mDI, TYushort), X!("DL", 2, mDX, TYuchar), X!("DX", 10, mDX, TYushort), X!("EAX", 16, mAX, TYulong), X!("EBP", 21, 0, TYulong), X!("EBX", 19, mBX, TYulong), X!("ECX", 17, mCX, TYulong), X!("EDI", 23, mDI, TYulong), X!("EDX", 18, mDX, TYulong), X!("ESI", 22, mSI, TYulong), X!("ESP", 20, 0, TYulong), X!("SI", 14, mSI, TYushort), X!("SP", 12, 0, TYushort), ]; } // Table for identifiers template Xtab(alias A, alias B, alias C, alias D) { enum Xtab = A; } private __gshared const(char)*[24] pseudotab = Y!Xtab; // Register number to use in addressing mode template Xreg(alias A, alias B, alias C, alias D) { enum Xreg = B; } __gshared ubyte[24] pseudoreg = Y!Xreg; // Mask to use for registers affected template Xmask(alias A, alias B, alias C, alias D) { enum Xmask = C; } __gshared regm_t[24] pseudomask = Y!Xmask; // Table for type of pseudo register variable template Xty(alias A, alias B, alias C, alias D) { enum Xty = mTYvolatile | D; } private __gshared const(tym_t)[24] pseudoty = Y!Xty; Please tell me this is not going to get into dmd :) templates are so much more expensive then macros. (Well, for now :) ) Those templates can and should be replaced by CTFE.