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
|
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,
|
||||||
|
|||||||
56
src/lib.rs
56
src/lib.rs
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user