extend liveness to compute intrablock liveness and add unit tests

This commit is contained in:
Niko Matsakis 2017-10-24 13:32:00 -04:00
parent ea03a43fe6
commit 1f06ba486f
5 changed files with 134 additions and 13 deletions

View file

@ -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);
visitor.visit_mir(mir);
let liveness = liveness::liveness_of_locals(&renumbered_mir);
let liveness = liveness::liveness_of_locals(mir);
mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
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);
})
}
}

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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