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