Rename Affinity::Any to Affinity::None.
This value affinity doesn't mean "whatever", it means that the value does not interact with any real instructions, where "real" means instructions that have a legal encoding. Update the liveness verifier to check this property: - Encoded instructions can only use real values. - Encoded instructions define real values. - Ghost instructions define ghost values.
This commit is contained in:
@@ -15,8 +15,11 @@ use isa::{TargetIsa, RegInfo, RegClassIndex, OperandConstraint, ConstraintKind};
|
|||||||
/// Preferred register allocation for an SSA value.
|
/// Preferred register allocation for an SSA value.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Affinity {
|
pub enum Affinity {
|
||||||
/// Don't care. This value can go anywhere.
|
/// No affinity.
|
||||||
Any,
|
///
|
||||||
|
/// This indicates a value that is not defined or used by any real instructions. It is a ghost
|
||||||
|
/// value that won't appear in the final program.
|
||||||
|
None,
|
||||||
|
|
||||||
/// This value should be placed in a spill slot on the stack.
|
/// This value should be placed in a spill slot on the stack.
|
||||||
Stack,
|
Stack,
|
||||||
@@ -27,14 +30,14 @@ pub enum Affinity {
|
|||||||
|
|
||||||
impl Default for Affinity {
|
impl Default for Affinity {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Affinity::Any
|
Affinity::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Affinity {
|
impl Affinity {
|
||||||
/// Create an affinity that satisfies a single constraint.
|
/// Create an affinity that satisfies a single constraint.
|
||||||
///
|
///
|
||||||
/// This will never create the indifferent `Affinity::Any`.
|
/// This will never create an `Affinity::None`.
|
||||||
/// Use the `Default` implementation for that.
|
/// Use the `Default` implementation for that.
|
||||||
pub fn new(constraint: &OperandConstraint) -> Affinity {
|
pub fn new(constraint: &OperandConstraint) -> Affinity {
|
||||||
if constraint.kind == ConstraintKind::Stack {
|
if constraint.kind == ConstraintKind::Stack {
|
||||||
@@ -47,19 +50,27 @@ impl Affinity {
|
|||||||
/// Create an affinity that matches an ABI argument for `isa`.
|
/// Create an affinity that matches an ABI argument for `isa`.
|
||||||
pub fn abi(arg: &ArgumentType, isa: &TargetIsa) -> Affinity {
|
pub fn abi(arg: &ArgumentType, isa: &TargetIsa) -> Affinity {
|
||||||
match arg.location {
|
match arg.location {
|
||||||
ArgumentLoc::Unassigned => Affinity::Any,
|
ArgumentLoc::Unassigned => Affinity::None,
|
||||||
ArgumentLoc::Reg(_) => Affinity::Reg(isa.regclass_for_abi_type(arg.value_type).into()),
|
ArgumentLoc::Reg(_) => Affinity::Reg(isa.regclass_for_abi_type(arg.value_type).into()),
|
||||||
ArgumentLoc::Stack(_) => Affinity::Stack,
|
ArgumentLoc::Stack(_) => Affinity::Stack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this the `None` affinity?
|
||||||
|
pub fn is_none(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Affinity::None => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Merge an operand constraint into this affinity.
|
/// Merge an operand constraint into this affinity.
|
||||||
///
|
///
|
||||||
/// Note that this does not guarantee that the register allocator will pick a register that
|
/// Note that this does not guarantee that the register allocator will pick a register that
|
||||||
/// satisfies the constraint.
|
/// satisfies the constraint.
|
||||||
pub fn merge(&mut self, constraint: &OperandConstraint, reg_info: &RegInfo) {
|
pub fn merge(&mut self, constraint: &OperandConstraint, reg_info: &RegInfo) {
|
||||||
match *self {
|
match *self {
|
||||||
Affinity::Any => *self = Affinity::new(constraint),
|
Affinity::None => *self = Affinity::new(constraint),
|
||||||
Affinity::Reg(rc) => {
|
Affinity::Reg(rc) => {
|
||||||
// If the preferred register class is a subclass of the constraint, there's no need
|
// If the preferred register class is a subclass of the constraint, there's no need
|
||||||
// to change anything.
|
// to change anything.
|
||||||
|
|||||||
@@ -327,7 +327,9 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Affinity::Stack => unimplemented!(),
|
Affinity::Stack => unimplemented!(),
|
||||||
Affinity::Any => unimplemented!(),
|
Affinity::None => {
|
||||||
|
panic!("Encoded instruction defines {} with no affinity", lv.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ use verifier::Result;
|
|||||||
///
|
///
|
||||||
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
|
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
|
||||||
/// all values.
|
/// all values.
|
||||||
pub fn verify_liveness(_isa: &TargetIsa,
|
pub fn verify_liveness(isa: &TargetIsa,
|
||||||
func: &Function,
|
func: &Function,
|
||||||
cfg: &ControlFlowGraph,
|
cfg: &ControlFlowGraph,
|
||||||
liveness: &Liveness)
|
liveness: &Liveness)
|
||||||
-> Result {
|
-> Result {
|
||||||
let verifier = LivenessVerifier {
|
let verifier = LivenessVerifier {
|
||||||
|
isa: isa,
|
||||||
func: func,
|
func: func,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
liveness: liveness,
|
liveness: liveness,
|
||||||
@@ -37,6 +38,7 @@ pub fn verify_liveness(_isa: &TargetIsa,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LivenessVerifier<'a> {
|
struct LivenessVerifier<'a> {
|
||||||
|
isa: &'a TargetIsa,
|
||||||
func: &'a Function,
|
func: &'a Function,
|
||||||
cfg: &'a ControlFlowGraph,
|
cfg: &'a ControlFlowGraph,
|
||||||
liveness: &'a Liveness,
|
liveness: &'a Liveness,
|
||||||
@@ -61,6 +63,8 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
fn check_insts(&self) -> Result {
|
fn check_insts(&self) -> Result {
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
for inst in self.func.layout.ebb_insts(ebb) {
|
for inst in self.func.layout.ebb_insts(ebb) {
|
||||||
|
let encoding = self.func.encodings.get_or_default(inst);
|
||||||
|
|
||||||
// Check the defs.
|
// Check the defs.
|
||||||
for &val in self.func.dfg.inst_results(inst) {
|
for &val in self.func.dfg.inst_results(inst) {
|
||||||
let lr = match self.liveness.get(val) {
|
let lr = match self.liveness.get(val) {
|
||||||
@@ -68,6 +72,24 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
None => return err!(inst, "{} has no live range", val),
|
None => return err!(inst, "{} has no live range", val),
|
||||||
};
|
};
|
||||||
self.check_lr(inst.into(), val, lr)?;
|
self.check_lr(inst.into(), val, lr)?;
|
||||||
|
|
||||||
|
if encoding.is_legal() {
|
||||||
|
// A legal instruction is not allowed to define ghost values.
|
||||||
|
if lr.affinity.is_none() {
|
||||||
|
return err!(inst,
|
||||||
|
"{} is a ghost value defined by a real [{}] instruction",
|
||||||
|
val,
|
||||||
|
self.isa.encoding_info().display(encoding));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// A non-encoded instruction can only define ghost values.
|
||||||
|
if !lr.affinity.is_none() {
|
||||||
|
return err!(inst,
|
||||||
|
"{} is a real {} value defined by a ghost instruction",
|
||||||
|
val,
|
||||||
|
lr.affinity.display(&self.isa.register_info()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the uses.
|
// Check the uses.
|
||||||
@@ -79,6 +101,16 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
if !self.live_at_use(lr, inst) {
|
if !self.live_at_use(lr, inst) {
|
||||||
return err!(inst, "{} is not live at this use", val);
|
return err!(inst, "{} is not live at this use", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if encoding.is_legal() {
|
||||||
|
// A legal instruction is not allowed to depend on ghost values.
|
||||||
|
if lr.affinity.is_none() {
|
||||||
|
return err!(inst,
|
||||||
|
"{} is a ghost value used by a real [{}] instruction",
|
||||||
|
val,
|
||||||
|
self.isa.encoding_info().display(encoding));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user