Repository navigation
llvm-inprocess RS4GC runs before text/native constant-folding converges, changing code and GC maps #8065
Description
Failure
Fresh local audit of exact origin/main fe4cd09db0f1e260fc0ff63ec2e5c0079fceac3d (PR #8062 atop #8063), using the substantive llvm-inprocess workflow commands and an isolated target, still fails the spike object-byte oracle:
PERRY_LLVM_INPROCESS=diff ... spike.ts
perry: in-process LLVM backend active (LLVM 22.1.4)
perry: [ir-diff] MISMATCH — object bytes differ (text 164063 vs native 164671)
The text and native executables both exit 0 and emit identical 92-byte output (SHA-256 ccfecd9c51f0cd29a7fa349f3047d527e96b2b60f2078a269f91d0e9b6a4bf9c). This is not a behavior-only failure and not metadata noise: the emitted code and the GC map differ.
This is a distinct follow-on to #8052/#8062. #8062 correctly made native construction apply precise-root lowering; doing so exposes a pass-order difference that its small one-root emitted-object fixture does not exercise.
Exact local setup
main: fe4cd09db0f1e260fc0ff63ec2e5c0079fceac3d
LLVM: Homebrew 22.1.4
host: arm64 macOS
build: cargo build --profile perry-dev -p perry -p perry-runtime-static -p perry-stdlib-static --features perry/llvm-inprocess
env: PERRY_RUNTIME_DIR=<isolated-target>/perry-dev PERRY_NO_AUTO_OPTIMIZE=1 PERRY_NO_CACHE=1
The focused perry-codegen --lib gate is otherwise green: 990/990, including all three corpus liveness lines and rs4gc_schedules_in_process.
First semantic delta
With PERRY_LLVM_DIFF_DIR set, the two dumped pre-opt modules show that LLVM's C-API builder folds constant instructions during native construction, while the whole-module text parser retains the equivalent instruction graph until optimization. Examples include a constant bitcast + inttoptr root becoming one constant expression and constant select/write-barrier predicates disappearing.
Production currently schedules:
function(mem2reg),rewrite-statepoints-for-gc
RS4GC therefore observes the two non-canonicalized SSA graphs before the normal O3 pipeline can converge them. The first assembly delta is inside _main: native has an additional live zero/root register and a different spill assignment. The final artifacts demonstrate that this reaches semantic sections:
text native
Mach-O object bytes 23056 23104
__text size 0x1dd0 0x1df4
__perry_gcmap size 0x059e 0x05a9
Relocations and compact-map root offsets shift with that code/spill difference; this is not safe to normalize away.
Causal probe
Running both exact dumped modules through:
function(mem2reg,instcombine),rewrite-statepoints-for-gc,default<O3>
and then the same LLVM target emission produces byte-identical full assembly:
text sha256 9df871b21a1ae73034ac213233cd30454070a70364bdb2cd13a123771d65e2bb
native sha256 9df871b21a1ae73034ac213233cd30454070a70364bdb2cd13a123771d65e2bb
cmp exit 0
That makes the narrow causal fix adding canonicalization before RS4GC, while preserving the exact byte oracle.
Acceptance
- Add the minimum pre-RS4GC canonicalization needed to make construction-time folding order-independent.
- Add an emitted-object regression with constant-foldable rooted values plus live dynamic roots; the current single-root fix(codegen): lower precise roots in native LLVM construction #8062 fixture is too small and already passes.
- The focused spike
PERRY_LLVM_INPROCESS=diffreportsir-diff. OK. - Do not weaken or normalize the emitted-byte oracle.
Activity