Skip to content

Commit 666e714

Browse files
committed
Auto merge of #41148 - arielb1:dead-unwind, r=nagisa
borrowck::mir::dataflow: ignore unwind edges of empty drops This avoids creating drop flags in many unnecessary situations. Fixes #41110. r? @nagisa beta-nominating because regression. However, that is merely a small perf regression and codegen changes are always risky, so we might let this slide for 1.17.
2 parents a610117 + 6979798 commit 666e714

File tree

4 files changed

+174
-38
lines changed

4 files changed

+174
-38
lines changed

src/librustc_borrowck/borrowck/mir/dataflow/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
181181
where O: BitDenotation
182182
{
183183
flow_state: DataflowState<O>,
184+
dead_unwinds: &'a IdxSet<mir::BasicBlock>,
184185
mir: &'a Mir<'tcx>,
185186
}
186187

@@ -377,6 +378,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
377378
{
378379
pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>,
379380
mir: &'a Mir<'tcx>,
381+
dead_unwinds: &'a IdxSet<mir::BasicBlock>,
380382
denotation: D) -> Self {
381383
let bits_per_block = denotation.bits_per_block();
382384
let usize_bits = mem::size_of::<usize>() * 8;
@@ -397,6 +399,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
397399

398400
DataflowAnalysis {
399401
mir: mir,
402+
dead_unwinds: dead_unwinds,
400403
flow_state: DataflowState {
401404
sets: AllSets {
402405
bits_per_block: bits_per_block,
@@ -452,7 +455,9 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
452455
ref target, value: _, location: _, unwind: Some(ref unwind)
453456
} => {
454457
self.propagate_bits_into_entry_set_for(in_out, changed, target);
455-
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
458+
if !self.dead_unwinds.contains(&bb) {
459+
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
460+
}
456461
}
457462
mir::TerminatorKind::SwitchInt { ref targets, .. } => {
458463
for target in targets {
@@ -461,7 +466,9 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
461466
}
462467
mir::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => {
463468
if let Some(ref unwind) = *cleanup {
464-
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
469+
if !self.dead_unwinds.contains(&bb) {
470+
self.propagate_bits_into_entry_set_for(in_out, changed, unwind);
471+
}
465472
}
466473
if let Some((ref dest_lval, ref dest_bb)) = *destination {
467474
// N.B.: This must be done *last*, after all other

src/librustc_borrowck/borrowck/mir/elaborate_drops.rs

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
use super::gather_moves::{HasMoveData, MoveData, MovePathIndex, LookupResult};
1212
use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
1313
use super::dataflow::{DataflowResults};
14-
use super::{drop_flag_effects_for_location, on_all_children_bits};
15-
use super::on_lookup_result_bits;
14+
use super::{on_all_children_bits, on_all_drop_children_bits};
15+
use super::{drop_flag_effects_for_location, on_lookup_result_bits};
1616
use super::MoveDataParamEnv;
1717
use rustc::ty::{self, TyCtxt};
1818
use rustc::mir::*;
@@ -24,6 +24,7 @@ use rustc_data_structures::indexed_vec::Idx;
2424
use rustc_mir::util::patch::MirPatch;
2525
use rustc_mir::util::elaborate_drops::{DropFlagState, elaborate_drop};
2626
use rustc_mir::util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode};
27+
use syntax::ast;
2728
use syntax_pos::Span;
2829

2930
use std::fmt;
@@ -49,12 +50,13 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
4950
move_data: move_data,
5051
param_env: param_env
5152
};
53+
let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env);
5254
let flow_inits =
53-
super::do_dataflow(tcx, mir, id, &[],
55+
super::do_dataflow(tcx, mir, id, &[], &dead_unwinds,
5456
MaybeInitializedLvals::new(tcx, mir, &env),
5557
|bd, p| &bd.move_data().move_paths[p]);
5658
let flow_uninits =
57-
super::do_dataflow(tcx, mir, id, &[],
59+
super::do_dataflow(tcx, mir, id, &[], &dead_unwinds,
5860
MaybeUninitializedLvals::new(tcx, mir, &env),
5961
|bd, p| &bd.move_data().move_paths[p]);
6062

@@ -74,6 +76,67 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
7476

7577
impl Pass for ElaborateDrops {}
7678

79+
/// Return the set of basic blocks whose unwind edges are known
80+
/// to not be reachable, because they are `drop` terminators
81+
/// that can't drop anything.
82+
fn find_dead_unwinds<'a, 'tcx>(
83+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
84+
mir: &Mir<'tcx>,
85+
id: ast::NodeId,
86+
env: &MoveDataParamEnv<'tcx>)
87+
-> IdxSetBuf<BasicBlock>
88+
{
89+
debug!("find_dead_unwinds({:?})", mir.span);
90+
// We only need to do this pass once, because unwind edges can only
91+
// reach cleanup blocks, which can't have unwind edges themselves.
92+
let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
93+
let flow_inits =
94+
super::do_dataflow(tcx, mir, id, &[], &dead_unwinds,
95+
MaybeInitializedLvals::new(tcx, mir, &env),
96+
|bd, p| &bd.move_data().move_paths[p]);
97+
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
98+
match bb_data.terminator().kind {
99+
TerminatorKind::Drop { ref location, unwind: Some(_), .. } |
100+
TerminatorKind::DropAndReplace { ref location, unwind: Some(_), .. } => {
101+
let mut init_data = InitializationData {
102+
live: flow_inits.sets().on_entry_set_for(bb.index()).to_owned(),
103+
dead: IdxSetBuf::new_empty(env.move_data.move_paths.len()),
104+
};
105+
debug!("find_dead_unwinds @ {:?}: {:?}; init_data={:?}",
106+
bb, bb_data, init_data.live);
107+
for stmt in 0..bb_data.statements.len() {
108+
let loc = Location { block: bb, statement_index: stmt };
109+
init_data.apply_location(tcx, mir, env, loc);
110+
}
111+
112+
let path = match env.move_data.rev_lookup.find(location) {
113+
LookupResult::Exact(e) => e,
114+
LookupResult::Parent(..) => {
115+
debug!("find_dead_unwinds: has parent; skipping");
116+
continue
117+
}
118+
};
119+
120+
debug!("find_dead_unwinds @ {:?}: path({:?})={:?}", bb, location, path);
121+
122+
let mut maybe_live = false;
123+
on_all_drop_children_bits(tcx, mir, &env, path, |child| {
124+
let (child_maybe_live, _) = init_data.state(child);
125+
maybe_live |= child_maybe_live;
126+
});
127+
128+
debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
129+
if !maybe_live {
130+
dead_unwinds.add(&bb);
131+
}
132+
}
133+
_ => {}
134+
}
135+
}
136+
137+
dead_unwinds
138+
}
139+
77140
struct InitializationData {
78141
live: IdxSetBuf<MovePathIndex>,
79142
dead: IdxSetBuf<MovePathIndex>
@@ -144,17 +207,14 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
144207
let mut some_live = false;
145208
let mut some_dead = false;
146209
let mut children_count = 0;
147-
on_all_children_bits(
148-
self.tcx(), self.mir(), self.ctxt.move_data(),
149-
path, |child| {
150-
if self.ctxt.path_needs_drop(child) {
151-
let (live, dead) = self.init_data.state(child);
152-
debug!("elaborate_drop: state({:?}) = {:?}",
153-
child, (live, dead));
154-
some_live |= live;
155-
some_dead |= dead;
156-
children_count += 1;
157-
}
210+
on_all_drop_children_bits(
211+
self.tcx(), self.mir(), self.ctxt.env, path, |child| {
212+
let (live, dead) = self.init_data.state(child);
213+
debug!("elaborate_drop: state({:?}) = {:?}",
214+
child, (live, dead));
215+
some_live |= live;
216+
some_dead |= dead;
217+
children_count += 1;
158218
});
159219
((some_live, some_dead), children_count != 1)
160220
}
@@ -276,15 +336,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
276336
self.patch
277337
}
278338

279-
fn path_needs_drop(&self, path: MovePathIndex) -> bool
280-
{
281-
let lvalue = &self.move_data().move_paths[path].lvalue;
282-
let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
283-
debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty);
284-
285-
self.tcx.type_needs_drop_given_env(ty, self.param_env())
286-
}
287-
288339
fn collect_drop_flags(&mut self)
289340
{
290341
for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
@@ -318,14 +369,12 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
318369
}
319370
};
320371

321-
on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| {
322-
if self.path_needs_drop(child) {
323-
let (maybe_live, maybe_dead) = init_data.state(child);
324-
debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
325-
child, location, path, (maybe_live, maybe_dead));
326-
if maybe_live && maybe_dead {
327-
self.create_drop_flag(child)
328-
}
372+
on_all_drop_children_bits(self.tcx, self.mir, self.env, path, |child| {
373+
let (maybe_live, maybe_dead) = init_data.state(child);
374+
debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}",
375+
child, location, path, (maybe_live, maybe_dead));
376+
if maybe_live && maybe_dead {
377+
self.create_drop_flag(child)
329378
}
330379
});
331380
}

src/librustc_borrowck/borrowck/mir/mod.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc::mir::{self, BasicBlock, BasicBlockData, Mir, Statement, Terminator, L
1717
use rustc::session::Session;
1818
use rustc::ty::{self, TyCtxt};
1919
use rustc_mir::util::elaborate_drops::DropFlagState;
20+
use rustc_data_structures::indexed_set::{IdxSet, IdxSetBuf};
2021

2122
mod abs_domain;
2223
pub mod elaborate_drops;
@@ -64,14 +65,18 @@ pub fn borrowck_mir(bcx: &mut BorrowckCtxt,
6465
let param_env = ty::ParameterEnvironment::for_item(tcx, id);
6566
let move_data = MoveData::gather_moves(mir, tcx, &param_env);
6667
let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
68+
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
6769
let flow_inits =
68-
do_dataflow(tcx, mir, id, attributes, MaybeInitializedLvals::new(tcx, mir, &mdpe),
70+
do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
71+
MaybeInitializedLvals::new(tcx, mir, &mdpe),
6972
|bd, i| &bd.move_data().move_paths[i]);
7073
let flow_uninits =
71-
do_dataflow(tcx, mir, id, attributes, MaybeUninitializedLvals::new(tcx, mir, &mdpe),
74+
do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
75+
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
7276
|bd, i| &bd.move_data().move_paths[i]);
7377
let flow_def_inits =
74-
do_dataflow(tcx, mir, id, attributes, DefinitelyInitializedLvals::new(tcx, mir, &mdpe),
78+
do_dataflow(tcx, mir, id, attributes, &dead_unwinds,
79+
DefinitelyInitializedLvals::new(tcx, mir, &mdpe),
7580
|bd, i| &bd.move_data().move_paths[i]);
7681

7782
if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() {
@@ -108,6 +113,7 @@ fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
108113
mir: &Mir<'tcx>,
109114
node_id: ast::NodeId,
110115
attributes: &[ast::Attribute],
116+
dead_unwinds: &IdxSet<BasicBlock>,
111117
bd: BD,
112118
p: P)
113119
-> DataflowResults<BD>
@@ -137,7 +143,7 @@ fn do_dataflow<'a, 'tcx, BD, P>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
137143
node_id: node_id,
138144
print_preflow_to: print_preflow_to,
139145
print_postflow_to: print_postflow_to,
140-
flow_state: DataflowAnalysis::new(tcx, mir, bd),
146+
flow_state: DataflowAnalysis::new(tcx, mir, dead_unwinds, bd),
141147
};
142148

143149
mbcx.dataflow(p);
@@ -303,6 +309,27 @@ fn on_all_children_bits<'a, 'tcx, F>(
303309
on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child);
304310
}
305311

312+
fn on_all_drop_children_bits<'a, 'tcx, F>(
313+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
314+
mir: &Mir<'tcx>,
315+
ctxt: &MoveDataParamEnv<'tcx>,
316+
path: MovePathIndex,
317+
mut each_child: F)
318+
where F: FnMut(MovePathIndex)
319+
{
320+
on_all_children_bits(tcx, mir, &ctxt.move_data, path, |child| {
321+
let lvalue = &ctxt.move_data.move_paths[path].lvalue;
322+
let ty = lvalue.ty(mir, tcx).to_ty(tcx);
323+
debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, lvalue, ty);
324+
325+
if tcx.type_needs_drop_given_env(ty, &ctxt.param_env) {
326+
each_child(child);
327+
} else {
328+
debug!("on_all_drop_children_bits - skipping")
329+
}
330+
})
331+
}
332+
306333
fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
307334
tcx: TyCtxt<'a, 'tcx, 'tcx>,
308335
mir: &Mir<'tcx>,

src/test/mir-opt/issue-41110.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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+
// check that we don't emit multiple drop flags when they are not needed.
12+
13+
fn main() {
14+
let x = S.other(S.id());
15+
}
16+
17+
pub fn test() {
18+
let u = S;
19+
let mut v = S;
20+
drop(v);
21+
v = u;
22+
}
23+
24+
struct S;
25+
impl Drop for S {
26+
fn drop(&mut self) {
27+
}
28+
}
29+
30+
impl S {
31+
fn id(self) -> Self { self }
32+
fn other(self, s: Self) {}
33+
}
34+
35+
// END RUST SOURCE
36+
// START rustc.node4.ElaborateDrops.after.mir
37+
// let mut _2: S;
38+
// let mut _3: ();
39+
// let mut _4: S;
40+
// let mut _5: S;
41+
// let mut _6: bool;
42+
//
43+
// bb0: {
44+
// END rustc.node4.ElaborateDrops.after.mir
45+
// START rustc.node13.ElaborateDrops.after.mir
46+
// let mut _2: ();
47+
// let mut _4: ();
48+
// let mut _5: S;
49+
// let mut _6: S;
50+
// let mut _7: bool;
51+
//
52+
// bb0: {
53+
// END rustc.node13.ElaborateDrops.after.mir

0 commit comments

Comments
 (0)