trans: implement CheckedBinaryOp in mir::constant.

This commit is contained in:
Eduard Burtescu 2016-05-27 14:40:05 +03:00
parent b8c5053a02
commit d735f6bf33

View File

@ -12,12 +12,13 @@ use llvm::{self, ValueRef};
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::ErrKind;
use rustc_const_math::ConstInt::*;
use rustc_const_math::ConstMathErr;
use rustc::hir::def_id::DefId;
use rustc::infer::TransNormalize;
use rustc::mir::repr as mir;
use rustc::mir::tcx::LvalueTy;
use rustc::traits;
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::cast::{CastTy, IntTy};
use rustc::ty::subst::Substs;
use {abi, adt, base, Disr};
@ -713,28 +714,70 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
let ty = lhs.ty;
let binop_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
let (lhs, rhs) = (lhs.llval, rhs.llval);
assert!(!ty.is_simd());
let is_float = ty.is_fp();
let signed = ty.is_signed();
if let (Some(lhs), Some(rhs)) = (to_const_int(lhs, ty, tcx),
to_const_int(rhs, ty, tcx)) {
let result = match op {
mir::BinOp::Add => lhs + rhs,
mir::BinOp::Sub => lhs - rhs,
mir::BinOp::Mul => lhs * rhs,
mir::BinOp::Div => lhs / rhs,
mir::BinOp::Rem => lhs % rhs,
mir::BinOp::Shl => lhs << rhs,
mir::BinOp::Shr => lhs >> rhs,
_ => Ok(lhs)
};
consts::const_err(self.ccx, span,
result.map_err(ErrKind::Math),
TrueConst::Yes)?;
Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty)
}
let llval = unsafe {
mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => {
let lhs = self.const_operand(lhs, span)?;
let rhs = self.const_operand(rhs, span)?;
let ty = lhs.ty;
let val_ty = self.mir.binop_ty(tcx, op, lhs.ty, rhs.ty);
let binop_ty = tcx.mk_tup(vec![val_ty, tcx.types.bool]);
let (lhs, rhs) = (lhs.llval, rhs.llval);
assert!(!ty.is_fp());
match const_scalar_checked_binop(tcx, op, lhs, rhs, ty) {
Some((llval, of)) => {
let llof = C_bool(self.ccx, of);
Const::new(C_struct(self.ccx, &[llval, llof], false), binop_ty)
}
None => {
span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}",
rvalue, Value(lhs), Value(rhs));
}
}
}
mir::Rvalue::UnaryOp(op, ref operand) => {
let operand = self.const_operand(operand, span)?;
let lloperand = operand.llval;
let llval = match op {
mir::UnOp::Not => {
unsafe {
llvm::LLVMConstNot(lloperand)
}
}
mir::UnOp::Neg => {
let is_float = operand.ty.is_fp();
unsafe {
if is_float {
llvm::LLVMConstFNeg(lloperand)
} else {
llvm::LLVMConstNeg(lloperand)
}
}
}
};
Const::new(llval, operand.ty)
}
_ => span_bug!(span, "{:?} in constant", rvalue)
};
Ok(val)
}
}
pub fn const_scalar_binop(op: mir::BinOp,
lhs: ValueRef,
rhs: ValueRef,
input_ty: Ty) -> ValueRef {
assert!(!input_ty.is_simd());
let is_float = input_ty.is_fp();
let signed = input_ty.is_signed();
unsafe {
match op {
mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
mir::BinOp::Add => llvm::LLVMConstAdd(lhs, rhs),
@ -778,42 +821,41 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
}
}
}
};
Const::new(llval, binop_ty)
}
}
mir::Rvalue::UnaryOp(op, ref operand) => {
let operand = self.const_operand(operand, span)?;
let lloperand = operand.llval;
let llval = match op {
mir::UnOp::Not => {
unsafe {
llvm::LLVMConstNot(lloperand)
pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
op: mir::BinOp,
lllhs: ValueRef,
llrhs: ValueRef,
input_ty: Ty<'tcx>)
-> Option<(ValueRef, bool)> {
if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx),
to_const_int(llrhs, input_ty, tcx)) {
let result = match op {
mir::BinOp::Add => lhs + rhs,
mir::BinOp::Sub => lhs - rhs,
mir::BinOp::Mul => lhs * rhs,
mir::BinOp::Shl => lhs << rhs,
mir::BinOp::Shr => lhs >> rhs,
_ => {
bug!("Operator `{:?}` is not a checkable operator", op)
}
};
let of = match result {
Ok(_) => false,
Err(ConstMathErr::Overflow(_)) |
Err(ConstMathErr::ShiftNegative) => true,
Err(err) => {
bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}",
op, lhs, rhs, err.description());
}
mir::UnOp::Neg => {
if let Some(cval) = to_const_int(lloperand, operand.ty, tcx) {
consts::const_err(self.ccx, span,
(-cval).map_err(ErrKind::Math),
TrueConst::Yes)?;
}
let is_float = operand.ty.is_fp();
unsafe {
if is_float {
llvm::LLVMConstFNeg(lloperand)
};
Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of))
} else {
llvm::LLVMConstNeg(lloperand)
}
}
}
};
Const::new(llval, operand.ty)
}
_ => span_bug!(span, "{:?} in constant", rvalue)
};
Ok(val)
None
}
}