Skip to content

Commit da22793

Browse files
committed
Create fewer basic blocks in match MIR lowering
1 parent 32c3377 commit da22793

30 files changed

+685
-653
lines changed

src/librustc_mir/build/matches/mod.rs

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

209+
let outer_source_info = self.source_info(span);
210+
209211
// this will generate code to test scrutinee_place and
210212
// branch to the appropriate arm block
211-
let otherwise = self.match_candidates(
213+
self.match_candidates(
212214
scrutinee_span,
215+
&mut Some(block),
216+
None,
213217
candidates,
214-
block,
215218
&mut fake_borrows,
216219
);
217220

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

@@ -247,8 +232,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
247232
let arm_source_info = self.source_info(arm.span);
248233
let region_scope = (arm.scope, arm_source_info);
249234
self.in_scope(region_scope, arm.lint_level, |this| {
250-
let mut arm_block = this.cfg.start_new_block();
251-
252235
let body = this.hir.mirror(arm.body.clone());
253236
let scope = this.declare_bindings(
254237
None,
@@ -258,23 +241,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
258241
Some((Some(&scrutinee_place), scrutinee_span)),
259242
);
260243

244+
let arm_block;
261245
if candidates.len() == 1 {
262-
arm_block = self.bind_and_guard_matched_candidate(
246+
arm_block = this.bind_and_guard_matched_candidate(
263247
candidates.pop().unwrap(),
264248
arm.guard.clone(),
265249
&fake_borrow_temps,
266250
scrutinee_span,
251+
region_scope,
267252
);
268253
} else {
269-
arm_block = self.cfg.start_new_block();
254+
arm_block = this.cfg.start_new_block();
270255
for candidate in candidates {
271-
let binding_end = self.bind_and_guard_matched_candidate(
256+
this.clear_top_scope(arm.scope);
257+
let binding_end = this.bind_and_guard_matched_candidate(
272258
candidate,
273259
arm.guard.clone(),
274260
&fake_borrow_temps,
275261
scrutinee_span,
262+
region_scope,
276263
);
277-
self.cfg.terminate(
264+
this.cfg.terminate(
278265
binding_end,
279266
source_info,
280267
TerminatorKind::Goto { target: arm_block },
@@ -286,18 +273,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
286273
this.source_scope = source_scope;
287274
}
288275

289-
for candidate in candidates {
290-
this.clear_top_scope(arm.scope);
291-
this.bind_and_guard_matched_candidate(
292-
candidate,
293-
arm.guard.clone(),
294-
arm_block,
295-
&fake_borrow_temps,
296-
scrutinee_span,
297-
region_scope,
298-
);
299-
}
300-
301276
this.into(destination, arm_block, body)
302277
})
303278
}).collect();
@@ -792,11 +767,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
792767
/// the value, we will generate a branch to the appropriate
793768
/// prebinding block.
794769
///
795-
/// The return value is a list of "otherwise" blocks. These are
796-
/// points in execution where we found that *NONE* of the
797-
/// candidates apply. In principle, this means that the input
798-
/// list was not exhaustive, though at present we sometimes are
799-
/// not smart enough to recognize all exhaustive inputs.
770+
/// If we find that *NONE* of the candidates apply, we branch to the
771+
/// `otherwise_block`. In principle, this means that the input list was not
772+
/// exhaustive, though at present we sometimes are not smart enough to
773+
/// recognize all exhaustive inputs.
800774
///
801775
/// It might be surprising that the input can be inexhaustive.
802776
/// Indeed, initially, it is not, because all matches are
@@ -810,13 +784,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
810784
fn match_candidates<'pat>(
811785
&mut self,
812786
span: Span,
787+
start_block: &mut Option<BasicBlock>,
788+
otherwise_block: Option<BasicBlock>,
813789
candidates: &mut [&mut Candidate<'pat, 'tcx>],
814-
mut block: BasicBlock,
815790
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
816-
) -> Vec<BasicBlock> {
791+
) {
817792
debug!(
818-
"matched_candidate(span={:?}, block={:?}, candidates={:?})",
819-
span, block, candidates
793+
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
794+
span,
795+
candidates,
796+
start_block,
797+
otherwise_block,
820798
);
821799

822800
// Start by simplifying candidates. Once this process is complete, all
@@ -839,52 +817,57 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
839817
);
840818
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
841819

820+
let block: BasicBlock;
821+
842822
if !matched_candidates.is_empty() {
843-
block = if let Some(last_otherwise_block) = self.select_matched_candidates(
823+
let otherwise_block = self.select_matched_candidates(
844824
matched_candidates,
845-
block,
825+
start_block,
846826
fake_borrows,
847-
) {
848-
last_otherwise_block
827+
);
828+
829+
if let Some(last_otherwise_block) = otherwise_block {
830+
block = last_otherwise_block
849831
} else {
850832
// Any remaining candidates are unreachable.
851833
if unmatched_candidates.is_empty() {
852-
return Vec::new();
853-
} else {
854-
self.cfg.start_new_block()
834+
return;
855835
}
836+
block = self.cfg.start_new_block();
856837
};
838+
} else {
839+
block = *start_block.get_or_insert_with(|| self.cfg.start_new_block());
857840
}
858841

859842
// If there are no candidates that still need testing, we're
860843
// done. Since all matches are exhaustive, execution should
861844
// never reach this point.
862845
if unmatched_candidates.is_empty() {
863-
return vec![block];
846+
let source_info = self.source_info(span);
847+
if let Some(otherwise) = otherwise_block {
848+
self.cfg.terminate(
849+
block,
850+
source_info,
851+
TerminatorKind::Goto { target: otherwise },
852+
);
853+
} else {
854+
self.cfg.terminate(
855+
block,
856+
source_info,
857+
TerminatorKind::Unreachable,
858+
)
859+
}
860+
return;
864861
}
865862

866-
// Test candidates where possible.
867-
let (otherwise, untested_candidates) = self.test_candidates(
863+
// Test for the remaining candidates.
864+
self.test_candidates(
868865
span,
869866
unmatched_candidates,
870867
block,
868+
otherwise_block,
871869
fake_borrows,
872870
);
873-
874-
// If the target candidates were exhaustive, then we are done.
875-
// But for borrowck continue build decision tree.
876-
if untested_candidates.is_empty() {
877-
return otherwise;
878-
}
879-
880-
// Otherwise, let's process those remaining candidates.
881-
let join_block = self.join_otherwise_blocks(span, otherwise);
882-
self.match_candidates(
883-
span,
884-
untested_candidates,
885-
join_block,
886-
fake_borrows,
887-
)
888871
}
889872

890873
/// Link up matched candidates. For example, if we have something like
@@ -908,7 +891,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
908891
fn select_matched_candidates(
909892
&mut self,
910893
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
911-
block: BasicBlock,
894+
start_block: &mut Option<BasicBlock>,
912895
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
913896
) -> Option<BasicBlock> {
914897
debug_assert!(
@@ -956,16 +939,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
956939
= matched_candidates.split_at_mut(fully_matched_with_guard + 1);
957940

958941
let first_candidate = &reachable_candidates[0];
942+
let first_prebinding_block = first_candidate.pre_binding_block;
959943

960-
let candidate_source_info = self.source_info(first_candidate.span);
961-
962-
self.cfg.terminate(
963-
block,
964-
candidate_source_info,
965-
TerminatorKind::Goto {
966-
target: first_candidate.pre_binding_block,
967-
},
968-
);
944+
if let Some(start_block) = *start_block {
945+
let source_info = self.source_info(first_candidate.span);
946+
self.cfg.terminate(
947+
start_block,
948+
source_info,
949+
TerminatorKind::Goto { target: first_prebinding_block },
950+
);
951+
} else {
952+
*start_block = Some(first_prebinding_block);
953+
}
969954

970955
for window in reachable_candidates.windows(2) {
971956
if let [first_candidate, second_candidate] = window {
@@ -1017,25 +1002,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10171002
}
10181003
}
10191004

1020-
fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
1021-
let source_info = self.source_info(span);
1022-
otherwise.sort();
1023-
otherwise.dedup(); // variant switches can introduce duplicate target blocks
1024-
if otherwise.len() == 1 {
1025-
otherwise[0]
1026-
} else {
1027-
let join_block = self.cfg.start_new_block();
1028-
for block in otherwise {
1029-
self.cfg.terminate(
1030-
block,
1031-
source_info,
1032-
TerminatorKind::Goto { target: join_block },
1033-
);
1034-
}
1035-
join_block
1036-
}
1037-
}
1038-
10391005
/// This is the most subtle part of the matching algorithm. At
10401006
/// this point, the input candidates have been fully simplified,
10411007
/// and so we know that all remaining match-pairs require some
@@ -1153,8 +1119,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11531119
span: Span,
11541120
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
11551121
block: BasicBlock,
1122+
mut otherwise_block: Option<BasicBlock>,
11561123
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
1157-
) -> (Vec<BasicBlock>, &'b mut [&'c mut Candidate<'pat, 'tcx>]) {
1124+
) {
11581125
// extract the match-pair from the highest priority candidate
11591126
let match_pair = &candidates.first().unwrap().match_pairs[0];
11601127
let mut test = self.test(match_pair);
@@ -1208,9 +1175,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12081175
"match_candidates: test={:?} match_pair={:?}",
12091176
test, match_pair
12101177
);
1211-
let target_blocks = self.perform_test(block, &match_place, &test);
12121178
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
1213-
target_candidates.resize_with(target_blocks.len(), Default::default);
1179+
target_candidates.resize_with(test.targets(), Default::default);
12141180

12151181
let total_candidate_count = candidates.len();
12161182

@@ -1236,20 +1202,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12361202
// apply. Collect a list of blocks where control flow will
12371203
// branch if one of the `target_candidate` sets is not
12381204
// exhaustive.
1239-
let otherwise: Vec<_> = target_blocks
1240-
.into_iter()
1241-
.zip(target_candidates)
1242-
.flat_map(|(target_block, mut target_candidates)| {
1205+
if !candidates.is_empty() {
1206+
let remainder_start = &mut None;
1207+
self.match_candidates(
1208+
span,
1209+
remainder_start,
1210+
otherwise_block,
1211+
candidates,
1212+
fake_borrows,
1213+
);
1214+
otherwise_block = Some(remainder_start.unwrap());
1215+
};
1216+
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
1217+
if candidates.len() != 0 {
1218+
let candidate_start = &mut None;
12431219
self.match_candidates(
12441220
span,
1245-
&mut *target_candidates,
1246-
target_block,
1221+
candidate_start,
1222+
otherwise_block,
1223+
&mut *candidates,
12471224
fake_borrows,
1248-
)
1249-
})
1250-
.collect();
1225+
);
1226+
candidate_start.unwrap()
1227+
} else {
1228+
*otherwise_block.get_or_insert_with(|| {
1229+
let unreachable = self.cfg.start_new_block();
1230+
let source_info = self.source_info(span);
1231+
self.cfg.terminate(
1232+
unreachable,
1233+
source_info,
1234+
TerminatorKind::Unreachable,
1235+
);
1236+
unreachable
1237+
})
1238+
}
1239+
}).collect();
12511240

1252-
(otherwise, candidates)
1241+
self.perform_test(
1242+
block,
1243+
&match_place,
1244+
&test,
1245+
target_blocks,
1246+
);
12531247
}
12541248

12551249
// Determine the fake borrows that are needed to ensure that the place
@@ -1323,7 +1317,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13231317
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13241318
scrutinee_span: Span,
13251319
region_scope: (region::Scope, SourceInfo),
1326-
) {
13271320
) -> BasicBlock {
13281321
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13291322

@@ -1345,10 +1338,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13451338
block,
13461339
fresh_block,
13471340
candidate.next_candidate_pre_binding_block,
1348-
candidate_source_info,
1349-
);
1341+
candidate_source_info,
1342+
);
13501343
block = fresh_block;
1351-
self.ascribe_types(block, &candidate.ascriptions);
1344+
self.ascribe_types(block, &candidate.ascriptions);
13521345
} else {
13531346
return block;
13541347
}

0 commit comments

Comments
 (0)