Inlining functions across modules when using MCJIT

I was wondering if there is a way to perform inlining of a function from one module to another using the same cost model as the SimpleInliner.

For more context, I am working on adding support for MCJIT to Terra (, a low-level language embedded in Lua and based on LLVM. When using the old JIT, we performed inlining by keeping everything in one module, and used a modified version of SimpleInliner that would perform inlining on a single strongly-connected component rather than the entire module. Moving to MCJIT, it is relatively easy to manage multiple modules (one for each SCC), but I don’t see an easy way to perform inlining across the modules. Since functions can be defined dynamically during evaluation of Lua, it is not possible for us to keep all functions that we want to consider for inlining in a single module. Some callee functions may have already been compiled and run before a caller is defined.

If there isn’t a way to do this at the moment, I’d be willing to working on modifying the inliner it if it is something that could be merged into LLVM and kept up to date.



Hi Zach, I built some similar functionality into Pyston, which you can find here:

The main thing I did was just copy the relevant bits from lib/Transforms/IPO/Inliner.cpp and hacked until it worked; I think a lot of the hackiness was unnecessary (I think it was since I didn’t originally structure it as a Pass), and probably the only fundamentally different things are:

  • You need to provide a way of mapping callsites to the IR that would get inlined, since they won’t point directly there any more.
  • You might inline in code that isn’t quite valid in the new module, such as calling functions defined in the module you are inlining from, so you have to go through and remap Function and GlobalVariable references.
  • I’m not sure if a CallGraph supports being multi-module, but if not then it might be difficult to get a report of what new callsites got inlined into the current function. This ends up being ok for us because we only inline in functions that have themselves already been fully inlined, so I didn’t investigate this much further.

Pyston generates only a specific pattern of IR so I don’t know if there are other issues, but the inliner seems to be working ok; I don’t think it would be too bad to refactor the existing inliner logic to be more consumable.