Implement value affinities for register allocation.

An SSA value is usually biased towards a specific register class or a
stack slot, depending on the constraints of the instructions using it.

Represent this bias as an Affinity enum, and implement a merging
algorithm for updating an affinity to satisfy a new constraint.

Affinities will be computed as part of the liveness analysis. This is
not implemented yet.
This commit is contained in:
Jakob Stoklund Olesen
2017-01-26 14:51:49 -08:00
parent a395f01b3e
commit 3c4d54c4bd
7 changed files with 90 additions and 7 deletions

View File

@@ -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 => {}
}
}
}