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:
Amanieu d'Antras
2021-09-13 08:33:17 +01:00
parent ef2c9b3f26
commit 35ed2109b1
2 changed files with 36 additions and 34 deletions

View File

@@ -116,8 +116,8 @@ however.
Implementation note: both vregs and operands are bit-packed into Implementation note: both vregs and operands are bit-packed into
u32s. This is essential for memory-efficiency. As a result of the u32s. This is essential for memory-efficiency. As a result of the
operand bit-packing in particular (including the policy constraints!), operand bit-packing in particular (including the policy constraints!),
the allocator supports up to 2^20 (1M) vregs per function, and 2^5 the allocator supports up to 2^21 (2M) vregs per function, and 2^6
(32) physical registers per class. Later we will also see a limit of (64) physical registers per class. Later we will also see a limit of
2^20 (1M) instructions per function. These limits are considered 2^20 (1M) instructions per function. These limits are considered
sufficient for the anticipated use-cases (e.g., compiling Wasm, which sufficient for the anticipated use-cases (e.g., compiling Wasm, which
also has function-size implementation limits); for larger functions, also has function-size implementation limits); for larger functions,

View File

@@ -55,13 +55,13 @@ pub enum RegClass {
/// register 0 is different than Float register 0. /// register 0 is different than Float register 0.
/// ///
/// Because of bit-packed encodings throughout the implementation, /// 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 /// The value returned by `index()`, in contrast, is in a single index
/// space shared by all classes, in order to enable uniform reasoning /// space shared by all classes, in order to enable uniform reasoning
/// about physical registers. This is done by putting the class bit at /// about physical registers. This is done by putting the class bit at
/// the MSB, or equivalently, declaring that indices 0..31 are the 32 /// the MSB, or equivalently, declaring that indices 0..63 are the 64
/// integer registers and indices 32..63 are the 32 float registers. /// integer registers and indices 64..127 are the 64 float registers.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PReg { pub struct PReg {
hw_enc: u8, hw_enc: u8,
@@ -69,7 +69,7 @@ pub struct PReg {
} }
impl 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: usize = (1 << Self::MAX_BITS) - 1;
pub const MAX_INDEX: usize = 1 << (Self::MAX_BITS + 1); // including RegClass bit pub const MAX_INDEX: usize = 1 << (Self::MAX_BITS + 1); // including RegClass bit
@@ -170,7 +170,7 @@ pub struct VReg {
} }
impl VReg { impl VReg {
pub const MAX_BITS: usize = 20; pub const MAX_BITS: usize = 21;
pub const MAX: usize = (1 << Self::MAX_BITS) - 1; pub const MAX: usize = (1 << Self::MAX_BITS) - 1;
#[inline(always)] #[inline(always)]
@@ -383,15 +383,15 @@ pub enum OperandPos {
/// that the conflict (overlap) is properly accounted for. See /// that the conflict (overlap) is properly accounted for. See
/// comments on the constructors below for more. /// comments on the constructors below for more.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
//#[repr(packed)]
pub struct Operand { pub struct Operand {
/// Bit-pack into 32 bits. /// 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 /// where `constraint` is an `OperandConstraint`, `kind` is an
/// `OperandKind`, `pos` is an `OperandPos`, `class` is a /// `OperandKind`, `pos` is an `OperandPos`, `class` is a
/// `RegClass`, `preg` is a `PReg` or an index for a reused-input /// `RegClass`, and `vreg` is a vreg index.
/// constraint, and `vreg` is a vreg index.
bits: u32, bits: u32,
} }
@@ -404,17 +404,17 @@ impl Operand {
kind: OperandKind, kind: OperandKind,
pos: OperandPos, pos: OperandPos,
) -> Self { ) -> Self {
let (preg_field, constraint_field): (u32, u32) = match constraint { let constraint_field = match constraint {
OperandConstraint::Any => (0, 0), OperandConstraint::Any => 0,
OperandConstraint::Reg => (0, 1), OperandConstraint::Reg => 1,
OperandConstraint::Stack => (0, 2), OperandConstraint::Stack => 2,
OperandConstraint::FixedReg(preg) => { OperandConstraint::FixedReg(preg) => {
assert_eq!(preg.class(), vreg.class()); assert_eq!(preg.class(), vreg.class());
(preg.hw_enc() as u32, 3) 0b1000000 | preg.hw_enc() as u32
} }
OperandConstraint::Reuse(which) => { OperandConstraint::Reuse(which) => {
assert!(which <= PReg::MAX); assert!(which <= 31);
(which as u32, 4) 0b0100000 | which as u32
} }
}; };
let class_field = vreg.class() as u8 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; let kind_field = kind as u8 as u32;
Operand { Operand {
bits: vreg.vreg() as u32 bits: vreg.vreg() as u32
| (preg_field << 20) | (class_field << 21)
| (class_field << 25) | (pos_field << 22)
| (pos_field << 26) | (kind_field << 23)
| (kind_field << 27) | (constraint_field << 25),
| (constraint_field << 29),
} }
} }
@@ -565,7 +564,7 @@ impl Operand {
/// Get the register class used by this operand. /// Get the register class used by this operand.
#[inline(always)] #[inline(always)]
pub fn class(self) -> RegClass { pub fn class(self) -> RegClass {
let class_field = (self.bits >> 25) & 1; let class_field = (self.bits >> 21) & 1;
match class_field { match class_field {
0 => RegClass::Int, 0 => RegClass::Int,
1 => RegClass::Float, 1 => RegClass::Float,
@@ -577,7 +576,7 @@ impl Operand {
/// (read), or a "mod" / modify (a read followed by a write). /// (read), or a "mod" / modify (a read followed by a write).
#[inline(always)] #[inline(always)]
pub fn kind(self) -> OperandKind { pub fn kind(self) -> OperandKind {
let kind_field = (self.bits >> 27) & 3; let kind_field = (self.bits >> 23) & 3;
match kind_field { match kind_field {
0 => OperandKind::Def, 0 => OperandKind::Def,
1 => OperandKind::Mod, 1 => OperandKind::Mod,
@@ -592,7 +591,7 @@ impl Operand {
/// at "after", though there are cases where this is not true. /// at "after", though there are cases where this is not true.
#[inline(always)] #[inline(always)]
pub fn pos(self) -> OperandPos { pub fn pos(self) -> OperandPos {
let pos_field = (self.bits >> 26) & 1; let pos_field = (self.bits >> 22) & 1;
match pos_field { match pos_field {
0 => OperandPos::Early, 0 => OperandPos::Early,
1 => OperandPos::Late, 1 => OperandPos::Late,
@@ -604,17 +603,20 @@ impl Operand {
/// its allocation must fulfill. /// its allocation must fulfill.
#[inline(always)] #[inline(always)]
pub fn constraint(self) -> OperandConstraint { pub fn constraint(self) -> OperandConstraint {
let constraint_field = (self.bits >> 29) & 7; let constraint_field = ((self.bits >> 25) as usize) & 127;
let preg_field = ((self.bits >> 20) as usize) & PReg::MAX; 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 { match constraint_field {
0 => OperandConstraint::Any, 0 => OperandConstraint::Any,
1 => OperandConstraint::Reg, 1 => OperandConstraint::Reg,
2 => OperandConstraint::Stack, 2 => OperandConstraint::Stack,
3 => OperandConstraint::FixedReg(PReg::new(preg_field, self.class())),
4 => OperandConstraint::Reuse(preg_field),
_ => unreachable!(), _ => unreachable!(),
} }
} }
}
/// Get the raw 32-bit encoding of this operand's fields. /// Get the raw 32-bit encoding of this operand's fields.
#[inline(always)] #[inline(always)]