Skip to content

Commit 4a38b98

Browse files
committed
Handle srem properly when avoid_div_traps is false.
The codegen for div/rem ops has two modes, depending on the `avoid_div_traps` flag: it can either do all checks for trapping conditions explicitly, and use explicit trap instructions, then use a hardware divide instruction that will not trap (`avoid_div_traps == true`); or it can run in a mode where a hardware FP fault on the divide instruction implies a Wasm trap (`avoid_div_traps == false`). Wasmtime uses the former while Lucet (for example) uses the latter. It turns out that because we run all our spec tests run under Wasmtime, we missed a spec corner case that fails in the latter: INT_MIN % -1 == 0 per the spec, but causes a trap with the x86 signed divide/remainder instruction. Hence, in Lucet, this specific remainder computation would incorrectly result in a Wasm trap. This PR fixes the issue by just forcing use of the explicit-checks implementation for `srem` even when `avoid_div_traps` is false.
1 parent db7ec95 commit 4a38b98

4 files changed

Lines changed: 33 additions & 2 deletions

File tree

cranelift/codegen/src/isa/x64/inst/emit.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,6 @@ pub(crate) fn emit(
887887
// idiv %divisor
888888
//
889889
// $done:
890-
debug_assert!(info.flags().avoid_div_traps());
891890

892891
// Check if the divisor is zero, first.
893892
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg());

cranelift/codegen/src/isa/x64/lower.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5181,7 +5181,8 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
51815181
input_ty,
51825182
));
51835183

5184-
if flags.avoid_div_traps() {
5184+
// Always do explicit checks for `srem`: otherwise, INT_MIN % -1 is not handled properly.
5185+
if flags.avoid_div_traps() || op == Opcode::Srem {
51855186
// A vcode meta-instruction is used to lower the inline checks, since they embed
51865187
// pc-relative offsets that must not change, thus requiring regalloc to not
51875188
// interfere by introducing spills and reloads.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
test compile
2+
set avoid_div_traps=false
3+
target x86_64
4+
feature "experimental_x64"
5+
6+
function %f0(i32, i32) -> i32 {
7+
block0(v0: i32, v1: i32):
8+
v2 = srem.i32 v0, v1
9+
return v2
10+
}
11+
12+
; run: %f0(0x80000000, 0xffffffff) == 0
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
test compile
2+
set avoid_div_traps=false
3+
target x86_64
4+
feature "experimental_x64"
5+
6+
;; We should get the checked-div/rem sequence (`srem` pseudoinst below) even
7+
;; when `avoid_div_traps` above is false (i.e. even when the host is normally
8+
;; willing to accept SIGFPEs as Wasm traps). The machine will SIGFPE in some
9+
;; cases when `srem` is valid (specifically -INT_MIN % -1).
10+
function %f0(i64, i64) -> i64 {
11+
block0(v0: i64, v1: i64):
12+
v2 = srem.i64 v0, v1
13+
; check: movq %rdi, %rax
14+
; nextln: movl $$0, %edx
15+
; nextln: srem $$rax:$$rdx, %rsi
16+
; nextln: movq %rdx, %rax
17+
18+
return v2
19+
}

0 commit comments

Comments
 (0)