Skip to content

Commit d7f31a6

Browse files
committed
Create fewer basic blocks in match MIR lowering
1 parent b9ba8e5 commit d7f31a6

28 files changed

+618
-562
lines changed

src/librustc_mir/build/matches/mod.rs

+110-106
Original file line numberDiff line numberDiff line change
@@ -205,33 +205,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
205205
.flat_map(|(_, candidates)| candidates)
206206
.collect::<Vec<_>>();
207207

208+
let outer_source_info = self.source_info(span);
209+
208210
// this will generate code to test scrutinee_place and
209211
// branch to the appropriate arm block
210-
let otherwise = self.match_candidates(
212+
self.match_candidates(
211213
scrutinee_span,
214+
&mut Some(block),
215+
None,
212216
candidates,
213-
block,
214217
&mut fake_borrows,
215218
);
216219

217-
let outer_source_info = self.source_info(span);
218-
219-
if !otherwise.is_empty() {
220-
// All matches are exhaustive. However, because some matches
221-
// only have exponentially-large exhaustive decision trees, we
222-
// sometimes generate an inexhaustive decision tree.
223-
//
224-
// In that case, the inexhaustive tips of the decision tree
225-
// can't be reached - terminate them with an `unreachable`.
226-
let mut otherwise = otherwise;
227-
otherwise.sort();
228-
otherwise.dedup(); // variant switches can introduce duplicate target blocks
229-
for block in otherwise {
230-
self.cfg
231-
.terminate(block, outer_source_info, TerminatorKind::Unreachable);
232-
}
233-
}
234-
235220
// Step 4. Determine the fake borrows that are needed from the above
236221
// places. Create the required temporaries for them.
237222

@@ -264,19 +249,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
264249
);
265250
} else {
266251
arm_block = self.cfg.start_new_block();
267-
for candidate in candidates {
252+
for candidate in candidates {
268253
let binding_end = self.bind_and_guard_matched_candidate(
269-
candidate,
270-
arm.guard.clone(),
271-
&fake_borrow_temps,
272-
scrutinee_span,
273-
);
254+
candidate,
255+
arm.guard.clone(),
256+
&fake_borrow_temps,
257+
scrutinee_span,
258+
);
274259
self.cfg.terminate(
275260
binding_end,
276261
source_info,
277262
TerminatorKind::Goto { target: arm_block },
278263
);
279-
}
264+
}
280265
}
281266

282267
if let Some(source_scope) = scope {
@@ -793,11 +778,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
793778
/// the value, we will generate a branch to the appropriate
794779
/// prebinding block.
795780
///
796-
/// The return value is a list of "otherwise" blocks. These are
797-
/// points in execution where we found that *NONE* of the
798-
/// candidates apply. In principle, this means that the input
799-
/// list was not exhaustive, though at present we sometimes are
800-
/// not smart enough to recognize all exhaustive inputs.
781+
/// If we find that *NONE* of the candidates apply, we branch to the
782+
/// `otherwise_block`. In principle, this means that the input list was not
783+
/// exhaustive, though at present we sometimes are not smart enough to
784+
/// recognize all exhaustive inputs.
801785
///
802786
/// It might be surprising that the input can be inexhaustive.
803787
/// Indeed, initially, it is not, because all matches are
@@ -811,13 +795,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
811795
fn match_candidates<'pat>(
812796
&mut self,
813797
span: Span,
798+
start_block: &mut Option<BasicBlock>,
799+
otherwise_block: Option<BasicBlock>,
814800
candidates: &mut [&mut Candidate<'pat, 'tcx>],
815-
mut block: BasicBlock,
816801
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
817-
) -> Vec<BasicBlock> {
802+
) {
818803
debug!(
819-
"matched_candidate(span={:?}, block={:?}, candidates={:?})",
820-
span, block, candidates
804+
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
805+
span,
806+
candidates,
807+
start_block,
808+
otherwise_block,
821809
);
822810

823811
// Start by simplifying candidates. Once this process is complete, all
@@ -840,52 +828,57 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
840828
);
841829
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
842830

831+
let block: BasicBlock;
832+
843833
if !matched_candidates.is_empty() {
844-
block = if let Some(last_otherwise_block) = self.select_matched_candidates(
834+
let otherwise_block = self.select_matched_candidates(
845835
matched_candidates,
846-
block,
836+
start_block,
847837
fake_borrows,
848-
) {
849-
last_otherwise_block
838+
);
839+
840+
if let Some(last_otherwise_block) = otherwise_block {
841+
block = last_otherwise_block
850842
} else {
851843
// Any remaining candidates are unreachable.
852844
if unmatched_candidates.is_empty() {
853-
return Vec::new();
854-
} else {
855-
self.cfg.start_new_block()
845+
return;
856846
}
847+
block = self.cfg.start_new_block();
857848
};
849+
} else {
850+
block = *start_block.get_or_insert_with(|| self.cfg.start_new_block());
858851
}
859852

860853
// If there are no candidates that still need testing, we're
861854
// done. Since all matches are exhaustive, execution should
862855
// never reach this point.
863856
if unmatched_candidates.is_empty() {
864-
return vec![block];
857+
let source_info = self.source_info(span);
858+
if let Some(otherwise) = otherwise_block {
859+
self.cfg.terminate(
860+
block,
861+
source_info,
862+
TerminatorKind::Goto { target: otherwise },
863+
);
864+
} else {
865+
self.cfg.terminate(
866+
block,
867+
source_info,
868+
TerminatorKind::Unreachable,
869+
)
870+
}
871+
return;
865872
}
866873

867-
// Test candidates where possible.
868-
let (otherwise, untested_candidates) = self.test_candidates(
874+
// Test for the remaining candidates.
875+
self.test_candidates(
869876
span,
870877
unmatched_candidates,
871878
block,
879+
otherwise_block,
872880
fake_borrows,
873881
);
874-
875-
// If the target candidates were exhaustive, then we are done.
876-
// But for borrowck continue build decision tree.
877-
if untested_candidates.is_empty() {
878-
return otherwise;
879-
}
880-
881-
// Otherwise, let's process those remaining candidates.
882-
let join_block = self.join_otherwise_blocks(span, otherwise);
883-
self.match_candidates(
884-
span,
885-
untested_candidates,
886-
join_block,
887-
&mut None,
888-
)
889882
}
890883

891884
/// Link up matched candidates. For example, if we have something like
@@ -909,7 +902,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
909902
fn select_matched_candidates(
910903
&mut self,
911904
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
912-
block: BasicBlock,
905+
start_block: &mut Option<BasicBlock>,
913906
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
914907
) -> Option<BasicBlock> {
915908
debug_assert!(
@@ -957,16 +950,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
957950
= matched_candidates.split_at_mut(fully_matched_with_guard + 1);
958951

959952
let first_candidate = &reachable_candidates[0];
953+
let first_prebinding_block = first_candidate.pre_binding_block;
960954

961-
let candidate_source_info = self.source_info(first_candidate.span);
962-
963-
self.cfg.terminate(
964-
block,
965-
candidate_source_info,
966-
TerminatorKind::Goto {
967-
target: first_candidate.pre_binding_block,
968-
},
969-
);
955+
if let Some(start_block) = *start_block {
956+
let source_info = self.source_info(first_candidate.span);
957+
self.cfg.terminate(
958+
start_block,
959+
source_info,
960+
TerminatorKind::Goto { target: first_prebinding_block },
961+
);
962+
} else {
963+
*start_block = Some(first_prebinding_block);
964+
}
970965

971966
for window in reachable_candidates.windows(2) {
972967
if let [first_candidate, second_candidate] = window {
@@ -1018,25 +1013,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10181013
}
10191014
}
10201015

1021-
fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
1022-
let source_info = self.source_info(span);
1023-
otherwise.sort();
1024-
otherwise.dedup(); // variant switches can introduce duplicate target blocks
1025-
if otherwise.len() == 1 {
1026-
otherwise[0]
1027-
} else {
1028-
let join_block = self.cfg.start_new_block();
1029-
for block in otherwise {
1030-
self.cfg.terminate(
1031-
block,
1032-
source_info,
1033-
TerminatorKind::Goto { target: join_block },
1034-
);
1035-
}
1036-
join_block
1037-
}
1038-
}
1039-
10401016
/// This is the most subtle part of the matching algorithm. At
10411017
/// this point, the input candidates have been fully simplified,
10421018
/// and so we know that all remaining match-pairs require some
@@ -1154,8 +1130,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11541130
span: Span,
11551131
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
11561132
block: BasicBlock,
1133+
mut otherwise_block: Option<BasicBlock>,
11571134
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
1158-
) -> (Vec<BasicBlock>, &'b mut [&'c mut Candidate<'pat, 'tcx>]) {
1135+
) {
11591136
// extract the match-pair from the highest priority candidate
11601137
let match_pair = &candidates.first().unwrap().match_pairs[0];
11611138
let mut test = self.test(match_pair);
@@ -1209,9 +1186,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12091186
"match_candidates: test={:?} match_pair={:?}",
12101187
test, match_pair
12111188
);
1212-
let target_blocks = self.perform_test(block, &match_place, &test);
12131189
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
1214-
target_candidates.resize_with(target_blocks.len(), Default::default);
1190+
target_candidates.resize_with(test.targets(), Default::default);
12151191

12161192
let total_candidate_count = candidates.len();
12171193

@@ -1237,20 +1213,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12371213
// apply. Collect a list of blocks where control flow will
12381214
// branch if one of the `target_candidate` sets is not
12391215
// exhaustive.
1240-
let otherwise: Vec<_> = target_blocks
1241-
.into_iter()
1242-
.zip(target_candidates)
1243-
.flat_map(|(target_block, mut target_candidates)| {
1216+
if !candidates.is_empty() {
1217+
let remainder_start = &mut None;
1218+
self.match_candidates(
1219+
span,
1220+
remainder_start,
1221+
otherwise_block,
1222+
candidates,
1223+
fake_borrows,
1224+
);
1225+
otherwise_block = Some(remainder_start.unwrap());
1226+
};
1227+
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
1228+
if candidates.len() != 0 {
1229+
let candidate_start = &mut None;
12441230
self.match_candidates(
12451231
span,
1246-
&mut *target_candidates,
1247-
target_block,
1232+
candidate_start,
1233+
otherwise_block,
1234+
&mut *candidates,
12481235
fake_borrows,
1249-
)
1250-
})
1251-
.collect();
1236+
);
1237+
candidate_start.unwrap()
1238+
} else {
1239+
*otherwise_block.get_or_insert_with(|| {
1240+
let unreachable = self.cfg.start_new_block();
1241+
let source_info = self.source_info(span);
1242+
self.cfg.terminate(
1243+
unreachable,
1244+
source_info,
1245+
TerminatorKind::Unreachable,
1246+
);
1247+
unreachable
1248+
})
1249+
}
1250+
}).collect();
12521251

1253-
(otherwise, candidates)
1252+
self.perform_test(
1253+
block,
1254+
&match_place,
1255+
&test,
1256+
target_blocks,
1257+
);
12541258
}
12551259

12561260
// Determine the fake borrows that are needed to ensure that the place
@@ -1344,10 +1348,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13441348
block,
13451349
fresh_block,
13461350
candidate.next_candidate_pre_binding_block,
1347-
candidate_source_info,
1348-
);
1351+
candidate_source_info,
1352+
);
13491353
block = fresh_block;
1350-
self.ascribe_types(block, &candidate.ascriptions);
1354+
self.ascribe_types(block, &candidate.ascriptions);
13511355
} else {
13521356
return block;
13531357
}

0 commit comments

Comments
 (0)