diff --git a/lib/cretonne/src/regalloc/context.rs b/lib/cretonne/src/regalloc/context.rs index b404602a34..ea2cdcd924 100644 --- a/lib/cretonne/src/regalloc/context.rs +++ b/lib/cretonne/src/regalloc/context.rs @@ -13,6 +13,7 @@ use regalloc::live_value_tracker::LiveValueTracker; use regalloc::liveness::Liveness; use regalloc::reload::Reload; use regalloc::spilling::Spilling; +use regalloc::virtregs::VirtRegs; use result::CtonResult; use topo_order::TopoOrder; use verifier::{verify_context, verify_liveness}; @@ -20,6 +21,7 @@ use verifier::{verify_context, verify_liveness}; /// Persistent memory allocations for register allocation. pub struct Context { liveness: Liveness, + virtregs: VirtRegs, topo: TopoOrder, tracker: LiveValueTracker, spilling: Spilling, @@ -35,6 +37,7 @@ impl Context { pub fn new() -> Context { Context { liveness: Liveness::new(), + virtregs: VirtRegs::new(), topo: TopoOrder::new(), tracker: LiveValueTracker::new(), spilling: Spilling::new(), @@ -54,6 +57,8 @@ impl Context { domtree: &DominatorTree) -> CtonResult { // `Liveness` and `Coloring` are self-clearing. + self.virtregs.clear(); + // Tracker state (dominator live sets) is actually reused between the spilling and coloring // phases. self.tracker.clear(); diff --git a/lib/cretonne/src/regalloc/mod.rs b/lib/cretonne/src/regalloc/mod.rs index 59eb341eb0..ca4624b45b 100644 --- a/lib/cretonne/src/regalloc/mod.rs +++ b/lib/cretonne/src/regalloc/mod.rs @@ -15,6 +15,7 @@ mod pressure; mod reload; mod solver; mod spilling; +mod virtregs; pub use self::allocatable_set::AllocatableSet; pub use self::context::Context; diff --git a/lib/cretonne/src/regalloc/virtregs.rs b/lib/cretonne/src/regalloc/virtregs.rs new file mode 100644 index 0000000000..6efe196805 --- /dev/null +++ b/lib/cretonne/src/regalloc/virtregs.rs @@ -0,0 +1,135 @@ +//! Virtual registers. +//! +//! A virtual register is a set of related SSA values whose live ranges don't interfere. If all the +//! values in a virtual register are assigned to the same location, fewer copies will result in the +//! output. +//! +//! A virtual register is typically built by merging together SSA values that are "phi-related" - +//! that is, one value is passed as an EBB argument to a branch and the other is the EBB parameter +//! value itself. +//! +//! If any values in a virtual register are spilled, they will use the same stack slot. This avoids +//! memory-to-memory copies when a spilled value is passed as an EBB argument. + +use entity_list::{EntityList, ListPool}; +use entity_map::{EntityMap, PrimaryEntityData}; +use ir::Value; +use packed_option::PackedOption; +use ref_slice::ref_slice; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +pub struct VirtReg(u32); +entity_impl!(VirtReg, "vreg"); + +type ValueList = EntityList; +impl PrimaryEntityData for ValueList {} + +/// Collection of virtual registers. +/// +/// Each virtual register is a list of values. Also maintain a map from values to their unique +/// virtual register, if any. +pub struct VirtRegs { + /// Memory pool for the value lists. + pool: ListPool, + + /// The primary table of virtual registers. + /// + /// The list of values ion a virtual register is kept sorted according to the dominator tree's + /// RPO of the value defs. + vregs: EntityMap, + + /// Each value belongs to at most one virtual register. + value_vregs: EntityMap>, +} + +#[allow(dead_code)] +impl VirtRegs { + /// Create a new virtual register collection. + pub fn new() -> VirtRegs { + VirtRegs { + pool: ListPool::new(), + vregs: EntityMap::new(), + value_vregs: EntityMap::new(), + } + } + + /// Clear all virtual registers. + pub fn clear(&mut self) { + self.vregs.clear(); + self.value_vregs.clear(); + self.pool.clear(); + } + + /// Get the virtual register containing `value`, if any. + pub fn get(&self, value: Value) -> Option { + self.value_vregs.get_or_default(value).into() + } + + /// Get the list of values in `vreg`. The values are ordered according to `DomTree::rpo_cmp` of + /// their definition points. + pub fn values(&self, vreg: VirtReg) -> &[Value] { + self.vregs[vreg].as_slice(&self.pool) + } + + /// Get the congruence class of `value`. + /// + /// If `value` belongs to a virtual register, the congruence class is the values of the virtual + /// register. Otherwise it is just the value itself. + pub fn congruence_class<'a, 'b>(&'a self, value: &'b Value) -> &'b [Value] + where 'a: 'b + { + self.get(*value) + .map(|vr| self.values(vr)) + .unwrap_or(ref_slice(value)) + } + + /// Check if `a` and `b` belong to the same congruence class. + pub fn same_class(&self, a: Value, b: Value) -> bool { + match (self.get(a), self.get(b)) { + (Some(va), Some(vb)) => va == vb, + _ => a == b, + } + } + + /// Unify `values` into a single virtual register. + /// + /// The values in the slice can be singletons or they can belong to a virtual register already. + /// If a value belongs to a virtual register, all of the values in that register must be + /// present. + /// + /// The values are assumed to already be in RPO order. + pub fn unify(&mut self, values: &[Value]) -> VirtReg { + // Start by clearing all virtual registers involved. + // Pick a virtual register to reuse (the smallest number) or allocate a new one. + let mut singletons = 0; + let mut cleared = 0; + let vreg = values + .iter() + .filter_map(|&v| { + let vr = self.get(v); + match vr { + None => singletons += 1, + Some(vr) => { + if !self.vregs[vr].is_empty() { + cleared += self.vregs[vr].len(&self.pool); + self.vregs[vr].clear(&mut self.pool); + } + } + } + vr + }) + .min() + .unwrap_or_else(|| self.vregs.push(Default::default())); + + assert_eq!(values.len(), + singletons + cleared, + "Can't unify partial virtual registers"); + + self.vregs[vreg].extend(values.iter().cloned(), &mut self.pool); + for &v in values { + *self.value_vregs.ensure(v) = vreg.into(); + } + + vreg + } +}