Skip to content

Commit 1f06ba4

Browse files
committed
extend liveness to compute intrablock liveness and add unit tests
1 parent ea03a43 commit 1f06ba4

File tree

5 files changed

+136
-15
lines changed

5 files changed

+136
-15
lines changed

src/librustc_mir/transform/nll/mod.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
1616
use rustc::mir::visit::{MutVisitor, Lookup};
1717
use rustc::mir::transform::{MirPass, MirSource};
1818
use rustc::infer::{self as rustc_infer, InferCtxt};
19-
use rustc::util::nodemap::FxHashSet;
19+
use rustc::util::nodemap::{FxHashMap, FxHashSet};
2020
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
2121
use syntax_pos::DUMMY_SP;
2222
use std::collections::HashMap;
@@ -144,21 +144,34 @@ impl MirPass for NLL {
144144
fn run_pass<'a, 'tcx>(&self,
145145
tcx: TyCtxt<'a, 'tcx, 'tcx>,
146146
source: MirSource,
147-
mir: &mut Mir<'tcx>) {
147+
input_mir: &mut Mir<'tcx>) {
148148
if !tcx.sess.opts.debugging_opts.nll {
149149
return;
150150
}
151151

152152
tcx.infer_ctxt().enter(|infcx| {
153153
// Clone mir so we can mutate it without disturbing the rest of the compiler
154-
let mut renumbered_mir = mir.clone();
154+
let mir = &mut input_mir.clone();
155155

156156
let mut visitor = NLLVisitor::new(&infcx);
157-
visitor.visit_mir(&mut renumbered_mir);
158-
159-
let liveness = liveness::liveness_of_locals(&renumbered_mir);
160-
161-
mir_util::dump_mir(tcx, None, "nll", &0, source, mir, |pass_where, out| {
157+
visitor.visit_mir(mir);
158+
159+
let liveness = liveness::liveness_of_locals(mir);
160+
161+
let liveness_per_location: FxHashMap<_, _> =
162+
mir
163+
.basic_blocks()
164+
.indices()
165+
.flat_map(|bb| {
166+
let mut results = vec![];
167+
liveness.simulate_block(&mir, bb, |location, local_set| {
168+
results.push((location, local_set.clone()));
169+
});
170+
results
171+
})
172+
.collect();
173+
174+
mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
162175
match pass_where {
163176
// Before the CFG, dump out the values for each region variable.
164177
PassWhere::BeforeCFG => {
@@ -177,15 +190,26 @@ impl MirPass for NLL {
177190
}
178191
}
179192

180-
PassWhere::InCFG(_) => { }
193+
PassWhere::InCFG(location) => {
194+
let local_set = &liveness_per_location[&location];
195+
let mut string = String::new();
196+
for local in local_set.iter() {
197+
string.push_str(&format!(", {:?}", local));
198+
}
199+
if !string.is_empty() {
200+
writeln!(out, " | Live variables here: [{}]", &string[2..])?;
201+
} else {
202+
writeln!(out, " | Live variables here: []")?;
203+
}
204+
}
181205

182206
PassWhere::AfterCFG => { }
183207
}
184208
Ok(())
185209
});
186210
let (_lookup_map, regions) = visitor.into_results();
187211
let mut inference_context = InferenceContext::new(regions);
188-
inference_context.solve(&infcx, &renumbered_mir);
212+
inference_context.solve(&infcx, mir);
189213
})
190214
}
191215
}

src/librustc_mir/util/liveness.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,90 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>) -> LivenessResult {
214214
}
215215
}
216216

217+
impl LivenessResult {
218+
/// Walks backwards through the statements/terminator in the given
219+
/// basic block `block`. At each point within `block`, invokes
220+
/// the callback `op` with the current location and the set of
221+
/// variables that are live on entry to that location.
222+
pub fn simulate_block<'tcx, OP>(&self,
223+
mir: &Mir<'tcx>,
224+
block: BasicBlock,
225+
mut callback: OP)
226+
where OP: FnMut(Location, &LocalSet)
227+
{
228+
let data = &mir[block];
229+
230+
// Get a copy of the bits on exit from the block.
231+
let mut bits = self.outs[block].clone();
232+
233+
// Start with the maximal statement index -- i.e., right before
234+
// the terminator executes.
235+
let mut statement_index = data.statements.len();
236+
237+
// Compute liveness right before terminator and invoke callback.
238+
let terminator_location = Location { block, statement_index };
239+
let terminator_defs_uses = self.defs_uses(mir, terminator_location, &data.terminator);
240+
terminator_defs_uses.apply(&mut bits);
241+
callback(terminator_location, &bits);
242+
243+
// Compute liveness before each statement (in rev order) and invoke callback.
244+
for statement in data.statements.iter().rev() {
245+
statement_index -= 1;
246+
let statement_location = Location { block, statement_index };
247+
let statement_defs_uses = self.defs_uses(mir, statement_location, statement);
248+
statement_defs_uses.apply(&mut bits);
249+
callback(statement_location, &bits);
250+
}
251+
252+
assert_eq!(bits, self.ins[block]);
253+
}
254+
255+
fn defs_uses<'tcx, V>(&self,
256+
mir: &Mir<'tcx>,
257+
location: Location,
258+
thing: &V)
259+
-> DefsUses
260+
where V: MirVisitable<'tcx>,
261+
{
262+
let locals = mir.local_decls.len();
263+
let mut visitor = DefsUses {
264+
defs: LocalSet::new_empty(locals),
265+
uses: LocalSet::new_empty(locals),
266+
};
267+
268+
// Visit the various parts of the basic block in reverse. If we go
269+
// forward, the logic in `add_def` and `add_use` would be wrong.
270+
thing.apply(location, &mut visitor);
271+
272+
visitor
273+
}
274+
}
275+
276+
trait MirVisitable<'tcx> {
277+
fn apply<V>(&self, location: Location, visitor: &mut V)
278+
where V: Visitor<'tcx>;
279+
}
280+
281+
impl<'tcx> MirVisitable<'tcx> for Statement<'tcx> {
282+
fn apply<V>(&self, location: Location, visitor: &mut V)
283+
where V: Visitor<'tcx>
284+
{
285+
visitor.visit_statement(location.block,
286+
self,
287+
location)
288+
}
289+
}
290+
291+
impl<'tcx> MirVisitable<'tcx> for Option<Terminator<'tcx>> {
292+
fn apply<V>(&self, location: Location, visitor: &mut V)
293+
where V: Visitor<'tcx>
294+
{
295+
visitor.visit_terminator(location.block,
296+
self.as_ref().unwrap(),
297+
location)
298+
}
299+
}
300+
217301
pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
218302
pass_name: &str,
219303
source: MirSource,

src/test/mir-opt/nll/liveness-call-subtlety.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,19 @@ fn main() {
2828
// START rustc.node12.nll.0.mir
2929
// | Variables live on entry to the block bb0:
3030
// bb0: {
31-
// StorageLive(_1); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:18:9: 18:14
32-
// _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
31+
// | Live variables here: []
32+
// StorageLive(_1);
33+
// | Live variables here: []
34+
// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> bb1;
3335
// }
3436
// END rustc.node12.nll.0.mir
3537
// START rustc.node12.nll.0.mir
3638
// | Variables live on entry to the block bb1:
3739
// | - _1
3840
// bb1: {
39-
// StorageLive(_2); // scope 1 at /Users/nmatsakis/versioned/rust-3/src/test/mir-opt/nll/liveness-call-subtlety.rs:19:9: 19:20
40-
// _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
41+
// | Live variables here: [_1]
42+
// StorageLive(_2);
43+
// | Live variables here: [_1]
44+
// _2 = const can_panic() -> [return: bb2, unwind: bb4];
4145
// }
4246
// END rustc.node12.nll.0.mir

src/test/mir-opt/nll/liveness-drop-intra-block.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ fn main() {
2727
// START rustc.node12.nll.0.mir
2828
// | Variables live on entry to the block bb1:
2929
// bb1: {
30+
// | Live variables here: []
3031
// _1 = const 55usize;
32+
// | Live variables here: [_1]
3133
// StorageLive(_3);
34+
// | Live variables here: [_1]
3235
// StorageLive(_4);
36+
// | Live variables here: [_1]
3337
// _4 = _1;
38+
// | Live variables here: [_4]
3439
// _3 = const use_x(_4) -> bb2;
3540
// }
3641
// END rustc.node12.nll.0.mir

src/test/mir-opt/nll/liveness-interblock.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
fn cond() -> bool { false }
1414

15-
fn make_live(x: usize) { }
15+
fn make_live(_: usize) { }
1616

1717
fn make_dead() { }
1818

@@ -32,14 +32,18 @@ fn main() {
3232
// | Variables live on entry to the block bb2:
3333
// | - _1
3434
// bb2: {
35+
// | Live variables here: [_1]
3536
// StorageLive(_4);
37+
// | Live variables here: [_1]
3638
// _4 = _1;
39+
// | Live variables here: [_4]
3740
// _3 = const make_live(_4) -> bb4;
3841
// }
3942
// END rustc.node18.nll.0.mir
4043
// START rustc.node18.nll.0.mir
4144
// | Variables live on entry to the block bb3:
4245
// bb3: {
46+
// | Live variables here: []
4347
// _5 = const make_dead() -> bb5;
4448
// }
4549
// END rustc.node18.nll.0.mir

0 commit comments

Comments
 (0)