Skip to content

Commit 53f8a84

Browse files
committed
schedule drops on bindings only after initializing them
This reduces the number of dynamic drops in libstd from 1141 to 899. However, without this change, the next patch would have created much more dynamic drops. A basic merge unswitching hack reduced the number of dynamic drops to 644, with no effect on stack usage. I should be writing a more dedicated drop unswitching pass. No performance measurements.
1 parent 1572bf1 commit 53f8a84

File tree

5 files changed

+98
-72
lines changed

5 files changed

+98
-72
lines changed

src/librustc_borrowck/borrowck/mir/elaborate_drops.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
161161
fn create_drop_flag(&mut self, index: MovePathIndex) {
162162
let tcx = self.tcx;
163163
let patch = &mut self.patch;
164+
debug!("create_drop_flag({:?})", self.mir.span);
164165
self.drop_flags.entry(index).or_insert_with(|| {
165166
patch.new_temp(tcx.types.bool)
166167
});

src/librustc_mir/build/block.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6767
this.expr_into_pattern(block, pattern, init)
6868
}));
6969
} else {
70-
this.storage_live_for_bindings(block, &pattern);
70+
this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| {
71+
this.storage_live_binding(block, node, span);
72+
this.schedule_drop_for_binding(node, span);
73+
})
7174
}
7275

7376
// Enter the visibility scope, after evaluating the initializer.

src/librustc_mir/build/matches/mod.rs

Lines changed: 50 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
123123
PatternKind::Binding { mode: BindingMode::ByValue,
124124
var,
125125
subpattern: None, .. } => {
126-
self.storage_live_for_bindings(block, &irrefutable_pat);
127-
let lvalue = Lvalue::Local(self.var_indices[&var]);
128-
return self.into(&lvalue, block, initializer);
126+
let lvalue = self.storage_live_binding(block, var, irrefutable_pat.span);
127+
unpack!(block = self.into(&lvalue, block, initializer));
128+
self.schedule_drop_for_binding(var, irrefutable_pat.span);
129+
block.unit()
130+
}
131+
_ => {
132+
let lvalue = unpack!(block = self.as_lvalue(block, initializer));
133+
self.lvalue_into_pattern(block, irrefutable_pat, &lvalue)
129134
}
130-
_ => {}
131135
}
132-
let lvalue = unpack!(block = self.as_lvalue(block, initializer));
133-
self.lvalue_into_pattern(block,
134-
irrefutable_pat,
135-
&lvalue)
136136
}
137137

138138
pub fn lvalue_into_pattern(&mut self,
@@ -174,79 +174,70 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
174174
scope_span: Span,
175175
pattern: &Pattern<'tcx>)
176176
-> Option<VisibilityScope> {
177-
match *pattern.kind {
178-
PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => {
179-
if var_scope.is_none() {
180-
var_scope = Some(self.new_visibility_scope(scope_span));
181-
}
182-
let source_info = SourceInfo {
183-
span: pattern.span,
184-
scope: var_scope.unwrap()
185-
};
186-
self.declare_binding(source_info, mutability, name, var, ty);
187-
if let Some(subpattern) = subpattern.as_ref() {
188-
var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
189-
}
190-
}
191-
PatternKind::Array { ref prefix, ref slice, ref suffix } |
192-
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
193-
for subpattern in prefix.iter().chain(slice).chain(suffix) {
194-
var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
195-
}
196-
}
197-
PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
198-
}
199-
PatternKind::Deref { ref subpattern } => {
200-
var_scope = self.declare_bindings(var_scope, scope_span, subpattern);
201-
}
202-
PatternKind::Leaf { ref subpatterns } |
203-
PatternKind::Variant { ref subpatterns, .. } => {
204-
for subpattern in subpatterns {
205-
var_scope = self.declare_bindings(var_scope, scope_span, &subpattern.pattern);
206-
}
177+
self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| {
178+
if var_scope.is_none() {
179+
var_scope = Some(this.new_visibility_scope(scope_span));
207180
}
208-
}
181+
let source_info = SourceInfo {
182+
span: span,
183+
scope: var_scope.unwrap()
184+
};
185+
this.declare_binding(source_info, mutability, name, var, ty);
186+
});
209187
var_scope
210188
}
211189

212-
/// Emit `StorageLive` for every binding in the pattern.
213-
pub fn storage_live_for_bindings(&mut self,
214-
block: BasicBlock,
215-
pattern: &Pattern<'tcx>) {
216-
match *pattern.kind {
217-
PatternKind::Binding { var, ref subpattern, .. } => {
218-
let lvalue = Lvalue::Local(self.var_indices[&var]);
219-
let source_info = self.source_info(pattern.span);
220-
self.cfg.push(block, Statement {
221-
source_info: source_info,
222-
kind: StatementKind::StorageLive(lvalue)
223-
});
190+
pub fn storage_live_binding(&mut self, block: BasicBlock, var: NodeId, span: Span)
191+
-> Lvalue<'tcx>
192+
{
193+
let local_id = self.var_indices[&var];
194+
let source_info = self.source_info(span);
195+
self.cfg.push(block, Statement {
196+
source_info: source_info,
197+
kind: StatementKind::StorageLive(Lvalue::Local(local_id))
198+
});
199+
Lvalue::Local(local_id)
200+
}
224201

202+
pub fn schedule_drop_for_binding(&mut self, var: NodeId, span: Span) {
203+
let local_id = self.var_indices[&var];
204+
let var_ty = self.local_decls[local_id].ty;
205+
let extent = self.hir.tcx().region_maps.var_scope(var);
206+
self.schedule_drop(span, extent, &Lvalue::Local(local_id), var_ty);
207+
}
208+
209+
pub fn visit_bindings<F>(&mut self, pattern: &Pattern<'tcx>, mut f: &mut F)
210+
where F: FnMut(&mut Self, Mutability, Name, NodeId, Span, Ty<'tcx>)
211+
{
212+
match *pattern.kind {
213+
PatternKind::Binding { mutability, name, var, ty, ref subpattern, .. } => {
214+
f(self, mutability, name, var, pattern.span, ty);
225215
if let Some(subpattern) = subpattern.as_ref() {
226-
self.storage_live_for_bindings(block, subpattern);
216+
self.visit_bindings(subpattern, f);
227217
}
228218
}
229219
PatternKind::Array { ref prefix, ref slice, ref suffix } |
230220
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
231221
for subpattern in prefix.iter().chain(slice).chain(suffix) {
232-
self.storage_live_for_bindings(block, subpattern);
222+
self.visit_bindings(subpattern, f);
233223
}
234224
}
235225
PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
236226
}
237227
PatternKind::Deref { ref subpattern } => {
238-
self.storage_live_for_bindings(block, subpattern);
228+
self.visit_bindings(subpattern, f);
239229
}
240230
PatternKind::Leaf { ref subpatterns } |
241231
PatternKind::Variant { ref subpatterns, .. } => {
242232
for subpattern in subpatterns {
243-
self.storage_live_for_bindings(block, &subpattern.pattern);
233+
self.visit_bindings(&subpattern.pattern, f);
244234
}
245235
}
246236
}
247237
}
248238
}
249239

240+
250241
/// List of blocks for each arm (and potentially other metadata in the
251242
/// future).
252243
struct ArmBlocks {
@@ -691,25 +682,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
691682

692683
// Assign each of the bindings. This may trigger moves out of the candidate.
693684
for binding in bindings {
694-
// Find the variable for the `var_id` being bound. It
695-
// should have been created by a previous call to
696-
// `declare_bindings`.
697-
let var_index = self.var_indices[&binding.var_id];
698-
685+
let source_info = self.source_info(binding.span);
686+
let local = self.storage_live_binding(block, binding.var_id, binding.span);
687+
self.schedule_drop_for_binding(binding.var_id, binding.span);
699688
let rvalue = match binding.binding_mode {
700689
BindingMode::ByValue =>
701690
Rvalue::Use(Operand::Consume(binding.source)),
702691
BindingMode::ByRef(region, borrow_kind) =>
703692
Rvalue::Ref(region, borrow_kind, binding.source),
704693
};
705-
706-
let source_info = self.source_info(binding.span);
707-
self.cfg.push(block, Statement {
708-
source_info: source_info,
709-
kind: StatementKind::StorageLive(Lvalue::Local(var_index))
710-
});
711-
self.cfg.push_assign(block, source_info,
712-
&Lvalue::Local(var_index), rvalue);
694+
self.cfg.push_assign(block, source_info, &local, rvalue);
713695
}
714696
}
715697

@@ -730,8 +712,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
730712
name: Some(name),
731713
source_info: Some(source_info),
732714
});
733-
let extent = self.hir.tcx().region_maps.var_scope(var_id);
734-
self.schedule_drop(source_info.span, extent, &Lvalue::Local(var), var_ty);
735715
self.var_indices.insert(var_id, var);
736716

737717
debug!("declare_binding: var={:?}", var);

src/test/mir-opt/storage_ranges.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ fn main() {
3131
// _3 = &_4;
3232
// StorageDead(_5);
3333
// _2 = ();
34-
// StorageDead(_4);
3534
// StorageDead(_3);
35+
// StorageDead(_4);
3636
// StorageLive(_6);
3737
// _6 = const 1i32;
3838
// _0 = ();

src/test/run-pass/mir_drop_order.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::cell::RefCell;
12+
13+
pub struct DropLogger<'a> {
14+
id: usize,
15+
log: &'a RefCell<Vec<usize>>
16+
}
17+
18+
impl<'a> Drop for DropLogger<'a> {
19+
fn drop(&mut self) {
20+
self.log.borrow_mut().push(self.id);
21+
}
22+
}
23+
24+
fn main() {
25+
let log = RefCell::new(vec![]);
26+
let d = |id| DropLogger { id: id, log: &log };
27+
let get = || -> Vec<_> {
28+
let mut m = log.borrow_mut();
29+
let n = m.drain(..);
30+
n.collect()
31+
};
32+
33+
{
34+
let _x = (d(0), &d(1), d(2), &d(3));
35+
// all borrows are extended - nothing has been dropped yet
36+
assert_eq!(get(), vec![]);
37+
}
38+
// in a let-statement, extended lvalues are dropped
39+
// *after* the let result (tho they have the same scope
40+
// as far as scope-based borrowck goes).
41+
assert_eq!(get(), vec![0, 2, 3, 1]);
42+
}

0 commit comments

Comments
 (0)