-
Notifications
You must be signed in to change notification settings - Fork 13.6k
introduce dirty list to liveness, eliminate ins
vector
#51896
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
388ff03
be0e778
865320f
73f8333
78ea952
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
use indexed_set::IdxSetBuf; | ||
use indexed_vec::Idx; | ||
use std::collections::VecDeque; | ||
|
||
/// A work queue is a handy data structure for tracking work left to | ||
/// do. (For example, basic blocks left to process.) It is basically a | ||
/// de-duplicating queue; so attempting to insert X if X is already | ||
/// enqueued has no effect. This implementation assumes that the | ||
/// elements are dense indices, so it can allocate the queue to size | ||
/// and also use a bit set to track occupancy. | ||
pub struct WorkQueue<T: Idx> { | ||
deque: VecDeque<T>, | ||
set: IdxSetBuf<T>, | ||
} | ||
|
||
impl<T: Idx> WorkQueue<T> { | ||
/// Create a new work queue with all the elements from (0..len). | ||
#[inline] | ||
pub fn with_all(len: usize) -> Self { | ||
WorkQueue { | ||
deque: (0..len).map(T::new).collect(), | ||
set: IdxSetBuf::new_filled(len), | ||
} | ||
} | ||
|
||
/// Create a new work queue that starts empty, where elements range from (0..len). | ||
#[inline] | ||
pub fn with_none(len: usize) -> Self { | ||
WorkQueue { | ||
deque: VecDeque::with_capacity(len), | ||
set: IdxSetBuf::new_empty(len), | ||
} | ||
} | ||
|
||
/// Attempt to enqueue `element` in the work queue. Returns false if it was already present. | ||
#[inline] | ||
pub fn insert(&mut self, element: T) -> bool { | ||
if self.set.add(&element) { | ||
self.deque.push_back(element); | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
|
||
/// Attempt to enqueue `element` in the work queue. Returns false if it was already present. | ||
#[inline] | ||
pub fn pop(&mut self) -> Option<T> { | ||
if let Some(element) = self.deque.pop_front() { | ||
self.set.remove(&element); | ||
Some(element) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// True if nothing is enqueued. | ||
#[inline] | ||
pub fn is_empty(&self) -> bool { | ||
self.deque.is_empty() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ use rustc::mir::*; | |
use rustc::mir::visit::{PlaceContext, Visitor}; | ||
use rustc_data_structures::indexed_vec::{Idx, IndexVec}; | ||
use rustc_data_structures::indexed_set::IdxSetBuf; | ||
use rustc_data_structures::work_queue::WorkQueue; | ||
use util::pretty::{dump_enabled, write_basic_block, write_mir_intro}; | ||
use rustc::ty::item_path; | ||
use rustc::mir::visit::MirVisitable; | ||
|
@@ -55,9 +56,6 @@ pub struct LivenessResult { | |
/// Liveness mode in use when these results were computed. | ||
pub mode: LivenessMode, | ||
|
||
/// Live variables on entry to each basic block. | ||
pub ins: IndexVec<BasicBlock, LocalSet>, | ||
|
||
/// Live variables on exit to each basic block. This is equal to | ||
/// the union of the `ins` for each successor. | ||
pub outs: IndexVec<BasicBlock, LocalSet>, | ||
|
@@ -124,37 +122,35 @@ pub fn liveness_of_locals<'tcx>(mir: &Mir<'tcx>, mode: LivenessMode) -> Liveness | |
.map(|b| block(mode, b, locals)) | ||
.collect(); | ||
|
||
let mut ins: IndexVec<_, _> = mir.basic_blocks() | ||
let mut outs: IndexVec<_, _> = mir.basic_blocks() | ||
.indices() | ||
.map(|_| LocalSet::new_empty(locals)) | ||
.collect(); | ||
let mut outs = ins.clone(); | ||
|
||
let mut changed = true; | ||
let mut bits = LocalSet::new_empty(locals); | ||
while changed { | ||
changed = false; | ||
|
||
for b in mir.basic_blocks().indices().rev() { | ||
// outs[b] = ∪ {ins of successors} | ||
bits.clear(); | ||
for &successor in mir.basic_blocks()[b].terminator().successors() { | ||
bits.union(&ins[successor]); | ||
} | ||
outs[b].overwrite(&bits); | ||
|
||
// bits = use ∪ (bits - def) | ||
def_use[b].apply(&mut bits); | ||
// queue of things that need to be re-processed, and a set containing | ||
// the things currently in the queue | ||
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_all(mir.basic_blocks().len()); | ||
|
||
let predecessors = mir.predecessors(); | ||
|
||
// update bits on entry and flag if they have changed | ||
if ins[b] != bits { | ||
ins[b].overwrite(&bits); | ||
changed = true; | ||
while let Some(bb) = dirty_queue.pop() { | ||
// bits = use ∪ (bits - def) | ||
bits.overwrite(&outs[bb]); | ||
def_use[bb].apply(&mut bits); | ||
|
||
// add `bits` to the out set for each predecessor; if those | ||
// bits were not already present, then enqueue the predecessor | ||
// as dirty. | ||
for &pred_bb in &predecessors[bb] { | ||
if outs[pred_bb].union(&bits) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there should be a comment indicating that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, those methods are undercommented. I'll add some comments. =) |
||
dirty_queue.insert(pred_bb); | ||
} | ||
} | ||
} | ||
|
||
LivenessResult { mode, ins, outs } | ||
LivenessResult { mode, outs } | ||
} | ||
|
||
impl LivenessResult { | ||
|
@@ -195,8 +191,6 @@ impl LivenessResult { | |
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 | ||
|
@@ -438,7 +432,6 @@ pub fn write_mir_fn<'a, 'tcx>( | |
.collect(); | ||
writeln!(w, "{} {{{}}}", prefix, live.join(", ")) | ||
}; | ||
print(w, " ", &result.ins)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The removal of this is unfortunate. I don't know if anyone but me have used this for debugging though. It shouldn't be too hard to add code to compute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have the code to recompute it -- and in fact in the MIR dump, we include those results. I could update this code to match I suppose. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (That is, in the NLL MIR dump) |
||
write_basic_block(tcx, block, mir, &mut |_, _| Ok(()), w)?; | ||
print(w, " ", &result.outs)?; | ||
if block.index() + 1 != mir.basic_blocks().len() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a remark that
bits
now contains the state at the entry point the block (and has the value the previousins
variable helt).