feat(jit): move the LLVM backend from LLVM 15 to LLVM 21 - #311
feat(jit): move the LLVM backend from LLVM 15 to LLVM 21#311AregGevorgyan wants to merge 2 commits into
Conversation
inkwell was never the constraint. inkwell 0.9 — already the pinned version — carries feature flags for LLVM 11 through 22; the version was one feature string, `llvm15-0-prefer-dynamic`. (inkwell 0.10 supports 12-22, so bumping the crate would drop a version rather than add one.) CI installs LLVM 21 from apt.llvm.org: Ubuntu 24.04's universe stops at llvm-19-dev. A new `.github/actions/setup-llvm` composite action adds the repository and exports LLVM_SYS_211_PREFIX; it is an action rather than nine copies because nine jobs across five workflows build `jit`. The codename is read from /etc/os-release so it survives the runner image moving off noble. macOS pins `brew install llvm@21` — Homebrew's unversioned `llvm` is already 22.1.8. RHEL 10 ships LLVM 21 as `llvm-devel`, so a distro package now gives a contributor the right version with no extra repository. The NVPTX libdevice cleanup is rewritten onto the new pass manager. LLVM 17 removed the legacy `PassManager`, so `--features cuda` did not merely warn on LLVM 21 — it failed to compile with four `no method named add_*_pass` errors. `run_libdevice_cleanup_passes` now calls `Module::run_passes` with the same four passes in the same order, run twice as before. VERIFIED ON HARDWARE (2x RTX 3090, LLVM 21.1.8), by a separate agent: `scripts/verify_cuda_on_gpu.sh` 9 passed / 0 failed / 0 skipped; 19 GPU tests with no silent CPU fallback; memcheck, racecheck, initcheck and synccheck all ERROR SUMMARY: 0. PTX was diffed against the PR #310 base built at LLVM 15 over a 14-kernel battery (poly, sin, cos, tan, exp, log, sqrt, abs, three `pow` forms, all_funcs, deep_nest, wide): the set of libdevice functions left out-of-line is identical in all 14, zero `__nv_*` and zero `tanh` survivors in either build, 14/14 assemble under `ptxas -arch=sm_86`, and 896/896 device results are bit-for-bit identical. The PTX does differ, but only in backend codegen — typeless `.b64` registers, `.ptr .align 1` param annotations, param loads hoisted above the bounds check, 2-12% FEWER instructions — and `poly`, which touches no libdevice at all, shows the same class of diff, which is what shows it is LLVM version noise rather than this pipeline change. NEGATIVE CONTROL: with the cleanup disabled, all 14 cases fail ptxas on LLVM 21 with exactly the documented signature (`Parsing error near '.nvvm'` on `llvm.nvvm.tanh.approx.f32`, inside `__nv_fast_tanhf`), and PTX goes 822 KB -> 975 bytes when it runs. The hazard is fully alive on LLVM 21; this pipeline is what kills it. The `+jit` and `+full` release wheels move from ubuntu-22.04 to ubuntu-24.04, raising those two wheels' glibc floor from 2.35 to 2.39. They are linux_x86_64-tagged and attached to GitHub Releases only, never PyPI; the default PyPI wheel is untouched and stays on 22.04. The `attach-optional-linux-wheels` artifact glob is updated to match, which would otherwise have silently stopped matching anything. `AGENTS.md`'s headline build line no longer carries `jit`: it needed LLVM dev headers the line never mentioned, so a fresh contributor's first command failed on an LLVM error, and `cranelift` already provides a JIT with no system dependencies. This is issue #30a of the 2026-08-19 autoresearch run. Also fixed from that review: two job-level `LLVM_SYS_211_PREFIX: /usr` overrides in release-build.yml, which would have beaten the prefix `setup-llvm` writes to $GITHUB_ENV and pointed at a path apt.llvm.org does not populate (its debs put llvm-config solely at /usr/lib/llvm-21/bin). Those jobs are release-gated, so it would have passed every PR and failed at the tag. And `budget::memory::tests::gmp_accounting_tracks_a_big_rational` — which reads a process-global counter while the rest of the suite allocates GMP on other threads — now samples until one pair is uncontended instead of trusting a single sample; it failed once in four `--workspace` runs before. Verified on LLVM 21.1.8 (RHEL 10 llvm-devel): cargo test --workspace --features "parallel egraph cranelift jit groebner" -> 2411 passed, 0 failed (incl. 29 doctests), exit 0; cargo clippy --all-targets --features jit -- -D warnings clean; cargo clippy --features "cuda jit groebner" -- -D warnings clean; cargo fmt --check clean; check_error_codes OK; all 11 workflows + the new action parse as YAML; compile_expr JIT output matches libm to the last bit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rade # Conflicts: # .github/workflows/ci-cross.yml
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
Moves the LLVM JIT backend from LLVM 15 to LLVM 21. Stacked on #310 — review with
git diff integrate/autoresearch-2026-08-19...feat/llvm21-upgradeto see only the LLVM work.inkwell was never the constraint
inkwell 0.9 — already the pinned version — carries feature flags for LLVM 11 through 22.
The version was one feature string,
llvm15-0-prefer-dynamic. Bumping to inkwell 0.10 wouldlose a version (it supports 12–22), not gain one.
So the non-CUDA half of this needed zero source changes: it builds and runs on LLVM 18 and
21 as-is. The only thing that genuinely broke is CUDA.
What actually breaks: the CUDA pass pipeline
LLVM 17 removed the legacy
PassManager, so--features cudadid not merely warn — it failedto compile with four
no method named add_*_passerrors.run_libdevice_cleanup_passesisrewritten onto
Module::run_passeswith the same four passes in the same order.Verified on hardware (2× RTX 3090, LLVM 21.1.8)
scripts/verify_cuda_on_gpu.sh: 9 passed, 0 failed, 0 skipped; 19 GPU tests with nosilent CPU fallback;
compute-sanitizermemcheck / racecheck / initcheck / synccheck allERROR SUMMARY: 0.tan, exp, log, sqrt, abs, three
powforms, all_funcs, deep_nest, wide): identical set ofout-of-line libdevice functions in all 14, zero
__nv_*and zerotanhsurvivors in eitherbuild, 14/14 assemble under
ptxas -arch=sm_86, and 896/896 device results bit-for-bitidentical.
.b64registers,.ptr .align 1param annotations, param loads hoisted above the bounds check, 2–12 % fewer instructions.
poly, which touches no libdevice at all, shows the same class of diff, which is whatestablishes it as LLVM version noise rather than a pipeline regression.
ptxason LLVM 21 withexactly the documented signature (
Parsing error near '.nvvm'onllvm.nvvm.tanh.approx.f32, inside__nv_fast_tanhf), and PTX goes 822 KB → 975 bytes whenit runs. The hazard is fully alive on LLVM 21, and this pipeline is what removes it.
Note nothing in CI builds
cuda(cuda_nightly.ymlisworkflow_dispatchwith no registeredrunner), so that verification is a manual GPU-box step and this PR does not change that.
CI
Ubuntu 24.04's universe stops at
llvm-19-dev, so LLVM 21 comes fromapt.llvm.orgvia a new.github/actions/setup-llvmcomposite action — nine jobs across five workflows buildjit,which is why it is an action and not nine copies. It reads the codename from
/etc/os-releaseso it survives the runner image moving off noble, and it fails loudly at
apt-get updateifthe pinned suite is ever retired. macOS pins
brew install llvm@21; Homebrew's unversionedllvmis already 22.1.8.Two job-level
LLVM_SYS_211_PREFIX: /usroverrides were removed fromrelease-build.yml:workflow-file
env:beats$GITHUB_ENV, and/usris wrong for an apt.llvm.org install (itsdebs put
llvm-configsolely at/usr/lib/llvm-21/bin; nothing lands directly in/usr/bin).Those jobs are release-gated, so this would have passed every PR and failed at the tag.
User-facing consequence
The
+jitand+fullrelease wheels moveubuntu-22.04→ubuntu-24.04, raising those twowheels' glibc floor from 2.35 to 2.39. They are
linux_x86_64-tagged and attached to GitHubReleases only, never PyPI. The default PyPI wheel is untouched and stays on 22.04. The
attach-optional-linux-wheelsartifact glob is updated to match, which would otherwise havesilently stopped matching anything.
cargo semver-checksreports no semver update required — there are no public API changes.Also here
AGENTS.md's headline build line no longer carriesjit: it needed LLVM dev headers the linenever mentioned, so a fresh contributor's first command failed on an LLVM error, and
craneliftalready provides a JIT with no system dependencies. This is issue #30a of the2026-08-19 autoresearch run.
budget::memory::tests::gmp_accounting_tracks_a_big_rational(from fix: the 2026-08-19 autoresearch run — two Critical soundness bugs, a false mathematical claim, and an uncatchable process abort #310) failing 1-in-4 under
--workspace: it reads a process-global counter while ~2360other tests allocate GMP concurrently. It now samples until one pair is uncontended, with both
assertions still strict.
Verification
cargo test --workspace --features "parallel egraph cranelift jit groebner"--features cuda jit groebnercargo +1.98.0 clippy— default / cranelift / jit / cuda+jit,-D warningscargo doc --workspacewithRUSTDOCFLAGS=-D warningscargo fmt --checkcargo semver-checksvsorigin/mainscripts/check_error_codes.pycompile_exprJIT outputVerified against LLVM 21.1.8 on two independent machines (RHEL 10
llvm-develhere, extractedapt.llvm.org debs on the GPU box). What neither of us could verify is the packaging path
itself —
setup-llvmhas never run on a real runner, andbrew install llvm@21is assertedfrom the formula API rather than observed. This PR's own CI run is the first real test of both,
and both fail loudly rather than silently.
🤖 Generated with Claude Code