Having JIT resolve extern "C" functions declared in executible

Hi,

I am having some difficulties getting the LLVM JIT to resolve extern "C" functions which I have defined in source file and invoking them via EE::runFunction() after generating a Function prototype for it. Is this possible or do I need to generate a .so for my functions are link against it?

Thanks in advanced,

Carter.

Sorry for the double post but apparently I mistakenly tagged this message onto the bottom of another thread.

If the JIT needs a pointer to a function, and that function has no body, it'll ask the ModuleProvider to materialize the function. If the MP returns false, it'll just ask the dynamic linker for the function with that name. If no such function is linked into your program, then the JIT will just give up. So you have three options:

(1) You can circumvent this entire process by giving the JIT an explicit pointer to the function using JIT::addGlobalMapping(). Obviously this requires the function to be compiled and linked into your program.
(2) You can compile and link the function into your program in such a way that the dynamic linker will find it.
(3) You can give the JIT a custom module provider which somehow materializes IR for the given function.

The highest-performance option is probably #1, but #2 can be more convenient. If the code is totally static, I see no reason to do #3 unless you really need the IR (e.g. if you want to inline the function).

Is that what you were asking?

John.

Hi John,

Thanks. I was considering pursuing option 2) initially but also started thinking about option 1) and I suspect with that hint I will be able to figure it out. Thanks.

Regards,

Carter.

John McCall wrote:

I am having some difficulties getting the LLVM JIT to resolve extern
"C" functions which I have defined in source file and invoking them
via EE::runFunction() after generating a Function prototype for it.
Is this possible or do I need to generate a .so for my functions are
link against it?

If the JIT needs a pointer to a function, and that function has no
body, it'll ask the ModuleProvider to materialize the function. If
the MP returns false, it'll just ask the dynamic linker for the
function with that name. If no such function is linked into your
program, then the JIT will just give up. So you have three options:

(1) You can circumvent this entire process by giving the JIT an
explicit pointer to the function using JIT::addGlobalMapping().
Obviously this requires the function to be compiled and linked into
your program.
(2) You can compile and link the function into your program in such a
way that the dynamic linker will find it.
(3) You can give the JIT a custom module provider which somehow
materializes IR for the given function.

Maybe I'm missing something, but this seems to be overkill. As John
mentioned, if the C function to be called is linked into your program
then the JIT should normally resolve it just fine. The Kaleidoscope
tutorial [1] illustrates how to do this.

[1] http://llvm.org/docs/tutorial/

There is one gotcha here, though: If the symbol is linked directly into
your main executable, as is in the Kaleidoscope example, then you *must*
use -rdynamic (or whatever flag your compiler provides to enable
backlinking) when linking the executable, in order to make this work.
This isn't necessary if the symbol is in a shared library linked into
your program.

Otherwise you just put the function into a shared library and load that
library through llvm::sys::DynamicLibrary::LoadLibraryPermanently() [2].
Then the JIT resolves it without the shared library being linked at
compile/link time.

[2] http://llvm.org/doxygen/classllvm_1_1sys_1_1DynamicLibrary.html

Only in unusual circumstances (i.e., you can't/don't want want to put
the stuff into a separate shared library *and* your C compiler doesn't
support backlinking a la -rdynamic), it's necessary to explicitly tell
the dynamic loader about your C function, by calling
sys::DynamicLibrary::AddSymbol() with a pointer to the function.

This is all I ever needed to interface to C functions using LLVM. It's
really easy. Of course you still need a prototype of the external
function (function definition without body) in your IR, but that's it.

HTH,
Albert

Hi Albert,

I'm having a similar problem and I found I can't declare the function
and use it, most likely because my syntax is wrong.

I have the function extern'd and am creating only the signature on the
IR code (attached). The idea is that it creates something like this:

My variable is DoubleTy and the expected parameter is also, but I'm
getting this error:

Assertion `(0 == FTy->getNumParams() || FTy->getParamType(0) ==
Actual->getType()) && "Calling a function with a bad signature!"'

cheers,
--renato

Reclaim your digital rights, eliminate DRM, learn more at
http://www.defectivebydesign.org/what_is_drm

extern.cpp (1.58 KB)

I think you might have to provide an empty list if your function doesn't take parameters. Maybe using an irbuilder would help?

-bw

It does take one parameter. Here's the important bits:

// My Function
extern "C"
void print(double X) {
    printf("%f\n", X);
}

// Args type
std::vector<const Type*> args(1, Type::getDoubleTy(getGlobalContext()));
// return void, 1 arg double, no varargs
FT = FunctionType::get(Type::getVoidTy(getGlobalContext()), args, false);
// creating stub for print function
Function* printFunction = Function::Create(FT,
Function::ExternalLinkage, "print", module);

By doing this (and the variable declaration below), my IR is:

; ModuleID = 'example'

define void @main() {
entry:
  %var = alloca double ; <double*> [#uses=1]
  store double 1.000000e+01, double* %var
}

declare void @print(double)
; ================== END

The function call is, then:
// Number 10
Value* value = ConstantFP::get(getGlobalContext(), APFloat(10.0));
// Variable var is double
Value* var = builder.CreateAlloca(Type::getDoubleTy(getGlobalContext()),
0, "var");
// Assign it value 10
builder.CreateStore(value, var);

// Create a call to the function, passing a double var
builder.CreateCall(printFunction, var, "print");

If var is DoubleTy and the arg is expecting DoubleTy, why is the assert failing?

I've tried creating a vector with the var inside and create the
function as below, but got the same error:
builder.CreateCall(printFunction, vec.begin(), vec.end(), "print");

cheers,
--renato

Reclaim your digital rights, eliminate DRM, learn more at
http://www.defectivebydesign.org/what_is_drm

Renato Golin <rengolin@systemcall.org> writes: