This also paves the way for unifying TargetIsa and MachBackend, since now they map one to one. In theory the two traits could be merged, which would be nice to limit the number of total concepts. Also they have quite different responsibilities, so it might be fine to keep them separate. Interestingly, this PR started as removing RegInfo from the TargetIsa trait since the adapter returned a dummy value there. From the fallout, noticed that all Display implementations didn't needed an ISA anymore (since these were only used to render ISA specific registers). Also the whole family of RegInfo / ValueLoc / RegUnit was exclusively used for the old backend, and these could be removed. Notably, some IR instructions needed to be removed, because they were using RegUnit too: this was the oddball of regfill / regmove / regspill / copy_special, which were IR instructions inserted by the old regalloc. Fare thee well!
67 lines
2.5 KiB
Rust
67 lines
2.5 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;
|
|
|
|
/// 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));
|
|
}
|
|
}
|