More function signatures for LLVMRunFunction?

Hello,

I am new to LLVM, and came across a snag when working through tutorials.

With the MC JIT execution engine, LLVMRunFunction only works on “main()”-like functions – other functions fail with the message “Full-featured argument passing not supported yet!”. The workaround recommended by the tutorials is to create a main()-compatible wrapper function and to send in parameters via pointer.

Are there plans to support arbitrary function calls without a wrapper?

For what it’s worth, I’ve been tinkering with a patch to the execution engine that will work with a wider array of functions, at least when the native host is X86. My idea is to support any function whose parameters all fit into registers by calling the function with a false (but ABI-compatible) function signature. E.g. on 64-bit X86 the parameter signature:

(double, double, double, double,
int64_t, int64_t, int64_t, int64_t)

…is ABI-compatible with any other function whose parameter list consists of up to 4 doubles and 4 integers / pointers. (Assuming 64-bit Windows or System V calling convention.) The nice part about this approach is that it doesn’t require any assembly, just some C-style casts of function pointers, and covers a large class of function calls, including all the main()-like functions covered by the current implementation. But it won’t cover function calls that require the stack for parameter passing (long doubles, large structs, varargs, etc).

I am writing to this list ahead of formally submitting a patch because I have a few questions…

  • Is there interest in this functionality? I didn’t see many references to LLVMRunFunction on the mailing list.

  • Relatedly, is someone else already working on addressing the limitations of MCJIT’s runFunction?

  • Where should I put test code for this kind of change? I assume llvm/trunk/unittests, anywhere else?

  • Right now the extended functionality just covers X86_64 with Windows and SysV calling conventions – falling back to current behavior in all other cases. Do I need to cover other architectures / calling conventions with the patch, or is a little bit of platform-specific functionality OK?

I have some code that “works on my machine” but I’d like some assurance that I’m not duplicating existing efforts – and also get a feel for what else I need to do before requesting a code review from the LLVM wizards. So any guidance on the above questions would be appreciated.

Thank you!

Evan

Hi Evan,

… is someone else already working on addressing the limitations of MCJIT’s runFunction?

As far as I know nobody is actively working on MCJIT any more. I’ve been working on the next generation of LLVM JIT APIs (ORC - see include/llvm/ExecutionEngine/Orc) for a while now, but they don’t have functionality for running arbitrary functions yet.

Are there plans to support arbitrary function calls without a wrapper?

Not yet. At some point I had hoped to write some utility code to help build the main()-compatible wrappers, but I haven’t had time to work on it yet.

Is there interest in this functionality?

Definitely. I’d be happy to see something like this in ORC, and I think there would be other people who would appreciate it too.

Right now the extended functionality just covers X86_64 with Windows and SysV calling conventions – falling back to current behavior in all other cases. Do I need to cover other architectures / calling conventions with the patch, or is a little bit of platform-specific functionality OK?

I’d like to see a generic implementation that can handle all architectures first, maybe with an specialized version for specific ABIs as an optimization.

  • Lang.

Hi Lang,

Thanks for the reply. Responses below.

Hi Evan,

Thanks for the pointer to ORC – it looks like the runFunction there is a copy-paste from MCJIT (minus the finalize() stuff).

The runFunction method in OrcMCJITReplacement is just a copy-paste, but OrcMCJITReplacement only exists for direct backwards-compatibility with MCJIT. In general ORC doesn’t have any equivalent of runFunction. Instead, it just has symbol lookup (via findSymbol) and leaves it to the user to cast the resulting TargetAddress to an appropriate function pointer and marshal arguments/returns.

The basic approach I am using is to add a function to TargetMachine called runFunctionNatively:

virtual GenericValue runFunctionNatively(Function *F, void *FPtr, ArrayRef ArgValues);

This houses most of the duplicated code currently in runFunction, and can then be called from either MCJIT or

OrcMCJITReplacement. I then have an override for X86TargetMachine that uses the register trick.

Very cool that you got this working. It’s a good fit for MCJIT as it stands (though I’d want to keep it in ExecutionEngine, rather than move it to TargetMachine). For Orc it might make sense to put it in ORCABISupport and drop the Function* argument (in favor of some sort of calling-convention description), since we want to make sure users can free Modules after compiling the code.

I agree a generic implementation would be ideal, but I don’t know how to do a generic dynamic dispatch in C. Lacking that generic implementation, would you be interested in seeing what I have so far?

I can think of one (rather heavyweight) answer: We depend on libClang. This solution would preclude including the feature in LLVMCore (we’d need to create some sort of LLVM JIT-extras project) and kill performance for the initial call, but it would be generic and work on remote-JITs.

Out of interest: How dynamic are your function signatures? If you know them ahead of time (even if they’re very complex) you can handle this by just casting to an appropriate function pointer type and calling. If the function signatures are dynamic (as in a REPL) then the usual answer is to use whatever fronted you used for the rest of the program to generate the main-like expression function, then call that. I can imagine that there might be use cases where neither of these are appropriate though.

  • Lang.

Hi Lang,