Skip to content

Commit 45fe3a1

Browse files
committed
emit an assume that cast-from enums are in range
Fixes #36955.
1 parent 9233366 commit 45fe3a1

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

src/librustc_trans/base.rs

+5
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,11 @@ pub fn need_invoke(bcx: Block) -> bool {
522522
}
523523
}
524524

525+
pub fn call_assume<'a, 'tcx>(b: &Builder<'a, 'tcx>, val: ValueRef) {
526+
let assume_intrinsic = b.ccx.get_intrinsic("llvm.assume");
527+
b.call(assume_intrinsic, &[val], None);
528+
}
529+
525530
/// Helper for loading values from memory. Does the necessary conversion if the in-memory type
526531
/// differs from the type used for SSA values. Also handles various special cases where the type
527532
/// gives us better information about what we are loading.

src/librustc_trans/mir/rvalue.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
use llvm::{self, ValueRef};
1212
use rustc::ty::{self, Ty};
1313
use rustc::ty::cast::{CastTy, IntTy};
14+
use rustc::ty::layout::Layout;
1415
use rustc::mir::repr as mir;
1516

1617
use asm;
1718
use base;
1819
use callee::Callee;
1920
use common::{self, val_ty, C_bool, C_null, C_uint, BlockAndBuilder, Result};
21+
use common::{C_integral};
2022
use debuginfo::DebugLoc;
2123
use adt;
2224
use machine;
@@ -282,7 +284,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
282284
}
283285
OperandValue::Pair(..) => bug!("Unexpected Pair operand")
284286
};
285-
(discr, adt::is_discr_signed(&l))
287+
let (signed, min, max) = match l {
288+
&Layout::CEnum { signed, min, max, .. } => {
289+
(signed, min, max)
290+
}
291+
_ => bug!("CEnum {:?} is not an enum", operand)
292+
};
293+
294+
if max > min {
295+
// We want `table[e as usize]` to not
296+
// have bound checks, and this is the most
297+
// convenient place to put the `assume`.
298+
299+
base::call_assume(&bcx, bcx.icmp(
300+
llvm::IntULE,
301+
discr,
302+
C_integral(common::val_ty(discr), max, false)
303+
))
304+
}
305+
306+
(discr, signed)
286307
} else {
287308
(operand.immediate(), operand.ty.is_signed())
288309
};

src/test/codegen/enum-bounds-check.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -O
12+
13+
#![crate_type = "lib"]
14+
15+
pub enum Foo {
16+
A, B
17+
}
18+
19+
// CHECK-LABEL: @lookup
20+
#[no_mangle]
21+
pub fn lookup(buf: &[u8; 2], f: Foo) -> u8 {
22+
// CHECK-NOT: panic_bounds_check
23+
buf[f as usize]
24+
}

0 commit comments

Comments
 (0)