Check arithmetic in the MIR
Add, Sub, Mul, Shl, and Shr are checked using a new Rvalue: CheckedBinaryOp, while Div, Rem and Neg are handled with explicit checks in the MIR.
This commit is contained in:
parent
f97c411548
commit
73f3054288
@ -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 } => {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<Rvalue<'tcx>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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, _, _) => {}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user