Review feedback.
This commit is contained in:
@@ -248,11 +248,10 @@ fn choose_dominating_block(
|
|||||||
if (allow_self || block != orig_block) && bool::arbitrary(u)? {
|
if (allow_self || block != orig_block) && bool::arbitrary(u)? {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if idom[block.index()] == block {
|
if idom[block.index()].is_invalid() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
block = idom[block.index()];
|
block = idom[block.index()];
|
||||||
assert!(block.is_valid());
|
|
||||||
}
|
}
|
||||||
let block = if block != orig_block || allow_self {
|
let block = if block != orig_block || allow_self {
|
||||||
block
|
block
|
||||||
@@ -591,12 +590,10 @@ impl std::fmt::Debug for Func {
|
|||||||
pub fn machine_env() -> MachineEnv {
|
pub fn machine_env() -> MachineEnv {
|
||||||
// Reg 31 is the scratch reg.
|
// Reg 31 is the scratch reg.
|
||||||
let regs: Vec<PReg> = (0..31).map(|i| PReg::new(i, RegClass::Int)).collect();
|
let regs: Vec<PReg> = (0..31).map(|i| PReg::new(i, RegClass::Int)).collect();
|
||||||
let preferred_regs_by_class: Vec<Vec<PReg>> =
|
let preferred_regs_by_class: [Vec<PReg>; 2] = [regs.iter().cloned().take(24).collect(), vec![]];
|
||||||
vec![regs.iter().cloned().take(24).collect(), vec![]];
|
let non_preferred_regs_by_class: [Vec<PReg>; 2] =
|
||||||
let non_preferred_regs_by_class: Vec<Vec<PReg>> =
|
[regs.iter().cloned().skip(24).collect(), vec![]];
|
||||||
vec![regs.iter().cloned().skip(24).collect(), vec![]];
|
let scratch_by_class: [PReg; 2] = [PReg::new(31, RegClass::Int), PReg::new(0, RegClass::Float)];
|
||||||
let scratch_by_class: Vec<PReg> =
|
|
||||||
vec![PReg::new(31, RegClass::Int), PReg::new(0, RegClass::Float)];
|
|
||||||
MachineEnv {
|
MachineEnv {
|
||||||
regs,
|
regs,
|
||||||
preferred_regs_by_class,
|
preferred_regs_by_class,
|
||||||
|
|||||||
110
src/lib.rs
110
src/lib.rs
@@ -219,10 +219,7 @@ impl std::fmt::Display for SpillSlot {
|
|||||||
/// `LAllocation` in Ion).
|
/// `LAllocation` in Ion).
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Operand {
|
pub struct Operand {
|
||||||
/// Bit-pack into 32 bits. Note that `constraint` overlaps with `kind`
|
/// Bit-pack into 32 bits.
|
||||||
/// in `Allocation` and we use mutually disjoint tag-value ranges
|
|
||||||
/// so that clients, if they wish, can track just one `u32` per
|
|
||||||
/// register slot and edit it in-place after allocation.
|
|
||||||
///
|
///
|
||||||
/// constraint:3 kind:2 pos:1 class:1 preg:5 vreg:20
|
/// constraint:3 kind:2 pos:1 class:1 preg:5 vreg:20
|
||||||
bits: u32,
|
bits: u32,
|
||||||
@@ -464,13 +461,7 @@ pub enum OperandPos {
|
|||||||
/// Operand.
|
/// Operand.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Allocation {
|
pub struct Allocation {
|
||||||
/// Bit-pack in 32 bits. Note that `kind` overlaps with the
|
/// Bit-pack in 32 bits.
|
||||||
/// `constraint` field in `Operand`, and we are careful to use
|
|
||||||
/// disjoint ranges of values in this field for each type. We also
|
|
||||||
/// leave the def-or-use bit (`kind` for `Operand`) unused here so
|
|
||||||
/// that we can use it below in `OperandOrAllocation` to record
|
|
||||||
/// whether `Allocation`s are defs or uses (which is often useful
|
|
||||||
/// to know).
|
|
||||||
///
|
///
|
||||||
/// kind:3 unused:1 index:28
|
/// kind:3 unused:1 index:28
|
||||||
bits: u32,
|
bits: u32,
|
||||||
@@ -519,9 +510,9 @@ impl Allocation {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn kind(self) -> AllocationKind {
|
pub fn kind(self) -> AllocationKind {
|
||||||
match (self.bits >> 29) & 7 {
|
match (self.bits >> 29) & 7 {
|
||||||
5 => AllocationKind::None,
|
0 => AllocationKind::None,
|
||||||
6 => AllocationKind::Reg,
|
1 => AllocationKind::Reg,
|
||||||
7 => AllocationKind::Stack,
|
2 => AllocationKind::Stack,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -576,14 +567,12 @@ impl Allocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: These values must be *disjoint* with the values used to
|
|
||||||
// encode `OperandConstraint`, because they share a 3-bit field.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum AllocationKind {
|
pub enum AllocationKind {
|
||||||
None = 5,
|
None = 0,
|
||||||
Reg = 6,
|
Reg = 1,
|
||||||
Stack = 7,
|
Stack = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Allocation {
|
impl Allocation {
|
||||||
@@ -597,76 +586,6 @@ impl Allocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper that wraps either an `Operand` or an `Allocation` and is
|
|
||||||
/// able to tell which it is based on the tag bits.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct OperandOrAllocation {
|
|
||||||
bits: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OperandOrAllocation {
|
|
||||||
pub fn from_operand(operand: Operand) -> Self {
|
|
||||||
debug_assert!(operand.bits() >> 29 <= 4);
|
|
||||||
Self {
|
|
||||||
bits: operand.bits(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_alloc(alloc: Allocation) -> Self {
|
|
||||||
debug_assert!(alloc.bits() >> 29 >= 5);
|
|
||||||
Self { bits: alloc.bits() }
|
|
||||||
}
|
|
||||||
pub fn from_alloc_and_kind(alloc: Allocation, kind: OperandKind) -> Self {
|
|
||||||
debug_assert!(alloc.bits() >> 29 >= 5);
|
|
||||||
let bits = alloc.bits()
|
|
||||||
| match kind {
|
|
||||||
OperandKind::Def => 0,
|
|
||||||
OperandKind::Mod => 1 << 27,
|
|
||||||
OperandKind::Use => 2 << 27,
|
|
||||||
};
|
|
||||||
Self { bits }
|
|
||||||
}
|
|
||||||
pub fn is_operand(&self) -> bool {
|
|
||||||
(self.bits >> 29) <= 4
|
|
||||||
}
|
|
||||||
pub fn is_allocation(&self) -> bool {
|
|
||||||
(self.bits >> 29) >= 5
|
|
||||||
}
|
|
||||||
pub fn as_operand(&self) -> Option<Operand> {
|
|
||||||
if self.is_operand() {
|
|
||||||
Some(Operand::from_bits(self.bits))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_allocation(&self) -> Option<Allocation> {
|
|
||||||
if self.is_allocation() {
|
|
||||||
// Remove the kind (def/use/mod) bits -- the canonical
|
|
||||||
// `Allocation` does not have this, and we want allocs to
|
|
||||||
// continue to be comparable whether they are used for
|
|
||||||
// reads or writes.
|
|
||||||
Some(Allocation::from_bits(self.bits & !(3 << 27)))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn kind(&self) -> OperandKind {
|
|
||||||
let kind_field = (self.bits >> 28) & 1;
|
|
||||||
match kind_field {
|
|
||||||
0 => OperandKind::Def,
|
|
||||||
1 => OperandKind::Mod,
|
|
||||||
2 => OperandKind::Use,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the Operand with an Allocation, keeping the def/use bit.
|
|
||||||
pub fn replace_with_alloc(&mut self, alloc: Allocation) {
|
|
||||||
self.bits &= 1 << 28;
|
|
||||||
self.bits |= alloc.bits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait defined by the regalloc client to provide access to its
|
/// A trait defined by the regalloc client to provide access to its
|
||||||
/// machine-instruction / CFG representation.
|
/// machine-instruction / CFG representation.
|
||||||
///
|
///
|
||||||
@@ -937,17 +856,24 @@ pub struct MachineEnv {
|
|||||||
pub regs: Vec<PReg>,
|
pub regs: Vec<PReg>,
|
||||||
/// Preferred physical registers for each class. These are the
|
/// Preferred physical registers for each class. These are the
|
||||||
/// registers that will be allocated first, if free.
|
/// registers that will be allocated first, if free.
|
||||||
pub preferred_regs_by_class: Vec<Vec<PReg>>,
|
pub preferred_regs_by_class: [Vec<PReg>; 2],
|
||||||
/// Non-preferred physical registers for each class. These are the
|
/// Non-preferred physical registers for each class. These are the
|
||||||
/// registers that will be allocated if a preferred register is
|
/// registers that will be allocated if a preferred register is
|
||||||
/// not available; using one of these is considered suboptimal,
|
/// not available; using one of these is considered suboptimal,
|
||||||
/// but still better than spilling.
|
/// but still better than spilling.
|
||||||
pub non_preferred_regs_by_class: Vec<Vec<PReg>>,
|
pub non_preferred_regs_by_class: [Vec<PReg>; 2],
|
||||||
/// One scratch register per class. This is needed to perform
|
/// One scratch register per class. This is needed to perform
|
||||||
/// moves between registers when cyclic move patterns occur. The
|
/// moves between registers when cyclic move patterns occur. The
|
||||||
/// register should not be placed in either the preferred or
|
/// register should not be placed in either the preferred or
|
||||||
/// non-preferred list (i.e., it is not otherwise allocatable).
|
/// non-preferred list (i.e., it is not otherwise allocatable).
|
||||||
pub scratch_by_class: Vec<PReg>,
|
///
|
||||||
|
/// Note that the register allocator will freely use this register
|
||||||
|
/// between instructions, but *within* the machine code generated
|
||||||
|
/// by a single (regalloc-level) instruction, the client is free
|
||||||
|
/// to use the scratch register. E.g., if one "instruction" causes
|
||||||
|
/// the emission of two machine-code instructions, this lowering
|
||||||
|
/// can use the scratch register between them.
|
||||||
|
pub scratch_by_class: [PReg; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The output of the register allocator.
|
/// The output of the register allocator.
|
||||||
|
|||||||
Reference in New Issue
Block a user