Live Value Tracker.
Keep track of which values are live and dead as we move through the instructions in an EBB.
This commit is contained in:
@@ -25,4 +25,5 @@ mod constant_hash;
|
||||
mod predicates;
|
||||
mod legalizer;
|
||||
mod ref_slice;
|
||||
mod partition_slice;
|
||||
mod packed_option;
|
||||
|
||||
253
lib/cretonne/src/regalloc/live_value_tracker.rs
Normal file
253
lib/cretonne/src/regalloc/live_value_tracker.rs
Normal file
@@ -0,0 +1,253 @@
|
||||
//! Track which values are live in an EBB with instruction granularity.
|
||||
//!
|
||||
//! The `LiveValueTracker` keeps track of the set of live SSA values at each instruction in an EBB.
|
||||
//! The sets of live values are computed on the fly as the tracker is moved from instruction to
|
||||
//! instruction, starting at the EBB header.
|
||||
|
||||
use dominator_tree::DominatorTree;
|
||||
use entity_list::{EntityList, ListPool};
|
||||
use ir::instructions::BranchInfo;
|
||||
use ir::{Inst, Ebb, Value, DataFlowGraph, ProgramOrder, ExpandedProgramPoint};
|
||||
use partition_slice::partition_slice;
|
||||
use regalloc::liveness::Liveness;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
type ValueList = EntityList<Value>;
|
||||
|
||||
/// Compute and track live values throughout an EBB.
|
||||
pub struct LiveValueTracker {
|
||||
/// The set of values that are live at the current program point.
|
||||
live: LiveValueVec,
|
||||
|
||||
/// Saved set of live values for every jump and branch that can potentially be an immediate
|
||||
/// dominator of an EBB.
|
||||
///
|
||||
/// This is the set of values that are live *before* the branch.
|
||||
idom_sets: HashMap<Inst, ValueList>,
|
||||
|
||||
/// Memory pool for the live sets.
|
||||
idom_pool: ListPool<Value>,
|
||||
}
|
||||
|
||||
/// Information about a value that is live at the current program point.
|
||||
pub struct LiveValue {
|
||||
/// The live value.
|
||||
pub value: Value,
|
||||
|
||||
/// The local ending point of the live range in the current EBB, as returned by
|
||||
/// `LiveRange::def_local_end()` or `LiveRange::livein_local_end()`.
|
||||
pub endpoint: Inst,
|
||||
}
|
||||
|
||||
struct LiveValueVec {
|
||||
/// The set of values that are live at the current program point.
|
||||
values: Vec<LiveValue>,
|
||||
|
||||
/// How many values at the front of `values` are known to be live after `inst`?
|
||||
///
|
||||
/// This is used to pass a much smaller slice to `partition_slice` when its called a second
|
||||
/// time for the same instruction.
|
||||
live_prefix: Option<(Inst, usize)>,
|
||||
}
|
||||
|
||||
impl LiveValueVec {
|
||||
fn new() -> LiveValueVec {
|
||||
LiveValueVec {
|
||||
values: Vec::new(),
|
||||
live_prefix: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new live value to `values`.
|
||||
fn push(&mut self, value: Value, endpoint: Inst) {
|
||||
self.values.push(LiveValue {
|
||||
value: value,
|
||||
endpoint: endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
/// Remove all elements.
|
||||
fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
self.live_prefix = None;
|
||||
}
|
||||
|
||||
/// Make sure that the values killed by `next_inst` are moved to the end of the `values`
|
||||
/// vector.
|
||||
///
|
||||
/// Returns the number of values that will be live after `next_inst`.
|
||||
fn live_after(&mut self, next_inst: Inst) -> usize {
|
||||
// How many values at the front of the vector are already known to survive `next_inst`?
|
||||
// We don't need to pass this prefix to `partition_slice()`
|
||||
let keep = match self.live_prefix {
|
||||
Some((i, prefix)) if i == next_inst => prefix,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
// Move the remaining surviving values to the front partition of the vector.
|
||||
let prefix = keep + partition_slice(&mut self.values[keep..], |v| v.endpoint != next_inst);
|
||||
|
||||
// Remember the new prefix length in case we get called again for the same `next_inst`.
|
||||
self.live_prefix = Some((next_inst, prefix));
|
||||
prefix
|
||||
}
|
||||
|
||||
/// Remove the values killed by `next_inst`.
|
||||
fn remove_kill_values(&mut self, next_inst: Inst) {
|
||||
let keep = self.live_after(next_inst);
|
||||
self.values.truncate(keep);
|
||||
}
|
||||
}
|
||||
|
||||
impl LiveValueTracker {
|
||||
/// Create a new blank tracker.
|
||||
pub fn new() -> LiveValueTracker {
|
||||
LiveValueTracker {
|
||||
live: LiveValueVec::new(),
|
||||
idom_sets: HashMap::new(),
|
||||
idom_pool: ListPool::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all cached information.
|
||||
pub fn clear(&mut self) {
|
||||
self.live.clear();
|
||||
self.idom_sets.clear();
|
||||
self.idom_pool.clear();
|
||||
}
|
||||
|
||||
/// Get the set of currently live values.
|
||||
///
|
||||
/// Between calls to `process_inst()` and `drop_dead()`, this includes both values killed and
|
||||
/// defined by the current instruction.
|
||||
pub fn live(&self) -> &[LiveValue] {
|
||||
&self.live.values
|
||||
}
|
||||
|
||||
/// Move the current position to the top of `ebb`.
|
||||
///
|
||||
/// This depends on the stored live value set at `ebb`'s immediate dominator, so that must have
|
||||
/// been visited first.
|
||||
pub fn ebb_top<PO: ProgramOrder>(&mut self,
|
||||
ebb: Ebb,
|
||||
dfg: &DataFlowGraph,
|
||||
liveness: &Liveness,
|
||||
program_order: &PO,
|
||||
domtree: &DominatorTree) {
|
||||
// Start over, compute the set of live values at the top of the EBB from two sources:
|
||||
//
|
||||
// 1. Values that were live before `ebb`'s immediate dominator, filtered for those that are
|
||||
// actually live-in.
|
||||
// 2. Arguments to `ebb` that are not dead.
|
||||
//
|
||||
self.live.clear();
|
||||
|
||||
// Compute the live-in values. Start by filtering the set of values that were live before
|
||||
// the immediate dominator. Just use the empty set if there's no immediate dominator (i.e.,
|
||||
// the entry block or an unreachable block).
|
||||
if let Some(idom) = domtree.idom(ebb) {
|
||||
// If the immediate dominator exits, we must have a stored list for it. This is a
|
||||
// requirement to the order EBBs are visited: All dominators must have been processed
|
||||
// before the current EBB.
|
||||
let idom_live_list =
|
||||
self.idom_sets.get(&idom).expect("No stored live set for dominator");
|
||||
// Get just the values that are live-in to `ebb`.
|
||||
for &value in idom_live_list.as_slice(&self.idom_pool) {
|
||||
let lr = liveness.get(value)
|
||||
.expect("Immediate dominator value has no live range");
|
||||
|
||||
// Check if this value is live-in here.
|
||||
if let Some(endpoint) = lr.livein_local_end(ebb, program_order) {
|
||||
self.live.push(value, endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now add all the live arguments to `ebb`.
|
||||
for value in dfg.ebb_args(ebb) {
|
||||
let lr = liveness.get(value).expect("EBB argument value has no live range");
|
||||
assert_eq!(lr.def(), ebb.into());
|
||||
match lr.def_local_end().into() {
|
||||
ExpandedProgramPoint::Inst(endpoint) => {
|
||||
self.live.push(value, endpoint);
|
||||
}
|
||||
ExpandedProgramPoint::Ebb(local_ebb) => {
|
||||
// This is a dead EBB argument which is not even live into the first
|
||||
// instruction in the EBB. We can ignore it.
|
||||
assert_eq!(local_ebb,
|
||||
ebb,
|
||||
"EBB argument live range ends at wrong EBB header");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare to move past `inst`.
|
||||
///
|
||||
/// Determine the set of already live values that are killed by `inst`, and add the new defined
|
||||
/// values to the tracked set.
|
||||
///
|
||||
/// Returns `(kills, defs)` as a pair of slices. The `defs` slice is guaranteed to be in the
|
||||
/// same order as `inst`'s results, and includes dead defines. The order of `kills` is
|
||||
/// arbitrary.
|
||||
///
|
||||
/// The `drop_dead()` method must be called next to actually remove the dead values from the
|
||||
/// tracked set after the two returned slices are no longer needed.
|
||||
pub fn process_inst(&mut self,
|
||||
inst: Inst,
|
||||
dfg: &DataFlowGraph,
|
||||
liveness: &Liveness)
|
||||
-> (&[LiveValue], &[LiveValue]) {
|
||||
// Save a copy of the live values before any branches or jumps that could be somebody's
|
||||
// immediate dominator.
|
||||
match dfg[inst].analyze_branch() {
|
||||
BranchInfo::NotABranch => {}
|
||||
_ => self.save_idom_live_set(inst),
|
||||
}
|
||||
|
||||
// Move killed values to the end of the vector.
|
||||
// Don't remove them yet, `drop_dead()` will do that.
|
||||
let first_kill = self.live.live_after(inst);
|
||||
|
||||
// Add the values defined by `inst`.
|
||||
let first_def = self.live.values.len();
|
||||
for value in dfg.inst_results(inst) {
|
||||
let lr = liveness.get(value).expect("Instruction result has no live range");
|
||||
assert_eq!(lr.def(), inst.into());
|
||||
match lr.def_local_end().into() {
|
||||
ExpandedProgramPoint::Inst(endpoint) => {
|
||||
self.live.push(value, endpoint);
|
||||
}
|
||||
ExpandedProgramPoint::Ebb(ebb) => {
|
||||
panic!("Instruction result live range can't end at {}", ebb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(&self.live.values[first_kill..first_def], &self.live.values[first_def..])
|
||||
}
|
||||
|
||||
/// Drop the values that are now dead after moving past `inst`.
|
||||
///
|
||||
/// This removes both live values that were killed by `inst` and dead defines on `inst` itself.
|
||||
///
|
||||
/// This must be called after `process_inst(inst)` and before proceeding to the next
|
||||
/// instruction.
|
||||
pub fn drop_dead(&mut self, inst: Inst) {
|
||||
// Remove both live values that were killed by `inst` and dead defines from `inst`.
|
||||
self.live.remove_kill_values(inst);
|
||||
}
|
||||
|
||||
/// Save the current set of live values so it is associated with `idom`.
|
||||
fn save_idom_live_set(&mut self, idom: Inst) {
|
||||
let values = self.live.values.iter().map(|lv| lv.value);
|
||||
let pool = &mut self.idom_pool;
|
||||
// If there already is a set saved for `idom`, just keep it.
|
||||
self.idom_sets.entry(idom).or_insert_with(|| {
|
||||
let mut list = ValueList::default();
|
||||
list.extend(values, pool);
|
||||
list
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -284,6 +284,11 @@ impl Liveness {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the live range for `value`, if it exists.
|
||||
pub fn get(&self, value: Value) -> Option<&LiveRange> {
|
||||
self.ranges.get(value)
|
||||
}
|
||||
|
||||
/// Compute the live ranges of all SSA values used in `func`.
|
||||
/// This clears out any existing analysis stored in this data structure.
|
||||
pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, isa: &TargetIsa) {
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
pub mod liverange;
|
||||
pub mod liveness;
|
||||
pub mod allocatable_set;
|
||||
pub mod live_value_tracker;
|
||||
|
||||
mod affinity;
|
||||
|
||||
Reference in New Issue
Block a user