Rename operand positions to Early and Late, and make weights f16/f32 values.

This commit is contained in:
Chris Fallin
2021-08-31 17:31:23 -07:00
parent 3a18564e98
commit b19fa4857f
8 changed files with 145 additions and 92 deletions

View File

@@ -306,8 +306,8 @@ impl CheckerState {
// the requirements of the OperandConstraint. // the requirements of the OperandConstraint.
for (op, alloc) in operands.iter().zip(allocs.iter()) { for (op, alloc) in operands.iter().zip(allocs.iter()) {
let is_here = match (op.pos(), pos) { let is_here = match (op.pos(), pos) {
(OperandPos::Before, InstPosition::Before) => true, (OperandPos::Early, InstPosition::Before) => true,
(OperandPos::After, InstPosition::After) => true, (OperandPos::Late, InstPosition::After) => true,
_ => false, _ => false,
}; };
if !is_here { if !is_here {

View File

@@ -402,9 +402,9 @@ impl Func {
while let Some(vreg) = vregs_by_block_to_be_defined[block].pop() { while let Some(vreg) = vregs_by_block_to_be_defined[block].pop() {
let def_constraint = OperandConstraint::arbitrary(u)?; let def_constraint = OperandConstraint::arbitrary(u)?;
let def_pos = if bool::arbitrary(u)? { let def_pos = if bool::arbitrary(u)? {
OperandPos::Before OperandPos::Early
} else { } else {
OperandPos::After OperandPos::Late
}; };
let mut operands = vec![Operand::new( let mut operands = vec![Operand::new(
vreg, vreg,
@@ -442,7 +442,7 @@ impl Func {
vreg, vreg,
use_constraint, use_constraint,
OperandKind::Use, OperandKind::Use,
OperandPos::Before, OperandPos::Early,
)); ));
allocations.push(Allocation::none()); allocations.push(Allocation::none());
} }
@@ -456,7 +456,7 @@ impl Func {
op.vreg(), op.vreg(),
OperandConstraint::Reuse(reused), OperandConstraint::Reuse(reused),
op.kind(), op.kind(),
OperandPos::After, OperandPos::Late,
); );
// Make sure reused input is a Reg. // Make sure reused input is a Reg.
let op = operands[reused]; let op = operands[reused];
@@ -464,7 +464,7 @@ impl Func {
op.vreg(), op.vreg(),
OperandConstraint::Reg, OperandConstraint::Reg,
op.kind(), op.kind(),
OperandPos::Before, OperandPos::Early,
); );
} else if opts.fixed_regs && bool::arbitrary(u)? { } else if opts.fixed_regs && bool::arbitrary(u)? {
let mut fixed = vec![]; let mut fixed = vec![];

View File

@@ -13,6 +13,7 @@
//! Data structures for backtracking allocator. //! Data structures for backtracking allocator.
use super::liveranges::SpillWeight;
use crate::cfg::CFGInfo; use crate::cfg::CFGInfo;
use crate::index::ContainerComparator; use crate::index::ContainerComparator;
use crate::indexset::IndexSet; use crate::indexset::IndexSet;
@@ -141,14 +142,15 @@ impl LiveRange {
self.uses_spill_weight_and_flags |= flag_word; self.uses_spill_weight_and_flags |= flag_word;
} }
#[inline(always)] #[inline(always)]
pub fn uses_spill_weight(&self) -> u32 { pub fn uses_spill_weight(&self) -> SpillWeight {
self.uses_spill_weight_and_flags & 0x1fff_ffff let bits = (self.uses_spill_weight_and_flags & 0x1fff_ffff) << 2;
SpillWeight::from_f32(f32::from_bits(bits))
} }
#[inline(always)] #[inline(always)]
pub fn set_uses_spill_weight(&mut self, weight: u32) { pub fn set_uses_spill_weight(&mut self, weight: SpillWeight) {
assert!(weight < (1 << 29)); let weight_bits = (weight.to_f32().to_bits() >> 2) & 0x1fff_ffff;
self.uses_spill_weight_and_flags = self.uses_spill_weight_and_flags =
(self.uses_spill_weight_and_flags & 0xe000_0000) | weight; (self.uses_spill_weight_and_flags & 0xe000_0000) | weight_bits;
} }
} }

View File

@@ -37,7 +37,7 @@ impl<'a, F: Function> Env<'a, F> {
log::trace!("Ranges:"); log::trace!("Ranges:");
for (i, r) in self.ranges.iter().enumerate() { for (i, r) in self.ranges.iter().enumerate() {
log::trace!( log::trace!(
"range{}: range={:?} vreg={:?} bundle={:?} weight={}", "range{}: range={:?} vreg={:?} bundle={:?} weight={:?}",
i, i,
r.range, r.range,
r.vreg, r.vreg,

View File

@@ -26,26 +26,73 @@ use crate::{
use fxhash::FxHashSet; use fxhash::FxHashSet;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::collections::{HashSet, VecDeque}; use std::collections::{HashSet, VecDeque};
use std::convert::TryFrom;
/// A spill weight computed for a certain Use.
#[derive(Clone, Copy, Debug)]
pub struct SpillWeight(f32);
#[inline(always)] #[inline(always)]
pub fn spill_weight_from_constraint( pub fn spill_weight_from_constraint(
constraint: OperandConstraint, constraint: OperandConstraint,
loop_depth: usize, loop_depth: usize,
is_def: bool, is_def: bool,
) -> u32 { ) -> SpillWeight {
// A bonus of 1000 for one loop level, 4000 for two loop levels, // A bonus of 1000 for one loop level, 4000 for two loop levels,
// 16000 for three loop levels, etc. Avoids exponentiation. // 16000 for three loop levels, etc. Avoids exponentiation.
// Bound `loop_depth` at 2 so that `hot_bonus` is at most 16000. let loop_depth = std::cmp::min(10, loop_depth);
let loop_depth = std::cmp::min(2, loop_depth); let hot_bonus: f32 = (0..loop_depth).fold(1000.0, |a, _| a * 4.0);
let hot_bonus = 1000 * (1 << (2 * loop_depth)); let def_bonus: f32 = if is_def { 2000.0 } else { 0.0 };
let def_bonus = if is_def { 2000 } else { 0 }; let constraint_bonus: f32 = match constraint {
let constraint_bonus = match constraint { OperandConstraint::Any => 1000.0,
OperandConstraint::Any => 1000, OperandConstraint::Reg | OperandConstraint::FixedReg(_) => 2000.0,
OperandConstraint::Reg | OperandConstraint::FixedReg(_) => 2000, _ => 0.0,
_ => 0,
}; };
hot_bonus + def_bonus + constraint_bonus SpillWeight(hot_bonus + def_bonus + constraint_bonus)
}
impl SpillWeight {
/// Convert a floating-point weight to a u16 that can be compactly
/// stored in a `Use`. We simply take the top 16 bits of the f32; this
/// is equivalent to the bfloat16 format
/// (https://en.wikipedia.org/wiki/Bfloat16_floating-point_format).
pub fn to_bits(self) -> u16 {
(self.0.to_bits() >> 15) as u16
}
/// Convert a value that was returned from
/// `SpillWeight::to_bits()` back into a `SpillWeight`. Note that
/// some precision may be lost when round-tripping from a spill
/// weight to packed bits and back.
pub fn from_bits(bits: u16) -> SpillWeight {
let x = f32::from_bits((bits as u32) << 15);
SpillWeight(x)
}
/// Get a zero spill weight.
pub fn zero() -> SpillWeight {
SpillWeight(0.0)
}
/// Convert to a raw floating-point value.
pub fn to_f32(self) -> f32 {
self.0
}
/// Create a `SpillWeight` from a raw floating-point value.
pub fn from_f32(x: f32) -> SpillWeight {
SpillWeight(x)
}
pub fn to_int(self) -> u32 {
self.0 as u32
}
}
impl std::ops::Add<SpillWeight> for SpillWeight {
type Output = SpillWeight;
fn add(self, other: SpillWeight) -> Self {
SpillWeight(self.0 + other.0)
}
} }
impl<'a, F: Function> Env<'a, F> { impl<'a, F: Function> Env<'a, F> {
@@ -196,10 +243,10 @@ impl<'a, F: Function> Env<'a, F> {
loop_depth, loop_depth,
operand.kind() != OperandKind::Use, operand.kind() != OperandKind::Use,
); );
u.weight = u16::try_from(weight).expect("weight too large for u16 field"); u.weight = weight.to_bits();
log::trace!( log::trace!(
"insert use {:?} into lr {:?} with weight {}", "insert use {:?} into lr {:?} with weight {:?}",
u, u,
into, into,
weight, weight,
@@ -212,9 +259,10 @@ impl<'a, F: Function> Env<'a, F> {
self.ranges[into.index()].uses.push(u); self.ranges[into.index()].uses.push(u);
// Update stats. // Update stats.
self.ranges[into.index()].uses_spill_weight_and_flags += weight; let range_weight = self.ranges[into.index()].uses_spill_weight() + weight;
self.ranges[into.index()].set_uses_spill_weight(range_weight);
log::trace!( log::trace!(
" -> now range has weight {}", " -> now range has weight {:?}",
self.ranges[into.index()].uses_spill_weight(), self.ranges[into.index()].uses_spill_weight(),
); );
} }
@@ -279,7 +327,7 @@ impl<'a, F: Function> Env<'a, F> {
live.set(src.vreg().vreg(), true); live.set(src.vreg().vreg(), true);
} }
for pos in &[OperandPos::After, OperandPos::Before] { for pos in &[OperandPos::Late, OperandPos::Early] {
for op in self.func.inst_operands(inst) { for op in self.func.inst_operands(inst) {
if op.pos() == *pos { if op.pos() == *pos {
let was_live = live.get(op.vreg().vreg()); let was_live = live.get(op.vreg().vreg());
@@ -437,9 +485,9 @@ impl<'a, F: Function> Env<'a, F> {
assert_eq!(src.class(), dst.class()); assert_eq!(src.class(), dst.class());
assert_eq!(src.kind(), OperandKind::Use); assert_eq!(src.kind(), OperandKind::Use);
assert_eq!(src.pos(), OperandPos::Before); assert_eq!(src.pos(), OperandPos::Early);
assert_eq!(dst.kind(), OperandKind::Def); assert_eq!(dst.kind(), OperandKind::Def);
assert_eq!(dst.pos(), OperandPos::After); assert_eq!(dst.pos(), OperandPos::Late);
// If both src and dest are pinned, emit the // If both src and dest are pinned, emit the
// move right here, right now. // move right here, right now.
@@ -506,7 +554,7 @@ impl<'a, F: Function> Env<'a, F> {
dst.vreg(), dst.vreg(),
src.vreg(), src.vreg(),
OperandKind::Def, OperandKind::Def,
OperandPos::After, OperandPos::Late,
ProgPoint::after(inst), ProgPoint::after(inst),
) )
} else { } else {
@@ -516,7 +564,7 @@ impl<'a, F: Function> Env<'a, F> {
src.vreg(), src.vreg(),
dst.vreg(), dst.vreg(),
OperandKind::Use, OperandKind::Use,
OperandPos::Before, OperandPos::Early,
ProgPoint::after(inst), ProgPoint::after(inst),
) )
}; };
@@ -720,13 +768,13 @@ impl<'a, F: Function> Env<'a, F> {
src.vreg(), src.vreg(),
src_constraint, src_constraint,
OperandKind::Use, OperandKind::Use,
OperandPos::After, OperandPos::Late,
); );
let dst = Operand::new( let dst = Operand::new(
dst.vreg(), dst.vreg(),
dst_constraint, dst_constraint,
OperandKind::Def, OperandKind::Def,
OperandPos::Before, OperandPos::Early,
); );
if self.annotations_enabled { if self.annotations_enabled {
@@ -843,9 +891,9 @@ impl<'a, F: Function> Env<'a, F> {
let operand = self.func.inst_operands(inst)[i]; let operand = self.func.inst_operands(inst)[i];
let pos = match (operand.kind(), operand.pos()) { let pos = match (operand.kind(), operand.pos()) {
(OperandKind::Mod, _) => ProgPoint::before(inst), (OperandKind::Mod, _) => ProgPoint::before(inst),
(OperandKind::Def, OperandPos::Before) => ProgPoint::before(inst), (OperandKind::Def, OperandPos::Early) => ProgPoint::before(inst),
(OperandKind::Def, OperandPos::After) => ProgPoint::after(inst), (OperandKind::Def, OperandPos::Late) => ProgPoint::after(inst),
(OperandKind::Use, OperandPos::After) => ProgPoint::after(inst), (OperandKind::Use, OperandPos::Late) => ProgPoint::after(inst),
// If this is a branch, extend `pos` to // If this is a branch, extend `pos` to
// the end of the block. (Branch uses are // the end of the block. (Branch uses are
// blockparams and need to be live at the // blockparams and need to be live at the
@@ -858,12 +906,12 @@ impl<'a, F: Function> Env<'a, F> {
// reused input, force `pos` to // reused input, force `pos` to
// `After`. (See note below for why; it's // `After`. (See note below for why; it's
// very subtle!) // very subtle!)
(OperandKind::Use, OperandPos::Before) (OperandKind::Use, OperandPos::Early)
if reused_input.is_some() && reused_input.unwrap() != i => if reused_input.is_some() && reused_input.unwrap() != i =>
{ {
ProgPoint::after(inst) ProgPoint::after(inst)
} }
(OperandKind::Use, OperandPos::Before) => ProgPoint::before(inst), (OperandKind::Use, OperandPos::Early) => ProgPoint::before(inst),
}; };
if pos.pos() != cur_pos { if pos.pos() != cur_pos {
@@ -1058,7 +1106,7 @@ impl<'a, F: Function> Env<'a, F> {
self.vreg_regs[vreg.index()], self.vreg_regs[vreg.index()],
OperandConstraint::Stack, OperandConstraint::Stack,
OperandKind::Use, OperandKind::Use,
OperandPos::Before, OperandPos::Early,
); );
log::trace!( log::trace!(

View File

@@ -759,7 +759,7 @@ impl<'a, F: Function> Env<'a, F> {
let operand = self.func.inst_operands(inst)[output_idx]; let operand = self.func.inst_operands(inst)[output_idx];
if let OperandConstraint::Reuse(input_idx) = operand.constraint() { if let OperandConstraint::Reuse(input_idx) = operand.constraint() {
debug_assert!(!input_reused.contains(&input_idx)); debug_assert!(!input_reused.contains(&input_idx));
debug_assert_eq!(operand.pos(), OperandPos::After); debug_assert_eq!(operand.pos(), OperandPos::Late);
input_reused.push(input_idx); input_reused.push(input_idx);
let input_alloc = self.get_alloc(inst, input_idx); let input_alloc = self.get_alloc(inst, input_idx);
let output_alloc = self.get_alloc(inst, output_idx); let output_alloc = self.get_alloc(inst, output_idx);

View File

@@ -16,7 +16,7 @@
use super::{ use super::{
spill_weight_from_constraint, CodeRange, Env, LiveBundleIndex, LiveBundleVec, LiveRangeFlag, spill_weight_from_constraint, CodeRange, Env, LiveBundleIndex, LiveBundleVec, LiveRangeFlag,
LiveRangeIndex, LiveRangeKey, LiveRangeList, LiveRangeListEntry, PRegIndex, RegTraversalIter, LiveRangeIndex, LiveRangeKey, LiveRangeList, LiveRangeListEntry, PRegIndex, RegTraversalIter,
Requirement, UseList, Requirement, SpillWeight, UseList,
}; };
use crate::{ use crate::{
Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint, Allocation, Function, Inst, InstPosition, OperandConstraint, OperandKind, PReg, ProgPoint,
@@ -310,23 +310,24 @@ impl<'a, F: Function> Env<'a, F> {
1_000_000 1_000_000
} }
} else { } else {
let mut total = 0; let mut total = SpillWeight::zero();
for entry in &self.bundles[bundle.index()].ranges { for entry in &self.bundles[bundle.index()].ranges {
let range_data = &self.ranges[entry.index.index()]; let range_data = &self.ranges[entry.index.index()];
log::trace!( log::trace!(
" -> uses spill weight: +{}", " -> uses spill weight: +{:?}",
range_data.uses_spill_weight() range_data.uses_spill_weight()
); );
total += range_data.uses_spill_weight(); total = total + range_data.uses_spill_weight();
} }
if self.bundles[bundle.index()].prio > 0 { if self.bundles[bundle.index()].prio > 0 {
let final_weight = (total.to_f32() as u32) / self.bundles[bundle.index()].prio;
log::trace!( log::trace!(
" -> dividing by prio {}; final weight {}", " -> dividing by prio {}; final weight {}",
self.bundles[bundle.index()].prio, self.bundles[bundle.index()].prio,
total / self.bundles[bundle.index()].prio final_weight
); );
total / self.bundles[bundle.index()].prio final_weight
} else { } else {
0 0
} }
@@ -346,9 +347,9 @@ impl<'a, F: Function> Env<'a, F> {
pub fn recompute_range_properties(&mut self, range: LiveRangeIndex) { pub fn recompute_range_properties(&mut self, range: LiveRangeIndex) {
let rangedata = &mut self.ranges[range.index()]; let rangedata = &mut self.ranges[range.index()];
let mut w = 0; let mut w = SpillWeight::zero();
for u in &rangedata.uses { for u in &rangedata.uses {
w += u.weight as u32; w = w + SpillWeight::from_bits(u.weight);
log::trace!("range{}: use {:?}", range.index(), u); log::trace!("range{}: use {:?}", range.index(), u);
} }
rangedata.set_uses_spill_weight(w); rangedata.set_uses_spill_weight(w);
@@ -890,7 +891,8 @@ impl<'a, F: Function> Env<'a, F> {
OperandConstraint::Reg, OperandConstraint::Reg,
loop_depth as usize, loop_depth as usize,
/* is_def = */ true, /* is_def = */ true,
); )
.to_int();
if lowest_cost_split_conflict_cost.is_none() if lowest_cost_split_conflict_cost.is_none()
|| (conflict_cost + move_cost) || (conflict_cost + move_cost)
< lowest_cost_split_conflict_cost.unwrap() < lowest_cost_split_conflict_cost.unwrap()
@@ -909,7 +911,8 @@ impl<'a, F: Function> Env<'a, F> {
OperandConstraint::Reg, OperandConstraint::Reg,
loop_depth as usize, loop_depth as usize,
/* is_def = */ true, /* is_def = */ true,
); )
.to_int();
if lowest_cost_split_conflict_cost.is_none() if lowest_cost_split_conflict_cost.is_none()
|| (max_cost + move_cost) < lowest_cost_split_conflict_cost.unwrap() || (max_cost + move_cost) < lowest_cost_split_conflict_cost.unwrap()

View File

@@ -335,26 +335,27 @@ pub enum OperandKind {
} }
/// The "position" of the operand: where it has its read/write /// The "position" of the operand: where it has its read/write
/// effects. These are positions "in" the instruction, and "before" /// effects. These are positions "in" the instruction, and "early" and
/// and "after" are relative to the instruction's actual semantics. In /// "late" are relative to the instruction's main effect or
/// other words, the allocator assumes that the instruction (i) /// computation. In other words, the allocator assumes that the
/// performs all reads of "before" operands, (ii) does its work, and /// instruction (i) performs all reads and writes of "early" operands,
/// (iii) performs all writes of its "after" operands. /// (ii) does its work, and (iii) performs all reads and writes of its
/// "late" operands.
/// ///
/// A "write" (def) at "before" or a "read" (use) at "after" may be /// A "write" (def) at "early" or a "read" (use) at "late" may be
/// slightly nonsensical, given the above; but, it is consistent with /// slightly nonsensical, given the above, if the read is necessary
/// the notion that the value (even if a result of execution) *could* /// for the computation or the write is a result of it. A way to think
/// have been written to the register at "Before", or the value (even /// of it is that the value (even if a result of execution) *could*
/// if depended upon by the execution) *could* have been read from the /// have been read or written at the given location without causing
/// regster at "After". In other words, these write-before or /// any register-usage conflicts. In other words, these write-early or
/// use-after operands ensure that the particular allocations are /// use-late operands ensure that the particular allocations are valid
/// valid for longer than usual and that a register is not reused /// for longer than usual and that a register is not reused between
/// between the use (normally complete at "Before") and the def /// the use (normally complete at "Early") and the def (normally
/// (normally starting at "After"). See `Operand` for more. /// starting at "Late"). See `Operand` for more.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OperandPos { pub enum OperandPos {
Before = 0, Early = 0,
After = 1, Late = 1,
} }
/// An `Operand` encodes everything about a mention of a register in /// An `Operand` encodes everything about a mention of a register in
@@ -365,21 +366,20 @@ pub enum OperandPos {
/// `LAllocation` in Ion). /// `LAllocation` in Ion).
/// ///
/// Generally, regalloc2 considers operands to have their effects at /// Generally, regalloc2 considers operands to have their effects at
/// one of two program points that surround an instruction: "Before" /// one of two points that exist in an instruction: "Early" or
/// or "After". All operands at a given program-point are assigned /// "Late". All operands at a given program-point are assigned
/// non-conflicting locations based on their constraints. Each operand /// non-conflicting locations based on their constraints. Each operand
/// has a "kind", one of use/def/mod, corresponding to /// has a "kind", one of use/def/mod, corresponding to
/// read/write/read-write, respectively. /// read/write/read-write, respectively.
/// ///
/// Usually, an instruction's inputs will be uses-at-Before and /// Usually, an instruction's inputs will be "early uses" and outputs
/// outputs will be defs-at-After, though there are valid use-cases /// will be "late defs", though there are valid use-cases for other
/// for other combinations too. For example, a single "instruction" /// combinations too. For example, a single "instruction" seen by the
/// seen by the regalloc that lowers into multiple machine /// regalloc that lowers into multiple machine instructions and reads
/// instructions and reads some of its inputs after it starts to write /// some of its inputs after it starts to write outputs must either
/// outputs must either make those input(s) uses-at-After or those /// make those input(s) "late uses" or those output(s) "early defs" so
/// output(s) defs-at-Before so that the conflict (overlap) is /// that the conflict (overlap) is properly accounted for. See
/// properly accounted for. See comments on the constructors below for /// comments on the constructors below for more.
/// more.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Operand { pub struct Operand {
/// Bit-pack into 32 bits. /// Bit-pack into 32 bits.
@@ -437,7 +437,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reg, OperandConstraint::Reg,
OperandKind::Use, OperandKind::Use,
OperandPos::Before, OperandPos::Early,
) )
} }
@@ -450,7 +450,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reg, OperandConstraint::Reg,
OperandKind::Use, OperandKind::Use,
OperandPos::After, OperandPos::Late,
) )
} }
@@ -464,7 +464,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reg, OperandConstraint::Reg,
OperandKind::Def, OperandKind::Def,
OperandPos::After, OperandPos::Late,
) )
} }
@@ -478,7 +478,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reg, OperandConstraint::Reg,
OperandKind::Def, OperandKind::Def,
OperandPos::Before, OperandPos::Early,
) )
} }
@@ -496,7 +496,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reg, OperandConstraint::Reg,
OperandKind::Def, OperandKind::Def,
OperandPos::Before, OperandPos::Early,
) )
} }
@@ -511,7 +511,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::Reuse(idx), OperandConstraint::Reuse(idx),
OperandKind::Def, OperandKind::Def,
OperandPos::After, OperandPos::Late,
) )
} }
@@ -525,7 +525,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::FixedReg(preg), OperandConstraint::FixedReg(preg),
OperandKind::Use, OperandKind::Use,
OperandPos::Before, OperandPos::Early,
) )
} }
@@ -539,7 +539,7 @@ impl Operand {
vreg, vreg,
OperandConstraint::FixedReg(preg), OperandConstraint::FixedReg(preg),
OperandKind::Def, OperandKind::Def,
OperandPos::After, OperandPos::Late,
) )
} }
@@ -585,8 +585,8 @@ impl Operand {
pub fn pos(self) -> OperandPos { pub fn pos(self) -> OperandPos {
let pos_field = (self.bits >> 26) & 1; let pos_field = (self.bits >> 26) & 1;
match pos_field { match pos_field {
0 => OperandPos::Before, 0 => OperandPos::Early,
1 => OperandPos::After, 1 => OperandPos::Late,
_ => unreachable!(), _ => unreachable!(),
} }
} }
@@ -631,8 +631,8 @@ impl std::fmt::Debug for Operand {
impl std::fmt::Display for Operand { impl std::fmt::Display for Operand {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match (self.kind(), self.pos()) { match (self.kind(), self.pos()) {
(OperandKind::Def, OperandPos::After) (OperandKind::Def, OperandPos::Late)
| (OperandKind::Mod | OperandKind::Use, OperandPos::Before) => { | (OperandKind::Mod | OperandKind::Use, OperandPos::Early) => {
write!(f, "{:?}", self.kind())?; write!(f, "{:?}", self.kind())?;
} }
_ => { _ => {