1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! Register constraints for instruction operands.
//!
//! An encoding recipe specifies how an instruction is encoded as binary machine code, but it only
//! works if the operands and results satisfy certain constraints. Constraints on immediate
//! operands are checked by instruction predicates when the recipe is chosen.
//!
//! It is the register allocator's job to make sure that the register constraints on value operands
//! are satisfied.

use crate::binemit::CodeOffset;
use crate::ir::{Function, Inst, ValueLoc};
use crate::isa::{RegClass, RegUnit};
use crate::regalloc::RegDiversions;

/// Register constraint for a single value operand or instruction result.
#[derive(PartialEq, Debug)]
pub struct OperandConstraint {
    /// The kind of constraint.
    pub kind: ConstraintKind,

    /// The register class of the operand.
    ///
    /// This applies to all kinds of constraints, but with slightly different meaning.
    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) | ConstraintKind::FixedTied(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, Debug)]
pub enum ConstraintKind {
    /// This operand or result must be a register from the given register class.
    Reg,

    /// This operand or result must be a fixed register.
    ///
    /// The constraint's `regclass` field is the top-level register class containing the fixed
    /// register.
    FixedReg(RegUnit),

    /// This result value must use the same register as an input value operand.
    ///
    /// The associated number is the index of the input value operand this result is tied to. The
    /// constraint's `regclass` field is the same as the tied operand's register class.
    ///
    /// When an (in, out) operand pair is tied, this constraint kind appears in both the `ins` and
    /// the `outs` arrays. The constraint for the in operand is `Tied(out)`, and the constraint for
    /// the out operand is `Tied(in)`.
    Tied(u8),

    /// This operand must be a fixed register, and it has a tied counterpart.
    ///
    /// This works just like `FixedReg`, but additionally indicates that there are identical
    /// input/output operands for this fixed register. For an input operand, this means that the
    /// value will be clobbered by the instruction
    FixedTied(RegUnit),

    /// This operand must be a value in a stack slot.
    ///
    /// The constraint's `regclass` field is the register class that would normally be used to load
    /// and store values of this type.
    Stack,
}

/// Value operand constraints for an encoding recipe.
#[derive(PartialEq, Clone)]
pub struct RecipeConstraints {
    /// Constraints for the instruction's fixed value operands.
    ///
    /// If the instruction takes a variable number of operands, the register constraints for those
    /// operands must be computed dynamically.
    ///
    /// - For branches and jumps, block arguments must match the expectations of the destination block.
    /// - For calls and returns, the calling convention ABI specifies constraints.
    pub ins: &'static [OperandConstraint],

    /// Constraints for the instruction's fixed results.
    ///
    /// If the instruction produces a variable number of results, it's probably a call and the
    /// constraints must be derived from the calling convention ABI.
    pub outs: &'static [OperandConstraint],

    /// Are any of the input constraints `FixedReg` or `FixedTied`?
    pub fixed_ins: bool,

    /// Are any of the output constraints `FixedReg` or `FixedTied`?
    pub fixed_outs: bool,

    /// Are any of the input/output constraints `Tied` (but not `FixedTied`)?
    pub tied_ops: bool,

    /// Does this instruction clobber the CPU flags?
    ///
    /// When true, SSA values of type `iflags` or `fflags` can not be live across the instruction.
    pub clobbers_flags: bool,
}

impl RecipeConstraints {
    /// Check that these constraints are satisfied by the operands on `inst`.
    pub fn satisfied(&self, inst: Inst, divert: &RegDiversions, func: &Function) -> bool {
        for (&arg, constraint) in func.dfg.inst_args(inst).iter().zip(self.ins) {
            let loc = divert.get(arg, &func.locations);

            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 = divert.get(arg, &func.locations);
            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.
/// The origin depends on the ISA and the specific instruction:
///
/// - RISC-V and ARM Aarch64 use the address of the branch instruction, `origin = 0`.
/// - x86 uses the address of the instruction following the branch, `origin = 2` for a 2-byte
///   branch instruction.
/// - ARM's A32 encoding uses the address of the branch instruction + 8 bytes, `origin = 8`.
#[derive(Clone, Copy, Debug)]
pub struct BranchRange {
    /// Offset in bytes from the address of the branch instruction to the origin used for computing
    /// the branch displacement. This is the destination of a branch that encodes a 0 displacement.
    pub origin: u8,

    /// Number of bits in the signed byte displacement encoded in the instruction. This does not
    /// account for branches that can only target aligned addresses.
    pub bits: u8,
}

impl BranchRange {
    /// Determine if this branch range can represent the range from `branch` to `dest`, where
    /// `branch` is the code offset of the branch instruction itself and `dest` is the code offset
    /// of the destination block header.
    ///
    /// This method does not detect if the range is larger than 2 GB.
    pub fn contains(self, branch: CodeOffset, dest: CodeOffset) -> bool {
        let d = dest.wrapping_sub(branch + CodeOffset::from(self.origin)) as i32;
        let s = 32 - self.bits;
        d == d << s >> s
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn branch_range() {
        // ARM T1 branch.
        let t1 = BranchRange { origin: 4, bits: 9 };
        assert!(t1.contains(0, 0));
        assert!(t1.contains(0, 2));
        assert!(t1.contains(2, 0));
        assert!(t1.contains(1000, 1000));

        // Forward limit.
        assert!(t1.contains(1000, 1258));
        assert!(!t1.contains(1000, 1260));

        // Backward limit
        assert!(t1.contains(1000, 748));
        assert!(!t1.contains(1000, 746));
    }
}