ldc
ldc copied to clipboard
Stub TypeInfo with changed member types silently leads to infinite codegen loop
Here's my reduced test case:
// dmain.d
module dmain;
private extern(C) void _start()
{ }
// object.d
module object;
alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);
alias string = immutable(char)[];
extern(C) __gshared void* _Dmodule_ref;
struct ModuleInfo
{
uint _flags;
uint _index;
}
class Object
{ }
class TypeInfo
{ }
class TypeInfo_Struct : TypeInfo
{
ubyte[120] ignore;
}
class TypeInfo_Class : TypeInfo
{
ubyte[125] ignore;
ubyte[1] ignore1;
ubyte[1] ignore2;
ubyte[1] ignore3;
ubyte[1] ignore4;
ubyte[1] ignore5;
ubyte[1] ignore6;
ubyte[1] ignore7;
ubyte[1] ignore8;
ubyte[1] ignore9;
ubyte[1] ignore10;
ubyte[1] ignore11;
}
class TypeInfo_AssociativeArray : TypeInfo
{
ubyte[16] ignore;
}
compile with ldc2 -c -singleobj -defaultlib= dmain.d object.d -of=test. This will never exit and produce an ever-growing .o file.
output of ls -al
-rw-r--r-- 1 mike users 286892032 Nov 9 12:54 test.o
Tested with LDC 0.14.0 and 0.15.0 alpha1 in Arch Linux 64.
I'm still not sure what's causing this, but I compiled with -v and -v-cg and it hangs here:
...
codegen: main (app/main.d)
codegen: __entrypoint (__entrypoint.d)
I did a little more debugging to find out what's going on here. It seems to be hanging in toobj.cpp:94, Passes.run(m) on the first module. It doesn't seem to matter which module it is.
This is getting beyond me now, as that appears to be LLVM code. Anyway, I hope this information is useful for someone who wishes to look a little deeper into this.
@JinShil - Mike, I think this is because TypeInfo_Class initializer generated by gen/classes.cpp is hardcoded to types specified in regular druntime/src/object_.d. Since TypeInfo_Class in above object.d just tries to match sizeof, this leads to a type mismatch in IR. It can be seen by compiling to IR and then using llc.
$ ldc2 -c -singleobj -defaultlib= dmain.d object.d -of=test -output-ll
$ llc test.ll
llc: test.ll:38:53: error: element 2 of struct initializer doesn't match struct element type
@_D6Object7__ClassZ = global %object.TypeInfo_Class { %object.TypeInfo_Class.__vtbl* @_D14TypeInfo_Class6__vtblZ, i8* null, { i64, i8* } { i64 16, i8* bitcast (%object.Object_init* @_D6Object6__initZ to i8*) }, { i64, i8* } { i64 13, i8* getelementptr inbounds ([14 x i8]* @.str1, i32 0, i32 0) }, { i64, i8** } { i64 1, i8** bitcast (%object.Object.__vtbl* @_D6Object6__vtblZ to i8**) }, [1 x i8] zeroinitializer, %object.TypeInfo_Class* null, i8* null, [1 x i8] zeroinitializer, i32 34, i8* null, [1 x i8] zeroinitializer, [1 x i8] zeroinitializer, i8* null }
Not sure why this LLVM error doesn't show up when going directly to object code, perhaps assertions need to be enabled in LLVM.
Anyway, if I take your example object.d above but change just declaration of TypeInfo_Class to following, then it all compiles.
class TypeInfo_Class : TypeInfo
{
byte[] init;
string name;
void*[] vtbl;
void*[] interfaces;
TypeInfo_Class base;
void* destructor;
void function(Object) classInvariant;
uint m_flags;
void* deallocator;
void*[] m_offTi;
void function(Object) defaultConstructor; // default Constructor
immutable(void)* m_RTInfo; // data for precise GC
}
We should see if we can't introduce a proper frontend error/ICE for such mismatches, no matter what solution we end up with for Mike's use case.
AFAIK, we check the number of fields, but not their type.