]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/util/liveness.rs
New upstream version 1.37.0+dfsg1
[rustc.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_data_structures::bit_set::BitSet;
34 use rustc_data_structures::indexed_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<'tcx>(
60 body: &Body<'tcx>,
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> = WorkQueue::with_none(body.basic_blocks().len());
87 for (bb, _) in traversal::postorder(body) {
88 dirty_queue.insert(bb);
89 }
90
91 // Add blocks which are not reachable from START_BLOCK to the work queue. These blocks will
92 // be processed after the ones added above.
93 for bb in body.basic_blocks().indices() {
94 dirty_queue.insert(bb);
95 }
96
97 let predecessors = body.predecessors();
98
99 while let Some(bb) = dirty_queue.pop() {
100 // bits = use ∪ (bits - def)
101 bits.overwrite(&outs[bb]);
102 def_use[bb].apply(&mut bits);
103
104 // `bits` now contains the live variables on entry. Therefore,
105 // add `bits` to the `out` set for each predecessor; if those
106 // bits were not already present, then enqueue the predecessor
107 // as dirty.
108 //
109 // (note that `union` returns true if the `self` set changed)
110 for &pred_bb in &predecessors[bb] {
111 if outs[pred_bb].union(&bits) {
112 dirty_queue.insert(pred_bb);
113 }
114 }
115 }
116
117 LivenessResult { outs }
118 }
119
120 #[derive(Eq, PartialEq, Clone)]
121 pub enum DefUse {
122 Def,
123 Use,
124 Drop,
125 }
126
127 pub fn categorize(context: PlaceContext) -> Option<DefUse> {
128 match context {
129 ///////////////////////////////////////////////////////////////////////////
130 // DEFS
131
132 PlaceContext::MutatingUse(MutatingUseContext::Store) |
133
134 // This is potentially both a def and a use...
135 PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
136
137 // We let Call define the result in both the success and
138 // unwind cases. This is not really correct, however it
139 // does not seem to be observable due to the way that we
140 // generate MIR. To do things properly, we would apply
141 // the def in call only to the input from the success
142 // path and not the unwind path. -nmatsakis
143 PlaceContext::MutatingUse(MutatingUseContext::Call) |
144
145 // Storage live and storage dead aren't proper defines, but we can ignore
146 // values that come before them.
147 PlaceContext::NonUse(NonUseContext::StorageLive) |
148 PlaceContext::NonUse(NonUseContext::StorageDead) => Some(DefUse::Def),
149
150 ///////////////////////////////////////////////////////////////////////////
151 // REGULAR USES
152 //
153 // These are uses that occur *outside* of a drop. For the
154 // purposes of NLL, these are special in that **all** the
155 // lifetimes appearing in the variable must be live for each regular use.
156
157 PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) |
158 PlaceContext::MutatingUse(MutatingUseContext::Projection) |
159
160 // Borrows only consider their local used at the point of the borrow.
161 // This won't affect the results since we use this analysis for generators
162 // and we only care about the result at suspension points. Borrows cannot
163 // cross suspension points so this behavior is unproblematic.
164 PlaceContext::MutatingUse(MutatingUseContext::Borrow) |
165 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) |
166 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow) |
167 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow) |
168
169 PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) |
170 PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) |
171 PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) |
172 PlaceContext::NonUse(NonUseContext::AscribeUserTy) |
173 PlaceContext::MutatingUse(MutatingUseContext::Retag) =>
174 Some(DefUse::Use),
175
176 ///////////////////////////////////////////////////////////////////////////
177 // DROP USES
178 //
179 // These are uses that occur in a DROP (a MIR drop, not a
180 // call to `std::mem::drop()`). For the purposes of NLL,
181 // uses in drop are special because `#[may_dangle]`
182 // attributes can affect whether lifetimes must be live.
183
184 PlaceContext::MutatingUse(MutatingUseContext::Drop) =>
185 Some(DefUse::Drop),
186 }
187 }
188
189 struct DefsUsesVisitor
190 {
191 defs_uses: DefsUses,
192 }
193
194 #[derive(Eq, PartialEq, Clone)]
195 struct DefsUses {
196 defs: LiveVarSet,
197 uses: LiveVarSet,
198 }
199
200 impl DefsUses {
201 fn apply(&self, bits: &mut LiveVarSet) -> bool {
202 bits.subtract(&self.defs) | bits.union(&self.uses)
203 }
204
205 fn add_def(&mut self, index: Local) {
206 // If it was used already in the block, remove that use
207 // now that we found a definition.
208 //
209 // Example:
210 //
211 // // Defs = {X}, Uses = {}
212 // X = 5
213 // // Defs = {}, Uses = {X}
214 // use(X)
215 self.uses.remove(index);
216 self.defs.insert(index);
217 }
218
219 fn add_use(&mut self, index: Local) {
220 // Inverse of above.
221 //
222 // Example:
223 //
224 // // Defs = {}, Uses = {X}
225 // use(X)
226 // // Defs = {X}, Uses = {}
227 // X = 5
228 // // Defs = {}, Uses = {X}
229 // use(X)
230 self.defs.remove(index);
231 self.uses.insert(index);
232 }
233 }
234
235 impl<'tcx> Visitor<'tcx> for DefsUsesVisitor
236 {
237 fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
238 match categorize(context) {
239 Some(DefUse::Def) => self.defs_uses.add_def(local),
240 Some(DefUse::Use) | Some(DefUse::Drop) => self.defs_uses.add_use(local),
241 _ => (),
242 }
243 }
244 }
245
246 fn block<'tcx>(
247 b: &BasicBlockData<'tcx>,
248 locals: usize,
249 ) -> DefsUses {
250 let mut visitor = DefsUsesVisitor {
251 defs_uses: DefsUses {
252 defs: LiveVarSet::new_empty(locals),
253 uses: LiveVarSet::new_empty(locals),
254 },
255 };
256
257 let dummy_location = Location {
258 block: BasicBlock::new(0),
259 statement_index: 0,
260 };
261
262 // Visit the various parts of the basic block in reverse. If we go
263 // forward, the logic in `add_def` and `add_use` would be wrong.
264 visitor.visit_terminator(b.terminator(), dummy_location);
265 for statement in b.statements.iter().rev() {
266 visitor.visit_statement(statement, dummy_location);
267 }
268
269 visitor.defs_uses
270 }
271
272 pub fn dump_mir<'tcx>(
273 tcx: TyCtxt<'tcx>,
274 pass_name: &str,
275 source: MirSource<'tcx>,
276 body: &Body<'tcx>,
277 result: &LivenessResult,
278 ) {
279 if !dump_enabled(tcx, pass_name, source) {
280 return;
281 }
282 let node_path = ty::print::with_forced_impl_filename_line(|| {
283 // see notes on #41697 below
284 tcx.def_path_str(source.def_id())
285 });
286 dump_matched_mir_node(tcx, pass_name, &node_path, source, body, result);
287 }
288
289 fn dump_matched_mir_node<'tcx>(
290 tcx: TyCtxt<'tcx>,
291 pass_name: &str,
292 node_path: &str,
293 source: MirSource<'tcx>,
294 body: &Body<'tcx>,
295 result: &LivenessResult,
296 ) {
297 let mut file_path = PathBuf::new();
298 file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
299 let item_id = tcx.hir().as_local_hir_id(source.def_id()).unwrap();
300 let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name);
301 file_path.push(&file_name);
302 let _ = fs::File::create(&file_path).and_then(|mut file| {
303 writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
304 writeln!(file, "// source = {:?}", source)?;
305 writeln!(file, "// pass_name = {}", pass_name)?;
306 writeln!(file, "")?;
307 write_mir_fn(tcx, source, body, &mut file, result)?;
308 Ok(())
309 });
310 }
311
312 pub fn write_mir_fn<'tcx>(
313 tcx: TyCtxt<'tcx>,
314 src: MirSource<'tcx>,
315 body: &Body<'tcx>,
316 w: &mut dyn Write,
317 result: &LivenessResult,
318 ) -> io::Result<()> {
319 write_mir_intro(tcx, src, body, w)?;
320 for block in body.basic_blocks().indices() {
321 let print = |w: &mut dyn Write, prefix, result: &IndexVec<BasicBlock, LiveVarSet>| {
322 let live: Vec<String> = result[block]
323 .iter()
324 .map(|local| format!("{:?}", local))
325 .collect();
326 writeln!(w, "{} {{{}}}", prefix, live.join(", "))
327 };
328 write_basic_block(tcx, block, body, &mut |_, _| Ok(()), w)?;
329 print(w, " ", &result.outs)?;
330 if block.index() + 1 != body.basic_blocks().len() {
331 writeln!(w, "")?;
332 }
333 }
334
335 writeln!(w, "}}")?;
336 Ok(())
337 }