Skip to content

Commit 640cfc8

Browse files
committed
Auto merge of #43576 - arielb1:no-unneeded-unwind, r=eddyb
rustc_mir: don't build unused unwind cleanup blocks When building a scope exit, don't build unwind cleanup blocks unless they will actually be used by the unwind path of a drop - the unused blocks are removed by SimplifyCfg, but they can cause a significant performance slowdown before they are removed. That fixes #43511. Also a few other small MIR cleanups & optimizations. r? @eddyb
2 parents dd53dd5 + ce0ca76 commit 640cfc8

File tree

13 files changed

+210
-178
lines changed

13 files changed

+210
-178
lines changed

src/librustc/mir/visit.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use ty::subst::Substs;
1414
use ty::{ClosureSubsts, Region, Ty};
1515
use mir::*;
1616
use rustc_const_math::ConstUsize;
17-
use rustc_data_structures::indexed_vec::Idx;
1817
use syntax_pos::Span;
1918

2019
// # The MIR Visitor
@@ -260,9 +259,15 @@ macro_rules! make_mir_visitor {
260259

261260
fn super_mir(&mut self,
262261
mir: & $($mutability)* Mir<'tcx>) {
263-
for index in 0..mir.basic_blocks().len() {
264-
let block = BasicBlock::new(index);
265-
self.visit_basic_block_data(block, &$($mutability)* mir[block]);
262+
// for best performance, we want to use an iterator rather
263+
// than a for-loop, to avoid calling Mir::invalidate for
264+
// each basic block.
265+
macro_rules! basic_blocks {
266+
(mut) => (mir.basic_blocks_mut().iter_enumerated_mut());
267+
() => (mir.basic_blocks().iter_enumerated());
268+
};
269+
for (bb, data) in basic_blocks!($($mutability)*) {
270+
self.visit_basic_block_data(bb, data);
266271
}
267272

268273
for scope in &$($mutability)* mir.visibility_scopes {

src/librustc_mir/build/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8686
let tcx = this.hir.tcx();
8787

8888
// Enter the remainder scope, i.e. the bindings' destruction scope.
89-
this.push_scope(remainder_scope);
89+
this.push_scope((remainder_scope, source_info));
9090
let_extent_stack.push(remainder_scope);
9191

9292
// Declare the bindings, which may create a visibility scope.

src/librustc_mir/build/expr/into.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
237237
.collect();
238238

239239
let success = this.cfg.start_new_block();
240-
let cleanup = this.diverge_cleanup(expr_span);
240+
let cleanup = this.diverge_cleanup();
241241
this.cfg.terminate(block, source_info, TerminatorKind::Call {
242242
func: fun,
243243
args: args,

src/librustc_mir/build/matches/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
306306
let bool_ty = self.hir.bool_ty();
307307
let eq_result = self.temp(bool_ty, test.span);
308308
let eq_block = self.cfg.start_new_block();
309-
let cleanup = self.diverge_cleanup(test.span);
309+
let cleanup = self.diverge_cleanup();
310310
self.cfg.terminate(block, source_info, TerminatorKind::Call {
311311
func: Operand::Constant(box Constant {
312312
span: test.span,

src/librustc_mir/build/scope.rs

+72-42
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ pub struct Scope<'tcx> {
107107
/// the extent of this scope within source code.
108108
extent: CodeExtent,
109109

110+
/// the span of that extent
111+
extent_span: Span,
112+
110113
/// Whether there's anything to do for the cleanup path, that is,
111114
/// when unwinding through this scope. This includes destructors,
112115
/// but not StorageDead statements, which don't get emitted at all
@@ -116,7 +119,7 @@ pub struct Scope<'tcx> {
116119
/// * pollutting the cleanup MIR with StorageDead creates
117120
/// landing pads even though there's no actual destructors
118121
/// * freeing up stack space has no effect during unwinding
119-
pub(super) needs_cleanup: bool,
122+
needs_cleanup: bool,
120123

121124
/// set of lvalues to drop when exiting this scope. This starts
122125
/// out empty but grows as variables are declared during the
@@ -197,6 +200,15 @@ pub struct BreakableScope<'tcx> {
197200
pub break_destination: Lvalue<'tcx>,
198201
}
199202

203+
impl DropKind {
204+
fn may_panic(&self) -> bool {
205+
match *self {
206+
DropKind::Value { .. } => true,
207+
DropKind::Storage => false
208+
}
209+
}
210+
}
211+
200212
impl<'tcx> Scope<'tcx> {
201213
/// Invalidate all the cached blocks in the scope.
202214
///
@@ -282,7 +294,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
282294
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
283295
{
284296
debug!("in_opt_scope(opt_extent={:?}, block={:?})", opt_extent, block);
285-
if let Some(extent) = opt_extent { self.push_scope(extent.0); }
297+
if let Some(extent) = opt_extent { self.push_scope(extent); }
286298
let rv = unpack!(block = f(self));
287299
if let Some(extent) = opt_extent {
288300
unpack!(block = self.pop_scope(extent, block));
@@ -301,7 +313,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
301313
where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) -> BlockAnd<R>
302314
{
303315
debug!("in_scope(extent={:?}, block={:?})", extent, block);
304-
self.push_scope(extent.0);
316+
self.push_scope(extent);
305317
let rv = unpack!(block = f(self));
306318
unpack!(block = self.pop_scope(extent, block));
307319
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
@@ -312,12 +324,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
312324
/// scope and call `pop_scope` afterwards. Note that these two
313325
/// calls must be paired; using `in_scope` as a convenience
314326
/// wrapper maybe preferable.
315-
pub fn push_scope(&mut self, extent: CodeExtent) {
327+
pub fn push_scope(&mut self, extent: (CodeExtent, SourceInfo)) {
316328
debug!("push_scope({:?})", extent);
317329
let vis_scope = self.visibility_scope;
318330
self.scopes.push(Scope {
319331
visibility_scope: vis_scope,
320-
extent: extent,
332+
extent: extent.0,
333+
extent_span: extent.1.span,
321334
needs_cleanup: false,
322335
drops: vec![],
323336
free: None,
@@ -333,9 +346,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
333346
mut block: BasicBlock)
334347
-> BlockAnd<()> {
335348
debug!("pop_scope({:?}, {:?})", extent, block);
336-
// We need to have `cached_block`s available for all the drops, so we call diverge_cleanup
337-
// to make sure all the `cached_block`s are filled in.
338-
self.diverge_cleanup(extent.1.span);
349+
// If we are emitting a `drop` statement, we need to have the cached
350+
// diverge cleanup pads ready in case that drop panics.
351+
let may_panic =
352+
self.scopes.last().unwrap().drops.iter().any(|s| s.kind.may_panic());
353+
if may_panic {
354+
self.diverge_cleanup();
355+
}
339356
let scope = self.scopes.pop().unwrap();
340357
assert_eq!(scope.extent, extent.0);
341358
unpack!(block = build_scope_drops(&mut self.cfg,
@@ -366,6 +383,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
366383
let len = self.scopes.len();
367384
assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes");
368385
let tmp = self.get_unit_temp();
386+
387+
// If we are emitting a `drop` statement, we need to have the cached
388+
// diverge cleanup pads ready in case that drop panics.
389+
let may_panic = self.scopes[(len - scope_count)..].iter()
390+
.any(|s| s.drops.iter().any(|s| s.kind.may_panic()));
391+
if may_panic {
392+
self.diverge_cleanup();
393+
}
394+
369395
{
370396
let mut rest = &mut self.scopes[(len - scope_count)..];
371397
while let Some((scope, rest_)) = {rest}.split_last_mut() {
@@ -618,7 +644,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
618644
/// This path terminates in Resume. Returns the start of the path.
619645
/// See module comment for more details. None indicates there’s no
620646
/// cleanup to do at this point.
621-
pub fn diverge_cleanup(&mut self, span: Span) -> Option<BasicBlock> {
647+
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
622648
if !self.scopes.iter().any(|scope| scope.needs_cleanup) {
623649
return None;
624650
}
@@ -652,7 +678,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
652678
};
653679

654680
for scope in scopes.iter_mut() {
655-
target = build_diverge_scope(hir.tcx(), cfg, &unit_temp, span, scope, target);
681+
target = build_diverge_scope(
682+
hir.tcx(), cfg, &unit_temp, scope.extent_span, scope, target);
656683
}
657684
Some(target)
658685
}
@@ -668,7 +695,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
668695
}
669696
let source_info = self.source_info(span);
670697
let next_target = self.cfg.start_new_block();
671-
let diverge_target = self.diverge_cleanup(span);
698+
let diverge_target = self.diverge_cleanup();
672699
self.cfg.terminate(block, source_info,
673700
TerminatorKind::Drop {
674701
location: location,
@@ -686,7 +713,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
686713
value: Operand<'tcx>) -> BlockAnd<()> {
687714
let source_info = self.source_info(span);
688715
let next_target = self.cfg.start_new_block();
689-
let diverge_target = self.diverge_cleanup(span);
716+
let diverge_target = self.diverge_cleanup();
690717
self.cfg.terminate(block, source_info,
691718
TerminatorKind::DropAndReplace {
692719
location: location,
@@ -709,7 +736,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
709736
let source_info = self.source_info(span);
710737

711738
let success_block = self.cfg.start_new_block();
712-
let cleanup = self.diverge_cleanup(span);
739+
let cleanup = self.diverge_cleanup();
713740

714741
self.cfg.terminate(block, source_info,
715742
TerminatorKind::Assert {
@@ -731,45 +758,48 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
731758
mut block: BasicBlock,
732759
arg_count: usize)
733760
-> BlockAnd<()> {
761+
debug!("build_scope_drops({:?} -> {:?})", block, scope);
734762
let mut iter = scope.drops.iter().rev().peekable();
735763
while let Some(drop_data) = iter.next() {
736764
let source_info = scope.source_info(drop_data.span);
737-
if let DropKind::Value { .. } = drop_data.kind {
738-
// Try to find the next block with its cached block
739-
// for us to diverge into in case the drop panics.
740-
let on_diverge = iter.peek().iter().filter_map(|dd| {
741-
match dd.kind {
742-
DropKind::Value { cached_block } => cached_block,
743-
DropKind::Storage => None
744-
}
745-
}).next();
746-
// If there’s no `cached_block`s within current scope,
747-
// we must look for one in the enclosing scope.
748-
let on_diverge = on_diverge.or_else(||{
749-
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
750-
});
751-
let next = cfg.start_new_block();
752-
cfg.terminate(block, source_info, TerminatorKind::Drop {
753-
location: drop_data.location.clone(),
754-
target: next,
755-
unwind: on_diverge
756-
});
757-
block = next;
758-
}
759765
match drop_data.kind {
760-
DropKind::Value { .. } |
761-
DropKind::Storage => {
762-
// Only temps and vars need their storage dead.
763-
match drop_data.location {
764-
Lvalue::Local(index) if index.index() > arg_count => {}
765-
_ => continue
766-
}
766+
DropKind::Value { .. } => {
767+
// Try to find the next block with its cached block
768+
// for us to diverge into in case the drop panics.
769+
let on_diverge = iter.peek().iter().filter_map(|dd| {
770+
match dd.kind {
771+
DropKind::Value { cached_block: None } =>
772+
span_bug!(drop_data.span, "cached block not present?"),
773+
DropKind::Value { cached_block } => cached_block,
774+
DropKind::Storage => None
775+
}
776+
}).next();
777+
// If there’s no `cached_block`s within current scope,
778+
// we must look for one in the enclosing scope.
779+
let on_diverge = on_diverge.or_else(|| {
780+
earlier_scopes.iter().rev().flat_map(|s| s.cached_block()).next()
781+
});
782+
let next = cfg.start_new_block();
783+
cfg.terminate(block, source_info, TerminatorKind::Drop {
784+
location: drop_data.location.clone(),
785+
target: next,
786+
unwind: on_diverge
787+
});
788+
block = next;
789+
}
790+
DropKind::Storage => {}
791+
}
767792

793+
// Drop the storage for both value and storage drops.
794+
// Only temps and vars need their storage dead.
795+
match drop_data.location {
796+
Lvalue::Local(index) if index.index() > arg_count => {
768797
cfg.push(block, Statement {
769798
source_info: source_info,
770799
kind: StatementKind::StorageDead(drop_data.location.clone())
771800
});
772801
}
802+
_ => continue
773803
}
774804
}
775805
block.unit()

src/librustc_mir/transform/simplify.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
105105
}
106106

107107
pub fn simplify(mut self) {
108+
self.strip_nops();
109+
108110
loop {
109111
let mut changed = false;
110112

@@ -141,8 +143,6 @@ impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
141143

142144
if !changed { break }
143145
}
144-
145-
self.strip_nops()
146146
}
147147

148148
// Collapse a goto chain starting from `start`

src/test/mir-opt/basic_assignment.rs

+13-19
Original file line numberDiff line numberDiff line change
@@ -47,42 +47,36 @@ fn main() {
4747
// StorageDead(_3);
4848
// StorageLive(_4);
4949
// _4 = std::option::Option<std::boxed::Box<u32>>::None;
50+
// StorageLive(_5);
5051
// StorageLive(_6);
51-
// StorageLive(_7);
52-
// _7 = _4;
53-
// replace(_6 <- _7) -> [return: bb6, unwind: bb7];
52+
// _6 = _4;
53+
// replace(_5 <- _6) -> [return: bb1, unwind: bb5];
5454
// }
5555
// bb1: {
56-
// resume;
56+
// drop(_6) -> [return: bb6, unwind: bb4];
5757
// }
5858
// bb2: {
59-
// drop(_4) -> bb1;
59+
// resume;
6060
// }
6161
// bb3: {
62-
// goto -> bb2;
62+
// drop(_4) -> bb2;
6363
// }
6464
// bb4: {
65-
// drop(_6) -> bb3;
65+
// drop(_5) -> bb3;
6666
// }
6767
// bb5: {
68-
// goto -> bb4;
68+
// drop(_6) -> bb4;
6969
// }
7070
// bb6: {
71-
// drop(_7) -> [return: bb8, unwind: bb4];
71+
// StorageDead(_6);
72+
// _0 = ();
73+
// drop(_5) -> [return: bb7, unwind: bb3];
7274
// }
7375
// bb7: {
74-
// drop(_7) -> bb5;
76+
// StorageDead(_5);
77+
// drop(_4) -> bb8;
7578
// }
7679
// bb8: {
77-
// StorageDead(_7);
78-
// _0 = ();
79-
// drop(_6) -> [return: bb9, unwind: bb2];
80-
// }
81-
// bb9: {
82-
// StorageDead(_6);
83-
// drop(_4) -> bb10;
84-
// }
85-
// bb10: {
8680
// StorageDead(_4);
8781
// StorageDead(_2);
8882
// StorageDead(_1);

0 commit comments

Comments
 (0)