Skip to content

rv32: i32.rem_s(INT_MIN,-1) spuriously traps — div_s overflow ebreak guard wrongly copied into rem_s (irem_s must return 0) #666

Description

@avrabe

Summary

On the RISC-V rv32 backend, i32.rem_s(INT_MIN, -1) spuriously traps. WASM Core §4.3.2 (irem_s) defines the remainder as 0 in this case — it must NOT trap (only div_s traps on the INT_MIN/-1 overflow). The rv32 selector copies the div_s INT_MIN/-1 overflow ebreak guard into the rem_s lowering, so rem_s(INT_MIN,-1) faults instead of returning 0. The Thumb-2 backend is correct here; this is rv32-selector-specific.

  • synth 0.34.0, -b riscv -t rv32imac
  • oracle: wasmtime 42.0.1

Reproducer

(module (func (export "f") (param i32 i32) (result i32)
  (i32.rem_s (local.get 0) (local.get 1))))
  • wasmtime --invoke f -2147483648 -10 (correct; rem_s overflow result is 0, no trap).
  • synth rv32 under qemu-system-riscv32 → traps (ebreak), never returns.

Root cause (rv32 disassembly of f)

bnez t1, .divok
ebreak                 ; divisor == 0  -> trap (correct for both div_s and rem_s)
.divok:
lui  t3, 0x80000       ; t3 = INT_MIN
bne  t0, t3, .ok
li   t4, -1
bne  t1, t4, .ok
ebreak                 ; INT_MIN / -1   -> trap   <-- WRONG for rem_s: irem_s(INT_MIN,-1)=0, no trap
.ok:
rem ...

The INT_MIN, -1 -> ebreak overflow guard is correct for div_s (idiv_s traps) but must NOT be present for rem_s (irem_s returns 0). The rv32 selector shares the div overflow guard with the rem path. RISC-V hardware rem already returns the correct INT_MIN % -1 = 0 without any guard, so the fix is simply to omit the INT_MIN/-1 ebreak for rem_s (keep the divide-by-zero ebreak).

Thumb-2 i32.rem_s(INT_MIN,-1) correctly returns 0; rv32 i64.rem_s is unaffected — this is the rv32 i32 rem_s path only. Distinct from the closed #317 (rv32 div_s/rem_s sign computation).

Found via the pulseengine challenge harness (rv32 disasm + wasmtime oracle; qemu-system-riscv32 execution).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions