Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust Operand encoding #13

Merged
merged 2 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
66 changes: 34 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing but a pedantic nit on my own comment: could you adjust to either 0..=63 or 0..64 (and likewise for the second range)? Inconsistency w.r.t. Rust open/cosed-range syntax is bothering me :-)

/// 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,
class: RegClass,
}

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

Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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)]
Copy link

@lqd lqd Sep 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a stray comment ?

Suggested change
//#[repr(packed)]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops that's from an old test I was doing.

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,
}

Expand All @@ -404,29 +404,28 @@ 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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a block comment here describing the bit encoding?

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;
let pos_field = pos as u8 as u32;
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),
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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!(),
}
}
}

Expand Down