Repository navigation
feat(vcr-ra): i64 pair-spill via param frame-backing — VCR-RA-001 acceptance closes (v0.11.40) - #325
Merged
Merged
Conversation
…iterion closes (#242) The last hard-fail named by the v0.11.40 acceptance review: a high-pressure i64 module now compiles with no hard-fail. ROOT CAUSE (differs from the review's reading): the pair allocator (alloc_consecutive_pair) has ALWAYS spilled register-resident stack values — pair-aware, both halves into one 8-byte slot (#171). The remaining "no consecutive pair of free registers for i64" Err fires only when the operand stack holds nothing register-resident and the blockers are the PINNED PARAM HOME REGISTERS (#193 `reserved`, up to r0-r3) plus the popped operand pairs (`extra_avoid`, up to 4 regs) — 8 of the 9-register pool, no pair left, and no amount of stack spilling can free a param home register. FIX (same structural-bit-identity pattern as #320): a third retry mode, `set_param_backing_on_exhaustion`, forces the proven #204 `param_slots` frame-backing for call-free functions — params spill to frame slots at entry and reload on read, so `reserved` empties and a free consecutive pair always exists after stack spilling (extra_avoid is at most two adjacent pairs among nine registers; pigeonhole over the free segments). The backend's select_direct ladder: pass 1 default → pass 2 spill-only (#320, on the single-reg Err) → pass 3 spill+param-backing (on the pair Err). Functions that compile on an earlier pass are selected by exactly yesterday's code. Loop bound unchanged: each spill_deepest_reg iteration converts one StackVal::Reg to Spilled. Honest remaining bound: the i64 spill-slot pool ("spill-slot pool exhausted"), deliberately NOT retried. EVIDENCE - scripts/repro/high_pressure_i64.wat: 4 simultaneously-live i64 consts + 4 pinned i32 params. On main: skipped with the exact pair Err. On this branch: compiles; high_pressure_i64_differential.py (unicorn vs wasmtime, i64 result in r0:r1, large/negative vectors) 6/6 ORACLE PASS. - Bit-identity vs origin/main (cortex-m4, per differential headers): control_step 8248…32f6, div_const cada…45ede, flight_seam_flat 03ce…8579, high_pressure_i32 355c…e6c2 — all pairwise sha256-identical; all three frozen differentials ORACLE PASS on the branch. - Tests: 5 new (pair victim spill, single victims, pinned-blockers Err preserved verbatim, spilled-pair reload on pop, end-to-end 3-pass ladder); synth-synthesis 382 lib tests green; workspace 1468 passed / 0 failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This was referenced Jun 11, 2026
This was referenced Jul 2, 2026
avrabe
added a commit
that referenced
this pull request
Jul 3, 2026
…emainder (#587, #242) (#589) Extend the SYNTH_SPILL_ON_EXHAUST pre-step (#580) from i32 singles to i64 register PAIRS on the optimized path. When an even-aligned pair allocation would fail, the pre-step evicts either (a) a coherent live PAIR or (b) two independent singles freeing a legal even pair — choosing the candidate whose displaced values have the farthest soonest-next-use (pair-aware Belady), deterministic tie-breaks by fixed candidate order. Pair victims get 8-byte-aligned slot couples (lo at off, hi at off+4 — the #171/#325 direct-path convention) and reload as coherent pairs into any legal even pair at next use; a spilled final i64 result reloads straight into R0:R1 in the epilogue. If no sound eviction exists the function falls through to the existing #496 decline — loud skip, never wrong code. Scope stays honest: only the modeled i64 subset is admitted (const/load-param/ add/sub/and/or/xor/mul, comparisons+eqz, i32<->i64 conversions); shifts/ rotates (encoders clobber the amount pair in place), div/rem, clz/ctz/popcnt, non-param i64 locals, and i64+globals/memory.size mixing (R9/R10 conventions collide with the (R8,R9)/(R10,R11) candidates) keep declining. The candidate list is shared with alloc_i64_pair via SPILL_I64_PAIR_CANDIDATES so evictor and allocator cannot drift. Red -> green: scripts/repro/i64_pair_exhaust_587.wat (5 simultaneously-live i64s over 4 candidate pairs) declined on BOTH flag states at v0.24.0; now flag-on it stays on the optimized path (no recovery rung) and matches wasmtime under unicorn on all 8 vectors (flag-off decline pinned + still matches via the direct selector). Flag-off ELF is byte-identical to the pre-change baseline; frozen_codegen_bytes (incl. escape hatches), const_cse golden, spill_on_exhaust_242 (i32) and i64_param_518 differentials all pass. Flag stays default-OFF pending gale silicon, per the #587 triage. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The last criterion
The #242 acceptance review left one gap: "a high-pressure i64 module compiles with no hard-fail." Investigation revised the diagnosis: the pair-spill loop already existed (#171) — the true blocker was pinned param home registers (#193's
reserved, up to R0–R3) which no stack spill can free. The fix is a third retry mode that forces the proven #204 param frame-backing onto call-free functions, emptying the reserve; pigeonhole over the freed pool then guarantees a consecutive pair.Backend retry ladder (each pass only on its predecessor's exact
Err, so bit-identity stays structural): default → spill-only (#320) → spill+param-backing (this PR).Evidence
scripts/repro/high_pressure_i64.wat: hard pair-exhaustionErron main → compiles + 6/6 unicorn-vs-wasmtime on the branch (incl. all-ones/sign-bit vectors).VCR-RA-001 status after this merges: all five criteria met
compile-no-hard-fail ✅ (both pressure classes) · fixtures bit-identical ✅ · cycles equal-or-better ✅ (gale silicon) · cost-gate revert ✅ (executed, #322) · validator guarding ✅ (#323). Honest bounds named: the 8-slot spill pool, the distinct R8-pair/i64-param-convention class (epic-tracked), stack-passed params.
Release: v0.11.40 — the allocator's acceptance release.
🤖 Generated with Claude Code