Skip to content

fix(riscv): signed-div overflow guard must not clobber the dividend (#232, v0.11.27) - #233

Merged
avrabe merged 1 commit into
mainfrom
fix/v0.11.27-rv32-signed-div-guard-clobber
Jun 3, 2026
Merged

avrabe merged 1 commit into
mainfrom
fix/v0.11.27-rv32-signed-div-guard-clobber

Conversation

@avrabe

@avrabe avrabe commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

gale #232 — v0.11.26 regression: silent miscompile on signed div-by-const

Introduced by #231 (lowest-free allocator). The i32.div_s INT_MIN / -1 overflow guard pops its dividend/divisor off the vstack, then allocates scratch registers for the INT_MIN and -1 comparison constants. Because the popped operands are no longer on the vstack, live_regs didn't protect them — the lowest-free allocator reused the dividend's register for the INT_MIN constant, clobbering it before the guard's bne rs1, tmin read it.

filter_axis_decide(1000,100,500) → 0 (expected 1088) on qemu_riscv32. Round-robin (v0.11.25) had marched to a different register and masked the latent defect — same lesson as before: an allocation-order change can expose a latent bug without being its root cause.

Fix

New alloc_temp_avoiding(&[Reg]): the guard materializes its constants into a register that avoids the popped-but-live dividend/divisor. Applied to:

  • the i32 guard (the reported, reachable bug), and
  • the i64 INT64_MIN / -1 guard — same latent defect, though i64 div_s is currently unreachable (i64 params/locals unimplemented), so it's defensive. (The broader i64 magnitude-reduction register discipline should be re-verified when i64 params land — noted, not claimed-fixed here.)

Same liveness-across-a-branch-region class as #226.

Oracle

  • New committed fixture scripts/repro/signed_div_const.{wat,wasm} + signed_div_const_riscv_differential.py: 5/5 vectors match wasmtime — the 1088 repro, the INT_MIN overflow edge (-2147483648/?), and negative dividends.
  • Unit test signed_div_guard_does_not_clobber_operands_232.
  • All five prior fixtures stay bit-identical (ARM div_const/control_step/flight_seam; RV32 control_step/controller_step). The cleanup(riscv): allocator prefers caller-saved (lowest-free, not round-robin) (#230, v0.11.26) #231 caller-saved win is preserved — leaf functions still spill 0 callee-saved registers.

Closes #232.

🤖 Generated with Claude Code

…232, v0.11.27)

Regression from v0.11.26 (#231 lowest-free allocator). The i32.div_s INT_MIN/-1
overflow guard pops dividend/divisor off the vstack, then allocs scratch regs for
its INT_MIN and -1 comparison constants. Since the popped operands are no longer
on the vstack, live_regs didn't protect them and the lowest-free allocator reused
the dividend's register for the INT_MIN constant, clobbering it before the guard's
bne read it. filter_axis_decide(1000,100,500) returned 0 instead of 1088 on
qemu_riscv32 (round-robin masked the latent bug in v0.11.25).

Fix: alloc_temp_avoiding(&[Reg]) — the guard materializes its constants into a
register that avoids the popped-but-live dividend and divisor. Applied to both the
i32 guard and the i64 INT64_MIN/-1 guard (i64 path not yet reachable but same
latent defect). Same liveness-across-a-branch-region class as #226.

Regression proof: scripts/repro/signed_div_const.{wat,wasm} +
signed_div_const_riscv_differential.py — 5/5 match wasmtime (incl. 1088 repro,
INT_MIN edge, negatives). Unit test signed_div_guard_does_not_clobber_operands_232.
All five prior fixtures stay bit-identical; the #231 caller-saved win is preserved.

Closes #232.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 3, 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 847278f into main Jun 3, 2026
14 checks passed
@avrabe
avrabe deleted the fix/v0.11.27-rv32-signed-div-guard-clobber branch June 3, 2026 13:00
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.

v0.11.26 RV32: i32.div_s overflow guard clobbers dividend register (signed div-by-const → wrong result)

1 participant