1
votes

I can call a C++ function from a JITed LLVM function: I use clang to compile the C++ function into LLVM IR, and then I peek at its LLVM type. For example, a std::array<uint64_t, 2>(*)() type in C++ becomes a function returning {i64, i64} in LLVM IR. Then, I can create an appropriate FunctionType, and call my desired function.

But in the opposite direction, if I have a LLVM function that returns [i64 x 3], how do I call this function from C++ code? I tried casting the returned function pointer to (std::array<uint64_t, 3>(*)()), and then calling it, but it's clear this is not working because the return values don't show up when I try to print them. Ideally, I'd like my C++ code to be able to call any function that returns [i64 x N], for arbitrary N >= 2.

/lib/ExecutionEngine/Orc/OrcMCJITReplacement.cpp has a mild attempt at handling function return types, but it bails on the cases I'm interested in. (it says, "Full-featured argument passing not supported yet!")

1
(My current workaround is to create and JIT a trampoline LLVM function that calls the desired LLVM function. The trampoline function mallocs memory and then stores the values there, then returns a pointer to this memory. This works since it's easy for LLVM IR functions to call other LLVM IR functions. But the overhead of this workaround is huge, about 30% of total program run time.) - Kevin Yin
(The second possible workaround I can think of is to change the function type of the original LLVM IR function, forcing it to match the ABI of the corresponding C++ function. This means making the LLVM IR function accept a pointer to memory as its first argument.) - Kevin Yin

1 Answers

1
votes

It's not possible with pure C++. According to Returning structs in registers - ARM ABI in GCC, ABI matters can't be handled without assembly. Empirically, I see that on Linux x64, the LLVM IR convention is that up to three return integers are returned in the eax, edx, ecx registers, and then 4-or-more are returned in memory. In C++, it's only possible to return a single object in registers; larger objects must go in a struct, which must then be returned in memory.