Path: blob/main/cranelift/codegen/src/isa/aarch64/inst/args.rs
1693 views
//! AArch64 ISA definitions: instruction arguments.12use crate::ir::types::*;3use crate::isa::aarch64::inst::*;45//=============================================================================6// Instruction sub-components: shift and extend descriptors78/// A shift operator for a register or immediate.9#[derive(Clone, Copy, Debug, PartialEq, Eq)]10#[repr(u8)]11pub enum ShiftOp {12/// Logical shift left.13LSL = 0b00,14/// Logical shift right.15LSR = 0b01,16/// Arithmetic shift right.17ASR = 0b10,18/// Rotate right.19ROR = 0b11,20}2122impl ShiftOp {23/// Get the encoding of this shift op.24pub fn bits(self) -> u8 {25self as u826}27}2829/// A shift operator amount.30#[derive(Clone, Copy, Debug)]31pub struct ShiftOpShiftImm(u8);3233impl ShiftOpShiftImm {34/// Maximum shift for shifted-register operands.35pub const MAX_SHIFT: u64 = 63;3637/// Create a new shiftop shift amount, if possible.38pub fn maybe_from_shift(shift: u64) -> Option<ShiftOpShiftImm> {39if shift <= Self::MAX_SHIFT {40Some(ShiftOpShiftImm(shift as u8))41} else {42None43}44}4546/// Return the shift amount.47pub fn value(self) -> u8 {48self.049}5051/// Mask down to a given number of bits.52pub fn mask(self, bits: u8) -> ShiftOpShiftImm {53ShiftOpShiftImm(self.0 & (bits - 1))54}55}5657/// A shift operator with an amount, guaranteed to be within range.58#[derive(Copy, Clone, Debug)]59pub struct ShiftOpAndAmt {60/// The shift operator.61op: ShiftOp,62/// The shift operator amount.63shift: ShiftOpShiftImm,64}6566impl ShiftOpAndAmt {67/// Create a new shift operator with an amount.68pub fn new(op: ShiftOp, shift: ShiftOpShiftImm) -> ShiftOpAndAmt {69ShiftOpAndAmt { op, shift }70}7172/// Get the shift op.73pub fn op(&self) -> ShiftOp {74self.op75}7677/// Get the shift amount.78pub fn amt(&self) -> ShiftOpShiftImm {79self.shift80}81}8283/// An extend operator for a register.84#[derive(Clone, Copy, Debug)]85#[repr(u8)]86pub enum ExtendOp {87/// Unsigned extend byte.88UXTB = 0b000,89/// Unsigned extend halfword.90UXTH = 0b001,91/// Unsigned extend word.92UXTW = 0b010,93/// Unsigned extend doubleword.94UXTX = 0b011,95/// Signed extend byte.96SXTB = 0b100,97/// Signed extend halfword.98SXTH = 0b101,99/// Signed extend word.100SXTW = 0b110,101/// Signed extend doubleword.102SXTX = 0b111,103}104105impl ExtendOp {106/// Encoding of this op.107pub fn bits(self) -> u8 {108self as u8109}110}111112//=============================================================================113// Instruction sub-components (memory addresses): definitions114115/// A reference to some memory address.116#[derive(Clone, Debug)]117pub enum MemLabel {118/// An address in the code, a constant pool or jumptable, with relative119/// offset from this instruction. This form must be used at emission time;120/// see `memlabel_finalize()` for how other forms are lowered to this one.121PCRel(i32),122/// An address that refers to a label within a `MachBuffer`, for example a123/// constant that lives in the pool at the end of the function.124Mach(MachLabel),125}126127impl AMode {128/// Memory reference using an address in a register.129pub fn reg(reg: Reg) -> AMode {130// Use UnsignedOffset rather than Unscaled to use ldr rather than ldur.131// This also does not use PostIndexed / PreIndexed as they update the register.132AMode::UnsignedOffset {133rn: reg,134uimm12: UImm12Scaled::zero(I64),135}136}137138/// Memory reference using `reg1 + sizeof(ty) * reg2` as an address, with `reg2` sign- or139/// zero-extended as per `op`.140pub fn reg_plus_reg_scaled_extended(reg1: Reg, reg2: Reg, op: ExtendOp) -> AMode {141AMode::RegScaledExtended {142rn: reg1,143rm: reg2,144extendop: op,145}146}147}148149pub use crate::isa::aarch64::lower::isle::generated_code::PairAMode;150151//=============================================================================152// Instruction sub-components (conditions, branches and branch targets):153// definitions154155/// Condition for conditional branches.156#[derive(Clone, Copy, Debug, PartialEq, Eq)]157#[repr(u8)]158pub enum Cond {159/// Equal.160Eq = 0,161/// Not equal.162Ne = 1,163/// Unsigned greater than or equal to.164Hs = 2,165/// Unsigned less than.166Lo = 3,167/// Minus, negative.168Mi = 4,169/// Positive or zero.170Pl = 5,171/// Signed overflow.172Vs = 6,173/// No signed overflow.174Vc = 7,175/// Unsigned greater than.176Hi = 8,177/// Unsigned less than or equal to.178Ls = 9,179/// Signed greater or equal to.180Ge = 10,181/// Signed less than.182Lt = 11,183/// Signed greater than.184Gt = 12,185/// Signed less than or equal.186Le = 13,187/// Always executed.188Al = 14,189/// Always executed.190Nv = 15,191}192193impl Cond {194/// Return the inverted condition.195pub fn invert(self) -> Cond {196match self {197Cond::Eq => Cond::Ne,198Cond::Ne => Cond::Eq,199200Cond::Hs => Cond::Lo,201Cond::Lo => Cond::Hs,202203Cond::Mi => Cond::Pl,204Cond::Pl => Cond::Mi,205206Cond::Vs => Cond::Vc,207Cond::Vc => Cond::Vs,208209Cond::Hi => Cond::Ls,210Cond::Ls => Cond::Hi,211212Cond::Ge => Cond::Lt,213Cond::Lt => Cond::Ge,214215Cond::Gt => Cond::Le,216Cond::Le => Cond::Gt,217218Cond::Al => Cond::Nv,219Cond::Nv => Cond::Al,220}221}222223/// Return the machine encoding of this condition.224pub fn bits(self) -> u32 {225self as u32226}227}228229/// The kind of conditional branch: the common-case-optimized "reg-is-zero" /230/// "reg-is-nonzero" variants, or the generic one that tests the machine231/// condition codes.232#[derive(Clone, Copy, Debug)]233pub enum CondBrKind {234/// Condition: given register is zero.235Zero(Reg, OperandSize),236/// Condition: given register is nonzero.237NotZero(Reg, OperandSize),238/// Condition: the given condition-code test is true.239Cond(Cond),240}241242impl CondBrKind {243/// Return the inverted branch condition.244pub fn invert(self) -> CondBrKind {245match self {246CondBrKind::Zero(reg, size) => CondBrKind::NotZero(reg, size),247CondBrKind::NotZero(reg, size) => CondBrKind::Zero(reg, size),248CondBrKind::Cond(c) => CondBrKind::Cond(c.invert()),249}250}251}252253/// A branch target. Either unresolved (basic-block index) or resolved (offset254/// from end of current instruction).255#[derive(Clone, Copy, Debug, PartialEq, Eq)]256pub enum BranchTarget {257/// An unresolved reference to a Label, as passed into258/// `lower_branch_group()`.259Label(MachLabel),260/// A fixed PC offset.261ResolvedOffset(i32),262}263264impl BranchTarget {265/// Return the target's label, if it is a label-based target.266pub fn as_label(self) -> Option<MachLabel> {267match self {268BranchTarget::Label(l) => Some(l),269_ => None,270}271}272273/// Return the target's offset, if specified, or zero if label-based.274pub fn as_offset14_or_zero(self) -> u32 {275self.as_offset_bounded(14)276}277278/// Return the target's offset, if specified, or zero if label-based.279pub fn as_offset19_or_zero(self) -> u32 {280self.as_offset_bounded(19)281}282283/// Return the target's offset, if specified, or zero if label-based.284pub fn as_offset26_or_zero(self) -> u32 {285self.as_offset_bounded(26)286}287288fn as_offset_bounded(self, bits: u32) -> u32 {289let off = match self {290BranchTarget::ResolvedOffset(off) => off >> 2,291_ => 0,292};293let hi = (1 << (bits - 1)) - 1;294let lo = -(1 << bits - 1);295assert!(off <= hi);296assert!(off >= lo);297(off as u32) & ((1 << bits) - 1)298}299}300301impl PrettyPrint for ShiftOpAndAmt {302fn pretty_print(&self, _: u8) -> String {303format!("{:?} {}", self.op(), self.amt().value())304}305}306307impl PrettyPrint for ExtendOp {308fn pretty_print(&self, _: u8) -> String {309format!("{self:?}")310}311}312313impl PrettyPrint for MemLabel {314fn pretty_print(&self, _: u8) -> String {315match self {316MemLabel::PCRel(off) => format!("pc+{off}"),317MemLabel::Mach(off) => format!("label({})", off.as_u32()),318}319}320}321322fn shift_for_type(size_bytes: u8) -> usize {323match size_bytes {3241 => 0,3252 => 1,3264 => 2,3278 => 3,32816 => 4,329_ => panic!("unknown type size: {size_bytes}"),330}331}332333impl PrettyPrint for AMode {334fn pretty_print(&self, size_bytes: u8) -> String {335debug_assert!(size_bytes != 0);336match self {337&AMode::Unscaled { rn, simm9 } => {338let reg = pretty_print_reg(rn);339if simm9.value != 0 {340let simm9 = simm9.pretty_print(8);341format!("[{reg}, {simm9}]")342} else {343format!("[{reg}]")344}345}346&AMode::UnsignedOffset { rn, uimm12 } => {347let reg = pretty_print_reg(rn);348if uimm12.value() != 0 {349let uimm12 = uimm12.pretty_print(8);350format!("[{reg}, {uimm12}]")351} else {352format!("[{reg}]")353}354}355&AMode::RegReg { rn, rm } => {356let r1 = pretty_print_reg(rn);357let r2 = pretty_print_reg(rm);358format!("[{r1}, {r2}]")359}360&AMode::RegScaled { rn, rm } => {361let r1 = pretty_print_reg(rn);362let r2 = pretty_print_reg(rm);363let shift = shift_for_type(size_bytes);364format!("[{r1}, {r2}, LSL #{shift}]")365}366&AMode::RegScaledExtended { rn, rm, extendop } => {367let shift = shift_for_type(size_bytes);368let size = match extendop {369ExtendOp::SXTW | ExtendOp::UXTW => OperandSize::Size32,370_ => OperandSize::Size64,371};372let r1 = pretty_print_reg(rn);373let r2 = pretty_print_ireg(rm, size);374let op = extendop.pretty_print(0);375format!("[{r1}, {r2}, {op} #{shift}]")376}377&AMode::RegExtended { rn, rm, extendop } => {378let size = match extendop {379ExtendOp::SXTW | ExtendOp::UXTW => OperandSize::Size32,380_ => OperandSize::Size64,381};382let r1 = pretty_print_reg(rn);383let r2 = pretty_print_ireg(rm, size);384let op = extendop.pretty_print(0);385format!("[{r1}, {r2}, {op}]")386}387&AMode::Label { ref label } => label.pretty_print(0),388&AMode::SPPreIndexed { simm9 } => {389let simm9 = simm9.pretty_print(8);390format!("[sp, {simm9}]!")391}392&AMode::SPPostIndexed { simm9 } => {393let simm9 = simm9.pretty_print(8);394format!("[sp], {simm9}")395}396AMode::Const { addr } => format!("[const({})]", addr.as_u32()),397398// Eliminated by `mem_finalize()`.399&AMode::SPOffset { .. }400| &AMode::FPOffset { .. }401| &AMode::IncomingArg { .. }402| &AMode::SlotOffset { .. }403| &AMode::RegOffset { .. } => {404panic!("Unexpected pseudo mem-arg mode: {self:?}")405}406}407}408}409410impl PrettyPrint for PairAMode {411fn pretty_print(&self, _: u8) -> String {412match self {413&PairAMode::SignedOffset { reg, simm7 } => {414let reg = pretty_print_reg(reg);415if simm7.value != 0 {416let simm7 = simm7.pretty_print(8);417format!("[{reg}, {simm7}]")418} else {419format!("[{reg}]")420}421}422&PairAMode::SPPreIndexed { simm7 } => {423let simm7 = simm7.pretty_print(8);424format!("[sp, {simm7}]!")425}426&PairAMode::SPPostIndexed { simm7 } => {427let simm7 = simm7.pretty_print(8);428format!("[sp], {simm7}")429}430}431}432}433434impl PrettyPrint for Cond {435fn pretty_print(&self, _: u8) -> String {436let mut s = format!("{self:?}");437s.make_ascii_lowercase();438s439}440}441442impl PrettyPrint for BranchTarget {443fn pretty_print(&self, _: u8) -> String {444match self {445&BranchTarget::Label(label) => format!("label{:?}", label.as_u32()),446&BranchTarget::ResolvedOffset(off) => format!("{off}"),447}448}449}450451/// Type used to communicate the operand size of a machine instruction, as AArch64 has 32- and452/// 64-bit variants of many instructions (and integer registers).453#[derive(Clone, Copy, Debug, PartialEq, Eq)]454pub enum OperandSize {455/// 32-bit.456Size32,457/// 64-bit.458Size64,459}460461impl OperandSize {462/// 32-bit case?463pub fn is32(self) -> bool {464self == OperandSize::Size32465}466467/// 64-bit case?468pub fn is64(self) -> bool {469self == OperandSize::Size64470}471472/// Convert from a needed width to the smallest size that fits.473pub fn from_bits<I: Into<usize>>(bits: I) -> OperandSize {474let bits: usize = bits.into();475assert!(bits <= 64);476if bits <= 32 {477OperandSize::Size32478} else {479OperandSize::Size64480}481}482483/// Return the operand size in bits.484pub fn bits(&self) -> u8 {485match self {486OperandSize::Size32 => 32,487OperandSize::Size64 => 64,488}489}490491/// Convert from an integer type into the smallest size that fits.492pub fn from_ty(ty: Type) -> OperandSize {493debug_assert!(!ty.is_vector());494495Self::from_bits(ty_bits(ty))496}497498/// Convert to I32, I64, or I128.499pub fn to_ty(self) -> Type {500match self {501OperandSize::Size32 => I32,502OperandSize::Size64 => I64,503}504}505506/// Register interpretation bit.507/// When 0, the register is interpreted as the 32-bit version.508/// When 1, the register is interpreted as the 64-bit version.509pub fn sf_bit(&self) -> u32 {510match self {511OperandSize::Size32 => 0,512OperandSize::Size64 => 1,513}514}515516/// The maximum unsigned value representable in a value of this size.517pub fn max_value(&self) -> u64 {518match self {519OperandSize::Size32 => u32::MAX as u64,520OperandSize::Size64 => u64::MAX,521}522}523}524525/// Type used to communicate the size of a scalar SIMD & FP operand.526#[derive(Clone, Copy, Debug, PartialEq, Eq)]527pub enum ScalarSize {528/// 8-bit.529Size8,530/// 16-bit.531Size16,532/// 32-bit.533Size32,534/// 64-bit.535Size64,536/// 128-bit.537Size128,538}539540impl ScalarSize {541/// Convert to an integer operand size.542pub fn operand_size(&self) -> OperandSize {543match self {544ScalarSize::Size8 | ScalarSize::Size16 | ScalarSize::Size32 => OperandSize::Size32,545ScalarSize::Size64 => OperandSize::Size64,546_ => panic!("Unexpected operand_size request for: {self:?}"),547}548}549550/// Return the encoding bits that are used by some scalar FP instructions551/// for a particular operand size.552pub fn ftype(&self) -> u32 {553match self {554ScalarSize::Size16 => 0b11,555ScalarSize::Size32 => 0b00,556ScalarSize::Size64 => 0b01,557_ => panic!("Unexpected scalar FP operand size: {self:?}"),558}559}560561/// Return the widened version of the scalar size.562pub fn widen(&self) -> ScalarSize {563match self {564ScalarSize::Size8 => ScalarSize::Size16,565ScalarSize::Size16 => ScalarSize::Size32,566ScalarSize::Size32 => ScalarSize::Size64,567ScalarSize::Size64 => ScalarSize::Size128,568ScalarSize::Size128 => panic!("can't widen 128-bits"),569}570}571572/// Return the narrowed version of the scalar size.573pub fn narrow(&self) -> ScalarSize {574match self {575ScalarSize::Size8 => panic!("can't narrow 8-bits"),576ScalarSize::Size16 => ScalarSize::Size8,577ScalarSize::Size32 => ScalarSize::Size16,578ScalarSize::Size64 => ScalarSize::Size32,579ScalarSize::Size128 => ScalarSize::Size64,580}581}582583/// Return a type with the same size as this scalar.584pub fn ty(&self) -> Type {585match self {586ScalarSize::Size8 => I8,587ScalarSize::Size16 => I16,588ScalarSize::Size32 => I32,589ScalarSize::Size64 => I64,590ScalarSize::Size128 => I128,591}592}593}594595/// Type used to communicate the size of a vector operand.596#[derive(Clone, Copy, Debug, PartialEq, Eq)]597pub enum VectorSize {598/// 8-bit, 8 lanes.599Size8x8,600/// 8 bit, 16 lanes.601Size8x16,602/// 16-bit, 4 lanes.603Size16x4,604/// 16-bit, 8 lanes.605Size16x8,606/// 32-bit, 2 lanes.607Size32x2,608/// 32-bit, 4 lanes.609Size32x4,610/// 64-bit, 2 lanes.611Size64x2,612}613614impl VectorSize {615/// Get the vector operand size with the given scalar size as lane size.616pub fn from_lane_size(size: ScalarSize, is_128bit: bool) -> VectorSize {617match (size, is_128bit) {618(ScalarSize::Size8, false) => VectorSize::Size8x8,619(ScalarSize::Size8, true) => VectorSize::Size8x16,620(ScalarSize::Size16, false) => VectorSize::Size16x4,621(ScalarSize::Size16, true) => VectorSize::Size16x8,622(ScalarSize::Size32, false) => VectorSize::Size32x2,623(ScalarSize::Size32, true) => VectorSize::Size32x4,624(ScalarSize::Size64, true) => VectorSize::Size64x2,625_ => panic!("Unexpected scalar FP operand size: {size:?}"),626}627}628629/// Get the integer operand size that corresponds to a lane of a vector with a certain size.630pub fn operand_size(&self) -> OperandSize {631match self {632VectorSize::Size64x2 => OperandSize::Size64,633_ => OperandSize::Size32,634}635}636637/// Get the scalar operand size that corresponds to a lane of a vector with a certain size.638pub fn lane_size(&self) -> ScalarSize {639match self {640VectorSize::Size8x8 | VectorSize::Size8x16 => ScalarSize::Size8,641VectorSize::Size16x4 | VectorSize::Size16x8 => ScalarSize::Size16,642VectorSize::Size32x2 | VectorSize::Size32x4 => ScalarSize::Size32,643VectorSize::Size64x2 => ScalarSize::Size64,644}645}646647/// Returns true if the VectorSize is 128-bits.648pub fn is_128bits(&self) -> bool {649match self {650VectorSize::Size8x8 => false,651VectorSize::Size8x16 => true,652VectorSize::Size16x4 => false,653VectorSize::Size16x8 => true,654VectorSize::Size32x2 => false,655VectorSize::Size32x4 => true,656VectorSize::Size64x2 => true,657}658}659660/// Return the encoding bits that are used by some SIMD instructions661/// for a particular operand size.662pub fn enc_size(&self) -> (u32, u32) {663let q = self.is_128bits() as u32;664let size = match self.lane_size() {665ScalarSize::Size8 => 0b00,666ScalarSize::Size16 => 0b01,667ScalarSize::Size32 => 0b10,668ScalarSize::Size64 => 0b11,669_ => unreachable!(),670};671672(q, size)673}674675/// Return the encoding bit that is used by some floating-point SIMD676/// instructions for a particular operand size.677pub fn enc_float_size(&self) -> u32 {678match self.lane_size() {679ScalarSize::Size32 => 0b0,680ScalarSize::Size64 => 0b1,681size => panic!("Unsupported floating-point size for vector op: {size:?}"),682}683}684}685686impl APIKey {687/// Returns the encoding of the `auti{key}` instruction used to decrypt the688/// `lr` register.689pub fn enc_auti_hint(&self) -> u32 {690let (crm, op2) = match self {691APIKey::AZ => (0b0011, 0b100),692APIKey::ASP => (0b0011, 0b101),693APIKey::BZ => (0b0011, 0b110),694APIKey::BSP => (0b0011, 0b111),695};6960xd503201f | (crm << 8) | (op2 << 5)697}698}699700pub use crate::isa::aarch64::lower::isle::generated_code::TestBitAndBranchKind;701702impl TestBitAndBranchKind {703/// Complements this branch condition to act on the opposite result.704pub fn complement(&self) -> TestBitAndBranchKind {705match self {706TestBitAndBranchKind::Z => TestBitAndBranchKind::NZ,707TestBitAndBranchKind::NZ => TestBitAndBranchKind::Z,708}709}710}711712713