Skip to content

Commit f078a38

Browse files
committed
Emit range metadata on calls returning scalars (fixes #50157)
1 parent 65d201f commit f078a38

File tree

3 files changed

+51
-3
lines changed

3 files changed

+51
-3
lines changed

src/librustc_trans/abi.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@ impl<'a, 'tcx> FnType<'tcx> {
10421042
}
10431043
}
10441044

1045-
pub fn apply_attrs_callsite(&self, callsite: ValueRef) {
1045+
pub fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) {
10461046
let mut i = 0;
10471047
let mut apply = |attrs: &ArgAttributes| {
10481048
attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
@@ -1055,6 +1055,27 @@ impl<'a, 'tcx> FnType<'tcx> {
10551055
PassMode::Indirect(ref attrs) => apply(attrs),
10561056
_ => {}
10571057
}
1058+
if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi {
1059+
let (min, max) = (scalar.valid_range.start, scalar.valid_range.end);
1060+
let max_next = max.wrapping_add(1);
1061+
let bits = scalar.value.size(bx.cx).bits();
1062+
assert!(bits <= 128);
1063+
let mask = !0u128 >> (128 - bits);
1064+
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
1065+
// However, that is fine here (it would still represent the full range),
1066+
// i.e., if the range is everything. The lo==hi case would be
1067+
// rejected by the LLVM verifier (it would mean either an
1068+
// empty set, which is impossible, or the entire range of the
1069+
// type, which is pointless).
1070+
match scalar.value {
1071+
layout::Int(..) if !scalar.is_bool() && max_next & mask != min & mask => {
1072+
// llvm::ConstantRange can deal with ranges that wrap around,
1073+
// so an overflow on (max + 1) is fine.
1074+
bx.range_metadata(callsite, min..max_next);
1075+
}
1076+
_ => {}
1077+
}
1078+
}
10581079
for arg in &self.args {
10591080
if arg.pad.is_some() {
10601081
apply(&ArgAttributes::new());

src/librustc_trans/mir/block.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
127127
ret_bx,
128128
llblock(this, cleanup),
129129
cleanup_bundle);
130-
fn_ty.apply_attrs_callsite(invokeret);
130+
fn_ty.apply_attrs_callsite(&bx, invokeret);
131131

132132
if let Some((ret_dest, target)) = destination {
133133
let ret_bx = this.build_block(target);
@@ -136,7 +136,7 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
136136
}
137137
} else {
138138
let llret = bx.call(fn_ptr, &llargs, cleanup_bundle);
139-
fn_ty.apply_attrs_callsite(llret);
139+
fn_ty.apply_attrs_callsite(&bx, llret);
140140
if this.mir[bb].is_cleanup {
141141
// Cleanup is always the cold path. Don't inline
142142
// drop glue. Also, when there is a deeply-nested

src/test/codegen/call-metadata.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
// Checks that range metadata gets emitted on calls to functions returning a
12+
// scalar value.
13+
14+
// compile-flags: -C no-prepopulate-passes
15+
16+
#![crate_type = "lib"]
17+
18+
pub fn test() {
19+
// CHECK: call i8 @some_true(), !range [[R0:![0-9]+]]
20+
// CHECK: [[R0]] = !{i8 0, i8 3}
21+
some_true();
22+
}
23+
24+
#[no_mangle]
25+
fn some_true() -> Option<bool> {
26+
Some(true)
27+
}

0 commit comments

Comments
 (0)