[llvm-dev] [newbie] trouble with global variables and CreateLoad/Store in JIT

Nikodemus Siivola via llvm-dev
2017-06-05T19:57:39Z

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<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugPointer>, <regmask %BH %BL %BP %BPL %BX
%DI
%DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame instruction>
        PUSHi32 <ga:@foo>, %ESP<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugInt>, <regmask %BH %BL %BP %BPL %BX %DI
%DIL
%EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame 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<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugInt>, <regmask %BH %BL %BP %BPL %BX %DI
%DIL
%EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame 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<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugPointer>, <regmask %BH %BL %BP %BPL %BX
%DI
%DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame 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<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugPointer>, <regmask %BH %BL %BP %BPL %BX
%DI
%DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame instruction>
        PUSH32i8 123, %ESP<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        PUSHi32 <ga:@JazzFixnumClass>, %ESP<imp-def>,
%ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        PUSHi32 <ga:@foo>, %ESP<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@setGlobal>, <regmask %BH %BL %BP %BPL %BX %DI
%DIL
%EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 12,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame instruction>
        PUSHi32 <ga:@foo>, %ESP<imp-def>, %ESP<imp-use>
        CFI_INSTRUCTION <call frame instruction>
        CALLpcrel32 <ga:@debugPointer>, <regmask %BH %BL %BP %BPL %BX
%DI
%DIL %EBP %EBX %EDI %ESI %SI %SIL>, %ESP<imp-use>, %ESP<imp-def>,
%EAX<imp-def,dead>, %EDX<imp-def,dead>
        %ESP<def,tied1> = ADD32ri8 %ESP<tied0>, 4,
%EFLAGS<imp-def,dead>
        CFI_INSTRUCTION <call frame instruction>
        %EAX<def> = MOV32ri <ga:@JazzFixnumClass>
        %EDX<def> = MOV32ri 123
        RETL %EAX<kill>, %EDX<kill>

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
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20170605/8c7c85cb/attachment.html>