Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9637884

Browse files
committedOct 6, 2024·
Auto merge of rust-lang#129931 - DianQK:match-br-copy, r=<try>
mir-opt: Merge all branch BBs into a single copy statement rust-lang#128299 simplified ```rust match a { Foo::A(x) => Foo::A(*x), Foo::B => Foo::B } ``` to ```rust match a { Foo::A(x) => a, // copy a Foo::B => Foo::B } ``` The switch branch can be simplified into a single copy statement. This PR implements a relatively general simplification.
2 parents 68301a6 + 7846743 commit 9637884

14 files changed

+611
-70
lines changed
 

‎compiler/rustc_mir_transform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ mod lower_intrinsics;
8686
mod lower_slice_len;
8787
mod match_branches;
8888
mod mentioned_items;
89+
mod merge_branches;
8990
mod multiple_return_terminators;
9091
mod nrvo;
9192
mod post_drop_elaboration;
@@ -611,6 +612,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
611612
&dead_store_elimination::DeadStoreElimination::Initial,
612613
&gvn::GVN,
613614
&simplify::SimplifyLocals::AfterGVN,
615+
&merge_branches::MergeBranchSimplification,
614616
&dataflow_const_prop::DataflowConstProp,
615617
&single_use_consts::SingleUseConsts,
616618
&o1(simplify_branches::SimplifyConstCondition::AfterConstProp),
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
//! This pass attempts to merge all branches to eliminate switch terminator.
2+
//! Ideally, we could combine it with `MatchBranchSimplification`, as these two passes
3+
//! match and merge statements with different patterns. Given the compile time and
4+
//! code complexity, we have not merged them into a more general pass for now.
5+
use rustc_const_eval::const_eval::mk_eval_cx_for_const_val;
6+
use rustc_index::bit_set::BitSet;
7+
use rustc_middle::mir::patch::MirPatch;
8+
use rustc_middle::mir::*;
9+
use rustc_middle::ty;
10+
use rustc_middle::ty::util::Discr;
11+
use rustc_middle::ty::{ParamEnv, TyCtxt};
12+
use rustc_mir_dataflow::impls::{MaybeTransitiveLiveLocals, borrowed_locals};
13+
use rustc_mir_dataflow::{Analysis, ResultsCursor};
14+
15+
pub(super) struct MergeBranchSimplification;
16+
17+
impl<'tcx> crate::MirPass<'tcx> for MergeBranchSimplification {
18+
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
19+
sess.mir_opt_level() >= 2
20+
}
21+
22+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
23+
let def_id = body.source.def_id();
24+
let param_env = tcx.param_env_reveal_all_normalized(def_id);
25+
26+
let borrowed_locals = borrowed_locals(body);
27+
let mut stmt_live_result = StatementLiveResult::new(tcx, body, &borrowed_locals);
28+
for i in 0..body.basic_blocks.len() {
29+
let bbs = &*body.basic_blocks;
30+
let switch_bb_idx = BasicBlock::from_usize(i);
31+
let Some((switch_discr, targets)) = bbs[switch_bb_idx].terminator().kind.as_switch()
32+
else {
33+
continue;
34+
};
35+
// Check if the copy source matches the following pattern.
36+
// _2 = discriminant(*_1); // "*_1" is the expected the copy source.
37+
// switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
38+
let Some(&Statement {
39+
kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(src_place))),
40+
..
41+
}) = bbs[switch_bb_idx].statements.last()
42+
else {
43+
continue;
44+
};
45+
if switch_discr.place() != Some(discr_place) {
46+
continue;
47+
}
48+
let src_ty = src_place.ty(body.local_decls(), tcx);
49+
if !src_ty.ty.is_enum() || src_ty.variant_index.is_some() {
50+
continue;
51+
}
52+
// We require that the possible target blocks all be distinct.
53+
if !targets.is_distinct() {
54+
continue;
55+
}
56+
if !bbs[targets.otherwise()].is_empty_unreachable() {
57+
continue;
58+
}
59+
// Check that destinations are identical, and if not, then don't optimize this block.
60+
let mut targets_iter = targets.iter();
61+
let first_terminator_kind = &bbs[targets_iter.next().unwrap().1].terminator().kind;
62+
if !targets_iter.all(|(_, other_target)| {
63+
first_terminator_kind == &bbs[other_target].terminator().kind
64+
}) {
65+
continue;
66+
}
67+
if let Some(dest_place) = can_simplify_to_copy(
68+
tcx,
69+
param_env,
70+
body,
71+
targets,
72+
src_place,
73+
src_ty,
74+
&mut stmt_live_result,
75+
) {
76+
let statement_index = bbs[switch_bb_idx].statements.len();
77+
let parent_end = Location { block: switch_bb_idx, statement_index };
78+
let mut patch = MirPatch::new(body);
79+
patch.add_assign(parent_end, dest_place, Rvalue::Use(Operand::Copy(src_place)));
80+
patch.patch_terminator(switch_bb_idx, first_terminator_kind.clone());
81+
patch.apply(body);
82+
super::simplify::remove_dead_blocks(body);
83+
// After modifying the MIR, the result of `MaybeTransitiveLiveLocals` may become invalid,
84+
// keeping it simple to process only once.
85+
break;
86+
}
87+
}
88+
}
89+
}
90+
91+
struct StatementLiveResult<'tcx, 'mir, 'a> {
92+
tcx: TyCtxt<'tcx>,
93+
body: &'mir Body<'tcx>,
94+
result: Option<ResultsCursor<'mir, 'tcx, MaybeTransitiveLiveLocals<'a>>>,
95+
borrowed_locals: &'a BitSet<Local>,
96+
}
97+
98+
impl<'tcx, 'mir, 'a> StatementLiveResult<'tcx, 'mir, 'a> {
99+
fn new(tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, borrowed_locals: &'a BitSet<Local>) -> Self {
100+
Self { tcx, body, result: None, borrowed_locals }
101+
}
102+
103+
fn is_live(&mut self, loc: Location, local: Local) -> bool {
104+
if self.borrowed_locals.contains(local) {
105+
return true;
106+
}
107+
let maybe_live = self.result.get_or_insert_with(|| {
108+
MaybeTransitiveLiveLocals::new(&self.borrowed_locals)
109+
.into_engine(self.tcx, self.body)
110+
.iterate_to_fixpoint()
111+
.into_results_cursor(self.body)
112+
});
113+
maybe_live.seek_before_primary_effect(loc);
114+
maybe_live.get().contains(local)
115+
}
116+
}
117+
118+
/// The GVN simplified
119+
/// ```ignore (syntax-highlighting-only)
120+
/// match a {
121+
/// Foo::A(x) => Foo::A(*x),
122+
/// Foo::B => Foo::B
123+
/// }
124+
/// ```
125+
/// to
126+
/// ```ignore (syntax-highlighting-only)
127+
/// match a {
128+
/// Foo::A(_x) => a, // copy a
129+
/// Foo::B => Foo::B
130+
/// }
131+
/// ```
132+
/// This function answers whether it can be simplified to a copy statement
133+
/// by returning the copy destination.
134+
fn can_simplify_to_copy<'tcx>(
135+
tcx: TyCtxt<'tcx>,
136+
param_env: ParamEnv<'tcx>,
137+
body: &Body<'tcx>,
138+
targets: &SwitchTargets,
139+
src_place: Place<'tcx>,
140+
src_ty: tcx::PlaceTy<'tcx>,
141+
stmt_live_result: &mut StatementLiveResult<'tcx, '_, '_>,
142+
) -> Option<Place<'tcx>> {
143+
let mut targets_iter = targets.iter();
144+
let dest_place = targets_iter.next().and_then(|(index, target)| {
145+
find_copy_assign(tcx, param_env, body, index, target, src_place, src_ty, stmt_live_result)
146+
})?;
147+
let dest_ty = dest_place.ty(body.local_decls(), tcx);
148+
if dest_ty.ty != src_ty.ty || dest_ty.variant_index.is_some() {
149+
return None;
150+
}
151+
if targets_iter.any(|(other_index, other_target)| {
152+
Some(dest_place)
153+
!= find_copy_assign(
154+
tcx,
155+
param_env,
156+
body,
157+
other_index,
158+
other_target,
159+
src_place,
160+
src_ty,
161+
stmt_live_result,
162+
)
163+
}) {
164+
return None;
165+
}
166+
Some(dest_place)
167+
}
168+
169+
fn find_copy_assign<'tcx>(
170+
tcx: TyCtxt<'tcx>,
171+
param_env: ParamEnv<'tcx>,
172+
body: &Body<'tcx>,
173+
index: u128,
174+
target_block: BasicBlock,
175+
src_place: Place<'tcx>,
176+
src_ty: tcx::PlaceTy<'tcx>,
177+
stmt_live_result: &mut StatementLiveResult<'tcx, '_, '_>,
178+
) -> Option<Place<'tcx>> {
179+
let statements = &body.basic_blocks[target_block].statements;
180+
if statements.is_empty() {
181+
return None;
182+
}
183+
let assign_stmt = if statements.len() == 1 {
184+
0
185+
} else {
186+
// We are matching a statement copied from the source to the same destination from the BB,
187+
// and dead statements can be ignored.
188+
// We can treat the rvalue is the source if it's equal to the source.
189+
let mut lived_stmts: BitSet<usize> = BitSet::new_filled(statements.len());
190+
let mut expected_assign_stmt = None;
191+
for (statement_index, statement) in statements.iter().enumerate().rev() {
192+
let loc = Location { block: target_block, statement_index };
193+
if let StatementKind::Assign(assign) = &statement.kind {
194+
if !assign.1.is_safe_to_remove() {
195+
return None;
196+
}
197+
}
198+
match &statement.kind {
199+
StatementKind::Assign(box (dest_place, _))
200+
| StatementKind::SetDiscriminant { place: box dest_place, .. }
201+
| StatementKind::Deinit(box dest_place) => {
202+
if dest_place.is_indirect() {
203+
return None;
204+
}
205+
if !stmt_live_result.is_live(loc, dest_place.local) {
206+
lived_stmts.remove(statement_index);
207+
} else if matches!(statement.kind, StatementKind::Assign(_))
208+
&& expected_assign_stmt.is_none()
209+
{
210+
// There is only one statement that cannot be ignored
211+
// that can be used as an expected copy statement.
212+
expected_assign_stmt = Some(statement_index);
213+
lived_stmts.remove(statement_index);
214+
} else {
215+
return None;
216+
}
217+
}
218+
StatementKind::StorageLive(_)
219+
| StatementKind::StorageDead(_)
220+
| StatementKind::Nop => (),
221+
222+
StatementKind::Retag(_, _)
223+
| StatementKind::Coverage(_)
224+
| StatementKind::Intrinsic(_)
225+
| StatementKind::ConstEvalCounter
226+
| StatementKind::PlaceMention(_)
227+
| StatementKind::FakeRead(_)
228+
| StatementKind::AscribeUserType(_, _) => {
229+
return None;
230+
}
231+
}
232+
}
233+
let expected_assign = expected_assign_stmt?;
234+
// We can ignore the paired StorageLive and StorageDead.
235+
let mut storage_live_locals: BitSet<Local> = BitSet::new_empty(body.local_decls.len());
236+
for stmt_index in lived_stmts.iter() {
237+
let statement = &statements[stmt_index];
238+
match &statement.kind {
239+
StatementKind::StorageLive(local) if storage_live_locals.insert(*local) => {}
240+
StatementKind::StorageDead(local) if storage_live_locals.remove(*local) => {}
241+
StatementKind::Nop => {}
242+
_ => return None,
243+
}
244+
}
245+
if !storage_live_locals.is_empty() {
246+
return None;
247+
}
248+
expected_assign
249+
};
250+
let Statement { kind: StatementKind::Assign(box (dest_place, ref rvalue)), .. } =
251+
statements[assign_stmt]
252+
else {
253+
return None;
254+
};
255+
let dest_ty = dest_place.ty(body.local_decls(), tcx);
256+
if dest_ty.ty != src_ty.ty || dest_ty.variant_index.is_some() {
257+
return None;
258+
}
259+
let ty::Adt(def, _) = dest_ty.ty.kind() else {
260+
return None;
261+
};
262+
match rvalue {
263+
// Check if `_3 = const Foo::B` can be transformed to `_3 = copy *_1`.
264+
Rvalue::Use(Operand::Constant(box constant))
265+
if let Const::Val(const_, ty) = constant.const_ =>
266+
{
267+
let (ecx, op) = mk_eval_cx_for_const_val(tcx.at(constant.span), param_env, const_, ty)?;
268+
let variant = ecx.read_discriminant(&op).discard_err()?;
269+
if !def.variants()[variant].fields.is_empty() {
270+
return None;
271+
}
272+
let Discr { val, .. } = ty.discriminant_for_variant(tcx, variant)?;
273+
if val != index {
274+
return None;
275+
}
276+
}
277+
Rvalue::Use(Operand::Copy(place)) if *place == src_place => {}
278+
// Check if `_3 = Foo::B` can be transformed to `_3 = copy *_1`.
279+
Rvalue::Aggregate(box AggregateKind::Adt(_, variant_index, _, _, None), fields)
280+
if fields.is_empty()
281+
&& let Some(Discr { val, .. }) =
282+
src_ty.ty.discriminant_for_variant(tcx, *variant_index)
283+
&& val == index => {}
284+
_ => return None,
285+
}
286+
Some(dest_place)
287+
}

‎tests/codegen/match-optimizes-away.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//
2-
//@ compile-flags: -O
1+
//@ compile-flags: -O -Cno-prepopulate-passes
2+
33
#![crate_type = "lib"]
44

55
pub enum Three {
@@ -19,8 +19,9 @@ pub enum Four {
1919
#[no_mangle]
2020
pub fn three_valued(x: Three) -> Three {
2121
// CHECK-LABEL: @three_valued
22-
// CHECK-NEXT: {{^.*:$}}
23-
// CHECK-NEXT: ret i8 %0
22+
// CHECK-SAME: (i8{{.*}} [[X:%x]])
23+
// CHECK-NEXT: start:
24+
// CHECK-NEXT: ret i8 [[X]]
2425
match x {
2526
Three::A => Three::A,
2627
Three::B => Three::B,
@@ -31,8 +32,9 @@ pub fn three_valued(x: Three) -> Three {
3132
#[no_mangle]
3233
pub fn four_valued(x: Four) -> Four {
3334
// CHECK-LABEL: @four_valued
34-
// CHECK-NEXT: {{^.*:$}}
35-
// CHECK-NEXT: ret i16 %0
35+
// CHECK-SAME: (i16{{.*}} [[X:%x]])
36+
// CHECK-NEXT: start:
37+
// CHECK-NEXT: ret i16 [[X]]
3638
match x {
3739
Four::A => Four::A,
3840
Four::B => Four::B,

‎tests/codegen/try_question_mark_nop.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
//@ compile-flags: -O -Z merge-functions=disabled --edition=2021
22
//@ only-x86_64
3-
// FIXME: Remove the `min-llvm-version`.
4-
//@ revisions: NINETEEN TWENTY
5-
//@[NINETEEN] min-llvm-version: 19
6-
//@[NINETEEN] ignore-llvm-version: 20-99
7-
//@[TWENTY] min-llvm-version: 20
83

94
#![crate_type = "lib"]
105
#![feature(try_blocks)]
@@ -16,10 +11,7 @@ use std::ptr::NonNull;
1611
#[no_mangle]
1712
pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> {
1813
// CHECK: start:
19-
// NINETEEN-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1
20-
// NINETEEN-NEXT: [[FIRST:%.*]] = select i1 [[TRUNC]], i32 %0
21-
// NINETEEN-NEXT: insertvalue { i32, i32 } poison, i32 [[FIRST]], 0
22-
// TWENTY-NEXT: insertvalue { i32, i32 } poison, i32 %0, 0
14+
// CHECK-NEXT: insertvalue { i32, i32 }
2315
// CHECK-NEXT: insertvalue { i32, i32 }
2416
// CHECK-NEXT: ret { i32, i32 }
2517
match x {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
- // MIR for `no_fields` before MergeBranchSimplification
2+
+ // MIR for `no_fields` after MergeBranchSimplification
3+
4+
fn no_fields(_1: NoFields) -> NoFields {
5+
debug a => _1;
6+
let mut _0: NoFields;
7+
let mut _2: isize;
8+
9+
bb0: {
10+
_2 = discriminant(_1);
11+
- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
12+
+ _0 = copy _1;
13+
+ goto -> bb1;
14+
}
15+
16+
bb1: {
17+
- unreachable;
18+
- }
19+
-
20+
- bb2: {
21+
- _0 = NoFields::B;
22+
- goto -> bb4;
23+
- }
24+
-
25+
- bb3: {
26+
- _0 = NoFields::A;
27+
- goto -> bb4;
28+
- }
29+
-
30+
- bb4: {
31+
return;
32+
}
33+
}
34+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
- // MIR for `no_fields_failed` before MergeBranchSimplification
2+
+ // MIR for `no_fields_failed` after MergeBranchSimplification
3+
4+
fn no_fields_failed(_1: NoFields) -> NoFields {
5+
debug a => _1;
6+
let mut _0: NoFields;
7+
let mut _2: isize;
8+
9+
bb0: {
10+
_2 = discriminant(_1);
11+
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
12+
}
13+
14+
bb1: {
15+
unreachable;
16+
}
17+
18+
bb2: {
19+
_0 = NoFields::A;
20+
goto -> bb4;
21+
}
22+
23+
bb3: {
24+
_0 = NoFields::B;
25+
goto -> bb4;
26+
}
27+
28+
bb4: {
29+
return;
30+
}
31+
}
32+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
- // MIR for `no_fields_mismatch_type_failed` before MergeBranchSimplification
2+
+ // MIR for `no_fields_mismatch_type_failed` after MergeBranchSimplification
3+
4+
fn no_fields_mismatch_type_failed(_1: NoFields) -> NoFields2 {
5+
debug a => _1;
6+
let mut _0: NoFields2;
7+
let mut _2: isize;
8+
9+
bb0: {
10+
_2 = discriminant(_1);
11+
switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
12+
}
13+
14+
bb1: {
15+
unreachable;
16+
}
17+
18+
bb2: {
19+
_0 = NoFields2::B;
20+
goto -> bb4;
21+
}
22+
23+
bb3: {
24+
_0 = NoFields2::A;
25+
goto -> bb4;
26+
}
27+
28+
bb4: {
29+
return;
30+
}
31+
}
32+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
- // MIR for `no_fields_ref` before MergeBranchSimplification
2+
+ // MIR for `no_fields_ref` after MergeBranchSimplification
3+
4+
fn no_fields_ref(_1: &NoFields) -> NoFields {
5+
debug a => _1;
6+
let mut _0: NoFields;
7+
let mut _2: isize;
8+
9+
bb0: {
10+
_2 = discriminant((*_1));
11+
- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1];
12+
+ _0 = copy (*_1);
13+
+ goto -> bb1;
14+
}
15+
16+
bb1: {
17+
- unreachable;
18+
- }
19+
-
20+
- bb2: {
21+
- _0 = NoFields::B;
22+
- goto -> bb4;
23+
- }
24+
-
25+
- bb3: {
26+
- _0 = NoFields::A;
27+
- goto -> bb4;
28+
- }
29+
-
30+
- bb4: {
31+
return;
32+
}
33+
}
34+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
- // MIR for `option` before MergeBranchSimplification
2+
+ // MIR for `option` after MergeBranchSimplification
3+
4+
fn option(_1: Option<i32>) -> Option<i32> {
5+
debug a => _1;
6+
let mut _0: std::option::Option<i32>;
7+
let mut _2: isize;
8+
let _3: i32;
9+
scope 1 {
10+
debug _b => _3;
11+
}
12+
13+
bb0: {
14+
_2 = discriminant(_1);
15+
- switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
16+
- }
17+
-
18+
- bb1: {
19+
- unreachable;
20+
- }
21+
-
22+
- bb2: {
23+
- _0 = Option::<i32>::None;
24+
- goto -> bb4;
25+
- }
26+
-
27+
- bb3: {
28+
- StorageLive(_3);
29+
- _3 = copy ((_1 as Some).0: i32);
30+
_0 = copy _1;
31+
- StorageDead(_3);
32+
- goto -> bb4;
33+
+ goto -> bb1;
34+
}
35+
36+
- bb4: {
37+
+ bb1: {
38+
return;
39+
}
40+
}
41+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
- // MIR for `option_dse_failed` before MergeBranchSimplification
2+
+ // MIR for `option_dse_failed` after MergeBranchSimplification
3+
4+
fn option_dse_failed(_1: Option<i32>, _2: &mut i32) -> Option<i32> {
5+
debug a => _1;
6+
debug b => _2;
7+
let mut _0: std::option::Option<i32>;
8+
let mut _3: isize;
9+
10+
bb0: {
11+
_3 = discriminant(_1);
12+
switchInt(move _3) -> [0: bb2, 1: bb3, otherwise: bb1];
13+
}
14+
15+
bb1: {
16+
unreachable;
17+
}
18+
19+
bb2: {
20+
_0 = Option::<i32>::None;
21+
goto -> bb4;
22+
}
23+
24+
bb3: {
25+
(*_2) = const 1_i32;
26+
_0 = copy _1;
27+
goto -> bb4;
28+
}
29+
30+
bb4: {
31+
return;
32+
}
33+
}
34+

‎tests/mir-opt/merge_br/merge_br.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//@ test-mir-pass: MergeBranchSimplification
2+
//@ compile-flags: -Cdebuginfo=2
3+
4+
pub enum NoFields {
5+
A,
6+
B,
7+
}
8+
9+
pub enum NoFields2 {
10+
A,
11+
B,
12+
}
13+
14+
// EMIT_MIR merge_br.no_fields.MergeBranchSimplification.diff
15+
pub fn no_fields(a: NoFields) -> NoFields {
16+
// CHECK-LABEL: no_fields(
17+
// CHECK: bb0: {
18+
// CHECK-NEXT: _{{.*}} = discriminant([[SRC:_1]]);
19+
// CHECK-NEXT: _0 = copy [[SRC]];
20+
// CHECK-NEXT return;
21+
match a {
22+
NoFields::A => NoFields::A,
23+
NoFields::B => NoFields::B,
24+
}
25+
}
26+
27+
// EMIT_MIR merge_br.no_fields_ref.MergeBranchSimplification.diff
28+
pub fn no_fields_ref(a: &NoFields) -> NoFields {
29+
// CHECK-LABEL: no_fields_ref(
30+
// CHECK: bb0: {
31+
// CHECK-NEXT: _{{.*}} = discriminant([[SRC:\(\*_1\)]]);
32+
// CHECK-NEXT: _0 = copy [[SRC]];
33+
// CHECK-NEXT return;
34+
match a {
35+
NoFields::A => NoFields::A,
36+
NoFields::B => NoFields::B,
37+
}
38+
}
39+
40+
// EMIT_MIR merge_br.no_fields_mismatch_type_failed.MergeBranchSimplification.diff
41+
pub fn no_fields_mismatch_type_failed(a: NoFields) -> NoFields2 {
42+
// CHECK-LABEL: no_fields_mismatch_type_failed(
43+
// CHECK: bb0: {
44+
// CHECK-NEXT: _{{.*}} = discriminant([[SRC:_1]]);
45+
// CHECK-NOT: _0 = copy [[SRC]];
46+
match a {
47+
NoFields::A => NoFields2::A,
48+
NoFields::B => NoFields2::B,
49+
}
50+
}
51+
52+
// EMIT_MIR merge_br.no_fields_failed.MergeBranchSimplification.diff
53+
pub fn no_fields_failed(a: NoFields) -> NoFields {
54+
// CHECK-LABEL: no_fields_failed(
55+
// CHECK: bb0: {
56+
// CHECK-NEXT: _{{.*}} = discriminant([[SRC:_1]]);
57+
// CHECK-NOT: _0 = copy [[SRC]];
58+
match a {
59+
NoFields::A => NoFields::B,
60+
NoFields::B => NoFields::A,
61+
}
62+
}
63+
64+
// EMIT_MIR merge_br.option.MergeBranchSimplification.diff
65+
pub fn option(a: Option<i32>) -> Option<i32> {
66+
// CHECK-LABEL: option(
67+
// CHECK: bb0: {
68+
// CHECK-NEXT: _{{.*}} = discriminant([[SRC:_1]]);
69+
// CHECK-NEXT: _0 = copy [[SRC]];
70+
// CHECK-NEXT return;
71+
match a {
72+
Some(_b) => a,
73+
None => None,
74+
}
75+
}
76+
77+
// EMIT_MIR merge_br.option_dse_failed.MergeBranchSimplification.diff
78+
pub fn option_dse_failed(a: Option<i32>, b: &mut i32) -> Option<i32> {
79+
// CHECK-LABEL: option_dse_failed(
80+
// CHECK: bb0: {
81+
// CHECK-NEXT: [[DISCR:_.*]] = discriminant([[SRC:_1]]);
82+
// CHECK-NEXT: switchInt(move [[DISCR]])
83+
match a {
84+
Some(_) => {
85+
*b = 1;
86+
a
87+
}
88+
None => None,
89+
}
90+
}

‎tests/mir-opt/pre-codegen/clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 {
55
let mut _0: Enum1;
66
scope 1 (inlined <Enum1 as Clone>::clone) {
77
debug self => _1;
8-
let mut _2: isize;
8+
let _2: &AllCopy;
99
let mut _3: &AllCopy;
10-
let mut _4: &NestCopy;
10+
let _4: &NestCopy;
11+
let mut _5: &NestCopy;
1112
scope 2 {
12-
debug __self_0 => _3;
13+
debug __self_0 => _2;
1314
scope 6 (inlined <AllCopy as Clone>::clone) {
1415
debug self => _3;
1516
}
1617
}
1718
scope 3 {
1819
debug __self_0 => _4;
1920
scope 4 (inlined <NestCopy as Clone>::clone) {
20-
debug self => _4;
21-
let _5: &AllCopy;
21+
debug self => _5;
22+
let _6: &AllCopy;
2223
scope 5 (inlined <AllCopy as Clone>::clone) {
23-
debug self => _5;
24+
debug self => _6;
2425
}
2526
}
2627
}
@@ -30,33 +31,14 @@ fn enum_clone_as_copy(_1: &Enum1) -> Enum1 {
3031
StorageLive(_2);
3132
StorageLive(_3);
3233
StorageLive(_4);
33-
_2 = discriminant((*_1));
34-
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4];
35-
}
36-
37-
bb1: {
38-
_3 = &(((*_1) as A).0: AllCopy);
39-
_0 = copy (*_1);
40-
goto -> bb3;
41-
}
42-
43-
bb2: {
44-
_4 = &(((*_1) as B).0: NestCopy);
4534
StorageLive(_5);
46-
_5 = &((((*_1) as B).0: NestCopy).1: AllCopy);
47-
StorageDead(_5);
35+
StorageLive(_6);
4836
_0 = copy (*_1);
49-
goto -> bb3;
50-
}
51-
52-
bb3: {
37+
StorageDead(_6);
38+
StorageDead(_5);
5339
StorageDead(_4);
5440
StorageDead(_3);
5541
StorageDead(_2);
5642
return;
5743
}
58-
59-
bb4: {
60-
unreachable;
61-
}
6244
}

‎tests/mir-opt/pre-codegen/clone_as_copy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ fn clone_as_copy(v: &NestCopy) -> NestCopy {
3232
v.clone()
3333
}
3434

35-
// FIXME: We can merge into exactly one assignment statement.
35+
// We can merge into exactly one assignment statement.
3636
// EMIT_MIR clone_as_copy.enum_clone_as_copy.PreCodegen.after.mir
3737
fn enum_clone_as_copy(v: &Enum1) -> Enum1 {
3838
// CHECK-LABEL: fn enum_clone_as_copy(
3939
// CHECK-NOT: = Enum1::
4040
// CHECK: _0 = copy (*_1);
41-
// CHECK: _0 = copy (*_1);
41+
// CHECK-NOT: _0 = copy (*_1);
4242
v.clone()
4343
}

‎tests/mir-opt/pre-codegen/try_identity.old.PreCodegen.after.mir

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,17 @@
33
fn old(_1: Result<T, E>) -> Result<T, E> {
44
debug x => _1;
55
let mut _0: std::result::Result<T, E>;
6-
let mut _2: isize;
7-
let _3: T;
8-
let _4: E;
6+
let _2: T;
7+
let _3: E;
98
scope 1 {
10-
debug v => _3;
9+
debug v => _2;
1110
}
1211
scope 2 {
13-
debug e => _4;
12+
debug e => _3;
1413
}
1514

1615
bb0: {
17-
_2 = discriminant(_1);
18-
switchInt(move _2) -> [0: bb1, 1: bb2, otherwise: bb4];
19-
}
20-
21-
bb1: {
22-
_3 = copy ((_1 as Ok).0: T);
23-
_0 = copy _1;
24-
goto -> bb3;
25-
}
26-
27-
bb2: {
28-
_4 = copy ((_1 as Err).0: E);
2916
_0 = copy _1;
30-
goto -> bb3;
31-
}
32-
33-
bb3: {
3417
return;
3518
}
36-
37-
bb4: {
38-
unreachable;
39-
}
4019
}

0 commit comments

Comments
 (0)
This repository has been archived.