diff --git a/cranelift/filetests/filetests/runtests/arithmetic.clif b/cranelift/filetests/filetests/runtests/arithmetic.clif index f8398b3b40..c3cfe07c4c 100644 --- a/cranelift/filetests/filetests/runtests/arithmetic.clif +++ b/cranelift/filetests/filetests/runtests/arithmetic.clif @@ -196,7 +196,8 @@ block0(v0: i64,v1: i64): ; run: %sdiv_i64(3, -2) == -1 ; run: %sdiv_i64(-19, 7) == -2 ; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0xF81FFDDD_DBD96000 -; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0x7E00222_2426A000 +; run: %sdiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0x7E002222_426A000 +; run: %sdiv_i64(0x80000000_00000000, -2) == 0x40000000_00000000 function %sdiv_i32(i32, i32) -> i32 { block0(v0: i32,v1: i32): @@ -212,6 +213,7 @@ block0(v0: i32,v1: i32): ; run: %sdiv_i32(-19, 7) == -2 ; run: %sdiv_i32(0xC0FFEEEE, 8) == 0xF81FFDDE ; run: %sdiv_i32(0xC0FFEEEE, -8) == 0x7E00222 +; run: %sdiv_i32(0x80000000, -2) == 0x40000000 function %sdiv_i16(i16, i16) -> i16 { block0(v0: i16,v1: i16): @@ -227,6 +229,7 @@ block0(v0: i16,v1: i16): ; run: %sdiv_i16(-19, 7) == -2 ; run: %sdiv_i16(0xC0FF, 8) == 0xF820 ; run: %sdiv_i16(0xC0FF, -8) == 0x07E0 +; run: %sdiv_i16(0x8000, -2) == 0x4000 function %sdiv_i8(i8, i8) -> i8 { block0(v0: i8,v1: i8): @@ -242,6 +245,7 @@ block0(v0: i8,v1: i8): ; run: %sdiv_i8(-19, 7) == -2 ; run: %sdiv_i8(0xC0, 8) == 0xF8 ; run: %sdiv_i8(0xC0, -8) == 0x08 +; run: %sdiv_i8(0x80, -2) == 0x40 function %udiv_i64(i64, i64) -> i64 { @@ -258,6 +262,8 @@ block0(v0: i64,v1: i64): ; run: %udiv_i64(-19, 7) == 0x24924924_9249248F ; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, 8) == 0x181FFDDD_DBD95FFF ; run: %udiv_i64(0xC0FFEEEE_DECAFFFF, -8) == 0 +; run: %udiv_i64(0x80000000_00000000, -1) == 0 +; run: %udiv_i64(0x80000000_00000000, -2) == 0 function %udiv_i32(i32, i32) -> i32 { block0(v0: i32,v1: i32): @@ -273,6 +279,8 @@ block0(v0: i32,v1: i32): ; run: %udiv_i32(-19, 7) == 0x24924921 ; run: %udiv_i32(0xC0FFEEEE, 8) == 0x181FFDDD ; run: %udiv_i32(0xC0FFEEEE, -8) == 0 +; run: %udiv_i32(0x80000000, -1) == 0 +; run: %udiv_i32(0x80000000, -2) == 0 function %udiv_i16(i16, i16) -> i16 { block0(v0: i16,v1: i16): @@ -288,6 +296,8 @@ block0(v0: i16,v1: i16): ; run: %udiv_i16(-19, 7) == 0x248F ; run: %udiv_i16(0xC0FF, 8) == 0x181F ; run: %udiv_i16(0xC0FF, -8) == 0 +; run: %udiv_i16(0x8000, -1) == 0 +; run: %udiv_i16(0x8000, -2) == 0 function %udiv_i8(i8, i8) -> i8 { block0(v0: i8,v1: i8): @@ -303,3 +313,5 @@ block0(v0: i8,v1: i8): ; run: %udiv_i8(-19, 7) == 0x21 ; run: %udiv_i8(0xC0, 8) == 0x18 ; run: %udiv_i8(0xC0, -8) == 0 +; run: %udiv_i8(0x80, -1) == 0 +; run: %udiv_i8(0x80, -2) == 0 diff --git a/cranelift/interpreter/src/interpreter.rs b/cranelift/interpreter/src/interpreter.rs index 3c9964c431..c8a81c87e5 100644 --- a/cranelift/interpreter/src/interpreter.rs +++ b/cranelift/interpreter/src/interpreter.rs @@ -340,6 +340,27 @@ mod tests { } } + #[test] + fn sdiv_min_by_neg_one_traps_with_overflow() { + let code = "function %test() -> i8 { + block0: + v0 = iconst.i32 -2147483648 + v1 = sdiv_imm.i32 v0, -1 + return v1 + }"; + + let func = parse_functions(code).unwrap().into_iter().next().unwrap(); + let mut env = FunctionStore::default(); + env.add(func.name.to_string(), &func); + let state = InterpreterState::default().with_function_store(env); + let result = Interpreter::new(state).call_by_name("%test", &[]).unwrap(); + + match result { + ControlFlow::Trap(CraneliftTrap::User(TrapCode::IntegerOverflow)) => {} + _ => panic!("Unexpected ControlFlow: {:?}", result), + } + } + // This test verifies that functions can refer to each other using the function store. A double indirection is // required, which is tricky to get right: a referenced function is a FuncRef when called but a FuncIndex inside the // function store. This test would preferably be a CLIF filetest but the filetest infrastructure only looks at a diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index c9c0372980..aed999af48 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -81,6 +81,9 @@ where Err(ValueError::IntegerDivisionByZero) => Ok(ControlFlow::Trap(CraneliftTrap::User( TrapCode::IntegerDivisionByZero, ))), + Err(ValueError::IntegerOverflow) => Ok(ControlFlow::Trap(CraneliftTrap::User( + TrapCode::IntegerOverflow, + ))), Err(e) => Err(e), }; diff --git a/cranelift/interpreter/src/value.rs b/cranelift/interpreter/src/value.rs index ff64436fc9..7f8bbf8bc0 100644 --- a/cranelift/interpreter/src/value.rs +++ b/cranelift/interpreter/src/value.rs @@ -71,6 +71,8 @@ pub enum ValueError { InvalidDataValueCast(#[from] DataValueCastFailure), #[error("performed a division by zero")] IntegerDivisionByZero, + #[error("performed a operation that overflowed this integer type")] + IntegerOverflow, } #[derive(Debug, PartialEq)] @@ -350,7 +352,15 @@ impl Value for DataValue { } fn div(self, other: Self) -> ValueResult { - if other.clone().into_int()? == 0 { + let denominator = other.clone().into_int()?; + + // Check if we are dividing INT_MIN / -1. This causes an integer overflow trap. + let min = Value::int(1i128 << (self.ty().bits() - 1), self.ty())?; + if self == min && denominator == -1 { + return Err(ValueError::IntegerOverflow); + } + + if denominator == 0 { return Err(ValueError::IntegerDivisionByZero); }