Thread overview | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 20, 2017 The X Macro using D | ||||
---|---|---|---|---|
| ||||
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 <g>.) ================ 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; |
July 20, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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.
|
July 20, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | 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!
|
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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;
}());
|
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 |
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | 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 |
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | 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.
|
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stefan Koch | 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 |
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 |
July 21, 2017 Re: The X Macro using D | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 <g>.) > > ================ 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. |
Copyright © 1999-2021 by the D Language Foundation