Created
August 7, 2022 21:01
-
-
Save JohanEngelen/137a2a8a86a2e1bcdadcc8c7ac81f373 to your computer and use it in GitHub Desktop.
Generates random struct with bitfields in C and D code and tests whether D compiler correctly compiles C bitfields
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Test importC bitfields. | |
// Generates random bitfield structure type, generates the C code and header file, generates D file that tests reading/writing from bitfields in C-land and D-land. | |
/+ | |
struct Test { | |
{<empty>|some __alignment...} {<empty>|signed|unsigned} {int|char|long|size_t|float|...} member1 {<empty>|:<some number 0..bitwidth of the storage type}; | |
... repeat for member2, member3, random number of members | |
} | |
void resetMember1_C(Test* t) | |
{ | |
t->member1 = 0; | |
} | |
...repeat for how many members there are | |
size_t sizeofTest_C() | |
{ | |
return sizeof(Test); | |
} | |
While generating the C file, you also generate a D file: | |
extern(C) | |
struct Test { | |
// ofcourse corresponding to the generated C type! | |
{<empty>|align(...)} {int|uint|bool|byte|char|long|size_t|float|...} member1 {<empty>|:<some number 0..bitwidth of the storage type}; | |
... repeat for member2, member3, random number of members | |
} | |
extern(C) void resetMember1_C(Test* t); | |
void resetMember1_D(Test* t) | |
{ | |
t.member1 = 0; | |
} | |
...repeat for how many members there are | |
extern(C) size_t sizeofTest_C(); | |
size_t sizeofTest_D() | |
{ | |
return Test.sizeof(); | |
} | |
void main() | |
{ | |
assert(sizeofTest_C() == sizeofTest_D()); | |
foreach(i; number of members in Test) { | |
Test s_C; | |
Test s_D; | |
memset(&s_C, 0xFF, Test.sizeof); | |
memset(&s_D, 0xFF, Test.sizeof); | |
resetMemberi_C(&s_C); | |
resetMemberi_D(&s_D); | |
assert(memcmp(&s_C, &s_D, Test.sizeof)); | |
} | |
} | |
+/ | |
import std.stdio; | |
import std.random; | |
import std.conv; | |
import std.algorithm; | |
import std.process; | |
enum BuiltinType { | |
BOOL, | |
CHAR, | |
UNSIGNED_CHAR, | |
INT, | |
UNSIGNED_INT, | |
LONG, | |
UNSIGNED_LONG, | |
} | |
BuiltinType getRandomType() { | |
return uniform!BuiltinType(); | |
} | |
uint getTypeBitWidth(BuiltinType t) { | |
with (BuiltinType) final switch (t) { | |
case BOOL: return 1; | |
case CHAR: return 8; | |
case UNSIGNED_CHAR: return 8; | |
case INT: return 32; | |
case UNSIGNED_INT: return 32; | |
case LONG: return 64; | |
case UNSIGNED_LONG: return 64; | |
} | |
} | |
string getCTypeString(BuiltinType t) { | |
with (BuiltinType) final switch (t) { | |
case BOOL: return "_Bool"; | |
case CHAR: return "char"; | |
case UNSIGNED_CHAR: return "unsigned char"; | |
case INT: return "int"; | |
case UNSIGNED_INT: return "unsigned"; | |
case LONG: return "long"; | |
case UNSIGNED_LONG: return "unsigned long"; | |
} | |
} | |
string getDTypeString(BuiltinType t) { | |
with (BuiltinType) final switch (t) { | |
case BOOL: return "bool"; | |
case CHAR: return "char"; | |
case UNSIGNED_CHAR: return "ubyte"; | |
case INT: return "int"; | |
case UNSIGNED_INT: return "uint"; | |
case LONG: return "long"; | |
case UNSIGNED_LONG: return "ulong"; | |
} | |
} | |
struct Member { | |
string name; | |
BuiltinType type; | |
bool isBitfield; // is this a bitfield or not | |
uint bitwidth; // number of bits if it is a bitfield. bitwidth must be less than or equal to the bit width of the specified type. | |
this(string _name) { | |
name = _name; | |
type = getRandomType(); | |
isBitfield = randomBitfieldOrNot(); | |
bitwidth = (uniform(0,10) < 2) ? 0 : (type == BuiltinType.BOOL) ? 1 : uniform(0, getTypeBitWidth(type)); | |
} | |
} | |
bool randomBitfieldOrNot() { | |
return uniform(0,20) < 18; // more chance to generate a bitfield than not | |
} | |
struct StructTypeDescription | |
{ | |
Member[] members; | |
} | |
StructTypeDescription generateRandomType() { | |
StructTypeDescription s; | |
auto numMembers = uniform(1, 20); | |
foreach (i; 0..numMembers) | |
{ | |
s.members ~= Member("member"~to!string(i)); | |
} | |
return s; | |
} | |
void writeCTestCodeFile(StructTypeDescription s, string testname) { | |
auto f = File(testname ~ "_c.c", "w"); | |
f.writeln("typedef unsigned long size_t;"); | |
f.writeln("typedef struct {"); | |
foreach (member; s.members) { | |
f.write(" "); | |
f.write(getCTypeString(member.type)); | |
f.write(" "); | |
if (! (member.isBitfield && member.bitwidth == 0)) // 0-size bitfield must be anonymous | |
f.write(member.name); | |
if (member.isBitfield) { | |
f.write(" : "); | |
f.write(to!string(member.bitwidth)); | |
} | |
f.writeln(";"); | |
} | |
f.writeln("} Test;"); | |
f.writeln(`size_t sizeofTest_C() { return sizeof(Test); }`); | |
foreach (member; s.members) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`void reset_`~member.name~`_C(Test* t) { t->`~member.name~` = 0; }`); | |
} | |
foreach (member; s.members) with(member) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`void set_`~name~`_C(Test* t) { t->`~name~` = 0; t->`~name~` = ~t->`~name~`; }`); | |
} | |
} | |
void writeDTestCodeFile(StructTypeDescription s, string testname) { | |
auto f = File(testname ~ ".d", "w"); | |
f.writeln(`import ` ~ testname ~ `_c;`); | |
f.writeln(`import core.stdc.string;`); | |
foreach (member; s.members) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`void reset_`~member.name~`_D(Test* t) { t.`~member.name~` = 0; }`); | |
} | |
foreach (member; s.members) with(member) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`void set_`~name~`_D(Test* t) { t.`~name~` = 0; t.`~name~` = cast(` ~ getDTypeString(type) ~ `)(~t.`~name~`); }`); | |
} | |
f.writeln(` | |
void print_struct(Test* t) { | |
import std.stdio; | |
ubyte* tv = cast(ubyte*) t; | |
foreach (i; 0..Test.sizeof) | |
writef!"%x "(tv[i]); | |
writeln(); | |
} | |
void main() | |
{ | |
assert(sizeofTest_C() == Test.sizeof); | |
Test s_C; | |
Test s_D; | |
`); | |
foreach (i, member; s.members) with(member) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`memset(&s_C, 0xFF, Test.sizeof);`); | |
f.writeln(`memset(&s_D, 0xFF, Test.sizeof);`); | |
f.writeln(`reset_`~member.name~`_C(&s_C);`); | |
f.writeln(`reset_`~member.name~`_D(&s_D);`); | |
f.writeln(`assert(memcmp(&s_C, &s_D, Test.sizeof) == 0);`); | |
f.writeln(); | |
} | |
foreach (i, member; s.members) with(member) { | |
if (member.isBitfield && member.bitwidth == 0) | |
continue; | |
f.writeln(`memset(&s_C, 0, Test.sizeof);`); | |
f.writeln(`memset(&s_D, 0, Test.sizeof);`); | |
f.writeln(`set_`~member.name~`_C(&s_C);`); | |
f.writeln(`set_`~member.name~`_D(&s_D);`); | |
f.writeln(`assert(memcmp(&s_C, &s_D, Test.sizeof) == 0);`); | |
f.writeln(); | |
} | |
f.writeln(`}`); | |
} | |
string Dcompiler = "./dmd-2.100.0/osx/bin/dmd"; | |
void writeShellCmdFile(StructTypeDescription s, string testname) { | |
auto f = File(testname ~ ".sh", "w"); | |
f.writeln(`gcc -c ` ~ testname ~ `_c.c -o ` ~ testname ~ `_c.o`); | |
f.writeln(Dcompiler ~ " " ~ testname ~ `_c.c -run ` ~ testname ~ `.d`); | |
} | |
bool testStruct(StructTypeDescription s, string testname = "bf0") { | |
write("gen, "); | |
writeCTestCodeFile(s, testname); | |
writeDTestCodeFile(s, testname); | |
writeShellCmdFile(s, testname); | |
write("gcc, "); | |
auto gcc_output = execute([`gcc`, `-c`, testname ~ `_c.c`, `-o`, testname ~ `_c.o`]); | |
if (gcc_output.status != 0) { | |
writeln("C compilation failed:\n", gcc_output.output); | |
return false; | |
} | |
write("dmd + run"); | |
auto dmd = execute([Dcompiler, testname ~ `_c.c`, `-run`, testname ~ `.d`]); | |
if (dmd.status != 0) { | |
writeln("D compilation/execution failed:\n", dmd.output); | |
return false; | |
} | |
writeln("."); | |
return true; | |
} | |
void main() { | |
bool success; | |
do { | |
auto s = generateRandomType(); | |
string testname = "bf0"; | |
success =testStruct(s, testname); | |
} while (success); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment