Skip to content

Commit 3acfa09

Browse files
committed
Only run MaybeInitializedPlaces once for drop elaboration.
1 parent 5173d85 commit 3acfa09

8 files changed

+125
-116
lines changed

compiler/rustc_mir_dataflow/src/impls/initialized.rs

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::framework::SwitchIntEdgeEffects;
1010
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
1111
use crate::on_lookup_result_bits;
1212
use crate::MoveDataParamEnv;
13-
use crate::{drop_flag_effects, on_all_children_bits};
14-
use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
13+
use crate::{drop_flag_effects, on_all_children_bits, on_all_drop_children_bits};
14+
use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeUnreachable};
1515

1616
/// `MaybeInitializedPlaces` tracks all places that might be
1717
/// initialized upon reaching a particular point in the control flow
@@ -52,11 +52,33 @@ pub struct MaybeInitializedPlaces<'a, 'tcx> {
5252
tcx: TyCtxt<'tcx>,
5353
body: &'a Body<'tcx>,
5454
mdpe: &'a MoveDataParamEnv<'tcx>,
55+
skip_unreachable_unwind: bool,
5556
}
5657

5758
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
5859
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
59-
MaybeInitializedPlaces { tcx, body, mdpe }
60+
MaybeInitializedPlaces { tcx, body, mdpe, skip_unreachable_unwind: false }
61+
}
62+
63+
pub fn skipping_unreachable_unwind(mut self) -> Self {
64+
self.skip_unreachable_unwind = true;
65+
self
66+
}
67+
68+
pub fn is_unwind_dead(
69+
&self,
70+
place: mir::Place<'tcx>,
71+
state: &MaybeUnreachable<ChunkedBitSet<MovePathIndex>>,
72+
) -> bool {
73+
if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) {
74+
let mut maybe_live = false;
75+
on_all_drop_children_bits(self.tcx, self.body, self.mdpe, path, |child| {
76+
maybe_live |= state.contains(child);
77+
});
78+
!maybe_live
79+
} else {
80+
false
81+
}
6082
}
6183
}
6284

@@ -107,11 +129,18 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> {
107129
mdpe: &'a MoveDataParamEnv<'tcx>,
108130

109131
mark_inactive_variants_as_uninit: bool,
132+
skip_unreachable_unwind: BitSet<mir::BasicBlock>,
110133
}
111134

112135
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
113136
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
114-
MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false }
137+
MaybeUninitializedPlaces {
138+
tcx,
139+
body,
140+
mdpe,
141+
mark_inactive_variants_as_uninit: false,
142+
skip_unreachable_unwind: BitSet::new_empty(body.basic_blocks.len()),
143+
}
115144
}
116145

117146
/// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
@@ -123,6 +152,14 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
123152
self.mark_inactive_variants_as_uninit = true;
124153
self
125154
}
155+
156+
pub fn skipping_unreachable_unwind(
157+
mut self,
158+
unreachable_unwind: BitSet<mir::BasicBlock>,
159+
) -> Self {
160+
self.skip_unreachable_unwind = unreachable_unwind;
161+
self
162+
}
126163
}
127164

128165
impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
@@ -271,18 +308,21 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
271308
}
272309

273310
impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
274-
type Domain = ChunkedBitSet<MovePathIndex>;
311+
type Domain = MaybeUnreachable<ChunkedBitSet<MovePathIndex>>;
275312
const NAME: &'static str = "maybe_init";
276313

277314
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
278315
// bottom = uninitialized
279-
ChunkedBitSet::new_empty(self.move_data().move_paths.len())
316+
MaybeUnreachable::Unreachable
280317
}
281318

282319
fn initialize_start_block(&self, _: &mir::Body<'tcx>, state: &mut Self::Domain) {
320+
*state = MaybeUnreachable::Reachable(ChunkedBitSet::new_empty(
321+
self.move_data().move_paths.len(),
322+
));
283323
drop_flag_effects_for_function_entry(self.tcx, self.body, self.mdpe, |path, s| {
284324
assert!(s == DropFlagState::Present);
285-
state.insert(path);
325+
state.gen(path);
286326
});
287327
}
288328
}
@@ -324,10 +364,18 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
324364
terminator: &'mir mir::Terminator<'tcx>,
325365
location: Location,
326366
) -> TerminatorEdge<'mir, 'tcx> {
367+
let mut edges = terminator.edges();
368+
if self.skip_unreachable_unwind
369+
&& let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind
370+
&& matches!(unwind, mir::UnwindAction::Cleanup(_))
371+
&& self.is_unwind_dead(place, state)
372+
{
373+
edges = TerminatorEdge::Single(target);
374+
}
327375
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
328376
Self::update_bits(state, path, s)
329377
});
330-
terminator.edges()
378+
edges
331379
}
332380

333381
fn call_return_effect(
@@ -448,7 +496,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
448496
drop_flag_effects_for_location(self.tcx, self.body, self.mdpe, location, |path, s| {
449497
Self::update_bits(trans, path, s)
450498
});
451-
terminator.edges()
499+
if self.skip_unreachable_unwind.contains(location.block) {
500+
let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() };
501+
assert!(matches!(unwind, mir::UnwindAction::Cleanup(_)));
502+
TerminatorEdge::Single(target)
503+
} else {
504+
terminator.edges()
505+
}
452506
}
453507

454508
fn call_return_effect(

compiler/rustc_mir_transform/src/elaborate_drops.rs

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use std::fmt;
4848
pub struct ElaborateDrops;
4949

5050
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
51+
#[instrument(level = "trace", skip(self, tcx, body))]
5152
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
5253
debug!("elaborate_drops({:?} @ {:?})", body.source, body.span);
5354

@@ -65,23 +66,29 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
6566
};
6667
let elaborate_patch = {
6768
let env = MoveDataParamEnv { move_data, param_env };
68-
remove_dead_unwinds(tcx, body, &env);
6969

70-
let inits = MaybeInitializedPlaces::new(tcx, body, &env)
70+
let mut inits = MaybeInitializedPlaces::new(tcx, body, &env)
71+
.skipping_unreachable_unwind()
7172
.into_engine(tcx, body)
7273
.pass_name("elaborate_drops")
7374
.iterate_to_fixpoint()
7475
.into_results_cursor(body);
76+
let dead_unwinds = compute_dead_unwinds(&body, &mut inits);
77+
let mut reachable = BitSet::new_empty(body.basic_blocks.len());
78+
for bb in body.basic_blocks.indices() {
79+
if inits.results().entry_set_for_block(bb).is_reachable() {
80+
reachable.insert(bb);
81+
}
82+
}
7583

7684
let uninits = MaybeUninitializedPlaces::new(tcx, body, &env)
7785
.mark_inactive_variants_as_uninit()
86+
.skipping_unreachable_unwind(dead_unwinds)
7887
.into_engine(tcx, body)
7988
.pass_name("elaborate_drops")
8089
.iterate_to_fixpoint()
8190
.into_results_cursor(body);
8291

83-
let reachable = traversal::reachable_as_bitset(body);
84-
8592
let drop_flags = IndexVec::from_elem(None, &env.move_data.move_paths);
8693
ElaborateDropsCtxt {
8794
tcx,
@@ -101,63 +108,28 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
101108

102109
/// Removes unwind edges which are known to be unreachable, because they are in `drop` terminators
103110
/// that can't drop anything.
104-
fn remove_dead_unwinds<'tcx>(
105-
tcx: TyCtxt<'tcx>,
106-
body: &mut Body<'tcx>,
107-
env: &MoveDataParamEnv<'tcx>,
108-
) {
109-
debug!("remove_dead_unwinds({:?})", body.span);
111+
#[instrument(level = "trace", skip(body, flow_inits), ret)]
112+
fn compute_dead_unwinds<'mir, 'tcx>(
113+
body: &'mir Body<'tcx>,
114+
flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
115+
) -> BitSet<BasicBlock> {
110116
// We only need to do this pass once, because unwind edges can only
111117
// reach cleanup blocks, which can't have unwind edges themselves.
112-
let mut dead_unwinds = Vec::new();
113-
let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env)
114-
.into_engine(tcx, body)
115-
.pass_name("remove_dead_unwinds")
116-
.iterate_to_fixpoint()
117-
.into_results_cursor(body);
118+
let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len());
118119
for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
119-
let place = match bb_data.terminator().kind {
120-
TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } => place,
121-
_ => continue,
122-
};
123-
124-
debug!("remove_dead_unwinds @ {:?}: {:?}", bb, bb_data);
125-
126-
let LookupResult::Exact(path) = env.move_data.rev_lookup.find(place.as_ref()) else {
127-
debug!("remove_dead_unwinds: has parent; skipping");
120+
let TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } =
121+
bb_data.terminator().kind
122+
else {
128123
continue;
129124
};
130125

131126
flow_inits.seek_before_primary_effect(body.terminator_loc(bb));
132-
debug!(
133-
"remove_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}",
134-
bb,
135-
place,
136-
path,
137-
flow_inits.get()
138-
);
139-
140-
let mut maybe_live = false;
141-
on_all_drop_children_bits(tcx, body, &env, path, |child| {
142-
maybe_live |= flow_inits.contains(child);
143-
});
144-
145-
debug!("remove_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
146-
if !maybe_live {
147-
dead_unwinds.push(bb);
127+
if flow_inits.analysis().is_unwind_dead(place, flow_inits.get()) {
128+
dead_unwinds.insert(bb);
148129
}
149130
}
150131

151-
if dead_unwinds.is_empty() {
152-
return;
153-
}
154-
155-
let basic_blocks = body.basic_blocks.as_mut();
156-
for &bb in dead_unwinds.iter() {
157-
if let Some(unwind) = basic_blocks[bb].terminator_mut().unwind_mut() {
158-
*unwind = UnwindAction::Unreachable;
159-
}
160-
}
132+
dead_unwinds
161133
}
162134

163135
struct InitializationData<'mir, 'tcx> {

compiler/rustc_mir_transform/src/remove_uninit_drops.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use rustc_middle::ty::GenericArgsRef;
44
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, VariantDef};
55
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
66
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
7-
use rustc_mir_dataflow::{self, move_path_children_matching, Analysis, MoveDataParamEnv};
7+
use rustc_mir_dataflow::{
8+
self, move_path_children_matching, Analysis, MaybeUnreachable, MoveDataParamEnv,
9+
};
810
use rustc_target::abi::FieldIdx;
911

1012
use crate::MirPass;
@@ -41,6 +43,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
4143
let TerminatorKind::Drop { place, .. } = &terminator.kind else { continue };
4244

4345
maybe_inits.seek_before_primary_effect(body.terminator_loc(bb));
46+
let MaybeUnreachable::Reachable(maybe_inits) = maybe_inits.get() else { continue };
4447

4548
// If there's no move path for the dropped place, it's probably a `Deref`. Let it alone.
4649
let LookupResult::Exact(mpi) = mdpe.move_data.rev_lookup.find(place.as_ref()) else {
@@ -50,7 +53,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
5053
let should_keep = is_needs_drop_and_init(
5154
tcx,
5255
param_env,
53-
maybe_inits.get(),
56+
maybe_inits,
5457
&mdpe.move_data,
5558
place.ty(body, tcx).ty,
5659
mpi,

tests/mir-opt/basic_assignment.main.ElaborateDrops.diff

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,6 @@
8080

8181
bb8 (cleanup): {
8282
resume;
83-
+ }
84-
+
85-
+ bb9 (cleanup): {
86-
+ unreachable;
8783
}
8884
}
8985

tests/mir-opt/issue_41110.test.ElaborateDrops.panic-abort.diff

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,19 @@
8080

8181
bb9 (cleanup): {
8282
- drop(_1) -> [return: bb10, unwind terminate];
83-
+ goto -> bb13;
83+
+ goto -> bb12;
8484
}
8585

8686
bb10 (cleanup): {
8787
resume;
8888
+ }
8989
+
9090
+ bb11 (cleanup): {
91-
+ unreachable;
92-
+ }
93-
+
94-
+ bb12 (cleanup): {
9591
+ drop(_1) -> [return: bb10, unwind terminate];
9692
+ }
9793
+
98-
+ bb13 (cleanup): {
99-
+ switchInt(_6) -> [0: bb10, otherwise: bb12];
94+
+ bb12 (cleanup): {
95+
+ switchInt(_6) -> [0: bb10, otherwise: bb11];
10096
}
10197
}
10298

tests/mir-opt/issue_41110.test.ElaborateDrops.panic-unwind.diff

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,19 @@
8080

8181
bb9 (cleanup): {
8282
- drop(_1) -> [return: bb10, unwind terminate];
83-
+ goto -> bb13;
83+
+ goto -> bb12;
8484
}
8585

8686
bb10 (cleanup): {
8787
resume;
8888
+ }
8989
+
9090
+ bb11 (cleanup): {
91-
+ unreachable;
92-
+ }
93-
+
94-
+ bb12 (cleanup): {
9591
+ drop(_1) -> [return: bb10, unwind terminate];
9692
+ }
9793
+
98-
+ bb13 (cleanup): {
99-
+ switchInt(_6) -> [0: bb10, otherwise: bb12];
94+
+ bb12 (cleanup): {
95+
+ switchInt(_6) -> [0: bb10, otherwise: bb11];
10096
}
10197
}
10298

0 commit comments

Comments
 (0)