Skip to content

Commit 9363342

Browse files
committed
Auto merge of #51896 - nikomatsakis:nll-liveness-dirty-list, r=Zoxc
introduce dirty list to liveness, eliminate `ins` vector At least in my measurements, this seems to knock much of the liveness computation off the profile. r? @Zoxc cc @nnethercote
2 parents b58b721 + 78ea952 commit 9363342

File tree

10 files changed

+120
-39
lines changed

10 files changed

+120
-39
lines changed

src/librustc_data_structures/indexed_set.rs

+6
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,20 @@ impl<T: Idx> IdxSet<T> {
239239
self.words_mut().clone_from_slice(other.words());
240240
}
241241

242+
/// Set `self = self | other` and return true if `self` changed
243+
/// (i.e., if new bits were added).
242244
pub fn union(&mut self, other: &IdxSet<T>) -> bool {
243245
bitwise(self.words_mut(), other.words(), &Union)
244246
}
245247

248+
/// Set `self = self - other` and return true if `self` changed.
249+
/// (i.e., if any bits were removed).
246250
pub fn subtract(&mut self, other: &IdxSet<T>) -> bool {
247251
bitwise(self.words_mut(), other.words(), &Subtract)
248252
}
249253

254+
/// Set `self = self & other` and return true if `self` changed.
255+
/// (i.e., if any bits were removed).
250256
pub fn intersect(&mut self, other: &IdxSet<T>) -> bool {
251257
bitwise(self.words_mut(), other.words(), &Intersect)
252258
}

src/librustc_data_structures/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub mod sync;
7777
pub mod owning_ref;
7878
pub mod tiny_list;
7979
pub mod sorted_map;
80+
pub mod work_queue;
8081

8182
pub struct OnDrop<F: Fn()>(pub F);
8283

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use indexed_set::IdxSetBuf;
12+
use indexed_vec::Idx;
13+
use std::collections::VecDeque;
14+
15+
/// A work queue is a handy data structure for tracking work left to
16+
/// do. (For example, basic blocks left to process.) It is basically a
17+
/// de-duplicating queue; so attempting to insert X if X is already
18+
/// enqueued has no effect. This implementation assumes that the
19+
/// elements are dense indices, so it can allocate the queue to size
20+
/// and also use a bit set to track occupancy.
21+
pub struct WorkQueue<T: Idx> {
22+
deque: VecDeque<T>,
23+
set: IdxSetBuf<T>,
24+
}
25+
26+
impl<T: Idx> WorkQueue<T> {
27+
/// Create a new work queue with all the elements from (0..len).
28+
#[inline]
29+
pub fn with_all(len: usize) -> Self {
30+
WorkQueue {
31+
deque: (0..len).map(T::new).collect(),
32+
set: IdxSetBuf::new_filled(len),
33+
}
34+
}
35+
36+
/// Create a new work queue that starts empty, where elements range from (0..len).
37+
#[inline]
38+
pub fn with_none(len: usize) -> Self {
39+
WorkQueue {
40+
deque: VecDeque::with_capacity(len),
41+
set: IdxSetBuf::new_empty(len),
42+
}
43+
}
44+
45+
/// Attempt to enqueue `element` in the work queue. Returns false if it was already present.
46+
#[inline]
47+
pub fn insert(&mut self, element: T) -> bool {
48+
if self.set.add(&element) {
49+
self.deque.push_back(element);
50+
true
51+
} else {
52+
false
53+
}
54+
}
55+
56+
/// Attempt to enqueue `element` in the work queue. Returns false if it was already present.
57+
#[inline]
58+
pub fn pop(&mut self) -> Option<T> {
59+
if let Some(element) = self.deque.pop_front() {
60+
self.set.remove(&element);
61+
Some(element)
62+
} else {
63+
None
64+
}
65+
}
66+
67+
/// True if nothing is enqueued.
68+
#[inline]
69+
pub fn is_empty(&self) -> bool {
70+
self.deque.is_empty()
71+
}
72+
}

src/librustc_mir/borrow_check/nll/mod.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,6 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
263263
}
264264
}
265265

266-
// Before each basic block, dump out the values
267-
// that are live on entry to the basic block.
268-
PassWhere::BeforeBlock(bb) => {
269-
let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]);
270-
writeln!(out, " | Live variables on entry to {:?}: {}", bb, s)?;
271-
}
272-
273266
PassWhere::BeforeLocation(location) => {
274267
let s = live_variable_set(
275268
&regular_liveness_per_location[&location],
@@ -285,7 +278,14 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
285278
)?;
286279
}
287280

288-
PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
281+
// After each basic block, dump out the values
282+
// that are live on exit from the basic block.
283+
PassWhere::AfterTerminator(bb) => {
284+
let s = live_variable_set(&liveness.regular.outs[bb], &liveness.drop.outs[bb]);
285+
writeln!(out, " | Live variables on exit from {:?}: {}", bb, s)?;
286+
}
287+
288+
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
289289
}
290290
Ok(())
291291
},

src/librustc_mir/util/liveness.rs

+22-26
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc::mir::*;
3737
use rustc::mir::visit::{PlaceContext, Visitor};
3838
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
3939
use rustc_data_structures::indexed_set::IdxSetBuf;
40+
use rustc_data_structures::work_queue::WorkQueue;
4041
use util::pretty::{dump_enabled, write_basic_block, write_mir_intro};
4142
use rustc::ty::item_path;
4243
use rustc::mir::visit::MirVisitable;
@@ -55,9 +56,6 @@ pub struct LivenessResult {
5556
/// Liveness mode in use when these results were computed.
5657
pub mode: LivenessMode,
5758

58-
/// Live variables on entry to each basic block.
59-
pub ins: IndexVec<BasicBlock, LocalSet>,
60-
6159
/// Live variables on exit to each basic block. This is equal to
6260
/// the union of the `ins` for each successor.
6361
pub outs: IndexVec<BasicBlock, LocalSet>,
@@ -124,37 +122,38 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> Liveness
124122
.map(|b| block(mode, b, locals))
125123
.collect();
126124

127-
let mut ins: IndexVec<_, _> = mir.basic_blocks()
125+
let mut outs: IndexVec<_, _> = mir.basic_blocks()
128126
.indices()
129127
.map(|_| LocalSet::new_empty(locals))
130128
.collect();
131-
let mut outs = ins.clone();
132129

133-
let mut changed = true;
134130
let mut bits = LocalSet::new_empty(locals);
135-
while changed {
136-
changed = false;
137-
138-
for b in mir.basic_blocks().indices().rev() {
139-
// outs[b] = ∪ {ins of successors}
140-
bits.clear();
141-
for &successor in mir.basic_blocks()[b].terminator().successors() {
142-
bits.union(&ins[successor]);
143-
}
144-
outs[b].overwrite(&bits);
145131

146-
// bits = use ∪ (bits - def)
147-
def_use[b].apply(&mut bits);
132+
// queue of things that need to be re-processed, and a set containing
133+
// the things currently in the queue
134+
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_all(mir.basic_blocks().len());
135+
136+
let predecessors = mir.predecessors();
148137

149-
// update bits on entry and flag if they have changed
150-
if ins[b] != bits {
151-
ins[b].overwrite(&bits);
152-
changed = true;
138+
while let Some(bb) = dirty_queue.pop() {
139+
// bits = use ∪ (bits - def)
140+
bits.overwrite(&outs[bb]);
141+
def_use[bb].apply(&mut bits);
142+
143+
// `bits` now contains the live variables on entry. Therefore,
144+
// add `bits` to the `out` set for each predecessor; if those
145+
// bits were not already present, then enqueue the predecessor
146+
// as dirty.
147+
//
148+
// (note that `union` returns true if the `self` set changed)
149+
for &pred_bb in &predecessors[bb] {
150+
if outs[pred_bb].union(&bits) {
151+
dirty_queue.insert(pred_bb);
153152
}
154153
}
155154
}
156155

157-
LivenessResult { mode, ins, outs }
156+
LivenessResult { mode, outs }
158157
}
159158

160159
impl LivenessResult {
@@ -195,8 +194,6 @@ impl LivenessResult {
195194
statement_defs_uses.apply(&mut bits);
196195
callback(statement_location, &bits);
197196
}
198-
199-
assert_eq!(bits, self.ins[block]);
200197
}
201198

202199
fn defs_uses<'tcx, V>(&self, mir: &Mir<'tcx>, location: Location, thing: &V) -> DefsUses
@@ -438,7 +435,6 @@ pub fn write_mir_fn<'a, 'tcx>(
438435
.collect();
439436
writeln!(w, "{} {{{}}}", prefix, live.join(", "))
440437
};
441-
print(w, " ", &result.ins)?;
442438
write_basic_block(tcx, block, mir, &mut |_, _| Ok(()), w)?;
443439
print(w, " ", &result.outs)?;
444440
if block.index() + 1 != mir.basic_blocks().len() {

src/librustc_mir/util/pretty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ pub enum PassWhere {
4444

4545
/// We just dumped the given statement or terminator.
4646
AfterLocation(Location),
47+
48+
/// We just dumped the terminator for a block but not the closing `}`.
49+
AfterTerminator(BasicBlock),
4750
}
4851

4952
/// If the session is properly configured, dumps a human-readable
@@ -351,6 +354,7 @@ where
351354
})?;
352355

353356
extra_data(PassWhere::AfterLocation(current_location), w)?;
357+
extra_data(PassWhere::AfterTerminator(block), w)?;
354358

355359
writeln!(w, "{}}}", INDENT)
356360
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ fn main() {
2626
//
2727
// END RUST SOURCE
2828
// START rustc.main.nll.0.mir
29-
// | Live variables on entry to bb0: []
3029
// bb0: {
3130
// | Live variables on entry to bb0[0]: []
3231
// StorageLive(_1);
3332
// | Live variables on entry to bb0[1]: []
3433
// _1 = const <std::boxed::Box<T>>::new(const 22usize) -> [return: bb2, unwind: bb1];
34+
// | Live variables on exit from bb0: [_1 (drop)]
3535
// }
3636
// END rustc.main.nll.0.mir
3737
// START rustc.main.nll.0.mir
38-
// | Live variables on entry to bb2: [_1 (drop)]
3938
// bb2: {
4039
// | Live variables on entry to bb2[0]: [_1 (drop)]
4140
// StorageLive(_2);
4241
// | Live variables on entry to bb2[1]: [_1 (drop)]
4342
// _2 = const can_panic() -> [return: bb3, unwind: bb4];
43+
// | Live variables on exit from bb2: [_1 (drop), _2]
4444
// }
4545
// END rustc.main.nll.0.mir

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ fn main() {
2525

2626
// END RUST SOURCE
2727
// START rustc.main.nll.0.mir
28-
// | Live variables on entry to bb3: []
2928
// bb3: {
3029
// | Live variables on entry to bb3[0]: []
3130
// _1 = const 55usize;
@@ -37,5 +36,6 @@ fn main() {
3736
// _4 = _1;
3837
// | Live variables on entry to bb3[4]: [_4]
3938
// _3 = const use_x(move _4) -> [return: bb4, unwind: bb1];
39+
// | Live variables on exit from bb3: [_3]
4040
// }
4141
// END rustc.main.nll.0.mir

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ fn main() {
2929

3030
// END RUST SOURCE
3131
// START rustc.main.nll.0.mir
32-
// | Live variables on entry to bb3: [_1]
3332
// bb3: {
3433
// | Live variables on entry to bb3[0]: [_1]
3534
// StorageLive(_4);
3635
// | Live variables on entry to bb3[1]: [_1]
3736
// _4 = _1;
3837
// | Live variables on entry to bb3[2]: [_4]
3938
// _3 = const make_live(move _4) -> [return: bb5, unwind: bb1];
39+
// | Live variables on exit from bb3: []
4040
// }
4141
// END rustc.main.nll.0.mir
4242
// START rustc.main.nll.0.mir
43-
// | Live variables on entry to bb4: []
4443
// bb4: {
4544
// | Live variables on entry to bb4[0]: []
4645
// _5 = const make_dead() -> [return: bb6, unwind: bb1];
46+
// | Live variables on exit from bb4: []
4747
// }
4848
// END rustc.main.nll.0.mir

src/test/mir-opt/nll/region-liveness-basic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ fn main() {
4242
// _2 = &'_#2r _1[_3];
4343
// | Live variables on entry to bb2[1]: [_2]
4444
// switchInt(const true) -> [false: bb4, otherwise: bb3];
45+
// | Live variables on exit from bb2: [_2]
4546
// }
4647
// END rustc.main.nll.0.mir
4748
// START rustc.main.nll.0.mir
@@ -52,5 +53,6 @@ fn main() {
5253
// _7 = (*_2);
5354
// | Live variables on entry to bb3[2]: [_7]
5455
// _6 = const use_x(move _7) -> [return: bb5, unwind: bb1];
56+
// | Live variables on exit from bb3: []
5557
// }
5658
// END rustc.main.nll.0.mir

0 commit comments

Comments
 (0)