Skip to content

Commit adcf24a

Browse files
committed
Avoid unnecessary false edges in MIR match lowering
1 parent 2f5807c commit adcf24a

File tree

2 files changed

+98
-53
lines changed

2 files changed

+98
-53
lines changed

src/librustc_mir/build/matches/mod.rs

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -143,19 +143,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
143143

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

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

161152
let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
@@ -171,9 +162,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
171162
let arm_candidates: Vec<_> = arm.patterns
172163
.iter()
173164
.zip(candidate_pre_binding_blocks.by_ref())
174-
.zip(next_candidate_pre_binding_blocks.by_ref())
175165
.map(
176-
|((pattern, pre_binding_block), next_candidate_pre_binding_block)| {
166+
|(pattern, pre_binding_block)| {
177167
Candidate {
178168
span: pattern.span,
179169
match_pairs: vec![
@@ -188,7 +178,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
188178
},
189179
pre_binding_block: *pre_binding_block,
190180
next_candidate_pre_binding_block:
191-
*next_candidate_pre_binding_block,
181+
next_candidate_pre_binding_blocks.next().copied(),
192182
}
193183
},
194184
)
@@ -225,6 +215,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
225215
&mut fake_borrows,
226216
);
227217

218+
let outer_source_info = self.source_info(span);
219+
228220
if !otherwise.is_empty() {
229221
// All matches are exhaustive. However, because some matches
230222
// only have exponentially-large exhaustive decision trees, we
@@ -251,12 +243,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
251243
};
252244

253245
// Step 5. Create everything else: the guards and the arms.
254-
255-
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| {
246+
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, mut candidates)| {
256247
let arm_source_info = self.source_info(arm.span);
257248
let region_scope = (arm.scope, arm_source_info);
258249
self.in_scope(region_scope, arm.lint_level, |this| {
259-
let arm_block = this.cfg.start_new_block();
250+
let mut arm_block = this.cfg.start_new_block();
260251

261252
let body = this.hir.mirror(arm.body.clone());
262253
let scope = this.declare_bindings(
@@ -267,6 +258,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
267258
Some((Some(&scrutinee_place), scrutinee_span)),
268259
);
269260

261+
if candidates.len() == 1 {
262+
arm_block = self.bind_and_guard_matched_candidate(
263+
candidates.pop().unwrap(),
264+
arm.guard.clone(),
265+
&fake_borrow_temps,
266+
scrutinee_span,
267+
);
268+
} else {
269+
arm_block = self.cfg.start_new_block();
270+
for candidate in candidates {
271+
let binding_end = self.bind_and_guard_matched_candidate(
272+
candidate,
273+
arm.guard.clone(),
274+
&fake_borrow_temps,
275+
scrutinee_span,
276+
);
277+
self.cfg.terminate(
278+
binding_end,
279+
source_info,
280+
TerminatorKind::Goto { target: arm_block },
281+
);
282+
}
283+
}
284+
270285
if let Some(source_scope) = scope {
271286
this.source_scope = source_scope;
272287
}
@@ -434,7 +449,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
434449
// since we don't call `match_candidates`, next fields are unused
435450
otherwise_block: None,
436451
pre_binding_block: block,
437-
next_candidate_pre_binding_block: block,
452+
next_candidate_pre_binding_block: None,
438453
};
439454

440455
// Simplify the candidate. Since the pattern is irrefutable, this should
@@ -691,7 +706,7 @@ pub struct Candidate<'pat, 'tcx: 'pat> {
691706

692707
// ...and the blocks for add false edges between candidates
693708
pre_binding_block: BasicBlock,
694-
next_candidate_pre_binding_block: BasicBlock,
709+
next_candidate_pre_binding_block: Option<BasicBlock>,
695710
}
696711

697712
#[derive(Clone, Debug)]
@@ -958,14 +973,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
958973
if let [first_candidate, second_candidate] = window {
959974
let source_info = self.source_info(first_candidate.span);
960975
if let Some(otherwise_block) = first_candidate.otherwise_block {
961-
self.cfg.terminate(
976+
self.false_edges(
962977
otherwise_block,
978+
second_candidate.pre_binding_block,
979+
first_candidate.next_candidate_pre_binding_block,
963980
source_info,
964-
TerminatorKind::FalseEdges {
965-
real_target: second_candidate.pre_binding_block,
966-
imaginary_target: first_candidate.next_candidate_pre_binding_block,
967-
}
968-
)
981+
);
969982
} else {
970983
bug!("candidate other than the last has no guard");
971984
}
@@ -979,13 +992,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
979992
if let Some(otherwise) = candidate.otherwise_block {
980993
let source_info = self.source_info(candidate.span);
981994
let unreachable = self.cfg.start_new_block();
982-
self.cfg.terminate(
995+
self.false_edges(
983996
otherwise,
997+
unreachable,
998+
candidate.next_candidate_pre_binding_block,
984999
source_info,
985-
TerminatorKind::FalseEdges {
986-
real_target: unreachable,
987-
imaginary_targets: candidate.next_candidate_pre_binding_block,
988-
}
9891000
);
9901001
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
9911002
}
@@ -996,13 +1007,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9961007
if let Some(otherwise) = last_candidate.otherwise_block {
9971008
let source_info = self.source_info(last_candidate.span);
9981009
let block = self.cfg.start_new_block();
999-
self.cfg.terminate(
1010+
self.false_edges(
10001011
otherwise,
1012+
block,
1013+
last_candidate.next_candidate_pre_binding_block,
10011014
source_info,
1002-
TerminatorKind::FalseEdges {
1003-
real_target: block,
1004-
imaginary_target: last_candidate.next_candidate_pre_binding_block,
1005-
}
10061015
);
10071016
Some(block)
10081017
} else {
@@ -1313,27 +1322,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13131322
&mut self,
13141323
candidate: Candidate<'pat, 'tcx>,
13151324
guard: Option<Guard<'tcx>>,
1316-
arm_block: BasicBlock,
13171325
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13181326
scrutinee_span: Span,
13191327
region_scope: (region::Scope, SourceInfo),
13201328
) {
1329+
) -> BasicBlock {
13211330
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13221331

13231332
debug_assert!(candidate.match_pairs.is_empty());
13241333

13251334
let candidate_source_info = self.source_info(candidate.span);
13261335

1327-
let mut block = self.cfg.start_new_block();
1328-
self.cfg.terminate(
1329-
candidate.pre_binding_block,
1336+
let mut block = candidate.pre_binding_block;
1337+
1338+
// If we are adding our own statements, then we need a fresh block.
1339+
let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
1340+
|| !candidate.bindings.is_empty()
1341+
|| !candidate.ascriptions.is_empty()
1342+
|| guard.is_some();
1343+
1344+
if create_fresh_block {
1345+
let fresh_block = self.cfg.start_new_block();
1346+
self.false_edges(
1347+
block,
1348+
fresh_block,
1349+
candidate.next_candidate_pre_binding_block,
13301350
candidate_source_info,
1331-
TerminatorKind::FalseEdges {
1332-
real_target: block,
1333-
imaginary_target: candidate.next_candidate_pre_binding_block,
1334-
},
13351351
);
1352+
block = fresh_block;
13361353
self.ascribe_types(block, &candidate.ascriptions);
1354+
} else {
1355+
return block;
1356+
}
13371357

13381358
// rust-lang/rust#27282: The `autoref` business deserves some
13391359
// explanation here.
@@ -1478,7 +1498,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
14781498
// because that would be before we've checked the result
14791499
// from the guard.
14801500
//
1481-
// But binding them on `arm_block` is *too late*, because
1501+
// But binding them on the arm is *too late*, because
14821502
// then all of the candidates for a single arm would be
14831503
// bound in the same place, that would cause a case like:
14841504
//
@@ -1554,22 +1574,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15541574
by_value_bindings,
15551575
);
15561576

1557-
self.cfg.terminate(
1558-
post_guard_block,
1559-
source_info,
1560-
TerminatorKind::Goto { target: arm_block },
1561-
);
1577+
post_guard_block
15621578
} else {
15631579
assert!(candidate.otherwise_block.is_none());
15641580
// (Here, it is not too early to bind the matched
15651581
// candidate on `block`, because there is no guard result
15661582
// that we have to inspect before we bind them.)
15671583
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
1568-
self.cfg.terminate(
1569-
block,
1570-
candidate_source_info,
1571-
TerminatorKind::Goto { target: arm_block },
1572-
);
1584+
block
15731585
}
15741586
}
15751587

src/librustc_mir/build/matches/util.rs

Lines changed: 33 additions & 0 deletions
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)