Background on MCJIT and ORC
Feel free to skip this message if you’re familiar with the APIs.
MCJIT was introduced in 2011 as LLVM’s second generation JIT API. It aimed to maximize re-use of the static compiler pipeline by replacing the bespoke backend of LLVM’s original JIT APIs with a novel “JIT linker” system called RuntimeDyld. MCJIT could transform IR into relocatable object files in memory using the existing pipeline, and then use RuntimeDyld to link these in-memory object files to produce runnable code. This re-use of the existing pipeline simplified the JIT implementation and eliminated some classes maintenance and bugs (e.g. JIT’d instruction encoding errors) from LLVM. MCJIT’s success led to it being ported to many targets, and adopted widely in both internal and external projects, including LLDB (for expression evaluation), OpenCL, Julia, Cling, Numpy, Halide, Clasp, and many others.
ORC was introduced in 2015 as LLVM’s third generation of JIT API. It aimed to provide a replacement for MCJIT that could also support lazy compilation, concurrent compilation, and the full suite of object format features (including things like thread local storage that require runtime support). In the years since its introduction ORC has developed its own JIT linker (JITLink) and runtime library to support the features mentioned above, though it still allows use of RuntimeDyld as a fall-back to support older targets.
While ORC is not a drop-in replacement for MCJIT (it doesn’t implement the ExecutionEngine
interface), the LLJIT
class that ORC provides has a similar API and many clients have migrated from MCJIT
to LLJIT
without undue difficulty.
Project status:
MCJIT and RuntimeDyld receive occasional bug fixes, but have not been actively developed since 2016. While they have been successfully used in production environments they also contain a number of known bugs and deficiencies that would be difficult to fix without destabilizing changes.
ORC and JITLink are actively developed and have been successfully used in production (e.g. in Xcode Previews, Julia, Cling, Clasp, and others).
Target support:
ORC/JITLink’s target support largely overlaps MCJIT/RuntimeDyld’s, but each has some unique targets:
Supported in both MCJIT and ORC:
Format |
Architectures |
COFF |
x86-64 |
ELF |
aarch32, aarch64, i386, LoongArch, PPC64, RISCV, x86-64 |
MachO |
aarch64, x86-64 |
Supported in ORC/JITLink only:
Format |
Architectures |
ELF |
LoongArch, RISCV |
Supported in MCJIT/RuntimeDyld only:
Format |
Architectures |
COFF |
aarch32, aarch64, i386 |
ELF |
MIPS, PPC32, SPARC |
MachO |
aarch32, i386 |
Adding support for the missing targets to JITLink should be relatively straightforward: new target support is a GSoC-sized or smaller project, depending on the target and the relocation / code model(s) to be supported.
Feature support:
MCJIT currently supports a couple of object format features that ORC does not:
- OProfile support. (Perf and Intel OTune profiling are already supported in ORC)
- GNU ifuncs.
Neither of these should be prohibitively difficult to implement.
ORC provides a number of features that MCJIT either does not provide or does not implement fully, including:
- Native thread local storage*.
- Static initializers and deinitializers in out-of-process mode.
- Exception handling in out-of-process mode.
- Language metadata (e.g. for Swift and Objective-C).
- System loader API emulation (e.g. dlopen, dlsym, etc.)
* Native TLS is supported on MachO, and the General Dynamic model is supported on ELF.