]> git.lizzy.rs Git - rust.git/blobdiff - src/num.rs
Merge pull request #1056 from bjorn3/misc_fixes
[rust.git] / src / num.rs
index 103c15eca4136c781e409523d46a7ddce49340b8..22269b5ee29e596a8029cfcbcec4ffcdbaf979be 100644 (file)
@@ -1,6 +1,6 @@
 use crate::prelude::*;
 
-pub fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option<IntCC> {
+pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option<IntCC> {
     use BinOp::*;
     use IntCC::*;
     Some(match bin_op {
@@ -46,12 +46,12 @@ fn codegen_compare_bin_op<'tcx>(
     rhs: Value,
 ) -> CValue<'tcx> {
     let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap();
-    let val = codegen_icmp(fx, intcc, lhs, rhs);
+    let val = fx.bcx.ins().icmp(intcc, lhs, rhs);
     let val = fx.bcx.ins().bint(types::I8, val);
     CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
 }
 
-pub fn codegen_binop<'tcx>(
+pub(crate) fn codegen_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,
@@ -100,7 +100,7 @@ pub fn codegen_binop<'tcx>(
     }
 }
 
-pub fn trans_bool_binop<'tcx>(
+pub(crate) fn trans_bool_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,
@@ -121,7 +121,7 @@ pub fn trans_bool_binop<'tcx>(
     CValue::by_val(res, fx.layout_of(fx.tcx.types.bool))
 }
 
-pub fn trans_int_binop<'tcx>(
+pub(crate) fn trans_int_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,
@@ -168,16 +168,18 @@ pub fn trans_int_binop<'tcx>(
         BinOp::BitOr => b.bor(lhs, rhs),
         BinOp::Shl => {
             let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
-            let rhs = clif_intcast(fx, rhs, lhs_ty, false);
-            fx.bcx.ins().ishl(lhs, rhs)
+            let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
+            let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
+            fx.bcx.ins().ishl(lhs, actual_shift)
         }
         BinOp::Shr => {
             let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
-            let rhs = clif_intcast(fx, rhs, lhs_ty, false);
+            let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
+            let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
             if signed {
-                fx.bcx.ins().sshr(lhs, rhs)
+                fx.bcx.ins().sshr(lhs, actual_shift)
             } else {
-                fx.bcx.ins().ushr(lhs, rhs)
+                fx.bcx.ins().ushr(lhs, actual_shift)
             }
         }
         // Compare binops handles by `codegen_binop`.
@@ -192,7 +194,7 @@ pub fn trans_int_binop<'tcx>(
     CValue::by_val(val, in_lhs.layout())
 }
 
-pub fn trans_checked_int_binop<'tcx>(
+pub(crate) fn trans_checked_int_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,
@@ -245,19 +247,48 @@ pub 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);
+            let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
+            let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
+            let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
+            let val = fx.bcx.ins().ishl(lhs, actual_shift);
             let ty = fx.bcx.func.dfg.value_type(val);
             let max_shift = i64::from(ty.bits()) - 1;
             let has_overflow =
@@ -265,10 +296,13 @@ pub fn trans_checked_int_binop<'tcx>(
             (val, has_overflow)
         }
         BinOp::Shr => {
+            let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
+            let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1));
+            let actual_shift = clif_intcast(fx, actual_shift, types::I8, false);
             let val = if !signed {
-                fx.bcx.ins().ushr(lhs, rhs)
+                fx.bcx.ins().ushr(lhs, actual_shift)
             } else {
-                fx.bcx.ins().sshr(lhs, rhs)
+                fx.bcx.ins().sshr(lhs, actual_shift)
             };
             let ty = fx.bcx.func.dfg.value_type(val);
             let max_shift = i64::from(ty.bits()) - 1;
@@ -297,7 +331,7 @@ pub fn trans_checked_int_binop<'tcx>(
     out_place.to_cvalue(fx)
 }
 
-pub fn trans_float_binop<'tcx>(
+pub(crate) fn trans_float_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,
@@ -342,7 +376,7 @@ pub fn trans_float_binop<'tcx>(
     CValue::by_val(res, in_lhs.layout())
 }
 
-pub fn trans_ptr_binop<'tcx>(
+pub(crate) fn trans_ptr_binop<'tcx>(
     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
     bin_op: BinOp,
     in_lhs: CValue<'tcx>,