Skip to content

test(vcr-oracle): #518 oracle-first — characterize i64-param miscompile on both selectors (#242)#525

Merged
avrabe merged 1 commit into
mainfrom
oracle/i64-param-518-characterization
Jun 27, 2026
Merged

test(vcr-oracle): #518 oracle-first — characterize i64-param miscompile on both selectors (#242)#525
avrabe merged 1 commit into
mainfrom
oracle/i64-param-518-characterization

Conversation

@avrabe

@avrabe avrabe commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

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:

  • DIRECT (--relocatable / select_with_stack): infer_i64_locals learns width only from LocalSet/LocalTee 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).

Frozen-safe (no codegen touched — frozen anchors 3/3 green)

The byte-changing fix is a SEPARATE gated step (re-freeze + differential + silicon), not this idle tick.

Refs #242, #518.

🤖 Generated with Claude Code

…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

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit 0223567 into main Jun 27, 2026
20 checks passed
@avrabe avrabe deleted the oracle/i64-param-518-characterization branch June 27, 2026 04:42
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant