]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/remove_noop_landing_pads.rs
73089a2106f6b4f46885d7ea607dbf227e39ad4b
[rust.git] / src / librustc_mir / transform / remove_noop_landing_pads.rs
1 use rustc::ty::TyCtxt;
2 use rustc::mir::*;
3 use rustc_data_structures::bit_set::BitSet;
4 use crate::transform::{MirPass, MirSource};
5 use crate::util::patch::MirPatch;
6
7 /// A pass that removes noop landing pads and replaces jumps to them with
8 /// `None`. This is important because otherwise LLVM generates terrible
9 /// code for these.
10 pub struct RemoveNoopLandingPads;
11
12 pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
13     if tcx.sess.no_landing_pads() {
14         return
15     }
16     debug!("remove_noop_landing_pads({:?})", body);
17
18     RemoveNoopLandingPads.remove_nop_landing_pads(body)
19 }
20
21 impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
22     fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
23         remove_noop_landing_pads(tcx, body);
24     }
25 }
26
27 impl RemoveNoopLandingPads {
28     fn is_nop_landing_pad(
29         &self,
30         bb: BasicBlock,
31         body: &Body<'_>,
32         nop_landing_pads: &BitSet<BasicBlock>,
33     ) -> bool {
34         for stmt in &body[bb].statements {
35             match stmt.kind {
36                 StatementKind::FakeRead(..) |
37                 StatementKind::StorageLive(_) |
38                 StatementKind::StorageDead(_) |
39                 StatementKind::AscribeUserType(..) |
40                 StatementKind::Nop => {
41                     // These are all nops in a landing pad
42                 }
43
44                 StatementKind::Assign(Place {
45                     base: PlaceBase::Local(_),
46                     projection: None,
47                 }, box Rvalue::Use(_)) => {
48                     // Writing to a local (e.g., a drop flag) does not
49                     // turn a landing pad to a non-nop
50                 }
51
52                 StatementKind::Assign { .. } |
53                 StatementKind::SetDiscriminant { .. } |
54                 StatementKind::InlineAsm { .. } |
55                 StatementKind::Retag { .. } => {
56                     return false;
57                 }
58             }
59         }
60
61         let terminator = body[bb].terminator();
62         match terminator.kind {
63             TerminatorKind::Goto { .. } |
64             TerminatorKind::Resume |
65             TerminatorKind::SwitchInt { .. } |
66             TerminatorKind::FalseEdges { .. } |
67             TerminatorKind::FalseUnwind { .. } => {
68                 terminator.successors().all(|&succ| {
69                     nop_landing_pads.contains(succ)
70                 })
71             },
72             TerminatorKind::GeneratorDrop |
73             TerminatorKind::Yield { .. } |
74             TerminatorKind::Return |
75             TerminatorKind::Abort |
76             TerminatorKind::Unreachable |
77             TerminatorKind::Call { .. } |
78             TerminatorKind::Assert { .. } |
79             TerminatorKind::DropAndReplace { .. } |
80             TerminatorKind::Drop { .. } => {
81                 false
82             }
83         }
84     }
85
86     fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
87         // make sure there's a single resume block
88         let resume_block = {
89             let patch = MirPatch::new(body);
90             let resume_block = patch.resume_block();
91             patch.apply(body);
92             resume_block
93         };
94         debug!("remove_noop_landing_pads: resume block is {:?}", resume_block);
95
96         let mut jumps_folded = 0;
97         let mut landing_pads_removed = 0;
98         let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks().len());
99
100         // This is a post-order traversal, so that if A post-dominates B
101         // then A will be visited before B.
102         let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
103         for bb in postorder {
104             debug!("  processing {:?}", bb);
105             for target in body[bb].terminator_mut().successors_mut() {
106                 if *target != resume_block && nop_landing_pads.contains(*target) {
107                     debug!("    folding noop jump to {:?} to resume block", target);
108                     *target = resume_block;
109                     jumps_folded += 1;
110                 }
111             }
112
113             match body[bb].terminator_mut().unwind_mut() {
114                 Some(unwind) => {
115                     if *unwind == Some(resume_block) {
116                         debug!("    removing noop landing pad");
117                         jumps_folded -= 1;
118                         landing_pads_removed += 1;
119                         *unwind = None;
120                     }
121                 }
122                 _ => {}
123             }
124
125             let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads);
126             if is_nop_landing_pad {
127                 nop_landing_pads.insert(bb);
128             }
129             debug!("    is_nop_landing_pad({:?}) = {}", bb, is_nop_landing_pad);
130         }
131
132         debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed);
133     }
134 }