1 //! Register constraints for instruction operands. 2 //! 3 //! An encoding recipe specifies how an instruction is encoded as binary machine code, but it only 4 //! works if the operands and results satisfy certain constraints. Constraints on immediate 5 //! operands are checked by instruction predicates when the recipe is chosen. 6 //! 7 //! It is the register allocator's job to make sure that the register constraints on value operands 8 //! are satisfied. 9 10 use crate::binemit::CodeOffset; 11 use crate::ir::{Function, Inst, ValueLoc}; 12 use crate::isa::{RegClass, RegUnit}; 13 use crate::regalloc::RegDiversions; 14 15 /// Register constraint for a single value operand or instruction result. 16 #[derive(PartialEq, Debug)] 17 pub struct OperandConstraint { 18 /// The kind of constraint. 19 pub kind: ConstraintKind, 20 21 /// The register class of the operand. 22 /// 23 /// This applies to all kinds of constraints, but with slightly different meaning. 24 pub regclass: RegClass, 25 } 26 27 impl OperandConstraint { 28 /// Check if this operand constraint is satisfied by the given value location. 29 /// For tied constraints, this only checks the register class, not that the 30 /// counterpart operand has the same value location. satisfied(&self, loc: ValueLoc) -> bool31 pub fn satisfied(&self, loc: ValueLoc) -> bool { 32 match self.kind { 33 ConstraintKind::Reg | ConstraintKind::Tied(_) => { 34 if let ValueLoc::Reg(reg) = loc { 35 self.regclass.contains(reg) 36 } else { 37 false 38 } 39 } 40 ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => { 41 loc == ValueLoc::Reg(reg) && self.regclass.contains(reg) 42 } 43 ConstraintKind::Stack => { 44 if let ValueLoc::Stack(_) = loc { 45 true 46 } else { 47 false 48 } 49 } 50 } 51 } 52 } 53 54 /// The different kinds of operand constraints. 55 #[derive(Clone, Copy, PartialEq, Eq, Debug)] 56 pub enum ConstraintKind { 57 /// This operand or result must be a register from the given register class. 58 Reg, 59 60 /// This operand or result must be a fixed register. 61 /// 62 /// The constraint's `regclass` field is the top-level register class containing the fixed 63 /// register. 64 FixedReg(RegUnit), 65 66 /// This result value must use the same register as an input value operand. 67 /// 68 /// The associated number is the index of the input value operand this result is tied to. The 69 /// constraint's `regclass` field is the same as the tied operand's register class. 70 /// 71 /// When an (in, out) operand pair is tied, this constraint kind appears in both the `ins` and 72 /// the `outs` arrays. The constraint for the in operand is `Tied(out)`, and the constraint for 73 /// the out operand is `Tied(in)`. 74 Tied(u8), 75 76 /// This operand must be a fixed register, and it has a tied counterpart. 77 /// 78 /// This works just like `FixedReg`, but additionally indicates that there are identical 79 /// input/output operands for this fixed register. For an input operand, this means that the 80 /// value will be clobbered by the instruction 81 FixedTied(RegUnit), 82 83 /// This operand must be a value in a stack slot. 84 /// 85 /// The constraint's `regclass` field is the register class that would normally be used to load 86 /// and store values of this type. 87 Stack, 88 } 89 90 /// Value operand constraints for an encoding recipe. 91 #[derive(PartialEq, Clone)] 92 pub struct RecipeConstraints { 93 /// Constraints for the instruction's fixed value operands. 94 /// 95 /// If the instruction takes a variable number of operands, the register constraints for those 96 /// operands must be computed dynamically. 97 /// 98 /// - For branches and jumps, block arguments must match the expectations of the destination block. 99 /// - For calls and returns, the calling convention ABI specifies constraints. 100 pub ins: &'static [OperandConstraint], 101 102 /// Constraints for the instruction's fixed results. 103 /// 104 /// If the instruction produces a variable number of results, it's probably a call and the 105 /// constraints must be derived from the calling convention ABI. 106 pub outs: &'static [OperandConstraint], 107 108 /// Are any of the input constraints `FixedReg` or `FixedTied`? 109 pub fixed_ins: bool, 110 111 /// Are any of the output constraints `FixedReg` or `FixedTied`? 112 pub fixed_outs: bool, 113 114 /// Are any of the input/output constraints `Tied` (but not `FixedTied`)? 115 pub tied_ops: bool, 116 117 /// Does this instruction clobber the CPU flags? 118 /// 119 /// When true, SSA values of type `iflags` or `fflags` can not be live across the instruction. 120 pub clobbers_flags: bool, 121 } 122 123 impl RecipeConstraints { 124 /// Check that these constraints are satisfied by the operands on `inst`. satisfied(&self, inst: Inst, divert: &RegDiversions, func: &Function) -> bool125 pub fn satisfied(&self, inst: Inst, divert: &RegDiversions, func: &Function) -> bool { 126 for (&arg, constraint) in func.dfg.inst_args(inst).iter().zip(self.ins) { 127 let loc = divert.get(arg, &func.locations); 128 129 if let ConstraintKind::Tied(out_index) = constraint.kind { 130 let out_val = func.dfg.inst_results(inst)[out_index as usize]; 131 let out_loc = func.locations[out_val]; 132 if loc != out_loc { 133 return false; 134 } 135 } 136 137 if !constraint.satisfied(loc) { 138 return false; 139 } 140 } 141 142 for (&arg, constraint) in func.dfg.inst_results(inst).iter().zip(self.outs) { 143 let loc = divert.get(arg, &func.locations); 144 if !constraint.satisfied(loc) { 145 return false; 146 } 147 } 148 149 true 150 } 151 } 152 153 /// Constraints on the range of a branch instruction. 154 /// 155 /// A branch instruction usually encodes its destination as a signed n-bit offset from an origin. 156 /// The origin depends on the ISA and the specific instruction: 157 /// 158 /// - RISC-V and ARM Aarch64 use the address of the branch instruction, `origin = 0`. 159 /// - x86 uses the address of the instruction following the branch, `origin = 2` for a 2-byte 160 /// branch instruction. 161 /// - ARM's A32 encoding uses the address of the branch instruction + 8 bytes, `origin = 8`. 162 #[derive(Clone, Copy, Debug)] 163 pub struct BranchRange { 164 /// Offset in bytes from the address of the branch instruction to the origin used for computing 165 /// the branch displacement. This is the destination of a branch that encodes a 0 displacement. 166 pub origin: u8, 167 168 /// Number of bits in the signed byte displacement encoded in the instruction. This does not 169 /// account for branches that can only target aligned addresses. 170 pub bits: u8, 171 } 172 173 impl BranchRange { 174 /// Determine if this branch range can represent the range from `branch` to `dest`, where 175 /// `branch` is the code offset of the branch instruction itself and `dest` is the code offset 176 /// of the destination block header. 177 /// 178 /// This method does not detect if the range is larger than 2 GB. contains(self, branch: CodeOffset, dest: CodeOffset) -> bool179 pub fn contains(self, branch: CodeOffset, dest: CodeOffset) -> bool { 180 let d = dest.wrapping_sub(branch + CodeOffset::from(self.origin)) as i32; 181 let s = 32 - self.bits; 182 d == d << s >> s 183 } 184 } 185 186 #[cfg(test)] 187 mod tests { 188 use super::*; 189 190 #[test] branch_range()191 fn branch_range() { 192 // ARM T1 branch. 193 let t1 = BranchRange { origin: 4, bits: 9 }; 194 assert!(t1.contains(0, 0)); 195 assert!(t1.contains(0, 2)); 196 assert!(t1.contains(2, 0)); 197 assert!(t1.contains(1000, 1000)); 198 199 // Forward limit. 200 assert!(t1.contains(1000, 1258)); 201 assert!(!t1.contains(1000, 1260)); 202 203 // Backward limit 204 assert!(t1.contains(1000, 748)); 205 assert!(!t1.contains(1000, 746)); 206 } 207 } 208