From 2873019779365118ae6f034f4830850de4d79d87 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 2 May 2017 13:48:19 -0700 Subject: [PATCH] Add a register diversion tracker. Keep track of the current location of register values as regmove instructions are encountered throughout an EBB. --- lib/cretonne/src/regalloc/diversion.rs | 114 +++++++++++++++++++++++++ lib/cretonne/src/regalloc/mod.rs | 2 + 2 files changed, 116 insertions(+) create mode 100644 lib/cretonne/src/regalloc/diversion.rs diff --git a/lib/cretonne/src/regalloc/diversion.rs b/lib/cretonne/src/regalloc/diversion.rs new file mode 100644 index 0000000000..2096af8722 --- /dev/null +++ b/lib/cretonne/src/regalloc/diversion.rs @@ -0,0 +1,114 @@ +//! Register diversions. +//! +//! Normally, a value is assigned to a single register or stack location by the register allocator. +//! Sometimes, it is necessary to move register values to a different register in order to satisfy +//! instruction constraints. +//! +//! These register diversions are local to an EBB. No values can be diverted when entering a new +//! EBB. + +use entity_map::EntityMap; +use ir::{Value, ValueLoc}; +use isa::RegUnit; + +/// A diversion of a value from its original register location to a new register. +/// +/// In IL, a diversion is represented by a `regmove` instruction, possibly a chain of them for the +/// same value. +/// +/// When tracking diversions, the `from` field is the original assigned value location, and `to` is +/// the current one. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Diversion { + /// The value that is diverted. + pub value: Value, + /// The original register value location. + pub from: RegUnit, + /// The current register value location. + pub to: RegUnit, +} + +impl Diversion { + /// Make a new register diversion. + pub fn new(value: Value, from: RegUnit, to: RegUnit) -> Diversion { + Diversion { value, from, to } + } +} + +/// Keep track of register diversions in an EBB. +pub struct RegDiversions { + current: Vec, +} + +impl RegDiversions { + /// Create a new empty diversion tracker. + pub fn new() -> RegDiversions { + RegDiversions { current: Vec::new() } + } + + /// Clear the tracker, preparing for a new EBB. + pub fn clear(&mut self) { + self.current.clear() + } + + /// Get the current diversion of `value`, if any. + pub fn diversion(&self, value: Value) -> Option<&Diversion> { + self.current.iter().find(|d| d.value == value) + } + + /// Get all current diversion. + pub fn all(&self) -> &[Diversion] { + self.current.as_slice() + } + + /// Get the current register location for `value`. Fall back to the assignment map for + /// non-diverted values. + pub fn reg(&self, value: Value, locations: &EntityMap) -> RegUnit { + match self.diversion(value) { + Some(d) => d.to, + None => locations[value].unwrap_reg(), + } + } + + /// Record a register move. + pub fn regmove(&mut self, value: Value, from: RegUnit, to: RegUnit) { + if let Some(i) = self.current.iter().position(|d| d.value == value) { + debug_assert_eq!(self.current[i].to, from, "Bad regmove chain for {}", value); + if self.current[i].from != to { + self.current[i].to = to; + } else { + self.current.swap_remove(i); + } + } else { + self.current.push(Diversion::new(value, from, to)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ir::Value; + use entity_map::EntityRef; + + #[test] + fn inserts() { + let mut divs = RegDiversions::new(); + let v1 = Value::new(1); + let v2 = Value::new(2); + + divs.regmove(v1, 10, 12); + assert_eq!(divs.diversion(v1), + Some(&Diversion { + value: v1, + from: 10, + to: 12, + })); + assert_eq!(divs.diversion(v2), None); + + divs.regmove(v1, 12, 11); + assert_eq!(divs.diversion(v1).unwrap().to, 11); + divs.regmove(v1, 11, 10); + assert_eq!(divs.diversion(v1), None); + } +} diff --git a/lib/cretonne/src/regalloc/mod.rs b/lib/cretonne/src/regalloc/mod.rs index 0682be2fd6..9803c3af3f 100644 --- a/lib/cretonne/src/regalloc/mod.rs +++ b/lib/cretonne/src/regalloc/mod.rs @@ -10,6 +10,8 @@ pub mod coloring; mod affinity; mod context; +mod diversion; pub use self::allocatable_set::AllocatableSet; pub use self::context::Context; +pub use self::diversion::RegDiversions;