Generate prebinding blocks lazily

This commit is contained in:
Matthew Jasper 2019-12-27 12:25:53 +00:00
parent 425e494fce
commit eecee76652

View File

@ -26,7 +26,6 @@ mod simplify;
mod test;
mod util;
use itertools::Itertools;
use std::convert::TryFrom;
impl<'a, 'tcx> Builder<'a, 'tcx> {
@ -66,12 +65,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// We generate MIR in the following steps:
///
/// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]).
/// 2. Create the prebinding and otherwise blocks ([Builder::create_match_candidates]).
/// 3. Create the decision tree ([Builder::lower_match_tree]).
/// 4. Determine the fake borrows that are needed from the places that were
/// 2. Create the decision tree ([Builder::lower_match_tree]).
/// 3. Determine the fake borrows that are needed from the places that were
/// matched against and create the required temporaries for them
/// ([Builder::calculate_fake_borrows]).
/// 5. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
/// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]).
///
/// ## False edges
///
@ -148,13 +146,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee: &Place<'tcx>,
arms: &'pat [Arm<'tcx>],
) -> Vec<(&'pat Arm<'tcx>, Vec<Candidate<'pat, 'tcx>>)> {
let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::<usize>();
let pre_binding_blocks: Vec<_> =
(0..candidate_count).map(|_| self.cfg.start_new_block()).collect();
let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1);
// Assemble a list of candidates: there is one candidate per pattern,
// which means there may be more than one candidate *per arm*.
arms.iter()
@ -163,21 +154,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let arm_candidates: Vec<_> = arm
.top_pats_hack()
.iter()
.zip(candidate_pre_binding_blocks.by_ref())
.map(|(pattern, pre_binding_block)| Candidate {
.map(|pattern| Candidate {
span: pattern.span,
has_guard: arm_has_guard,
match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)],
bindings: vec![],
ascriptions: vec![],
otherwise_block: if arm_has_guard {
Some(self.cfg.start_new_block())
} else {
None
},
pre_binding_block: *pre_binding_block,
next_candidate_pre_binding_block: next_candidate_pre_binding_blocks
.next()
.copied(),
otherwise_block: None,
pre_binding_block: None,
next_candidate_pre_binding_block: None,
})
.collect();
(arm, arm_candidates)
@ -203,16 +188,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// them.
let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None };
let mut otherwise = None;
// This will generate code to test scrutinee_place and
// branch to the appropriate arm block
self.match_candidates(
scrutinee_span,
&mut Some(block),
None,
block,
&mut otherwise,
&mut candidates,
&mut fake_borrows,
);
if let Some(otherwise_block) = otherwise {
let source_info = self.source_info(scrutinee_span);
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
}
let mut next_prebinding_block = None;
for candidate in candidates.iter_mut().rev() {
candidate.next_candidate_pre_binding_block = next_prebinding_block;
next_prebinding_block = candidate.pre_binding_block;
}
if let Some(ref borrows) = fake_borrows {
self.calculate_fake_borrows(borrows, scrutinee_span)
} else {
@ -427,13 +426,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// create a dummy candidate
let mut candidate = Candidate {
span: irrefutable_pat.span,
has_guard: false,
match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)],
bindings: vec![],
ascriptions: vec![],
// since we don't call `match_candidates`, next fields are unused
otherwise_block: None,
pre_binding_block: block,
pre_binding_block: None,
next_candidate_pre_binding_block: None,
};
@ -645,6 +645,8 @@ crate struct Candidate<'pat, 'tcx> {
// span of the original pattern that gave rise to this candidate
span: Span,
has_guard: bool,
// all of these must be satisfied...
match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
@ -658,7 +660,7 @@ crate struct Candidate<'pat, 'tcx> {
otherwise_block: Option<BasicBlock>,
// ...and the blocks for add false edges between candidates
pre_binding_block: BasicBlock,
pre_binding_block: Option<BasicBlock>,
next_candidate_pre_binding_block: Option<BasicBlock>,
}
@ -758,13 +760,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// which of these candidates, if any, is the correct one. The
/// candidates are sorted such that the first item in the list
/// has the highest priority. When a candidate is found to match
/// the value, we will generate a branch to the appropriate
/// the value, we will set and generate a branch to the appropriate
/// prebinding block.
///
/// 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.
/// `otherwise_block`, setting it to `Some` if required. 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
@ -778,8 +780,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn match_candidates<'pat>(
&mut self,
span: Span,
start_block: &mut Option<BasicBlock>,
otherwise_block: Option<BasicBlock>,
start_block: BasicBlock,
otherwise_block: &mut Option<BasicBlock>,
candidates: &mut [&mut Candidate<'pat, 'tcx>],
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
@ -802,7 +804,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
let block: BasicBlock = if !matched_candidates.is_empty() {
let block = if !matched_candidates.is_empty() {
let otherwise_block =
self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
@ -816,7 +818,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.start_new_block()
}
} else {
*start_block.get_or_insert_with(|| self.cfg.start_new_block())
start_block
};
// If there are no candidates that still need testing, we're
@ -824,9 +826,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// never reach this point.
if unmatched_candidates.is_empty() {
let source_info = self.source_info(span);
match otherwise_block {
Some(otherwise) => self.cfg.goto(block, source_info, otherwise),
None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable),
if let Some(otherwise) = *otherwise_block {
self.cfg.goto(block, source_info, otherwise);
} else {
*otherwise_block = Some(block);
}
return;
}
@ -856,7 +859,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn select_matched_candidates(
&mut self,
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
start_block: &mut Option<BasicBlock>,
start_block: BasicBlock,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) -> Option<BasicBlock> {
debug_assert!(
@ -899,66 +902,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fully_matched_with_guard = matched_candidates
.iter()
.position(|c| c.otherwise_block.is_none())
.position(|c| !c.has_guard)
.unwrap_or(matched_candidates.len() - 1);
let (reachable_candidates, unreachable_candidates) =
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 mut next_prebinding = start_block;
// `goto -> first_prebinding_block` from the `start_block` if there is one.
if let Some(start_block) = *start_block {
let source_info = self.source_info(first_candidate.span);
self.cfg.goto(start_block, source_info, first_prebinding_block);
} else {
*start_block = Some(first_prebinding_block);
}
for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() {
let source_info = self.source_info(first_candidate.span);
if let Some(otherwise_block) = first_candidate.otherwise_block {
self.false_edges(
otherwise_block,
second_candidate.pre_binding_block,
first_candidate.next_candidate_pre_binding_block,
source_info,
);
} else {
bug!("candidate other than the last has no guard");
for candidate in reachable_candidates.iter_mut() {
assert!(candidate.otherwise_block.is_none());
assert!(candidate.pre_binding_block.is_none());
candidate.pre_binding_block = Some(next_prebinding);
if candidate.has_guard {
next_prebinding = self.cfg.start_new_block();
candidate.otherwise_block = Some(next_prebinding);
}
}
debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates);
debug!(
"match_candidates: add pre_binding_blocks for unreachable {:?}",
unreachable_candidates,
);
for candidate in unreachable_candidates {
if let Some(otherwise) = candidate.otherwise_block {
let source_info = self.source_info(candidate.span);
let unreachable = self.cfg.start_new_block();
self.false_edges(
otherwise,
unreachable,
candidate.next_candidate_pre_binding_block,
source_info,
);
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
}
assert!(candidate.pre_binding_block.is_none());
candidate.pre_binding_block = Some(self.cfg.start_new_block());
}
let last_candidate = reachable_candidates.last().unwrap();
if let Some(otherwise) = last_candidate.otherwise_block {
let source_info = self.source_info(last_candidate.span);
let block = self.cfg.start_new_block();
self.false_edges(
otherwise,
block,
last_candidate.next_candidate_pre_binding_block,
source_info,
);
Some(block)
} else {
None
}
reachable_candidates.last_mut().unwrap().otherwise_block
}
/// This is the most subtle part of the matching algorithm. At
@ -1078,7 +1049,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span: Span,
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
block: BasicBlock,
mut otherwise_block: Option<BasicBlock>,
otherwise_block: &mut Option<BasicBlock>,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
// extract the match-pair from the highest priority candidate
@ -1150,12 +1121,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// improves the speed of llvm when optimizing long string literal
// matches
let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
// The block that we should branch to if none of the
// `target_candidates` match. This is either the block where we
// start matching the untested candidates if there are any,
// otherwise it's the `otherwise_block`.
let remainder_start = &mut None;
let remainder_start =
if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
// For each outcome of test, process the candidates that still
// apply. Collect a list of blocks where control flow will
// branch if one of the `target_candidate` sets is not
// exhaustive.
let target_blocks: Vec<_> = target_candidates
.into_iter()
.map(|mut candidates| {
if candidates.len() != 0 {
let candidate_start = this.cfg.start_new_block();
this.match_candidates(
span,
candidate_start,
remainder_start,
&mut *candidates,
fake_borrows,
);
candidate_start
} else {
*remainder_start.get_or_insert_with(|| this.cfg.start_new_block())
}
})
.collect();
if !candidates.is_empty() {
let remainder_start = &mut None;
let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block());
this.match_candidates(
span,
remainder_start,
@ -1163,36 +1161,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidates,
fake_borrows,
);
otherwise_block = Some(remainder_start.unwrap());
};
target_candidates
.into_iter()
.map(|mut candidates| {
if candidates.len() != 0 {
let candidate_start = &mut None;
this.match_candidates(
span,
candidate_start,
otherwise_block,
&mut *candidates,
fake_borrows,
);
candidate_start.unwrap()
} else {
*otherwise_block.get_or_insert_with(|| {
let unreachable = this.cfg.start_new_block();
let source_info = this.source_info(span);
this.cfg.terminate(
unreachable,
source_info,
TerminatorKind::Unreachable,
);
unreachable
})
}
})
.collect()
target_blocks
};
self.perform_test(block, &match_place, &test, make_target_blocks);
@ -1297,7 +1268,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let candidate_source_info = self.source_info(candidate.span);
let mut block = candidate.pre_binding_block;
let mut block = candidate.pre_binding_block.unwrap();
// If we are adding our own statements, then we need a fresh block.
let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some()
@ -1437,11 +1408,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp));
}
let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| {
let unreachable = self.cfg.start_new_block();
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
unreachable
});
let outside_scope = self.cfg.start_new_block();
self.exit_scope(
source_info.span,
region_scope,
otherwise_post_guard_block,
candidate.otherwise_block.unwrap(),
outside_scope,
);
self.false_edges(
outside_scope,
otherwise_block,
candidate.next_candidate_pre_binding_block,
source_info,
);
// We want to ensure that the matched candidates are bound