Since the getelementptrs were implicitly generated by the CreateStore/Load I’m not sure how to get access to them.
So I hacked the assignment to be done thrice: once using a manual decomposition into two GEPs and stores, once using the “big” CreateStore, once via the setGlobal function, printing addresses and memory contents at each point to the degree that I have access to them.
It seems the following GEPs compute the same address?! I can buy myself not understanding how GEP works and doing it wrong, but builder.CreateStore() creates what look like identical GEPs implicitly…
i8** getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 0), align 4
i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1), align 4
The details.
This is the relevant part from my codegen:
auto ty = val->getType();
cout << “val type:” << endl;
ty->dump();
cout << “ptr type:” << endl;
ptr->getType()->dump();
// Print memory
ctx.EmitCall1(“debugPointer”, ptr);
// Set class pointer
auto c = ctx.bld.CreateExtractValue(val, 0, “class”);
auto cp = ctx.bld.CreateConstGEP2_32(ty, ptr, 0, 0);
auto cx = ctx.bld.CreatePtrToInt(cp, ctx.Int32Type());
ctx.EmitCall1(“debugInt”, cx);
ctx.bld.CreateStore(c, cp);
// Set datum
auto d = ctx.bld.CreateExtractValue(val, 1, “datum”);
auto dp = ctx.bld.CreateConstGEP2_32(ty, ptr, 0, 1);
auto dx = ctx.bld.CreatePtrToInt(dp, ctx.Int32Type());
ctx.EmitCall1(“debugInt”, dx);
ctx.bld.CreateStore(d, dp);
// Print memory
ctx.EmitCall1(“debugPointer”, ptr);
// Do the same with a single store
ctx.bld.CreateStore(val, ptr);
// Print memory
ctx.EmitCall1(“debugPointer”, ptr);
// Call out
ctx.EmitCall2(“setGlobal”, ptr, val);
// Print memory
ctx.EmitCall1(“debugPointer”, ptr);
Here is the compile-time output showing types of the value and the pointer:
val type:
{ i8*, i32 }
ptr type:
{ i8*, i32 }*
Here is the IR dump for the function (after a couple of passes), right before it’s fed to the JIT:
define { i8*, i32 } @“__anonToplevel/0”() prefix { i8*, i32 } (i32)* @“XEP:__anonToplevel/0” {
entry:
%0 = call { i8*, i32 } @debugPointer({ i8*, i32 }* nonnull @foo)
%1 = call { i8*, i32 } @debugInt(i32 ptrtoint ({ i8*, i32 }* @foo to i32))
store i8* @FixnumClass, i8** getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 0), align 4
%2 = call { i8*, i32 } @debugInt(i32 ptrtoint (i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1) to i32))
store i32 123, i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1), align 4
%3 = call { i8*, i32 } @debugPointer({ i8*, i32 }* nonnull @foo)
store i8* @FixnumClass, i8** getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 0), align 4
store i32 123, i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1), align 4
%4 = call { i8*, i32 } @debugPointer({ i8*, i32 }* nonnull @foo)
call void @setGlobal({ i8*, i32 }* nonnull @foo, { i8*, i32 } { i8* @FixnumClass, i32 123 })
%5 = call { i8*, i32 } @debugPointer({ i8*, i32 }* nonnull @foo)
ret { i8*, i32 } { i8* @FixnumClass, i32 123 }
}
Here is the runtime from calling the JITed function, including memory addresses and contents, with my annotations:
Before
p = 03C10000
class: 00000000
datum: 00000000
Should be address of the class slot → correct
x = 03C10000
Should be address of the datum slot, ie address of class slot + 4 → incorrect
x = 03C10000
Yeah, both values want to class slot, so actual class pointer got clobbered
p = 03C10000
class: 0000007B
datum: 00000000
Same result from the single CreateStore
p = 03C10000
class: 0000007B
datum: 00000000
Calling out to setGlobal as in my first email works
p = 03C10000
class: 039D2E98
datum: 0000007B
Finally, I didn’t manage nice disassembly yet, so here is the last output from --print-after-all for the function. The bizarre thing is that even this looks correct: the debugInt is called first with @foo, then @foo+4, and the stores seem to be going to the right addresses as well: @foo and @foo+4!
BB#0: derived from LLVM BB %entry
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugPointer, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugInt, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
MOV32mi %noreg, 1, %noreg, ga:foo, %noreg, ga:JazzFixnumClass; mem:ST4[getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 0)]
PUSHi32 ga:foo+4, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugInt, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
MOV32mi %noreg, 1, %noreg, ga:foo+4, %noreg, 123; mem:ST4[getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1)]
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugPointer, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
MOV32mi %noreg, 1, %noreg, ga:foo, %noreg, ga:JazzFixnumClass; mem:ST4[getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 0)]
MOV32mi %noreg, 1, %noreg, ga:foo+4, %noreg, 123; mem:ST4[getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @foo, i32 0, i32 1)]
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugPointer, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
PUSH32i8 123, %ESP, %ESP
CFI_INSTRUCTION
PUSHi32 ga:JazzFixnumClass, %ESP, %ESP
CFI_INSTRUCTION
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:setGlobal, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP
%ESP<def,tied1> = ADD32ri8 %ESP, 12, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
PUSHi32 ga:foo, %ESP, %ESP
CFI_INSTRUCTION
CALLpcrel32 ga:debugPointer, <regmask %BH %BL %BP %BPL %BX %DI %DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP, %ESP, %EAX<imp-def,dead>, %EDX<imp-def,dead>
%ESP<def,tied1> = ADD32ri8 %ESP, 4, %EFLAGS<imp-def,dead>
CFI_INSTRUCTION
%EAX = MOV32ri ga:JazzFixnumClass
%EDX = MOV32ri 123
RETL %EAX, %EDX
Also, I have essentially identical code working perfectly fine when the memory being written to is from @alloca.
I am completely clueless. Any suggestions most welcome.
Cheers,
– nikodemus