Skip to content

Commit 68a09fc

Browse files
committed
Auto merge of #50164 - nox:rval-range-metadata, r=eddyb
Emit range metadata on calls returning scalars (fixes #50157)
2 parents 1e01e22 + 9065644 commit 68a09fc

File tree

7 files changed

+78
-21
lines changed

7 files changed

+78
-21
lines changed

src/librustc_target/abi/mod.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub use self::Primitive::*;
1414
use spec::Target;
1515

1616
use std::cmp;
17-
use std::ops::{Add, Deref, Sub, Mul, AddAssign, RangeInclusive};
17+
use std::ops::{Add, Deref, Sub, Mul, AddAssign, Range, RangeInclusive};
1818

1919
pub mod call;
2020

@@ -544,6 +544,23 @@ impl Scalar {
544544
false
545545
}
546546
}
547+
548+
/// Returns the valid range as a `x..y` range.
549+
///
550+
/// If `x` and `y` are equal, the range is full, not empty.
551+
pub fn valid_range_exclusive<C: HasDataLayout>(&self, cx: C) -> Range<u128> {
552+
// For a (max) value of -1, max will be `-1 as usize`, which overflows.
553+
// However, that is fine here (it would still represent the full range),
554+
// i.e., if the range is everything.
555+
let bits = self.value.size(cx).bits();
556+
assert!(bits <= 128);
557+
let mask = !0u128 >> (128 - bits);
558+
let start = self.valid_range.start;
559+
let end = self.valid_range.end;
560+
assert_eq!(start, start & mask);
561+
assert_eq!(end, end & mask);
562+
start..(end.wrapping_add(1) & mask)
563+
}
547564
}
548565

549566
/// Describes how the fields of a type are located in memory.

src/librustc_target/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#![feature(const_fn)]
3030
#![feature(fs_read_write)]
3131
#![feature(inclusive_range)]
32+
#![feature(inclusive_range_fields)]
3233
#![feature(slice_patterns)]
3334

3435
#[macro_use]

src/librustc_trans/abi.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ pub trait FnTypeExt<'a, 'tcx> {
265265
fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type;
266266
fn llvm_cconv(&self) -> llvm::CallConv;
267267
fn apply_attrs_llfn(&self, llfn: ValueRef);
268-
fn apply_attrs_callsite(&self, callsite: ValueRef);
268+
fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef);
269269
}
270270

271271
impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
@@ -640,7 +640,7 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
640640
}
641641
}
642642

643-
fn apply_attrs_callsite(&self, callsite: ValueRef) {
643+
fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) {
644644
let mut i = 0;
645645
let mut apply = |attrs: &ArgAttributes| {
646646
attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite);
@@ -653,6 +653,24 @@ impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> {
653653
PassMode::Indirect(ref attrs) => apply(attrs),
654654
_ => {}
655655
}
656+
if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi {
657+
// If the value is a boolean, the range is 0..2 and that ultimately
658+
// become 0..0 when the type becomes i1, which would be rejected
659+
// by the LLVM verifier.
660+
match scalar.value {
661+
layout::Int(..) if !scalar.is_bool() => {
662+
let range = scalar.valid_range_exclusive(bx.cx);
663+
if range.start != range.end {
664+
// FIXME(nox): This causes very weird type errors about
665+
// SHL operators in constants in stage 2 with LLVM 3.9.
666+
if unsafe { llvm::LLVMRustVersionMajor() >= 4 } {
667+
bx.range_metadata(callsite, range);
668+
}
669+
}
670+
}
671+
_ => {}
672+
}
673+
}
656674
for arg in &self.args {
657675
if arg.pad.is_some() {
658676
apply(&ArgAttributes::new());

src/librustc_trans/lib.rs

+1
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

+2-2
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

+7-16
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,15 @@ 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).
94+
let vr = scalar.valid_range.clone();
10595
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);
96+
layout::Int(..) => {
97+
let range = scalar.valid_range_exclusive(bx.cx);
98+
if range.start != range.end {
99+
bx.range_metadata(load, range);
100+
}
110101
}
111-
layout::Pointer if 0 < min && min < max => {
102+
layout::Pointer if vr.start < vr.end && !vr.contains(&0) => {
112103
bx.nonnull_metadata(load);
113104
}
114105
_ => {}

src/test/codegen/call-metadata.rs

+29
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)