diff --git a/.gitignore b/.gitignore index 9ceaac3ed5..765772864e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tags target Cargo.lock +.*.rustfmt diff --git a/lib/cretonne/src/isa/constraints.rs b/lib/cretonne/src/isa/constraints.rs index bdd2bea958..43a0ddb271 100644 --- a/lib/cretonne/src/isa/constraints.rs +++ b/lib/cretonne/src/isa/constraints.rs @@ -21,6 +21,7 @@ pub struct OperandConstraint { } /// The different kinds of operand constraints. +#[derive(Clone, Copy, PartialEq, Eq)] pub enum ConstraintKind { /// This operand or result must be a register from the given register class. Reg, diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index 0bd9e5e9fa..ee33bb4c47 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -41,8 +41,8 @@ //! concurrent function compilations. pub use isa::encoding::Encoding; -pub use isa::registers::{RegInfo, RegUnit, RegClass}; -pub use isa::constraints::RecipeConstraints; +pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex}; +pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind}; use settings; use ir::{InstructionData, DataFlowGraph}; diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index f8c2ee5731..5e89110284 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -133,6 +133,12 @@ impl RegClassData { Some(RegClassIndex(mask.trailing_zeros() as u8)) } } + + /// Returns true if `other` is a subclass of this register class. + /// A register class is considerd to be a subclass of itself. + pub fn has_subclass>(&self, other: RCI) -> bool { + self.subclasses & (1 << other.into().0) != 0 + } } /// A small reference to a register class. diff --git a/lib/cretonne/src/regalloc/affinity.rs b/lib/cretonne/src/regalloc/affinity.rs new file mode 100644 index 0000000000..529828759e --- /dev/null +++ b/lib/cretonne/src/regalloc/affinity.rs @@ -0,0 +1,68 @@ +//! Value affinity for register allocation. +//! +//! An SSA value's affinity is a hint used to guide the register allocator. It specifies the class +//! of allocation that is likely to cause the least amount of fixup moves in order to satisfy +//! instruction operand constraints. +//! +//! For values that want to be in registers, the affinity hint includes a register class or +//! subclass. This is just a hint, and the register allocator is allowed to pick a register from a +//! larger register class instead. + +use isa::{RegInfo, RegClassIndex, OperandConstraint, ConstraintKind}; + +/// Preferred register allocation for an SSA value. +#[derive(Clone, Copy)] +pub enum Affinity { + /// Don't care. This value can go anywhere. + Any, + + /// This value should be placed in a spill slot on the stack. + Stack, + + /// This value prefers a register from the given register class. + Reg(RegClassIndex), +} + +impl Default for Affinity { + fn default() -> Self { + Affinity::Any + } +} + +impl Affinity { + /// Create an affinity that satisfies a single constraint. + /// + /// This will never create the indifferent `Affinity::Any`. + /// Use the `Default` implementation for that. + pub fn new(constraint: &OperandConstraint) -> Affinity { + if constraint.kind == ConstraintKind::Stack { + Affinity::Stack + } else { + Affinity::Reg(constraint.regclass.into()) + } + } + + /// Merge an operand constraint into this affinity. + /// + /// Note that this does not guarantee that the register allocator will pick a register that + /// satisfies the constraint. + pub fn merge(&mut self, constraint: &OperandConstraint, reg_info: &RegInfo) { + match *self { + Affinity::Any => *self = Affinity::new(constraint), + Affinity::Reg(rc) => { + // If the preferred register class is a subclass of the constraint, there's no need + // to change anything. + if constraint.kind != ConstraintKind::Stack && + !constraint.regclass.has_subclass(rc) { + // If the register classes don't overlap, `intersect` returns `None`, and we + // just keep our previous affinity. + if let Some(subclass) = constraint.regclass.intersect(reg_info.rc(rc)) { + // This constraint shrinks our preferred register class. + *self = Affinity::Reg(subclass); + } + } + } + Affinity::Stack => {} + } + } +} diff --git a/lib/cretonne/src/regalloc/liverange.rs b/lib/cretonne/src/regalloc/liverange.rs index e192efdc4d..67667a3a47 100644 --- a/lib/cretonne/src/regalloc/liverange.rs +++ b/lib/cretonne/src/regalloc/liverange.rs @@ -73,9 +73,9 @@ //! 3. A numerical order by EBB number. Performant because it doesn't need to indirect through the //! `ProgramOrder` for comparisons. //! -//! These orderings will cause small differences in coalescing opportinities, but all of them would +//! These orderings will cause small differences in coalescing opportunities, but all of them would //! do a decent job of compressing a long live range. The numerical order might be preferable -//! beacuse: +//! because: //! //! - It has better performance because EBB numbers can be compared directly without any table //! lookups. @@ -92,7 +92,7 @@ //! //! Coalescing is an important compression technique because some live ranges can span thousands of //! EBBs. We can represent that by switching to a sorted `Vec` representation where -//! an `[Ebb, Inst]` pair represents a coalesced range, while an `Inst` entry without a preceeding +//! an `[Ebb, Inst]` pair represents a coalesced range, while an `Inst` entry without a preceding //! `Ebb` entry represents a single live-in interval. //! //! This representation is more compact for a live range with many uncoalesced live-in intervals. @@ -109,6 +109,7 @@ use std::cmp::Ordering; use ir::{Inst, Ebb, Value, ProgramPoint, ProgramOrder}; +use regalloc::affinity::Affinity; use sparse_map::SparseMapValue; /// Global live range of a single SSA value. @@ -143,6 +144,9 @@ pub struct LiveRange { /// This member can't be modified in case the live range is stored in a `SparseMap`. value: Value, + /// The preferred register allocation for this value. + pub affinity: Affinity, + /// The instruction or EBB header where this value is defined. def_begin: ProgramPoint, @@ -167,7 +171,7 @@ pub struct LiveRange { /// An additional contiguous interval of a global live range. /// /// This represents a live-in interval for a single EBB, or a coalesced set of live-in intervals -/// for contiguous EBBs where all but the last live-in inteval covers the whole EBB. +/// for contiguous EBBs where all but the last live-in interval covers the whole EBB. /// struct Interval { /// Interval starting point. @@ -205,6 +209,7 @@ impl LiveRange { let def = def.into(); LiveRange { value: value, + affinity: Default::default(), def_begin: def, def_end: def, liveins: Vec::new(), @@ -238,7 +243,7 @@ impl LiveRange { /// is live-in to `ebb`, extending to `to`. Return true. /// /// 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 precedessor blocks. + /// `ebb`. This can trigger recursive extensions in `ebb`'s CFG predecessor blocks. pub fn extend_in_ebb(&mut self, ebb: Ebb, to: Inst, order: &PO) -> bool { // First check if we're extending the def interval. // diff --git a/lib/cretonne/src/regalloc/mod.rs b/lib/cretonne/src/regalloc/mod.rs index 9e0f7b820d..bd1c34cd23 100644 --- a/lib/cretonne/src/regalloc/mod.rs +++ b/lib/cretonne/src/regalloc/mod.rs @@ -5,3 +5,5 @@ pub mod liverange; pub mod liveness; pub mod allocatable_set; + +mod affinity;