diff --git a/lib/cretonne/src/regalloc/affinity.rs b/lib/cretonne/src/regalloc/affinity.rs index f6ca587485..64d2b640df 100644 --- a/lib/cretonne/src/regalloc/affinity.rs +++ b/lib/cretonne/src/regalloc/affinity.rs @@ -15,8 +15,11 @@ use isa::{TargetIsa, RegInfo, RegClassIndex, OperandConstraint, ConstraintKind}; /// Preferred register allocation for an SSA value. #[derive(Clone, Copy)] pub enum Affinity { - /// Don't care. This value can go anywhere. - Any, + /// No affinity. + /// + /// 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. Stack, @@ -27,14 +30,14 @@ pub enum Affinity { impl Default for Affinity { fn default() -> Self { - Affinity::Any + Affinity::None } } impl Affinity { /// 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. pub fn new(constraint: &OperandConstraint) -> Affinity { if constraint.kind == ConstraintKind::Stack { @@ -47,19 +50,27 @@ impl Affinity { /// Create an affinity that matches an ABI argument for `isa`. pub fn abi(arg: &ArgumentType, isa: &TargetIsa) -> Affinity { 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::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. /// /// Note that this does not guarantee that the register allocator will pick a register that /// satisfies the constraint. pub fn merge(&mut self, constraint: &OperandConstraint, reg_info: &RegInfo) { match *self { - Affinity::Any => *self = Affinity::new(constraint), + Affinity::None => *self = Affinity::new(constraint), Affinity::Reg(rc) => { // If the preferred register class is a subclass of the constraint, there's no need // to change anything. diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index b9e5fe7148..84e81660d7 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -327,7 +327,9 @@ impl<'a> Context<'a> { } } Affinity::Stack => unimplemented!(), - Affinity::Any => unimplemented!(), + Affinity::None => { + panic!("Encoded instruction defines {} with no affinity", lv.value) + } } } diff --git a/lib/cretonne/src/verifier/liveness.rs b/lib/cretonne/src/verifier/liveness.rs index d80f18a8b3..d3d841b68e 100644 --- a/lib/cretonne/src/verifier/liveness.rs +++ b/lib/cretonne/src/verifier/liveness.rs @@ -21,12 +21,13 @@ use verifier::Result; /// /// We don't verify that live ranges are minimal. This would require recomputing live ranges for /// all values. -pub fn verify_liveness(_isa: &TargetIsa, +pub fn verify_liveness(isa: &TargetIsa, func: &Function, cfg: &ControlFlowGraph, liveness: &Liveness) -> Result { let verifier = LivenessVerifier { + isa: isa, func: func, cfg: cfg, liveness: liveness, @@ -37,6 +38,7 @@ pub fn verify_liveness(_isa: &TargetIsa, } struct LivenessVerifier<'a> { + isa: &'a TargetIsa, func: &'a Function, cfg: &'a ControlFlowGraph, liveness: &'a Liveness, @@ -61,6 +63,8 @@ impl<'a> LivenessVerifier<'a> { fn check_insts(&self) -> Result { for ebb in self.func.layout.ebbs() { for inst in self.func.layout.ebb_insts(ebb) { + let encoding = self.func.encodings.get_or_default(inst); + // Check the defs. for &val in self.func.dfg.inst_results(inst) { let lr = match self.liveness.get(val) { @@ -68,6 +72,24 @@ impl<'a> LivenessVerifier<'a> { None => return err!(inst, "{} has no live range", val), }; 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. @@ -79,6 +101,16 @@ impl<'a> LivenessVerifier<'a> { if !self.live_at_use(lr, inst) { 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)); + } + } } } }