]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/simplify.rs
Auto merge of #67900 - nikic:prepare-llvm-10, r=nagisa
[rust.git] / src / librustc_mir / transform / simplify.rs
1 //! A number of passes which remove various redundancies in the CFG.
2 //!
3 //! The `SimplifyCfg` pass gets rid of unnecessary blocks in the CFG, whereas the `SimplifyLocals`
4 //! gets rid of all the unnecessary local variable declarations.
5 //!
6 //! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often.
7 //! Most of the passes should not care or be impacted in meaningful ways due to extra locals
8 //! either, so running the pass once, right before codegen, should suffice.
9 //!
10 //! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus
11 //! one should run it after every pass which may modify CFG in significant ways. This pass must
12 //! also be run before any analysis passes because it removes dead blocks, and some of these can be
13 //! ill-typed.
14 //!
15 //! The cause of this typing issue is typeck allowing most blocks whose end is not reachable have
16 //! an arbitrary return type, rather than having the usual () return type (as a note, typeck's
17 //! notion of reachability is in fact slightly weaker than MIR CFG reachability - see #31617). A
18 //! standard example of the situation is:
19 //!
20 //! ```rust
21 //!   fn example() {
22 //!       let _a: char = { return; };
23 //!   }
24 //! ```
25 //!
26 //! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we
27 //! naively generate still contains the `_a = ()` write in the unreachable block "after" the
28 //! return.
29
30 use crate::transform::{MirPass, MirSource};
31 use rustc::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
32 use rustc::mir::*;
33 use rustc::ty::{self, TyCtxt};
34 use rustc_index::bit_set::BitSet;
35 use rustc_index::vec::{Idx, IndexVec};
36 use std::borrow::Cow;
37
38 pub struct SimplifyCfg {
39     label: String,
40 }
41
42 impl SimplifyCfg {
43     pub fn new(label: &str) -> Self {
44         SimplifyCfg { label: format!("SimplifyCfg-{}", label) }
45     }
46 }
47
48 pub fn simplify_cfg(body: &mut BodyAndCache<'_>) {
49     CfgSimplifier::new(body).simplify();
50     remove_dead_blocks(body);
51
52     // FIXME: Should probably be moved into some kind of pass manager
53     body.basic_blocks_mut().raw.shrink_to_fit();
54 }
55
56 impl<'tcx> MirPass<'tcx> for SimplifyCfg {
57     fn name(&self) -> Cow<'_, str> {
58         Cow::Borrowed(&self.label)
59     }
60
61     fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
62         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body);
63         simplify_cfg(body);
64     }
65 }
66
67 pub struct CfgSimplifier<'a, 'tcx> {
68     basic_blocks: &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
69     pred_count: IndexVec<BasicBlock, u32>,
70 }
71
72 impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
73     pub fn new(body: &'a mut BodyAndCache<'tcx>) -> Self {
74         let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks());
75
76         // we can't use mir.predecessors() here because that counts
77         // dead blocks, which we don't want to.
78         pred_count[START_BLOCK] = 1;
79
80         for (_, data) in traversal::preorder(body) {
81             if let Some(ref term) = data.terminator {
82                 for &tgt in term.successors() {
83                     pred_count[tgt] += 1;
84                 }
85             }
86         }
87
88         let basic_blocks = body.basic_blocks_mut();
89
90         CfgSimplifier { basic_blocks, pred_count }
91     }
92
93     pub fn simplify(mut self) {
94         self.strip_nops();
95
96         let mut start = START_BLOCK;
97
98         loop {
99             let mut changed = false;
100
101             self.collapse_goto_chain(&mut start, &mut changed);
102
103             for bb in self.basic_blocks.indices() {
104                 if self.pred_count[bb] == 0 {
105                     continue;
106                 }
107
108                 debug!("simplifying {:?}", bb);
109
110                 let mut terminator =
111                     self.basic_blocks[bb].terminator.take().expect("invalid terminator state");
112
113                 for successor in terminator.successors_mut() {
114                     self.collapse_goto_chain(successor, &mut changed);
115                 }
116
117                 let mut new_stmts = vec![];
118                 let mut inner_changed = true;
119                 while inner_changed {
120                     inner_changed = false;
121                     inner_changed |= self.simplify_branch(&mut terminator);
122                     inner_changed |= self.merge_successor(&mut new_stmts, &mut terminator);
123                     changed |= inner_changed;
124                 }
125
126                 let data = &mut self.basic_blocks[bb];
127                 data.statements.extend(new_stmts);
128                 data.terminator = Some(terminator);
129
130                 changed |= inner_changed;
131             }
132
133             if !changed {
134                 break;
135             }
136         }
137
138         if start != START_BLOCK {
139             debug_assert!(self.pred_count[START_BLOCK] == 0);
140             self.basic_blocks.swap(START_BLOCK, start);
141             self.pred_count.swap(START_BLOCK, start);
142
143             // pred_count == 1 if the start block has no predecessor _blocks_.
144             if self.pred_count[START_BLOCK] > 1 {
145                 for (bb, data) in self.basic_blocks.iter_enumerated_mut() {
146                     if self.pred_count[bb] == 0 {
147                         continue;
148                     }
149
150                     for target in data.terminator_mut().successors_mut() {
151                         if *target == start {
152                             *target = START_BLOCK;
153                         }
154                     }
155                 }
156             }
157         }
158     }
159
160     // Collapse a goto chain starting from `start`
161     fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
162         let mut terminator = match self.basic_blocks[*start] {
163             BasicBlockData {
164                 ref statements,
165                 terminator:
166                     ref mut terminator @ Some(Terminator { kind: TerminatorKind::Goto { .. }, .. }),
167                 ..
168             } if statements.is_empty() => terminator.take(),
169             // if `terminator` is None, this means we are in a loop. In that
170             // case, let all the loop collapse to its entry.
171             _ => return,
172         };
173
174         let target = match terminator {
175             Some(Terminator { kind: TerminatorKind::Goto { ref mut target }, .. }) => {
176                 self.collapse_goto_chain(target, changed);
177                 *target
178             }
179             _ => unreachable!(),
180         };
181         self.basic_blocks[*start].terminator = terminator;
182
183         debug!("collapsing goto chain from {:?} to {:?}", *start, target);
184
185         *changed |= *start != target;
186
187         if self.pred_count[*start] == 1 {
188             // This is the last reference to *start, so the pred-count to
189             // to target is moved into the current block.
190             self.pred_count[*start] = 0;
191         } else {
192             self.pred_count[target] += 1;
193             self.pred_count[*start] -= 1;
194         }
195
196         *start = target;
197     }
198
199     // merge a block with 1 `goto` predecessor to its parent
200     fn merge_successor(
201         &mut self,
202         new_stmts: &mut Vec<Statement<'tcx>>,
203         terminator: &mut Terminator<'tcx>,
204     ) -> bool {
205         let target = match terminator.kind {
206             TerminatorKind::Goto { target } if self.pred_count[target] == 1 => target,
207             _ => return false,
208         };
209
210         debug!("merging block {:?} into {:?}", target, terminator);
211         *terminator = match self.basic_blocks[target].terminator.take() {
212             Some(terminator) => terminator,
213             None => {
214                 // unreachable loop - this should not be possible, as we
215                 // don't strand blocks, but handle it correctly.
216                 return false;
217             }
218         };
219         new_stmts.extend(self.basic_blocks[target].statements.drain(..));
220         self.pred_count[target] = 0;
221
222         true
223     }
224
225     // turn a branch with all successors identical to a goto
226     fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
227         match terminator.kind {
228             TerminatorKind::SwitchInt { .. } => {}
229             _ => return false,
230         };
231
232         let first_succ = {
233             if let Some(&first_succ) = terminator.successors().nth(0) {
234                 if terminator.successors().all(|s| *s == first_succ) {
235                     let count = terminator.successors().count();
236                     self.pred_count[first_succ] -= (count - 1) as u32;
237                     first_succ
238                 } else {
239                     return false;
240                 }
241             } else {
242                 return false;
243             }
244         };
245
246         debug!("simplifying branch {:?}", terminator);
247         terminator.kind = TerminatorKind::Goto { target: first_succ };
248         true
249     }
250
251     fn strip_nops(&mut self) {
252         for blk in self.basic_blocks.iter_mut() {
253             blk.statements
254                 .retain(|stmt| if let StatementKind::Nop = stmt.kind { false } else { true })
255         }
256     }
257 }
258
259 pub fn remove_dead_blocks(body: &mut BodyAndCache<'_>) {
260     let mut seen = BitSet::new_empty(body.basic_blocks().len());
261     for (bb, _) in traversal::preorder(body) {
262         seen.insert(bb.index());
263     }
264
265     let basic_blocks = body.basic_blocks_mut();
266
267     let num_blocks = basic_blocks.len();
268     let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
269     let mut used_blocks = 0;
270     for alive_index in seen.iter() {
271         replacements[alive_index] = BasicBlock::new(used_blocks);
272         if alive_index != used_blocks {
273             // Swap the next alive block data with the current available slot. Since
274             // alive_index is non-decreasing this is a valid operation.
275             basic_blocks.raw.swap(alive_index, used_blocks);
276         }
277         used_blocks += 1;
278     }
279     basic_blocks.raw.truncate(used_blocks);
280
281     for block in basic_blocks {
282         for target in block.terminator_mut().successors_mut() {
283             *target = replacements[target.index()];
284         }
285     }
286 }
287
288 pub struct SimplifyLocals;
289
290 impl<'tcx> MirPass<'tcx> for SimplifyLocals {
291     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
292         trace!("running SimplifyLocals on {:?}", source);
293         let locals = {
294             let read_only_cache = read_only!(body);
295             let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()), body };
296             marker.visit_body(read_only_cache);
297             // Return pointer and arguments are always live
298             marker.locals.insert(RETURN_PLACE);
299             for arg in body.args_iter() {
300                 marker.locals.insert(arg);
301             }
302
303             marker.locals
304         };
305
306         let map = make_local_map(&mut body.local_decls, locals);
307         // Update references to all vars and tmps now
308         LocalUpdater { map, tcx }.visit_body(body);
309         body.local_decls.shrink_to_fit();
310     }
311 }
312
313 /// Construct the mapping while swapping out unused stuff out from the `vec`.
314 fn make_local_map<V>(
315     vec: &mut IndexVec<Local, V>,
316     mask: BitSet<Local>,
317 ) -> IndexVec<Local, Option<Local>> {
318     let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*vec);
319     let mut used = Local::new(0);
320     for alive_index in mask.iter() {
321         map[alive_index] = Some(used);
322         if alive_index != used {
323             vec.swap(alive_index, used);
324         }
325         used.increment_by(1);
326     }
327     vec.truncate(used.index());
328     map
329 }
330
331 struct DeclMarker<'a, 'tcx> {
332     pub locals: BitSet<Local>,
333     pub body: &'a Body<'tcx>,
334 }
335
336 impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
337     fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
338         // Ignore storage markers altogether, they get removed along with their otherwise unused
339         // decls.
340         // FIXME: Extend this to all non-uses.
341         if ctx.is_storage_marker() {
342             return;
343         }
344
345         // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many
346         // of these locals. However, if the local is still needed, then it will be referenced in
347         // another place and we'll mark it as being used there.
348         if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store)
349             || ctx == PlaceContext::MutatingUse(MutatingUseContext::Projection)
350         {
351             let block = &self.body.basic_blocks()[location.block];
352             if location.statement_index != block.statements.len() {
353                 let stmt = &block.statements[location.statement_index];
354
355                 if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) =
356                     &stmt.kind
357                 {
358                     match c.literal.val {
359                         // Keep assignments from unevaluated constants around, since the evaluation
360                         // may report errors, even if the use of the constant is dead code.
361                         ty::ConstKind::Unevaluated(..) => {}
362                         _ => {
363                             if !p.is_indirect() {
364                                 trace!("skipping store of const value {:?} to {:?}", c, p);
365                                 return;
366                             }
367                         }
368                     }
369                 }
370             }
371         }
372
373         self.locals.insert(*local);
374     }
375 }
376
377 struct LocalUpdater<'tcx> {
378     map: IndexVec<Local, Option<Local>>,
379     tcx: TyCtxt<'tcx>,
380 }
381
382 impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
383     fn tcx(&self) -> TyCtxt<'tcx> {
384         self.tcx
385     }
386
387     fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
388         // Remove unnecessary StorageLive and StorageDead annotations.
389         data.statements.retain(|stmt| match &stmt.kind {
390             StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => self.map[*l].is_some(),
391             StatementKind::Assign(box (place, _)) => self.map[place.local].is_some(),
392             _ => true,
393         });
394         self.super_basic_block_data(block, data);
395     }
396
397     fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
398         *l = self.map[*l].unwrap();
399     }
400
401     fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option<PlaceElem<'tcx>> {
402         match elem {
403             PlaceElem::Index(local) => Some(PlaceElem::Index(self.map[*local].unwrap())),
404             _ => None,
405         }
406     }
407 }