Skip to content

Commit 32c3377

Browse files
committed
Avoid unnecessary false edges in MIR match lowering
1 parent 33bc396 commit 32c3377

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
@@ -689,7 +704,7 @@ pub struct Candidate<'pat, 'tcx: 'pat> {
689704

690705
// ...and the blocks for add false edges between candidates
691706
pre_binding_block: BasicBlock,
692-
next_candidate_pre_binding_block: BasicBlock,
707+
next_candidate_pre_binding_block: Option<BasicBlock>,
693708
}
694709

695710
#[derive(Clone, Debug)]
@@ -956,14 +971,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
956971
if let [first_candidate, second_candidate] = window {
957972
let source_info = self.source_info(first_candidate.span);
958973
if let Some(otherwise_block) = first_candidate.otherwise_block {
959-
self.cfg.terminate(
974+
self.false_edges(
960975
otherwise_block,
976+
second_candidate.pre_binding_block,
977+
first_candidate.next_candidate_pre_binding_block,
961978
source_info,
962-
TerminatorKind::FalseEdges {
963-
real_target: second_candidate.pre_binding_block,
964-
imaginary_target: first_candidate.next_candidate_pre_binding_block,
965-
}
966-
)
979+
);
967980
} else {
968981
bug!("candidate other than the last has no guard");
969982
}
@@ -977,13 +990,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
977990
if let Some(otherwise) = candidate.otherwise_block {
978991
let source_info = self.source_info(candidate.span);
979992
let unreachable = self.cfg.start_new_block();
980-
self.cfg.terminate(
993+
self.false_edges(
981994
otherwise,
995+
unreachable,
996+
candidate.next_candidate_pre_binding_block,
982997
source_info,
983-
TerminatorKind::FalseEdges {
984-
real_target: unreachable,
985-
imaginary_targets: candidate.next_candidate_pre_binding_block,
986-
}
987998
);
988999
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
9891000
}
@@ -994,13 +1005,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9941005
if let Some(otherwise) = last_candidate.otherwise_block {
9951006
let source_info = self.source_info(last_candidate.span);
9961007
let block = self.cfg.start_new_block();
997-
self.cfg.terminate(
1008+
self.false_edges(
9981009
otherwise,
1010+
block,
1011+
last_candidate.next_candidate_pre_binding_block,
9991012
source_info,
1000-
TerminatorKind::FalseEdges {
1001-
real_target: block,
1002-
imaginary_target: last_candidate.next_candidate_pre_binding_block,
1003-
}
10041013
);
10051014
Some(block)
10061015
} else {
@@ -1311,27 +1320,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13111320
&mut self,
13121321
candidate: Candidate<'pat, 'tcx>,
13131322
guard: Option<Guard<'tcx>>,
1314-
arm_block: BasicBlock,
13151323
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13161324
scrutinee_span: Span,
13171325
region_scope: (region::Scope, SourceInfo),
13181326
) {
1327+
) -> BasicBlock {
13191328
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13201329

13211330
debug_assert!(candidate.match_pairs.is_empty());
13221331

13231332
let candidate_source_info = self.source_info(candidate.span);
13241333

1325-
let mut block = self.cfg.start_new_block();
1326-
self.cfg.terminate(
1327-
candidate.pre_binding_block,
1334+
let mut block = candidate.pre_binding_block;
1335+
1336+
// If we are adding our own statements, then we need a fresh block.
1337+
let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
1338+
|| !candidate.bindings.is_empty()
1339+
|| !candidate.ascriptions.is_empty()
1340+
|| guard.is_some();
1341+
1342+
if create_fresh_block {
1343+
let fresh_block = self.cfg.start_new_block();
1344+
self.false_edges(
1345+
block,
1346+
fresh_block,
1347+
candidate.next_candidate_pre_binding_block,
13281348
candidate_source_info,
1329-
TerminatorKind::FalseEdges {
1330-
real_target: block,
1331-
imaginary_target: candidate.next_candidate_pre_binding_block,
1332-
},
13331349
);
1350+
block = fresh_block;
13341351
self.ascribe_types(block, &candidate.ascriptions);
1352+
} else {
1353+
return block;
1354+
}
13351355

13361356
// rust-lang/rust#27282: The `autoref` business deserves some
13371357
// explanation here.
@@ -1476,7 +1496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
14761496
// because that would be before we've checked the result
14771497
// from the guard.
14781498
//
1479-
// But binding them on `arm_block` is *too late*, because
1499+
// But binding them on the arm is *too late*, because
14801500
// then all of the candidates for a single arm would be
14811501
// bound in the same place, that would cause a case like:
14821502
//
@@ -1552,22 +1572,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15521572
by_value_bindings,
15531573
);
15541574

1555-
self.cfg.terminate(
1556-
post_guard_block,
1557-
source_info,
1558-
TerminatorKind::Goto { target: arm_block },
1559-
);
1575+
post_guard_block
15601576
} else {
15611577
assert!(candidate.otherwise_block.is_none());
15621578
// (Here, it is not too early to bind the matched
15631579
// candidate on `block`, because there is no guard result
15641580
// that we have to inspect before we bind them.)
15651581
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
1566-
self.cfg.terminate(
1567-
block,
1568-
candidate_source_info,
1569-
TerminatorKind::Goto { target: arm_block },
1570-
);
1582+
block
15711583
}
15721584
}
15731585

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)