Skip to content

Commit 28bc505

Browse files
committed
Auto merge of rust-lang#135610 - scottmcm:assume-less, r=<try>
Emit fewer `assume`s now that we have `range` metadata on parameters We still need the `assume` for the *target* type's range, but we no longer need it for the *source* type's range. The first stab at this regressed a test, but thanks to good advice in llvm/llvm-project#123278 (comment) the second commit here changes how we emit these range assertions to the form that LLVM apparently likes better (and, conveniently, is easier to emit too) which got that test passing again 🎉 Hopefully this means less crud for LLVM to churn through in `opt` builds...
2 parents bcd0683 + 8ab474a commit 28bc505

File tree

6 files changed

+115
-144
lines changed

6 files changed

+115
-144
lines changed

Diff for: compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+20-51
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
257257
if let OperandValueKind::Immediate(to_scalar) = cast_kind
258258
&& from_scalar.size(self.cx) == to_scalar.size(self.cx)
259259
{
260-
let from_backend_ty = bx.backend_type(operand.layout);
261260
let to_backend_ty = bx.backend_type(cast);
262261
Some(OperandValue::Immediate(self.transmute_immediate(
263262
bx,
264263
imm,
265264
from_scalar,
266-
from_backend_ty,
267265
to_scalar,
268266
to_backend_ty,
269267
)))
@@ -279,13 +277,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
279277
&& in_a.size(self.cx) == out_a.size(self.cx)
280278
&& in_b.size(self.cx) == out_b.size(self.cx)
281279
{
282-
let in_a_ibty = bx.scalar_pair_element_backend_type(operand.layout, 0, false);
283-
let in_b_ibty = bx.scalar_pair_element_backend_type(operand.layout, 1, false);
284280
let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
285281
let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
286282
Some(OperandValue::Pair(
287-
self.transmute_immediate(bx, imm_a, in_a, in_a_ibty, out_a, out_a_ibty),
288-
self.transmute_immediate(bx, imm_b, in_b, in_b_ibty, out_b, out_b_ibty),
283+
self.transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
284+
self.transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
289285
))
290286
} else {
291287
None
@@ -309,12 +305,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
309305
) -> Option<Bx::Value> {
310306
use abi::Primitive::*;
311307

312-
// When scalars are passed by value, there's no metadata recording their
313-
// valid ranges. For example, `char`s are passed as just `i32`, with no
314-
// way for LLVM to know that they're 0x10FFFF at most. Thus we assume
315-
// the range of the input value too, not just the output range.
316-
self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
317-
318308
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
319309
(Int(_, is_signed), Int(..)) => bx.intcast(imm, to_backend_ty, is_signed),
320310
(Float(_), Float(_)) => {
@@ -356,7 +346,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
356346
bx: &mut Bx,
357347
mut imm: Bx::Value,
358348
from_scalar: abi::Scalar,
359-
from_backend_ty: Bx::Type,
360349
to_scalar: abi::Scalar,
361350
to_backend_ty: Bx::Type,
362351
) -> Bx::Value {
@@ -365,11 +354,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
365354
use abi::Primitive::*;
366355
imm = bx.from_immediate(imm);
367356

368-
// When scalars are passed by value, there's no metadata recording their
369-
// valid ranges. For example, `char`s are passed as just `i32`, with no
370-
// way for LLVM to know that they're 0x10FFFF at most. Thus we assume
371-
// the range of the input value too, not just the output range.
372-
self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty);
357+
// We used to `assume` the `from_scalar` here too, but that's no longer needed
358+
// because if we have a scalar, we must already know its range. Either
359+
// 1) It's a parameter with `range` parameter metadata,
360+
// 2) It's something we `load`ed with `!range` metadata, or
361+
// 3) It's something we transmuted and already `assume`d the range.
362+
// And thus in all those cases another `assume` is just wasteful.
363+
// (Case 1 didn't used to be covered, and thus the `assume` was needed.)
373364

374365
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
375366
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
@@ -389,18 +380,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
389380
bx.bitcast(int_imm, to_backend_ty)
390381
}
391382
};
392-
self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty);
383+
384+
// This `assume` remains important for cases like
385+
// transmute::<u32, NonZeroU32>(x) == 0
386+
// since it's never passed to something with parameter metadata (especially
387+
// after MIR inlining) so the only way to tell the backend about the
388+
// constraint that the `transmute` introduced is to `assume` it.
389+
self.assume_scalar_range(bx, imm, to_scalar);
390+
393391
imm = bx.to_immediate_scalar(imm, to_scalar);
394392
imm
395393
}
396394

397-
fn assume_scalar_range(
398-
&self,
399-
bx: &mut Bx,
400-
imm: Bx::Value,
401-
scalar: abi::Scalar,
402-
backend_ty: Bx::Type,
403-
) {
395+
fn assume_scalar_range(&self, bx: &mut Bx, imm: Bx::Value, scalar: abi::Scalar) {
404396
if matches!(self.cx.sess().opts.optimize, OptLevel::No)
405397
// For now, the critical niches are all over `Int`eger values.
406398
// Should floating-point values or pointers ever get more complex
@@ -411,31 +403,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
411403
return;
412404
}
413405

414-
let abi::WrappingRange { start, end } = scalar.valid_range(self.cx);
415-
416-
if start <= end {
417-
if start > 0 {
418-
let low = bx.const_uint_big(backend_ty, start);
419-
let cmp = bx.icmp(IntPredicate::IntUGE, imm, low);
420-
bx.assume(cmp);
421-
}
422-
423-
let type_max = scalar.size(self.cx).unsigned_int_max();
424-
if end < type_max {
425-
let high = bx.const_uint_big(backend_ty, end);
426-
let cmp = bx.icmp(IntPredicate::IntULE, imm, high);
427-
bx.assume(cmp);
428-
}
429-
} else {
430-
let low = bx.const_uint_big(backend_ty, start);
431-
let cmp_low = bx.icmp(IntPredicate::IntUGE, imm, low);
432-
433-
let high = bx.const_uint_big(backend_ty, end);
434-
let cmp_high = bx.icmp(IntPredicate::IntULE, imm, high);
435-
436-
let or = bx.or(cmp_low, cmp_high);
437-
bx.assume(or);
438-
}
406+
let range = scalar.valid_range(self.cx);
407+
bx.assume_integer_range(imm, range);
439408
}
440409

441410
pub(crate) fn codegen_rvalue_unsized(

Diff for: compiler/rustc_codegen_ssa/src/traits/builder.rs

+22
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,28 @@ pub trait BuilderMethods<'a, 'tcx>:
217217
dest: PlaceRef<'tcx, Self::Value>,
218218
);
219219

220+
/// Emits an `assume` that the integer value `imm` is contained in `range`.
221+
///
222+
/// This *always* emits the assumption, so you probably want to check the
223+
/// optimization level and `Scalar::is_always_valid` before calling it.
224+
fn assume_integer_range(&mut self, imm: Self::Value, range: WrappingRange) {
225+
let WrappingRange { start, end } = range;
226+
let backend_ty = self.cx().val_ty(imm);
227+
228+
// Perhaps one day we'll be able to use assume operand bundles for this,
229+
// but for now this encoding with a single icmp+assume is best per
230+
// <https://github.com/llvm/llvm-project/issues/123278#issuecomment-2597440158>
231+
let shifted = if start == 0 {
232+
imm
233+
} else {
234+
let low = self.const_uint_big(backend_ty, start);
235+
self.sub(imm, low)
236+
};
237+
let width = self.const_uint_big(backend_ty, u128::wrapping_sub(end, start));
238+
let cmp = self.icmp(IntPredicate::IntULE, shifted, width);
239+
self.assume(cmp);
240+
}
241+
220242
fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
221243
fn nonnull_metadata(&mut self, load: Self::Value);
222244

Diff for: tests/codegen/intrinsics/transmute-niched.rs

+59-63
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ revisions: OPT DBG
22
//@ [OPT] compile-flags: -C opt-level=3 -C no-prepopulate-passes
3+
//@ [OPT] min-llvm-version: 19 (for range parameter metadata)
34
//@ [DBG] compile-flags: -C opt-level=0 -C no-prepopulate-passes
45
//@ only-64bit (so I don't need to worry about usize)
56
#![crate_type = "lib"]
@@ -17,26 +18,24 @@ pub enum SmallEnum {
1718
// CHECK-LABEL: @check_to_enum(
1819
#[no_mangle]
1920
pub unsafe fn check_to_enum(x: i8) -> SmallEnum {
20-
// OPT: %0 = icmp uge i8 %x, 10
21-
// OPT: call void @llvm.assume(i1 %0)
22-
// OPT: %1 = icmp ule i8 %x, 12
21+
// CHECK-NOT: icmp
22+
// CHECK-NOT: assume
23+
// OPT: %0 = sub i8 %x, 10
24+
// OPT: %1 = icmp ule i8 %0, 2
2325
// OPT: call void @llvm.assume(i1 %1)
24-
// DBG-NOT: icmp
25-
// DBG-NOT: assume
26+
// CHECK-NOT: icmp
27+
// CHECK-NOT: assume
2628
// CHECK: ret i8 %x
2729

2830
transmute(x)
2931
}
3032

3133
// CHECK-LABEL: @check_from_enum(
34+
// OPT-SAME: range(i8 10, 13){{.+}}%x
3235
#[no_mangle]
3336
pub unsafe fn check_from_enum(x: SmallEnum) -> i8 {
34-
// OPT: %0 = icmp uge i8 %x, 10
35-
// OPT: call void @llvm.assume(i1 %0)
36-
// OPT: %1 = icmp ule i8 %x, 12
37-
// OPT: call void @llvm.assume(i1 %1)
38-
// DBG-NOT: icmp
39-
// DBG-NOT: assume
37+
// CHECK-NOT: icmp
38+
// CHECK-NOT: assume
4039
// CHECK: ret i8 %x
4140

4241
transmute(x)
@@ -45,26 +44,24 @@ pub unsafe fn check_from_enum(x: SmallEnum) -> i8 {
4544
// CHECK-LABEL: @check_to_ordering(
4645
#[no_mangle]
4746
pub unsafe fn check_to_ordering(x: u8) -> std::cmp::Ordering {
48-
// OPT: %0 = icmp uge i8 %x, -1
49-
// OPT: %1 = icmp ule i8 %x, 1
50-
// OPT: %2 = or i1 %0, %1
51-
// OPT: call void @llvm.assume(i1 %2)
52-
// DBG-NOT: icmp
53-
// DBG-NOT: assume
47+
// CHECK-NOT: icmp
48+
// CHECK-NOT: assume
49+
// OPT: %0 = sub i8 %x, -1
50+
// OPT: %1 = icmp ule i8 %0, 2
51+
// OPT: call void @llvm.assume(i1 %1)
52+
// CHECK-NOT: icmp
53+
// CHECK-NOT: assume
5454
// CHECK: ret i8 %x
5555

5656
transmute(x)
5757
}
5858

5959
// CHECK-LABEL: @check_from_ordering(
60+
// OPT-SAME: range(i8 -1, 2){{.+}}%x
6061
#[no_mangle]
6162
pub unsafe fn check_from_ordering(x: std::cmp::Ordering) -> u8 {
62-
// OPT: %0 = icmp uge i8 %x, -1
63-
// OPT: %1 = icmp ule i8 %x, 1
64-
// OPT: %2 = or i1 %0, %1
65-
// OPT: call void @llvm.assume(i1 %2)
66-
// DBG-NOT: icmp
67-
// DBG-NOT: assume
63+
// CHECK-NOT: icmp
64+
// CHECK-NOT: assume
6865
// CHECK: ret i8 %x
6966

7067
transmute(x)
@@ -96,50 +93,50 @@ pub enum Minus100ToPlus100 {
9693
}
9794

9895
// CHECK-LABEL: @check_enum_from_char(
96+
// OPT-SAME: range(i32 0, 1114112){{.+}}%x
9997
#[no_mangle]
10098
pub unsafe fn check_enum_from_char(x: char) -> Minus100ToPlus100 {
101-
// OPT: %0 = icmp ule i32 %x, 1114111
102-
// OPT: call void @llvm.assume(i1 %0)
103-
// OPT: %1 = icmp uge i32 %x, -100
104-
// OPT: %2 = icmp ule i32 %x, 100
105-
// OPT: %3 = or i1 %1, %2
106-
// OPT: call void @llvm.assume(i1 %3)
107-
// DBG-NOT: icmp
108-
// DBG-NOT: assume
99+
// CHECK-NOT: icmp
100+
// CHECK-NOT: assume
101+
// OPT: %0 = sub i32 %x, -100
102+
// OPT: %1 = icmp ule i32 %0, 200
103+
// OPT: call void @llvm.assume(i1 %1)
104+
// CHECK-NOT: icmp
105+
// CHECK-NOT: assume
109106
// CHECK: ret i32 %x
110107

111108
transmute(x)
112109
}
113110

114111
// CHECK-LABEL: @check_enum_to_char(
112+
// OPT-SAME: range(i32 -100, 101){{.+}}%x
115113
#[no_mangle]
116114
pub unsafe fn check_enum_to_char(x: Minus100ToPlus100) -> char {
117-
// OPT: %0 = icmp uge i32 %x, -100
118-
// OPT: %1 = icmp ule i32 %x, 100
119-
// OPT: %2 = or i1 %0, %1
120-
// OPT: call void @llvm.assume(i1 %2)
121-
// OPT: %3 = icmp ule i32 %x, 1114111
122-
// OPT: call void @llvm.assume(i1 %3)
123-
// DBG-NOT: icmp
124-
// DBG-NOT: assume
115+
// CHECK-NOT: icmp
116+
// CHECK-NOT: assume
117+
// OPT: %0 = icmp ule i32 %x, 1114111
118+
// OPT: call void @llvm.assume(i1 %0)
119+
// CHECK-NOT: icmp
120+
// CHECK-NOT: assume
125121
// CHECK: ret i32 %x
126122

127123
transmute(x)
128124
}
129125

130126
// CHECK-LABEL: @check_swap_pair(
127+
// OPT-SAME: range(i32 0, 1114112){{.+}}%x.0
128+
// OPT-SAME: range(i32 1, 0){{.+}}%x.1
131129
#[no_mangle]
132130
pub unsafe fn check_swap_pair(x: (char, NonZero<u32>)) -> (NonZero<u32>, char) {
133-
// OPT: %0 = icmp ule i32 %x.0, 1114111
134-
// OPT: call void @llvm.assume(i1 %0)
135-
// OPT: %1 = icmp uge i32 %x.0, 1
131+
// CHECK-NOT: icmp
132+
// CHECK-NOT: assume
133+
// OPT: %0 = sub i32 %x.0, 1
134+
// OPT: %1 = icmp ule i32 %0, -2
136135
// OPT: call void @llvm.assume(i1 %1)
137-
// OPT: %2 = icmp uge i32 %x.1, 1
136+
// OPT: %2 = icmp ule i32 %x.1, 1114111
138137
// OPT: call void @llvm.assume(i1 %2)
139-
// OPT: %3 = icmp ule i32 %x.1, 1114111
140-
// OPT: call void @llvm.assume(i1 %3)
141-
// DBG-NOT: icmp
142-
// DBG-NOT: assume
138+
// CHECK-NOT: icmp
139+
// CHECK-NOT: assume
143140
// CHECK: %[[P1:.+]] = insertvalue { i32, i32 } poison, i32 %x.0, 0
144141
// CHECK: %[[P2:.+]] = insertvalue { i32, i32 } %[[P1]], i32 %x.1, 1
145142
// CHECK: ret { i32, i32 } %[[P2]]
@@ -148,34 +145,33 @@ pub unsafe fn check_swap_pair(x: (char, NonZero<u32>)) -> (NonZero<u32>, char) {
148145
}
149146

150147
// CHECK-LABEL: @check_bool_from_ordering(
148+
// OPT-SAME: range(i8 -1, 2){{.+}}%x
151149
#[no_mangle]
152150
pub unsafe fn check_bool_from_ordering(x: std::cmp::Ordering) -> bool {
153-
// OPT: %0 = icmp uge i8 %x, -1
154-
// OPT: %1 = icmp ule i8 %x, 1
155-
// OPT: %2 = or i1 %0, %1
156-
// OPT: call void @llvm.assume(i1 %2)
157-
// OPT: %3 = icmp ule i8 %x, 1
158-
// OPT: call void @llvm.assume(i1 %3)
159-
// DBG-NOT: icmp
160-
// DBG-NOT: assume
151+
// CHECK-NOT: icmp
152+
// CHECK-NOT: assume
153+
// OPT: %0 = icmp ule i8 %x, 1
154+
// OPT: call void @llvm.assume(i1 %0)
155+
// CHECK-NOT: icmp
156+
// CHECK-NOT: assume
161157
// CHECK: %[[R:.+]] = trunc i8 %x to i1
162158
// CHECK: ret i1 %[[R]]
163159

164160
transmute(x)
165161
}
166162

167163
// CHECK-LABEL: @check_bool_to_ordering(
164+
// OPT-SAME: i1 {{.+}} %x
168165
#[no_mangle]
169166
pub unsafe fn check_bool_to_ordering(x: bool) -> std::cmp::Ordering {
170167
// CHECK: %_0 = zext i1 %x to i8
171-
// OPT: %0 = icmp ule i8 %_0, 1
172-
// OPT: call void @llvm.assume(i1 %0)
173-
// OPT: %1 = icmp uge i8 %_0, -1
174-
// OPT: %2 = icmp ule i8 %_0, 1
175-
// OPT: %3 = or i1 %1, %2
176-
// OPT: call void @llvm.assume(i1 %3)
177-
// DBG-NOT: icmp
178-
// DBG-NOT: assume
168+
// CHECK-NOT: icmp
169+
// CHECK-NOT: assume
170+
// OPT: %0 = sub i8 %_0, -1
171+
// OPT: %1 = icmp ule i8 %0, 2
172+
// OPT: call void @llvm.assume(i1 %1)
173+
// CHECK-NOT: icmp
174+
// CHECK-NOT: assume
179175
// CHECK: ret i8 %_0
180176

181177
transmute(x)

Diff for: tests/codegen/issues/issue-119422.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! for `NonZero` integer types.
33
//!
44
//@ compile-flags: -O --edition=2021 -Zmerge-functions=disabled
5+
//@ min-llvm-version: 19 (for range parameter metadata)
56
//@ only-64bit (because the LLVM type of i64 for usize shows up)
67
#![crate_type = "lib"]
78

0 commit comments

Comments
 (0)