提交 5bac0c39 编写于 作者: M Manish Goregaokar 提交者: GitHub

Rollup merge of #34307 - nagisa:more-cache, r=arielb1

[MIR] Cache drops for early scope exits

Previously we would rebuild all drops on every early exit from a scope, which for code like:

```rust
match x {
    A => return 1,
    B => return 2,
    ...
    C => return 27
}
```

would produce 27 exactly same chains of drops for each return, basically a `O(n*m)` explosion. [This](https://cloud.githubusercontent.com/assets/679122/16125192/3355e32c-33fb-11e6-8564-c37cab2477a0.png) is such a case for a match on 80-variant enum with 3 droppable variables in scope.

For [`::core::iter::Iterator::partial_cmp`](https://github.com/rust-lang/rust/blob/6edea2cfda2818f0a76f4bac2d18a30feb54c137/src/libcore/iter/iterator.rs#L1909) the CFG looked like [this](https://cloud.githubusercontent.com/assets/679122/16122708/ce0024d8-33f0-11e6-93c2-e1c44b910db2.png) (after initial SimplifyCfg). With this patch the CFG looks like [this](https://cloud.githubusercontent.com/assets/679122/16122806/294fb16e-33f1-11e6-95f6-16c5438231af.png) instead.

Some numbers (overall very small wins, however neither of the crates have many cases which abuse this corner case):

|                         | old time | old rss | new time | new rss  |
|-------------------------|----------|---------|----------|----------|
| core dump               | 0.879        |   224MB     |   0.871  |  223MB   |
| core MIR passes         | 0.759        | 224MB       | 0.718    | 223MB    |
| core MIR codegen passes | 1.762        | 230MB       | 1.442    | 228MB    |
| core trans              | 3.263        | 279MB       | 3.116    | 278MB    |
| core llvm passes        | 5.611        | 263MB       | 5.565    | 263MB    |
| std dump                | 0.487        |   190MB     |   0.475  |  192MB   |
| std MIR passes          | 0.311       | 190MB       | 0.288    | 192MB    |
| std MIR codegen passes  | 0.753        | 195MB       | 0.720    | 197MB    |
| std trans               | 2.589        | 287MB       | 2.523    | 287MB    |
| std llvm passes         | 7.268        | 245MB       | 7.447    | 246MB    |
......@@ -94,6 +94,7 @@
use rustc::mir::repr::*;
use syntax::codemap::Span;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::fnv::FnvHashMap;
pub struct Scope<'tcx> {
/// the scope-id within the scope_auxiliary
......@@ -127,12 +128,8 @@ pub struct Scope<'tcx> {
/// stage.
free: Option<FreeData<'tcx>>,
/// The cached block for the cleanups-on-diverge path. This block
/// contains a block that will just do a RESUME to an appropriate
/// place. This block does not execute any of the drops or free:
/// each of those has their own cached-blocks, which will branch
/// to this point.
cached_block: Option<BasicBlock>
/// The cache for drop chain on “normal” exit into a particular BasicBlock.
cached_exits: FnvHashMap<(BasicBlock, CodeExtent), BasicBlock>,
}
struct DropData<'tcx> {
......@@ -172,7 +169,7 @@ pub struct LoopScope {
pub continue_block: BasicBlock,
/// Block to branch into when the loop terminates (either by being `break`-en out from, or by
/// having its condition to become false)
pub break_block: BasicBlock, // where to go on a `break
pub break_block: BasicBlock,
/// Indicates the reachability of the break_block for this loop
pub might_break: bool
}
......@@ -183,7 +180,7 @@ impl<'tcx> Scope<'tcx> {
/// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
/// larger extent of code.
fn invalidate_cache(&mut self) {
self.cached_block = None;
self.cached_exits = FnvHashMap();
for dropdata in &mut self.drops {
dropdata.cached_block = None;
}
......@@ -192,7 +189,7 @@ fn invalidate_cache(&mut self) {
}
}
/// Returns the cached block for this scope.
/// Returns the cached entrypoint for diverging exit from this scope.
///
/// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
/// this method to work correctly.
......@@ -270,7 +267,7 @@ pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) {
extent: extent,
drops: vec![],
free: None,
cached_block: None,
cached_exits: FnvHashMap()
});
self.scope_auxiliary.push(ScopeAuxiliary {
extent: extent,
......@@ -314,13 +311,25 @@ pub fn exit_scope(&mut self,
.unwrap_or_else(||{
span_bug!(span, "extent {:?} does not enclose", extent)
});
let len = self.scopes.len();
assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
let tmp = self.get_unit_temp();
for (idx, ref scope) in self.scopes.iter().enumerate().rev().take(scope_count) {
unpack!(block = build_scope_drops(&mut self.cfg,
scope,
&self.scopes[..idx],
block));
{
let mut rest = &mut self.scopes[(len - scope_count)..];
while let Some((scope, rest_)) = {rest}.split_last_mut() {
rest = rest_;
block = if let Some(&e) = scope.cached_exits.get(&(target, extent)) {
self.cfg.terminate(block, scope.source_info(span),
TerminatorKind::Goto { target: e });
return;
} else {
let b = self.cfg.start_new_block();
self.cfg.terminate(block, scope.source_info(span),
TerminatorKind::Goto { target: b });
scope.cached_exits.insert((target, extent), b);
b
};
unpack!(block = build_scope_drops(&mut self.cfg, scope, rest, block));
if let Some(ref free_data) = scope.free {
let next = self.cfg.start_new_block();
let free = build_free(self.hir.tcx(), &tmp, free_data, next);
......@@ -331,14 +340,9 @@ pub fn exit_scope(&mut self,
.postdoms
.push(self.cfg.current_location(block));
}
assert!(scope_count < self.scopes.len(),
"should never use `exit_scope` to pop *ALL* scopes");
let scope = self.scopes.iter().rev().skip(scope_count)
.next()
.unwrap();
self.cfg.terminate(block,
scope.source_info(span),
}
let scope = &self.scopes[len - scope_count];
self.cfg.terminate(block, scope.source_info(span),
TerminatorKind::Goto { target: target });
}
......@@ -506,10 +510,9 @@ pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
resumeblk
};
for scope in scopes {
for scope in scopes.iter_mut().filter(|s| !s.drops.is_empty() || s.free.is_some()) {
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, scope, target);
}
Some(target)
}
......@@ -534,7 +537,7 @@ pub fn build_drop(&mut self,
next_target.unit()
}
/// Utility function for *non*-scope code to build their own drops
pub fn build_drop_and_replace(&mut self,
block: BasicBlock,
span: Span,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册