1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Liveness analysis which computes liveness of MIR local variables at the boundary of basic blocks
13 //! This analysis considers references as being used only at the point of the
14 //! borrow. This means that this does not track uses because of references that
20 //! // `x` is live here
21 //! GLOBAL = &x: *const u32;
22 //! // but not here, even while it can be accessed through `GLOBAL`.
25 //! // `x` is live again here, because it is assigned to `OTHER_GLOBAL`
26 //! OTHER_GLOBAL = &x: *const u32;
31 //! This means that users of this analysis still have to check whether
32 //! pre-existing references can be used to access the value (e.g. at movable
33 //! generator yield points, all pre-existing references are invalidated, so this
37 use rustc::mir::visit::{PlaceContext, Visitor};
38 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
39 use rustc_data_structures::indexed_set::IdxSetBuf;
40 use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
41 use rustc::ty::item_path;
42 use rustc::mir::visit::MirVisitable;
43 use std::path::{Path, PathBuf};
45 use rustc::ty::TyCtxt;
46 use std::io::{self, Write};
47 use transform::MirSource;
49 pub type LocalSet = IdxSetBuf<Local>;
51 /// This gives the result of the liveness analysis at the boundary of
52 /// basic blocks. You can use `simulate_block` to obtain the
53 /// intra-block results.
54 pub struct LivenessResult {
55 /// Liveness mode in use when these results were computed.
56 pub mode: LivenessMode,
58 /// Live variables on entry to each basic block.
59 pub ins: IndexVec<BasicBlock, LocalSet>,
61 /// Live variables on exit to each basic block. This is equal to
62 /// the union of the `ins` for each successor.
63 pub outs: IndexVec<BasicBlock, LocalSet>,
66 #[derive(Copy, Clone, Debug)]
67 pub struct LivenessMode {
68 /// If true, then we will consider "regular uses" of a variable to be live.
69 /// For example, if the user writes `foo(x)`, then this is a regular use of
71 pub include_regular_use: bool,
73 /// If true, then we will consider (implicit) drops of a variable
74 /// to be live. For example, if the user writes `{ let x =
75 /// vec![...]; .. }`, then the drop at the end of the block is an
78 /// NB. Despite its name, a call like `::std::mem::drop(x)` is
79 /// **not** considered a drop for this purposes, but rather a
81 pub include_drops: bool,
84 /// A combination of liveness results, used in NLL.
85 pub struct LivenessResults {
86 /// Liveness results where a regular use makes a variable X live,
88 pub regular: LivenessResult,
90 /// Liveness results where a drop makes a variable X live,
91 /// but not a regular use.
92 pub drop: LivenessResult,
95 impl LivenessResults {
96 pub fn compute<'tcx>(mir: &Mir<'tcx>) -> LivenessResults {
98 regular: liveness_of_locals(
101 include_regular_use: true,
102 include_drops: false,
106 drop: liveness_of_locals(
109 include_regular_use: false,
117 /// Compute which local variables are live within the given function
118 /// `mir`. The liveness mode `mode` determines what sorts of uses are
119 /// considered to make a variable live (e.g., do drops count?).
120 pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> LivenessResult {
121 let locals = mir.local_decls.len();
122 let def_use: IndexVec<_, _> = mir.basic_blocks()
124 .map(|b| block(mode, b, locals))
127 let mut ins: IndexVec<_, _> = mir.basic_blocks()
129 .map(|_| LocalSet::new_empty(locals))
131 let mut outs = ins.clone();
133 let mut changed = true;
134 let mut bits = LocalSet::new_empty(locals);
138 for b in mir.basic_blocks().indices().rev() {
139 // outs[b] = ∪ {ins of successors}
141 for &successor in mir.basic_blocks()[b].terminator().successors().into_iter() {
142 bits.union(&ins[successor]);
144 outs[b].clone_from(&bits);
146 // bits = use ∪ (bits - def)
147 def_use[b].apply(&mut bits);
149 // update bits on entry and flag if they have changed
151 ins[b].clone_from(&bits);
157 LivenessResult { mode, ins, outs }
160 impl LivenessResult {
161 /// Walks backwards through the statements/terminator in the given
162 /// basic block `block`. At each point within `block`, invokes
163 /// the callback `op` with the current location and the set of
164 /// variables that are live on entry to that location.
165 pub fn simulate_block<'tcx, OP>(&self, mir: &Mir<'tcx>, block: BasicBlock, mut callback: OP)
167 OP: FnMut(Location, &LocalSet),
169 let data = &mir[block];
171 // Get a copy of the bits on exit from the block.
172 let mut bits = self.outs[block].clone();
174 // Start with the maximal statement index -- i.e., right before
175 // the terminator executes.
176 let mut statement_index = data.statements.len();
178 // Compute liveness right before terminator and invoke callback.
179 let terminator_location = Location {
183 let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
184 terminator_defs_uses.apply(&mut bits);
185 callback(terminator_location, &bits);
187 // Compute liveness before each statement (in rev order) and invoke callback.
188 for statement in data.statements.iter().rev() {
189 statement_index -= 1;
190 let statement_location = Location {
194 let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
195 statement_defs_uses.apply(&mut bits);
196 callback(statement_location, &bits);
199 assert_eq!(bits, self.ins[block]);
202 fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
204 V: MirVisitable<'tcx>,
206 let locals = mir.local_decls.len();
207 let mut visitor = DefsUsesVisitor {
209 defs_uses: DefsUses {
210 defs: LocalSet::new_empty(locals),
211 uses: LocalSet::new_empty(locals),
215 // Visit the various parts of the basic block in reverse. If we go
216 // forward, the logic in `add_def` and `add_use` would be wrong.
217 thing.apply(location, &mut visitor);
223 #[derive(Eq, PartialEq, Clone)]
229 pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Option<DefUse> {
231 ///////////////////////////////////////////////////////////////////////////
234 PlaceContext::Store |
236 // This is potentially both a def and a use...
237 PlaceContext::AsmOutput |
239 // We let Call define the result in both the success and
240 // unwind cases. This is not really correct, however it
241 // does not seem to be observable due to the way that we
242 // generate MIR. See the test case
243 // `mir-opt/nll/liveness-call-subtlety.rs`. To do things
244 // properly, we would apply the def in call only to the
245 // input from the success path and not the unwind
249 // Storage live and storage dead aren't proper defines, but we can ignore
250 // values that come before them.
251 PlaceContext::StorageLive |
252 PlaceContext::StorageDead => Some(DefUse::Def),
254 ///////////////////////////////////////////////////////////////////////////
257 // These are uses that occur *outside* of a drop. For the
258 // purposes of NLL, these are special in that **all** the
259 // lifetimes appearing in the variable must be live for each regular use.
261 PlaceContext::Projection(..) |
263 // Borrows only consider their local used at the point of the borrow.
264 // This won't affect the results since we use this analysis for generators
265 // and we only care about the result at suspension points. Borrows cannot
266 // cross suspension points so this behavior is unproblematic.
267 PlaceContext::Borrow { .. } |
269 PlaceContext::Inspect |
272 PlaceContext::Validate => {
273 if mode.include_regular_use {
280 ///////////////////////////////////////////////////////////////////////////
283 // These are uses that occur in a DROP (a MIR drop, not a
284 // call to `std::mem::drop()`). For the purposes of NLL,
285 // uses in drop are special because `#[may_dangle]`
286 // attributes can affect whether lifetimes must be live.
288 PlaceContext::Drop => {
289 if mode.include_drops {
298 struct DefsUsesVisitor {
303 #[derive(Eq, PartialEq, Clone)]
310 fn apply(&self, bits: &mut LocalSet) -> bool {
311 bits.subtract(&self.defs) | bits.union(&self.uses)
314 fn add_def(&mut self, index: Local) {
315 // If it was used already in the block, remove that use
316 // now that we found a definition.
320 // // Defs = {X}, Uses = {}
322 // // Defs = {}, Uses = {X}
324 self.uses.remove(&index);
325 self.defs.add(&index);
328 fn add_use(&mut self, index: Local) {
333 // // Defs = {}, Uses = {X}
335 // // Defs = {X}, Uses = {}
337 // // Defs = {}, Uses = {X}
339 self.defs.remove(&index);
340 self.uses.add(&index);
344 impl<'tcx> Visitor<'tcx> for DefsUsesVisitor {
345 fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) {
346 match categorize(context, self.mode) {
347 Some(DefUse::Def) => {
348 self.defs_uses.add_def(local);
351 Some(DefUse::Use) => {
352 self.defs_uses.add_use(local);
360 fn block<'tcx>(mode: LivenessMode, b: &BasicBlockData<'tcx>, locals: usize) -> DefsUses {
361 let mut visitor = DefsUsesVisitor {
363 defs_uses: DefsUses {
364 defs: LocalSet::new_empty(locals),
365 uses: LocalSet::new_empty(locals),
369 let dummy_location = Location {
370 block: BasicBlock::new(0),
374 // Visit the various parts of the basic block in reverse. If we go
375 // forward, the logic in `add_def` and `add_use` would be wrong.
376 visitor.visit_terminator(BasicBlock::new(0), b.terminator(), dummy_location);
377 for statement in b.statements.iter().rev() {
378 visitor.visit_statement(BasicBlock::new(0), statement, dummy_location);
384 pub fn dump_mir<'a, 'tcx>(
385 tcx: TyCtxt<'a, 'tcx, 'tcx>,
389 result: &LivenessResult,
391 if !dump_enabled(tcx, pass_name, source) {
394 let node_path = item_path::with_forced_impl_filename_line(|| {
395 // see notes on #41697 below
396 tcx.item_path_str(source.def_id)
398 dump_matched_mir_node(tcx, pass_name, &node_path, source, mir, result);
401 fn dump_matched_mir_node<'a, 'tcx>(
402 tcx: TyCtxt<'a, 'tcx, 'tcx>,
407 result: &LivenessResult,
409 let mut file_path = PathBuf::new();
410 file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
411 let item_id = tcx.hir.as_local_node_id(source.def_id).unwrap();
412 let file_name = format!("rustc.node{}{}-liveness.mir", item_id, pass_name);
413 file_path.push(&file_name);
414 let _ = fs::File::create(&file_path).and_then(|mut file| {
415 writeln!(file, "// MIR local liveness analysis for `{}`", node_path)?;
416 writeln!(file, "// source = {:?}", source)?;
417 writeln!(file, "// pass_name = {}", pass_name)?;
419 write_mir_fn(tcx, source, mir, &mut file, result)?;
424 pub fn write_mir_fn<'a, 'tcx>(
425 tcx: TyCtxt<'a, 'tcx, 'tcx>,
429 result: &LivenessResult,
430 ) -> io::Result<()> {
431 write_mir_intro(tcx, src, mir, w)?;
432 for block in mir.basic_blocks().indices() {
433 let print = |w: &mut Write, prefix, result: &IndexVec<BasicBlock, LocalSet>| {
434 let live: Vec<String> = mir.local_decls
436 .filter(|i| result[block].contains(i))
437 .map(|i| format!("{:?}", i))
439 writeln!(w, "{} {{{}}}", prefix, live.join(", "))
441 print(w, " ", &result.ins)?;
442 write_basic_block(tcx, block, mir, &mut |_, _| Ok(()), w)?;
443 print(w, " ", &result.outs)?;
444 if block.index() + 1 != mir.basic_blocks().len() {