Skip to main content

rustc_mir_transform/
remove_noop_landing_pads.rs

1use 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
9/// A pass that removes noop landing pads and replaces jumps to them with
10/// `UnwindAction::Continue`. This is important because otherwise LLVM generates
11/// terrible code for these.
12pub(super) struct RemoveNoopLandingPads;
13
14impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads {
15    fn policy(&self, sess: &rustc_session::Session) -> PassPolicy {
16        // FIXME: isn't this an optimization? Or is the LLVM code so terrible we want this even with
17        // "no" optimizations?
18        PassPolicy::optional_non_optimization(sess.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        // Skip the pass if there are no blocks with a resume terminator.
27        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(body, None);
37
38        if nop_landing_pads.is_empty() {
39            debug!("no nop landing pads in MIR");
40            return;
41        }
42
43        // make sure there's a resume block without any statements
44        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        bbdata: &BasicBlockData<'tcx>,
78        body: &Body<'tcx>,
79        nop_landing_pads: &DenseBitSet<BasicBlock>,
80        extra: Option<&ExtraInfo<'tcx>>,
81    ) -> bool {
82        for stmt in &bbdata.statements {
83            match &stmt.kind {
84                StatementKind::FakeRead(..)
85                | StatementKind::StorageLive(_)
86                | StatementKind::StorageDead(_)
87                | StatementKind::PlaceMention(..)
88                | StatementKind::AscribeUserType(..)
89                | StatementKind::Coverage(..)
90                | StatementKind::ConstEvalCounter
91                | StatementKind::BackwardIncompatibleDropHint { .. }
92                | StatementKind::Nop => {
93                    // These are all noops in a landing pad
94                }
95
96                StatementKind::Assign((place, Rvalue::Use(..) | Rvalue::Discriminant(_))) => {
97                    if place.as_local().is_some() {
98                        // Writing to a local (e.g., a drop flag) does not
99                        // turn a landing pad to a non-nop
100                    } else {
101                        return false;
102                    }
103                }
104
105                StatementKind::Assign { .. }
106                | StatementKind::SetDiscriminant { .. }
107                | StatementKind::Intrinsic(..) => {
108                    return false;
109                }
110            }
111        }
112
113        let terminator = bbdata.terminator();
114        match terminator.kind {
115            TerminatorKind::Goto { .. }
116            | TerminatorKind::UnwindResume
117            | TerminatorKind::SwitchInt { .. }
118            | TerminatorKind::FalseEdge { .. }
119            | TerminatorKind::FalseUnwind { .. } => {
120                terminator.successors().all(|succ| nop_landing_pads.contains(succ))
121            }
122            TerminatorKind::Drop { place, .. } => {
123                if let Some(extra) = extra {
124                    let ty = place.ty(body, extra.tcx).ty;
125                    debug!("monomorphize: instance={:?}", extra.instance);
126                    let ty = extra.instance.instantiate_mir_and_normalize_erasing_regions(
127                        extra.tcx,
128                        extra.typing_env,
129                        ty::EarlyBinder::bind(extra.tcx, ty),
130                    );
131                    let drop_fn = Instance::resolve_drop_glue(extra.tcx, ty);
132                    if let ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None)) = drop_fn.def {
133                        // no need to drop anything, if all of our successors are also no-op then we
134                        // can be skipped.
135                        return terminator.successors().all(|succ| nop_landing_pads.contains(succ));
136                    }
137                }
138
139                false
140            }
141            TerminatorKind::CoroutineDrop
142            | TerminatorKind::Yield { .. }
143            | TerminatorKind::Return
144            | TerminatorKind::UnwindTerminate(_)
145            | TerminatorKind::Unreachable
146            | TerminatorKind::Call { .. }
147            | TerminatorKind::TailCall { .. }
148            | TerminatorKind::Assert { .. }
149            | TerminatorKind::InlineAsm { .. } => false,
150        }
151    }
152}
153
154/// This provides extra information that allows further analysis.
155///
156/// Used by rustc_codegen_ssa.
157pub struct ExtraInfo<'tcx> {
158    pub tcx: TyCtxt<'tcx>,
159    pub instance: Instance<'tcx>,
160    pub typing_env: ty::TypingEnv<'tcx>,
161}
162
163pub fn find_noop_landing_pads<'tcx>(
164    body: &Body<'tcx>,
165    extra: Option<ExtraInfo<'tcx>>,
166) -> DenseBitSet<BasicBlock> {
167    let mut nop_landing_pads = DenseBitSet::new_empty(body.basic_blocks.len());
168
169    // This is a post-order traversal, so that if A post-dominates B
170    // then A will be visited before B.
171    let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
172    for bb in postorder {
173        let is_nop_landing_pad = RemoveNoopLandingPads.is_nop_landing_pad(
174            &body.basic_blocks[bb],
175            body,
176            &nop_landing_pads,
177            extra.as_ref(),
178        );
179        if is_nop_landing_pad {
180            nop_landing_pads.insert(bb);
181        }
182        debug!("    is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad);
183    }
184
185    nop_landing_pads
186}