Skip to content

Commit

Permalink
Create fewer basic blocks in match MIR lowering
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed May 11, 2019
1 parent b9ba8e5 commit d7f31a6
Show file tree
Hide file tree
Showing 28 changed files with 618 additions and 562 deletions.
216 changes: 110 additions & 106 deletions src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,33 +205,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
.flat_map(|(_, candidates)| candidates)
.collect::<Vec<_>>();

let outer_source_info = self.source_info(span);

// this will generate code to test scrutinee_place and
// branch to the appropriate arm block
let otherwise = self.match_candidates(
self.match_candidates(
scrutinee_span,
&mut Some(block),
None,
candidates,
block,
&mut fake_borrows,
);

let outer_source_info = self.source_info(span);

if !otherwise.is_empty() {
// All matches are exhaustive. However, because some matches
// only have exponentially-large exhaustive decision trees, we
// sometimes generate an inexhaustive decision tree.
//
// In that case, the inexhaustive tips of the decision tree
// can't be reached - terminate them with an `unreachable`.
let mut otherwise = otherwise;
otherwise.sort();
otherwise.dedup(); // variant switches can introduce duplicate target blocks
for block in otherwise {
self.cfg
.terminate(block, outer_source_info, TerminatorKind::Unreachable);
}
}

// Step 4. Determine the fake borrows that are needed from the above
// places. Create the required temporaries for them.

Expand Down Expand Up @@ -264,19 +249,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
);
} else {
arm_block = self.cfg.start_new_block();
for candidate in candidates {
for candidate in candidates {
let binding_end = self.bind_and_guard_matched_candidate(
candidate,
arm.guard.clone(),
&fake_borrow_temps,
scrutinee_span,
);
candidate,
arm.guard.clone(),
&fake_borrow_temps,
scrutinee_span,
);
self.cfg.terminate(
binding_end,
source_info,
TerminatorKind::Goto { target: arm_block },
);
}
}
}

if let Some(source_scope) = scope {
Expand Down Expand Up @@ -793,11 +778,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// the value, we will generate a branch to the appropriate
/// prebinding block.
///
/// The return value is a list of "otherwise" blocks. These are
/// points in execution where we found that *NONE* of the
/// candidates apply. In principle, this means that the input
/// list was not exhaustive, though at present we sometimes are
/// not smart enough to recognize all exhaustive inputs.
/// If we find that *NONE* of the candidates apply, we branch to the
/// `otherwise_block`. In principle, this means that the input list was not
/// exhaustive, though at present we sometimes are not smart enough to
/// recognize all exhaustive inputs.
///
/// It might be surprising that the input can be inexhaustive.
/// Indeed, initially, it is not, because all matches are
Expand All @@ -811,13 +795,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn match_candidates<'pat>(
&mut self,
span: Span,
start_block: &mut Option<BasicBlock>,
otherwise_block: Option<BasicBlock>,
candidates: &mut [&mut Candidate<'pat, 'tcx>],
mut block: BasicBlock,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) -> Vec<BasicBlock> {
) {
debug!(
"matched_candidate(span={:?}, block={:?}, candidates={:?})",
span, block, candidates
"matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})",
span,
candidates,
start_block,
otherwise_block,
);

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

let block: BasicBlock;

if !matched_candidates.is_empty() {
block = if let Some(last_otherwise_block) = self.select_matched_candidates(
let otherwise_block = self.select_matched_candidates(
matched_candidates,
block,
start_block,
fake_borrows,
) {
last_otherwise_block
);

if let Some(last_otherwise_block) = otherwise_block {
block = last_otherwise_block
} else {
// Any remaining candidates are unreachable.
if unmatched_candidates.is_empty() {
return Vec::new();
} else {
self.cfg.start_new_block()
return;
}
block = self.cfg.start_new_block();
};
} else {
block = *start_block.get_or_insert_with(|| self.cfg.start_new_block());
}

// If there are no candidates that still need testing, we're
// done. Since all matches are exhaustive, execution should
// never reach this point.
if unmatched_candidates.is_empty() {
return vec![block];
let source_info = self.source_info(span);
if let Some(otherwise) = otherwise_block {
self.cfg.terminate(
block,
source_info,
TerminatorKind::Goto { target: otherwise },
);
} else {
self.cfg.terminate(
block,
source_info,
TerminatorKind::Unreachable,
)
}
return;
}

// Test candidates where possible.
let (otherwise, untested_candidates) = self.test_candidates(
// Test for the remaining candidates.
self.test_candidates(
span,
unmatched_candidates,
block,
otherwise_block,
fake_borrows,
);

// If the target candidates were exhaustive, then we are done.
// But for borrowck continue build decision tree.
if untested_candidates.is_empty() {
return otherwise;
}

// Otherwise, let's process those remaining candidates.
let join_block = self.join_otherwise_blocks(span, otherwise);
self.match_candidates(
span,
untested_candidates,
join_block,
&mut None,
)
}

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

let first_candidate = &reachable_candidates[0];
let first_prebinding_block = first_candidate.pre_binding_block;

let candidate_source_info = self.source_info(first_candidate.span);

self.cfg.terminate(
block,
candidate_source_info,
TerminatorKind::Goto {
target: first_candidate.pre_binding_block,
},
);
if let Some(start_block) = *start_block {
let source_info = self.source_info(first_candidate.span);
self.cfg.terminate(
start_block,
source_info,
TerminatorKind::Goto { target: first_prebinding_block },
);
} else {
*start_block = Some(first_prebinding_block);
}

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

fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
let source_info = self.source_info(span);
otherwise.sort();
otherwise.dedup(); // variant switches can introduce duplicate target blocks
if otherwise.len() == 1 {
otherwise[0]
} else {
let join_block = self.cfg.start_new_block();
for block in otherwise {
self.cfg.terminate(
block,
source_info,
TerminatorKind::Goto { target: join_block },
);
}
join_block
}
}

/// This is the most subtle part of the matching algorithm. At
/// this point, the input candidates have been fully simplified,
/// and so we know that all remaining match-pairs require some
Expand Down Expand Up @@ -1154,8 +1130,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
span: Span,
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
block: BasicBlock,
mut otherwise_block: Option<BasicBlock>,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) -> (Vec<BasicBlock>, &'b mut [&'c mut Candidate<'pat, 'tcx>]) {
) {
// extract the match-pair from the highest priority candidate
let match_pair = &candidates.first().unwrap().match_pairs[0];
let mut test = self.test(match_pair);
Expand Down Expand Up @@ -1209,9 +1186,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
"match_candidates: test={:?} match_pair={:?}",
test, match_pair
);
let target_blocks = self.perform_test(block, &match_place, &test);
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
target_candidates.resize_with(target_blocks.len(), Default::default);
target_candidates.resize_with(test.targets(), Default::default);

let total_candidate_count = candidates.len();

Expand All @@ -1237,20 +1213,48 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// apply. Collect a list of blocks where control flow will
// branch if one of the `target_candidate` sets is not
// exhaustive.
let otherwise: Vec<_> = target_blocks
.into_iter()
.zip(target_candidates)
.flat_map(|(target_block, mut target_candidates)| {
if !candidates.is_empty() {
let remainder_start = &mut None;
self.match_candidates(
span,
remainder_start,
otherwise_block,
candidates,
fake_borrows,
);
otherwise_block = Some(remainder_start.unwrap());
};
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
if candidates.len() != 0 {
let candidate_start = &mut None;
self.match_candidates(
span,
&mut *target_candidates,
target_block,
candidate_start,
otherwise_block,
&mut *candidates,
fake_borrows,
)
})
.collect();
);
candidate_start.unwrap()
} else {
*otherwise_block.get_or_insert_with(|| {
let unreachable = self.cfg.start_new_block();
let source_info = self.source_info(span);
self.cfg.terminate(
unreachable,
source_info,
TerminatorKind::Unreachable,
);
unreachable
})
}
}).collect();

(otherwise, candidates)
self.perform_test(
block,
&match_place,
&test,
target_blocks,
);
}

// Determine the fake borrows that are needed to ensure that the place
Expand Down Expand Up @@ -1344,10 +1348,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block,
fresh_block,
candidate.next_candidate_pre_binding_block,
candidate_source_info,
);
candidate_source_info,
);
block = fresh_block;
self.ascribe_types(block, &candidate.ascriptions);
self.ascribe_types(block, &candidate.ascriptions);
} else {
return block;
}
Expand Down
Loading

0 comments on commit d7f31a6

Please sign in to comment.