Skip to content

Commit 13f3924

Browse files
committed
Auto merge of #135322 - scottmcm:inst-simplify-repeat-one, r=oli-obk
[mir-opt] simplify `Repeat`s that don't actually repeat the operand Created because when I was writing this case in GVN https://github.com/rust-lang/rust/pull/133324/files#diff-292b215fdc6b59e3f3c4173c2270b14591c5673832fbfb05cd69a05c2ef0c30eR977-R979 I happened to notice that it worked for `[x]` but not for `[x; 1]`, so figured it would be good to simplify that `Repeat` to the simpler `Aggregate`.
2 parents 12445e0 + 7396ec3 commit 13f3924

7 files changed

+149
-29
lines changed

compiler/rustc_mir_transform/src/instsimplify.rs

+13
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify {
4949
ctx.simplify_ptr_aggregate(rvalue);
5050
ctx.simplify_cast(rvalue);
5151
ctx.simplify_repeated_aggregate(rvalue);
52+
ctx.simplify_repeat_once(rvalue);
5253
}
5354
_ => {}
5455
}
@@ -207,6 +208,18 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> {
207208
}
208209
}
209210

211+
/// Simplify `[x; 1]` to just `[x]`.
212+
fn simplify_repeat_once(&self, rvalue: &mut Rvalue<'tcx>) {
213+
if let Rvalue::Repeat(operand, count) = rvalue
214+
&& let Some(1) = count.try_to_target_usize(self.tcx)
215+
{
216+
*rvalue = Rvalue::Aggregate(
217+
Box::new(AggregateKind::Array(operand.ty(self.local_decls, self.tcx))),
218+
[operand.clone()].into(),
219+
);
220+
}
221+
}
222+
210223
fn simplify_primitive_clone(
211224
&self,
212225
terminator: &mut Terminator<'tcx>,

compiler/rustc_mir_transform/src/remove_zsts.rs

+25-17
Original file line numberDiff line numberDiff line change
@@ -36,31 +36,39 @@ struct Replacer<'a, 'tcx> {
3636
}
3737

3838
/// A cheap, approximate check to avoid unnecessary `layout_of` calls.
39-
fn maybe_zst(ty: Ty<'_>) -> bool {
39+
///
40+
/// `Some(true)` is definitely ZST; `Some(false)` is definitely *not* ZST.
41+
///
42+
/// `None` may or may not be, and must check `layout_of` to be sure.
43+
fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
4044
match ty.kind() {
41-
// maybe ZST (could be more precise)
42-
ty::Adt(..)
43-
| ty::Array(..)
44-
| ty::Closure(..)
45-
| ty::CoroutineClosure(..)
46-
| ty::Tuple(..)
47-
| ty::Alias(ty::Opaque, ..) => true,
4845
// definitely ZST
49-
ty::FnDef(..) | ty::Never => true,
50-
// unreachable or can't be ZST
51-
_ => false,
46+
ty::FnDef(..) | ty::Never => Some(true),
47+
ty::Tuple(fields) if fields.is_empty() => Some(true),
48+
ty::Array(_ty, len) if let Some(0) = len.try_to_target_usize(tcx) => Some(true),
49+
// clearly not ZST
50+
ty::Bool
51+
| ty::Char
52+
| ty::Int(..)
53+
| ty::Uint(..)
54+
| ty::Float(..)
55+
| ty::RawPtr(..)
56+
| ty::Ref(..)
57+
| ty::FnPtr(..) => Some(false),
58+
// check `layout_of` to see (including unreachable things we won't actually see)
59+
_ => None,
5260
}
5361
}
5462

5563
impl<'tcx> Replacer<'_, 'tcx> {
5664
fn known_to_be_zst(&self, ty: Ty<'tcx>) -> bool {
57-
if !maybe_zst(ty) {
58-
return false;
65+
if let Some(is_zst) = trivially_zst(ty, self.tcx) {
66+
is_zst
67+
} else {
68+
self.tcx
69+
.layout_of(self.typing_env.as_query_input(ty))
70+
.is_ok_and(|layout| layout.is_zst())
5971
}
60-
let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(ty)) else {
61-
return false;
62-
};
63-
layout.is_zst()
6472
}
6573

6674
fn make_zst(&self, ty: Ty<'tcx>) -> ConstOperand<'tcx> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- // MIR for `repeat_once_to_aggregate` before InstSimplify-after-simplifycfg
2+
+ // MIR for `repeat_once_to_aggregate` after InstSimplify-after-simplifycfg
3+
4+
fn repeat_once_to_aggregate(_1: T) -> [T; 1] {
5+
debug x => _1;
6+
let mut _0: [T; 1];
7+
let _2: [T; 1];
8+
let mut _3: T;
9+
let mut _4: T;
10+
scope 1 {
11+
debug other => _2;
12+
}
13+
14+
bb0: {
15+
StorageLive(_2);
16+
StorageLive(_3);
17+
_3 = copy _1;
18+
- _2 = [move _3; 1];
19+
+ _2 = [move _3];
20+
StorageDead(_3);
21+
StorageLive(_4);
22+
_4 = copy _1;
23+
- _0 = [move _4; 1];
24+
+ _0 = [move _4];
25+
StorageDead(_4);
26+
StorageDead(_2);
27+
return;
28+
}
29+
}
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ test-mir-pass: InstSimplify-after-simplifycfg
2+
//@ compile-flags: -C panic=abort
3+
#![crate_type = "lib"]
4+
5+
const MYSTERY: usize = 3_usize.pow(2) - 2_usize.pow(3);
6+
7+
// EMIT_MIR simplify_repeat.repeat_once_to_aggregate.InstSimplify-after-simplifycfg.diff
8+
pub fn repeat_once_to_aggregate<T: Copy>(x: T) -> [T; 1] {
9+
// CHECK-LABEL: fn repeat_once_to_aggregate(
10+
// CHECK: debug other => [[OTHER:_[0-9]+]]
11+
// CHECK-NOT: [move {{_[0-9]+}}; 1]
12+
// CHECK: [[OTHER]] = [move {{_[0-9]+}}];
13+
// CHECK-NOT: [move {{_[0-9]+}}; 1]
14+
// CHECK: _0 = [move {{_[0-9]+}}];
15+
// CHECK-NOT: [move {{_[0-9]+}}; 1]
16+
17+
let other = [x; MYSTERY];
18+
19+
[x; 1]
20+
}

tests/mir-opt/remove_zsts.get_union.PreCodegen.after.mir

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
- // MIR for `remove_generic_array` before RemoveZsts
2+
+ // MIR for `remove_generic_array` after RemoveZsts
3+
4+
fn remove_generic_array(_1: T) -> () {
5+
debug x => _1;
6+
let mut _0: ();
7+
let _2: [T; 0];
8+
let mut _3: T;
9+
let mut _5: T;
10+
scope 1 {
11+
- debug a => _2;
12+
+ debug a => const ZeroSized: [T; 0];
13+
let _4: [T; 0];
14+
scope 2 {
15+
- debug b => _4;
16+
+ debug b => const ZeroSized: [T; 0];
17+
}
18+
}
19+
20+
bb0: {
21+
- StorageLive(_2);
22+
+ nop;
23+
StorageLive(_3);
24+
_3 = copy _1;
25+
- _2 = [];
26+
+ nop;
27+
StorageDead(_3);
28+
- StorageLive(_4);
29+
+ nop;
30+
StorageLive(_5);
31+
_5 = copy _1;
32+
- _4 = [];
33+
+ nop;
34+
StorageDead(_5);
35+
- _0 = const ();
36+
- StorageDead(_4);
37+
- StorageDead(_2);
38+
+ nop;
39+
+ nop;
40+
+ nop;
41+
return;
42+
}
43+
}
44+

tests/mir-opt/remove_zsts.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
1-
// skip-filecheck
1+
//@ test-mir-pass: RemoveZsts
2+
23
union Foo {
34
x: (),
45
y: u64,
56
}
67

78
// EMIT_MIR remove_zsts.get_union.RemoveZsts.diff
8-
// EMIT_MIR remove_zsts.get_union.PreCodegen.after.mir
99
fn get_union() -> Foo {
10+
// CHECK-LABEL: fn get_union
11+
// CHECK: _0 = Foo { x: const () };
1012
Foo { x: () }
1113
}
1214

15+
const MYSTERY: usize = 280_usize.isqrt() - 260_usize.isqrt();
16+
17+
// EMIT_MIR remove_zsts.remove_generic_array.RemoveZsts.diff
18+
fn remove_generic_array<T: Copy>(x: T) {
19+
// CHECK-LABEL: fn remove_generic_array
20+
// CHECK: debug a => const ZeroSized: [T; 0];
21+
// CHECK: debug b => const ZeroSized: [T; 0];
22+
// CHECK-NOT: = [];
23+
// CHECK-NOT: ; 1]
24+
let a = [x; 0];
25+
let b = [x; MYSTERY];
26+
}
27+
1328
fn main() {
1429
get_union();
1530
}

0 commit comments

Comments
 (0)