Avoid unnecessary false edges in MIR match lowering

This commit is contained in:
Matthew Jasper 2019-05-06 16:13:20 +01:00
parent 33bc396ff7
commit 32c337724d
2 changed files with 98 additions and 53 deletions

View File

@ -143,19 +143,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// create binding start block for link them by false edges
let candidate_count = arms.iter().map(|c| c.patterns.len()).sum::<usize>();
let pre_binding_blocks: Vec<_> = (0..=candidate_count)
let pre_binding_blocks: Vec<_> = (0..candidate_count)
.map(|_| self.cfg.start_new_block())
.collect();
// There's one more pre_binding block than there are candidates so that
// every candidate can have a `next_candidate_pre_binding_block`.
let outer_source_info = self.source_info(span);
self.cfg.terminate(
*pre_binding_blocks.last().unwrap(),
outer_source_info,
TerminatorKind::Unreachable,
);
let mut match_has_guard = false;
let mut candidate_pre_binding_blocks = pre_binding_blocks.iter();
@ -171,9 +162,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let arm_candidates: Vec<_> = arm.patterns
.iter()
.zip(candidate_pre_binding_blocks.by_ref())
.zip(next_candidate_pre_binding_blocks.by_ref())
.map(
|((pattern, pre_binding_block), next_candidate_pre_binding_block)| {
|(pattern, pre_binding_block)| {
Candidate {
span: pattern.span,
match_pairs: vec![
@ -188,7 +178,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
},
pre_binding_block: *pre_binding_block,
next_candidate_pre_binding_block:
*next_candidate_pre_binding_block,
next_candidate_pre_binding_blocks.next().copied(),
}
},
)
@ -225,6 +215,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
&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
@ -251,12 +243,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
};
// Step 5. Create everything else: the guards and the arms.
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, candidates)| {
let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, mut candidates)| {
let arm_source_info = self.source_info(arm.span);
let region_scope = (arm.scope, arm_source_info);
self.in_scope(region_scope, arm.lint_level, |this| {
let arm_block = this.cfg.start_new_block();
let mut arm_block = this.cfg.start_new_block();
let body = this.hir.mirror(arm.body.clone());
let scope = this.declare_bindings(
@ -267,6 +258,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
Some((Some(&scrutinee_place), scrutinee_span)),
);
if candidates.len() == 1 {
arm_block = self.bind_and_guard_matched_candidate(
candidates.pop().unwrap(),
arm.guard.clone(),
&fake_borrow_temps,
scrutinee_span,
);
} else {
arm_block = self.cfg.start_new_block();
for candidate in candidates {
let binding_end = self.bind_and_guard_matched_candidate(
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 {
this.source_scope = source_scope;
}
@ -434,7 +449,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// since we don't call `match_candidates`, next fields are unused
otherwise_block: None,
pre_binding_block: block,
next_candidate_pre_binding_block: block,
next_candidate_pre_binding_block: None,
};
// Simplify the candidate. Since the pattern is irrefutable, this should
@ -689,7 +704,7 @@ pub struct Candidate<'pat, 'tcx: 'pat> {
// ...and the blocks for add false edges between candidates
pre_binding_block: BasicBlock,
next_candidate_pre_binding_block: BasicBlock,
next_candidate_pre_binding_block: Option<BasicBlock>,
}
#[derive(Clone, Debug)]
@ -956,14 +971,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let [first_candidate, second_candidate] = window {
let source_info = self.source_info(first_candidate.span);
if let Some(otherwise_block) = first_candidate.otherwise_block {
self.cfg.terminate(
self.false_edges(
otherwise_block,
second_candidate.pre_binding_block,
first_candidate.next_candidate_pre_binding_block,
source_info,
TerminatorKind::FalseEdges {
real_target: second_candidate.pre_binding_block,
imaginary_target: first_candidate.next_candidate_pre_binding_block,
}
)
);
} else {
bug!("candidate other than the last has no guard");
}
@ -977,13 +990,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(otherwise) = candidate.otherwise_block {
let source_info = self.source_info(candidate.span);
let unreachable = self.cfg.start_new_block();
self.cfg.terminate(
self.false_edges(
otherwise,
unreachable,
candidate.next_candidate_pre_binding_block,
source_info,
TerminatorKind::FalseEdges {
real_target: unreachable,
imaginary_targets: candidate.next_candidate_pre_binding_block,
}
);
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
}
@ -994,13 +1005,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
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.cfg.terminate(
self.false_edges(
otherwise,
block,
last_candidate.next_candidate_pre_binding_block,
source_info,
TerminatorKind::FalseEdges {
real_target: block,
imaginary_target: last_candidate.next_candidate_pre_binding_block,
}
);
Some(block)
} else {
@ -1311,27 +1320,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
&mut self,
candidate: Candidate<'pat, 'tcx>,
guard: Option<Guard<'tcx>>,
arm_block: BasicBlock,
fake_borrows: &Vec<(&Place<'tcx>, Local)>,
scrutinee_span: Span,
region_scope: (region::Scope, SourceInfo),
) {
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
debug_assert!(candidate.match_pairs.is_empty());
let candidate_source_info = self.source_info(candidate.span);
let mut block = self.cfg.start_new_block();
self.cfg.terminate(
candidate.pre_binding_block,
let mut block = candidate.pre_binding_block;
// 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()
|| !candidate.bindings.is_empty()
|| !candidate.ascriptions.is_empty()
|| guard.is_some();
if create_fresh_block {
let fresh_block = self.cfg.start_new_block();
self.false_edges(
block,
fresh_block,
candidate.next_candidate_pre_binding_block,
candidate_source_info,
TerminatorKind::FalseEdges {
real_target: block,
imaginary_target: candidate.next_candidate_pre_binding_block,
},
);
block = fresh_block;
self.ascribe_types(block, &candidate.ascriptions);
} else {
return block;
}
// rust-lang/rust#27282: The `autoref` business deserves some
// explanation here.
@ -1476,7 +1496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// because that would be before we've checked the result
// from the guard.
//
// But binding them on `arm_block` is *too late*, because
// But binding them on the arm is *too late*, because
// then all of the candidates for a single arm would be
// bound in the same place, that would cause a case like:
//
@ -1552,22 +1572,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
by_value_bindings,
);
self.cfg.terminate(
post_guard_block,
source_info,
TerminatorKind::Goto { target: arm_block },
);
post_guard_block
} else {
assert!(candidate.otherwise_block.is_none());
// (Here, it is not too early to bind the matched
// candidate on `block`, because there is no guard result
// that we have to inspect before we bind them.)
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings);
self.cfg.terminate(
block,
candidate_source_info,
TerminatorKind::Goto { target: arm_block },
);
block
}
}

View File

@ -65,6 +65,39 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
})
);
}
/// Creates a false edge to `imaginary_target` and a real edge to
/// real_target. If `imaginary_target` is none, or is the same as the real
/// target, a Goto is generated instead to simplify the generated MIR.
pub fn false_edges(
&mut self,
from_block: BasicBlock,
real_target: BasicBlock,
imaginary_target: Option<BasicBlock>,
source_info: SourceInfo,
) {
match imaginary_target {
Some(target) if target != real_target => {
self.cfg.terminate(
from_block,
source_info,
TerminatorKind::FalseEdges {
real_target,
imaginary_target: target,
},
);
}
_ => {
self.cfg.terminate(
from_block,
source_info,
TerminatorKind::Goto {
target: real_target
}
);
}
}
}
}
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {