Skip to content

Commit f0c078e

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. Admittedly there's one test not properly handled by LLVM today, but it's synthetic, so I'd still be fine doing this and just updating the test once LLVM fixes the bug (llvm/llvm-project#123278). All the other optimization tests still pass. Hopefully this means less crud for LLVM to churn through in `opt` builds...
2 parents 76a030a + 959fd5f commit f0c078e

File tree

4 files changed

+88
-99
lines changed

4 files changed

+88
-99
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+16-12
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
@@ -356,7 +352,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
356352
bx: &mut Bx,
357353
mut imm: Bx::Value,
358354
from_scalar: abi::Scalar,
359-
from_backend_ty: Bx::Type,
360355
to_scalar: abi::Scalar,
361356
to_backend_ty: Bx::Type,
362357
) -> Bx::Value {
@@ -365,11 +360,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
365360
use abi::Primitive::*;
366361
imm = bx.from_immediate(imm);
367362

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);
363+
// We used to `assume` the `from_scalar` here too, but that's no longer needed
364+
// because if we have a scalar, we must already know its range. Either
365+
// 1) It's a parameter with `range` parameter metadata,
366+
// 2) It's something we `load`ed with `!range` metadata, or
367+
// 3) It's something we transmuted and already `assume`d the range.
368+
// And thus in all those cases another `assume` is just wasteful.
369+
// (Case 1 didn't used to be covered, and thus the `assume` was needed.)
373370

374371
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
375372
(Int(..) | Float(_), Int(..) | Float(_)) => bx.bitcast(imm, to_backend_ty),
@@ -389,7 +386,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
389386
bx.bitcast(int_imm, to_backend_ty)
390387
}
391388
};
389+
390+
// This `assume` remains important for cases like
391+
// transmute::<u32, NonZeroU32>(x) == 0
392+
// since it's never passed to something with parameter metadata (especially
393+
// after MIR inlining) so the only way to tell the backend about the
394+
// constraint that the `transmute` introduced is to `assume` it.
392395
self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty);
396+
393397
imm = bx.to_immediate_scalar(imm, to_scalar);
394398
imm
395399
}

tests/codegen/intrinsics/transmute-niched.rs

+54-56
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@ pub enum SmallEnum {
1717
// CHECK-LABEL: @check_to_enum(
1818
#[no_mangle]
1919
pub unsafe fn check_to_enum(x: i8) -> SmallEnum {
20+
// CHECK-NOT: icmp
21+
// CHECK-NOT: assume
2022
// OPT: %0 = icmp uge i8 %x, 10
2123
// OPT: call void @llvm.assume(i1 %0)
2224
// OPT: %1 = icmp ule i8 %x, 12
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,25 @@ 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 {
47+
// CHECK-NOT: icmp
48+
// CHECK-NOT: assume
4849
// OPT: %0 = icmp uge i8 %x, -1
4950
// OPT: %1 = icmp ule i8 %x, 1
5051
// OPT: %2 = or i1 %0, %1
5152
// OPT: call void @llvm.assume(i1 %2)
52-
// DBG-NOT: icmp
53-
// DBG-NOT: assume
53+
// CHECK-NOT: icmp
54+
// CHECK-NOT: assume
5455
// CHECK: ret i8 %x
5556

5657
transmute(x)
5758
}
5859

5960
// CHECK-LABEL: @check_from_ordering(
61+
// OPT-SAME: range(i8 -1, 2){{.+}}%x
6062
#[no_mangle]
6163
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
64+
// CHECK-NOT: icmp
65+
// CHECK-NOT: assume
6866
// CHECK: ret i8 %x
6967

7068
transmute(x)
@@ -96,50 +94,50 @@ pub enum Minus100ToPlus100 {
9694
}
9795

9896
// CHECK-LABEL: @check_enum_from_char(
97+
// OPT-SAME: range(i32 0, 1114112){{.+}}%x
9998
#[no_mangle]
10099
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
100+
// CHECK-NOT: icmp
101+
// CHECK-NOT: assume
102+
// OPT: %0 = icmp uge i32 %x, -100
103+
// OPT: %1 = icmp ule i32 %x, 100
104+
// OPT: %2 = or i1 %0, %1
105+
// OPT: call void @llvm.assume(i1 %2)
106+
// CHECK-NOT: icmp
107+
// CHECK-NOT: assume
109108
// CHECK: ret i32 %x
110109

111110
transmute(x)
112111
}
113112

114113
// CHECK-LABEL: @check_enum_to_char(
114+
// OPT-SAME: range(i32 -100, 101){{.+}}%x
115115
#[no_mangle]
116116
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
117+
// CHECK-NOT: icmp
118+
// CHECK-NOT: assume
119+
// OPT: %0 = icmp ule i32 %x, 1114111
120+
// OPT: call void @llvm.assume(i1 %0)
121+
// CHECK-NOT: icmp
122+
// CHECK-NOT: assume
125123
// CHECK: ret i32 %x
126124

127125
transmute(x)
128126
}
129127

130128
// CHECK-LABEL: @check_swap_pair(
129+
// OPT-SAME: range(i32 0, 1114112){{.+}}%x.0
130+
// OPT-SAME: range(i32 1, 0){{.+}}%x.1
131131
#[no_mangle]
132132
pub unsafe fn check_swap_pair(x: (char, NonZero<u32>)) -> (NonZero<u32>, char) {
133-
// OPT: %0 = icmp ule i32 %x.0, 1114111
133+
// CHECK-NOT: icmp
134+
// CHECK-NOT: assume
135+
// OPT: %0 = icmp uge i32 %x.0, 1
134136
// OPT: call void @llvm.assume(i1 %0)
135-
// OPT: %1 = icmp uge i32 %x.0, 1
137+
// OPT: %1 = icmp ule i32 %x.1, 1114111
136138
// OPT: call void @llvm.assume(i1 %1)
137-
// OPT: %2 = icmp uge i32 %x.1, 1
138-
// 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
139+
// CHECK-NOT: icmp
140+
// CHECK-NOT: assume
143141
// CHECK: %[[P1:.+]] = insertvalue { i32, i32 } poison, i32 %x.0, 0
144142
// CHECK: %[[P2:.+]] = insertvalue { i32, i32 } %[[P1]], i32 %x.1, 1
145143
// CHECK: ret { i32, i32 } %[[P2]]
@@ -148,34 +146,34 @@ pub unsafe fn check_swap_pair(x: (char, NonZero<u32>)) -> (NonZero<u32>, char) {
148146
}
149147

150148
// CHECK-LABEL: @check_bool_from_ordering(
149+
// OPT-SAME: range(i8 -1, 2){{.+}}%x
151150
#[no_mangle]
152151
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
152+
// CHECK-NOT: icmp
153+
// CHECK-NOT: assume
154+
// OPT: %0 = icmp ule i8 %x, 1
155+
// OPT: call void @llvm.assume(i1 %0)
156+
// CHECK-NOT: icmp
157+
// CHECK-NOT: assume
161158
// CHECK: %[[R:.+]] = trunc i8 %x to i1
162159
// CHECK: ret i1 %[[R]]
163160

164161
transmute(x)
165162
}
166163

167164
// CHECK-LABEL: @check_bool_to_ordering(
165+
// OPT-SAME: i1 {{.+}} %x
168166
#[no_mangle]
169167
pub unsafe fn check_bool_to_ordering(x: bool) -> std::cmp::Ordering {
170168
// 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
169+
// CHECK-NOT: icmp
170+
// CHECK-NOT: assume
171+
// OPT: %0 = icmp uge i8 %_0, -1
172+
// OPT: %1 = icmp ule i8 %_0, 1
173+
// OPT: %2 = or i1 %0, %1
174+
// OPT: call void @llvm.assume(i1 %2)
175+
// CHECK-NOT: icmp
176+
// CHECK-NOT: assume
179177
// CHECK: ret i8 %_0
180178

181179
transmute(x)

tests/codegen/transmute-optimized.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,18 @@ pub enum OneTwoThree {
9090
}
9191

9292
// CHECK-LABEL: i8 @ordering_transmute_onetwothree(i8
93+
// CHECK-SAME: range(i8 -1, 2){{.+}}%x
9394
#[no_mangle]
9495
pub unsafe fn ordering_transmute_onetwothree(x: std::cmp::Ordering) -> OneTwoThree {
95-
// CHECK: ret i8 1
96+
// FIXME: this *should* just be `ret i8 1`, but that's not happening today.
97+
// cc <https://github.com/llvm/llvm-project/issues/123278>
98+
99+
// CHECK: %[[TEMP1:.+]] = icmp ne i8 %x, 0
100+
// CHECK: tail call void @llvm.assume(i1 %[[TEMP1]])
101+
// CHECK: %[[TEMP2:.+]] = icmp ult i8 %x, 4
102+
// CHECK: tail call void @llvm.assume(i1 %[[TEMP2]])
103+
104+
// CHECK: ret i8 %x
96105
std::mem::transmute(x)
97106
}
98107

@@ -110,3 +119,11 @@ pub fn char_is_negative(c: char) -> bool {
110119
let x: i32 = unsafe { std::mem::transmute(c) };
111120
x < 0
112121
}
122+
123+
// CHECK-LABEL: i1 @transmute_to_char_is_negative(i32
124+
#[no_mangle]
125+
pub fn transmute_to_char_is_negative(x: i32) -> bool {
126+
// CHECK: ret i1 false
127+
let _c: char = unsafe { std::mem::transmute(x) };
128+
x < 0
129+
}

tests/codegen/vec-in-place.rs

-30
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> {
4141
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
4242
// CHECK-NOT: loop
4343
// CHECK-NOT: call
44-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
45-
// CHECK-NOT: loop
46-
// CHECK-NOT: call
4744
vec.into_iter().map(|e| e as u8).collect()
4845
}
4946

@@ -55,9 +52,6 @@ pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> {
5552
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
5653
// CHECK-NOT: loop
5754
// CHECK-NOT: call
58-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
59-
// CHECK-NOT: loop
60-
// CHECK-NOT: call
6155
vec.into_iter().map(|e| Wrapper(e)).collect()
6256
}
6357

@@ -86,9 +80,6 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> {
8680
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
8781
// CHECK-NOT: loop
8882
// CHECK-NOT: call
89-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
90-
// CHECK-NOT: loop
91-
// CHECK-NOT: call
9283
vec.into_iter().map(|e| e.0).collect()
9384
}
9485

@@ -100,9 +91,6 @@ pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> {
10091
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
10192
// CHECK-NOT: loop
10293
// CHECK-NOT: call
103-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
104-
// CHECK-NOT: loop
105-
// CHECK-NOT: call
10694
vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect()
10795
}
10896

@@ -114,9 +102,6 @@ pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> {
114102
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
115103
// CHECK-NOT: loop
116104
// CHECK-NOT: call
117-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
118-
// CHECK-NOT: loop
119-
// CHECK-NOT: call
120105

121106
// Safety: For the purpose of this test we assume that Bar layout matches [u64; 4].
122107
// This currently is not guaranteed for repr(Rust) types, but it happens to work here and
@@ -133,9 +118,6 @@ pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> {
133118
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
134119
// CHECK-NOT: loop
135120
// CHECK-NOT: call
136-
// CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}})
137-
// CHECK-NOT: loop
138-
// CHECK-NOT: call
139121

140122
// Safety: For the purpose of this test we assume that Bar layout matches [u64; 4].
141123
// This currently is not guaranteed for repr(Rust) types, but it happens to work here and
@@ -156,12 +138,6 @@ pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> {
156138
// CHECK-NOT: call
157139
// CHECK-NOT: %{{.*}} = mul
158140
// CHECK-NOT: %{{.*}} = udiv
159-
// CHECK: call
160-
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
161-
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
162-
// CHECK-NOT: call
163-
// CHECK-NOT: %{{.*}} = mul
164-
// CHECK-NOT: %{{.*}} = udiv
165141

166142
vec.into_iter().map(|Wrapper(e)| e).collect()
167143
}
@@ -178,12 +154,6 @@ pub fn vec_iterator_cast_wrap_drop(vec: Vec<String>) -> Vec<Wrapper<String>> {
178154
// CHECK-NOT: call
179155
// CHECK-NOT: %{{.*}} = mul
180156
// CHECK-NOT: %{{.*}} = udiv
181-
// CHECK: call
182-
// CHECK-SAME: void @llvm.assume(i1 %{{.+}})
183-
// CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
184-
// CHECK-NOT: call
185-
// CHECK-NOT: %{{.*}} = mul
186-
// CHECK-NOT: %{{.*}} = udiv
187157
// CHECK: ret void
188158

189159
vec.into_iter().map(Wrapper).collect()

0 commit comments

Comments
 (0)