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

fix(riscv): signed-div overflow guard must not clobber the dividend (#232, v0.11.27)#233
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

Loading
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