Hello list,
I am trying to interface some JIT’d code I generate (via the llvm-c API) with a module I’ve compiled alongside the jit, which contains certain runtime functions.
I generate runtime.bc with the same clang invocation as the rest of the application, except using -O0 and -emit-llvm. This is loaded at runtime and my I insert new functions into that module as I need them. For runtime functions that just use scalars, this works well.
I’m having problems with structs, and my clang-generated module seems to define the struct as I’d expect, but functions that either take this struct as an arg, or return it seem to fall apart.
I have:
typedef struct {
uint32_t value;
uint32_t carryOut;
uint32_t overflow;
} CarryOp_Result;
This was once upon a time defined at the top of the module as { i32, i32, i32 } as expected, but I’ve since changed things and now thats not emitted in my runtime.bc anymore. I guess it’s not used/needed.
I also have a function in this runtime.bc module:
CarryOp_Result addWithCarry( a, b, carryIn);
Which is defined to return { i64, i32 }. I suspect this is because clang knows the ABI of my host PC and bakes the calling convention into the IR (x86_64-apple-darwin11.4.2). I know enough not to expect to be able to use the same .bc on different arches for reasons such as this, at least
Because two struct fields are merged, it’s not trivial to get the one I’m interested in. Even if I do emit the required masks and shifts, what if it all changes tomorrow when I want this to work after recompiling everything for say, 32b Linux, or ARM with a different calling convention?
So I had the bright idea to add some accessors to the runtime, which I could feed with the LLVMValue I have, regardless of the actual representation. This way for a given compile of the runtime everything would be self-consistent and “just work”, neatly sidestepping struct ABI packing issues …
uint32_t carryOpValue(CarryOpResult r) { return r.value; }
… or so I thought, anyway. This actually generates:
define i32 @CarryOpValue(i64 %v.coerce0, i32 %v.coerce1) #0 { … }
which takes two arguments, where I expected a single { i64, i32 } argument.
So now, even for my jit to emit
LLVMValueRef sum = CarryOpValue(addWithCarry(a, b, c));
The call site needs to know to unpack the single LLVMValue (representing the struct) into two arguments, in order to pass them to the accessor. So still I need to poke around in the innards of the struct, still knowing how the compiler felt like packing the struct on a given platform.
Is there a way I can have Clang simply use {i32, i32, i32} for that struct in a way that GEP/ExtractValue with indices 0, 1, 2 reliably works as expected?
Alternatively, could I cast between {i64, i32} and {i32, i32, i32} in a reliable way, would that be safe?
Or any other solution?
If worse comes to worst, I can end up writing addWithCarry either by hand or with the builder API to do exactly what I want, but that’s annoying enough to be the reason I went down the “runtime-module full of helpers” path in the first place and have clang do the annoying work for me!
Thanks in advance,
DavidM