1
Fork 0

Avoid scheduling repeated StorageDeads

Also add some comments
This commit is contained in:
Matthew Jasper 2020-01-25 00:10:40 +00:00
parent 9b9dafb2c8
commit 8dbbe4d144
3 changed files with 52 additions and 15 deletions

View file

@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&pattern, &pattern,
UserTypeProjections::none(), UserTypeProjections::none(),
&mut |this, _, _, _, node, span, _, _| { &mut |this, _, _, _, node, span, _, _| {
this.storage_live_binding(block, node, span, OutsideGuard); this.storage_live_binding(block, node, span, OutsideGuard, true);
this.schedule_drop_for_binding(node, span, OutsideGuard); this.schedule_drop_for_binding(node, span, OutsideGuard);
}, },
) )

View file

@ -274,9 +274,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
end_block.unit() end_block.unit()
} }
/// Binds the variables and ascribes types for a given `match` arm. /// Binds the variables and ascribes types for a given `match` arm or
/// `let` binding.
/// ///
/// Also check if the guard matches, if it's provided. /// Also check if the guard matches, if it's provided.
/// `arm_scope` should be `Some` if and only if this is called for a
/// `match` arm.
fn bind_pattern( fn bind_pattern(
&mut self, &mut self,
outer_source_info: SourceInfo, outer_source_info: SourceInfo,
@ -298,6 +301,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
true, true,
) )
} else { } else {
// It's helpful to avoid scheduling drops multiple times to save
// drop elaboration from having to clean up the extra drops.
//
// If we are in a `let` then we only schedule drops for the first
// candidate.
//
// If we're in a `match` arm then we could have a case like so:
//
// Ok(x) | Err(x) if return => { /* ... */ }
//
// In this case we don't want a drop of `x` scheduled when we
// return: it isn't bound by move until right before enter the arm.
// To handle this we instead unschedule it's drop after each time
// we lower the guard.
let target_block = self.cfg.start_new_block(); let target_block = self.cfg.start_new_block();
let mut schedule_drops = true; let mut schedule_drops = true;
// We keep a stack of all of the bindings and type asciptions // We keep a stack of all of the bindings and type asciptions
@ -308,7 +325,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut Vec::new(), &mut Vec::new(),
&mut |leaf_candidate, parent_bindings| { &mut |leaf_candidate, parent_bindings| {
if let Some(arm_scope) = arm_scope { if let Some(arm_scope) = arm_scope {
// Avoid scheduling drops multiple times by unscheduling drops.
self.clear_top_scope(arm_scope); self.clear_top_scope(arm_scope);
} }
let binding_end = self.bind_and_guard_matched_candidate( let binding_end = self.bind_and_guard_matched_candidate(
@ -320,9 +336,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
schedule_drops, schedule_drops,
); );
if arm_scope.is_none() { if arm_scope.is_none() {
// If we aren't in a match, then our bindings may not be
// the only thing in the top scope, so only schedule
// them to drop for the first pattern instead.
schedule_drops = false; schedule_drops = false;
} }
self.cfg.goto(binding_end, outer_source_info, target_block); self.cfg.goto(binding_end, outer_source_info, target_block);
@ -350,7 +363,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Optimize the case of `let x = ...` to write directly into `x` // Optimize the case of `let x = ...` to write directly into `x`
PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
let place = let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.into(&place, block, initializer)); unpack!(block = self.into(&place, block, initializer));
// Inject a fake read, see comments on `FakeReadCause::ForLet`. // Inject a fake read, see comments on `FakeReadCause::ForLet`.
@ -385,7 +398,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span }, hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span },
} => { } => {
let place = let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.into(&place, block, initializer)); unpack!(block = self.into(&place, block, initializer));
// Inject a fake read, see comments on `FakeReadCause::ForLet`. // Inject a fake read, see comments on `FakeReadCause::ForLet`.
@ -532,12 +545,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
var: HirId, var: HirId,
span: Span, span: Span,
for_guard: ForGuard, for_guard: ForGuard,
schedule_drop: bool,
) -> Place<'tcx> { ) -> Place<'tcx> {
let local_id = self.var_local_id(var, for_guard); let local_id = self.var_local_id(var, for_guard);
let source_info = self.source_info(span); let source_info = self.source_info(span);
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
self.schedule_drop(span, region_scope, local_id, DropKind::Storage); if schedule_drop {
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
}
Place::from(local_id) Place::from(local_id)
} }
@ -1060,25 +1076,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// | /// |
/// +----------------------------------------+------------------------------------+ /// +----------------------------------------+------------------------------------+
/// | | | /// | | |
/// V V V
/// [ P matches ] [ Q matches ] [ otherwise ] /// [ P matches ] [ Q matches ] [ otherwise ]
/// | | | /// | | |
/// V V |
/// [ match R, S ] [ match R, S ] | /// [ match R, S ] [ match R, S ] |
/// | | | /// | | |
/// +--------------+------------+ +--------------+------------+ | /// +--------------+------------+ +--------------+------------+ |
/// | | | | | | | /// | | | | | | |
/// V V V V V V |
/// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] | /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] |
/// | | | | | | | /// | | | | | | |
/// +--------------+------------|------------+--------------+ | | /// +--------------+------------|------------+--------------+ | |
/// | | | | /// | | | |
/// | +----------------------------------------+--------+ /// | +----------------------------------------+--------+
/// | | /// | |
/// V V
/// [ Success ] [ Failure ] /// [ Success ] [ Failure ]
/// ``` /// ```
/// ///
/// In practice there are some complications: /// In practice there are some complications:
/// ///
/// * If there's a guard, then the otherwise branch of the first match on /// * If there's a guard, then the otherwise branch of the first match on
/// `R | S` goes to a test for whether `Q` matches. /// `R | S` goes to a test for whether `Q` matches, and the control flow
/// doesn't merge into a single success block until after the guard is
/// tested.
/// * If neither `P` or `Q` has any bindings or type ascriptions and there /// * If neither `P` or `Q` has any bindings or type ascriptions and there
/// isn't a match guard, then we create a smaller CFG like: /// isn't a match guard, then we create a smaller CFG like:
/// ///
@ -1658,7 +1680,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.flat_map(|(bindings, _)| bindings) .flat_map(|(bindings, _)| bindings)
.chain(&candidate.bindings); .chain(&candidate.bindings);
self.bind_matched_candidate_for_guard(block, bindings.clone()); self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
let guard_frame = GuardFrame { let guard_frame = GuardFrame {
locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
}; };
@ -1807,6 +1829,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_matched_candidate_for_guard<'b>( fn bind_matched_candidate_for_guard<'b>(
&mut self, &mut self,
block: BasicBlock, block: BasicBlock,
schedule_drops: bool,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>, bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
) where ) where
'tcx: 'b, 'tcx: 'b,
@ -1825,8 +1848,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// a reference R: &T pointing to the location matched by // a reference R: &T pointing to the location matched by
// the pattern, and every occurrence of P within a guard // the pattern, and every occurrence of P within a guard
// denotes *R. // denotes *R.
let ref_for_guard = let ref_for_guard = self.storage_live_binding(
self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard); block,
binding.var_id,
binding.span,
RefWithinGuard,
schedule_drops,
);
match binding.binding_mode { match binding.binding_mode {
BindingMode::ByValue => { BindingMode::ByValue => {
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
@ -1838,6 +1866,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
binding.var_id, binding.var_id,
binding.span, binding.span,
OutsideGuard, OutsideGuard,
schedule_drops,
); );
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source);
@ -1863,8 +1892,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Assign each of the bindings. This may trigger moves out of the candidate. // Assign each of the bindings. This may trigger moves out of the candidate.
for binding in bindings { for binding in bindings {
let source_info = self.source_info(binding.span); let source_info = self.source_info(binding.span);
let local = let local = self.storage_live_binding(
self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard); block,
binding.var_id,
binding.span,
OutsideGuard,
schedule_drops,
);
if schedule_drops { if schedule_drops {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
} }

View file

@ -75,6 +75,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
/// Given `candidate` that has a single or-pattern for its match-pairs,
/// creates a fresh candidate for each of its input subpatterns passed via
/// `pats`.
fn create_or_subcandidates<'pat>( fn create_or_subcandidates<'pat>(
&mut self, &mut self,
candidate: &Candidate<'pat, 'tcx>, candidate: &Candidate<'pat, 'tcx>,