rustc_mir_transform/
remove_noop_landing_pads.rs1use rustc_index::bit_set::DenseBitSet;
2use rustc_middle::mir::*;
3use rustc_middle::ty::{self, Instance, TyCtxt};
4use tracing::{debug, instrument};
5
6use crate::PassPolicy;
7use crate::patch::MirPatch;
8
9pub(super) struct RemoveNoopLandingPads;
13
14impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
15 fn policy(&self, ctx: &crate::PassCtx<'_>) -> PassPolicy {
16 PassPolicy::optional(ctx.panic_strategy().unwinds())
19 }
20
21 #[instrument(level = "debug", skip(self, tcx, body))]
22 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
23 let def_id = body.source.def_id();
24 debug!(?def_id);
25
26 let has_resume = body
28 .basic_blocks
29 .iter_enumerated()
30 .any(|(_bb, block)| matches!(block.terminator().kind, TerminatorKind::UnwindResume));
31 if !has_resume {
32 debug!("no resume block in MIR");
33 return;
34 }
35
36 let nop_landing_pads = find_noop_landing_pads(tcx, body, None);
37
38 if nop_landing_pads.is_empty() {
39 debug!("no nop landing pads in MIR");
40 return;
41 }
42
43 let resume_block = {
45 let mut patch = MirPatch::new(body);
46 let resume_block = patch.resume_block();
47 patch.apply(body);
48 resume_block
49 };
50 debug!(?resume_block);
51
52 let basic_blocks = body.basic_blocks.as_mut();
53 for (bb, bbdata) in basic_blocks.iter_enumerated_mut() {
54 debug!("processing {:?}", bb);
55
56 if let Some(unwind) = bbdata.terminator_mut().unwind_mut()
57 && let UnwindAction::Cleanup(unwind_bb) = *unwind
58 && nop_landing_pads.contains(unwind_bb)
59 {
60 debug!(" removing noop landing pad");
61 *unwind = UnwindAction::Continue;
62 }
63
64 bbdata.terminator_mut().successors_mut(|target| {
65 if *target != resume_block && nop_landing_pads.contains(*target) {
66 debug!(" folding noop jump to {:?} to resume block", target);
67 *target = resume_block;
68 }
69 });
70 }
71 }
72}
73
74impl RemoveNoopLandingPads {
75 fn is_nop_landing_pad<'tcx>(
76 &self,
77 tcx: TyCtxt<'tcx>,
78 bbdata: &BasicBlockData<'tcx>,
79 body: &Body<'tcx>,
80 nop_landing_pads: &DenseBitSet<BasicBlock>,
81 extra: Option<(Instance<'tcx>, ty::TypingEnv<'tcx>)>,
83 ) -> bool {
84 for stmt in &bbdata.statements {
85 match &stmt.kind {
86 StatementKind::FakeRead(..)
87 | StatementKind::StorageLive(_)
88 | StatementKind::StorageDead(_)
89 | StatementKind::PlaceMention(..)
90 | StatementKind::AscribeUserType(..)
91 | StatementKind::Coverage(..)
92 | StatementKind::ConstEvalCounter
93 | StatementKind::BackwardIncompatibleDropHint { .. }
94 | StatementKind::Nop => {
95 }
97
98 StatementKind::Assign((place, Rvalue::Use(..) | Rvalue::Discriminant(_))) => {
99 if place.as_local().is_some() {
100 } else {
103 return false;
104 }
105 }
106
107 StatementKind::Assign { .. }
108 | StatementKind::SetDiscriminant { .. }
109 | StatementKind::Intrinsic(..) => {
110 return false;
111 }
112 }
113 }
114
115 let terminator = bbdata.terminator();
116 match terminator.kind {
117 TerminatorKind::Goto { .. }
118 | TerminatorKind::UnwindResume
119 | TerminatorKind::SwitchInt { .. }
120 | TerminatorKind::FalseEdge { .. }
121 | TerminatorKind::FalseUnwind { .. } => {
122 terminator.successors().all(|succ| nop_landing_pads.contains(succ))
123 }
124 TerminatorKind::Drop { place, .. } => {
125 if let Some((instance, typing_env)) = extra {
126 let ty = place.ty(body, tcx).ty;
127 debug!("monomorphize: instance={instance:?}");
128 let ty = instance.instantiate_mir_and_normalize_erasing_regions(
129 tcx,
130 typing_env,
131 ty::EarlyBinder::bind(tcx, ty),
132 );
133 let drop_fn = Instance::resolve_drop_glue(tcx, ty);
134 if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) = drop_fn.def {
135 return terminator.successors().all(|succ| nop_landing_pads.contains(succ));
138 }
139 }
140
141 false
142 }
143 TerminatorKind::CoroutineDrop
144 | TerminatorKind::Yield { .. }
145 | TerminatorKind::Return
146 | TerminatorKind::UnwindTerminate(_)
147 | TerminatorKind::Unreachable
148 | TerminatorKind::Call { .. }
149 | TerminatorKind::TailCall { .. }
150 | TerminatorKind::Assert { .. }
151 | TerminatorKind::InlineAsm { .. } => false,
152 }
153 }
154}
155
156pub(crate) fn find_noop_landing_pads_for_instance<'tcx>(
158 tcx: TyCtxt<'tcx>,
159 body: &Body<'tcx>,
160 instance: Instance<'tcx>,
161 typing_env: ty::TypingEnv<'tcx>,
162) -> DenseBitSet<BasicBlock> {
163 find_noop_landing_pads(tcx, body, Some((instance, typing_env)))
164}
165
166fn find_noop_landing_pads<'tcx>(
167 tcx: TyCtxt<'tcx>,
168 body: &Body<'tcx>,
169 extra: Option<(Instance<'tcx>, ty::TypingEnv<'tcx>)>,
170) -> DenseBitSet<BasicBlock> {
171 let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len());
172
173 let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
176 for bb in postorder {
177 let is_nop_landing_pad = RemoveNoopLandingPads.is_nop_landing_pad(
178 tcx,
179 &body.basic_blocks[bb],
180 body,
181 &nop_landing_pads,
182 extra,
183 );
184 if is_nop_landing_pad {
185 nop_landing_pads.insert(bb);
186 }
187 debug!(" is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad);
188 }
189
190 nop_landing_pads
191}