Multiple modules JITting

Hi,

Can you please clarify?

We're looking for a way to use LLVM to JIT many modules, assuming that the full list of modules and their content are not available at a time when some of jitted pieces are already in use.

Is it feasible to destruct ExecutionEngine but keep jitted code alive?
Are jitted binary codes position independent? (or is there a way to relocate) If the solution requires custom implementation of JITMemoryManager: is there a way to get exact (or sufficient) memory size for binary code before the actual code generation?

Regards,
Mikhail

From: llvmdev-bounces@cs.uiuc.edu [mailto:llvmdev-bounces@cs.uiuc.edu]
On Behalf Of Mikhail Lyapunov
Subject: [LLVMdev] Multiple modules JITting

We're looking for a way to use LLVM to JIT many modules, assuming that
the full list of modules and their content are not available at a time
when some of jitted pieces are already in use.

We do this in our project, using MCJIT.

Is it feasible to destruct ExecutionEngine but keep jitted code alive?

Yes; we use our own memory manager, extending SectionMemoryManager (not JITMemoryManager). An instance of the memory manager is created and destroyed with each ExecutionEngine, so we use it as a wrapper for our actual allocation mechanism; this allows the code/data lifetime to be managed independently of the ExecutionEngine and Module.

Are jitted binary codes position independent?

We have not found a way to do this, even when creating a TargetMachine with PIC_, but perhaps we're missing some trick.

is there a way to relocate

Haven't figured that out either.

If the solution requires custom implementation of JITMemoryManager: is
there a way to get exact (or sufficient) memory size for binary code
before the actual code generation?

The MCJIT makes calls to various allocate*() methods of SectionMemoryManager with exact sizes specified before writing out the code and data sections. This is a distinct advantage over the legacy JIT.

- Chuck

Are jitted binary codes position independent?

We have not found a way to do this, even when creating a TargetMachine with PIC_, but perhaps we're missing some trick.

I recently added support for the PIC relocation model for x86_64-based ELF binaries. We also support PIC for MachO. Some other architectures for ELF do not yet work with PIC relocations.

However, I feel it's worth qualifying this claim by saying that PIC in this case means that the loader (in this case MCJIT's RuntimeDyld component) can put the code in arbitrary locations. This is not the same as saying that the MCJIT client can move the code after it has been generated. However, see below on that topic.

is there a way to relocate

Haven't figured that out either.

This is possible. There is a state with MCJIT where code has been generated but not yet prepared for final execution. While the code is in this state, you may call ExecutionEngine::MapSectonAddress to give MCJIT/RuntimeDyld the address to which you would like to copy the code and then when relocations are applied to the code they will be applied as if the code were at that location. Note, however, that you should not actually copy the code to the new location until after it has been finalized.

I have made some recent changes to the MCJIT and RTDyldMemoryManager interfaces to make this process easier. In particular, there is now a method exposed to trigger code generation without also triggering finalization and now the memory manager receives notification when a module has been generated and loaded into memory but before the object code is finalized. This is intended as a place to allow custom memory managers to remap section addresses for the module.

You can find an example of how to do this in the lli tool, which has an option to inject generated code into an external process. The lli RemoteMemoryManager will be of particular interest.

If the solution requires custom implementation of JITMemoryManager: is
there a way to get exact (or sufficient) memory size for binary code
before the actual code generation?

The MCJIT makes calls to various allocate*() methods of SectionMemoryManager
with exact sizes specified before writing out the code and data sections.
This is a distinct advantage over the legacy JIT.

To clarify, MCJIT actually creates a single ObjectBufferStream into which the code is generated and the buffer grows as needed during the code generation process. The allocate*() methods are called to allocate space for individual sections as the generated code is copied from the generated object image into memory allocated by the memory manager.

-Andy