diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 1cd837e4853b0..2f16878a94cf9 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -787,6 +787,7 @@ pub enum Rvalue<'tcx> { Cast(CastKind, Operand<'tcx>, Ty<'tcx>), BinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), + CheckedBinaryOp(BinOp, Operand<'tcx>, Operand<'tcx>), UnaryOp(UnOp, Operand<'tcx>), @@ -880,6 +881,16 @@ pub enum BinOp { Gt, } +impl BinOp { + pub fn is_checkable(self) -> bool { + use self::BinOp::*; + match self { + Add | Sub | Mul | Shl | Shr => true, + _ => false + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum UnOp { /// The `!` operator for logical inversion @@ -898,6 +909,9 @@ impl<'tcx> Debug for Rvalue<'tcx> { Len(ref a) => write!(fmt, "Len({:?})", a), Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?})", lv, ty, kind), BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + CheckedBinaryOp(ref op, ref a, ref b) => { + write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) + } UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), Box(ref t) => write!(fmt, "Box({:?})", t), InlineAsm { ref asm, ref outputs, ref inputs } => { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index a1c0d92f60cd6..d0ac98a79587a 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -183,6 +183,13 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> { let rhs_ty = self.operand_ty(tcx, rhs); Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty)) } + Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + let lhs_ty = self.operand_ty(tcx, lhs); + let rhs_ty = self.operand_ty(tcx, rhs); + let ty = self.binop_ty(tcx, op, lhs_ty, rhs_ty); + let ty = tcx.mk_tup(vec![ty, tcx.types.bool]); + Some(ty) + } Rvalue::UnaryOp(_, ref operand) => { Some(self.operand_ty(tcx, operand)) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 17a8d040ab478..2f290c813fdce 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -461,6 +461,9 @@ macro_rules! make_mir_visitor { } Rvalue::BinaryOp(_bin_op, + ref $($mutability)* lhs, + ref $($mutability)* rhs) | + Rvalue::CheckedBinaryOp(_bin_op, ref $($mutability)* lhs, ref $($mutability)* rhs) => { self.visit_operand(lhs); diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index fcaa655f749d5..de806eef36f8f 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -595,7 +595,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD bb_ctxt.on_operand(SK::Repeat, operand, source), Rvalue::Cast(ref _kind, ref operand, ref _ty) => bb_ctxt.on_operand(SK::Cast, operand, source), - Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) => { + Rvalue::BinaryOp(ref _binop, ref operand1, ref operand2) | + Rvalue::CheckedBinaryOp(ref _binop, ref operand1, ref operand2) => { bb_ctxt.on_operand(SK::BinaryOp, operand1, source); bb_ctxt.on_operand(SK::BinaryOp, operand2, source); } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 2a73346240898..b5b4a01dcc641 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -10,12 +10,19 @@ //! See docs in build/expr/mod.rs +use std; + use rustc_data_structures::fnv::FnvHashMap; use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::{Category, RvalueFunc}; use hair::*; +use rustc_const_math::{ConstInt, ConstIsize}; +use rustc::middle::const_val::ConstVal; +use rustc::ty; use rustc::mir::repr::*; +use syntax::ast; +use syntax::codemap::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr`, yielding an rvalue. @@ -66,10 +73,34 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!(block = this.as_operand(block, lhs)); let rhs = unpack!(block = this.as_operand(block, rhs)); - block.and(Rvalue::BinaryOp(op, lhs, rhs)) + this.build_binary_op(block, op, expr_span, expr.ty, + lhs, rhs) } ExprKind::Unary { op, arg } => { let arg = unpack!(block = this.as_operand(block, arg)); + // Check for -MIN on signed integers + if op == UnOp::Neg && this.check_overflow() && expr.ty.is_signed() { + let bool_ty = this.hir.bool_ty(); + + let minval = this.minval_literal(expr_span, expr.ty); + let is_min = this.temp(bool_ty); + + this.cfg.push_assign(block, scope_id, expr_span, &is_min, + Rvalue::BinaryOp(BinOp::Eq, arg.clone(), minval)); + + let of_block = this.cfg.start_new_block(); + let ok_block = this.cfg.start_new_block(); + + this.cfg.terminate(block, scope_id, expr_span, + TerminatorKind::If { + cond: Operand::Consume(is_min), + targets: (of_block, ok_block) + }); + + this.panic(of_block, "attempted to negate with overflow", expr_span); + + block = ok_block; + } block.and(Rvalue::UnaryOp(op, arg)) } ExprKind::Box { value, value_extents } => { @@ -218,4 +249,166 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } + + pub fn build_binary_op(&mut self, mut block: BasicBlock, op: BinOp, span: Span, ty: ty::Ty<'tcx>, + lhs: Operand<'tcx>, rhs: Operand<'tcx>) -> BlockAnd> { + let scope_id = self.innermost_scope_id(); + let bool_ty = self.hir.bool_ty(); + if self.check_overflow() && op.is_checkable() && ty.is_integral() { + let result_tup = self.hir.tcx().mk_tup(vec![ty, bool_ty]); + let result_value = self.temp(result_tup); + + self.cfg.push_assign(block, scope_id, span, + &result_value, Rvalue::CheckedBinaryOp(op, + lhs, + rhs)); + let val_fld = Field::new(0); + let of_fld = Field::new(1); + + let val = result_value.clone().field(val_fld, ty); + let of = result_value.field(of_fld, bool_ty); + + let success = self.cfg.start_new_block(); + let failure = self.cfg.start_new_block(); + + self.cfg.terminate(block, scope_id, span, + TerminatorKind::If { + cond: Operand::Consume(of), + targets: (failure, success) + }); + let msg = if op == BinOp::Shl || op == BinOp::Shr { + "shift operation overflowed" + } else { + "arithmetic operation overflowed" + }; + self.panic(failure, msg, span); + success.and(Rvalue::Use(Operand::Consume(val))) + } else { + if ty.is_integral() && (op == BinOp::Div || op == BinOp::Rem) { + // Checking division and remainder is more complex, since we 1. always check + // and 2. there are two possible failure cases, divide-by-zero and overflow. + + let (zero_msg, overflow_msg) = if op == BinOp::Div { + ("attempted to divide by zero", + "attempted to divide with overflow") + } else { + ("attempted remainder with a divisor of zero", + "attempted remainder with overflow") + }; + + // Check for / 0 + let is_zero = self.temp(bool_ty); + let zero = self.zero_literal(span, ty); + self.cfg.push_assign(block, scope_id, span, &is_zero, + Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), zero)); + + let zero_block = self.cfg.start_new_block(); + let ok_block = self.cfg.start_new_block(); + + self.cfg.terminate(block, scope_id, span, + TerminatorKind::If { + cond: Operand::Consume(is_zero), + targets: (zero_block, ok_block) + }); + + self.panic(zero_block, zero_msg, span); + block = ok_block; + + // We only need to check for the overflow in one case: + // MIN / -1, and only for signed values. + if ty.is_signed() { + let neg_1 = self.neg_1_literal(span, ty); + let min = self.minval_literal(span, ty); + + let is_neg_1 = self.temp(bool_ty); + let is_min = self.temp(bool_ty); + let of = self.temp(bool_ty); + + // this does (rhs == -1) & (lhs == MIN). It could short-circuit instead + + self.cfg.push_assign(block, scope_id, span, &is_neg_1, + Rvalue::BinaryOp(BinOp::Eq, rhs.clone(), neg_1)); + self.cfg.push_assign(block, scope_id, span, &is_min, + Rvalue::BinaryOp(BinOp::Eq, lhs.clone(), min)); + + let is_neg_1 = Operand::Consume(is_neg_1); + let is_min = Operand::Consume(is_min); + self.cfg.push_assign(block, scope_id, span, &of, + Rvalue::BinaryOp(BinOp::BitAnd, is_neg_1, is_min)); + + let of_block = self.cfg.start_new_block(); + let ok_block = self.cfg.start_new_block(); + + self.cfg.terminate(block, scope_id, span, + TerminatorKind::If { + cond: Operand::Consume(of), + targets: (of_block, ok_block) + }); + + self.panic(of_block, overflow_msg, span); + + block = ok_block; + } + } + + block.and(Rvalue::BinaryOp(op, lhs, rhs)) + } + } + + // Helper to get a `-1` value of the appropriate type + fn neg_1_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> { + let literal = match ty.sty { + ty::TyInt(ity) => { + let val = match ity { + ast::IntTy::I8 => ConstInt::I8(-1), + ast::IntTy::I16 => ConstInt::I16(-1), + ast::IntTy::I32 => ConstInt::I32(-1), + ast::IntTy::I64 => ConstInt::I64(-1), + ast::IntTy::Is => { + let int_ty = self.hir.tcx().sess.target.int_type; + let val = ConstIsize::new(-1, int_ty).unwrap(); + ConstInt::Isize(val) + } + }; + + Literal::Value { value: ConstVal::Integral(val) } + } + _ => { + span_bug!(span, "Invalid type for neg_1_literal: `{:?}`", ty) + } + }; + + self.literal_operand(span, ty, literal) + } + + // Helper to get the minimum value of the appropriate type + fn minval_literal(&mut self, span: Span, ty: ty::Ty<'tcx>) -> Operand<'tcx> { + let literal = match ty.sty { + ty::TyInt(ity) => { + let val = match ity { + ast::IntTy::I8 => ConstInt::I8(std::i8::MIN), + ast::IntTy::I16 => ConstInt::I16(std::i16::MIN), + ast::IntTy::I32 => ConstInt::I32(std::i32::MIN), + ast::IntTy::I64 => ConstInt::I64(std::i64::MIN), + ast::IntTy::Is => { + let int_ty = self.hir.tcx().sess.target.int_type; + let min = match int_ty { + ast::IntTy::I32 => std::i32::MIN as i64, + ast::IntTy::I64 => std::i64::MIN, + _ => unreachable!() + }; + let val = ConstIsize::new(min, int_ty).unwrap(); + ConstInt::Isize(val) + } + }; + + Literal::Value { value: ConstVal::Integral(val) } + } + _ => { + span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty) + } + }; + + self.literal_operand(span, ty, literal) + } } diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index 3324467e70d1d..24369aaff3f5c 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -63,6 +63,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // only affects weird things like `x += {x += 1; x}` // -- is that equal to `x + (x + 1)` or `2*(x+1)`? + let lhs = this.hir.mirror(lhs); + let lhs_ty = lhs.ty; + // As above, RTL. let rhs = unpack!(block = this.as_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); @@ -70,10 +73,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // we don't have to drop prior contents or anything // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). - this.cfg.push_assign(block, scope_id, expr_span, &lhs, - Rvalue::BinaryOp(op, - Operand::Consume(lhs.clone()), - rhs)); + let result = unpack!(block = this.build_binary_op(block, op, expr_span, lhs_ty, + Operand::Consume(lhs.clone()), rhs)); + this.cfg.push_assign(block, scope_id, expr_span, &lhs, result); block.unit() } diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 7317c6f9b3133..00e8909527631 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -12,9 +12,14 @@ //! kind of thing. use build::Builder; -use rustc::ty::Ty; + +use rustc_const_math::{ConstInt, ConstUsize, ConstIsize}; +use rustc::middle::const_val::ConstVal; +use rustc::ty::{self, Ty}; + use rustc::mir::repr::*; use std::u32; +use syntax::ast; use syntax::codemap::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -50,6 +55,53 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Rvalue::Aggregate(AggregateKind::Tuple, vec![]) } + // Returns a zero literal operand for the appropriate type, works for + // bool, char, integers and floats. + pub fn zero_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { + let literal = match ty.sty { + ty::TyBool => { + self.hir.false_literal() + } + ty::TyChar => Literal::Value { value: ConstVal::Char('\0') }, + ty::TyUint(ity) => { + let val = match ity { + ast::UintTy::U8 => ConstInt::U8(0), + ast::UintTy::U16 => ConstInt::U16(0), + ast::UintTy::U32 => ConstInt::U32(0), + ast::UintTy::U64 => ConstInt::U64(0), + ast::UintTy::Us => { + let uint_ty = self.hir.tcx().sess.target.uint_type; + let val = ConstUsize::new(0, uint_ty).unwrap(); + ConstInt::Usize(val) + } + }; + + Literal::Value { value: ConstVal::Integral(val) } + } + ty::TyInt(ity) => { + let val = match ity { + ast::IntTy::I8 => ConstInt::I8(0), + ast::IntTy::I16 => ConstInt::I16(0), + ast::IntTy::I32 => ConstInt::I32(0), + ast::IntTy::I64 => ConstInt::I64(0), + ast::IntTy::Is => { + let int_ty = self.hir.tcx().sess.target.int_type; + let val = ConstIsize::new(0, int_ty).unwrap(); + ConstInt::Isize(val) + } + }; + + Literal::Value { value: ConstVal::Integral(val) } + } + ty::TyFloat(_) => Literal::Value { value: ConstVal::Float(0.0) }, + _ => { + span_bug!(span, "Invalid type for zero_literal: `{:?}`", ty) + } + }; + + self.literal_operand(span, ty, literal) + } + pub fn push_usize(&mut self, block: BasicBlock, scope_id: ScopeId, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 9d7818a9ba4d6..3faf95331ddab 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -378,6 +378,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } } + + pub fn check_overflow(&self) -> bool { + self.hir.tcx().sess.opts.debugging_opts.force_overflow_checks.unwrap_or( + self.hir.tcx().sess.opts.debug_assertions) + } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 18a1f1595f3c3..57fdfb281d2ef 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -630,6 +630,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::Use(_) | Rvalue::Repeat(..) | Rvalue::UnaryOp(..) | + Rvalue::CheckedBinaryOp(..) | Rvalue::Cast(CastKind::ReifyFnPointer, _, _) | Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) | Rvalue::Cast(CastKind::Unsize, _, _) => {} diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 6d141862ac3ff..cbd39fffce801 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::ValueRef; +use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::mir::repr as mir; @@ -16,7 +16,9 @@ use rustc::mir::repr as mir; use asm; use base; use callee::Callee; -use common::{self, C_uint, BlockAndBuilder, Result}; +use common::{self, val_ty, + C_null, + C_uint, C_undef, C_u8, BlockAndBuilder, Result}; use datum::{Datum, Lvalue}; use debuginfo::DebugLoc; use adt; @@ -430,6 +432,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; (bcx, operand) } + mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.trans_operand(&bcx, lhs); + let rhs = self.trans_operand(&bcx, rhs); + let result = self.trans_scalar_checked_binop(&bcx, op, + lhs.immediate(), rhs.immediate(), + lhs.ty); + let val_ty = self.mir.binop_ty(bcx.tcx(), op, lhs.ty, rhs.ty); + let operand_ty = bcx.tcx().mk_tup(vec![val_ty, bcx.tcx().types.bool]); + let operand = OperandRef { + val: OperandValue::Immediate(result), + ty: operand_ty + }; + + (bcx, operand) + } mir::Rvalue::UnaryOp(op, ref operand) => { let operand = self.trans_operand(&bcx, operand); @@ -556,6 +573,57 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } + + pub fn trans_scalar_checked_binop(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>) -> ValueRef { + let (val, of) = match op { + // These are checked using intrinsics + mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { + let oop = match op { + mir::BinOp::Add => OverflowOp::Add, + mir::BinOp::Sub => OverflowOp::Sub, + mir::BinOp::Mul => OverflowOp::Mul, + _ => unreachable!() + }; + let intrinsic = get_overflow_intrinsic(oop, bcx, input_ty); + let res = bcx.call(intrinsic, &[lhs, rhs], None); + + let val = bcx.extract_value(res, 0); + let of = bcx.extract_value(res, 1); + + (val, bcx.zext(of, Type::bool(bcx.ccx()))) + } + mir::BinOp::Shl | mir::BinOp::Shr => { + let lhs_llty = val_ty(lhs); + let rhs_llty = val_ty(rhs); + let invert_mask = bcx.with_block(|bcx| { + common::shift_mask_val(bcx, lhs_llty, rhs_llty, true) + }); + let outer_bits = bcx.and(rhs, invert_mask); + + let of = bcx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); + let val = self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty); + + (val, bcx.zext(of, Type::bool(bcx.ccx()))) + } + _ => { + // Fall back to regular translation with a constant-false overflow flag + (self.trans_scalar_binop(bcx, op, lhs, rhs, input_ty), + C_u8(bcx.ccx(), 0)) + } + }; + + let val_ty = val_ty(val); + let res_ty = Type::struct_(bcx.ccx(), &[val_ty, Type::bool(bcx.ccx())], false); + + let mut res_val = C_undef(res_ty); + res_val = bcx.insert_value(res_val, val, 0); + bcx.insert_value(res_val, of, 1) + } } pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, @@ -566,6 +634,7 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, mir::Rvalue::Len(..) | mir::Rvalue::Cast(..) | // (*) mir::Rvalue::BinaryOp(..) | + mir::Rvalue::CheckedBinaryOp(..) | mir::Rvalue::UnaryOp(..) | mir::Rvalue::Box(..) | mir::Rvalue::Use(..) => @@ -579,3 +648,75 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, // (*) this is only true if the type is suitable } + +#[derive(Copy, Clone)] +enum OverflowOp { + Add, Sub, Mul +} + +fn get_overflow_intrinsic(oop: OverflowOp, bcx: &BlockAndBuilder, ty: Ty) -> ValueRef { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + use rustc::ty::{TyInt, TyUint}; + + let tcx = bcx.tcx(); + + let new_sty = match ty.sty { + TyInt(Is) => match &tcx.sess.target.target.target_pointer_width[..] { + "32" => TyInt(I32), + "64" => TyInt(I64), + _ => panic!("unsupported target word size") + }, + TyUint(Us) => match &tcx.sess.target.target.target_pointer_width[..] { + "32" => TyUint(U32), + "64" => TyUint(U64), + _ => panic!("unsupported target word size") + }, + ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(), + _ => panic!("tried to get overflow intrinsic for op applied to non-int type") + }; + + let name = match oop { + OverflowOp::Add => match new_sty { + TyInt(I8) => "llvm.sadd.with.overflow.i8", + TyInt(I16) => "llvm.sadd.with.overflow.i16", + TyInt(I32) => "llvm.sadd.with.overflow.i32", + TyInt(I64) => "llvm.sadd.with.overflow.i64", + + TyUint(U8) => "llvm.uadd.with.overflow.i8", + TyUint(U16) => "llvm.uadd.with.overflow.i16", + TyUint(U32) => "llvm.uadd.with.overflow.i32", + TyUint(U64) => "llvm.uadd.with.overflow.i64", + + _ => unreachable!(), + }, + OverflowOp::Sub => match new_sty { + TyInt(I8) => "llvm.ssub.with.overflow.i8", + TyInt(I16) => "llvm.ssub.with.overflow.i16", + TyInt(I32) => "llvm.ssub.with.overflow.i32", + TyInt(I64) => "llvm.ssub.with.overflow.i64", + + TyUint(U8) => "llvm.usub.with.overflow.i8", + TyUint(U16) => "llvm.usub.with.overflow.i16", + TyUint(U32) => "llvm.usub.with.overflow.i32", + TyUint(U64) => "llvm.usub.with.overflow.i64", + + _ => unreachable!(), + }, + OverflowOp::Mul => match new_sty { + TyInt(I8) => "llvm.smul.with.overflow.i8", + TyInt(I16) => "llvm.smul.with.overflow.i16", + TyInt(I32) => "llvm.smul.with.overflow.i32", + TyInt(I64) => "llvm.smul.with.overflow.i64", + + TyUint(U8) => "llvm.umul.with.overflow.i8", + TyUint(U16) => "llvm.umul.with.overflow.i16", + TyUint(U32) => "llvm.umul.with.overflow.i32", + TyUint(U64) => "llvm.umul.with.overflow.i64", + + _ => unreachable!(), + }, + }; + + bcx.ccx().get_intrinsic(&name) +}