extend liveness to compute intrablock liveness and add unit tests
This commit is contained in:
parent
ea03a43fe6
commit
1f06ba486f
5 changed files with 134 additions and 13 deletions
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue