diff --git a/src/common.rs b/src/common.rs index 28287390c610..c9c733c3f6f6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -116,6 +116,74 @@ pub fn resolve_value_imm(func: &Function, val: Value) -> Option { } } +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>, diff --git a/src/intrinsics.rs b/src/intrinsics.rs index a5788bd21ac1..9c5565fa2b53 100644 --- a/src/intrinsics.rs +++ b/src/intrinsics.rs @@ -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_"), (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, (v x, v y) {