about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJonathan Turner <jonathandturner@users.noreply.github.com>2016-10-06 08:35:42 -0700
committerGitHub <noreply@github.com>2016-10-06 08:35:42 -0700
commited5a3c01a03da160b179166d2c1c839afac0b857 (patch)
tree1042de5f04e262e9b8e2c48ff430e3cc06893b09 /src
parent1a7aa75332083700d71436d8af296a98491f221a (diff)
parent45fe3a1a2ab2671bb9f726941eda6c2899eb6dff (diff)
Rollup merge of #36962 - arielb1:cast-assumptions, r=eddyb
Emit more assumptions in trans

Perf numbers pending.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_trans/base.rs175
-rw-r--r--src/librustc_trans/mir/block.rs4
-rw-r--r--src/librustc_trans/mir/lvalue.rs14
-rw-r--r--src/librustc_trans/mir/mod.rs14
-rw-r--r--src/librustc_trans/mir/operand.rs20
-rw-r--r--src/librustc_trans/mir/rvalue.rs118
-rw-r--r--src/test/codegen/enum-bounds-check.rs24
7 files changed, 193 insertions, 176 deletions
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index d80ed5e27cc..e0e808f2dcc 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -183,6 +183,14 @@ pub fn get_dataptr(bcx: Block, fat_ptr: ValueRef) -> ValueRef {
     StructGEP(bcx, fat_ptr, abi::FAT_PTR_ADDR)
 }
 
+pub fn get_meta_builder(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
+    b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA)
+}
+
+pub fn get_dataptr_builder(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
+    b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR)
+}
+
 fn require_alloc_fn<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, info_ty: Ty<'tcx>, it: LangItem) -> DefId {
     match bcx.tcx().lang_items.require(it) {
         Ok(id) => id,
@@ -247,124 +255,6 @@ pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate {
     }
 }
 
-pub fn compare_fat_ptrs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                    lhs_addr: ValueRef,
-                                    lhs_extra: ValueRef,
-                                    rhs_addr: ValueRef,
-                                    rhs_extra: ValueRef,
-                                    _t: Ty<'tcx>,
-                                    op: hir::BinOp_,
-                                    debug_loc: DebugLoc)
-                                    -> ValueRef {
-    match op {
-        hir::BiEq => {
-            let addr_eq = ICmp(bcx, llvm::IntEQ, lhs_addr, rhs_addr, debug_loc);
-            let extra_eq = ICmp(bcx, llvm::IntEQ, lhs_extra, rhs_extra, debug_loc);
-            And(bcx, addr_eq, extra_eq, debug_loc)
-        }
-        hir::BiNe => {
-            let addr_eq = ICmp(bcx, llvm::IntNE, lhs_addr, rhs_addr, debug_loc);
-            let extra_eq = ICmp(bcx, llvm::IntNE, lhs_extra, rhs_extra, debug_loc);
-            Or(bcx, addr_eq, extra_eq, debug_loc)
-        }
-        hir::BiLe | hir::BiLt | hir::BiGe | hir::BiGt => {
-            // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1)
-            let (op, strict_op) = match op {
-                hir::BiLt => (llvm::IntULT, llvm::IntULT),
-                hir::BiLe => (llvm::IntULE, llvm::IntULT),
-                hir::BiGt => (llvm::IntUGT, llvm::IntUGT),
-                hir::BiGe => (llvm::IntUGE, llvm::IntUGT),
-                _ => bug!(),
-            };
-
-            let addr_eq = ICmp(bcx, llvm::IntEQ, lhs_addr, rhs_addr, debug_loc);
-            let extra_op = ICmp(bcx, op, lhs_extra, rhs_extra, debug_loc);
-            let addr_eq_extra_op = And(bcx, addr_eq, extra_op, debug_loc);
-
-            let addr_strict = ICmp(bcx, strict_op, lhs_addr, rhs_addr, debug_loc);
-            Or(bcx, addr_strict, addr_eq_extra_op, debug_loc)
-        }
-        _ => {
-            bug!("unexpected fat ptr binop");
-        }
-    }
-}
-
-pub fn compare_scalar_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-                                        lhs: ValueRef,
-                                        rhs: ValueRef,
-                                        t: Ty<'tcx>,
-                                        op: hir::BinOp_,
-                                        debug_loc: DebugLoc)
-                                        -> ValueRef {
-    match t.sty {
-        ty::TyTuple(ref tys) if tys.is_empty() => {
-            // We don't need to do actual comparisons for nil.
-            // () == () holds but () < () does not.
-            match op {
-                hir::BiEq | hir::BiLe | hir::BiGe => return C_bool(bcx.ccx(), true),
-                hir::BiNe | hir::BiLt | hir::BiGt => return C_bool(bcx.ccx(), false),
-                // refinements would be nice
-                _ => bug!("compare_scalar_types: must be a comparison operator"),
-            }
-        }
-        ty::TyBool => {
-            // FIXME(#36856) -- using `from_immediate` forces these booleans into `i8`,
-            // which works around some LLVM bugs
-            ICmp(bcx,
-                 bin_op_to_icmp_predicate(op, false),
-                 from_immediate(bcx, lhs),
-                 from_immediate(bcx, rhs),
-                 debug_loc)
-        }
-        ty::TyFnDef(..) | ty::TyFnPtr(_) | ty::TyUint(_) | ty::TyChar => {
-            ICmp(bcx,
-                 bin_op_to_icmp_predicate(op, false),
-                 lhs,
-                 rhs,
-                 debug_loc)
-        }
-        ty::TyRawPtr(mt) if common::type_is_sized(bcx.tcx(), mt.ty) => {
-            ICmp(bcx,
-                 bin_op_to_icmp_predicate(op, false),
-                 lhs,
-                 rhs,
-                 debug_loc)
-        }
-        ty::TyRawPtr(_) => {
-            let lhs_addr = Load(bcx, GEPi(bcx, lhs, &[0, abi::FAT_PTR_ADDR]));
-            let lhs_extra = Load(bcx, GEPi(bcx, lhs, &[0, abi::FAT_PTR_EXTRA]));
-
-            let rhs_addr = Load(bcx, GEPi(bcx, rhs, &[0, abi::FAT_PTR_ADDR]));
-            let rhs_extra = Load(bcx, GEPi(bcx, rhs, &[0, abi::FAT_PTR_EXTRA]));
-            compare_fat_ptrs(bcx,
-                             lhs_addr,
-                             lhs_extra,
-                             rhs_addr,
-                             rhs_extra,
-                             t,
-                             op,
-                             debug_loc)
-        }
-        ty::TyInt(_) => {
-            ICmp(bcx,
-                 bin_op_to_icmp_predicate(op, true),
-                 lhs,
-                 rhs,
-                 debug_loc)
-        }
-        ty::TyFloat(_) => {
-            FCmp(bcx,
-                 bin_op_to_fcmp_predicate(op),
-                 lhs,
-                 rhs,
-                 debug_loc)
-        }
-        // Should never get here, because t is scalar.
-        _ => bug!("non-scalar type passed to compare_scalar_types"),
-    }
-}
-
 pub fn compare_simd_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                       lhs: ValueRef,
                                       rhs: ValueRef,
@@ -632,6 +522,11 @@ pub fn need_invoke(bcx: Block) -> bool {
     }
 }
 
+pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) {
+    let assume_intrinsic = b.ccx.get_intrinsic("llvm.assume");
+    b.call(assume_intrinsic, &[val], None);
+}
+
 /// Helper for loading values from memory. Does the necessary conversion if the in-memory type
 /// differs from the type used for SSA values. Also handles various special cases where the type
 /// gives us better information about what we are loading.
@@ -685,12 +580,9 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
     debug!("store_ty: {:?} : {:?} <- {:?}", Value(dst), t, Value(v));
 
     if common::type_is_fat_ptr(cx.tcx(), t) {
-        Store(cx,
-              ExtractValue(cx, v, abi::FAT_PTR_ADDR),
-              get_dataptr(cx, dst));
-        Store(cx,
-              ExtractValue(cx, v, abi::FAT_PTR_EXTRA),
-              get_meta(cx, dst));
+        let lladdr = ExtractValue(cx, v, abi::FAT_PTR_ADDR);
+        let llextra = ExtractValue(cx, v, abi::FAT_PTR_EXTRA);
+        store_fat_ptr(cx, lladdr, llextra, dst, t);
     } else {
         Store(cx, from_immediate(cx, v), dst);
     }
@@ -708,11 +600,36 @@ pub fn store_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
 
 pub fn load_fat_ptr<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
                                 src: ValueRef,
-                                _ty: Ty<'tcx>)
-                                -> (ValueRef, ValueRef) {
-    // FIXME: emit metadata
-    (Load(cx, get_dataptr(cx, src)),
-     Load(cx, get_meta(cx, src)))
+                                ty: Ty<'tcx>)
+                                -> (ValueRef, ValueRef)
+{
+    if cx.unreachable.get() {
+        // FIXME: remove me
+        return (Load(cx, get_dataptr(cx, src)),
+                Load(cx, get_meta(cx, src)));
+    }
+
+    load_fat_ptr_builder(&B(cx), src, ty)
+}
+
+pub fn load_fat_ptr_builder<'a, 'tcx>(
+    b: &Builder<'a, 'tcx>,
+    src: ValueRef,
+    t: Ty<'tcx>)
+    -> (ValueRef, ValueRef)
+{
+
+    let ptr = get_dataptr_builder(b, src);
+    let ptr = if t.is_region_ptr() || t.is_unique() {
+        b.load_nonnull(ptr)
+    } else {
+        b.load(ptr)
+    };
+
+    // FIXME: emit metadata on `meta`.
+    let meta = b.load(get_meta_builder(b, src));
+
+    (ptr, meta)
 }
 
 pub fn from_immediate(bcx: Block, val: ValueRef) -> ValueRef {
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index 9e2d947c5e5..9edb489decc 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -35,7 +35,7 @@ use syntax::parse::token;
 use super::{MirContext, LocalRef};
 use super::analyze::CleanupKind;
 use super::constant::Const;
-use super::lvalue::{LvalueRef, load_fat_ptr};
+use super::lvalue::{LvalueRef};
 use super::operand::OperandRef;
 use super::operand::OperandValue::*;
 
@@ -703,7 +703,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 for (n, &ty) in arg_types.iter().enumerate() {
                     let ptr = adt::trans_field_ptr_builder(bcx, tuple.ty, base, Disr(0), n);
                     let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
-                        let (lldata, llextra) = load_fat_ptr(bcx, ptr);
+                        let (lldata, llextra) = base::load_fat_ptr_builder(bcx, ptr, ty);
                         Pair(lldata, llextra)
                     } else {
                         // trans_argument will load this if it needs to
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 7102bd81caf..8e65eac2e80 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -13,10 +13,8 @@ use rustc::ty::{self, Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
 use rustc_data_structures::indexed_vec::Idx;
-use abi;
 use adt;
 use base;
-use builder::Builder;
 use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef};
 use consts;
 use machine;
@@ -69,18 +67,6 @@ impl<'tcx> LvalueRef<'tcx> {
     }
 }
 
-pub fn get_meta(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
-    b.struct_gep(fat_ptr, abi::FAT_PTR_EXTRA)
-}
-
-pub fn get_dataptr(b: &Builder, fat_ptr: ValueRef) -> ValueRef {
-    b.struct_gep(fat_ptr, abi::FAT_PTR_ADDR)
-}
-
-pub fn load_fat_ptr(b: &Builder, fat_ptr: ValueRef) -> (ValueRef, ValueRef) {
-    (b.load(get_dataptr(b, fat_ptr)), b.load(get_meta(b, fat_ptr)))
-}
-
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_lvalue(&mut self,
                         bcx: &BlockAndBuilder<'bcx, 'tcx>,
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index fe71023ea34..d5828f7c56c 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -34,7 +34,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 
 pub use self::constant::trans_static_initializer;
 
-use self::lvalue::{LvalueRef, get_dataptr, get_meta};
+use self::lvalue::{LvalueRef};
 use rustc::mir::traversal;
 
 use self::operand::{OperandRef, OperandValue};
@@ -384,8 +384,10 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                     // they are the two sub-fields of a single aggregate field.
                     let meta = &fcx.fn_ty.args[idx];
                     idx += 1;
-                    arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, dst));
-                    meta.store_fn_arg(bcx, &mut llarg_idx, get_meta(bcx, dst));
+                    arg.store_fn_arg(bcx, &mut llarg_idx,
+                                     base::get_dataptr_builder(bcx, dst));
+                    meta.store_fn_arg(bcx, &mut llarg_idx,
+                                      base::get_meta_builder(bcx, dst));
                 } else {
                     arg.store_fn_arg(bcx, &mut llarg_idx, dst);
                 }
@@ -466,8 +468,10 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                 // so make an alloca to store them in.
                 let meta = &fcx.fn_ty.args[idx];
                 idx += 1;
-                arg.store_fn_arg(bcx, &mut llarg_idx, get_dataptr(bcx, lltemp));
-                meta.store_fn_arg(bcx, &mut llarg_idx, get_meta(bcx, lltemp));
+                arg.store_fn_arg(bcx, &mut llarg_idx,
+                                 base::get_dataptr_builder(bcx, lltemp));
+                meta.store_fn_arg(bcx, &mut llarg_idx,
+                                  base::get_meta_builder(bcx, lltemp));
             } else  {
                 // otherwise, arg is passed by value, so make a
                 // temporary and store it there
diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs
index 4f1ec40398c..c9d83a33752 100644
--- a/src/librustc_trans/mir/operand.rs
+++ b/src/librustc_trans/mir/operand.rs
@@ -143,20 +143,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     {
         debug!("trans_load: {:?} @ {:?}", Value(llval), ty);
 
-        let val = if common::type_is_imm_pair(bcx.ccx(), ty) {
+        let val = if common::type_is_fat_ptr(bcx.tcx(), ty) {
+            let (lldata, llextra) = base::load_fat_ptr_builder(bcx, llval, ty);
+            OperandValue::Pair(lldata, llextra)
+        } else if common::type_is_imm_pair(bcx.ccx(), ty) {
+            let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx(), ty).unwrap();
             let a_ptr = bcx.struct_gep(llval, 0);
             let b_ptr = bcx.struct_gep(llval, 1);
 
-            // This is None only for fat pointers, which don't
-            // need any special load-time behavior anyway.
-            let pair_fields = common::type_pair_fields(bcx.ccx(), ty);
-            let (a, b) = if let Some([a_ty, b_ty]) = pair_fields {
-                (base::load_ty_builder(bcx, a_ptr, a_ty),
-                 base::load_ty_builder(bcx, b_ptr, b_ty))
-            } else {
-                (bcx.load(a_ptr), bcx.load(b_ptr))
-            };
-            OperandValue::Pair(a, b)
+            OperandValue::Pair(
+                base::load_ty_builder(bcx, a_ptr, a_ty),
+                base::load_ty_builder(bcx, b_ptr, b_ty)
+            )
         } else if common::type_is_immediate(bcx.ccx(), ty) {
             OperandValue::Immediate(base::load_ty_builder(bcx, llval, ty))
         } else {
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index b643dcd9871..97aa475be68 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -11,15 +11,18 @@
 use llvm::{self, ValueRef};
 use rustc::ty::{self, Ty};
 use rustc::ty::cast::{CastTy, IntTy};
+use rustc::ty::layout::Layout;
 use rustc::mir::repr as mir;
 
 use asm;
 use base;
 use callee::Callee;
 use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result};
+use common::{C_integral};
 use debuginfo::DebugLoc;
 use adt;
 use machine;
+use type_::Type;
 use type_of;
 use tvec;
 use value::Value;
@@ -28,7 +31,7 @@ use Disr;
 use super::MirContext;
 use super::constant::const_scalar_checked_binop;
 use super::operand::{OperandRef, OperandValue};
-use super::lvalue::{LvalueRef, get_dataptr};
+use super::lvalue::{LvalueRef};
 
 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_rvalue(&mut self,
@@ -98,7 +101,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 let tr_elem = self.trans_operand(&bcx, elem);
                 let size = count.value.as_u64(bcx.tcx().sess.target.uint_type);
                 let size = C_uint(bcx.ccx(), size);
-                let base = get_dataptr(&bcx, dest.llval);
+                let base = base::get_dataptr_builder(&bcx, dest.llval);
                 let bcx = bcx.map_block(|block| {
                     tvec::slice_for_each(block, base, tr_elem.ty, size, |block, llslot| {
                         self.store_operand_direct(block, llslot, tr_elem);
@@ -281,7 +284,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                 }
                                 OperandValue::Pair(..) => bug!("Unexpected Pair operand")
                             };
-                            (discr, adt::is_discr_signed(&l))
+                            let (signed, min, max) = match l {
+                                &Layout::CEnum { signed, min, max, .. } => {
+                                    (signed, min, max)
+                                }
+                                _ => bug!("CEnum {:?} is not an enum", operand)
+                            };
+
+                            if max > min {
+                                // We want `table[e as usize]` to not
+                                // have bound checks, and this is the most
+                                // convenient place to put the `assume`.
+
+                                base::call_assume(&bcx, bcx.icmp(
+                                    llvm::IntULE,
+                                    discr,
+                                    C_integral(common::val_ty(discr), max, false)
+                                ))
+                            }
+
+                            (discr, signed)
                         } else {
                             (operand.immediate(), operand.ty.is_signed())
                         };
@@ -382,13 +404,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                     match (lhs.val, rhs.val) {
                         (OperandValue::Pair(lhs_addr, lhs_extra),
                          OperandValue::Pair(rhs_addr, rhs_extra)) => {
-                            bcx.with_block(|bcx| {
-                                base::compare_fat_ptrs(bcx,
-                                                       lhs_addr, lhs_extra,
-                                                       rhs_addr, rhs_extra,
-                                                       lhs.ty, op.to_hir_binop(),
-                                                       debug_loc)
-                            })
+                            self.trans_fat_ptr_binop(&bcx, op,
+                                                     lhs_addr, lhs_extra,
+                                                     rhs_addr, rhs_extra,
+                                                     lhs.ty)
                         }
                         _ => bug!()
                     }
@@ -485,6 +504,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                               input_ty: Ty<'tcx>) -> ValueRef {
         let is_float = input_ty.is_fp();
         let is_signed = input_ty.is_signed();
+        let is_nil = input_ty.is_nil();
+        let is_bool = input_ty.is_bool();
         match op {
             mir::BinOp::Add => if is_float {
                 bcx.fadd(lhs, rhs)
@@ -535,12 +556,79 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                    DebugLoc::None)
                 })
             }
-            mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Gt |
-            mir::BinOp::Ne | mir::BinOp::Le | mir::BinOp::Ge => {
-                bcx.with_block(|bcx| {
-                    base::compare_scalar_types(bcx, lhs, rhs, input_ty,
-                                               op.to_hir_binop(), DebugLoc::None)
+            mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt |
+            mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil {
+                C_bool(bcx.ccx(), match op {
+                    mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false,
+                    mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true,
+                    _ => unreachable!()
                 })
+            } else if is_float {
+                bcx.fcmp(
+                    base::bin_op_to_fcmp_predicate(op.to_hir_binop()),
+                    lhs, rhs
+                )
+            } else {
+                let (lhs, rhs) = if is_bool {
+                    // FIXME(#36856) -- extend the bools into `i8` because
+                    // LLVM's i1 comparisons are broken.
+                    (bcx.zext(lhs, Type::i8(bcx.ccx())),
+                     bcx.zext(rhs, Type::i8(bcx.ccx())))
+                } else {
+                    (lhs, rhs)
+                };
+
+                bcx.icmp(
+                    base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed),
+                    lhs, rhs
+                )
+            }
+        }
+    }
+
+    pub fn trans_fat_ptr_binop(&mut self,
+                               bcx: &BlockAndBuilder<'bcx, 'tcx>,
+                               op: mir::BinOp,
+                               lhs_addr: ValueRef,
+                               lhs_extra: ValueRef,
+                               rhs_addr: ValueRef,
+                               rhs_extra: ValueRef,
+                               _input_ty: Ty<'tcx>)
+                               -> ValueRef {
+        match op {
+            mir::BinOp::Eq => {
+                bcx.and(
+                    bcx.icmp(llvm::IntEQ, lhs_addr, rhs_addr),
+                    bcx.icmp(llvm::IntEQ, lhs_extra, rhs_extra)
+                )
+            }
+            mir::BinOp::Ne => {
+                bcx.or(
+                    bcx.icmp(llvm::IntNE, lhs_addr, rhs_addr),
+                    bcx.icmp(llvm::IntNE, lhs_extra, rhs_extra)
+                )
+            }
+            mir::BinOp::Le | mir::BinOp::Lt |
+            mir::BinOp::Ge | mir::BinOp::Gt => {
+                // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1)
+                let (op, strict_op) = match op {
+                    mir::BinOp::Lt => (llvm::IntULT, llvm::IntULT),
+                    mir::BinOp::Le => (llvm::IntULE, llvm::IntULT),
+                    mir::BinOp::Gt => (llvm::IntUGT, llvm::IntUGT),
+                    mir::BinOp::Ge => (llvm::IntUGE, llvm::IntUGT),
+                    _ => bug!(),
+                };
+
+                bcx.or(
+                    bcx.icmp(strict_op, lhs_addr, rhs_addr),
+                    bcx.and(
+                        bcx.icmp(llvm::IntEQ, lhs_addr, rhs_addr),
+                        bcx.icmp(op, lhs_extra, rhs_extra)
+                    )
+                )
+            }
+            _ => {
+                bug!("unexpected fat ptr binop");
             }
         }
     }
diff --git a/src/test/codegen/enum-bounds-check.rs b/src/test/codegen/enum-bounds-check.rs
new file mode 100644
index 00000000000..4cfb5a752df
--- /dev/null
+++ b/src/test/codegen/enum-bounds-check.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -O
+
+#![crate_type = "lib"]
+
+pub enum Foo {
+    A, B
+}
+
+// CHECK-LABEL: @lookup
+#[no_mangle]
+pub fn lookup(buf: &[u8; 2], f: Foo) -> u8 {
+    // CHECK-NOT: panic_bounds_check
+    buf[f as usize]
+}