From 45fe3a1a2ab2671bb9f726941eda6c2899eb6dff Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 4 Oct 2016 19:24:49 +0300 Subject: [PATCH] emit an assume that cast-from enums are in range Fixes #36955. --- src/librustc_trans/base.rs | 5 +++++ src/librustc_trans/mir/rvalue.rs | 23 ++++++++++++++++++++++- src/test/codegen/enum-bounds-check.rs | 24 ++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/codegen/enum-bounds-check.rs diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 5d6dd27108b..e0e808f2dcc 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -522,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. diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index c30a9dfdd96..97aa475be68 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -11,12 +11,14 @@ 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; @@ -282,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()) }; 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 or the MIT license +// , 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] +}