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