]> git.lizzy.rs Git - rust.git/commitdiff
Implement real saturating behaviour for the saturating_* intrinsics with unsigned...
authorbjorn3 <bjorn3@users.noreply.github.com>
Thu, 8 Aug 2019 13:54:13 +0000 (15:54 +0200)
committerbjorn3 <bjorn3@users.noreply.github.com>
Fri, 9 Aug 2019 10:35:51 +0000 (12:35 +0200)
src/common.rs
src/intrinsics.rs

index 28287390c610b4aecaca56974db3d3aa4db3fe18..c9c733c3f6f6e1474e6094cbb0da63061f200f8f 100644 (file)
@@ -116,6 +116,74 @@ pub fn resolve_value_imm(func: &Function, val: Value) -> Option<u128> {
     }
 }
 
+pub fn type_min_max_value<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (i64, i64) {
+    use syntax::ast::UintTy::*;
+    use syntax::ast::IntTy::*;
+
+    let uint_usize_cvt = |uint| {
+        match uint {
+            UintTy::Usize => match pointer_ty(tcx) {
+                types::I16 => UintTy::U16,
+                types::I32 => UintTy::U32,
+                types::I64 => UintTy::U64,
+                ty => unreachable!("{:?}", ty),
+            }
+            _ => uint,
+        }
+    };
+
+    let int_isize_cvt = |int| {
+        match int {
+            IntTy::Isize => match pointer_ty(tcx) {
+                types::I16 => IntTy::I16,
+                types::I32 => IntTy::I32,
+                types::I64 => IntTy::I64,
+                ty => unreachable!("{:?}", ty),
+            }
+            _ => int,
+        }
+    };
+
+    let min = match ty.sty {
+        ty::Uint(uint) => match uint_usize_cvt(uint) {
+            U8 | U16 | U32 | U64 => 0i64,
+            U128 => unimplemented!(),
+            Usize => unreachable!(),
+        }
+        ty::Int(int) => match int_isize_cvt(int) {
+            I8 => i8::min_value() as i64,
+            I16 => i16::min_value() as i64,
+            I32 => i32::min_value() as i64,
+            I64 => i64::min_value(),
+            I128 => unimplemented!(),
+            Isize => unreachable!(),
+        }
+        _ => unreachable!(),
+    };
+
+    let max = match ty.sty {
+        ty::Uint(uint) => match uint_usize_cvt(uint) {
+            U8 => u8::max_value() as i64,
+            U16 => u16::max_value() as i64,
+            U32 => u32::max_value() as i64,
+            U64 => u64::max_value() as i64,
+            U128 => unimplemented!(),
+            Usize => unreachable!(),
+        }
+        ty::Int(int) => match int_isize_cvt(int) {
+            I8 => i8::max_value() as i64,
+            I16 => i16::max_value() as i64,
+            I32 => i32::max_value() as i64,
+            I64 => i64::max_value(),
+            I128 => unimplemented!(),
+            Isize => unreachable!(),
+        }
+        _ => unreachable!(),
+    };
+
+    (min, max)
+}
+
 pub struct FunctionCx<'a, 'tcx: 'a, B: Backend> {
     // FIXME use a reference to `CodegenCx` instead of `tcx`, `module` and `constants` and `caches`
     pub tcx: TyCtxt<'tcx>,
index a5788bd21ac1c3cc1ab45d99569729aa2f03f8bd..9c5565fa2b53050da7524c3473fd30e602163f99 100644 (file)
@@ -470,25 +470,21 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
                 "mul_with_overflow" => BinOp::Mul,
                 _ => unimplemented!("intrinsic {}", intrinsic),
             };
-            let res = match T.sty {
-                ty::Uint(_) => crate::base::trans_checked_int_binop(
-                    fx,
-                    bin_op,
-                    x,
-                    y,
-                    ret.layout().ty,
-                    false,
-                ),
-                ty::Int(_) => crate::base::trans_checked_int_binop(
-                    fx,
-                    bin_op,
-                    x,
-                    y,
-                    ret.layout().ty,
-                    true,
-                ),
-                _ => panic!(),
+
+            let signed = match T.sty {
+                ty::Uint(_) => false,
+                ty::Int(_) => true,
+                _ => unimplemented!("{} for {:?}", intrinsic, T),
             };
+
+            let res = crate::base::trans_checked_int_binop(
+                fx,
+                bin_op,
+                x,
+                y,
+                ret.layout().ty,
+                signed,
+            );
             ret.write_cvalue(fx, res);
         };
         _ if intrinsic.starts_with("overflowing_"), <T> (c x, c y) {
@@ -526,28 +522,44 @@ pub fn codegen_intrinsic_call<'a, 'tcx: 'a>(
             let bin_op = match intrinsic {
                 "saturating_add" => BinOp::Add,
                 "saturating_sub" => BinOp::Sub,
-                "saturating_mul" => BinOp::Mul,
                 _ => unimplemented!("intrinsic {}", intrinsic),
             };
-            let res = match T.sty {
-                ty::Uint(_) => crate::base::trans_int_binop(
-                    fx,
-                    bin_op,
-                    x,
-                    y,
-                    ret.layout().ty,
-                    false,
-                ),
-                ty::Int(_) => crate::base::trans_int_binop(
-                    fx,
-                    bin_op,
-                    x,
-                    y,
-                    ret.layout().ty,
-                    true,
-                ),
-                _ => panic!(),
+
+            let signed = match T.sty {
+                ty::Uint(_) => false,
+                ty::Int(_) => true,
+                _ => unimplemented!("{} for {:?}", intrinsic, T),
             };
+
+            let checked_res = crate::base::trans_checked_int_binop(
+                fx,
+                bin_op,
+                x,
+                y,
+                fx.tcx.mk_tup([T, fx.tcx.types.bool].into_iter()),
+                signed,
+            );
+
+            let (val, has_overflow) = checked_res.load_scalar_pair(fx);
+            let clif_ty = fx.clif_type(T).unwrap();
+
+            // `select.i8` is not implemented by Cranelift.
+            let has_overflow = fx.bcx.ins().uextend(types::I32, has_overflow);
+
+            let (min, max) = type_min_max_value(fx.tcx, T);
+            let min = fx.bcx.ins().iconst(clif_ty, min);
+            let max = fx.bcx.ins().iconst(clif_ty, max);
+
+            let val = match (intrinsic, signed) {
+                ("saturating_add", false) => fx.bcx.ins().select(has_overflow, max, val),
+                ("saturating_sub", false) => fx.bcx.ins().select(has_overflow, min, val),
+                ("saturating_add", true) => unimplemented!(),
+                ("saturating_sub", true) => unimplemented!(),
+                _ => unreachable!(),
+            };
+
+            let res = CValue::by_val(val, fx.layout_of(T));
+
             ret.write_cvalue(fx, res);
         };
         rotate_left, <T>(v x, v y) {