pub struct CachedEarlyExit {
label: EarlyExitLabel,
cleanup_block: BasicBlockRef,
+ last_cleanup: usize,
}
pub trait Cleanup<'tcx> {
for scope in self.scopes.borrow_mut().iter_mut().rev() {
if scope.kind.is_ast_with_id(cleanup_scope) {
scope.cleanups.push(cleanup);
- scope.clear_cached_exits();
+ scope.cached_landing_pad = None;
return;
} else {
// will be adding a cleanup to some enclosing scope
let mut scopes = self.scopes.borrow_mut();
let scope = &mut (*scopes)[custom_scope.index];
scope.cleanups.push(cleanup);
- scope.clear_cached_exits();
+ scope.cached_landing_pad = None;
}
/// Returns true if there are pending cleanups that should execute on panic.
let orig_scopes_len = self.scopes_len();
let mut prev_llbb;
let mut popped_scopes = vec!();
+ let mut skip = 0;
// First we pop off all the cleanup stacks that are
// traversed until the exit is reached, pushing them
}
}
+ // Pop off the scope, since we may be generating
+ // unwinding code for it.
+ let top_scope = self.pop_scope();
+ let cached_exit = top_scope.cached_early_exit(label);
+ popped_scopes.push(top_scope);
+
// Check if we have already cached the unwinding of this
// scope for this label. If so, we can stop popping scopes
// and branch to the cached label, since it contains the
// cleanups for any subsequent scopes.
- if let Some(exit) = self.top_scope(|s| s.cached_early_exit(label)) {
+ if let Some((exit, last_cleanup)) = cached_exit {
prev_llbb = exit;
+ skip = last_cleanup;
break;
}
- // Pop off the scope, since we will be generating
- // unwinding code for it. If we are searching for a loop exit,
+ // If we are searching for a loop exit,
// and this scope is that loop, then stop popping and set
// `prev_llbb` to the appropriate exit block from the loop.
- popped_scopes.push(self.pop_scope());
let scope = popped_scopes.last().unwrap();
match label {
UnwindExit(..) | ReturnExit => { }
let bcx_in = self.new_block(&name[..], None);
let exit_label = label.start(bcx_in);
let mut bcx_out = bcx_in;
- for cleanup in scope.cleanups.iter().rev() {
+ let len = scope.cleanups.len();
+ for cleanup in scope.cleanups.iter().rev().take(len - skip) {
bcx_out = cleanup.trans(bcx_out, scope.debug_loc);
}
+ skip = 0;
exit_label.branch(bcx_out, prev_llbb);
prev_llbb = bcx_in.llbb;
- scope.add_cached_early_exit(exit_label, prev_llbb);
+ scope.add_cached_early_exit(exit_label, prev_llbb, len);
}
self.push_scope(scope);
}
fn cached_early_exit(&self,
label: EarlyExitLabel)
- -> Option<BasicBlockRef> {
- self.cached_early_exits.iter().
+ -> Option<(BasicBlockRef, usize)> {
+ self.cached_early_exits.iter().rev().
find(|e| e.label == label).
- map(|e| e.cleanup_block)
+ map(|e| (e.cleanup_block, e.last_cleanup))
}
fn add_cached_early_exit(&mut self,
label: EarlyExitLabel,
- blk: BasicBlockRef) {
+ blk: BasicBlockRef,
+ last_cleanup: usize) {
self.cached_early_exits.push(
CachedEarlyExit { label: label,
- cleanup_block: blk });
+ cleanup_block: blk,
+ last_cleanup: last_cleanup});
}
/// True if this scope has cleanups that need unwinding
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes
+
+#![crate_type = "lib"]
+
+struct SomeUniqueName;
+
+impl Drop for SomeUniqueName {
+ fn drop(&mut self) {
+ }
+}
+
+pub fn possibly_unwinding() {
+}
+
+// CHECK-LABEL: @droppy
+#[no_mangle]
+pub fn droppy() {
+// Check that there are exactly 6 drop calls. The cleanups for the unwinding should be reused, so
+// that's one new drop call per call to possibly_unwinding(), and finally 3 drop calls for the
+// regular function exit. We used to have problems with quadratic growths of drop calls in such
+// functions.
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK: call{{.*}}SomeUniqueName{{.*}}drop
+// CHECK-NOT: call{{.*}}SomeUniqueName{{.*}}drop
+// The next line checks for the } that ends the function definition
+// CHECK-LABEL: {{^[}]}}
+ let _s = SomeUniqueName;
+ possibly_unwinding();
+ let _s = SomeUniqueName;
+ possibly_unwinding();
+ let _s = SomeUniqueName;
+ possibly_unwinding();
+}