Skip to content

Commit 662ef27

Browse files
committed
Create fewer basic blocks in match MIR lowering
1 parent adcf24a commit 662ef27

30 files changed

+685
-655
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();
@@ -794,11 +769,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
794769
/// the value, we will generate a branch to the appropriate
795770
/// prebinding block.
796771
///
797-
/// The return value is a list of "otherwise" blocks. These are
798-
/// points in execution where we found that *NONE* of the
799-
/// candidates apply. In principle, this means that the input
800-
/// list was not exhaustive, though at present we sometimes are
801-
/// not smart enough to recognize all exhaustive inputs.
772+
/// If we find that *NONE* of the candidates apply, we branch to the
773+
/// `otherwise_block`. In principle, this means that the input list was not
774+
/// exhaustive, though at present we sometimes are not smart enough to
775+
/// recognize all exhaustive inputs.
802776
///
803777
/// It might be surprising that the input can be inexhaustive.
804778
/// Indeed, initially, it is not, because all matches are
@@ -812,13 +786,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
812786
fn match_candidates<'pat>(
813787
&mut self,
814788
span: Span,
789+
start_block: &mut Option<BasicBlock>,
790+
otherwise_block: Option<BasicBlock>,
815791
candidates: &mut [&mut Candidate<'pat, 'tcx>],
816-
mut block: BasicBlock,
817792
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
818-
) -> Vec<BasicBlock> {
793+
) {
819794
debug!(
820-
"matched_candidate(span={:?}, block={:?}, candidates={:?})",
821-
span, block, candidates
795+
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
796+
span,
797+
candidates,
798+
start_block,
799+
otherwise_block,
822800
);
823801

824802
// Start by simplifying candidates. Once this process is complete, all
@@ -841,52 +819,57 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
841819
);
842820
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
843821

822+
let block: BasicBlock;
823+
844824
if !matched_candidates.is_empty() {
845-
block = if let Some(last_otherwise_block) = self.select_matched_candidates(
825+
let otherwise_block = self.select_matched_candidates(
846826
matched_candidates,
847-
block,
827+
start_block,
848828
fake_borrows,
849-
) {
850-
last_otherwise_block
829+
);
830+
831+
if let Some(last_otherwise_block) = otherwise_block {
832+
block = last_otherwise_block
851833
} else {
852834
// Any remaining candidates are unreachable.
853835
if unmatched_candidates.is_empty() {
854-
return Vec::new();
855-
} else {
856-
self.cfg.start_new_block()
836+
return;
857837
}
838+
block = self.cfg.start_new_block();
858839
};
840+
} else {
841+
block = *start_block.get_or_insert_with(|| self.cfg.start_new_block());
859842
}
860843

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

868-
// Test candidates where possible.
869-
let (otherwise, untested_candidates) = self.test_candidates(
865+
// Test for the remaining candidates.
866+
self.test_candidates(
870867
span,
871868
unmatched_candidates,
872869
block,
870+
otherwise_block,
873871
fake_borrows,
874872
);
875-
876-
// If the target candidates were exhaustive, then we are done.
877-
// But for borrowck continue build decision tree.
878-
if untested_candidates.is_empty() {
879-
return otherwise;
880-
}
881-
882-
// Otherwise, let's process those remaining candidates.
883-
let join_block = self.join_otherwise_blocks(span, otherwise);
884-
self.match_candidates(
885-
span,
886-
untested_candidates,
887-
join_block,
888-
fake_borrows,
889-
)
890873
}
891874

892875
/// Link up matched candidates. For example, if we have something like
@@ -910,7 +893,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
910893
fn select_matched_candidates(
911894
&mut self,
912895
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
913-
block: BasicBlock,
896+
start_block: &mut Option<BasicBlock>,
914897
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
915898
) -> Option<BasicBlock> {
916899
debug_assert!(
@@ -958,16 +941,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
958941
= matched_candidates.split_at_mut(fully_matched_with_guard + 1);
959942

960943
let first_candidate = &reachable_candidates[0];
944+
let first_prebinding_block = first_candidate.pre_binding_block;
961945

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

972957
for window in reachable_candidates.windows(2) {
973958
if let [first_candidate, second_candidate] = window {
@@ -1019,25 +1004,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10191004
}
10201005
}
10211006

1022-
fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
1023-
let source_info = self.source_info(span);
1024-
otherwise.sort();
1025-
otherwise.dedup(); // variant switches can introduce duplicate target blocks
1026-
if otherwise.len() == 1 {
1027-
otherwise[0]
1028-
} else {
1029-
let join_block = self.cfg.start_new_block();
1030-
for block in otherwise {
1031-
self.cfg.terminate(
1032-
block,
1033-
source_info,
1034-
TerminatorKind::Goto { target: join_block },
1035-
);
1036-
}
1037-
join_block
1038-
}
1039-
}
1040-
10411007
/// This is the most subtle part of the matching algorithm. At
10421008
/// this point, the input candidates have been fully simplified,
10431009
/// and so we know that all remaining match-pairs require some
@@ -1155,8 +1121,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11551121
span: Span,
11561122
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
11571123
block: BasicBlock,
1124+
mut otherwise_block: Option<BasicBlock>,
11581125
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
1159-
) -> (Vec<BasicBlock>, &'b mut [&'c mut Candidate<'pat, 'tcx>]) {
1126+
) {
11601127
// extract the match-pair from the highest priority candidate
11611128
let match_pair = &candidates.first().unwrap().match_pairs[0];
11621129
let mut test = self.test(match_pair);
@@ -1210,9 +1177,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12101177
"match_candidates: test={:?} match_pair={:?}",
12111178
test, match_pair
12121179
);
1213-
let target_blocks = self.perform_test(block, &match_place, &test);
12141180
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
1215-
target_candidates.resize_with(target_blocks.len(), Default::default);
1181+
target_candidates.resize_with(test.targets(), Default::default);
12161182

12171183
let total_candidate_count = candidates.len();
12181184

@@ -1238,20 +1204,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12381204
// apply. Collect a list of blocks where control flow will
12391205
// branch if one of the `target_candidate` sets is not
12401206
// exhaustive.
1241-
let otherwise: Vec<_> = target_blocks
1242-
.into_iter()
1243-
.zip(target_candidates)
1244-
.flat_map(|(target_block, mut target_candidates)| {
1207+
if !candidates.is_empty() {
1208+
let remainder_start = &mut None;
1209+
self.match_candidates(
1210+
span,
1211+
remainder_start,
1212+
otherwise_block,
1213+
candidates,
1214+
fake_borrows,
1215+
);
1216+
otherwise_block = Some(remainder_start.unwrap());
1217+
};
1218+
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
1219+
if candidates.len() != 0 {
1220+
let candidate_start = &mut None;
12451221
self.match_candidates(
12461222
span,
1247-
&mut *target_candidates,
1248-
target_block,
1223+
candidate_start,
1224+
otherwise_block,
1225+
&mut *candidates,
12491226
fake_borrows,
1250-
)
1251-
})
1252-
.collect();
1227+
);
1228+
candidate_start.unwrap()
1229+
} else {
1230+
*otherwise_block.get_or_insert_with(|| {
1231+
let unreachable = self.cfg.start_new_block();
1232+
let source_info = self.source_info(span);
1233+
self.cfg.terminate(
1234+
unreachable,
1235+
source_info,
1236+
TerminatorKind::Unreachable,
1237+
);
1238+
unreachable
1239+
})
1240+
}
1241+
}).collect();
12531242

1254-
(otherwise, candidates)
1243+
self.perform_test(
1244+
block,
1245+
&match_place,
1246+
&test,
1247+
target_blocks,
1248+
);
12551249
}
12561250

12571251
// Determine the fake borrows that are needed to ensure that the place
@@ -1325,7 +1319,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13251319
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
13261320
scrutinee_span: Span,
13271321
region_scope: (region::Scope, SourceInfo),
1328-
) {
13291322
) -> BasicBlock {
13301323
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
13311324

@@ -1347,10 +1340,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
13471340
block,
13481341
fresh_block,
13491342
candidate.next_candidate_pre_binding_block,
1350-
candidate_source_info,
1351-
);
1343+
candidate_source_info,
1344+
);
13521345
block = fresh_block;
1353-
self.ascribe_types(block, &candidate.ascriptions);
1346+
self.ascribe_types(block, &candidate.ascriptions);
13541347
} else {
13551348
return block;
13561349
}

0 commit comments

Comments
 (0)