about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbjorn3 <bjorn3@users.noreply.github.com>2020-06-20 15:15:28 +0200
committerbjorn3 <bjorn3@users.noreply.github.com>2020-06-20 15:15:28 +0200
commit5f54cc7658ffb538fa9ad06727ca0bedd009beaf (patch)
tree62ee568f397706d0032ad5cda53abcb6c766235a
parent177348fbb49631c9ea6ec974c709e9a6530820aa (diff)
downloadrust-5f54cc7658ffb538fa9ad06727ca0bedd009beaf.tar.gz
rust-5f54cc7658ffb538fa9ad06727ca0bedd009beaf.zip
Implement checked_mul
Fixes #6
-rw-r--r--Readme.md2
-rw-r--r--example/std_example.rs23
-rw-r--r--patches/0023-core-Ignore-failing-tests.patch40
-rw-r--r--src/num.rs46
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> = 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::<Vec<_>>(), &[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<T>(ten: T, two: T) where
- }
- 
- #[test]
-+#[ignore] // checked_mul impl not yet checking for overflow
- fn from_str_issue7588() {
-     let u: Option<u8> = 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);