about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-10-24 13:32:00 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-10-31 12:41:38 -0400
commit1f06ba486f9a7ac92aca81ab4960e251646f3b97 (patch)
treec09c1d6eed41ee373d5ec700ac98d58e852c4f00
parentea03a43fe67f06a13654dc7ddf89e62ba9dbe2e9 (diff)
downloadrust-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.rs44
-rw-r--r--src/librustc_mir/util/liveness.rs84
-rw-r--r--src/test/mir-opt/nll/liveness-call-subtlety.rs12
-rw-r--r--src/test/mir-opt/nll/liveness-drop-intra-block.rs5
-rw-r--r--src/test/mir-opt/nll/liveness-interblock.rs6
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