Rename OperandPolicy to OperandConstraint as per feedback from @julian-seward1.
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
//! conceptually generates a symbolic value "Vn" when storing to (or
|
//! conceptually generates a symbolic value "Vn" when storing to (or
|
||||||
//! modifying) a virtual register.
|
//! modifying) a virtual register.
|
||||||
//!
|
//!
|
||||||
//! Operand policies (fixed register, register, any) are also checked
|
//! Operand constraints (fixed register, register, any) are also checked
|
||||||
//! at each operand.
|
//! at each operand.
|
||||||
//!
|
//!
|
||||||
//! The dataflow analysis state at each program point is:
|
//! The dataflow analysis state at each program point is:
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, AllocationKind, Block, Edit, Function, Inst, InstPosition, Operand, OperandKind,
|
Allocation, AllocationKind, Block, Edit, Function, Inst, InstPosition, Operand, OperandKind,
|
||||||
OperandPolicy, OperandPos, Output, PReg, ProgPoint, SpillSlot, VReg,
|
OperandConstraint, OperandPos, Output, PReg, ProgPoint, SpillSlot, VReg,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
@@ -106,7 +106,7 @@ pub enum CheckerError {
|
|||||||
alloc: Allocation,
|
alloc: Allocation,
|
||||||
actual: VReg,
|
actual: VReg,
|
||||||
},
|
},
|
||||||
PolicyViolated {
|
ConstraintViolated {
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
op: Operand,
|
op: Operand,
|
||||||
alloc: Allocation,
|
alloc: Allocation,
|
||||||
@@ -266,7 +266,7 @@ impl CheckerState {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.check_policy(inst, op, alloc, allocs)?;
|
self.check_constraint(inst, op, alloc, allocs)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -287,14 +287,14 @@ impl CheckerState {
|
|||||||
// happens early.
|
// happens early.
|
||||||
let has_reused_input = operands
|
let has_reused_input = operands
|
||||||
.iter()
|
.iter()
|
||||||
.any(|op| matches!(op.policy(), OperandPolicy::Reuse(_)));
|
.any(|op| matches!(op.constraint(), OperandConstraint::Reuse(_)));
|
||||||
if has_reused_input && pos == InstPosition::After {
|
if has_reused_input && pos == InstPosition::After {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each operand, check (i) that the allocation
|
// For each operand, check (i) that the allocation
|
||||||
// contains the expected vreg, and (ii) that it meets
|
// contains the expected vreg, and (ii) that it meets
|
||||||
// the requirements of the OperandPolicy.
|
// 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::Before, InstPosition::Before) => true,
|
||||||
@@ -413,31 +413,31 @@ impl CheckerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_policy(
|
fn check_constraint(
|
||||||
&self,
|
&self,
|
||||||
inst: Inst,
|
inst: Inst,
|
||||||
op: Operand,
|
op: Operand,
|
||||||
alloc: Allocation,
|
alloc: Allocation,
|
||||||
allocs: &[Allocation],
|
allocs: &[Allocation],
|
||||||
) -> Result<(), CheckerError> {
|
) -> Result<(), CheckerError> {
|
||||||
match op.policy() {
|
match op.constraint() {
|
||||||
OperandPolicy::Any => {}
|
OperandConstraint::Any => {}
|
||||||
OperandPolicy::Reg => {
|
OperandConstraint::Reg => {
|
||||||
if alloc.kind() != AllocationKind::Reg {
|
if alloc.kind() != AllocationKind::Reg {
|
||||||
return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
|
return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperandPolicy::Stack => {
|
OperandConstraint::Stack => {
|
||||||
if alloc.kind() != AllocationKind::Stack {
|
if alloc.kind() != AllocationKind::Stack {
|
||||||
return Err(CheckerError::AllocationIsNotStack { inst, op, alloc });
|
return Err(CheckerError::AllocationIsNotStack { inst, op, alloc });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperandPolicy::FixedReg(preg) => {
|
OperandConstraint::FixedReg(preg) => {
|
||||||
if alloc != Allocation::reg(preg) {
|
if alloc != Allocation::reg(preg) {
|
||||||
return Err(CheckerError::AllocationIsNotFixedReg { inst, op, alloc });
|
return Err(CheckerError::AllocationIsNotFixedReg { inst, op, alloc });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperandPolicy::Reuse(idx) => {
|
OperandConstraint::Reuse(idx) => {
|
||||||
if alloc.kind() != AllocationKind::Reg {
|
if alloc.kind() != AllocationKind::Reg {
|
||||||
return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
|
return Err(CheckerError::AllocationIsNotReg { inst, op, alloc });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domtree, postorder, Allocation, Block, Function, Inst, InstRange, MachineEnv, Operand,
|
domtree, postorder, Allocation, Block, Function, Inst, InstRange, MachineEnv, Operand,
|
||||||
OperandKind, OperandPolicy, OperandPos, PReg, RegClass, VReg,
|
OperandKind, OperandConstraint, OperandPos, PReg, RegClass, VReg,
|
||||||
};
|
};
|
||||||
|
|
||||||
use arbitrary::Result as ArbitraryResult;
|
use arbitrary::Result as ArbitraryResult;
|
||||||
@@ -230,9 +230,9 @@ impl FuncBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arbitrary for OperandPolicy {
|
impl Arbitrary for OperandConstraint {
|
||||||
fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
|
fn arbitrary(u: &mut Unstructured) -> ArbitraryResult<Self> {
|
||||||
Ok(*u.choose(&[OperandPolicy::Any, OperandPolicy::Reg])?)
|
Ok(*u.choose(&[OperandConstraint::Any, OperandConstraint::Reg])?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,13 +401,13 @@ impl Func {
|
|||||||
let mut avail = block_params[block].clone();
|
let mut avail = block_params[block].clone();
|
||||||
let mut remaining_nonlocal_uses = u.int_in_range(0..=3)?;
|
let mut remaining_nonlocal_uses = u.int_in_range(0..=3)?;
|
||||||
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_policy = OperandPolicy::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::Before
|
||||||
} else {
|
} else {
|
||||||
OperandPos::After
|
OperandPos::After
|
||||||
};
|
};
|
||||||
let mut operands = vec![Operand::new(vreg, def_policy, OperandKind::Def, def_pos)];
|
let mut operands = vec![Operand::new(vreg, def_constraint, OperandKind::Def, def_pos)];
|
||||||
let mut allocations = vec![Allocation::none()];
|
let mut allocations = vec![Allocation::none()];
|
||||||
for _ in 0..u.int_in_range(0..=3)? {
|
for _ in 0..u.int_in_range(0..=3)? {
|
||||||
let vreg = if avail.len() > 0
|
let vreg = if avail.len() > 0
|
||||||
@@ -433,10 +433,10 @@ impl Func {
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
let use_policy = OperandPolicy::arbitrary(u)?;
|
let use_constraint = OperandConstraint::arbitrary(u)?;
|
||||||
operands.push(Operand::new(
|
operands.push(Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
use_policy,
|
use_constraint,
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
));
|
));
|
||||||
@@ -450,14 +450,14 @@ impl Func {
|
|||||||
let reused = u.int_in_range(1..=(operands.len() - 1))?;
|
let reused = u.int_in_range(1..=(operands.len() - 1))?;
|
||||||
operands[0] = Operand::new(
|
operands[0] = Operand::new(
|
||||||
op.vreg(),
|
op.vreg(),
|
||||||
OperandPolicy::Reuse(reused),
|
OperandConstraint::Reuse(reused),
|
||||||
op.kind(),
|
op.kind(),
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
);
|
);
|
||||||
// Make sure reused input is a Reg.
|
// Make sure reused input is a Reg.
|
||||||
let op = operands[reused];
|
let op = operands[reused];
|
||||||
operands[reused] =
|
operands[reused] =
|
||||||
Operand::new(op.vreg(), OperandPolicy::Reg, op.kind(), OperandPos::Before);
|
Operand::new(op.vreg(), OperandConstraint::Reg, op.kind(), OperandPos::Before);
|
||||||
} else if opts.fixed_regs && bool::arbitrary(u)? {
|
} else if opts.fixed_regs && bool::arbitrary(u)? {
|
||||||
let mut fixed = vec![];
|
let mut fixed = vec![];
|
||||||
for _ in 0..u.int_in_range(0..=operands.len() - 1)? {
|
for _ in 0..u.int_in_range(0..=operands.len() - 1)? {
|
||||||
@@ -471,7 +471,7 @@ impl Func {
|
|||||||
let op = operands[i];
|
let op = operands[i];
|
||||||
operands[i] = Operand::new(
|
operands[i] = Operand::new(
|
||||||
op.vreg(),
|
op.vreg(),
|
||||||
OperandPolicy::FixedReg(fixed_reg),
|
OperandConstraint::FixedReg(fixed_reg),
|
||||||
op.kind(),
|
op.kind(),
|
||||||
op.pos(),
|
op.pos(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::bitvec::BitVec;
|
use crate::bitvec::BitVec;
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, Block, Function, Inst, InstPosition, Operand, OperandKind, OperandPolicy,
|
Allocation, Block, Function, Inst, InstPosition, Operand, OperandKind, OperandConstraint,
|
||||||
OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
OperandPos, PReg, ProgPoint, RegAllocError, VReg,
|
||||||
};
|
};
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
@@ -29,19 +29,19 @@ use std::collections::{HashSet, VecDeque};
|
|||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn spill_weight_from_policy(policy: OperandPolicy, loop_depth: usize, is_def: bool) -> u32 {
|
pub fn spill_weight_from_constraint(constraint: OperandConstraint, loop_depth: usize, is_def: bool) -> u32 {
|
||||||
// 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.
|
// Bound `loop_depth` at 2 so that `hot_bonus` is at most 16000.
|
||||||
let loop_depth = std::cmp::min(2, loop_depth);
|
let loop_depth = std::cmp::min(2, loop_depth);
|
||||||
let hot_bonus = 1000 * (1 << (2 * loop_depth));
|
let hot_bonus = 1000 * (1 << (2 * loop_depth));
|
||||||
let def_bonus = if is_def { 2000 } else { 0 };
|
let def_bonus = if is_def { 2000 } else { 0 };
|
||||||
let policy_bonus = match policy {
|
let constraint_bonus = match constraint {
|
||||||
OperandPolicy::Any => 1000,
|
OperandConstraint::Any => 1000,
|
||||||
OperandPolicy::Reg | OperandPolicy::FixedReg(_) => 2000,
|
OperandConstraint::Reg | OperandConstraint::FixedReg(_) => 2000,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
hot_bonus + def_bonus + policy_bonus
|
hot_bonus + def_bonus + constraint_bonus
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, F: Function> Env<'a, F> {
|
impl<'a, F: Function> Env<'a, F> {
|
||||||
@@ -184,11 +184,11 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
pub fn insert_use_into_liverange(&mut self, into: LiveRangeIndex, mut u: Use) {
|
pub fn insert_use_into_liverange(&mut self, into: LiveRangeIndex, mut u: Use) {
|
||||||
let operand = u.operand;
|
let operand = u.operand;
|
||||||
let policy = operand.policy();
|
let constraint = operand.constraint();
|
||||||
let block = self.cfginfo.insn_block[u.pos.inst().index()];
|
let block = self.cfginfo.insn_block[u.pos.inst().index()];
|
||||||
let loop_depth = self.cfginfo.approx_loop_depth[block.index()] as usize;
|
let loop_depth = self.cfginfo.approx_loop_depth[block.index()] as usize;
|
||||||
let weight =
|
let weight =
|
||||||
spill_weight_from_policy(policy, loop_depth, operand.kind() != OperandKind::Use);
|
spill_weight_from_constraint(constraint, loop_depth, operand.kind() != OperandKind::Use);
|
||||||
u.weight = u16::try_from(weight).expect("weight too large for u16 field");
|
u.weight = u16::try_from(weight).expect("weight too large for u16 field");
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@@ -415,7 +415,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// proper interference wrt other inputs.
|
// proper interference wrt other inputs.
|
||||||
let mut reused_input = None;
|
let mut reused_input = None;
|
||||||
for op in self.func.inst_operands(inst) {
|
for op in self.func.inst_operands(inst) {
|
||||||
if let OperandPolicy::Reuse(i) = op.policy() {
|
if let OperandConstraint::Reuse(i) = op.constraint() {
|
||||||
reused_input = Some(i);
|
reused_input = Some(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -465,12 +465,12 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let src_preg = match src.policy() {
|
let src_preg = match src.constraint() {
|
||||||
OperandPolicy::FixedReg(r) => r,
|
OperandConstraint::FixedReg(r) => r,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let dst_preg = match dst.policy() {
|
let dst_preg = match dst.constraint() {
|
||||||
OperandPolicy::FixedReg(r) => r,
|
OperandConstraint::FixedReg(r) => r,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
self.insert_move(
|
self.insert_move(
|
||||||
@@ -484,7 +484,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// If exactly one of source and dest (but not
|
// If exactly one of source and dest (but not
|
||||||
// both) is a pinned-vreg, convert this into a
|
// both) is a pinned-vreg, convert this into a
|
||||||
// ghost use on the other vreg with a FixedReg
|
// ghost use on the other vreg with a FixedReg
|
||||||
// policy.
|
// constraint.
|
||||||
else if self.vregs[src.vreg().vreg()].is_pinned
|
else if self.vregs[src.vreg().vreg()].is_pinned
|
||||||
|| self.vregs[dst.vreg().vreg()].is_pinned
|
|| self.vregs[dst.vreg().vreg()].is_pinned
|
||||||
{
|
{
|
||||||
@@ -513,20 +513,20 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
ProgPoint::after(inst),
|
ProgPoint::after(inst),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let policy = OperandPolicy::FixedReg(preg);
|
let constraint = OperandConstraint::FixedReg(preg);
|
||||||
let operand = Operand::new(vreg, policy, kind, pos);
|
let operand = Operand::new(vreg, constraint, kind, pos);
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
concat!(
|
concat!(
|
||||||
" -> preg {:?} vreg {:?} kind {:?} ",
|
" -> preg {:?} vreg {:?} kind {:?} ",
|
||||||
"pos {:?} progpoint {:?} policy {:?} operand {:?}"
|
"pos {:?} progpoint {:?} constraint {:?} operand {:?}"
|
||||||
),
|
),
|
||||||
preg,
|
preg,
|
||||||
vreg,
|
vreg,
|
||||||
kind,
|
kind,
|
||||||
pos,
|
pos,
|
||||||
progpoint,
|
progpoint,
|
||||||
policy,
|
constraint,
|
||||||
operand
|
operand
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -701,23 +701,23 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
// positions of After and Before respectively
|
// positions of After and Before respectively
|
||||||
// (see note below), and to have Any
|
// (see note below), and to have Any
|
||||||
// constraints if they were originally Reg.
|
// constraints if they were originally Reg.
|
||||||
let src_policy = match src.policy() {
|
let src_constraint = match src.constraint() {
|
||||||
OperandPolicy::Reg => OperandPolicy::Any,
|
OperandConstraint::Reg => OperandConstraint::Any,
|
||||||
x => x,
|
x => x,
|
||||||
};
|
};
|
||||||
let dst_policy = match dst.policy() {
|
let dst_constraint = match dst.constraint() {
|
||||||
OperandPolicy::Reg => OperandPolicy::Any,
|
OperandConstraint::Reg => OperandConstraint::Any,
|
||||||
x => x,
|
x => x,
|
||||||
};
|
};
|
||||||
let src = Operand::new(
|
let src = Operand::new(
|
||||||
src.vreg(),
|
src.vreg(),
|
||||||
src_policy,
|
src_constraint,
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
);
|
);
|
||||||
let dst = Operand::new(
|
let dst = Operand::new(
|
||||||
dst.vreg(),
|
dst.vreg(),
|
||||||
dst_policy,
|
dst_constraint,
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
);
|
);
|
||||||
@@ -728,9 +728,9 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
format!(
|
format!(
|
||||||
" prog-move v{} ({:?}) -> v{} ({:?})",
|
" prog-move v{} ({:?}) -> v{} ({:?})",
|
||||||
src.vreg().vreg(),
|
src.vreg().vreg(),
|
||||||
src_policy,
|
src_constraint,
|
||||||
dst.vreg().vreg(),
|
dst.vreg().vreg(),
|
||||||
dst_policy,
|
dst_constraint,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1049,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
let pos = ProgPoint::before(self.safepoints[safepoint_idx]);
|
let pos = ProgPoint::before(self.safepoints[safepoint_idx]);
|
||||||
let operand = Operand::new(
|
let operand = Operand::new(
|
||||||
self.vreg_regs[vreg.index()],
|
self.vreg_regs[vreg.index()],
|
||||||
OperandPolicy::Stack,
|
OperandConstraint::Stack,
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
);
|
);
|
||||||
@@ -1116,7 +1116,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
}
|
}
|
||||||
last_point = Some(pos);
|
last_point = Some(pos);
|
||||||
|
|
||||||
if let OperandPolicy::FixedReg(preg) = op.policy() {
|
if let OperandConstraint::FixedReg(preg) = op.constraint() {
|
||||||
let vreg_idx = VRegIndex::new(op.vreg().vreg());
|
let vreg_idx = VRegIndex::new(op.vreg().vreg());
|
||||||
let preg_idx = PRegIndex::new(preg.index());
|
let preg_idx = PRegIndex::new(preg.index());
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@@ -1129,11 +1129,11 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
{
|
{
|
||||||
let orig_preg = first_preg[idx];
|
let orig_preg = first_preg[idx];
|
||||||
if orig_preg != preg_idx {
|
if orig_preg != preg_idx {
|
||||||
log::debug!(" -> duplicate; switching to policy Reg");
|
log::debug!(" -> duplicate; switching to constraint Reg");
|
||||||
fixups.push((pos, orig_preg, preg_idx, slot));
|
fixups.push((pos, orig_preg, preg_idx, slot));
|
||||||
*op = Operand::new(
|
*op = Operand::new(
|
||||||
op.vreg(),
|
op.vreg(),
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
op.kind(),
|
op.kind(),
|
||||||
op.pos(),
|
op.pos(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use super::{
|
|||||||
Env, LiveBundleIndex, LiveRangeIndex, LiveRangeKey, Requirement, SpillSet, SpillSetIndex,
|
Env, LiveBundleIndex, LiveRangeIndex, LiveRangeKey, Requirement, SpillSet, SpillSetIndex,
|
||||||
SpillSlotIndex, VRegIndex,
|
SpillSlotIndex, VRegIndex,
|
||||||
};
|
};
|
||||||
use crate::{Function, Inst, OperandPolicy, PReg};
|
use crate::{Function, Inst, OperandConstraint, PReg};
|
||||||
use smallvec::smallvec;
|
use smallvec::smallvec;
|
||||||
|
|
||||||
impl<'a, F: Function> Env<'a, F> {
|
impl<'a, F: Function> Env<'a, F> {
|
||||||
@@ -269,10 +269,10 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
let mut stack = false;
|
let mut stack = false;
|
||||||
for entry in &self.bundles[bundle.index()].ranges {
|
for entry in &self.bundles[bundle.index()].ranges {
|
||||||
for u in &self.ranges[entry.index.index()].uses {
|
for u in &self.ranges[entry.index.index()].uses {
|
||||||
if let OperandPolicy::FixedReg(_) = u.operand.policy() {
|
if let OperandConstraint::FixedReg(_) = u.operand.constraint() {
|
||||||
fixed = true;
|
fixed = true;
|
||||||
}
|
}
|
||||||
if let OperandPolicy::Stack = u.operand.policy() {
|
if let OperandConstraint::Stack = u.operand.constraint() {
|
||||||
stack = true;
|
stack = true;
|
||||||
}
|
}
|
||||||
if fixed && stack {
|
if fixed && stack {
|
||||||
@@ -306,10 +306,10 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
for inst in 0..self.func.insts() {
|
for inst in 0..self.func.insts() {
|
||||||
let inst = Inst::new(inst);
|
let inst = Inst::new(inst);
|
||||||
|
|
||||||
// Attempt to merge Reuse-policy operand outputs with the
|
// Attempt to merge Reuse-constraint operand outputs with the
|
||||||
// corresponding inputs.
|
// corresponding inputs.
|
||||||
for op in self.func.inst_operands(inst) {
|
for op in self.func.inst_operands(inst) {
|
||||||
if let OperandPolicy::Reuse(reuse_idx) = op.policy() {
|
if let OperandConstraint::Reuse(reuse_idx) = op.constraint() {
|
||||||
let src_vreg = op.vreg();
|
let src_vreg = op.vreg();
|
||||||
let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
|
let dst_vreg = self.func.inst_operands(inst)[reuse_idx].vreg();
|
||||||
if self.vregs[src_vreg.vreg()].is_pinned
|
if self.vregs[src_vreg.vreg()].is_pinned
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use super::{
|
|||||||
|
|
||||||
use crate::moves::ParallelMoves;
|
use crate::moves::ParallelMoves;
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, Block, Edit, Function, Inst, InstPosition, OperandKind, OperandPolicy, OperandPos,
|
Allocation, Block, Edit, Function, Inst, InstPosition, OperandKind, OperandConstraint, OperandPos,
|
||||||
ProgPoint, RegClass, VReg,
|
ProgPoint, RegClass, VReg,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@@ -489,7 +489,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
if slot != SLOT_NONE {
|
if slot != SLOT_NONE {
|
||||||
self.set_alloc(inst, slot as usize, alloc);
|
self.set_alloc(inst, slot as usize, alloc);
|
||||||
}
|
}
|
||||||
if let OperandPolicy::Reuse(_) = operand.policy() {
|
if let OperandConstraint::Reuse(_) = operand.constraint() {
|
||||||
reuse_input_insts.push(inst);
|
reuse_input_insts.push(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -755,7 +755,7 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
let mut input_reused: SmallVec<[usize; 4]> = smallvec![];
|
let mut input_reused: SmallVec<[usize; 4]> = smallvec![];
|
||||||
for output_idx in 0..self.func.inst_operands(inst).len() {
|
for output_idx in 0..self.func.inst_operands(inst).len() {
|
||||||
let operand = self.func.inst_operands(inst)[output_idx];
|
let operand = self.func.inst_operands(inst)[output_idx];
|
||||||
if let OperandPolicy::Reuse(input_idx) = operand.policy() {
|
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::After);
|
||||||
input_reused.push(input_idx);
|
input_reused.push(input_idx);
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
//! Main allocation loop that processes bundles.
|
//! Main allocation loop that processes bundles.
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
spill_weight_from_policy, 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, UseList,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Allocation, Function, Inst, InstPosition, OperandKind, OperandPolicy, PReg, ProgPoint,
|
Allocation, Function, Inst, InstPosition, OperandKind, OperandConstraint, PReg, ProgPoint,
|
||||||
RegAllocError,
|
RegAllocError,
|
||||||
};
|
};
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
@@ -273,11 +273,11 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
} else {
|
} else {
|
||||||
for u in &first_range_data.uses {
|
for u in &first_range_data.uses {
|
||||||
log::debug!(" -> use: {:?}", u);
|
log::debug!(" -> use: {:?}", u);
|
||||||
if let OperandPolicy::FixedReg(_) = u.operand.policy() {
|
if let OperandConstraint::FixedReg(_) = u.operand.constraint() {
|
||||||
log::debug!(" -> fixed use at {:?}: {:?}", u.pos, u.operand);
|
log::debug!(" -> fixed use at {:?}: {:?}", u.pos, u.operand);
|
||||||
fixed = true;
|
fixed = true;
|
||||||
}
|
}
|
||||||
if let OperandPolicy::Stack = u.operand.policy() {
|
if let OperandConstraint::Stack = u.operand.constraint() {
|
||||||
log::debug!(" -> stack use at {:?}: {:?}", u.pos, u.operand);
|
log::debug!(" -> stack use at {:?}: {:?}", u.pos, u.operand);
|
||||||
stack = true;
|
stack = true;
|
||||||
}
|
}
|
||||||
@@ -886,8 +886,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
let loop_depth = self.cfginfo.approx_loop_depth
|
let loop_depth = self.cfginfo.approx_loop_depth
|
||||||
[self.cfginfo.insn_block[first_conflict_point.inst().index()].index()];
|
[self.cfginfo.insn_block[first_conflict_point.inst().index()].index()];
|
||||||
let move_cost = spill_weight_from_policy(
|
let move_cost = spill_weight_from_constraint(
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
loop_depth as usize,
|
loop_depth as usize,
|
||||||
/* is_def = */ true,
|
/* is_def = */ true,
|
||||||
);
|
);
|
||||||
@@ -905,8 +905,8 @@ impl<'a, F: Function> Env<'a, F> {
|
|||||||
|
|
||||||
let loop_depth = self.cfginfo.approx_loop_depth
|
let loop_depth = self.cfginfo.approx_loop_depth
|
||||||
[self.cfginfo.insn_block[point.inst().index()].index()];
|
[self.cfginfo.insn_block[point.inst().index()].index()];
|
||||||
let move_cost = spill_weight_from_policy(
|
let move_cost = spill_weight_from_constraint(
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
loop_depth as usize,
|
loop_depth as usize,
|
||||||
/* is_def = */ true,
|
/* is_def = */ true,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Requirements computation.
|
//! Requirements computation.
|
||||||
|
|
||||||
use super::{Env, LiveBundleIndex};
|
use super::{Env, LiveBundleIndex};
|
||||||
use crate::{Function, Operand, OperandPolicy, PReg, RegClass};
|
use crate::{Function, Operand, OperandConstraint, PReg, RegClass};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Requirement {
|
pub enum Requirement {
|
||||||
@@ -64,10 +64,10 @@ impl Requirement {
|
|||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_operand(op: Operand) -> Requirement {
|
pub fn from_operand(op: Operand) -> Requirement {
|
||||||
match op.policy() {
|
match op.constraint() {
|
||||||
OperandPolicy::FixedReg(preg) => Requirement::Fixed(preg),
|
OperandConstraint::FixedReg(preg) => Requirement::Fixed(preg),
|
||||||
OperandPolicy::Reg | OperandPolicy::Reuse(_) => Requirement::Register(op.class()),
|
OperandConstraint::Reg | OperandConstraint::Reuse(_) => Requirement::Register(op.class()),
|
||||||
OperandPolicy::Stack => Requirement::Stack(op.class()),
|
OperandConstraint::Stack => Requirement::Stack(op.class()),
|
||||||
_ => Requirement::Any(op.class()),
|
_ => Requirement::Any(op.class()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
src/lib.rs
66
src/lib.rs
@@ -210,34 +210,34 @@ impl std::fmt::Display for SpillSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An `Operand` encodes everything about a mention of a register in
|
/// An `Operand` encodes everything about a mention of a register in
|
||||||
/// an instruction: virtual register number, and any constraint/policy
|
/// an instruction: virtual register number, and any constraint that
|
||||||
/// that applies to the register at this program point.
|
/// applies to the register at this program point.
|
||||||
///
|
///
|
||||||
/// An Operand may be a use or def (this corresponds to `LUse` and
|
/// An Operand may be a use or def (this corresponds to `LUse` and
|
||||||
/// `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 `policy` overlaps with `kind`
|
/// Bit-pack into 32 bits. Note that `constraint` overlaps with `kind`
|
||||||
/// in `Allocation` and we use mutually disjoint tag-value ranges
|
/// in `Allocation` and we use mutually disjoint tag-value ranges
|
||||||
/// so that clients, if they wish, can track just one `u32` per
|
/// so that clients, if they wish, can track just one `u32` per
|
||||||
/// register slot and edit it in-place after allocation.
|
/// register slot and edit it in-place after allocation.
|
||||||
///
|
///
|
||||||
/// policy: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,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Operand {
|
impl Operand {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(vreg: VReg, policy: OperandPolicy, kind: OperandKind, pos: OperandPos) -> Self {
|
pub fn new(vreg: VReg, constraint: OperandConstraint, kind: OperandKind, pos: OperandPos) -> Self {
|
||||||
let (preg_field, policy_field): (u32, u32) = match policy {
|
let (preg_field, constraint_field): (u32, u32) = match constraint {
|
||||||
OperandPolicy::Any => (0, 0),
|
OperandConstraint::Any => (0, 0),
|
||||||
OperandPolicy::Reg => (0, 1),
|
OperandConstraint::Reg => (0, 1),
|
||||||
OperandPolicy::Stack => (0, 2),
|
OperandConstraint::Stack => (0, 2),
|
||||||
OperandPolicy::FixedReg(preg) => {
|
OperandConstraint::FixedReg(preg) => {
|
||||||
assert_eq!(preg.class(), vreg.class());
|
assert_eq!(preg.class(), vreg.class());
|
||||||
(preg.hw_enc() as u32, 3)
|
(preg.hw_enc() as u32, 3)
|
||||||
}
|
}
|
||||||
OperandPolicy::Reuse(which) => {
|
OperandConstraint::Reuse(which) => {
|
||||||
assert!(which <= PReg::MAX);
|
assert!(which <= PReg::MAX);
|
||||||
(which as u32, 4)
|
(which as u32, 4)
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ impl Operand {
|
|||||||
| (class_field << 25)
|
| (class_field << 25)
|
||||||
| (pos_field << 26)
|
| (pos_field << 26)
|
||||||
| (kind_field << 27)
|
| (kind_field << 27)
|
||||||
| (policy_field << 29),
|
| (constraint_field << 29),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ impl Operand {
|
|||||||
pub fn reg_use(vreg: VReg) -> Self {
|
pub fn reg_use(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
@@ -268,7 +268,7 @@ impl Operand {
|
|||||||
pub fn reg_use_at_end(vreg: VReg) -> Self {
|
pub fn reg_use_at_end(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
@@ -277,7 +277,7 @@ impl Operand {
|
|||||||
pub fn reg_def(vreg: VReg) -> Self {
|
pub fn reg_def(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
@@ -286,7 +286,7 @@ impl Operand {
|
|||||||
pub fn reg_def_at_start(vreg: VReg) -> Self {
|
pub fn reg_def_at_start(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
@@ -295,7 +295,7 @@ impl Operand {
|
|||||||
pub fn reg_temp(vreg: VReg) -> Self {
|
pub fn reg_temp(vreg: VReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reg,
|
OperandConstraint::Reg,
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
@@ -304,7 +304,7 @@ impl Operand {
|
|||||||
pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self {
|
pub fn reg_reuse_def(vreg: VReg, idx: usize) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::Reuse(idx),
|
OperandConstraint::Reuse(idx),
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
@@ -313,7 +313,7 @@ impl Operand {
|
|||||||
pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self {
|
pub fn reg_fixed_use(vreg: VReg, preg: PReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::FixedReg(preg),
|
OperandConstraint::FixedReg(preg),
|
||||||
OperandKind::Use,
|
OperandKind::Use,
|
||||||
OperandPos::Before,
|
OperandPos::Before,
|
||||||
)
|
)
|
||||||
@@ -322,7 +322,7 @@ impl Operand {
|
|||||||
pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self {
|
pub fn reg_fixed_def(vreg: VReg, preg: PReg) -> Self {
|
||||||
Operand::new(
|
Operand::new(
|
||||||
vreg,
|
vreg,
|
||||||
OperandPolicy::FixedReg(preg),
|
OperandConstraint::FixedReg(preg),
|
||||||
OperandKind::Def,
|
OperandKind::Def,
|
||||||
OperandPos::After,
|
OperandPos::After,
|
||||||
)
|
)
|
||||||
@@ -366,15 +366,15 @@ impl Operand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn policy(self) -> OperandPolicy {
|
pub fn constraint(self) -> OperandConstraint {
|
||||||
let policy_field = (self.bits >> 29) & 7;
|
let constraint_field = (self.bits >> 29) & 7;
|
||||||
let preg_field = ((self.bits >> 20) as usize) & PReg::MAX;
|
let preg_field = ((self.bits >> 20) as usize) & PReg::MAX;
|
||||||
match policy_field {
|
match constraint_field {
|
||||||
0 => OperandPolicy::Any,
|
0 => OperandConstraint::Any,
|
||||||
1 => OperandPolicy::Reg,
|
1 => OperandConstraint::Reg,
|
||||||
2 => OperandPolicy::Stack,
|
2 => OperandConstraint::Stack,
|
||||||
3 => OperandPolicy::FixedReg(PReg::new(preg_field, self.class())),
|
3 => OperandConstraint::FixedReg(PReg::new(preg_field, self.class())),
|
||||||
4 => OperandPolicy::Reuse(preg_field),
|
4 => OperandConstraint::Reuse(preg_field),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,13 +409,13 @@ impl std::fmt::Display for Operand {
|
|||||||
RegClass::Int => "i",
|
RegClass::Int => "i",
|
||||||
RegClass::Float => "f",
|
RegClass::Float => "f",
|
||||||
},
|
},
|
||||||
self.policy()
|
self.constraint()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum OperandPolicy {
|
pub enum OperandConstraint {
|
||||||
/// Any location is fine (register or stack slot).
|
/// Any location is fine (register or stack slot).
|
||||||
Any,
|
Any,
|
||||||
/// Operand must be in a register. Register is read-only for Uses.
|
/// Operand must be in a register. Register is read-only for Uses.
|
||||||
@@ -428,7 +428,7 @@ pub enum OperandPolicy {
|
|||||||
Reuse(usize),
|
Reuse(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for OperandPolicy {
|
impl std::fmt::Display for OperandConstraint {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Any => write!(f, "any"),
|
Self::Any => write!(f, "any"),
|
||||||
@@ -458,7 +458,7 @@ pub enum OperandPos {
|
|||||||
#[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. Note that `kind` overlaps with the
|
||||||
/// `policy` field in `Operand`, and we are careful to use
|
/// `constraint` field in `Operand`, and we are careful to use
|
||||||
/// disjoint ranges of values in this field for each type. We also
|
/// disjoint ranges of values in this field for each type. We also
|
||||||
/// leave the def-or-use bit (`kind` for `Operand`) unused here so
|
/// leave the def-or-use bit (`kind` for `Operand`) unused here so
|
||||||
/// that we can use it below in `OperandOrAllocation` to record
|
/// that we can use it below in `OperandOrAllocation` to record
|
||||||
@@ -570,7 +570,7 @@ impl Allocation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: These values must be *disjoint* with the values used to
|
// N.B.: These values must be *disjoint* with the values used to
|
||||||
// encode `OperandPolicy`, because they share a 3-bit field.
|
// 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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user