Skip to content

Commit c4ba253

Browse files
committed
Auto merge of #50164 - nox:rval-range-metadata, r=<try>
Emit range metadata on calls returning scalars (fixes #50157)
2 parents 2a6200a + c7c292b commit c4ba253

File tree

6 files changed

+76
-20
lines changed

6 files changed

+76
-20
lines changed

src/librustc/ty/layout.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::fmt;
2323
use std::i128;
2424
use std::iter;
2525
use std::mem;
26-
use std::ops::{Add, Sub, Mul, AddAssign, Deref, RangeInclusive};
26+
use std::ops::{Add, Sub, Mul, AddAssign, Deref, Range, RangeInclusive};
2727

2828
use ich::StableHashingContext;
2929
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
@@ -648,6 +648,25 @@ impl Scalar {
648648
false
649649
}
650650
}
651+
652+
/// Returns a range suitable to be passed to LLVM for range metadata.
653+
pub fn range_metadata<C: HasDataLayout>(&self, cx: C) -> Option<Range<u128>> {
654+
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
655+
// However, that is fine here (it would still represent the full range),
656+
// i.e., if the range is everything.
657+
let bits = self.value.size(cx).bits();
658+
assert!(bits <= 128);
659+
let mask = !0u128 >> (128 - bits);
660+
let start = self.valid_range.start;
661+
let end = self.valid_range.end.wrapping_add(1);
662+
if start & mask == end & mask {
663+
// The lo==hi case would be rejected by the LLVM verifier
664+
// (it would mean either an empty set, which is impossible, or the
665+
// entire range of the type, which is pointless).
666+
return None;
667+
}
668+
Some(start..end)
669+
}
651670
}
652671

653672
/// The first half of a fat pointer.

src/librustc_trans/abi.rs

Lines changed: 18 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,23 @@ 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+
// If the value is a boolean, the range is 0..2 and that ultimately
1060+
// become 0..0 when the type becomes i1, which would be rejected
1061+
// by the LLVM verifier.
1062+
match scalar.value {
1063+
layout::Int(..) if !scalar.is_bool() => {
1064+
if let Some(range) = scalar.range_metadata(bx.cx) {
1065+
// FIXME(nox): This causes very weird type errors about
1066+
// SHL operators in constants in stage 2 with LLVM 3.9.
1067+
if unsafe { llvm::LLVMRustVersionMajor() >= 4 } {
1068+
bx.range_metadata(callsite, range);
1069+
}
1070+
}
1071+
}
1072+
_ => {}
1073+
}
1074+
}
10581075
for arg in &self.args {
10591076
if arg.pad.is_some() {
10601077
apply(&ArgAttributes::new());

src/librustc_trans/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#![allow(unused_attributes)]
2626
#![feature(libc)]
2727
#![feature(quote)]
28+
#![feature(range_contains)]
2829
#![feature(rustc_diagnostic_macros)]
2930
#![feature(slice_sort_by_cached_key)]
3031
#![feature(optin_builtin_traits)]

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/librustc_trans/mir/place.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,14 @@ impl<'a, 'tcx> PlaceRef<'tcx> {
9191
}
9292

9393
let scalar_load_metadata = |load, scalar: &layout::Scalar| {
94-
let (min, max) = (scalar.valid_range.start, scalar.valid_range.end);
95-
let max_next = max.wrapping_add(1);
96-
let bits = scalar.value.size(bx.cx).bits();
97-
assert!(bits <= 128);
98-
let mask = !0u128 >> (128 - bits);
99-
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
100-
// However, that is fine here (it would still represent the full range),
101-
// i.e., if the range is everything. The lo==hi case would be
102-
// rejected by the LLVM verifier (it would mean either an
103-
// empty set, which is impossible, or the entire range of the
104-
// type, which is pointless).
10594
match scalar.value {
106-
layout::Int(..) if max_next & mask != min & mask => {
107-
// llvm::ConstantRange can deal with ranges that wrap around,
108-
// so an overflow on (max + 1) is fine.
109-
bx.range_metadata(load, min..max_next);
95+
layout::Int(..) => {
96+
if let Some(range) = scalar.range_metadata(bx.cx) {
97+
bx.range_metadata(load, range);
98+
}
11099
}
111-
layout::Pointer if 0 < min && min < max => {
100+
layout::Pointer
101+
if (1..scalar.valid_range.end).contains(&scalar.valid_range.start) => {
112102
bx.nonnull_metadata(load);
113103
}
114104
_ => {}

src/test/codegen/call-metadata.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
// min-llvm-version 4.0
16+
17+
18+
#![crate_type = "lib"]
19+
20+
pub fn test() {
21+
// CHECK: call i8 @some_true(), !range [[R0:![0-9]+]]
22+
// CHECK: [[R0]] = !{i8 0, i8 3}
23+
some_true();
24+
}
25+
26+
#[no_mangle]
27+
fn some_true() -> Option<bool> {
28+
Some(true)
29+
}

0 commit comments

Comments
 (0)