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:
Jakob Stoklund Olesen
2017-04-25 15:40:06 -07:00
parent 2f866171ca
commit 1e51f97108
3 changed files with 53 additions and 8 deletions

View File

@@ -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.

View File

@@ -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)
}
} }
} }

View File

@@ -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));
}
}
} }
} }
} }