about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard Burtescu <edy.burt@gmail.com>2016-05-27 14:40:05 +0300
committerEduard Burtescu <edy.burt@gmail.com>2016-06-05 14:41:04 +0300
commitd735f6bf33a8654e387d248f46e0ad6d98280a05 (patch)
treeba721ae728b9f7021864771006783bdd984e0b53
parentb8c5053a02f5468e58f856d06586d1a06560d224 (diff)
trans: implement CheckedBinaryOp in mir::constant.
-rw-r--r--src/librustc_trans/mir/constant.rs182
1 files changed, 112 insertions, 70 deletions
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index e32e542ed89..a516a84f0c6 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -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,73 +714,28 @@ 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)?;
-                }
-
-                let llval = unsafe {
-                    match op {
-                        mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
-                        mir::BinOp::Add             => llvm::LLVMConstAdd(lhs, rhs),
-
-                        mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
-                        mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
-
-                        mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
-                        mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
-
-                        mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
-                        mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
-                        mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
+                Const::new(const_scalar_binop(op, lhs, rhs, ty), binop_ty)
+            }
 
-                        mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
-                        mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
-                        mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
+            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());
 
-                        mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
-                        mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
-                        mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
-                        mir::BinOp::Shl    => {
-                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
-                            llvm::LLVMConstShl(lhs, rhs)
-                        }
-                        mir::BinOp::Shr    => {
-                            let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
-                            if signed { llvm::LLVMConstAShr(lhs, rhs) }
-                            else      { llvm::LLVMConstLShr(lhs, rhs) }
-                        }
-                        mir::BinOp::Eq | mir::BinOp::Ne |
-                        mir::BinOp::Lt | mir::BinOp::Le |
-                        mir::BinOp::Gt | mir::BinOp::Ge => {
-                            if is_float {
-                                let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
-                                llvm::ConstFCmp(cmp, lhs, rhs)
-                            } else {
-                                let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
-                                                                         signed);
-                                llvm::ConstICmp(cmp, lhs, rhs)
-                            }
-                        }
+                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)
                     }
-                };
-                Const::new(llval, binop_ty)
+                    None => {
+                        span_bug!(span, "{:?} got non-integer operands: {:?} and {:?}",
+                                  rvalue, Value(lhs), Value(rhs));
+                    }
+                }
             }
 
             mir::Rvalue::UnaryOp(op, ref operand) => {
@@ -792,11 +748,6 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                         }
                     }
                     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 {
@@ -815,6 +766,97 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
 
         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),
+
+            mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
+            mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
+
+            mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
+            mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
+
+            mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
+            mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
+            mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
+
+            mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
+            mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
+            mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
+
+            mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
+            mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
+            mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
+            mir::BinOp::Shl    => {
+                let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                llvm::LLVMConstShl(lhs, rhs)
+            }
+            mir::BinOp::Shr    => {
+                let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
+                if signed { llvm::LLVMConstAShr(lhs, rhs) }
+                else      { llvm::LLVMConstLShr(lhs, rhs) }
+            }
+            mir::BinOp::Eq | mir::BinOp::Ne |
+            mir::BinOp::Lt | mir::BinOp::Le |
+            mir::BinOp::Gt | mir::BinOp::Ge => {
+                if is_float {
+                    let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
+                    llvm::ConstFCmp(cmp, lhs, rhs)
+                } else {
+                    let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
+                                                                signed);
+                    llvm::ConstICmp(cmp, lhs, rhs)
+                }
+            }
+        }
+    }
+}
+
+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());
+            }
+        };
+
+        Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of))
+    } else {
+        None
+    }
 }
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {