* Manually rename BasicBlock to BlockPredecessor BasicBlock is a pair of (Ebb, Inst) that is used to represent the basic block subcomponent of an Ebb that is a predecessor to an Ebb. Eventually we will be able to remove this struct, but for now it makes sense to give it a non-conflicting name so that we can start to transition Ebb to represent a basic block. I have not updated any comments that refer to BasicBlock, as eventually we will remove BlockPredecessor and replace with Block, which is a basic block, so the comments will become correct. * Manually rename SSABuilder block types to avoid conflict SSABuilder has its own Block and BlockData types. These along with associated identifier will cause conflicts in a later commit, so they are renamed to be more verbose here. * Automatically rename 'Ebb' to 'Block' in *.rs * Automatically rename 'EBB' to 'block' in *.rs * Automatically rename 'ebb' to 'block' in *.rs * Automatically rename 'extended basic block' to 'basic block' in *.rs * Automatically rename 'an basic block' to 'a basic block' in *.rs * Manually update comment for `Block` `Block`'s wikipedia article required an update. * Automatically rename 'an `Block`' to 'a `Block`' in *.rs * Automatically rename 'extended_basic_block' to 'basic_block' in *.rs * Automatically rename 'ebb' to 'block' in *.clif * Manually rename clif constant that contains 'ebb' as substring to avoid conflict * Automatically rename filecheck uses of 'EBB' to 'BB' 'regex: EBB' -> 'regex: BB' '$EBB' -> '$BB' * Automatically rename 'EBB' 'Ebb' to 'block' in *.clif * Automatically rename 'an block' to 'a block' in *.clif * Fix broken testcase when function name length increases Test function names are limited to 16 characters. This causes the new longer name to be truncated and fail a filecheck test. An outdated comment was also fixed.
208 lines
7.7 KiB
Rust
208 lines
7.7 KiB
Rust
//! 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));
|
|
}
|
|
}
|