test(vcr-oracle): #518 oracle-first — characterize i64-param miscompile on both selectors (#242)#525
Merged
Merged
Conversation
…le on both selectors (#242, #518) No new external signal this issue-hunt pass (gates #418/#472/#396-emitter still await external triggers; no new issues/comments since watermark; deps unchanged). Took the bounded behavior-frozen #242 increment: land the ORACLE + quantified characterization for the freshly root-caused #518, ahead of its gated fix. #518: an i64 binop reading an i64 PARAM silently miscompiles on BOTH selectors — one omission (an i64 param is never classified i64) with two surface forms: - DIRECT (--relocatable / select_with_stack): infer_i64_locals learns width only from LocalSet/Tee of an i64 producer, so a read-only param keeps is_i64=false; its implicit hi (i64_pair_hi(R0)=R1) is unreserved, the next i64.const lands in R1, and i64.add reads the clobbered high half. Low correct, high = const-low (t_add(7) -> 0x5_0000_000C). - OPTIMIZED (ir_to_arm): the I64Load param-home guard (num_params >= 2/4) confuses a register count with a param count; (param i64) has num_params==1, falls through to a fresh R4:R5 pair never homed from R0:R1 -> param dropped (t_add(7) -> 5, the bare const). This commit is FROZEN-SAFE — no codegen path touched (frozen anchors 3/3 green): - scripts/repro/i64_param_518.wat: minimal fixture (single i64 param, i64-after- i32 even-aligned, i32 control). - scripts/repro/i64_param_518_differential.py: AAPCS-correct arg placement (even-aligned i64 pairs), i64-aware readback, runs BOTH paths vs wasmtime. Measured: 6/6 i64-param fns diverge on each path; i32 control correct on both. Stable characterization (exit 0 = bug reproduces as documented); the gated fix flips EXPECT_MISCOMPILE->False and it becomes the correctness gate. - roadmap: VCR-ORACLE-001 #518 entry — dual root cause + bounded fix spec (direct: seed i64_locals from params_i64 + AAPCS even-aligned pair mapping; optimized: decline i64-param fns to direct; decline loudly at the #503-i64 boundary). The byte-changing fix is a SEPARATE gated step (re-freeze + differential + silicon), not this idle tick. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
avrabe
added a commit
that referenced
this pull request
Jun 27, 2026
…rix (#242, #518) (#526) No new external signal this issue-hunt pass (gates #418/#472/#396-emitter still await external triggers; no new issues/comments since watermark; deps unchanged). Bounded behavior-frozen #242 increment: harden the #518 characterization oracle (landed #525) to cover the AAPCS register-assignment cases the gated fix's mapping logic must handle but the single-param fixture missed. Measured (objdump force-thumb + unicorn vs wasmtime) — distinct failure modes the sequential `index_to_reg(i)` param mapping produces beyond the single-param clobber: - (i64,i64): p1 is the even-aligned pair R2:R3 but sequential mapping puts it at R1, so `i64.add(p0,p1)` emits `adds r3,r0,r1` = p0.lo+p0.hi (not p0+p1). - (i64,i32): the trailing i32 param (R2) is ignored entirely. - reading only the SECOND i64 param: sequential reads p1 from R1 (wrong) AND the i64.const clobbers R2 (the real p1.lo). Fixture grows by t_ii_add / t_i64_i32 / t_snd_i64; the differential gains those cases (AAPCS-correct arg placement already handles even-aligned pairs) and an explicit i32-control-regression guard (a broken control now FAILS the characterization rather than being masked by the divergence count). Result: 10/10 i64-param functions diverge on BOTH paths; i32 control correct on both. FROZEN-SAFE — no codegen touched (frozen anchors 3/3 green); rivet clean. The byte-changing fix stays a separate gated step. 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.
What
Issue-hunt pass with no new external signal (gates #418/#472/#396-emitter still await external triggers; no new issues/comments since the watermark; deps scry 2.3.0 / jess 0.6.0 / meld 0.37.0 / loom 1.1.16 unchanged). Took the bounded behavior-frozen #242 increment: land the oracle + quantified characterization for the freshly root-caused #518, ahead of its gated fix.
#518 — i64-param binop miscompiles on BOTH selectors
One omission (an i64 param is never classified i64) with two surface forms:
--relocatable/select_with_stack):infer_i64_localslearns width only fromLocalSet/LocalTeeof an i64 producer, so a read-only param keepsis_i64=false; its implicit hi (i64_pair_hi(R0)=R1) is unreserved, the nexti64.constlands in R1, andi64.addreads the clobbered high half. Low correct, high = const-low (t_add(7)→0x5_0000_000C).ir_to_arm): theI64Loadparam-home guard (num_params >= 2/>= 4) confuses a register count with a param count;(param i64)hasnum_params==1, falls through to a fresh R4:R5 pair never homed from R0:R1 → param dropped (t_add(7)→ 5, the bare const).Frozen-safe (no codegen touched — frozen anchors 3/3 green)
scripts/repro/i64_param_518.wat— minimal fixture.scripts/repro/i64_param_518_differential.py— AAPCS-correct arg placement (even-aligned i64 pairs), i64-aware readback, runs both paths vs wasmtime. Measured: 6/6 i64-param fns diverge on each path; the i32 control is correct on both. Stable characterization (exit 0= bug reproduces as documented); the gated fix flipsEXPECT_MISCOMPILE→Falseand it becomes the correctness gate.VCR-ORACLE-001arm: i64 binop with an i64 param silently miscompiles — param read from wrong registers (both paths) #518 entry — dual root cause + bounded fix spec: direct — seedi64_localsfromparams_i64+ AAPCS even-aligned pair mapping; optimized — decline i64-param fns to direct (the Arg-register lowering drops/shifts the first arg for calls with 5 args + struct(sret) return (cortex-m4f, --native-pointer-abi) #359/fuzz: i64_lowering_doesnt_clobber_params flags Mov R0,R8 as AAPCS clobber — real clobber or harness false-positive? #188/Optimized path silently miscompiles br_table — selector elided, all arms fall through (#483/#500 family) #507 pattern); decline loudly at the arm: functions needing the AAPCS stack-arg path (>8 scalar params, or any 64-bit stack param) are skipped, not lowered #503-i64 boundary.The byte-changing fix is a SEPARATE gated step (re-freeze + differential + silicon), not this idle tick.
Refs #242, #518.
🤖 Generated with Claude Code