Skip to content

fix(#776): aarch64 rotl clobbers a computed rotate operand (silent wrong result)#781

Merged
avrabe merged 1 commit into
mainfrom
fix/776-aarch64-rotl-clobber
Jul 16, 2026
Merged

fix(#776): aarch64 rotl clobbers a computed rotate operand (silent wrong result)#781
avrabe merged 1 commit into
mainfrom
fix/776-aarch64-rotl-clobber

Conversation

@avrabe

@avrabe avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Silent miscompile in i32.rotl/i64.rotl on the aarch64 backend when the rotated operand is computed — introduced by #769 (m2). Fix-first patch → v0.45.2.

Root cause

rotl = neg + rorv (no native A64 rotate-left). The neg scratch reused dst = alloc_temp(): neg(dst,k); rorv(dst,n,dst). For a computed n, alloc_temp can return n's freed register, so neg(dst,k) destroys n before rorv reads it. Param-only rotates escaped (n in a distinct arg reg).

Fix

neg(k,k); rorv(dst,n,k) — compute -k in k's own dead register; rorv reads n intact + -k. One-line-equivalent, reads-before-write safe for any dst aliasing.

Verification (red→green)

  • New i32_rotl_computed/i64_rotl_computed cases (n = add temp) in the m2 differential.
  • RED on pre-fix: 175/182 (i32_rotl_computed(0x12345678,4) A64=-49 vs wasmtime=1183502082, 7 fails).
  • GREEN after: 182/182 vs wasmtime. Rides the CI-wired aarch64-oracle job.

Closes #776.

🤖 Generated with Claude Code

…tch must not be dst

i32.rotl/i64.rotl lower to `neg + rorv` (A64 has no native rotate-left). The pre-fix
code reused `dst = alloc_temp()` as the neg scratch: `neg(dst,k); rorv(dst,n,dst)`.
When the rotated operand `n` is a COMPUTED value, alloc_temp can hand back the
register that held it (n,k are already popped), so `neg(dst,k)` destroys `n` before
`rorv` reads it — a silent wrong result. Param-only rotates escaped it because `n`
sits in a distinct argument register. Introduced by #769 (aarch64 m2).

Fix: compute `-k` in `k`'s own now-dead register (`neg(k,k); rorv(dst,n,k)`). rorv
reads `n` (intact) + `-k` and writes `dst` — reads-before-write holds even if `dst`
aliases an input, and n/k are distinct stack slots so `neg(k,k)` never touches `n`.

Red-first gate: i32_rotl_computed / i64_rotl_computed added to the m2 differential
(n = an add temp). RED on the pre-fix binary (A64=-49 vs wasmtime=1183502082 etc.,
7 cases), GREEN after (182/182 vs wasmtime). Rides the already-CI-wired
aarch64-oracle job.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend-aarch64/src/selector.rs 66.66% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe merged commit a8c9c92 into main Jul 16, 2026
38 of 39 checks passed
@avrabe avrabe deleted the fix/776-aarch64-rotl-clobber branch July 16, 2026 16:36
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.

aarch64: i32.rotl/i64.rotl clobbers a computed rotate operand (neg scratch aliases n) — silent wrong result

1 participant