Skip to content

Commit 64a585d

Browse files
committed
Return the otherwise_block instead of passing it as argument
This saves a few blocks and matches the common `unpack!` paradigm.
1 parent eff1a86 commit 64a585d

19 files changed

+334
-462
lines changed

compiler/rustc_mir_build/src/build/matches/mod.rs

+30-58
Original file line numberDiff line numberDiff line change
@@ -1250,11 +1250,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12501250
candidates: &mut [&mut Candidate<'pat, 'tcx>],
12511251
refutable: bool,
12521252
) -> BasicBlock {
1253+
// This will generate code to test scrutinee_place and branch to the appropriate arm block.
12531254
// See the doc comment on `match_candidates` for why we have an otherwise block.
1254-
let otherwise_block = self.cfg.start_new_block();
1255-
1256-
// This will generate code to test scrutinee_place and branch to the appropriate arm block
1257-
self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
1255+
let otherwise_block =
1256+
self.match_candidates(match_start_span, scrutinee_span, block, candidates);
12581257

12591258
// Link each leaf candidate to the `false_edge_start_block` of the next one.
12601259
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
@@ -1305,27 +1304,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13051304
otherwise_block
13061305
}
13071306

1308-
/// The main match algorithm. It begins with a set of candidates
1309-
/// `candidates` and has the job of generating code to determine
1310-
/// which of these candidates, if any, is the correct one. The
1307+
/// The main match algorithm. It begins with a set of candidates `candidates` and has the job of
1308+
/// generating code that branches to an appropriate block if the scrutinee matches one of these
1309+
/// candidates. The
13111310
/// candidates are sorted such that the first item in the list
13121311
/// has the highest priority. When a candidate is found to match
13131312
/// the value, we will set and generate a branch to the appropriate
13141313
/// pre-binding block.
13151314
///
1316-
/// If we find that *NONE* of the candidates apply, we branch to `otherwise_block`.
1315+
/// If none of the candidates apply, we continue to the returned `otherwise_block`.
13171316
///
13181317
/// It might be surprising that the input can be non-exhaustive.
1319-
/// Indeed, initially, it is not, because all matches are
1318+
/// Indeed, for matches, initially, it is not, because all matches are
13201319
/// exhaustive in Rust. But during processing we sometimes divide
13211320
/// up the list of candidates and recurse with a non-exhaustive
13221321
/// list. This is how our lowering approach (called "backtracking
13231322
/// automaton" in the literature) works.
13241323
/// See [`Builder::test_candidates`] for more details.
13251324
///
1326-
/// If `fake_borrows` is `Some`, then places which need fake borrows
1327-
/// will be added to it.
1328-
///
13291325
/// For an example of how we use `otherwise_block`, consider:
13301326
/// ```
13311327
/// # fn foo((x, y): (bool, bool)) -> u32 {
@@ -1350,7 +1346,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13501346
/// }
13511347
/// if y {
13521348
/// if x {
1353-
/// // This is actually unreachable because the `(true, true)` case was handled above.
1349+
/// // This is actually unreachable because the `(true, true)` case was handled above,
1350+
/// // but we don't know that from within the lowering algorithm.
13541351
/// // continue
13551352
/// } else {
13561353
/// return 3
@@ -1359,33 +1356,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13591356
/// return 2
13601357
/// }
13611358
/// // this is the final `otherwise_block`, which is unreachable because the match was exhaustive.
1362-
/// unreachable!()
1359+
/// unreachable_unchecked()
13631360
/// # }
13641361
/// ```
13651362
///
13661363
/// Every `continue` is an instance of branching to some `otherwise_block` somewhere deep within
13671364
/// the algorithm. For more details on why we lower like this, see [`Builder::test_candidates`].
13681365
///
13691366
/// Note how we test `x` twice. This is the tradeoff of backtracking automata: we prefer smaller
1370-
/// code size at the expense of non-optimal code paths.
1367+
/// code size so we accept non-optimal code paths.
13711368
#[instrument(skip(self), level = "debug")]
13721369
fn match_candidates(
13731370
&mut self,
13741371
span: Span,
13751372
scrutinee_span: Span,
13761373
start_block: BasicBlock,
1377-
otherwise_block: BasicBlock,
13781374
candidates: &mut [&mut Candidate<'_, 'tcx>],
1379-
) {
1375+
) -> BasicBlock {
13801376
ensure_sufficient_stack(|| {
1381-
self.match_candidates_with_enough_stack(
1382-
span,
1383-
scrutinee_span,
1384-
start_block,
1385-
otherwise_block,
1386-
candidates,
1387-
)
1388-
});
1377+
self.match_candidates_with_enough_stack(span, scrutinee_span, start_block, candidates)
1378+
})
13891379
}
13901380

13911381
/// Construct the decision tree for `candidates`. Don't call this, call `match_candidates`
@@ -1395,9 +1385,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13951385
span: Span,
13961386
scrutinee_span: Span,
13971387
start_block: BasicBlock,
1398-
otherwise_block: BasicBlock,
13991388
candidates: &mut [&mut Candidate<'_, 'tcx>],
1400-
) {
1389+
) -> BasicBlock {
14011390
if let [first, ..] = candidates {
14021391
if first.false_edge_start_block.is_none() {
14031392
first.false_edge_start_block = Some(start_block);
@@ -1408,9 +1397,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14081397
let rest = match candidates {
14091398
[] => {
14101399
// If there are no candidates that still need testing, we're done.
1411-
let source_info = self.source_info(span);
1412-
self.cfg.goto(start_block, source_info, otherwise_block);
1413-
return;
1400+
return start_block;
14141401
}
14151402
[first, remaining @ ..] if first.match_pairs.is_empty() => {
14161403
// The first candidate has satisfied all its match pairs; we link it up and continue
@@ -1431,13 +1418,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14311418

14321419
// Process any candidates that remain.
14331420
let BlockAnd(start_block, remaining_candidates) = rest;
1434-
self.match_candidates(
1435-
span,
1436-
scrutinee_span,
1437-
start_block,
1438-
otherwise_block,
1439-
remaining_candidates,
1440-
);
1421+
self.match_candidates(span, scrutinee_span, start_block, remaining_candidates)
14411422
}
14421423

14431424
/// Link up matched candidates.
@@ -1542,14 +1523,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15421523
}
15431524

15441525
// Process the expanded candidates.
1545-
let remainder_start = self.cfg.start_new_block();
1546-
// There might be new or-patterns obtained from expanding the old ones, so we call
1547-
// `match_candidates` again.
1548-
self.match_candidates(
1526+
let remainder_start = self.match_candidates(
15491527
span,
15501528
scrutinee_span,
15511529
start_block,
1552-
remainder_start,
15531530
expanded_candidates.as_mut_slice(),
15541531
);
15551532

@@ -1648,6 +1625,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16481625
self.merge_trivial_subcandidates(candidate);
16491626

16501627
if !candidate.match_pairs.is_empty() {
1628+
let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span);
1629+
let source_info = self.source_info(or_span);
16511630
// If more match pairs remain, test them after each subcandidate.
16521631
// We could add them to the or-candidates before the call to `test_or_pattern` but this
16531632
// would make it impossible to detect simplifiable or-patterns. That would guarantee
@@ -1661,6 +1640,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16611640
assert!(leaf_candidate.match_pairs.is_empty());
16621641
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
16631642
let or_start = leaf_candidate.pre_binding_block.unwrap();
1643+
let otherwise =
1644+
self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]);
16641645
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
16651646
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
16661647
// directly to `last_otherwise`. If there is a guard,
@@ -1671,13 +1652,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16711652
} else {
16721653
last_otherwise.unwrap()
16731654
};
1674-
self.match_candidates(
1675-
span,
1676-
scrutinee_span,
1677-
or_start,
1678-
or_otherwise,
1679-
&mut [leaf_candidate],
1680-
);
1655+
self.cfg.goto(otherwise, source_info, or_otherwise);
16811656
});
16821657
}
16831658
}
@@ -1956,15 +1931,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19561931
let target_blocks: FxIndexMap<_, _> = target_candidates
19571932
.into_iter()
19581933
.map(|(branch, mut candidates)| {
1959-
let candidate_start = self.cfg.start_new_block();
1960-
self.match_candidates(
1961-
span,
1962-
scrutinee_span,
1963-
candidate_start,
1964-
remainder_start,
1965-
&mut *candidates,
1966-
);
1967-
(branch, candidate_start)
1934+
let branch_start = self.cfg.start_new_block();
1935+
let branch_otherwise =
1936+
self.match_candidates(span, scrutinee_span, branch_start, &mut *candidates);
1937+
let source_info = self.source_info(span);
1938+
self.cfg.goto(branch_otherwise, source_info, remainder_start);
1939+
(branch, branch_start)
19681940
})
19691941
.collect();
19701942

tests/mir-opt/building/issue_101867.main.built.after.mir

+7-11
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ fn main() -> () {
2727
StorageLive(_5);
2828
PlaceMention(_1);
2929
_6 = discriminant(_1);
30-
switchInt(move _6) -> [1: bb5, otherwise: bb4];
30+
switchInt(move _6) -> [1: bb4, otherwise: bb3];
3131
}
3232

3333
bb1: {
3434
StorageLive(_3);
3535
StorageLive(_4);
36-
_4 = begin_panic::<&str>(const "explicit panic") -> bb9;
36+
_4 = begin_panic::<&str>(const "explicit panic") -> bb8;
3737
}
3838

3939
bb2: {
@@ -43,35 +43,31 @@ fn main() -> () {
4343
}
4444

4545
bb3: {
46-
goto -> bb8;
46+
goto -> bb7;
4747
}
4848

4949
bb4: {
50-
goto -> bb3;
50+
falseEdge -> [real: bb6, imaginary: bb3];
5151
}
5252

5353
bb5: {
54-
falseEdge -> [real: bb7, imaginary: bb3];
54+
goto -> bb3;
5555
}
5656

5757
bb6: {
58-
goto -> bb4;
59-
}
60-
61-
bb7: {
6258
_5 = ((_1 as Some).0: u8);
6359
_0 = const ();
6460
StorageDead(_5);
6561
StorageDead(_1);
6662
return;
6763
}
6864

69-
bb8: {
65+
bb7: {
7066
StorageDead(_5);
7167
goto -> bb1;
7268
}
7369

74-
bb9 (cleanup): {
70+
bb8 (cleanup): {
7571
resume;
7672
}
7773
}

tests/mir-opt/building/issue_49232.main.built.after.mir

+15-19
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ fn main() -> () {
1717
}
1818

1919
bb1: {
20-
falseUnwind -> [real: bb2, unwind: bb15];
20+
falseUnwind -> [real: bb2, unwind: bb14];
2121
}
2222

2323
bb2: {
2424
StorageLive(_2);
2525
StorageLive(_3);
2626
_3 = const true;
2727
PlaceMention(_3);
28-
switchInt(_3) -> [0: bb5, otherwise: bb7];
28+
switchInt(_3) -> [0: bb4, otherwise: bb6];
2929
}
3030

3131
bb3: {
@@ -34,63 +34,59 @@ fn main() -> () {
3434
}
3535

3636
bb4: {
37-
goto -> bb3;
37+
falseEdge -> [real: bb8, imaginary: bb6];
3838
}
3939

4040
bb5: {
41-
falseEdge -> [real: bb9, imaginary: bb7];
41+
goto -> bb3;
4242
}
4343

4444
bb6: {
45-
goto -> bb4;
45+
_0 = const ();
46+
goto -> bb13;
4647
}
4748

4849
bb7: {
49-
_0 = const ();
50-
goto -> bb14;
50+
goto -> bb3;
5151
}
5252

5353
bb8: {
54-
goto -> bb4;
54+
_2 = const 4_i32;
55+
goto -> bb11;
5556
}
5657

5758
bb9: {
58-
_2 = const 4_i32;
59-
goto -> bb12;
59+
unreachable;
6060
}
6161

6262
bb10: {
63-
unreachable;
63+
goto -> bb11;
6464
}
6565

6666
bb11: {
67-
goto -> bb12;
68-
}
69-
70-
bb12: {
7167
FakeRead(ForLet(None), _2);
7268
StorageDead(_3);
7369
StorageLive(_5);
7470
StorageLive(_6);
7571
_6 = &_2;
76-
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb13, unwind: bb15];
72+
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb12, unwind: bb14];
7773
}
7874

79-
bb13: {
75+
bb12: {
8076
StorageDead(_6);
8177
StorageDead(_5);
8278
_1 = const ();
8379
StorageDead(_2);
8480
goto -> bb1;
8581
}
8682

87-
bb14: {
83+
bb13: {
8884
StorageDead(_3);
8985
StorageDead(_2);
9086
return;
9187
}
9288

93-
bb15 (cleanup): {
89+
bb14 (cleanup): {
9490
resume;
9591
}
9692
}

0 commit comments

Comments
 (0)