LLVM 22 (llvmlite 0.48+)

As of version 0.48 llvmlite supports LLVM 22. This release upgrades the LLVM toolchain from LLVM 20 (llvmlite 0.45–0.47). The CMake-based build system, static linking defaults, and packaging layout introduced in LLVM 20 (llvmlite 0.45+) are unchanged.

If you are upgrading from llvmlite 0.47 or earlier on LLVM 20, read LLVM 20 (llvmlite 0.45+) for background on the 0.45 migration, then review the notes below for LLVM 22.

Size optimization flags removed

LLVM 22 removes the Os and Oz size optimization levels from OptimizationLevel. Size optimization is now expressed through the per-function optsize and minsize attributes instead. See Optimizing for size for how to request size optimization in llvmlite.

Pointer Provenance

In LLVM 22, inttoptr and ptrtoint use exposes current uncertainty in LLVM’s pointer provenance design:

LLVM currently ignores the fact that ptrtoint has an (exposure) side-effect

(from This Year in LLVM 2025)

Reproducing pattern

A numba issue demonstrates the resulting undefined behavior. Pattern: pointer address computed via integer round-trip instead of getelementptr:

%base_i    = ptrtoint ptr %base to i64      ; provenance lost
%addr_i    = add i64 %base_i, %offset
%addr      = inttoptr i64 %addr_i to ptr     ; provenance nominally restored
%val       = load double, ptr %addr

The IR above is legal per the LangRef, the inttoptr result is defined to be based on all pointer values contributing to the integer. However, during loop unrolling, the SCEVExpander pass cannot recover a real base pointer from the integer expression and rematerializes the address on ptr null:

%addr = getelementptr i8, ptr null, i64 %offset
%val  = load double, ptr %addr

A load from a null-based pointer is poison/undefined behavior. This causes unexpected behavior and miscompilation in the Numba case.

This loop-unroll-driven null-GEP case is the observed failure mode, though this issue is not Numba specific. It is also possible that other similar passes may trigger equivalent provenance loss through inttoptr/ptrtoint. To avoid this issue, it is best to avoid using inttoptr and ptrtoint whenever possible; use GEP-based address computation instead.

Known material issues with LLVM 22

The following issues were known with LLVM 20 and may still apply on LLVM 22; please report regressions or fixes on the issue tracker.

  1. Intel SVML is currently unsupported. The patch from LLVM 15 did not apply cleanly and fixing it was non-trivial. It is hoped that this can be resolved in the future.

  2. There is an issue with calling global constructors/destructors from MCJIT on OSX platforms the result of which is that calling an execution engine’s run_static_constructors method will result in an assertion error or a segmentation fault depending on whether LLVM has assertions enabled. The cause of this is that the use of __mod_term_func has been deprecated, which means that LLVM now emits a __cxa_atexit call in the constructor to schedule the execution of destructors, see https://github.com/llvm/llvm-project/commit/22570bac694396514fff18dec926558951643fa6. MCJIT does not correctly support __cxa_atexit and so hits an assertion error or segmentation faults when the static constructors are called (the linker should handle it so that it can be called with a live JIT library, but this does not happen, and so the atexit handlers for the process run it instead, by which time the JIT library has been destroyed, the result of which is typically a segmentation fault due to __cxa_atexit accessing an invalid address). Multiple workarounds were tried by “faking” various missing parts, but the effect was a lot of complexity that seemingly pushes the problem to some other area. It needs fixing at the LLVM level.

  3. The toolchain version used to compile LLVM and llvmlite needs to be the same in both builds. When using the Anaconda/conda-forge distribution toolchains, the result of using inconsistent versions is typically a segmentation fault. It is suspected that this issue arises due to minor ABI differences but investigation has not gone further than figuring out that the version difference across the packages was the cause of the problem. An example of correctly using versions would be: if LLVM is compiled with GCC 11 tooling, then llvmlite should also be compiled with GCC 11 tooling.

  4. The .text sections in ORC JIT compiled ELF objects are now called .ltext (other sections have a similar l prefix)

  5. The ORC JIT compilation chain now defaults to letting the JIT process access the symbols present in the “main” process.