Skip to content

Commit 313219c

Browse files
committed
Auto merge of rust-lang#106430 - tmiasko:rm-dead-unwinds, r=cjgillot
Remove dead unwinds before drop elaboration As a part of drop elaboration, we identify dead unwinds, i.e., unwind edges on a drop terminators which are known to be unreachable, because there is no need to drop anything. Previously, the data flow framework was informed about the dead unwinds, and it assumed those edges are absent from MIR. Unfortunately, the data flow framework wasn't consistent in maintaining this assumption. In particular, if a block was reachable only through a dead unwind edge, its state was propagated to other blocks still. This became an issue in the context of change removes DropAndReplace terminator, since it introduces initialization into cleanup blocks. To avoid this issue, remove unreachable unwind edges before the drop elaboration, and elaborate only blocks that remain reachable. cc `@Zeegomo`
2 parents 6ffabf3 + 2a70524 commit 313219c

11 files changed

+389
-402
lines changed

compiler/rustc_mir_dataflow/src/framework/direction.rs

+3-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use rustc_index::bit_set::BitSet;
21
use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets};
32
use rustc_middle::ty::TyCtxt;
43
use std::ops::RangeInclusive;
@@ -54,7 +53,6 @@ pub trait Direction {
5453
analysis: &A,
5554
tcx: TyCtxt<'tcx>,
5655
body: &mir::Body<'tcx>,
57-
dead_unwinds: Option<&BitSet<BasicBlock>>,
5856
exit_state: &mut A::Domain,
5957
block: (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
6058
propagate: impl FnMut(BasicBlock, &A::Domain),
@@ -221,7 +219,6 @@ impl Direction for Backward {
221219
analysis: &A,
222220
_tcx: TyCtxt<'tcx>,
223221
body: &mir::Body<'tcx>,
224-
dead_unwinds: Option<&BitSet<BasicBlock>>,
225222
exit_state: &mut A::Domain,
226223
(bb, _bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
227224
mut propagate: impl FnMut(BasicBlock, &A::Domain),
@@ -278,20 +275,6 @@ impl Direction for Backward {
278275
}
279276
}
280277

281-
// Ignore dead unwinds.
282-
mir::TerminatorKind::Call { cleanup: Some(unwind), .. }
283-
| mir::TerminatorKind::Assert { cleanup: Some(unwind), .. }
284-
| mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
285-
| mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
286-
| mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
287-
| mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. }
288-
if unwind == bb =>
289-
{
290-
if dead_unwinds.map_or(true, |dead| !dead.contains(pred)) {
291-
propagate(pred, exit_state);
292-
}
293-
}
294-
295278
_ => propagate(pred, exit_state),
296279
}
297280
}
@@ -304,7 +287,6 @@ struct BackwardSwitchIntEdgeEffectsApplier<'a, 'tcx, D, F> {
304287
exit_state: &'a mut D,
305288
bb: BasicBlock,
306289
propagate: &'a mut F,
307-
308290
effects_applied: bool,
309291
}
310292

@@ -484,7 +466,6 @@ impl Direction for Forward {
484466
analysis: &A,
485467
_tcx: TyCtxt<'tcx>,
486468
_body: &mir::Body<'tcx>,
487-
dead_unwinds: Option<&BitSet<BasicBlock>>,
488469
exit_state: &mut A::Domain,
489470
(bb, bb_data): (BasicBlock, &'_ mir::BasicBlockData<'tcx>),
490471
mut propagate: impl FnMut(BasicBlock, &A::Domain),
@@ -502,9 +483,7 @@ impl Direction for Forward {
502483
| DropAndReplace { target, unwind, value: _, place: _ }
503484
| FalseUnwind { real_target: target, unwind } => {
504485
if let Some(unwind) = unwind {
505-
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
506-
propagate(unwind, exit_state);
507-
}
486+
propagate(unwind, exit_state);
508487
}
509488

510489
propagate(target, exit_state);
@@ -534,9 +513,7 @@ impl Direction for Forward {
534513
fn_span: _,
535514
} => {
536515
if let Some(unwind) = cleanup {
537-
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
538-
propagate(unwind, exit_state);
539-
}
516+
propagate(unwind, exit_state);
540517
}
541518

542519
if let Some(target) = target {
@@ -560,9 +537,7 @@ impl Direction for Forward {
560537
cleanup,
561538
} => {
562539
if let Some(unwind) = cleanup {
563-
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
564-
propagate(unwind, exit_state);
565-
}
540+
propagate(unwind, exit_state);
566541
}
567542

568543
if let Some(target) = destination {

compiler/rustc_mir_dataflow/src/framework/engine.rs

+2-30
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rustc_ast as ast;
1212
use rustc_data_structures::work_queue::WorkQueue;
1313
use rustc_graphviz as dot;
1414
use rustc_hir::def_id::DefId;
15-
use rustc_index::bit_set::BitSet;
1615
use rustc_index::vec::{Idx, IndexVec};
1716
use rustc_middle::mir::{self, traversal, BasicBlock};
1817
use rustc_middle::mir::{create_dump_file, dump_enabled};
@@ -78,7 +77,6 @@ where
7877
{
7978
tcx: TyCtxt<'tcx>,
8079
body: &'a mir::Body<'tcx>,
81-
dead_unwinds: Option<&'a BitSet<BasicBlock>>,
8280
entry_sets: IndexVec<BasicBlock, A::Domain>,
8381
pass_name: Option<&'static str>,
8482
analysis: A,
@@ -154,25 +152,7 @@ where
154152
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
155153
}
156154

157-
Engine {
158-
analysis,
159-
tcx,
160-
body,
161-
dead_unwinds: None,
162-
pass_name: None,
163-
entry_sets,
164-
apply_trans_for_block,
165-
}
166-
}
167-
168-
/// Signals that we do not want dataflow state to propagate across unwind edges for these
169-
/// `BasicBlock`s.
170-
///
171-
/// You must take care that `dead_unwinds` does not contain a `BasicBlock` that *can* actually
172-
/// unwind during execution. Otherwise, your dataflow results will not be correct.
173-
pub fn dead_unwinds(mut self, dead_unwinds: &'a BitSet<BasicBlock>) -> Self {
174-
self.dead_unwinds = Some(dead_unwinds);
175-
self
155+
Engine { analysis, tcx, body, pass_name: None, entry_sets, apply_trans_for_block }
176156
}
177157

178158
/// Adds an identifier to the graphviz output for this particular run of a dataflow analysis.
@@ -190,14 +170,7 @@ where
190170
A::Domain: DebugWithContext<A>,
191171
{
192172
let Engine {
193-
analysis,
194-
body,
195-
dead_unwinds,
196-
mut entry_sets,
197-
tcx,
198-
apply_trans_for_block,
199-
pass_name,
200-
..
173+
analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, ..
201174
} = self;
202175

203176
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
@@ -236,7 +209,6 @@ where
236209
&analysis,
237210
tcx,
238211
body,
239-
dead_unwinds,
240212
&mut state,
241213
(bb, bb_data),
242214
|target: BasicBlock, state: &A::Domain| {

compiler/rustc_mir_transform/src/elaborate_drops.rs

+39-18
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,24 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
6767
};
6868
let un_derefer = UnDerefer { tcx: tcx, derefer_sidetable: side_table };
6969
let elaborate_patch = {
70-
let body = &*body;
7170
let env = MoveDataParamEnv { move_data, param_env };
72-
let dead_unwinds = find_dead_unwinds(tcx, body, &env, &un_derefer);
71+
remove_dead_unwinds(tcx, body, &env, &un_derefer);
7372

7473
let inits = MaybeInitializedPlaces::new(tcx, body, &env)
7574
.into_engine(tcx, body)
76-
.dead_unwinds(&dead_unwinds)
7775
.pass_name("elaborate_drops")
7876
.iterate_to_fixpoint()
7977
.into_results_cursor(body);
8078

8179
let uninits = MaybeUninitializedPlaces::new(tcx, body, &env)
8280
.mark_inactive_variants_as_uninit()
8381
.into_engine(tcx, body)
84-
.dead_unwinds(&dead_unwinds)
8582
.pass_name("elaborate_drops")
8683
.iterate_to_fixpoint()
8784
.into_results_cursor(body);
8885

86+
let reachable = traversal::reachable_as_bitset(body);
87+
8988
ElaborateDropsCtxt {
9089
tcx,
9190
body,
@@ -94,6 +93,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
9493
drop_flags: Default::default(),
9594
patch: MirPatch::new(body),
9695
un_derefer: un_derefer,
96+
reachable,
9797
}
9898
.elaborate()
9999
};
@@ -102,22 +102,21 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
102102
}
103103
}
104104

105-
/// Returns the set of basic blocks whose unwind edges are known
106-
/// to not be reachable, because they are `drop` terminators
105+
/// Removes unwind edges which are known to be unreachable, because they are in `drop` terminators
107106
/// that can't drop anything.
108-
fn find_dead_unwinds<'tcx>(
107+
fn remove_dead_unwinds<'tcx>(
109108
tcx: TyCtxt<'tcx>,
110-
body: &Body<'tcx>,
109+
body: &mut Body<'tcx>,
111110
env: &MoveDataParamEnv<'tcx>,
112111
und: &UnDerefer<'tcx>,
113-
) -> BitSet<BasicBlock> {
114-
debug!("find_dead_unwinds({:?})", body.span);
112+
) {
113+
debug!("remove_dead_unwinds({:?})", body.span);
115114
// We only need to do this pass once, because unwind edges can only
116115
// reach cleanup blocks, which can't have unwind edges themselves.
117-
let mut dead_unwinds = BitSet::new_empty(body.basic_blocks.len());
116+
let mut dead_unwinds = Vec::new();
118117
let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env)
119118
.into_engine(tcx, body)
120-
.pass_name("find_dead_unwinds")
119+
.pass_name("remove_dead_unwinds")
121120
.iterate_to_fixpoint()
122121
.into_results_cursor(body);
123122
for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
@@ -129,16 +128,16 @@ fn find_dead_unwinds<'tcx>(
129128
_ => continue,
130129
};
131130

132-
debug!("find_dead_unwinds @ {:?}: {:?}", bb, bb_data);
131+
debug!("remove_dead_unwinds @ {:?}: {:?}", bb, bb_data);
133132

134133
let LookupResult::Exact(path) = env.move_data.rev_lookup.find(place.as_ref()) else {
135-
debug!("find_dead_unwinds: has parent; skipping");
134+
debug!("remove_dead_unwinds: has parent; skipping");
136135
continue;
137136
};
138137

139138
flow_inits.seek_before_primary_effect(body.terminator_loc(bb));
140139
debug!(
141-
"find_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}",
140+
"remove_dead_unwinds @ {:?}: path({:?})={:?}; init_data={:?}",
142141
bb,
143142
place,
144143
path,
@@ -150,13 +149,22 @@ fn find_dead_unwinds<'tcx>(
150149
maybe_live |= flow_inits.contains(child);
151150
});
152151

153-
debug!("find_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
152+
debug!("remove_dead_unwinds @ {:?}: maybe_live={}", bb, maybe_live);
154153
if !maybe_live {
155-
dead_unwinds.insert(bb);
154+
dead_unwinds.push(bb);
156155
}
157156
}
158157

159-
dead_unwinds
158+
if dead_unwinds.is_empty() {
159+
return;
160+
}
161+
162+
let basic_blocks = body.basic_blocks.as_mut();
163+
for &bb in dead_unwinds.iter() {
164+
if let Some(unwind) = basic_blocks[bb].terminator_mut().unwind_mut() {
165+
*unwind = None;
166+
}
167+
}
160168
}
161169

162170
struct InitializationData<'mir, 'tcx> {
@@ -290,6 +298,7 @@ struct ElaborateDropsCtxt<'a, 'tcx> {
290298
drop_flags: FxHashMap<MovePathIndex, Local>,
291299
patch: MirPatch<'tcx>,
292300
un_derefer: UnDerefer<'tcx>,
301+
reachable: BitSet<BasicBlock>,
293302
}
294303

295304
impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
@@ -329,6 +338,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
329338

330339
fn collect_drop_flags(&mut self) {
331340
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
341+
if !self.reachable.contains(bb) {
342+
continue;
343+
}
332344
let terminator = data.terminator();
333345
let place = match terminator.kind {
334346
TerminatorKind::Drop { ref place, .. }
@@ -384,6 +396,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
384396

385397
fn elaborate_drops(&mut self) {
386398
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
399+
if !self.reachable.contains(bb) {
400+
continue;
401+
}
387402
let loc = Location { block: bb, statement_index: data.statements.len() };
388403
let terminator = data.terminator();
389404

@@ -541,6 +556,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
541556

542557
fn drop_flags_for_fn_rets(&mut self) {
543558
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
559+
if !self.reachable.contains(bb) {
560+
continue;
561+
}
544562
if let TerminatorKind::Call {
545563
destination, target: Some(tgt), cleanup: Some(_), ..
546564
} = data.terminator().kind
@@ -576,6 +594,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
576594
// clobbered before they are read.
577595

578596
for (bb, data) in self.body.basic_blocks.iter_enumerated() {
597+
if !self.reachable.contains(bb) {
598+
continue;
599+
}
579600
debug!("drop_flags_for_locs({:?})", data);
580601
for i in 0..(data.statements.len() + 1) {
581602
debug!("drop_flag_for_locs: stmt {}", i);

tests/mir-opt/issue_41110.main.ElaborateDrops.after.mir

-70
This file was deleted.

0 commit comments

Comments
 (0)