Skip to content

Commit

Permalink
rustc_mir: don't build unused unwind cleanup blocks
Browse files Browse the repository at this point in the history
The unused blocks are removed by SimplifyCfg, but they can cause a
significant performance slowdown before they are removed.
  • Loading branch information
arielb1 committed Jul 31, 2017
1 parent 1447daa commit 85c1027
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 153 deletions.
89 changes: 57 additions & 32 deletions src/librustc_mir/build/scope.rs
Expand Up @@ -200,6 +200,15 @@ pub struct BreakableScope<'tcx> {
pub break_destination: Lvalue<'tcx>,
}

impl DropKind {
fn may_panic(&self) -> bool {
match *self {
DropKind::Value { .. } => true,
DropKind::Storage => false
}
}
}

impl<'tcx> Scope<'tcx> {
/// Invalidate all the cached blocks in the scope.
///
Expand Down Expand Up @@ -337,9 +346,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
mut block: BasicBlock)
-> BlockAnd<()> {
debug!("pop_scope({:?}, {:?})", extent, block);
// We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
// to make sure all the `cached_block`s are filled in.
self.diverge_cleanup();
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.
let may_panic =
self.scopes.last().unwrap().drops.iter().any(|s| s.kind.may_panic());
if may_panic {
self.diverge_cleanup();
}
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.extent, extent.0);
unpack!(block = build_scope_drops(&mut self.cfg,
Expand Down Expand Up @@ -370,6 +383,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let len = self.scopes.len();
assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
let tmp = self.get_unit_temp();

// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.
let may_panic =
self.scopes[(len - scope_count)..].iter().any(|s| s.drops.iter().any(|s| s.kind.may_panic()));
if may_panic {
self.diverge_cleanup();
}

{
let mut rest = &mut self.scopes[(len - scope_count)..];
while let Some((scope, rest_)) = {rest}.split_last_mut() {
Expand Down Expand Up @@ -736,45 +758,48 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
mut block: BasicBlock,
arg_count: usize)
-> BlockAnd<()> {
debug!("build_scope_drops({:?} -> {:?})", block, scope);
let mut iter = scope.drops.iter().rev().peekable();
while let Some(drop_data) = iter.next() {
let source_info = scope.source_info(drop_data.span);
if let DropKind::Value { .. } = drop_data.kind {
// Try to find the next block with its cached block
// for us to diverge into in case the drop panics.
let on_diverge = iter.peek().iter().filter_map(|dd| {
match dd.kind {
DropKind::Value { cached_block } => cached_block,
DropKind::Storage => None
}
}).next();
// If there’s no `cached_block`s within current scope,
// we must look for one in the enclosing scope.
let on_diverge = on_diverge.or_else(||{
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
});
let next = cfg.start_new_block();
cfg.terminate(block, source_info, TerminatorKind::Drop {
location: drop_data.location.clone(),
target: next,
unwind: on_diverge
});
block = next;
}
match drop_data.kind {
DropKind::Value { .. } |
DropKind::Storage => {
// Only temps and vars need their storage dead.
match drop_data.location {
Lvalue::Local(index) if index.index() > arg_count => {}
_ => continue
}
DropKind::Value { .. } => {
// Try to find the next block with its cached block
// for us to diverge into in case the drop panics.
let on_diverge = iter.peek().iter().filter_map(|dd| {
match dd.kind {
DropKind::Value { cached_block: None } =>
span_bug!(drop_data.span, "cached block not present?"),
DropKind::Value { cached_block } => cached_block,
DropKind::Storage => None
}
}).next();
// If there’s no `cached_block`s within current scope,
// we must look for one in the enclosing scope.
let on_diverge = on_diverge.or_else(|| {
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
});
let next = cfg.start_new_block();
cfg.terminate(block, source_info, TerminatorKind::Drop {
location: drop_data.location.clone(),
target: next,
unwind: on_diverge
});
block = next;
}
DropKind::Storage => {}
}

// Drop the storage for both value and storage drops.
// Only temps and vars need their storage dead.
match drop_data.location {
Lvalue::Local(index) if index.index() > arg_count => {
cfg.push(block, Statement {
source_info: source_info,
kind: StatementKind::StorageDead(drop_data.location.clone())
});
}
_ => continue
}
}
block.unit()
Expand Down
26 changes: 13 additions & 13 deletions src/test/mir-opt/basic_assignment.rs
Expand Up @@ -47,39 +47,39 @@ fn main() {
// StorageDead(_3);
// StorageLive(_4);
// _4 = std::option::Option<std::boxed::Box<u32>>::None;
// StorageLive(_5);
// StorageLive(_6);
// StorageLive(_7);
// _7 = _4;
// replace(_6 <- _7) -> [return: bb6, unwind: bb7];
// _6 = _4;
// replace(_5 <- _6) -> [return: bb1, unwind: bb7];
// }
// bb1: {
// resume;
// drop(_6) -> [return: bb8, unwind: bb5];
// }
// bb2: {
// drop(_4) -> bb1;
// resume;
// }
// bb3: {
// goto -> bb2;
// drop(_4) -> bb2;
// }
// bb4: {
// drop(_6) -> bb3;
// goto -> bb3;
// }
// bb5: {
// goto -> bb4;
// drop(_5) -> bb4;
// }
// bb6: {
// drop(_7) -> [return: bb8, unwind: bb4];
// goto -> bb5;
// }
// bb7: {
// drop(_7) -> bb5;
// drop(_6) -> bb6;
// }
// bb8: {
// StorageDead(_7);
// StorageDead(_6);
// _0 = ();
// drop(_6) -> [return: bb9, unwind: bb2];
// drop(_5) -> [return: bb9, unwind: bb3];
// }
// bb9: {
// StorageDead(_6);
// StorageDead(_5);
// drop(_4) -> bb10;
// }
// bb10: {
Expand Down
38 changes: 19 additions & 19 deletions src/test/mir-opt/end_region_4.rs
Expand Up @@ -32,41 +32,41 @@ fn foo(i: i32) {
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// let mut _0: ();
// let _1: D;
// let _3: i32;
// let _4: &'6_2rce i32;
// let _2: i32;
// let _3: &'6_2rce i32;
// let _7: &'6_4rce i32;
// let mut _5: ();
// let mut _6: i32;
//
// let mut _4: ();
// let mut _5: i32;
// let mut _6: ();
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_2);
// _2 = const 0i32;
// StorageLive(_3);
// _3 = const 0i32;
// StorageLive(_4);
// _4 = &'6_2rce _3;
// StorageLive(_6);
// _6 = (*_4);
// _5 = const foo(_6) -> [return: bb2, unwind: bb3];
// _3 = &'6_2rce _2;
// StorageLive(_5);
// _5 = (*_3);
// _4 = const foo(_5) -> [return: bb1, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_6);
// StorageDead(_5);
// StorageLive(_7);
// _7 = &'6_4rce _3;
// _7 = &'6_4rce _2;
// _0 = ();
// StorageDead(_7);
// EndRegion('6_4rce);
// StorageDead(_4);
// EndRegion('6_2rce);
// StorageDead(_3);
// EndRegion('6_2rce);
// StorageDead(_2);
// drop(_1) -> bb4;
// }
// bb2: {
// resume;
// }
// bb3: {
// EndRegion('6_2rce);
// drop(_1) -> bb1;
// drop(_1) -> bb2;
// }
// bb4: {
// StorageDead(_1);
Expand Down
27 changes: 13 additions & 14 deletions src/test/mir-opt/end_region_5.rs
Expand Up @@ -31,32 +31,31 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {
// let mut _0: ();
// let _1: D;
// let mut _2: ();
// let mut _3: ();
// let mut _4: [closure@NodeId(18) d: &'19mce D];
// let mut _5: &'19mce D;
//
// let mut _3: [closure@NodeId(18) d:&'19mce D];
// let mut _4: &'19mce D;
// let mut _5: ();
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_3);
// StorageLive(_4);
// StorageLive(_5);
// _5 = &'19mce _1;
// _4 = [closure@NodeId(18)] { d: _5 };
// StorageDead(_5);
// _3 = const foo(_4) -> [return: bb2, unwind: bb3];
// _4 = &'19mce _1;
// _3 = [closure@NodeId(18)] { d: _4 };
// StorageDead(_4);
// _2 = const foo(_3) -> [return: bb1, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_4);
// StorageDead(_3);
// EndRegion('19mce);
// _0 = ();
// drop(_1) -> bb4;
// }
// bb2: {
// resume;
// }
// bb3: {
// EndRegion('19mce);
// drop(_1) -> bb1;
// drop(_1) -> bb2;
// }
// bb4: {
// StorageDead(_1);
Expand Down
28 changes: 14 additions & 14 deletions src/test/mir-opt/end_region_6.rs
Expand Up @@ -27,35 +27,35 @@ fn foo<F>(f: F) where F: FnOnce() -> i32 {

// END RUST SOURCE
// START rustc.node4.SimplifyCfg-qualify-consts.after.mir
// fn main() -> () {
// let mut _0: ();
// let _1: D;
// let mut _2: ();
// let mut _3: ();
// let mut _4: [closure@NodeId(22) d:&'23mce D];
// let mut _5: &'23mce D;
//
// let mut _3: [closure@NodeId(22) d:&'23mce D];
// let mut _4: &'23mce D;
// let mut _5: ();
// bb0: {
// StorageLive(_1);
// _1 = D::{{constructor}}(const 0i32,);
// StorageLive(_3);
// StorageLive(_4);
// StorageLive(_5);
// _5 = &'23mce _1;
// _4 = [closure@NodeId(22)] { d: _5 };
// StorageDead(_5);
// _3 = const foo(_4) -> [return: bb2, unwind: bb3];
// _4 = &'23mce _1;
// _3 = [closure@NodeId(22)] { d: _4 };
// StorageDead(_4);
// _2 = const foo(_3) -> [return: bb1, unwind: bb3];
// }
// bb1: {
// resume;
// }
// bb2: {
// StorageDead(_4);
// StorageDead(_3);
// EndRegion('23mce);
// _0 = ();
// drop(_1) -> bb4;
// }
// bb2: {
// resume;
// }
// bb3: {
// EndRegion('23mce);
// drop(_1) -> bb1;
// drop(_1) -> bb2;
// }
// bb4: {
// StorageDead(_1);
Expand Down

0 comments on commit 85c1027

Please sign in to comment.