diff options
| author | Niko Matsakis <niko@alum.mit.edu> | 2017-10-24 13:32:00 -0400 |
|---|---|---|
| committer | Niko Matsakis <niko@alum.mit.edu> | 2017-10-31 12:41:38 -0400 |
| commit | 1f06ba486f9a7ac92aca81ab4960e251646f3b97 (patch) | |
| tree | c09c1d6eed41ee373d5ec700ac98d58e852c4f00 | |
| parent | ea03a43fe67f06a13654dc7ddf89e62ba9dbe2e9 (diff) | |
| download | rust-1f06ba486f9a7ac92aca81ab4960e251646f3b97.tar.gz rust-1f06ba486f9a7ac92aca81ab4960e251646f3b97.zip | |
extend liveness to compute intrablock liveness and add unit tests
| -rw-r--r-- | src/librustc_mir/transform/nll/mod.rs | 44 | ||||
| -rw-r--r-- | src/librustc_mir/util/liveness.rs | 84 | ||||
| -rw-r--r-- | src/test/mir-opt/nll/liveness-call-subtlety.rs | 12 | ||||
| -rw-r--r-- | src/test/mir-opt/nll/liveness-drop-intra-block.rs | 5 | ||||
| -rw-r--r-- | src/test/mir-opt/nll/liveness-interblock.rs | 6 |
5 files changed, 136 insertions, 15 deletions
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index ebc241e44ff..fc42fd42b15 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -16,7 +16,7 @@ use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::visit::{MutVisitor, Lookup}; use rustc::mir::transform::{MirPass, MirSource}; use rustc::infer::{self as rustc_infer, InferCtxt}; -use rustc::util::nodemap::FxHashSet; +use rustc::util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use syntax_pos::DUMMY_SP; use std::collections::HashMap; @@ -144,21 +144,34 @@ impl MirPass for NLL { fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, source: MirSource, - mir: &mut Mir<'tcx>) { + input_mir: &mut Mir<'tcx>) { if !tcx.sess.opts.debugging_opts.nll { return; } tcx.infer_ctxt().enter(|infcx| { // Clone mir so we can mutate it without disturbing the rest of the compiler - let mut renumbered_mir = mir.clone(); + let mir = &mut input_mir.clone(); let mut visitor = NLLVisitor::new(&infcx); - visitor.visit_mir(&mut renumbered_mir); - - let liveness = liveness::liveness_of_locals(&renumbered_mir); - - mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| { + visitor.visit_mir(mir); + + let liveness = liveness::liveness_of_locals(mir); + + let liveness_per_location: FxHashMap<_, _> = + mir + .basic_blocks() + .indices() + .flat_map(|bb| { + let mut results = vec![]; + liveness.simulate_block(&mir, bb, |location, local_set| { + results.push((location, local_set.clone())); + }); + results + }) + .collect(); + + mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| { match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => { @@ -177,7 +190,18 @@ impl MirPass for NLL { } } - PassWhere::InCFG(_) => { } + PassWhere::InCFG(location) => { + let local_set = &liveness_per_location[&location]; + let mut string = String::new(); + for local in local_set.iter() { + string.push_str(&format!(", {:?}", local)); + } + if !string.is_empty() { + writeln!(out, " | Live variables here: [{}]", &string[2..])?; + } else { + writeln!(out, " | Live variables here: []")?; + } + } PassWhere::AfterCFG => { } } @@ -185,7 +209,7 @@ impl MirPass for NLL { }); let (_lookup_map, regions) = visitor.into_results(); let mut inference_context = InferenceContext::new(regions); - inference_context.solve(&infcx, &renumbered_mir); + inference_context.solve(&infcx, mir); }) } } diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs index d3a10225cdc..7e4529e9b5e 100644 --- a/src/librustc_mir/util/liveness.rs +++ b/src/librustc_mir/util/liveness.rs @@ -214,6 +214,90 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult { } } +impl LivenessResult { + /// Walks backwards through the statements/terminator in the given + /// basic block `block`. At each point within `block`, invokes + /// the callback `op` with the current location and the set of + /// variables that are live on entry to that location. + pub fn simulate_block<'tcx, OP>(&self, + mir: &Mir<'tcx>, + block: BasicBlock, + mut callback: OP) + where OP: FnMut(Location, &LocalSet) + { + let data = &mir[block]; + + // Get a copy of the bits on exit from the block. + let mut bits = self.outs[block].clone(); + + // Start with the maximal statement index -- i.e., right before + // the terminator executes. + let mut statement_index = data.statements.len(); + + // Compute liveness right before terminator and invoke callback. + let terminator_location = Location { block, statement_index }; + let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator); + terminator_defs_uses.apply(&mut bits); + callback(terminator_location, &bits); + + // Compute liveness before each statement (in rev order) and invoke callback. + for statement in data.statements.iter().rev() { + statement_index -= 1; + let statement_location = Location { block, statement_index }; + let statement_defs_uses = self.defs_uses(mir, statement_location, statement); + statement_defs_uses.apply(&mut bits); + callback(statement_location, &bits); + } + + assert_eq!(bits, self.ins[block]); + } + + fn defs_uses<'tcx, V>(&self, + mir: &Mir<'tcx>, + location: Location, + thing: &V) + -> DefsUses + where V: MirVisitable<'tcx>, + { + let locals = mir.local_decls.len(); + let mut visitor = DefsUses { + defs: LocalSet::new_empty(locals), + uses: LocalSet::new_empty(locals), + }; + + // Visit the various parts of the basic block in reverse. If we go + // forward, the logic in `add_def` and `add_use` would be wrong. + thing.apply(location, &mut visitor); + + visitor + } +} + +trait MirVisitable<'tcx> { + fn apply<V>(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx>; +} + +impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> { + fn apply<V>(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx> + { + visitor.visit_statement(location.block, + self, + location) + } +} + +impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> { + fn apply<V>(&self, location: Location, visitor: &mut V) + where V: Visitor<'tcx> + { + visitor.visit_terminator(location.block, + self.as_ref().unwrap(), + location) + } +} + pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pass_name: &str, source: MirSource, diff --git a/src/test/mir-opt/nll/liveness-call-subtlety.rs b/src/test/mir-opt/nll/liveness-call-subtlety.rs index 99ed5fe186e..f0d56db0573 100644 --- a/src/test/mir-opt/nll/liveness-call-subtlety.rs +++ b/src/test/mir-opt/nll/liveness-call-subtlety.rs @@ -28,15 +28,19 @@ fn main() { // START rustc.node12.nll.0.mir // | Variables live on entry to the block bb0: // bb0: { -// StorageLive(_1); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:9: 18:14 -// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:17: 18:29 +// | Live variables here: [] +// StorageLive(_1); +// | Live variables here: [] +// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1; // } // END rustc.node12.nll.0.mir // START rustc.node12.nll.0.mir // | Variables live on entry to the block bb1: // | - _1 // bb1: { -// StorageLive(_2); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 -// _2 = const can_panic() -> [return: bb2, unwind: bb4]; // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20 +// | Live variables here: [_1] +// StorageLive(_2); +// | Live variables here: [_1] +// _2 = const can_panic() -> [return: bb2, unwind: bb4]; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-drop-intra-block.rs b/src/test/mir-opt/nll/liveness-drop-intra-block.rs index 2d7aceb558f..1fac9484bdb 100644 --- a/src/test/mir-opt/nll/liveness-drop-intra-block.rs +++ b/src/test/mir-opt/nll/liveness-drop-intra-block.rs @@ -27,10 +27,15 @@ fn main() { // START rustc.node12.nll.0.mir // | Variables live on entry to the block bb1: // bb1: { +// | Live variables here: [] // _1 = const 55usize; +// | Live variables here: [_1] // StorageLive(_3); +// | Live variables here: [_1] // StorageLive(_4); +// | Live variables here: [_1] // _4 = _1; +// | Live variables here: [_4] // _3 = const use_x(_4) -> bb2; // } // END rustc.node12.nll.0.mir diff --git a/src/test/mir-opt/nll/liveness-interblock.rs b/src/test/mir-opt/nll/liveness-interblock.rs index e3c7ab3cc0c..4380698e704 100644 --- a/src/test/mir-opt/nll/liveness-interblock.rs +++ b/src/test/mir-opt/nll/liveness-interblock.rs @@ -12,7 +12,7 @@ fn cond() -> bool { false } -fn make_live(x: usize) { } +fn make_live(_: usize) { } fn make_dead() { } @@ -32,14 +32,18 @@ fn main() { // | Variables live on entry to the block bb2: // | - _1 // bb2: { +// | Live variables here: [_1] // StorageLive(_4); +// | Live variables here: [_1] // _4 = _1; +// | Live variables here: [_4] // _3 = const make_live(_4) -> bb4; // } // END rustc.node18.nll.0.mir // START rustc.node18.nll.0.mir // | Variables live on entry to the block bb3: // bb3: { +// | Live variables here: [] // _5 = const make_dead() -> bb5; // } // END rustc.node18.nll.0.mir |
