Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserve local scopes in generator MIR #60840

Merged
merged 4 commits into from
May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {

fn_span: Span,
arg_count: usize,
is_generator: bool,

/// The current set of scopes, updated as we traverse;
/// see the `scope` module for more details.
Expand Down Expand Up @@ -689,7 +690,8 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
return_ty,
return_ty_span,
upvar_debuginfo,
upvar_mutbls);
upvar_mutbls,
body.is_generator);

let call_site_scope = region::Scope {
id: body.value.hir_id.local_id,
Expand Down Expand Up @@ -759,6 +761,7 @@ fn construct_const<'a, 'gcx, 'tcx>(
const_ty_span,
vec![],
vec![],
false,
);

let mut block = START_BLOCK;
Expand Down Expand Up @@ -788,7 +791,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
let owner_id = hir.tcx().hir().body_owner(body_id);
let span = hir.tcx().hir().span(owner_id);
let ty = hir.tcx().types.err;
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![]);
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![], false);
let source_info = builder.source_info(span);
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
builder.finish(None)
Expand All @@ -802,14 +805,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
return_ty: Ty<'tcx>,
return_span: Span,
__upvar_debuginfo_codegen_only_do_not_use: Vec<UpvarDebuginfo>,
upvar_mutbls: Vec<Mutability>)
upvar_mutbls: Vec<Mutability>,
is_generator: bool)
-> Builder<'a, 'gcx, 'tcx> {
let lint_level = LintLevel::Explicit(hir.root_lint_level);
let mut builder = Builder {
hir,
cfg: CFG { basic_blocks: IndexVec::new() },
fn_span: span,
arg_count,
is_generator,
scopes: vec![],
block_context: BlockContext::new(),
source_scopes: IndexVec::new(),
Expand Down
114 changes: 76 additions & 38 deletions src/librustc_mir/build/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@ use rustc::middle::region;
use rustc::ty::Ty;
use rustc::hir;
use rustc::mir::*;
use syntax_pos::{Span};
use syntax_pos::{Span, DUMMY_SP};
use rustc_data_structures::fx::FxHashMap;
use std::collections::hash_map::Entry;
use std::mem;

#[derive(Debug)]
pub struct Scope<'tcx> {
Expand All @@ -107,6 +108,8 @@ pub struct Scope<'tcx> {
/// * polluting the cleanup MIR with StorageDead creates
/// landing pads even though there's no actual destructors
/// * freeing up stack space has no effect during unwinding
/// Note that for generators we do emit StorageDeads, for the
/// use of optimizations in the MIR generator transform.
needs_cleanup: bool,

/// set of places to drop when exiting this scope. This starts
Expand Down Expand Up @@ -466,10 +469,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
/// This path terminates in GeneratorDrop. Returns the start of the path.
/// None indicates there’s no cleanup to do at this point.
pub fn generator_drop_cleanup(&mut self) -> Option<BasicBlock> {
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
return None;
}

// Fill in the cache for unwinds
self.diverge_cleanup_gen(true);

Expand All @@ -480,7 +479,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let result = block;

while let Some(scope) = scopes.next() {
if !scope.needs_cleanup {
if !scope.needs_cleanup && !self.is_generator {
continue;
}

Expand Down Expand Up @@ -802,7 +801,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {

for scope in self.scopes[first_uncached..].iter_mut() {
target = build_diverge_scope(&mut self.cfg, scope.region_scope_span,
scope, target, generator_drop);
scope, target, generator_drop, self.is_generator);
}

target
Expand Down Expand Up @@ -900,12 +899,6 @@ fn build_scope_drops<'tcx>(
// drops panic (panicking while unwinding will abort, so there's no need for
// another set of arrows). The drops for the unwind path should have already
// been generated by `diverge_cleanup_gen`.
//
// The code in this function reads from right to left.
// Storage dead drops have to be done left to right (since we can only push
// to the end of a Vec). So, we find the next drop and then call
// push_storage_deads which will iterate backwards through them so that
// they are added in the correct order.

let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| {
if let DropKind::Value { cached_block } = drop_data.kind {
Expand Down Expand Up @@ -936,11 +929,6 @@ fn build_scope_drops<'tcx>(
block = next;
}
DropKind::Storage => {
// We do not need to emit StorageDead for generator drops
if generator_drop {
continue
}

// Drop the storage for both value and storage drops.
// Only temps and vars need their storage dead.
match drop_data.location {
Expand All @@ -962,7 +950,8 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
span: Span,
scope: &mut Scope<'tcx>,
mut target: BasicBlock,
generator_drop: bool)
generator_drop: bool,
is_generator: bool)
-> BasicBlock
{
// Build up the drops in **reverse** order. The end result will
Expand All @@ -981,41 +970,90 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
scope: source_scope
};

// Next, build up the drops. Here we iterate the vector in
// We keep track of StorageDead statements to prepend to our current block
// and store them here, in reverse order.
let mut storage_deads = vec![];

let mut target_built_by_us = false;

// Build up the drops. Here we iterate the vector in
// *forward* order, so that we generate drops[0] first (right to
// left in diagram above).
for (j, drop_data) in scope.drops.iter_mut().enumerate() {
debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data);
// Only full value drops are emitted in the diverging path,
// not StorageDead.
// not StorageDead, except in the case of generators.
//
// Note: This may not actually be what we desire (are we
// "freeing" stack storage as we unwind, or merely observing a
// frozen stack)? In particular, the intent may have been to
// match the behavior of clang, but on inspection eddyb says
// this is not what clang does.
let cached_block = match drop_data.kind {
DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop),
DropKind::Storage => continue
};
target = if let Some(cached_block) = *cached_block {
cached_block
} else {
let block = cfg.start_new_cleanup_block();
cfg.terminate(block, source_info(drop_data.span),
TerminatorKind::Drop {
location: drop_data.location.clone(),
target,
unwind: None
});
*cached_block = Some(block);
block
match drop_data.kind {
DropKind::Storage if is_generator => {
// Only temps and vars need their storage dead.
match drop_data.location {
Place::Base(PlaceBase::Local(index)) => {
storage_deads.push(Statement {
source_info: source_info(drop_data.span),
kind: StatementKind::StorageDead(index)
});
}
_ => unreachable!(),
};
}
DropKind::Storage => {}
DropKind::Value { ref mut cached_block } => {
let cached_block = cached_block.ref_mut(generator_drop);
target = if let Some(cached_block) = *cached_block {
storage_deads.clear();
target_built_by_us = false;
cached_block
} else {
push_storage_deads(
cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope);
let block = cfg.start_new_cleanup_block();
cfg.terminate(block, source_info(drop_data.span),
TerminatorKind::Drop {
location: drop_data.location.clone(),
target,
unwind: None
});
*cached_block = Some(block);
target_built_by_us = true;
block
};
}
};
}

push_storage_deads(cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope);
*scope.cached_unwind.ref_mut(generator_drop) = Some(target);

assert!(storage_deads.is_empty());
debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target);

target
}

fn push_storage_deads(cfg: &mut CFG<'tcx>,
target: &mut BasicBlock,
storage_deads: &mut Vec<Statement<'tcx>>,
target_built_by_us: bool,
source_scope: SourceScope) {
if storage_deads.is_empty() { return; }
if !target_built_by_us {
// We cannot add statements to an existing block, so we create a new
// block for our StorageDead statements.
let block = cfg.start_new_cleanup_block();
let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope };
cfg.terminate(block, source_info, TerminatorKind::Goto { target: *target });
*target = block;
}
let statements = &mut cfg.block_data_mut(*target).statements;
storage_deads.reverse();
debug!("push_storage_deads({:?}), storage_deads={:?}, statements={:?}",
*target, storage_deads, statements);
storage_deads.append(statements);
mem::swap(statements, storage_deads);
assert!(storage_deads.is_empty());
}
11 changes: 8 additions & 3 deletions src/librustc_mir/dataflow/impls/storage_liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
}

fn terminator_effect(&self,
_sets: &mut BlockSets<'_, Local>,
_loc: Location) {
// Terminators have no effect
sets: &mut BlockSets<'_, Local>,
loc: Location) {
match &self.mir[loc.block].terminator().kind {
TerminatorKind::Drop { location, .. } => if let Some(l) = location.local() {
sets.kill(l);
}
_ => (),
}
}

fn propagate_call_return(
Expand Down
1 change: 1 addition & 0 deletions src/test/mir-opt/generator-drop-cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn main() {
// switchInt(move _5) -> [0u32: bb4, 3u32: bb7, otherwise: bb8];
// }
// bb1: {
// StorageDead(_3);
// goto -> bb5;
// }
// bb2: {
Expand Down