transitions from C code to LLVM JIT code and back

Started playing with LLVM this week, picking apart the examples to see how they work.

So I think I understand the basic process of instantiating a program and getting LLVM to generate the runnable code, and then running it.

What I have not seen an example of, and want to know if it's possible, is having LLVM generated code calling back out to plain C code during its run. What operations do you insert to encode the concept of calling non-LLVM generated code (putting the needed args on the stack, actually making the call, then cleaning up on return to the LLVM generated code).

Sorry if this is in the docs and I just didn't spot it yet.

Rob

What I have not seen an example of, and want to know if it's
possible, is having LLVM generated code calling back out to plain C
code during its run.

When I was first looking at LLVM, I googled a lot, and found somebody
saying to do it this way:

http://onesadcookie.com/trac/browser/AbandonedExperiments/Argh/CodeGen.g
(lines 43-51). I'm pretty sure now though that that's the wrong way
to do it, that you can just add an existing C function to LLVM's
function table and it'll get called automatically. There was somebody
talking about this on this list recently...

-Keith

Keith Bauer wrote:

What I have not seen an example of, and want to know if it's
possible, is having LLVM generated code calling back out to plain C
code during its run.
    
When I was first looking at LLVM, I googled a lot, and found somebody
saying to do it this way:

OneSadCookie
(lines 43-51). I'm pretty sure now though that that's the wrong way
to do it, that you can just add an existing C function to LLVM's
function table and it'll get called automatically. There was somebody
talking about this on this list recently...

My students have had some success with doing calls from LLVM-generated code into outside-compiled code when using the JIT in 2.0 (we haven't upgraded to 2.1 yet) and there were only two things we had to do:

1) make sure all the functions we wanted to call were actually linked into the JIT binary or brought in by loading a dynamic library. Because of the nature of our project we chose the former and just copied lli.cpp to our own project/tools and built a new JIT with the stuff we wanted linked in.

2) if you link the functions into the JIT, you have to make the JIT's symbols available for lookup; this may actually be automatically done in 2.0, but you can guarantee it by adding a call:
  sys::DynamicLibrary::LoadLibraryPermanently(0);
into main of your copy of the JIT

We didn't have to declare the C functions to LLVM or anything like that; llvm-ld assumes if it doesn't have a definition for a symbol, then it must be coming from somewhere else, unlike most system linkers.

Note that going the other way (calling LLVM-generated code from outside code) is quite a bit harder because the symbol you're calling can't be resolved to an address without making LLVM API calls. We haven't had to do this in our project.

Disclaimer: I'm pretty new to LLVM and there might be something horribly wrong about the way we did this, but it seems to work fine for us.

--David

Started playing with LLVM this week, picking apart the examples to
see how they work.

So I think I understand the basic process of instantiating a program
and getting LLVM to generate the runnable code, and then running it.

What I have not seen an example of, and want to know if it's
possible, is having LLVM generated code calling back out to plain C
code during its run.

Yep, it absolutely is.

What operations do you insert to encode the
concept of calling non-LLVM generated code (putting the needed args
on the stack, actually making the call, then cleaning up on return to
the LLVM generated code).

Assuming you're talking about simple C functions, there are two ways:

1) just make an llvm function declaration, use C calling convention, and call it like any other function. If you're using the JIT, it will call 'dlsym' to resolve the function to a function of the same name, and generated code will call it directly. If you're using llc, the native linker will match it up like any other function.

2) if you're using the JIT, you can explicitly add mappings to the JIT table, by calling the ExecutionEngine::addGlobalMapping(Func, addr) method. This lets you map an arbitrary llvm function (e.g. a declaration) to an arbitrary address. This lets you avoid tying the name of the llvm code into the name of functions in your address space and even lets you have the LLVM JIT generate code that calls other peoples' JITs if you care to :wink:

-Chris