Question

I am implementing a front-end for a JIT compiler using LLVM. I started by following the Kaleidoscope example in the LLVM tutorial. I know how to generate and JIT LLVM IR using the LLVM C++ API. I also know how to call the JITed function, using the "getPointerToFunction" method of llvm::ExecutionEngine.

getPointerToFunction returns a void* which I must then cast to the correct function type. For example, in my compiler I have unit test that looks like the following:

void* compiled_func = compiler.get_function("f");   
auto f = reinterpret_cast<int32_t(*)(int32_t)>(compiled_func);
int32_t result = f(10);

The problem is that I have to know the function signature beforehand. In the example above, I have a function "f" which takes takes a 32-bit integer and returns a 32-bit integer. Since I create "f" myself, I know what the function type is, so I'm able to call the JIT'ed function. However, in general, I do not know what the function signature is (or what the struct types are) that are entered by the user. The user can create arbitrary functions, with arbitrary arguments and return types, so I don't know what function pointer type to cast the void* from LLVM's getPointerToFunction. My runtime needs to be able to call those functions (for a Read-Evaluate-Print loop, for example). How can I handle such arbitrary functions from my JIT runtime?

Thanks

Was it helpful?

Solution

There's not much information you get can from compiled_func - as you wrote, it's just a void*. But when you write "in general, I do not know what the function signature is", that's not accurate - you've just compiled that function, so you should have access to the LLVM Function object, which can be queried about its type. It's true that it's an LLVM IR type and not a C++ type, but you can often know which translates to which.

For example, if we borrow code from the tutorial's section on JITting Kaleidoscope:

if (Function *LF = F->Codegen()) {
  LF->dump();  // Dump the function for exposition purposes.

  // JIT the function, returning a function pointer.
  void *FPtr = TheExecutionEngine->getPointerToFunction(LF);

  // Cast it to the right type (takes no arguments, returns a double) so we
  // can call it as a native function.
  double (*FP)() = (double (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %f\n", FP());
}

Then yes, FPtr was "assumed" to be of type double (), but there's also LF of type Function* here, so you could have done something like:

Type* RetTy = LF->getReturnType();
if (RetTy->isDoubleTy()) {
  double (*FP)() = (double (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %f\n", FP());
} else if (RetTy->isIntegerTy(32)) {
  int (*FP)() = (int (*)())(intptr_t)FPtr;
  fprintf(stderr, "Evaluated to %d\n", FP());
} else ...

And in much the same way, you can query a function about its parameter types.

A bit cumbersome? You can use your execution engine to invoke the function, via its handy runFunction method, which receives a vector of GenericValues and returns a GenericValue. You should still query the Function type to find what the underlying type under each GenericValue should be.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top