From 5f54cc7658ffb538fa9ad06727ca0bedd009beaf Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 20 Jun 2020 15:15:28 +0200 Subject: [PATCH] Implement checked_mul Fixes #6 --- Readme.md | 2 +- example/std_example.rs | 23 ++++++++++ patches/0023-core-Ignore-failing-tests.patch | 40 ----------------- src/num.rs | 46 +++++++++++++++----- 4 files changed, 60 insertions(+), 51 deletions(-) diff --git a/Readme.md b/Readme.md index e850b320fe2..1146f7e415e 100644 --- a/Readme.md +++ b/Readme.md @@ -67,6 +67,6 @@ function jit_calc() { ## Not yet supported * Good non-rust abi support ([several problems](https://github.com/bjorn3/rustc_codegen_cranelift/issues/10)) -* Checked binops ([some missing instructions in cranelift](https://github.com/bytecodealliance/wasmtime/issues/1044)) + * Proc macros * Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041), not coming soon) * SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work) diff --git a/example/std_example.rs b/example/std_example.rs index 7038df874a4..ebac391f1e5 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -87,6 +87,8 @@ fn main() { panic!(); } + test_checked_mul(); + let _a = 1u32 << 2u8; let empty: [i32; 0] = []; @@ -258,6 +260,27 @@ unsafe fn test_mm_extract_epi8() { assert_eq!(r2, 3); } +fn test_checked_mul() { + let u: Option = u8::from_str_radix("1000", 10).ok(); + assert_eq!(u, None); + + assert_eq!(1u8.checked_mul(255u8), Some(255u8)); + assert_eq!(255u8.checked_mul(255u8), None); + assert_eq!(1i8.checked_mul(127i8), Some(127i8)); + assert_eq!(127i8.checked_mul(127i8), None); + assert_eq!((-1i8).checked_mul(-127i8), Some(127i8)); + assert_eq!(1i8.checked_mul(-128i8), Some(-128i8)); + assert_eq!((-128i8).checked_mul(-128i8), None); + + assert_eq!(1u64.checked_mul(u64::max_value()), Some(u64::max_value())); + assert_eq!(u64::max_value().checked_mul(u64::max_value()), None); + assert_eq!(1i64.checked_mul(i64::max_value()), Some(i64::max_value())); + assert_eq!(i64::max_value().checked_mul(i64::max_value()), None); + assert_eq!((-1i64).checked_mul(i64::min_value() + 1), Some(i64::max_value())); + assert_eq!(1i64.checked_mul(i64::min_value()), Some(i64::min_value())); + assert_eq!(i64::min_value().checked_mul(i64::min_value()), None); +} + #[derive(PartialEq)] enum LoopState { Continue(()), diff --git a/patches/0023-core-Ignore-failing-tests.patch b/patches/0023-core-Ignore-failing-tests.patch index 1c9097f21ab..bb3a03338f2 100644 --- a/patches/0023-core-Ignore-failing-tests.patch +++ b/patches/0023-core-Ignore-failing-tests.patch @@ -30,38 +30,10 @@ index 4bc44e9..8e3c7a4 100644 #[test] fn empty_array_is_always_default() { -diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs -index c9096b7..be37fcd 100644 ---- a/src/libcore/tests/iter.rs -+++ b/src/libcore/tests/iter.rs -@@ -342,6 +342,7 @@ fn test_iterator_step_by_nth() { - } - - #[test] -+#[ignore] // checked_mul impl not yet checking for overflow - fn test_iterator_step_by_nth_overflow() { - #[cfg(target_pointer_width = "8")] - type Bigger = u16; -@@ -2305,6 +2308,7 @@ fn test_repeat_with_take_collect() { - } - - #[test] -+#[ignore] // checked_mul impl not yet checking for overflow - fn test_successors() { - let mut powers_of_10 = successors(Some(1_u16), |n| n.checked_mul(10)); - assert_eq!(powers_of_10.by_ref().collect::>(), &[1, 10, 100, 1_000, 10_000]); diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index a17c094..5bb11d2 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs -@@ -63,6 +63,7 @@ pub fn test_num(ten: T, two: T) where - } - - #[test] -+#[ignore] // checked_mul impl not yet checking for overflow - fn from_str_issue7588() { - let u: Option = u8::from_str_radix("1000", 10).ok(); - assert_eq!(u, None); @@ -640,6 +639,7 @@ macro_rules! test_float { mod $modname { // FIXME(nagisa): these tests should test for sign of -0.0 @@ -78,17 +50,5 @@ index a17c094..5bb11d2 100644 fn max() { assert_eq!((0.0 as $fty).max(0.0), 0.0); assert_eq!((-0.0 as $fty).max(-0.0), -0.0); -diff --git a/src/libcore/tests/time.rs b/src/libcore/tests/time.rs -index fac70c4..9107a02 100644 ---- a/src/libcore/tests/time.rs -+++ b/src/libcore/tests/time.rs -@@ -127,6 +127,7 @@ fn mul() { - } - - #[test] -+#[ignore] // checked_mul impl not yet checking for overflow - fn checked_mul() { - assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2))); - assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); -- 2.21.0 (Apple Git-122) diff --git a/src/num.rs b/src/num.rs index 5acff80768e..5cf281cba6f 100644 --- a/src/num.rs +++ b/src/num.rs @@ -245,16 +245,42 @@ pub(crate) fn trans_checked_int_binop<'tcx>( (val, has_overflow) } BinOp::Mul => { - let val = fx.bcx.ins().imul(lhs, rhs); - /*let val_hi = if !signed { - fx.bcx.ins().umulhi(lhs, rhs) - } else { - fx.bcx.ins().smulhi(lhs, rhs) - }; - let has_overflow = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0);*/ - // TODO: check for overflow - let has_overflow = fx.bcx.ins().bconst(types::B1, false); - (val, has_overflow) + let ty = fx.bcx.func.dfg.value_type(lhs); + match ty { + types::I8 | types::I16 | types::I32 if !signed => { + let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs); + let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, (1 << ty.bits()) - 1); + let val = fx.bcx.ins().ireduce(ty, val); + (val, has_overflow) + } + types::I8 | types::I16 | types::I32 if signed => { + let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs); + let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1))); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, (1 << (ty.bits() - 1)) - 1); + let val = fx.bcx.ins().ireduce(ty, val); + (val, fx.bcx.ins().bor(has_underflow, has_overflow)) + } + types::I64 => { + //let val = fx.easy_call("__mulodi4", &[lhs, rhs, overflow_ptr], types::I64); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_overflow = if !signed { + let val_hi = fx.bcx.ins().umulhi(lhs, rhs); + fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0) + } else { + let val_hi = fx.bcx.ins().smulhi(lhs, rhs); + let not_all_zero = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0); + let not_all_ones = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64); + fx.bcx.ins().band(not_all_zero, not_all_ones) + }; + (val, has_overflow) + } + types::I128 => unreachable!("i128 should have been handled by codegen_i128::maybe_codegen"), + _ => unreachable!("invalid non-integer type {}", ty), + } } BinOp::Shl => { let val = fx.bcx.ins().ishl(lhs, rhs);