Skip to content

fix(#273,#274): preserve div/rem traps — no const-fold of INT_MIN/-1, div/rem not dead-eliminable#276

Open
avrabe wants to merge 1 commit into
mainfrom
fix/273-274-trap-preservation
Open

fix(#273,#274): preserve div/rem traps — no const-fold of INT_MIN/-1, div/rem not dead-eliminable#276
avrabe wants to merge 1 commit into
mainfrom
fix/273-274-trap-preservation

Conversation

@avrabe

@avrabe avrabe commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #273. Fixes #274. Two trap-elimination MISCOMPILES (optimized code returned a value where the original TRAPS). Both pre-existed on 1.1.18. wasmtime-verified: optimized modules now trap identically to the original.

#273 — constant-folding dropped the overflow trap

loom-shared/src/lib.rs (rewrite_pure_impl, the Div/Rem S arms): the signed const-fold used wrapping_div/wrapping_rem guarded only on divisor != 0, so div_s(INT_MIN, -1) wrapped to INT_MIN — dropping the mandatory integer overflow trap. Added guard !(l == INT_MIN && r == -1) to the div_s fold (i32 + i64) so it stays a trapping op. rem_s(INT_MIN,-1) does NOT trap in wasm (→ 0), so it's folded explicitly to 0. Unsigned already sound.

#274 — DCE/carrier-forwarding deleted trapping div/rem

loom-core/src/lib.rs (is_simple_pure_instr): div/rem were listed as pure, so the #219 carrier-forwarding dead-path deleted an unused div_s/div_u/rem_s/rem_u (variable divisor), dropping its div-by-zero / overflow trap. Removed all 8 div/rem variants from the purity predicate — they are potentially-trapping, hence not dead-eliminable. Conservative + lossless for #219 (the seam carrier is extend/shift/or/and, never div/rem).

Tests

13 regression tests: div_s(INT_MIN,-1) not folded (i32+i64); rem_s(INT_MIN,-1)→0; dead-result div/rem survive the full pipeline (all 4 ops); dead pure add still eliminated (no over-suppression). wasmtime confirms trap-equivalence. cargo test green on loom-core/shared/isle/cli; fmt+clippy clean.

Folds into v1.2.0 (the correctness release). 🤖 Generated with Claude Code

…div/rem not dead-eliminable)

Integer div_s/div_u/rem_s/rem_u carry mandatory wasm traps: divide-by-zero
(any op) and signed overflow INT_MIN / -1 (div_s). Two optimizations were
silently dropping these traps, returning a value where the original traps.

#273 — constant folding dropped the overflow trap. The signed const-fold
arms in loom-shared used wrapping_div/wrapping_rem guarded only on divisor
!= 0, so i32/i64 div_s(INT_MIN,-1) folded to the constant INT_MIN instead
of staying a trapping div_s. Fixed: skip folding div_s when
dividend == INT_MIN && divisor == -1 (leave the op to trap at runtime).
rem_s(INT_MIN,-1) does NOT trap (result 0) — folded explicitly to 0,
avoiding wrapping_rem's panic path. div_u/rem_u by-zero was already
unfoldable (guarded on divisor != 0); unsigned has no overflow case.

#274 — dead-value/carrier forwarding deleted trapping div/rem. The #219
carrier-forwarding pass classified div/rem as pure via is_simple_pure_instr;
its dead-carrier path (forward_carrier_in_body) DELETES the carrier's RHS
run outright, so an unused div/rem with a runtime-variable divisor was
removed, dropping its potential trap. Fixed: div/rem removed from
is_simple_pure_instr (potentially-trapping ⇒ not dead-eliminable). This is
conservative — the #219 seam carrier is extend/shift/or/and, never div/rem —
so no perf regression. Pure non-trapping ops stay eliminable.

Regression tests (i32+i64, all four ops): #273 div_s(INT_MIN,-1) not folded;
rem_s(INT_MIN,-1) folds to 0; normal div still folds. #274 dead-result
div_s/div_u/rem_s/rem_u not eliminated through the full pipeline and through
forward_carrier_locals directly; dead pure i32.add still eliminated.
Confirmed via wasmtime that the optimized modules trap identically to the
originals (integer overflow / integer divide by zero) rather than returning.

Fixes: #273
Fixes: #274
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant