]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/liveness.rs
Auto merge of #66828 - GuillaumeGomez:less-minification, r=kinnison
[rust.git] / src / librustc_mir / util / liveness.rs
1 //! Liveness analysis which computes liveness of MIR local variables at the boundary of basic
2 //! blocks.
3 //!
4 //! This analysis considers references as being used only at the point of the
5 //! borrow. This means that this does not track uses because of references that
6 //! already exist:
7 //!
8 //! ```rust
9 //! fn foo() {
10 //!     x = 0;
11 //!     // `x` is live here ...
12 //!     GLOBAL = &x: *const u32;
13 //!     // ... but not here, even while it can be accessed through `GLOBAL`.
14 //!     foo();
15 //!     x = 1;
16 //!     // `x` is live again here, because it is assigned to `OTHER_GLOBAL`.
17 //!     OTHER_GLOBAL = &x: *const u32;
18 //!     // ...
19 //! }
20 //! ```
21 //!
22 //! This means that users of this analysis still have to check whether
23 //! pre-existing references can be used to access the value (e.g., at movable
24 //! generator yield points, all pre-existing references are invalidated, so this
25 //! doesn't matter).
26
27 use rustc::mir::visit::{
28     PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext, NonUseContext,
29 };
30 use rustc::mir::Local;
31 use rustc::mir::*;
32 use rustc::ty::{self, TyCtxt};
33 use rustc_index::bit_set::BitSet;
34 use rustc_index::vec::{Idx, IndexVec};
35 use rustc_data_structures::work_queue::WorkQueue;
36 use std::fs;
37 use std::io::{self, Write};
38 use std::path::{Path, PathBuf};
39 use crate::transform::MirSource;
40 use crate::util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
41
42 pub type LiveVarSet = BitSet<Local>;
43
44 /// This gives the result of the liveness analysis at the boundary of
45 /// basic blocks.
46 ///
47 /// The `V` type defines the set of variables that we computed
48 /// liveness for. This is often `Local`, in which case we computed
49 /// liveness for all variables -- but it can also be some other type,
50 /// which indicates a subset of the variables within the graph.
51 pub struct LivenessResult {
52     /// Live variables on exit to each basic block. This is equal to
53     /// the union of the `ins` for each successor.
54     pub outs: IndexVec<BasicBlock, LiveVarSet>,
55 }
56
57 /// Computes which local variables are live within the given function
58 /// `mir`, including drops.
59 pub fn liveness_of_locals(
60     body: ReadOnlyBodyCache<'_, '_>,
61 ) -> LivenessResult {
62     let num_live_vars = body.local_decls.len();
63
64     let def_use: IndexVec<_, DefsUses> = body
65         .basic_blocks()
66         .iter()
67         .map(|b| block(b, num_live_vars))
68         .collect();
69
70     let mut outs: IndexVec<_, LiveVarSet> = body
71         .basic_blocks()
72         .indices()
73         .map(|_| LiveVarSet::new_empty(num_live_vars))
74         .collect();
75
76     let mut bits = LiveVarSet::new_empty(num_live_vars);
77
78     // The dirty queue contains the set of basic blocks whose entry sets have changed since they
79     // were last processed. At the start of the analysis, we initialize the queue in post-order to
80     // make it more likely that the entry set for a given basic block will have the effects of all
81     // its successors in the CFG applied before it is processed.
82     //
83     // FIXME(ecstaticmorse): Reverse post-order on the reverse CFG may generate a better iteration
84     // order when cycles are present, but the overhead of computing the reverse CFG may outweigh
85     // any benefits. Benchmark this and find out.
86     let mut dirty_queue: WorkQueue<BasicBlock>
87         = WorkQueue::with_none(body.basic_blocks().len());
88     for (bb, _) in traversal::postorder(&body) {
89         dirty_queue.insert(bb);
90     }
91
92     // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will
93     // be processed after the ones added above.
94     for bb in body.basic_blocks().indices() {
95         dirty_queue.insert(bb);
96     }
97
98     let predecessors = body.predecessors();
99
100     while let Some(bb) = dirty_queue.pop() {
101         // bits = use ∪ (bits - def)
102         bits.overwrite(&outs[bb]);
103         def_use[bb].apply(&mut bits);
104
105         // `bits` now contains the live variables on entry. Therefore,
106         // add `bits` to the `out` set for each predecessor; if those
107         // bits were not already present, then enqueue the predecessor
108         // as dirty.
109         //
110         // (note that `union` returns true if the `self` set changed)
111         for &pred_bb in &predecessors[bb] {
112             if outs[pred_bb].union(&bits) {
113                 dirty_queue.insert(pred_bb);
114             }
115         }
116     }
117
118     LivenessResult { outs }
119 }
120
121 #[derive(Eq, PartialEq, Clone)]
122 pub enum DefUse {
123     Def,
124     Use,
125     Drop,
126 }
127
128 pub fn categorize(context: PlaceContext) -> Option<DefUse> {
129     match context {
130         ///////////////////////////////////////////////////////////////////////////
131         // DEFS
132
133         PlaceContext::MutatingUse(MutatingUseContext::Store) |
134
135         // This is potentially both a def and a use...
136         PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
137
138         // We let Call define the result in both the success and
139         // unwind cases. This is not really correct, however it
140         // does not seem to be observable due to the way that we
141         // generate MIR. To do things properly, we would apply
142         // the def in call only to the input from the success
143         // path and not the unwind path. -nmatsakis
144         PlaceContext::MutatingUse(MutatingUseContext::Call) |
145
146         // Storage live and storage dead aren't proper defines, but we can ignore
147         // values that come before them.
148         PlaceContext::NonUse(NonUseContext::StorageLive) |
149         PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def),
150
151         ///////////////////////////////////////////////////////////////////////////
152         // REGULAR USES
153         //
154         // These are uses that occur *outside* of a drop. For the
155         // purposes of NLL, these are special in that **all** the
156         // lifetimes appearing in the variable must be live for each regular use.
157
158         PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) |
159         PlaceContext::MutatingUse(MutatingUseContext::Projection) |
160
161         // Borrows only consider their local used at the point of the borrow.
162         // This won't affect the results since we use this analysis for generators
163         // and we only care about the result at suspension points. Borrows cannot
164         // cross suspension points so this behavior is unproblematic.
165         PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
166         PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
167         PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
168         PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
169
170         PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
171         PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
172         PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
173         PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
174         PlaceContext::MutatingUse(MutatingUseContext::Retag) =>
175             Some(DefUse::Use),
176
177         ///////////////////////////////////////////////////////////////////////////
178         // DROP USES
179         //
180         // These are uses that occur in a DROP (a MIR drop, not a
181         // call to `std::mem::drop()`). For the purposes of NLL,
182         // uses in drop are special because `#[may_dangle]`
183         // attributes can affect whether lifetimes must be live.
184
185         PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
186             Some(DefUse::Drop),
187
188         // Debug info is neither def nor use.
189         PlaceContext::NonUse(NonUseContext::VarDebugInfo) => None,
190     }
191 }
192
193 struct DefsUsesVisitor
194 {
195     defs_uses: DefsUses,
196 }
197
198 #[derive(Eq, PartialEq, Clone)]
199 struct DefsUses {
200     defs: LiveVarSet,
201     uses: LiveVarSet,
202 }
203
204 impl DefsUses {
205     fn apply(&self, bits: &mut LiveVarSet) -> bool {
206         bits.subtract(&self.defs) | bits.union(&self.uses)
207     }
208
209     fn add_def(&mut self, index: Local) {
210         // If it was used already in the block, remove that use
211         // now that we found a definition.
212         //
213         // Example:
214         //
215         //     // Defs = {X}, Uses = {}
216         //     X = 5
217         //     // Defs = {}, Uses = {X}
218         //     use(X)
219         self.uses.remove(index);
220         self.defs.insert(index);
221     }
222
223     fn add_use(&mut self, index: Local) {
224         // Inverse of above.
225         //
226         // Example:
227         //
228         //     // Defs = {}, Uses = {X}
229         //     use(X)
230         //     // Defs = {X}, Uses = {}
231         //     X = 5
232         //     // Defs = {}, Uses = {X}
233         //     use(X)
234         self.defs.remove(index);
235         self.uses.insert(index);
236     }
237 }
238
239 impl<'tcx> Visitor<'tcx> for DefsUsesVisitor
240 {
241     fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
242         match categorize(context) {
243             Some(DefUse::Def) => self.defs_uses.add_def(local),
244             Some(DefUse::Use) | Some(DefUse::Drop) => self.defs_uses.add_use(local),
245             _ => (),
246         }
247     }
248 }
249
250 fn block(
251     b: &BasicBlockData<'_>,
252     locals: usize,
253 ) -> DefsUses {
254     let mut visitor = DefsUsesVisitor {
255         defs_uses: DefsUses {
256             defs: LiveVarSet::new_empty(locals),
257             uses: LiveVarSet::new_empty(locals),
258         },
259     };
260
261     let dummy_location = Location {
262         block: BasicBlock::new(0),
263         statement_index: 0,
264     };
265
266     // Visit the various parts of the basic block in reverse. If we go
267     // forward, the logic in `add_def` and `add_use` would be wrong.
268     visitor.visit_terminator(b.terminator(), dummy_location);
269     for statement in b.statements.iter().rev() {
270         visitor.visit_statement(statement, dummy_location);
271     }
272
273     visitor.defs_uses
274 }
275
276 pub fn dump_mir<'tcx>(
277     tcx: TyCtxt<'tcx>,
278     pass_name: &str,
279     source: MirSource<'tcx>,
280     body: &Body<'tcx>,
281     result: &LivenessResult,
282 ) {
283     if !dump_enabled(tcx, pass_name, source) {
284         return;
285     }
286     let node_path = ty::print::with_forced_impl_filename_line(|| {
287         // see notes on #41697 below
288         tcx.def_path_str(source.def_id())
289     });
290     dump_matched_mir_node(tcx, pass_name, &node_path, source, body, result);
291 }
292
293 fn dump_matched_mir_node<'tcx>(
294     tcx: TyCtxt<'tcx>,
295     pass_name: &str,
296     node_path: &str,
297     source: MirSource<'tcx>,
298     body: &Body<'tcx>,
299     result: &LivenessResult,
300 ) {
301     let mut file_path = PathBuf::new();
302     file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
303     let item_id = tcx.hir().as_local_hir_id(source.def_id()).unwrap();
304     let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name);
305     file_path.push(&file_name);
306     let _ = fs::File::create(&file_path).and_then(|mut file| {
307         writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
308         writeln!(file, "// source = {:?}", source)?;
309         writeln!(file, "// pass_name = {}", pass_name)?;
310         writeln!(file, "")?;
311         write_mir_fn(tcx, source, body, &mut file, result)?;
312         Ok(())
313     });
314 }
315
316 pub fn write_mir_fn<'tcx>(
317     tcx: TyCtxt<'tcx>,
318     src: MirSource<'tcx>,
319     body: &Body<'tcx>,
320     w: &mut dyn Write,
321     result: &LivenessResult,
322 ) -> io::Result<()> {
323     write_mir_intro(tcx, src, body, w)?;
324     for block in body.basic_blocks().indices() {
325         let print = |w: &mut dyn Write, prefix, result: &IndexVec<BasicBlock, LiveVarSet>| {
326             let live: Vec<String> = result[block]
327                 .iter()
328                 .map(|local| format!("{:?}", local))
329                 .collect();
330             writeln!(w, "{} {{{}}}", prefix, live.join(", "))
331         };
332         write_basic_block(tcx, block, body, &mut |_, _| Ok(()), w)?;
333         print(w, "   ", &result.outs)?;
334         if block.index() + 1 != body.basic_blocks().len() {
335             writeln!(w, "")?;
336         }
337     }
338
339     writeln!(w, "}}")?;
340     Ok(())
341 }