diff --git a/cranelift/filetests/isa/intel/binary64.cton b/cranelift/filetests/isa/intel/binary64.cton index e67f630753..a70c9fdf08 100644 --- a/cranelift/filetests/isa/intel/binary64.cton +++ b/cranelift/filetests/isa/intel/binary64.cton @@ -1,6 +1,7 @@ ; binary emission of 64-bit code. test binemit set is_64bit +set is_compressed isa intel haswell ; The binary encodings can be verified with the command: diff --git a/cranelift/src/filetest/binemit.rs b/cranelift/src/filetest/binemit.rs index 6fd4ed1fcf..0ea634548a 100644 --- a/cranelift/src/filetest/binemit.rs +++ b/cranelift/src/filetest/binemit.rs @@ -104,16 +104,32 @@ impl SubTest for TestBinEmit { // value locations. The current error reporting is just crashing... let mut func = func.into_owned(); + let is_compressed = isa.flags().is_compressed(); + // Give an encoding to any instruction that doesn't already have one. for ebb in func.layout.ebbs() { for inst in func.layout.ebb_insts(ebb) { if !func.encodings[inst].is_legal() { - if let Ok(enc) = isa.encode( + let mut legal_encodings = isa.legal_encodings( &func.dfg, &func.dfg[inst], func.dfg.ctrl_typevar(inst), - ) - { + ); + + let enc = if is_compressed { + // Get the smallest legal encoding + legal_encodings + .filter(|e| { + let recipe_constraints = &encinfo.constraints[e.recipe()]; + recipe_constraints.satisfied(inst, &func) + }) + .min_by_key(|&e| encinfo.bytes(e)) + } else { + // If not using compressed, just use the first encoding. + legal_encodings.next() + }; + + if let Some(enc) = enc { func.encodings[inst] = enc; } } diff --git a/lib/cretonne/meta/isa/intel/recipes.py b/lib/cretonne/meta/isa/intel/recipes.py index 8d07f06de1..c271ab1e34 100644 --- a/lib/cretonne/meta/isa/intel/recipes.py +++ b/lib/cretonne/meta/isa/intel/recipes.py @@ -13,7 +13,7 @@ from .registers import GPR, ABCD, FPR try: from typing import Tuple, Dict # noqa from cdsl.instructions import InstructionFormat # noqa - from cdsl.isa import ConstraintSeq, BranchRange, PredNode # noqa + from cdsl.isa import ConstraintSeq, BranchRange, PredNode, OperandConstraint # noqa except ImportError: pass diff --git a/lib/cretonne/src/isa/constraints.rs b/lib/cretonne/src/isa/constraints.rs index c5f1d4bd16..d9fc3aa8ea 100644 --- a/lib/cretonne/src/isa/constraints.rs +++ b/lib/cretonne/src/isa/constraints.rs @@ -9,6 +9,7 @@ use binemit::CodeOffset; use isa::{RegClass, RegUnit}; +use ir::{Function, ValueLoc, Inst}; /// Register constraint for a single value operand or instruction result. pub struct OperandConstraint { @@ -21,6 +22,34 @@ pub struct OperandConstraint { pub regclass: RegClass, } +impl OperandConstraint { + /// Check if this operand constraint is satisfied by the given value location. + /// For tied constraints, this only checks the register class, not that the + /// counterpart operand has the same value location. + pub fn satisfied(&self, loc: ValueLoc) -> bool { + match self.kind { + ConstraintKind::Reg | + ConstraintKind::Tied(_) => { + if let ValueLoc::Reg(reg) = loc { + self.regclass.contains(reg) + } else { + false + } + } + ConstraintKind::FixedReg(reg) => { + loc == ValueLoc::Reg(reg) && self.regclass.contains(reg) + } + ConstraintKind::Stack => { + if let ValueLoc::Stack(_) = loc { + true + } else { + false + } + } + } + } +} + /// The different kinds of operand constraints. #[derive(Clone, Copy, PartialEq, Eq)] pub enum ConstraintKind { @@ -78,6 +107,36 @@ pub struct RecipeConstraints { pub tied_ops: bool, } +impl RecipeConstraints { + /// Check that these constraints are satisfied by the operands on `inst`. + pub fn satisfied(&self, inst: Inst, func: &Function) -> bool { + for (&arg, constraint) in func.dfg.inst_args(inst).iter().zip(self.ins) { + let loc = func.locations[arg]; + + if let ConstraintKind::Tied(out_index) = constraint.kind { + let out_val = func.dfg.inst_results(inst)[out_index as usize]; + let out_loc = func.locations[out_val]; + if loc != out_loc { + return false; + } + } + + if !constraint.satisfied(loc) { + return false; + } + } + + for (&arg, constraint) in func.dfg.inst_results(inst).iter().zip(self.outs) { + let loc = func.locations[arg]; + if !constraint.satisfied(loc) { + return false; + } + } + + true + } +} + /// Constraints on the range of a branch instruction. /// /// A branch instruction usually encodes its destination as a signed n-bit offset from an origin.