Make LiveRange a type alias for GenLiveRange<Layout>.

This makes the whole LiveRange generic over the program order instead of
having a number of methods that are individually program order-generic.
This makes is possible to have data members that depend on the program
order, as we will shortly.

This also gives us stronger type checking on the public LiveRange
methods which now require a Layout argument, not just any program order.
This commit is contained in:
Jakob Stoklund Olesen
2017-12-04 12:57:04 -08:00
parent f106e4266a
commit 27d5543adc

View File

@@ -108,9 +108,10 @@
//! //!
use entity::SparseMapValue; use entity::SparseMapValue;
use ir::{Inst, Ebb, Value, ProgramPoint, ExpandedProgramPoint, ProgramOrder}; use ir::{Inst, Ebb, Value, Layout, ProgramPoint, ExpandedProgramPoint, ProgramOrder};
use regalloc::affinity::Affinity; use regalloc::affinity::Affinity;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::marker::PhantomData;
/// Global live range of a single SSA value. /// Global live range of a single SSA value.
/// ///
@@ -139,7 +140,13 @@ use std::cmp::Ordering;
/// Inserting new instructions in the layout is safe, but removing instructions is not. Besides the /// Inserting new instructions in the layout is safe, but removing instructions is not. Besides the
/// instructions using or defining their value, `LiveRange` structs can contain references to /// instructions using or defining their value, `LiveRange` structs can contain references to
/// branch and jump instructions. /// branch and jump instructions.
pub struct LiveRange { pub type LiveRange = GenLiveRange<Layout>;
/// Generic live range implementation.
///
/// The intended generic parameter is `PO=Layout`, but tests are simpler with a mock order.
/// Use `LiveRange` instead of using this generic directly.
pub struct GenLiveRange<PO: ProgramOrder> {
/// The value described by this live range. /// The value described by this live range.
/// This member can't be modified in case the live range is stored in a `SparseMap`. /// This member can't be modified in case the live range is stored in a `SparseMap`.
value: Value, value: Value,
@@ -166,6 +173,8 @@ pub struct LiveRange {
/// - Not overlapping defining EBB: For all `i`: /// - Not overlapping defining EBB: For all `i`:
/// `liveins[i].end < def_begin` or `liveins[i].begin > def_end`. /// `liveins[i].end < def_begin` or `liveins[i].begin > def_end`.
liveins: Vec<Interval>, liveins: Vec<Interval>,
unused: PhantomData<PO>,
} }
/// An additional contiguous interval of a global live range. /// An additional contiguous interval of a global live range.
@@ -202,17 +211,18 @@ impl Interval {
} }
} }
impl LiveRange { impl<PO: ProgramOrder> GenLiveRange<PO> {
/// Create a new live range for `value` defined at `def`. /// Create a new live range for `value` defined at `def`.
/// ///
/// The live range will be created as dead, but it can be extended with `extend_in_ebb()`. /// The live range will be created as dead, but it can be extended with `extend_in_ebb()`.
pub fn new(value: Value, def: ProgramPoint, affinity: Affinity) -> LiveRange { pub fn new(value: Value, def: ProgramPoint, affinity: Affinity) -> GenLiveRange<PO> {
LiveRange { GenLiveRange {
value, value,
affinity, affinity,
def_begin: def, def_begin: def,
def_end: def, def_end: def,
liveins: Vec::new(), liveins: Vec::new(),
unused: PhantomData,
} }
} }
@@ -220,7 +230,7 @@ impl LiveRange {
/// ///
/// Return `Ok(n)` if `liveins[n]` already contains `ebb`. /// Return `Ok(n)` if `liveins[n]` already contains `ebb`.
/// Otherwise, return `Err(n)` with the index where such an interval should be inserted. /// Otherwise, return `Err(n)` with the index where such an interval should be inserted.
fn find_ebb_interval<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Result<usize, usize> { fn find_ebb_interval(&self, ebb: Ebb, order: &PO) -> Result<usize, usize> {
self.liveins self.liveins
.binary_search_by(|intv| order.cmp(intv.begin, ebb)) .binary_search_by(|intv| order.cmp(intv.begin, ebb))
.or_else(|n| { .or_else(|n| {
@@ -244,7 +254,7 @@ impl LiveRange {
/// ///
/// The return value can be used to detect if we just learned that the value is live-in to /// The return value can be used to detect if we just learned that the value is live-in to
/// `ebb`. This can trigger recursive extensions in `ebb`'s CFG predecessor blocks. /// `ebb`. This can trigger recursive extensions in `ebb`'s CFG predecessor blocks.
pub fn extend_in_ebb<PO: ProgramOrder>(&mut self, ebb: Ebb, to: Inst, order: &PO) -> bool { pub fn extend_in_ebb(&mut self, ebb: Ebb, to: Inst, order: &PO) -> bool {
// First check if we're extending the def interval. // First check if we're extending the def interval.
// //
// We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't // We're assuming here that `to` never precedes `def_begin` in the same EBB, but we can't
@@ -377,7 +387,7 @@ impl LiveRange {
/// If the live range is live through all of `ebb`, the terminator of `ebb` is a correct /// If the live range is live through all of `ebb`, the terminator of `ebb` is a correct
/// answer, but it is also possible that an even later program point is returned. So don't /// answer, but it is also possible that an even later program point is returned. So don't
/// depend on the returned `Inst` to belong to `ebb`. /// depend on the returned `Inst` to belong to `ebb`.
pub fn livein_local_end<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> Option<Inst> { pub fn livein_local_end(&self, ebb: Ebb, order: &PO) -> Option<Inst> {
self.find_ebb_interval(ebb, order).ok().map(|n| { self.find_ebb_interval(ebb, order).ok().map(|n| {
self.liveins[n].end self.liveins[n].end
}) })
@@ -386,7 +396,7 @@ impl LiveRange {
/// Is this value live-in to `ebb`? /// Is this value live-in to `ebb`?
/// ///
/// An EBB argument is not considered to be live in. /// An EBB argument is not considered to be live in.
pub fn is_livein<PO: ProgramOrder>(&self, ebb: Ebb, order: &PO) -> bool { pub fn is_livein(&self, ebb: Ebb, order: &PO) -> bool {
self.livein_local_end(ebb, order).is_some() self.livein_local_end(ebb, order).is_some()
} }
@@ -396,10 +406,7 @@ impl LiveRange {
} }
/// Check if this live range overlaps a definition in `ebb`. /// Check if this live range overlaps a definition in `ebb`.
pub fn overlaps_def<PO>(&self, def: ExpandedProgramPoint, ebb: Ebb, order: &PO) -> bool pub fn overlaps_def(&self, def: ExpandedProgramPoint, ebb: Ebb, order: &PO) -> bool {
where
PO: ProgramOrder,
{
// Check for an overlap with the local range. // Check for an overlap with the local range.
if order.cmp(def, self.def_begin) != Ordering::Less && if order.cmp(def, self.def_begin) != Ordering::Less &&
order.cmp(def, self.def_end) == Ordering::Less order.cmp(def, self.def_end) == Ordering::Less
@@ -415,10 +422,7 @@ impl LiveRange {
} }
/// Check if this live range reaches a use at `user` in `ebb`. /// Check if this live range reaches a use at `user` in `ebb`.
pub fn reaches_use<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool pub fn reaches_use(&self, user: Inst, ebb: Ebb, order: &PO) -> bool {
where
PO: ProgramOrder,
{
// Check for an overlap with the local range. // Check for an overlap with the local range.
if order.cmp(user, self.def_begin) == Ordering::Greater && if order.cmp(user, self.def_begin) == Ordering::Greater &&
order.cmp(user, self.def_end) != Ordering::Greater order.cmp(user, self.def_end) != Ordering::Greater
@@ -434,16 +438,13 @@ impl LiveRange {
} }
/// Check if this live range is killed at `user` in `ebb`. /// Check if this live range is killed at `user` in `ebb`.
pub fn killed_at<PO>(&self, user: Inst, ebb: Ebb, order: &PO) -> bool pub fn killed_at(&self, user: Inst, ebb: Ebb, order: &PO) -> bool {
where
PO: ProgramOrder,
{
self.def_local_end() == user.into() || self.livein_local_end(ebb, order) == Some(user) self.def_local_end() == user.into() || self.livein_local_end(ebb, order) == Some(user)
} }
} }
/// Allow a `LiveRange` to be stored in a `SparseMap` indexed by values. /// Allow a `LiveRange` to be stored in a `SparseMap` indexed by values.
impl SparseMapValue<Value> for LiveRange { impl<PO: ProgramOrder> SparseMapValue<Value> for GenLiveRange<PO> {
fn key(&self) -> Value { fn key(&self) -> Value {
self.value self.value
} }
@@ -451,7 +452,7 @@ impl SparseMapValue<Value> for LiveRange {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::LiveRange; use super::GenLiveRange;
use ir::{Inst, Ebb, Value}; use ir::{Inst, Ebb, Value};
use entity::EntityRef; use entity::EntityRef;
use ir::{ProgramOrder, ExpandedProgramPoint}; use ir::{ProgramOrder, ExpandedProgramPoint};
@@ -502,7 +503,7 @@ mod tests {
} }
// Validate the live range invariants. // Validate the live range invariants.
fn validate(&self, lr: &LiveRange) { fn validate(&self, lr: &GenLiveRange<ProgOrder>) {
// The def interval must cover a single EBB. // The def interval must cover a single EBB.
let def_ebb = self.pp_ebb(lr.def_begin); let def_ebb = self.pp_ebb(lr.def_begin);
assert_eq!(def_ebb, self.pp_ebb(lr.def_end)); assert_eq!(def_ebb, self.pp_ebb(lr.def_end));
@@ -545,7 +546,7 @@ mod tests {
let v0 = Value::new(0); let v0 = Value::new(0);
let i1 = Inst::new(1); let i1 = Inst::new(1);
let e2 = Ebb::new(2); let e2 = Ebb::new(2);
let lr = LiveRange::new(v0, i1.into(), Default::default()); let lr = GenLiveRange::new(v0, i1.into(), Default::default());
assert!(lr.is_dead()); assert!(lr.is_dead());
assert!(lr.is_local()); assert!(lr.is_local());
assert_eq!(lr.def(), i1.into()); assert_eq!(lr.def(), i1.into());
@@ -558,7 +559,7 @@ mod tests {
fn dead_arg_range() { fn dead_arg_range() {
let v0 = Value::new(0); let v0 = Value::new(0);
let e2 = Ebb::new(2); let e2 = Ebb::new(2);
let lr = LiveRange::new(v0, e2.into(), Default::default()); let lr = GenLiveRange::new(v0, e2.into(), Default::default());
assert!(lr.is_dead()); assert!(lr.is_dead());
assert!(lr.is_local()); assert!(lr.is_local());
assert_eq!(lr.def(), e2.into()); assert_eq!(lr.def(), e2.into());
@@ -575,7 +576,7 @@ mod tests {
let i11 = Inst::new(11); let i11 = Inst::new(11);
let i12 = Inst::new(12); let i12 = Inst::new(12);
let i13 = Inst::new(13); let i13 = Inst::new(13);
let mut lr = LiveRange::new(v0, i11.into(), Default::default()); let mut lr = GenLiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e10, i13, PO), false); assert_eq!(lr.extend_in_ebb(e10, i13, PO), false);
PO.validate(&lr); PO.validate(&lr);
@@ -598,7 +599,7 @@ mod tests {
let i11 = Inst::new(11); let i11 = Inst::new(11);
let i12 = Inst::new(12); let i12 = Inst::new(12);
let i13 = Inst::new(13); let i13 = Inst::new(13);
let mut lr = LiveRange::new(v0, e10.into(), Default::default()); let mut lr = GenLiveRange::new(v0, e10.into(), Default::default());
// Extending a dead EBB argument in its own block should not indicate that a live-in // Extending a dead EBB argument in its own block should not indicate that a live-in
// interval was created. // interval was created.
@@ -632,7 +633,7 @@ mod tests {
let i21 = Inst::new(21); let i21 = Inst::new(21);
let i22 = Inst::new(22); let i22 = Inst::new(22);
let i23 = Inst::new(23); let i23 = Inst::new(23);
let mut lr = LiveRange::new(v0, i11.into(), Default::default()); let mut lr = GenLiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e10, i12, PO), false); assert_eq!(lr.extend_in_ebb(e10, i12, PO), false);
@@ -661,7 +662,7 @@ mod tests {
let i31 = Inst::new(31); let i31 = Inst::new(31);
let e40 = Ebb::new(40); let e40 = Ebb::new(40);
let i41 = Inst::new(41); let i41 = Inst::new(41);
let mut lr = LiveRange::new(v0, i11.into(), Default::default()); let mut lr = GenLiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e30, i31, PO), true); assert_eq!(lr.extend_in_ebb(e30, i31, PO), true);
assert_eq!(lr.liveins.len(), 1); assert_eq!(lr.liveins.len(), 1);
@@ -678,7 +679,7 @@ mod tests {
assert_eq!(lr.liveins[0].begin, e20); assert_eq!(lr.liveins[0].begin, e20);
assert_eq!(lr.liveins[0].end, i41); assert_eq!(lr.liveins[0].end, i41);
let mut lr = LiveRange::new(v0, i11.into(), Default::default()); let mut lr = GenLiveRange::new(v0, i11.into(), Default::default());
assert_eq!(lr.extend_in_ebb(e40, i41, PO), true); assert_eq!(lr.extend_in_ebb(e40, i41, PO), true);
assert_eq!(lr.liveins.len(), 1); assert_eq!(lr.liveins.len(), 1);