You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Branch: exp/llvm-inprocess · Write-up: docs/llvm-inprocess-experiment.md on that branch.
Perry ships LLVM IR as text to a user-supplied clang -c — a runtime, unpinned LLVM dependency (1.4 GB transient IR on the Claude Code bundle, #4880 whole-module -Os demotion, Apple clang 21 vs LLVM 22 parse skew, errors as line numbers in gigabyte files). This experiment builds the module pipeline in-process via the LLVM C API, exactly like the exp/stackmap-viability pattern: isolated branch, opt-in, default path untouched.
Phase 0 answer (feasibility: CONFIRMED)
Bindings: inkwell 0.9.0 + llvm-sys 221 for the few unwrapped calls (LLVMSetGC, LLVMParseCommandLineOptions, LLVMGetVersion). Nothing Perry's IR needs was inexpressible: NaN-box constants keep exact payload bits through f64::from_bits → const_float (verified end-to-end by running the compiled artifact), inline asm with exact constraint strings, module-level asm, appending @llvm.used, and gc "statepoint-example" all construct, verify, and emit.
LLVM version: 22 (llvm22-1 / Homebrew llvm 22.1.4). Picking 22 removes the version-skew problem — no second toolchain parses our output anymore.
Discovery: LLVM_SYS_221_PREFIX (or llvm-config on PATH) at build time.
Contributor cost: none by default. The llvm-inprocess cargo feature is off; the default build links no LLVM and is byte-for-byte unchanged. Enabling it needs LLVM 22 dev libs and adds ~171 MB to a static-linked perry-dev binary.
Parity evidence
On the same IR + flags, the in-process pipeline emits an object byte-identical to clang -c (Homebrew clang 22).
77-test gap-corpus A/B through an unmodified perry (shim as PERRY_LLVM_CLANG; both caches already key on it): 76/77 outputs byte-identical; the 1 diff is a pre-existing perry-ext-http SIGABRT whose stderr embeds the OS thread id (differs run-to-run on the same arm).
26-test slice through the integratedPERRY_LLVM_INPROCESS=1 backend (one binary, flag-only A/B, liveness asserted per compile): 25/26 identical, same pre-existing panic family for the remainder.
.ll → parse → print round-trip diff classes are all mechanical (alignment made explicit, attr-group renumbering, float formatting, headers) — the normalization catalogue for a future construction-parity harness.
crates/perry-codegen/src/inprocess.rs behind the llvm-inprocess feature: compile_ll_to_object gets an in-process implementation (memory-buffer parse → verify → default<O_n> → object-to-memory; zero disk transit). It interprets the same build_clang_compile_plan argv as the clang path, so backend decisions can't drift independently; unknown flags are a hard error. PERRY_LLVM_INPROCESS participates in build-cache and object-cache keys; a featureless build fails loudly under the flag; first use prints a liveness witness line.
The brief's instruction-level seam ("lowering ↔ IR construction") is not where this codebase's seam is: the emitter is ~340k lines of string-oriented code, and compile_ll_to_object is the boundary the code itself documents as "the seam". Recommendation in the write-up: land the transport seam opt-in, profile string-build cost through it at scale, and only then decide the construction rewrite.
-fno-math-errno has no C-API equivalent; measured byte-identical on macOS (Darwin defaults errno-free) — must be re-measured on Linux.
cl::opt state is process-global; Windows/COFF and cross-targets untested; llvm-sys/inkwell version pins are ours to maintain.
Next steps
Gap suite (full) under PERRY_LLVM_INPROCESS=1, macOS + Linux.
Claude Code bundle compile through the flag on a quiet box: fill the measurement table (wall/user, peak RSS, transient disk, binary size/sections, runtime + RSS vs pinned Node).
Branch:
exp/llvm-inprocess· Write-up:docs/llvm-inprocess-experiment.mdon that branch.Perry ships LLVM IR as text to a user-supplied
clang -c— a runtime, unpinned LLVM dependency (1.4 GB transient IR on the Claude Code bundle, #4880 whole-module-Osdemotion, Apple clang 21 vs LLVM 22 parse skew, errors as line numbers in gigabyte files). This experiment builds the module pipeline in-process via the LLVM C API, exactly like theexp/stackmap-viabilitypattern: isolated branch, opt-in, default path untouched.Phase 0 answer (feasibility: CONFIRMED)
inkwell 0.9.0+llvm-sys 221for the few unwrapped calls (LLVMSetGC,LLVMParseCommandLineOptions,LLVMGetVersion). Nothing Perry's IR needs was inexpressible: NaN-box constants keep exact payload bits throughf64::from_bits → const_float(verified end-to-end by running the compiled artifact), inline asm with exact constraint strings, module-level asm, appending@llvm.used, andgc "statepoint-example"all construct, verify, and emit.llvm22-1/ Homebrewllvm22.1.4). Picking 22 removes the version-skew problem — no second toolchain parses our output anymore.LLVM_SYS_221_PREFIX(orllvm-configon PATH) at build time.llvm-inprocesscargo feature is off; the default build links no LLVM and is byte-for-byte unchanged. Enabling it needs LLVM 22 dev libs and adds ~171 MB to a static-linked perry-dev binary.Parity evidence
clang -c(Homebrew clang 22).PERRY_LLVM_CLANG; both caches already key on it): 76/77 outputs byte-identical; the 1 diff is a pre-existingperry-ext-httpSIGABRT whose stderr embeds the OS thread id (differs run-to-run on the same arm).PERRY_LLVM_INPROCESS=1backend (one binary, flag-only A/B, liveness asserted per compile): 25/26 identical, same pre-existing panic family for the remainder..ll → parse → printround-trip diff classes are all mechanical (alignment made explicit, attr-group renumbering, float formatting, headers) — the normalization catalogue for a future construction-parity harness.What's on the branch
experiments/llvm-inprocess-spike/— standalone spike: builder-APIdemo, clang-argv shim, corpus A/B script.crates/perry-codegen/src/inprocess.rsbehind thellvm-inprocessfeature:compile_ll_to_objectgets an in-process implementation (memory-buffer parse → verify →default<O_n>→ object-to-memory; zero disk transit). It interprets the samebuild_clang_compile_planargv as the clang path, so backend decisions can't drift independently; unknown flags are a hard error.PERRY_LLVM_INPROCESSparticipates in build-cache and object-cache keys; a featureless build fails loudly under the flag; first use prints a liveness witness line.Honest negatives / open work
-O3is. The claimed wins live at bundle scale (parse + disk transit), in version control, and in what the seam unlocks (per-function opt vs Compile-time blowup on wide object literals (2100 keys ≈ 2 min, 3000 keys > 7 min) #4880, DWARF viaDIBuilder, in-process RS4GC for gc: RewriteStatepointsForGC over managed-pointer SSA — the measured-only road to native-root file-size parity #7174). Those need a quiet box, ≥25 GB disk, and the gap suite green under the flag before any number is quoted.compile_ll_to_objectis the boundary the code itself documents as "the seam". Recommendation in the write-up: land the transport seam opt-in, profile string-build cost through it at scale, and only then decide the construction rewrite.-fno-math-errnohas no C-API equivalent; measured byte-identical on macOS (Darwin defaults errno-free) — must be re-measured on Linux.cl::optstate is process-global; Windows/COFF and cross-targets untested; llvm-sys/inkwell version pins are ours to maintain.Next steps
PERRY_LLVM_INPROCESS=1, macOS + Linux.RewriteStatepointsForGCin-process — that branch owns the statepoint work.DIBuilderscope from the measured profile.