Implement checked_mul

Fixes 
This commit is contained in:
bjorn3 2020-06-20 15:15:28 +02:00
parent 177348fbb4
commit 5f54cc7658
4 changed files with 60 additions and 51 deletions

@ -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)

@ -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(()),

@ -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)

@ -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);