Crash in JIT

Hello,

[Using LLVM r155315, according to svn log | head]

I am experimenting with programatically building and jitting functions in a module, and I seem to be coming across a crash in some generated code. Using the llvm-c interface I build up the module which dumps like this:

; ModuleID = 'MyModule’
target datalayout = "i686-apple-darwin11"
target triple = "i686-apple-darwin11"

define i32 @functionName(i32 %m, i32 %x, i32 %b) {
entry:
%mx = mul i32 %m, %x
%y = add i32 %mx, %b
ret i32 %y
}

Which looks OK to me. Note I’m not 100% sure (actually, I suspect is wrong) about the ‘target datalayout’ but with or without that line makes no difference to my results.

When I attempt to run the JIT (interpreter works fine) I get a EXC_BAD_ACCESS with the following backtrace:

  • thread #1: tid = 0x2007, 0x0000000102e00042, stop reason = EXC_BAD_ACCESS (code=1, address=0x60c93a2c)
    frame #0: 0x0000000102e00042
    frame #1: 0x00000001015c01fb LLVMPlayGroundllvm::JIT::runFunction(llvm::Function*, std::vector<llvm::GenericValue, std::allocator<llvm::GenericValue> > const&) + 4747 at JIT.cpp:547 frame #2: 0x00000001015a0db2 LLVMPlayGroundLLVMRunFunction + 322 at ExecutionEngineBindings.cpp:195
    frame #3: 0x0000000101096125 LLVMPlayGroundmain + 741 at main.c:91 frame #4: 0x0000000101095e34 LLVMPlayGroundstart + 52

Which looks to be in some non-c code. At least XCode cannot find any corresponding source:

0x102e00042: movl $10, (%rsp) ← DIE
0x102e00049: movl $5, 4(%rsp)
0x102e00051: movl $1, 8(%rsp)
0x102e00059: movl %eax, 24(%rsp)
0x102e0005d: movl %ecx, 20(%rsp)
It looks like marshalling the 3 arguments to my function.

Looking at rsp it looks like the top 32bits have been truncated, compared to other pointers floating around the system:

rbp = 0x00007fff60c94070
rsp = 0x0000000060c93a2c
r8 = 0x00007fff60c93800
r9 = 0x00007fff60c93808

If I use the debugger to poke 0x00007fff into the top half of rsp, stepping through works, and after one jump I get to my function; I see the mul and add instruction. Continuing to step, I need one more fixup of rsp (perhaps corresponding to two jitted functions: mine, and the auto-built nullary stub?) before returning to my main function.

Is there anything obvious I’ve missed? Sorry for the long mail but I figure if I put all my data, then where I went wrong would be more obvious to someone who knows what they’re doing :slight_smile:

Thanks, and please let me know if there’s anything extra I can add.
-DavidM

PS: below is my complete source.

#include <stdio.h>
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS

#include “llvm-c/Core.h”
#include “llvm-c/ExecutionEngine.h”

int main(int argc, const char * argv[])
{

LLVMContextRef llvm;
llvm = LLVMContextCreate();

LLVMModuleRef module;
module = LLVMModuleCreateWithNameInContext(“MyModule”, llvm);
//LLVMSetDataLayout(module, “i686-apple-darwin11”); ← is needed? What is correct?
LLVMSetTarget(module, “i686-apple-darwin11”);

LLVMTypeRef int32 = LLVMInt32TypeInContext(llvm);

LLVMTypeRef funcType;
LLVMTypeRef threeInts[] = {int32, int32, int32};
funcType = LLVMFunctionType(int32, threeInts, 3, 0);

LLVMValueRef func;
func = LLVMAddFunction(module, “functionName”, funcType);

LLVMValueRef mParam = LLVMGetParam(func, 0);
LLVMSetValueName(mParam, “m”);

LLVMValueRef xParam = LLVMGetParam(func, 1);
LLVMSetValueName(xParam, “x”);

LLVMValueRef bParam = LLVMGetParam(func, 2);
LLVMSetValueName(bParam, “b”);

LLVMBasicBlockRef entryBB;
entryBB = LLVMAppendBasicBlockInContext(llvm, func, “entry”);

LLVMBuilderRef builder;
builder = LLVMCreateBuilderInContext(llvm);

LLVMPositionBuilderAtEnd(builder, entryBB);

LLVMValueRef mx;
mx = LLVMBuildMul(builder, mParam, xParam, “mx”);

LLVMValueRef y;
y = LLVMBuildAdd(builder, mx, bParam, “y”);

LLVMValueRef retInst;
retInst = LLVMBuildRet(builder, y);
(void) retInst;

LLVMDisposeBuilder(builder);

LLVMLinkInJIT();
LLVMLinkInInterpreter();
LLVMInitializeNativeTarget();

LLVMDumpModule(module);

/* Now run it! */

LLVMExecutionEngineRef jit = NULL;
char *err;
// LLVMBool result = LLVMCreateExecutionEngineForModule(&jit, module, &err);
LLVMBool result = LLVMCreateJITCompilerForModule(&jit, module, 0, &err);
if (result) {
printf(“Fail: %s\n”, err);
return -1;
}
printf(“JIT is %p\n”, jit);

LLVMGenericValueRef argM = LLVMCreateGenericValueOfInt(int32, 10, 0);
LLVMGenericValueRef argX = LLVMCreateGenericValueOfInt(int32, 5, 0);
LLVMGenericValueRef argB = LLVMCreateGenericValueOfInt(int32, 1, 0);

LLVMGenericValueRef args[] = {argM, argX, argB};
LLVMGenericValueRef result2 = LLVMRunFunction(jit, func, 3, args);
unsigned long long answer = LLVMGenericValueToInt(result2, 0);
printf(“And the answer is %d\n”, (int)answer);

LLVMDisposeModule(module);
LLVMContextDispose(llvm);
return 0;
}

Hi David,

I’m not certain, but to me the “LLVMSetTarget(module, “i686-apple-darwin11”);” line looks suspicious. I’m not familiar with all the ins and outs of how target triples get handled, but it looks to me like that’s requesting 32-bit code.

I think that if you omit that line completely then the target will be inferred from the execution environment. My best guess as to what you would want if you do want to specify something explicit is “x86_64-apple-darwin11”.

Someone who knows more about how the target works may correct me.

-Andy

Hi Andy,

You are of course correct. My mail bounced because I forgot I wasn't actually subscribed (fixed that too!) and in the meantime I nutted it out on IRC.

So it turns out with that triple, it was generating a 32-bit stack manipulation ('subl #12' - which had scrolled away in XCode when I pasted into my original email), which cleared the top bits and caused trouble. Changing the triple to exactly as you mentioned means things start to work perfectly.

My x86/x64 is pretty terrible so it took me longer to figure exactly what was happening than maybe it should have :slight_smile:

Although I am somewhat (but not overly) surprised the execution engine accepted the module with such a target, and didn't complain that it didn't match 'InitializeNativeTarget()'. But for now I'm just glad it works.

I've since managed to get the JIT'd code to import and call helper functions my app provides - it's really *cool* stuff and am excited to further the project!

Cheers for the tips,
DavidM