Skip to content

Commit b9ba8e5

Browse files
committed
Avoid unnecessary false edges in MIR match lowering
1 parent eef7615 commit b9ba8e5

File tree

2 files changed

+90
-57
lines changed

2 files changed

+90
-57
lines changed

src/librustc_mir/build/matches/mod.rs

+57-57
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
142142

143143
// create binding start block for link them by false edges
144144
let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::<usize>();
145-
let pre_binding_blocks: Vec<_> = (0..=candidate_count)
145+
let pre_binding_blocks: Vec<_> = (0..candidate_count)
146146
.map(|_| self.cfg.start_new_block())
147147
.collect();
148148

149-
// There's one more pre_binding block than there are candidates so that
150-
// every candidate can have a `next_candidate_pre_binding_block`.
151-
let outer_source_info = self.source_info(span);
152-
self.cfg.terminate(
153-
*pre_binding_blocks.last().unwrap(),
154-
outer_source_info,
155-
TerminatorKind::Unreachable,
156-
);
157-
158149
let mut match_has_guard = false;
159150

160151
let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
@@ -170,9 +161,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
170161
let arm_candidates: Vec<_> = arm.patterns
171162
.iter()
172163
.zip(candidate_pre_binding_blocks.by_ref())
173-
.zip(next_candidate_pre_binding_blocks.by_ref())
174164
.map(
175-
|((pattern, pre_binding_block), next_candidate_pre_binding_block)| {
165+
|(pattern, pre_binding_block)| {
176166
Candidate {
177167
span: pattern.span,
178168
match_pairs: vec![
@@ -187,7 +177,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
187177
},
188178
pre_binding_block: *pre_binding_block,
189179
next_candidate_pre_binding_block:
190-
*next_candidate_pre_binding_block,
180+
next_candidate_pre_binding_blocks.next().cloned(),
191181
}
192182
},
193183
)
@@ -224,6 +214,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
224214
&mut fake_borrows,
225215
);
226216

217+
let outer_source_info = self.source_info(span);
218+
227219
if !otherwise.is_empty() {
228220
// All matches are exhaustive. However, because some matches
229221
// only have exponentially-large exhaustive decision trees, we
@@ -250,10 +242,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
250242
};
251243

252244
// Step 5. Create everything else: the guards and the arms.
253-
254-
let outer_source_info = self.source_info(span);
255-
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| {
256-
let mut arm_block = self.cfg.start_new_block();
245+
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, mut candidates)| {
257246

258247
let body = self.hir.mirror(arm.body.clone());
259248
let scope = self.declare_bindings(
@@ -265,14 +254,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
265254
Some((Some(&scrutinee_place), scrutinee_span)),
266255
);
267256

257+
let mut arm_block;
258+
if candidates.len() == 1 {
259+
arm_block = self.bind_and_guard_matched_candidate(
260+
candidates.pop().unwrap(),
261+
arm.guard.clone(),
262+
&fake_borrow_temps,
263+
scrutinee_span,
264+
);
265+
} else {
266+
arm_block = self.cfg.start_new_block();
268267
for candidate in candidates {
269-
self.bind_and_guard_matched_candidate(
268+
let binding_end = self.bind_and_guard_matched_candidate(
270269
candidate,
271270
arm.guard.clone(),
272-
arm_block,
273271
&fake_borrow_temps,
274272
scrutinee_span,
275273
);
274+
self.cfg.terminate(
275+
binding_end,
276+
source_info,
277+
TerminatorKind::Goto { target: arm_block },
278+
);
279+
}
276280
}
277281

278282
if let Some(source_scope) = scope {
@@ -431,7 +435,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
431435
// since we don't call `match_candidates`, next fields are unused
432436
otherwise_block: None,
433437
pre_binding_block: block,
434-
next_candidate_pre_binding_block: block,
438+
next_candidate_pre_binding_block: None,
435439
};
436440

437441
// Simplify the candidate. Since the pattern is irrefutable, this should
@@ -701,7 +705,7 @@ pub struct Candidate<'pat, 'tcx: 'pat> {
701705

702706
// ...and the blocks for add false edges between candidates
703707
pre_binding_block: BasicBlock,
704-
next_candidate_pre_binding_block: BasicBlock,
708+
next_candidate_pre_binding_block: Option<BasicBlock>,
705709
}
706710

707711
#[derive(Clone, Debug)]
@@ -968,14 +972,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
968972
if let [first_candidate, second_candidate] = window {
969973
let source_info = self.source_info(first_candidate.span);
970974
if let Some(otherwise_block) = first_candidate.otherwise_block {
971-
self.cfg.terminate(
975+
self.false_edges(
972976
otherwise_block,
977+
second_candidate.pre_binding_block,
978+
first_candidate.next_candidate_pre_binding_block,
973979
source_info,
974-
TerminatorKind::FalseEdges {
975-
real_target: second_candidate.pre_binding_block,
976-
imaginary_target: first_candidate.next_candidate_pre_binding_block,
977-
}
978-
)
980+
);
979981
} else {
980982
bug!("candidate other than the last has no guard");
981983
}
@@ -989,13 +991,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
989991
if let Some(otherwise) = candidate.otherwise_block {
990992
let source_info = self.source_info(candidate.span);
991993
let unreachable = self.cfg.start_new_block();
992-
self.cfg.terminate(
994+
self.false_edges(
993995
otherwise,
996+
unreachable,
997+
candidate.next_candidate_pre_binding_block,
994998
source_info,
995-
TerminatorKind::FalseEdges {
996-
real_target: unreachable,
997-
imaginary_targets: candidate.next_candidate_pre_binding_block,
998-
}
999999
);
10001000
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
10011001
}
@@ -1006,13 +1006,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10061006
if let Some(otherwise) = last_candidate.otherwise_block {
10071007
let source_info = self.source_info(last_candidate.span);
10081008
let block = self.cfg.start_new_block();
1009-
self.cfg.terminate(
1009+
self.false_edges(
10101010
otherwise,
1011+
block,
1012+
last_candidate.next_candidate_pre_binding_block,
10111013
source_info,
1012-
TerminatorKind::FalseEdges {
1013-
real_target: block,
1014-
imaginary_target: last_candidate.next_candidate_pre_binding_block,
1015-
}
10161014
);
10171015
Some(block)
10181016
} else {
@@ -1323,26 +1321,36 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13231321
&mut self,
13241322
candidate: Candidate<'pat, 'tcx>,
13251323
guard: Option<Guard<'tcx>>,
1326-
arm_block: BasicBlock,
13271324
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13281325
scrutinee_span: Span,
1329-
) {
1326+
) -> BasicBlock {
13301327
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13311328

13321329
debug_assert!(candidate.match_pairs.is_empty());
13331330

13341331
let candidate_source_info = self.source_info(candidate.span);
13351332

1336-
let mut block = self.cfg.start_new_block();
1337-
self.cfg.terminate(
1338-
candidate.pre_binding_block,
1333+
let mut block = candidate.pre_binding_block;
1334+
1335+
// If we are adding our own statements, then we need a fresh block.
1336+
let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
1337+
|| !candidate.bindings.is_empty()
1338+
|| !candidate.ascriptions.is_empty()
1339+
|| guard.is_some();
1340+
1341+
if create_fresh_block {
1342+
let fresh_block = self.cfg.start_new_block();
1343+
self.false_edges(
1344+
block,
1345+
fresh_block,
1346+
candidate.next_candidate_pre_binding_block,
13391347
candidate_source_info,
1340-
TerminatorKind::FalseEdges {
1341-
real_target: block,
1342-
imaginary_target: candidate.next_candidate_pre_binding_block,
1343-
},
13441348
);
1349+
block = fresh_block;
13451350
self.ascribe_types(block, &candidate.ascriptions);
1351+
} else {
1352+
return block;
1353+
}
13461354

13471355
// rust-lang/rust#27282: The `autoref` business deserves some
13481356
// explanation here.
@@ -1487,7 +1495,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
14871495
// because that would be before we've checked the result
14881496
// from the guard.
14891497
//
1490-
// But binding them on `arm_block` is *too late*, because
1498+
// But binding them on the arm is *too late*, because
14911499
// then all of the candidates for a single arm would be
14921500
// bound in the same place, that would cause a case like:
14931501
//
@@ -1540,22 +1548,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15401548
by_value_bindings,
15411549
);
15421550

1543-
self.cfg.terminate(
1544-
post_guard_block,
1545-
source_info,
1546-
TerminatorKind::Goto { target: arm_block },
1547-
);
1551+
post_guard_block
15481552
} else {
15491553
assert!(candidate.otherwise_block.is_none());
15501554
// (Here, it is not too early to bind the matched
15511555
// candidate on `block`, because there is no guard result
15521556
// that we have to inspect before we bind them.)
15531557
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
1554-
self.cfg.terminate(
1555-
block,
1556-
candidate_source_info,
1557-
TerminatorKind::Goto { target: arm_block },
1558-
);
1558+
block
15591559
}
15601560
}
15611561

src/librustc_mir/build/matches/util.rs

+33
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,39 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6565
})
6666
);
6767
}
68+
69+
/// Creates a false edge to `imaginary_target` and a real edge to
70+
/// real_target. If `imaginary_target` is none, or is the same as the real
71+
/// target, a Goto is generated instead to simplify the generated MIR.
72+
pub fn false_edges(
73+
&mut self,
74+
from_block: BasicBlock,
75+
real_target: BasicBlock,
76+
imaginary_target: Option<BasicBlock>,
77+
source_info: SourceInfo,
78+
) {
79+
match imaginary_target {
80+
Some(target) if target != real_target => {
81+
self.cfg.terminate(
82+
from_block,
83+
source_info,
84+
TerminatorKind::FalseEdges {
85+
real_target,
86+
imaginary_target: target,
87+
},
88+
);
89+
}
90+
_ => {
91+
self.cfg.terminate(
92+
from_block,
93+
source_info,
94+
TerminatorKind::Goto {
95+
target: real_target
96+
}
97+
);
98+
}
99+
}
100+
}
68101
}
69102

70103
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {

0 commit comments

Comments
 (0)