fix(arm): home i64 params per AAPCS register-pairs — close #518 silent miscompile (#242)#531
Open
avrabe wants to merge 1 commit into
Open
fix(arm): home i64 params per AAPCS register-pairs — close #518 silent miscompile (#242)#531avrabe wants to merge 1 commit into
avrabe wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…t miscompile (#518, #242) An i64 binop reading an i64 PARAM silently miscompiled on BOTH ARM selectors: an i64 param occupies an AAPCS register PAIR (param0 = R0:R1), but both selectors treated a param as a single i32-width register. Found by footgun/adversarial differential testing; flip-independent, pre-existing. Root cause + fix, by selector: - DIRECT (select_with_stack): `infer_i64_locals` learns i64-ness only from LocalSet/Tee, so a read-only i64 param stayed is_i64=false → its implicit hi register was unreserved and a following i64.const was allocated INTO it (movw r1,#K clobbered R1 = hi of an i64 param in R0:R1). Fixes: (a) seed `i64_locals` from `self.params_i64`; (b) reserve the hi half of a live i64 param in the #193 reservation (live_param_regs); (c) map params to registers via the new `aapcs_param_regs` (even-aligned pairs — (i32,i64) -> R0,R2:R3, not the sequential R1:R2 the old index_to_reg used). All-i32 signatures map identically to index_to_reg, so non-i64-param functions are byte-identical. - OPTIMIZED (ir_to_arm): the I64Load param-home guard `num_params >= 2/4` conflated register-count with param-count and dropped the param (read from a fresh R4:R5 pair). Since ir_to_arm lacks per-param TYPES it cannot compute AAPCS pairing — decline any function that reads an i64 param to the direct selector (the #359/#188/#507 honest-degradation pattern). Two i64-param sub-cases are declined LOUDLY (Ok-or-Err, never silent wrong-code), their correct lowering tracked as follow-ups: - an i64 param AAPCS-passed PAST R3 (stack) — the open #503-i64 case; - an i64 param in a FRAME-BACKING function (has a call, or the pair-exhaustion retry) — the param_slots path sizes an i64 param's slot from a width set that excludes params, dropping the high half. So no silent wrong-code remains: every i64-param function is either correctly compiled (leaf, register-resident, the common case) or loud-skipped. Gate: scripts/repro/i64_param_518_differential.py flipped to the correctness gate (EXPECT_MISCOMPILE=False) — 11 leaf cases match wasmtime on BOTH paths across the full AAPCS matrix (single i64, (i32,i64), (i64,i64), second-i64-param), PLUS a decline-contract check (i64_param_518_decline.wat): d_past_r3 + d_call loud-skip, d_leaf emitted + executes correctly. Frozen anchors 3/3 byte-identical (control_step 0x00210A55 / flight_algo 0x07FDF307 / divseam — no i64 params). Updated the #94 hi32-extract byte-size test to source its i64 from a sign-extended i32 param (a non-param i64 that stays on the optimized path) — the old i64-param shape was itself exercising the now-declined miscompile, masked by a size-only assertion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
e008e9d to
10cdf20
Compare
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
Closes #518: an i64 binop reading an i64 parameter silently miscompiled on both ARM selectors. An i64 param occupies an AAPCS register pair (param0 = R0:R1), but both selectors treated a param as a single i32-width register. Found by footgun/adversarial differential testing; flip-independent, pre-existing.
Root cause + fix, by selector
select_with_stack):infer_i64_localslearns i64-ness only fromLocalSet/Tee, so a read-only i64 param stayedis_i64=false→ its implicit hi register was unreserved and a followingi64.constwas allocated into it (movw r1,#Kclobbered R1 = hi of an i64 param in R0:R1). Fixes: (a) seedi64_localsfromself.params_i64; (b) reserve the hi half of a live i64 param in the arm regalloc: select_with_stack allocates param registers (r0-r3) for temps/results before live param reads — i64_lowering fuzz class #193 reservation; (c) map params to registers via the newaapcs_param_regs(even-aligned pairs —(i32,i64)→R0,R2:R3, not the sequential R1:R2 the oldindex_to_regused). All-i32 signatures map identically, so non-i64-param functions are byte-identical.ir_to_arm): theI64Loadparam-home guardnum_params >= 2/4conflated register-count with param-count and dropped the param (read from a fresh R4:R5 pair). Sinceir_to_armlacks per-param types it can't compute AAPCS pairing — decline any function that reads an i64 param to the direct selector (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 honest-degradation pattern).Bounded: two sub-cases declined LOUDLY (Ok-or-Err, never silent wrong-code)
param_slotspath sizes an i64 slot from a width set that excludes params, dropping the high half.So no silent wrong-code remains: every i64-param function is either correctly compiled (leaf, register-resident — the common case) or loud-skipped.
Gate (oracle-first → correctness gate)
scripts/repro/i64_param_518_differential.pyflipped to the correctness gate (EXPECT_MISCOMPILE=False): 11 leaf cases match wasmtime on both paths across the full AAPCS matrix (single i64,(i32,i64),(i64,i64), second-i64-param), PLUS a decline-contract check (i64_param_518_decline.wat):d_past_r3+d_callloud-skip,d_leafemitted + executes correctly.0x00210A55/ flight_algo0x07FDF307/ divseam — no i64 params).test_ir_to_arm_i64_add_*to also pin the decline contract.Refs #242, #503 (the i64-stack-param follow-up the decline points at).
🤖 Generated with Claude Code