Adjust Operand encoding
The encoding for OperandConstraint is adjusted to free up 2 bits which allows for 2^21 vregs and 2^6 pregs.
This commit is contained in:
@@ -116,8 +116,8 @@ however.
|
||||
Implementation note: both vregs and operands are bit-packed into
|
||||
u32s. This is essential for memory-efficiency. As a result of the
|
||||
operand bit-packing in particular (including the policy constraints!),
|
||||
the allocator supports up to 2^20 (1M) vregs per function, and 2^5
|
||||
(32) physical registers per class. Later we will also see a limit of
|
||||
the allocator supports up to 2^21 (2M) vregs per function, and 2^6
|
||||
(64) physical registers per class. Later we will also see a limit of
|
||||
2^20 (1M) instructions per function. These limits are considered
|
||||
sufficient for the anticipated use-cases (e.g., compiling Wasm, which
|
||||
also has function-size implementation limits); for larger functions,
|
||||
|
||||
66
src/lib.rs
66
src/lib.rs
@@ -55,13 +55,13 @@ pub enum RegClass {
|
||||
/// register 0 is different than Float register 0.
|
||||
///
|
||||
/// Because of bit-packed encodings throughout the implementation,
|
||||
/// `hw_enc` must fit in 5 bits, i.e., at most 32 registers per class.
|
||||
/// `hw_enc` must fit in 6 bits, i.e., at most 64 registers per class.
|
||||
///
|
||||
/// The value returned by `index()`, in contrast, is in a single index
|
||||
/// space shared by all classes, in order to enable uniform reasoning
|
||||
/// about physical registers. This is done by putting the class bit at
|
||||
/// the MSB, or equivalently, declaring that indices 0..31 are the 32
|
||||
/// integer registers and indices 32..63 are the 32 float registers.
|
||||
/// the MSB, or equivalently, declaring that indices 0..63 are the 64
|
||||
/// integer registers and indices 64..127 are the 64 float registers.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct PReg {
|
||||
hw_enc: u8,
|
||||
@@ -69,7 +69,7 @@ pub struct PReg {
|
||||
}
|
||||
|
||||
impl PReg {
|
||||
pub const MAX_BITS: usize = 5;
|
||||
pub const MAX_BITS: usize = 6;
|
||||
pub const MAX: usize = (1 << Self::MAX_BITS) - 1;
|
||||
pub const MAX_INDEX: usize = 1 << (Self::MAX_BITS + 1); // including RegClass bit
|
||||
|
||||
@@ -170,7 +170,7 @@ pub struct VReg {
|
||||
}
|
||||
|
||||
impl VReg {
|
||||
pub const MAX_BITS: usize = 20;
|
||||
pub const MAX_BITS: usize = 21;
|
||||
pub const MAX: usize = (1 << Self::MAX_BITS) - 1;
|
||||
|
||||
#[inline(always)]
|
||||
@@ -383,15 +383,15 @@ pub enum OperandPos {
|
||||
/// that the conflict (overlap) is properly accounted for. See
|
||||
/// comments on the constructors below for more.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
//#[repr(packed)]
|
||||
pub struct Operand {
|
||||
/// Bit-pack into 32 bits.
|
||||
///
|
||||
/// constraint:3 kind:2 pos:1 class:1 preg:5 vreg:20
|
||||
/// constraint:7 kind:2 pos:1 class:1 vreg:21
|
||||
///
|
||||
/// where `constraint` is an `OperandConstraint`, `kind` is an
|
||||
/// `OperandKind`, `pos` is an `OperandPos`, `class` is a
|
||||
/// `RegClass`, `preg` is a `PReg` or an index for a reused-input
|
||||
/// constraint, and `vreg` is a vreg index.
|
||||
/// `RegClass`, and `vreg` is a vreg index.
|
||||
bits: u32,
|
||||
}
|
||||
|
||||
@@ -404,17 +404,17 @@ impl Operand {
|
||||
kind: OperandKind,
|
||||
pos: OperandPos,
|
||||
) -> Self {
|
||||
let (preg_field, constraint_field): (u32, u32) = match constraint {
|
||||
OperandConstraint::Any => (0, 0),
|
||||
OperandConstraint::Reg => (0, 1),
|
||||
OperandConstraint::Stack => (0, 2),
|
||||
let constraint_field = match constraint {
|
||||
OperandConstraint::Any => 0,
|
||||
OperandConstraint::Reg => 1,
|
||||
OperandConstraint::Stack => 2,
|
||||
OperandConstraint::FixedReg(preg) => {
|
||||
assert_eq!(preg.class(), vreg.class());
|
||||
(preg.hw_enc() as u32, 3)
|
||||
0b1000000 | preg.hw_enc() as u32
|
||||
}
|
||||
OperandConstraint::Reuse(which) => {
|
||||
assert!(which <= PReg::MAX);
|
||||
(which as u32, 4)
|
||||
assert!(which <= 31);
|
||||
0b0100000 | which as u32
|
||||
}
|
||||
};
|
||||
let class_field = vreg.class() as u8 as u32;
|
||||
@@ -422,11 +422,10 @@ impl Operand {
|
||||
let kind_field = kind as u8 as u32;
|
||||
Operand {
|
||||
bits: vreg.vreg() as u32
|
||||
| (preg_field << 20)
|
||||
| (class_field << 25)
|
||||
| (pos_field << 26)
|
||||
| (kind_field << 27)
|
||||
| (constraint_field << 29),
|
||||
| (class_field << 21)
|
||||
| (pos_field << 22)
|
||||
| (kind_field << 23)
|
||||
| (constraint_field << 25),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +564,7 @@ impl Operand {
|
||||
/// Get the register class used by this operand.
|
||||
#[inline(always)]
|
||||
pub fn class(self) -> RegClass {
|
||||
let class_field = (self.bits >> 25) & 1;
|
||||
let class_field = (self.bits >> 21) & 1;
|
||||
match class_field {
|
||||
0 => RegClass::Int,
|
||||
1 => RegClass::Float,
|
||||
@@ -577,7 +576,7 @@ impl Operand {
|
||||
/// (read), or a "mod" / modify (a read followed by a write).
|
||||
#[inline(always)]
|
||||
pub fn kind(self) -> OperandKind {
|
||||
let kind_field = (self.bits >> 27) & 3;
|
||||
let kind_field = (self.bits >> 23) & 3;
|
||||
match kind_field {
|
||||
0 => OperandKind::Def,
|
||||
1 => OperandKind::Mod,
|
||||
@@ -592,7 +591,7 @@ impl Operand {
|
||||
/// at "after", though there are cases where this is not true.
|
||||
#[inline(always)]
|
||||
pub fn pos(self) -> OperandPos {
|
||||
let pos_field = (self.bits >> 26) & 1;
|
||||
let pos_field = (self.bits >> 22) & 1;
|
||||
match pos_field {
|
||||
0 => OperandPos::Early,
|
||||
1 => OperandPos::Late,
|
||||
@@ -604,15 +603,18 @@ impl Operand {
|
||||
/// its allocation must fulfill.
|
||||
#[inline(always)]
|
||||
pub fn constraint(self) -> OperandConstraint {
|
||||
let constraint_field = (self.bits >> 29) & 7;
|
||||
let preg_field = ((self.bits >> 20) as usize) & PReg::MAX;
|
||||
match constraint_field {
|
||||
0 => OperandConstraint::Any,
|
||||
1 => OperandConstraint::Reg,
|
||||
2 => OperandConstraint::Stack,
|
||||
3 => OperandConstraint::FixedReg(PReg::new(preg_field, self.class())),
|
||||
4 => OperandConstraint::Reuse(preg_field),
|
||||
_ => unreachable!(),
|
||||
let constraint_field = ((self.bits >> 25) as usize) & 127;
|
||||
if constraint_field & 0b1000000 != 0 {
|
||||
OperandConstraint::FixedReg(PReg::new(constraint_field & 0b0111111, self.class()))
|
||||
} else if constraint_field & 0b0100000 != 0 {
|
||||
OperandConstraint::Reuse(constraint_field & 0b0011111)
|
||||
} else {
|
||||
match constraint_field {
|
||||
0 => OperandConstraint::Any,
|
||||
1 => OperandConstraint::Reg,
|
||||
2 => OperandConstraint::Stack,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user