Path: blob/main/cranelift/codegen/src/isa/aarch64/inst/mod.rs
1693 views
//! This module defines aarch64-specific machine instruction types.12use crate::binemit::{Addend, CodeOffset, Reloc};3use crate::ir::types::{F16, F32, F64, F128, I8, I8X16, I16, I32, I64, I128};4use crate::ir::{MemFlags, Type, types};5use crate::isa::{CallConv, FunctionAlignment};6use crate::machinst::*;7use crate::{CodegenError, CodegenResult, settings};89use crate::machinst::{PrettyPrint, Reg, RegClass, Writable};1011use alloc::vec::Vec;12use core::slice;13use smallvec::{SmallVec, smallvec};14use std::fmt::Write;15use std::string::{String, ToString};1617pub(crate) mod regs;18pub(crate) use self::regs::*;19pub mod imms;20pub use self::imms::*;21pub mod args;22pub use self::args::*;23pub mod emit;24pub(crate) use self::emit::*;25use crate::isa::aarch64::abi::AArch64MachineDeps;2627pub(crate) mod unwind;2829#[cfg(test)]30mod emit_tests;3132//=============================================================================33// Instructions (top level): definition3435pub use crate::isa::aarch64::lower::isle::generated_code::{36ALUOp, ALUOp3, AMode, APIKey, AtomicRMWLoopOp, AtomicRMWOp, BitOp, BranchTargetType, FPUOp1,37FPUOp2, FPUOp3, FpuRoundMode, FpuToIntOp, IntToFpuOp, MInst as Inst, MoveWideOp, VecALUModOp,38VecALUOp, VecExtendOp, VecLanesOp, VecMisc2, VecPairOp, VecRRLongOp, VecRRNarrowOp,39VecRRPairLongOp, VecRRRLongModOp, VecRRRLongOp, VecShiftImmModOp, VecShiftImmOp,40};4142/// A floating-point unit (FPU) operation with two args, a register and an immediate.43#[derive(Copy, Clone, Debug)]44pub enum FPUOpRI {45/// Unsigned right shift. Rd = Rn << #imm46UShr32(FPURightShiftImm),47/// Unsigned right shift. Rd = Rn << #imm48UShr64(FPURightShiftImm),49}5051/// A floating-point unit (FPU) operation with two args, a register and52/// an immediate that modifies its dest (so takes that input value as a53/// separate virtual register).54#[derive(Copy, Clone, Debug)]55pub enum FPUOpRIMod {56/// Shift left and insert. Rd |= Rn << #imm57Sli32(FPULeftShiftImm),58/// Shift left and insert. Rd |= Rn << #imm59Sli64(FPULeftShiftImm),60}6162impl BitOp {63/// Get the assembly mnemonic for this opcode.64pub fn op_str(&self) -> &'static str {65match self {66BitOp::RBit => "rbit",67BitOp::Clz => "clz",68BitOp::Cls => "cls",69BitOp::Rev16 => "rev16",70BitOp::Rev32 => "rev32",71BitOp::Rev64 => "rev64",72}73}74}7576/// Additional information for `return_call[_ind]` instructions, left out of77/// line to lower the size of the `Inst` enum.78#[derive(Clone, Debug)]79pub struct ReturnCallInfo<T> {80/// Where this call is going to81pub dest: T,82/// Arguments to the call instruction.83pub uses: CallArgList,84/// The size of the new stack frame's stack arguments. This is necessary85/// for copying the frame over our current frame. It must already be86/// allocated on the stack.87pub new_stack_arg_size: u32,88/// API key to use to restore the return address, if any.89pub key: Option<APIKey>,90}9192fn count_zero_half_words(mut value: u64, num_half_words: u8) -> usize {93let mut count = 0;94for _ in 0..num_half_words {95if value & 0xffff == 0 {96count += 1;97}98value >>= 16;99}100101count102}103104impl Inst {105/// Create an instruction that loads a constant, using one of several options (MOVZ, MOVN,106/// logical immediate, or constant pool).107pub fn load_constant(rd: Writable<Reg>, value: u64) -> SmallVec<[Inst; 4]> {108// NB: this is duplicated in `lower/isle.rs` and `inst.isle` right now,109// if modifications are made here before this is deleted after moving to110// ISLE then those locations should be updated as well.111112if let Some(imm) = MoveWideConst::maybe_from_u64(value) {113// 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVZ114smallvec![Inst::MovWide {115op: MoveWideOp::MovZ,116rd,117imm,118size: OperandSize::Size64119}]120} else if let Some(imm) = MoveWideConst::maybe_from_u64(!value) {121// 16-bit immediate (shifted by 0, 16, 32 or 48 bits) in MOVN122smallvec![Inst::MovWide {123op: MoveWideOp::MovN,124rd,125imm,126size: OperandSize::Size64127}]128} else if let Some(imml) = ImmLogic::maybe_from_u64(value, I64) {129// Weird logical-instruction immediate in ORI using zero register130smallvec![Inst::AluRRImmLogic {131alu_op: ALUOp::Orr,132size: OperandSize::Size64,133rd,134rn: zero_reg(),135imml,136}]137} else {138let mut insts = smallvec![];139140// If the top 32 bits are zero, use 32-bit `mov` operations.141let (num_half_words, size, negated) = if value >> 32 == 0 {142(2, OperandSize::Size32, (!value << 32) >> 32)143} else {144(4, OperandSize::Size64, !value)145};146147// If the number of 0xffff half words is greater than the number of 0x0000 half words148// it is more efficient to use `movn` for the first instruction.149let first_is_inverted = count_zero_half_words(negated, num_half_words)150> count_zero_half_words(value, num_half_words);151152// Either 0xffff or 0x0000 half words can be skipped, depending on the first153// instruction used.154let ignored_halfword = if first_is_inverted { 0xffff } else { 0 };155156let halfwords: SmallVec<[_; 4]> = (0..num_half_words)157.filter_map(|i| {158let imm16 = (value >> (16 * i)) & 0xffff;159if imm16 == ignored_halfword {160None161} else {162Some((i, imm16))163}164})165.collect();166167let mut prev_result = None;168for (i, imm16) in halfwords {169let shift = i * 16;170171if let Some(rn) = prev_result {172let imm = MoveWideConst::maybe_with_shift(imm16 as u16, shift).unwrap();173insts.push(Inst::MovK { rd, rn, imm, size });174} else {175if first_is_inverted {176let imm =177MoveWideConst::maybe_with_shift(((!imm16) & 0xffff) as u16, shift)178.unwrap();179insts.push(Inst::MovWide {180op: MoveWideOp::MovN,181rd,182imm,183size,184});185} else {186let imm = MoveWideConst::maybe_with_shift(imm16 as u16, shift).unwrap();187insts.push(Inst::MovWide {188op: MoveWideOp::MovZ,189rd,190imm,191size,192});193}194}195196prev_result = Some(rd.to_reg());197}198199assert!(prev_result.is_some());200201insts202}203}204205/// Generic constructor for a load (zero-extending where appropriate).206pub fn gen_load(into_reg: Writable<Reg>, mem: AMode, ty: Type, flags: MemFlags) -> Inst {207match ty {208I8 => Inst::ULoad8 {209rd: into_reg,210mem,211flags,212},213I16 => Inst::ULoad16 {214rd: into_reg,215mem,216flags,217},218I32 => Inst::ULoad32 {219rd: into_reg,220mem,221flags,222},223I64 => Inst::ULoad64 {224rd: into_reg,225mem,226flags,227},228_ => {229if ty.is_vector() || ty.is_float() {230let bits = ty_bits(ty);231let rd = into_reg;232233match bits {234128 => Inst::FpuLoad128 { rd, mem, flags },23564 => Inst::FpuLoad64 { rd, mem, flags },23632 => Inst::FpuLoad32 { rd, mem, flags },23716 => Inst::FpuLoad16 { rd, mem, flags },238_ => unimplemented!("gen_load({})", ty),239}240} else {241unimplemented!("gen_load({})", ty);242}243}244}245}246247/// Generic constructor for a store.248pub fn gen_store(mem: AMode, from_reg: Reg, ty: Type, flags: MemFlags) -> Inst {249match ty {250I8 => Inst::Store8 {251rd: from_reg,252mem,253flags,254},255I16 => Inst::Store16 {256rd: from_reg,257mem,258flags,259},260I32 => Inst::Store32 {261rd: from_reg,262mem,263flags,264},265I64 => Inst::Store64 {266rd: from_reg,267mem,268flags,269},270_ => {271if ty.is_vector() || ty.is_float() {272let bits = ty_bits(ty);273let rd = from_reg;274275match bits {276128 => Inst::FpuStore128 { rd, mem, flags },27764 => Inst::FpuStore64 { rd, mem, flags },27832 => Inst::FpuStore32 { rd, mem, flags },27916 => Inst::FpuStore16 { rd, mem, flags },280_ => unimplemented!("gen_store({})", ty),281}282} else {283unimplemented!("gen_store({})", ty);284}285}286}287}288289/// What type does this load or store instruction access in memory? When290/// uimm12 encoding is used, the size of this type is the amount that291/// immediate offsets are scaled by.292pub fn mem_type(&self) -> Option<Type> {293match self {294Inst::ULoad8 { .. } => Some(I8),295Inst::SLoad8 { .. } => Some(I8),296Inst::ULoad16 { .. } => Some(I16),297Inst::SLoad16 { .. } => Some(I16),298Inst::ULoad32 { .. } => Some(I32),299Inst::SLoad32 { .. } => Some(I32),300Inst::ULoad64 { .. } => Some(I64),301Inst::FpuLoad16 { .. } => Some(F16),302Inst::FpuLoad32 { .. } => Some(F32),303Inst::FpuLoad64 { .. } => Some(F64),304Inst::FpuLoad128 { .. } => Some(I8X16),305Inst::Store8 { .. } => Some(I8),306Inst::Store16 { .. } => Some(I16),307Inst::Store32 { .. } => Some(I32),308Inst::Store64 { .. } => Some(I64),309Inst::FpuStore16 { .. } => Some(F16),310Inst::FpuStore32 { .. } => Some(F32),311Inst::FpuStore64 { .. } => Some(F64),312Inst::FpuStore128 { .. } => Some(I8X16),313_ => None,314}315}316}317318//=============================================================================319// Instructions: get_regs320321fn memarg_operands(memarg: &mut AMode, collector: &mut impl OperandVisitor) {322match memarg {323AMode::Unscaled { rn, .. } | AMode::UnsignedOffset { rn, .. } => {324collector.reg_use(rn);325}326AMode::RegReg { rn, rm, .. }327| AMode::RegScaled { rn, rm, .. }328| AMode::RegScaledExtended { rn, rm, .. }329| AMode::RegExtended { rn, rm, .. } => {330collector.reg_use(rn);331collector.reg_use(rm);332}333AMode::Label { .. } => {}334AMode::SPPreIndexed { .. } | AMode::SPPostIndexed { .. } => {}335AMode::FPOffset { .. } | AMode::IncomingArg { .. } => {}336AMode::SPOffset { .. } | AMode::SlotOffset { .. } => {}337AMode::RegOffset { rn, .. } => {338collector.reg_use(rn);339}340AMode::Const { .. } => {}341}342}343344fn pairmemarg_operands(pairmemarg: &mut PairAMode, collector: &mut impl OperandVisitor) {345match pairmemarg {346PairAMode::SignedOffset { reg, .. } => {347collector.reg_use(reg);348}349PairAMode::SPPreIndexed { .. } | PairAMode::SPPostIndexed { .. } => {}350}351}352353fn aarch64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {354match inst {355Inst::AluRRR { rd, rn, rm, .. } => {356collector.reg_def(rd);357collector.reg_use(rn);358collector.reg_use(rm);359}360Inst::AluRRRR { rd, rn, rm, ra, .. } => {361collector.reg_def(rd);362collector.reg_use(rn);363collector.reg_use(rm);364collector.reg_use(ra);365}366Inst::AluRRImm12 { rd, rn, .. } => {367collector.reg_def(rd);368collector.reg_use(rn);369}370Inst::AluRRImmLogic { rd, rn, .. } => {371collector.reg_def(rd);372collector.reg_use(rn);373}374Inst::AluRRImmShift { rd, rn, .. } => {375collector.reg_def(rd);376collector.reg_use(rn);377}378Inst::AluRRRShift { rd, rn, rm, .. } => {379collector.reg_def(rd);380collector.reg_use(rn);381collector.reg_use(rm);382}383Inst::AluRRRExtend { rd, rn, rm, .. } => {384collector.reg_def(rd);385collector.reg_use(rn);386collector.reg_use(rm);387}388Inst::BitRR { rd, rn, .. } => {389collector.reg_def(rd);390collector.reg_use(rn);391}392Inst::ULoad8 { rd, mem, .. }393| Inst::SLoad8 { rd, mem, .. }394| Inst::ULoad16 { rd, mem, .. }395| Inst::SLoad16 { rd, mem, .. }396| Inst::ULoad32 { rd, mem, .. }397| Inst::SLoad32 { rd, mem, .. }398| Inst::ULoad64 { rd, mem, .. } => {399collector.reg_def(rd);400memarg_operands(mem, collector);401}402Inst::Store8 { rd, mem, .. }403| Inst::Store16 { rd, mem, .. }404| Inst::Store32 { rd, mem, .. }405| Inst::Store64 { rd, mem, .. } => {406collector.reg_use(rd);407memarg_operands(mem, collector);408}409Inst::StoreP64 { rt, rt2, mem, .. } => {410collector.reg_use(rt);411collector.reg_use(rt2);412pairmemarg_operands(mem, collector);413}414Inst::LoadP64 { rt, rt2, mem, .. } => {415collector.reg_def(rt);416collector.reg_def(rt2);417pairmemarg_operands(mem, collector);418}419Inst::Mov { rd, rm, .. } => {420collector.reg_def(rd);421collector.reg_use(rm);422}423Inst::MovFromPReg { rd, rm } => {424debug_assert!(rd.to_reg().is_virtual());425collector.reg_def(rd);426collector.reg_fixed_nonallocatable(*rm);427}428Inst::MovToPReg { rd, rm } => {429debug_assert!(rm.is_virtual());430collector.reg_fixed_nonallocatable(*rd);431collector.reg_use(rm);432}433Inst::MovK { rd, rn, .. } => {434collector.reg_use(rn);435collector.reg_reuse_def(rd, 0); // `rn` == `rd`.436}437Inst::MovWide { rd, .. } => {438collector.reg_def(rd);439}440Inst::CSel { rd, rn, rm, .. } => {441collector.reg_def(rd);442collector.reg_use(rn);443collector.reg_use(rm);444}445Inst::CSNeg { rd, rn, rm, .. } => {446collector.reg_def(rd);447collector.reg_use(rn);448collector.reg_use(rm);449}450Inst::CSet { rd, .. } | Inst::CSetm { rd, .. } => {451collector.reg_def(rd);452}453Inst::CCmp { rn, rm, .. } => {454collector.reg_use(rn);455collector.reg_use(rm);456}457Inst::CCmpImm { rn, .. } => {458collector.reg_use(rn);459}460Inst::AtomicRMWLoop {461op,462addr,463operand,464oldval,465scratch1,466scratch2,467..468} => {469collector.reg_fixed_use(addr, xreg(25));470collector.reg_fixed_use(operand, xreg(26));471collector.reg_fixed_def(oldval, xreg(27));472collector.reg_fixed_def(scratch1, xreg(24));473if *op != AtomicRMWLoopOp::Xchg {474collector.reg_fixed_def(scratch2, xreg(28));475}476}477Inst::AtomicRMW { rs, rt, rn, .. } => {478collector.reg_use(rs);479collector.reg_def(rt);480collector.reg_use(rn);481}482Inst::AtomicCAS { rd, rs, rt, rn, .. } => {483collector.reg_reuse_def(rd, 1); // reuse `rs`.484collector.reg_use(rs);485collector.reg_use(rt);486collector.reg_use(rn);487}488Inst::AtomicCASLoop {489addr,490expected,491replacement,492oldval,493scratch,494..495} => {496collector.reg_fixed_use(addr, xreg(25));497collector.reg_fixed_use(expected, xreg(26));498collector.reg_fixed_use(replacement, xreg(28));499collector.reg_fixed_def(oldval, xreg(27));500collector.reg_fixed_def(scratch, xreg(24));501}502Inst::LoadAcquire { rt, rn, .. } => {503collector.reg_use(rn);504collector.reg_def(rt);505}506Inst::StoreRelease { rt, rn, .. } => {507collector.reg_use(rn);508collector.reg_use(rt);509}510Inst::Fence {} | Inst::Csdb {} => {}511Inst::FpuMove32 { rd, rn } => {512collector.reg_def(rd);513collector.reg_use(rn);514}515Inst::FpuMove64 { rd, rn } => {516collector.reg_def(rd);517collector.reg_use(rn);518}519Inst::FpuMove128 { rd, rn } => {520collector.reg_def(rd);521collector.reg_use(rn);522}523Inst::FpuMoveFromVec { rd, rn, .. } => {524collector.reg_def(rd);525collector.reg_use(rn);526}527Inst::FpuExtend { rd, rn, .. } => {528collector.reg_def(rd);529collector.reg_use(rn);530}531Inst::FpuRR { rd, rn, .. } => {532collector.reg_def(rd);533collector.reg_use(rn);534}535Inst::FpuRRR { rd, rn, rm, .. } => {536collector.reg_def(rd);537collector.reg_use(rn);538collector.reg_use(rm);539}540Inst::FpuRRI { rd, rn, .. } => {541collector.reg_def(rd);542collector.reg_use(rn);543}544Inst::FpuRRIMod { rd, ri, rn, .. } => {545collector.reg_reuse_def(rd, 1); // reuse `ri`.546collector.reg_use(ri);547collector.reg_use(rn);548}549Inst::FpuRRRR { rd, rn, rm, ra, .. } => {550collector.reg_def(rd);551collector.reg_use(rn);552collector.reg_use(rm);553collector.reg_use(ra);554}555Inst::VecMisc { rd, rn, .. } => {556collector.reg_def(rd);557collector.reg_use(rn);558}559560Inst::VecLanes { rd, rn, .. } => {561collector.reg_def(rd);562collector.reg_use(rn);563}564Inst::VecShiftImm { rd, rn, .. } => {565collector.reg_def(rd);566collector.reg_use(rn);567}568Inst::VecShiftImmMod { rd, ri, rn, .. } => {569collector.reg_reuse_def(rd, 1); // `rd` == `ri`.570collector.reg_use(ri);571collector.reg_use(rn);572}573Inst::VecExtract { rd, rn, rm, .. } => {574collector.reg_def(rd);575collector.reg_use(rn);576collector.reg_use(rm);577}578Inst::VecTbl { rd, rn, rm } => {579collector.reg_use(rn);580collector.reg_use(rm);581collector.reg_def(rd);582}583Inst::VecTblExt { rd, ri, rn, rm } => {584collector.reg_use(rn);585collector.reg_use(rm);586collector.reg_reuse_def(rd, 3); // `rd` == `ri`.587collector.reg_use(ri);588}589590Inst::VecTbl2 { rd, rn, rn2, rm } => {591// Constrain to v30 / v31 so that we satisfy the "adjacent592// registers" constraint without use of pinned vregs in593// lowering.594collector.reg_fixed_use(rn, vreg(30));595collector.reg_fixed_use(rn2, vreg(31));596collector.reg_use(rm);597collector.reg_def(rd);598}599Inst::VecTbl2Ext {600rd,601ri,602rn,603rn2,604rm,605} => {606// Constrain to v30 / v31 so that we satisfy the "adjacent607// registers" constraint without use of pinned vregs in608// lowering.609collector.reg_fixed_use(rn, vreg(30));610collector.reg_fixed_use(rn2, vreg(31));611collector.reg_use(rm);612collector.reg_reuse_def(rd, 4); // `rd` == `ri`.613collector.reg_use(ri);614}615Inst::VecLoadReplicate { rd, rn, .. } => {616collector.reg_def(rd);617collector.reg_use(rn);618}619Inst::VecCSel { rd, rn, rm, .. } => {620collector.reg_def(rd);621collector.reg_use(rn);622collector.reg_use(rm);623}624Inst::FpuCmp { rn, rm, .. } => {625collector.reg_use(rn);626collector.reg_use(rm);627}628Inst::FpuLoad16 { rd, mem, .. } => {629collector.reg_def(rd);630memarg_operands(mem, collector);631}632Inst::FpuLoad32 { rd, mem, .. } => {633collector.reg_def(rd);634memarg_operands(mem, collector);635}636Inst::FpuLoad64 { rd, mem, .. } => {637collector.reg_def(rd);638memarg_operands(mem, collector);639}640Inst::FpuLoad128 { rd, mem, .. } => {641collector.reg_def(rd);642memarg_operands(mem, collector);643}644Inst::FpuStore16 { rd, mem, .. } => {645collector.reg_use(rd);646memarg_operands(mem, collector);647}648Inst::FpuStore32 { rd, mem, .. } => {649collector.reg_use(rd);650memarg_operands(mem, collector);651}652Inst::FpuStore64 { rd, mem, .. } => {653collector.reg_use(rd);654memarg_operands(mem, collector);655}656Inst::FpuStore128 { rd, mem, .. } => {657collector.reg_use(rd);658memarg_operands(mem, collector);659}660Inst::FpuLoadP64 { rt, rt2, mem, .. } => {661collector.reg_def(rt);662collector.reg_def(rt2);663pairmemarg_operands(mem, collector);664}665Inst::FpuStoreP64 { rt, rt2, mem, .. } => {666collector.reg_use(rt);667collector.reg_use(rt2);668pairmemarg_operands(mem, collector);669}670Inst::FpuLoadP128 { rt, rt2, mem, .. } => {671collector.reg_def(rt);672collector.reg_def(rt2);673pairmemarg_operands(mem, collector);674}675Inst::FpuStoreP128 { rt, rt2, mem, .. } => {676collector.reg_use(rt);677collector.reg_use(rt2);678pairmemarg_operands(mem, collector);679}680Inst::FpuToInt { rd, rn, .. } => {681collector.reg_def(rd);682collector.reg_use(rn);683}684Inst::IntToFpu { rd, rn, .. } => {685collector.reg_def(rd);686collector.reg_use(rn);687}688Inst::FpuCSel16 { rd, rn, rm, .. }689| Inst::FpuCSel32 { rd, rn, rm, .. }690| Inst::FpuCSel64 { rd, rn, rm, .. } => {691collector.reg_def(rd);692collector.reg_use(rn);693collector.reg_use(rm);694}695Inst::FpuRound { rd, rn, .. } => {696collector.reg_def(rd);697collector.reg_use(rn);698}699Inst::MovToFpu { rd, rn, .. } => {700collector.reg_def(rd);701collector.reg_use(rn);702}703Inst::FpuMoveFPImm { rd, .. } => {704collector.reg_def(rd);705}706Inst::MovToVec { rd, ri, rn, .. } => {707collector.reg_reuse_def(rd, 1); // `rd` == `ri`.708collector.reg_use(ri);709collector.reg_use(rn);710}711Inst::MovFromVec { rd, rn, .. } | Inst::MovFromVecSigned { rd, rn, .. } => {712collector.reg_def(rd);713collector.reg_use(rn);714}715Inst::VecDup { rd, rn, .. } => {716collector.reg_def(rd);717collector.reg_use(rn);718}719Inst::VecDupFromFpu { rd, rn, .. } => {720collector.reg_def(rd);721collector.reg_use(rn);722}723Inst::VecDupFPImm { rd, .. } => {724collector.reg_def(rd);725}726Inst::VecDupImm { rd, .. } => {727collector.reg_def(rd);728}729Inst::VecExtend { rd, rn, .. } => {730collector.reg_def(rd);731collector.reg_use(rn);732}733Inst::VecMovElement { rd, ri, rn, .. } => {734collector.reg_reuse_def(rd, 1); // `rd` == `ri`.735collector.reg_use(ri);736collector.reg_use(rn);737}738Inst::VecRRLong { rd, rn, .. } => {739collector.reg_def(rd);740collector.reg_use(rn);741}742Inst::VecRRNarrowLow { rd, rn, .. } => {743collector.reg_use(rn);744collector.reg_def(rd);745}746Inst::VecRRNarrowHigh { rd, ri, rn, .. } => {747collector.reg_use(rn);748collector.reg_reuse_def(rd, 2); // `rd` == `ri`.749collector.reg_use(ri);750}751Inst::VecRRPair { rd, rn, .. } => {752collector.reg_def(rd);753collector.reg_use(rn);754}755Inst::VecRRRLong { rd, rn, rm, .. } => {756collector.reg_def(rd);757collector.reg_use(rn);758collector.reg_use(rm);759}760Inst::VecRRRLongMod { rd, ri, rn, rm, .. } => {761collector.reg_reuse_def(rd, 1); // `rd` == `ri`.762collector.reg_use(ri);763collector.reg_use(rn);764collector.reg_use(rm);765}766Inst::VecRRPairLong { rd, rn, .. } => {767collector.reg_def(rd);768collector.reg_use(rn);769}770Inst::VecRRR { rd, rn, rm, .. } => {771collector.reg_def(rd);772collector.reg_use(rn);773collector.reg_use(rm);774}775Inst::VecRRRMod { rd, ri, rn, rm, .. } | Inst::VecFmlaElem { rd, ri, rn, rm, .. } => {776collector.reg_reuse_def(rd, 1); // `rd` == `ri`.777collector.reg_use(ri);778collector.reg_use(rn);779collector.reg_use(rm);780}781Inst::MovToNZCV { rn } => {782collector.reg_use(rn);783}784Inst::MovFromNZCV { rd } => {785collector.reg_def(rd);786}787Inst::Extend { rd, rn, .. } => {788collector.reg_def(rd);789collector.reg_use(rn);790}791Inst::Args { args } => {792for ArgPair { vreg, preg } in args {793collector.reg_fixed_def(vreg, *preg);794}795}796Inst::Rets { rets } => {797for RetPair { vreg, preg } in rets {798collector.reg_fixed_use(vreg, *preg);799}800}801Inst::Ret { .. } | Inst::AuthenticatedRet { .. } => {}802Inst::Jump { .. } => {}803Inst::Call { info, .. } => {804let CallInfo { uses, defs, .. } = &mut **info;805for CallArgPair { vreg, preg } in uses {806collector.reg_fixed_use(vreg, *preg);807}808for CallRetPair { vreg, location } in defs {809match location {810RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),811RetLocation::Stack(..) => collector.any_def(vreg),812}813}814collector.reg_clobbers(info.clobbers);815if let Some(try_call_info) = &mut info.try_call_info {816try_call_info.collect_operands(collector);817}818}819Inst::CallInd { info, .. } => {820let CallInfo {821dest, uses, defs, ..822} = &mut **info;823collector.reg_use(dest);824for CallArgPair { vreg, preg } in uses {825collector.reg_fixed_use(vreg, *preg);826}827for CallRetPair { vreg, location } in defs {828match location {829RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),830RetLocation::Stack(..) => collector.any_def(vreg),831}832}833collector.reg_clobbers(info.clobbers);834if let Some(try_call_info) = &mut info.try_call_info {835try_call_info.collect_operands(collector);836}837}838Inst::ReturnCall { info } => {839for CallArgPair { vreg, preg } in &mut info.uses {840collector.reg_fixed_use(vreg, *preg);841}842}843Inst::ReturnCallInd { info } => {844// TODO(https://github.com/bytecodealliance/regalloc2/issues/145):845// This shouldn't be a fixed register constraint, but it's not clear how to pick a846// register that won't be clobbered by the callee-save restore code emitted with a847// return_call_indirect.848collector.reg_fixed_use(&mut info.dest, xreg(1));849for CallArgPair { vreg, preg } in &mut info.uses {850collector.reg_fixed_use(vreg, *preg);851}852}853Inst::CondBr { kind, .. } => match kind {854CondBrKind::Zero(rt, _) | CondBrKind::NotZero(rt, _) => collector.reg_use(rt),855CondBrKind::Cond(_) => {}856},857Inst::TestBitAndBranch { rn, .. } => {858collector.reg_use(rn);859}860Inst::IndirectBr { rn, .. } => {861collector.reg_use(rn);862}863Inst::Nop0 | Inst::Nop4 => {}864Inst::Brk => {}865Inst::Udf { .. } => {}866Inst::TrapIf { kind, .. } => match kind {867CondBrKind::Zero(rt, _) | CondBrKind::NotZero(rt, _) => collector.reg_use(rt),868CondBrKind::Cond(_) => {}869},870Inst::Adr { rd, .. } | Inst::Adrp { rd, .. } => {871collector.reg_def(rd);872}873Inst::Word4 { .. } | Inst::Word8 { .. } => {}874Inst::JTSequence {875ridx, rtmp1, rtmp2, ..876} => {877collector.reg_use(ridx);878collector.reg_early_def(rtmp1);879collector.reg_early_def(rtmp2);880}881Inst::LoadExtNameGot { rd, .. }882| Inst::LoadExtNameNear { rd, .. }883| Inst::LoadExtNameFar { rd, .. } => {884collector.reg_def(rd);885}886Inst::LoadAddr { rd, mem } => {887collector.reg_def(rd);888memarg_operands(mem, collector);889}890Inst::Paci { .. } | Inst::Xpaclri => {891// Neither LR nor SP is an allocatable register, so there is no need892// to do anything.893}894Inst::Bti { .. } => {}895896Inst::ElfTlsGetAddr { rd, tmp, .. } => {897// TLSDESC has a very neat calling convention. It is required to preserve898// all registers except x0 and x30. X30 is non allocatable in cranelift since899// its the link register.900//901// Additionally we need a second register as a temporary register for the902// TLSDESC sequence. This register can be any register other than x0 (and x30).903collector.reg_fixed_def(rd, regs::xreg(0));904collector.reg_early_def(tmp);905}906Inst::MachOTlsGetAddr { rd, .. } => {907collector.reg_fixed_def(rd, regs::xreg(0));908let mut clobbers =909AArch64MachineDeps::get_regs_clobbered_by_call(CallConv::AppleAarch64, false);910clobbers.remove(regs::xreg_preg(0));911collector.reg_clobbers(clobbers);912}913Inst::Unwind { .. } => {}914Inst::EmitIsland { .. } => {}915Inst::DummyUse { reg } => {916collector.reg_use(reg);917}918Inst::LabelAddress { dst, .. } => {919collector.reg_def(dst);920}921Inst::StackProbeLoop { start, end, .. } => {922collector.reg_early_def(start);923collector.reg_use(end);924}925}926}927928//=============================================================================929// Instructions: misc functions and external interface930931impl MachInst for Inst {932type ABIMachineSpec = AArch64MachineDeps;933type LabelUse = LabelUse;934935// "CLIF" in hex, to make the trap recognizable during936// debugging.937const TRAP_OPCODE: &'static [u8] = &0xc11f_u32.to_le_bytes();938939fn get_operands(&mut self, collector: &mut impl OperandVisitor) {940aarch64_get_operands(self, collector);941}942943fn is_move(&self) -> Option<(Writable<Reg>, Reg)> {944match self {945&Inst::Mov {946size: OperandSize::Size64,947rd,948rm,949} => Some((rd, rm)),950&Inst::FpuMove64 { rd, rn } => Some((rd, rn)),951&Inst::FpuMove128 { rd, rn } => Some((rd, rn)),952_ => None,953}954}955956fn is_included_in_clobbers(&self) -> bool {957let (caller, callee, is_exception) = match self {958Inst::Args { .. } => return false,959Inst::Call { info } => (960info.caller_conv,961info.callee_conv,962info.try_call_info.is_some(),963),964Inst::CallInd { info } => (965info.caller_conv,966info.callee_conv,967info.try_call_info.is_some(),968),969_ => return true,970};971972// We exclude call instructions from the clobber-set when they are calls973// from caller to callee that both clobber the same register (such as974// using the same or similar ABIs). Such calls cannot possibly force any975// new registers to be saved in the prologue, because anything that the976// callee clobbers, the caller is also allowed to clobber. This both977// saves work and enables us to more precisely follow the978// half-caller-save, half-callee-save SysV ABI for some vector979// registers.980//981// See the note in [crate::isa::aarch64::abi::is_caller_save_reg] for982// more information on this ABI-implementation hack.983let caller_clobbers = AArch64MachineDeps::get_regs_clobbered_by_call(caller, false);984let callee_clobbers = AArch64MachineDeps::get_regs_clobbered_by_call(callee, is_exception);985986let mut all_clobbers = caller_clobbers;987all_clobbers.union_from(callee_clobbers);988all_clobbers != caller_clobbers989}990991fn is_trap(&self) -> bool {992match self {993Self::Udf { .. } => true,994_ => false,995}996}997998fn is_args(&self) -> bool {999match self {1000Self::Args { .. } => true,1001_ => false,1002}1003}10041005fn call_type(&self) -> CallType {1006match self {1007Inst::Call { .. }1008| Inst::CallInd { .. }1009| Inst::ElfTlsGetAddr { .. }1010| Inst::MachOTlsGetAddr { .. } => CallType::Regular,10111012Inst::ReturnCall { .. } | Inst::ReturnCallInd { .. } => CallType::TailCall,10131014_ => CallType::None,1015}1016}10171018fn is_term(&self) -> MachTerminator {1019match self {1020&Inst::Rets { .. } => MachTerminator::Ret,1021&Inst::ReturnCall { .. } | &Inst::ReturnCallInd { .. } => MachTerminator::RetCall,1022&Inst::Jump { .. } => MachTerminator::Branch,1023&Inst::CondBr { .. } => MachTerminator::Branch,1024&Inst::TestBitAndBranch { .. } => MachTerminator::Branch,1025&Inst::IndirectBr { .. } => MachTerminator::Branch,1026&Inst::JTSequence { .. } => MachTerminator::Branch,1027&Inst::Call { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,1028&Inst::CallInd { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,1029_ => MachTerminator::None,1030}1031}10321033fn is_mem_access(&self) -> bool {1034match self {1035&Inst::ULoad8 { .. }1036| &Inst::SLoad8 { .. }1037| &Inst::ULoad16 { .. }1038| &Inst::SLoad16 { .. }1039| &Inst::ULoad32 { .. }1040| &Inst::SLoad32 { .. }1041| &Inst::ULoad64 { .. }1042| &Inst::LoadP64 { .. }1043| &Inst::FpuLoad16 { .. }1044| &Inst::FpuLoad32 { .. }1045| &Inst::FpuLoad64 { .. }1046| &Inst::FpuLoad128 { .. }1047| &Inst::FpuLoadP64 { .. }1048| &Inst::FpuLoadP128 { .. }1049| &Inst::Store8 { .. }1050| &Inst::Store16 { .. }1051| &Inst::Store32 { .. }1052| &Inst::Store64 { .. }1053| &Inst::StoreP64 { .. }1054| &Inst::FpuStore16 { .. }1055| &Inst::FpuStore32 { .. }1056| &Inst::FpuStore64 { .. }1057| &Inst::FpuStore128 { .. } => true,1058// TODO: verify this carefully1059_ => false,1060}1061}10621063fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {1064let bits = ty.bits();10651066assert!(bits <= 128);1067assert!(to_reg.to_reg().class() == from_reg.class());1068match from_reg.class() {1069RegClass::Int => Inst::Mov {1070size: OperandSize::Size64,1071rd: to_reg,1072rm: from_reg,1073},1074RegClass::Float => {1075if bits > 64 {1076Inst::FpuMove128 {1077rd: to_reg,1078rn: from_reg,1079}1080} else {1081Inst::FpuMove64 {1082rd: to_reg,1083rn: from_reg,1084}1085}1086}1087RegClass::Vector => unreachable!(),1088}1089}10901091fn is_safepoint(&self) -> bool {1092match self {1093Inst::Call { .. } | Inst::CallInd { .. } => true,1094_ => false,1095}1096}10971098fn gen_dummy_use(reg: Reg) -> Inst {1099Inst::DummyUse { reg }1100}11011102fn gen_nop(preferred_size: usize) -> Inst {1103if preferred_size == 0 {1104return Inst::Nop0;1105}1106// We can't give a NOP (or any insn) < 4 bytes.1107assert!(preferred_size >= 4);1108Inst::Nop41109}11101111fn rc_for_type(ty: Type) -> CodegenResult<(&'static [RegClass], &'static [Type])> {1112match ty {1113I8 => Ok((&[RegClass::Int], &[I8])),1114I16 => Ok((&[RegClass::Int], &[I16])),1115I32 => Ok((&[RegClass::Int], &[I32])),1116I64 => Ok((&[RegClass::Int], &[I64])),1117F16 => Ok((&[RegClass::Float], &[F16])),1118F32 => Ok((&[RegClass::Float], &[F32])),1119F64 => Ok((&[RegClass::Float], &[F64])),1120F128 => Ok((&[RegClass::Float], &[F128])),1121I128 => Ok((&[RegClass::Int, RegClass::Int], &[I64, I64])),1122_ if ty.is_vector() && ty.bits() <= 128 => {1123let types = &[types::I8X2, types::I8X4, types::I8X8, types::I8X16];1124Ok((1125&[RegClass::Float],1126slice::from_ref(&types[ty.bytes().ilog2() as usize - 1]),1127))1128}1129_ if ty.is_dynamic_vector() => Ok((&[RegClass::Float], &[I8X16])),1130_ => Err(CodegenError::Unsupported(format!(1131"Unexpected SSA-value type: {ty}"1132))),1133}1134}11351136fn canonical_type_for_rc(rc: RegClass) -> Type {1137match rc {1138RegClass::Float => types::I8X16,1139RegClass::Int => types::I64,1140RegClass::Vector => unreachable!(),1141}1142}11431144fn gen_jump(target: MachLabel) -> Inst {1145Inst::Jump {1146dest: BranchTarget::Label(target),1147}1148}11491150fn worst_case_size() -> CodeOffset {1151// The maximum size, in bytes, of any `Inst`'s emitted code. We have at least one case of1152// an 8-instruction sequence (saturating int-to-float conversions) with three embedded1153// 64-bit f64 constants.1154//1155// Note that inline jump-tables handle island/pool insertion separately, so we do not need1156// to account for them here (otherwise the worst case would be 2^31 * 4, clearly not1157// feasible for other reasons).1158441159}11601161fn ref_type_regclass(_: &settings::Flags) -> RegClass {1162RegClass::Int1163}11641165fn gen_block_start(1166is_indirect_branch_target: bool,1167is_forward_edge_cfi_enabled: bool,1168) -> Option<Self> {1169if is_indirect_branch_target && is_forward_edge_cfi_enabled {1170Some(Inst::Bti {1171targets: BranchTargetType::J,1172})1173} else {1174None1175}1176}11771178fn function_alignment() -> FunctionAlignment {1179// We use 32-byte alignment for performance reasons, but for correctness1180// we would only need 4-byte alignment.1181FunctionAlignment {1182minimum: 4,1183preferred: 32,1184}1185}1186}11871188//=============================================================================1189// Pretty-printing of instructions.11901191fn mem_finalize_for_show(mem: &AMode, access_ty: Type, state: &EmitState) -> (String, String) {1192let (mem_insts, mem) = mem_finalize(None, mem, access_ty, state);1193let mut mem_str = mem_insts1194.into_iter()1195.map(|inst| inst.print_with_state(&mut EmitState::default()))1196.collect::<Vec<_>>()1197.join(" ; ");1198if !mem_str.is_empty() {1199mem_str += " ; ";1200}12011202let mem = mem.pretty_print(access_ty.bytes() as u8);1203(mem_str, mem)1204}12051206fn pretty_print_try_call(info: &TryCallInfo) -> String {1207format!(1208"; b {:?}; catch [{}]",1209info.continuation,1210info.pretty_print_dests()1211)1212}12131214impl Inst {1215fn print_with_state(&self, state: &mut EmitState) -> String {1216fn op_name(alu_op: ALUOp) -> &'static str {1217match alu_op {1218ALUOp::Add => "add",1219ALUOp::Sub => "sub",1220ALUOp::Orr => "orr",1221ALUOp::And => "and",1222ALUOp::AndS => "ands",1223ALUOp::Eor => "eor",1224ALUOp::AddS => "adds",1225ALUOp::SubS => "subs",1226ALUOp::SMulH => "smulh",1227ALUOp::UMulH => "umulh",1228ALUOp::SDiv => "sdiv",1229ALUOp::UDiv => "udiv",1230ALUOp::AndNot => "bic",1231ALUOp::OrrNot => "orn",1232ALUOp::EorNot => "eon",1233ALUOp::Extr => "extr",1234ALUOp::Lsr => "lsr",1235ALUOp::Asr => "asr",1236ALUOp::Lsl => "lsl",1237ALUOp::Adc => "adc",1238ALUOp::AdcS => "adcs",1239ALUOp::Sbc => "sbc",1240ALUOp::SbcS => "sbcs",1241}1242}12431244match self {1245&Inst::Nop0 => "nop-zero-len".to_string(),1246&Inst::Nop4 => "nop".to_string(),1247&Inst::AluRRR {1248alu_op,1249size,1250rd,1251rn,1252rm,1253} => {1254let op = op_name(alu_op);1255let rd = pretty_print_ireg(rd.to_reg(), size);1256let rn = pretty_print_ireg(rn, size);1257let rm = pretty_print_ireg(rm, size);1258format!("{op} {rd}, {rn}, {rm}")1259}1260&Inst::AluRRRR {1261alu_op,1262size,1263rd,1264rn,1265rm,1266ra,1267} => {1268let (op, da_size) = match alu_op {1269ALUOp3::MAdd => ("madd", size),1270ALUOp3::MSub => ("msub", size),1271ALUOp3::UMAddL => ("umaddl", OperandSize::Size64),1272ALUOp3::SMAddL => ("smaddl", OperandSize::Size64),1273};1274let rd = pretty_print_ireg(rd.to_reg(), da_size);1275let rn = pretty_print_ireg(rn, size);1276let rm = pretty_print_ireg(rm, size);1277let ra = pretty_print_ireg(ra, da_size);12781279format!("{op} {rd}, {rn}, {rm}, {ra}")1280}1281&Inst::AluRRImm12 {1282alu_op,1283size,1284rd,1285rn,1286ref imm12,1287} => {1288let op = op_name(alu_op);1289let rd = pretty_print_ireg(rd.to_reg(), size);1290let rn = pretty_print_ireg(rn, size);12911292if imm12.bits == 0 && alu_op == ALUOp::Add && size.is64() {1293// special-case MOV (used for moving into SP).1294format!("mov {rd}, {rn}")1295} else {1296let imm12 = imm12.pretty_print(0);1297format!("{op} {rd}, {rn}, {imm12}")1298}1299}1300&Inst::AluRRImmLogic {1301alu_op,1302size,1303rd,1304rn,1305ref imml,1306} => {1307let op = op_name(alu_op);1308let rd = pretty_print_ireg(rd.to_reg(), size);1309let rn = pretty_print_ireg(rn, size);1310let imml = imml.pretty_print(0);1311format!("{op} {rd}, {rn}, {imml}")1312}1313&Inst::AluRRImmShift {1314alu_op,1315size,1316rd,1317rn,1318ref immshift,1319} => {1320let op = op_name(alu_op);1321let rd = pretty_print_ireg(rd.to_reg(), size);1322let rn = pretty_print_ireg(rn, size);1323let immshift = immshift.pretty_print(0);1324format!("{op} {rd}, {rn}, {immshift}")1325}1326&Inst::AluRRRShift {1327alu_op,1328size,1329rd,1330rn,1331rm,1332ref shiftop,1333} => {1334let op = op_name(alu_op);1335let rd = pretty_print_ireg(rd.to_reg(), size);1336let rn = pretty_print_ireg(rn, size);1337let rm = pretty_print_ireg(rm, size);1338let shiftop = shiftop.pretty_print(0);1339format!("{op} {rd}, {rn}, {rm}, {shiftop}")1340}1341&Inst::AluRRRExtend {1342alu_op,1343size,1344rd,1345rn,1346rm,1347ref extendop,1348} => {1349let op = op_name(alu_op);1350let rd = pretty_print_ireg(rd.to_reg(), size);1351let rn = pretty_print_ireg(rn, size);1352let rm = pretty_print_ireg(rm, size);1353let extendop = extendop.pretty_print(0);1354format!("{op} {rd}, {rn}, {rm}, {extendop}")1355}1356&Inst::BitRR { op, size, rd, rn } => {1357let op = op.op_str();1358let rd = pretty_print_ireg(rd.to_reg(), size);1359let rn = pretty_print_ireg(rn, size);1360format!("{op} {rd}, {rn}")1361}1362&Inst::ULoad8 { rd, ref mem, .. }1363| &Inst::SLoad8 { rd, ref mem, .. }1364| &Inst::ULoad16 { rd, ref mem, .. }1365| &Inst::SLoad16 { rd, ref mem, .. }1366| &Inst::ULoad32 { rd, ref mem, .. }1367| &Inst::SLoad32 { rd, ref mem, .. }1368| &Inst::ULoad64 { rd, ref mem, .. } => {1369let is_unscaled = match &mem {1370&AMode::Unscaled { .. } => true,1371_ => false,1372};1373let (op, size) = match (self, is_unscaled) {1374(&Inst::ULoad8 { .. }, false) => ("ldrb", OperandSize::Size32),1375(&Inst::ULoad8 { .. }, true) => ("ldurb", OperandSize::Size32),1376(&Inst::SLoad8 { .. }, false) => ("ldrsb", OperandSize::Size64),1377(&Inst::SLoad8 { .. }, true) => ("ldursb", OperandSize::Size64),1378(&Inst::ULoad16 { .. }, false) => ("ldrh", OperandSize::Size32),1379(&Inst::ULoad16 { .. }, true) => ("ldurh", OperandSize::Size32),1380(&Inst::SLoad16 { .. }, false) => ("ldrsh", OperandSize::Size64),1381(&Inst::SLoad16 { .. }, true) => ("ldursh", OperandSize::Size64),1382(&Inst::ULoad32 { .. }, false) => ("ldr", OperandSize::Size32),1383(&Inst::ULoad32 { .. }, true) => ("ldur", OperandSize::Size32),1384(&Inst::SLoad32 { .. }, false) => ("ldrsw", OperandSize::Size64),1385(&Inst::SLoad32 { .. }, true) => ("ldursw", OperandSize::Size64),1386(&Inst::ULoad64 { .. }, false) => ("ldr", OperandSize::Size64),1387(&Inst::ULoad64 { .. }, true) => ("ldur", OperandSize::Size64),1388_ => unreachable!(),1389};13901391let rd = pretty_print_ireg(rd.to_reg(), size);1392let mem = mem.clone();1393let access_ty = self.mem_type().unwrap();1394let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);13951396format!("{mem_str}{op} {rd}, {mem}")1397}1398&Inst::Store8 { rd, ref mem, .. }1399| &Inst::Store16 { rd, ref mem, .. }1400| &Inst::Store32 { rd, ref mem, .. }1401| &Inst::Store64 { rd, ref mem, .. } => {1402let is_unscaled = match &mem {1403&AMode::Unscaled { .. } => true,1404_ => false,1405};1406let (op, size) = match (self, is_unscaled) {1407(&Inst::Store8 { .. }, false) => ("strb", OperandSize::Size32),1408(&Inst::Store8 { .. }, true) => ("sturb", OperandSize::Size32),1409(&Inst::Store16 { .. }, false) => ("strh", OperandSize::Size32),1410(&Inst::Store16 { .. }, true) => ("sturh", OperandSize::Size32),1411(&Inst::Store32 { .. }, false) => ("str", OperandSize::Size32),1412(&Inst::Store32 { .. }, true) => ("stur", OperandSize::Size32),1413(&Inst::Store64 { .. }, false) => ("str", OperandSize::Size64),1414(&Inst::Store64 { .. }, true) => ("stur", OperandSize::Size64),1415_ => unreachable!(),1416};14171418let rd = pretty_print_ireg(rd, size);1419let mem = mem.clone();1420let access_ty = self.mem_type().unwrap();1421let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);14221423format!("{mem_str}{op} {rd}, {mem}")1424}1425&Inst::StoreP64 {1426rt, rt2, ref mem, ..1427} => {1428let rt = pretty_print_ireg(rt, OperandSize::Size64);1429let rt2 = pretty_print_ireg(rt2, OperandSize::Size64);1430let mem = mem.clone();1431let mem = mem.pretty_print_default();1432format!("stp {rt}, {rt2}, {mem}")1433}1434&Inst::LoadP64 {1435rt, rt2, ref mem, ..1436} => {1437let rt = pretty_print_ireg(rt.to_reg(), OperandSize::Size64);1438let rt2 = pretty_print_ireg(rt2.to_reg(), OperandSize::Size64);1439let mem = mem.clone();1440let mem = mem.pretty_print_default();1441format!("ldp {rt}, {rt2}, {mem}")1442}1443&Inst::Mov { size, rd, rm } => {1444let rd = pretty_print_ireg(rd.to_reg(), size);1445let rm = pretty_print_ireg(rm, size);1446format!("mov {rd}, {rm}")1447}1448&Inst::MovFromPReg { rd, rm } => {1449let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64);1450let rm = show_ireg_sized(rm.into(), OperandSize::Size64);1451format!("mov {rd}, {rm}")1452}1453&Inst::MovToPReg { rd, rm } => {1454let rd = show_ireg_sized(rd.into(), OperandSize::Size64);1455let rm = pretty_print_ireg(rm, OperandSize::Size64);1456format!("mov {rd}, {rm}")1457}1458&Inst::MovWide {1459op,1460rd,1461ref imm,1462size,1463} => {1464let op_str = match op {1465MoveWideOp::MovZ => "movz",1466MoveWideOp::MovN => "movn",1467};1468let rd = pretty_print_ireg(rd.to_reg(), size);1469let imm = imm.pretty_print(0);1470format!("{op_str} {rd}, {imm}")1471}1472&Inst::MovK {1473rd,1474rn,1475ref imm,1476size,1477} => {1478let rn = pretty_print_ireg(rn, size);1479let rd = pretty_print_ireg(rd.to_reg(), size);1480let imm = imm.pretty_print(0);1481format!("movk {rd}, {rn}, {imm}")1482}1483&Inst::CSel { rd, rn, rm, cond } => {1484let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64);1485let rn = pretty_print_ireg(rn, OperandSize::Size64);1486let rm = pretty_print_ireg(rm, OperandSize::Size64);1487let cond = cond.pretty_print(0);1488format!("csel {rd}, {rn}, {rm}, {cond}")1489}1490&Inst::CSNeg { rd, rn, rm, cond } => {1491let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64);1492let rn = pretty_print_ireg(rn, OperandSize::Size64);1493let rm = pretty_print_ireg(rm, OperandSize::Size64);1494let cond = cond.pretty_print(0);1495format!("csneg {rd}, {rn}, {rm}, {cond}")1496}1497&Inst::CSet { rd, cond } => {1498let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64);1499let cond = cond.pretty_print(0);1500format!("cset {rd}, {cond}")1501}1502&Inst::CSetm { rd, cond } => {1503let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size64);1504let cond = cond.pretty_print(0);1505format!("csetm {rd}, {cond}")1506}1507&Inst::CCmp {1508size,1509rn,1510rm,1511nzcv,1512cond,1513} => {1514let rn = pretty_print_ireg(rn, size);1515let rm = pretty_print_ireg(rm, size);1516let nzcv = nzcv.pretty_print(0);1517let cond = cond.pretty_print(0);1518format!("ccmp {rn}, {rm}, {nzcv}, {cond}")1519}1520&Inst::CCmpImm {1521size,1522rn,1523imm,1524nzcv,1525cond,1526} => {1527let rn = pretty_print_ireg(rn, size);1528let imm = imm.pretty_print(0);1529let nzcv = nzcv.pretty_print(0);1530let cond = cond.pretty_print(0);1531format!("ccmp {rn}, {imm}, {nzcv}, {cond}")1532}1533&Inst::AtomicRMW {1534rs, rt, rn, ty, op, ..1535} => {1536let op = match op {1537AtomicRMWOp::Add => "ldaddal",1538AtomicRMWOp::Clr => "ldclral",1539AtomicRMWOp::Eor => "ldeoral",1540AtomicRMWOp::Set => "ldsetal",1541AtomicRMWOp::Smax => "ldsmaxal",1542AtomicRMWOp::Umax => "ldumaxal",1543AtomicRMWOp::Smin => "ldsminal",1544AtomicRMWOp::Umin => "lduminal",1545AtomicRMWOp::Swp => "swpal",1546};15471548let size = OperandSize::from_ty(ty);1549let rs = pretty_print_ireg(rs, size);1550let rt = pretty_print_ireg(rt.to_reg(), size);1551let rn = pretty_print_ireg(rn, OperandSize::Size64);15521553let ty_suffix = match ty {1554I8 => "b",1555I16 => "h",1556_ => "",1557};1558format!("{op}{ty_suffix} {rs}, {rt}, [{rn}]")1559}1560&Inst::AtomicRMWLoop {1561ty,1562op,1563addr,1564operand,1565oldval,1566scratch1,1567scratch2,1568..1569} => {1570let op = match op {1571AtomicRMWLoopOp::Add => "add",1572AtomicRMWLoopOp::Sub => "sub",1573AtomicRMWLoopOp::Eor => "eor",1574AtomicRMWLoopOp::Orr => "orr",1575AtomicRMWLoopOp::And => "and",1576AtomicRMWLoopOp::Nand => "nand",1577AtomicRMWLoopOp::Smin => "smin",1578AtomicRMWLoopOp::Smax => "smax",1579AtomicRMWLoopOp::Umin => "umin",1580AtomicRMWLoopOp::Umax => "umax",1581AtomicRMWLoopOp::Xchg => "xchg",1582};1583let addr = pretty_print_ireg(addr, OperandSize::Size64);1584let operand = pretty_print_ireg(operand, OperandSize::Size64);1585let oldval = pretty_print_ireg(oldval.to_reg(), OperandSize::Size64);1586let scratch1 = pretty_print_ireg(scratch1.to_reg(), OperandSize::Size64);1587let scratch2 = pretty_print_ireg(scratch2.to_reg(), OperandSize::Size64);1588format!(1589"atomic_rmw_loop_{}_{} addr={} operand={} oldval={} scratch1={} scratch2={}",1590op,1591ty.bits(),1592addr,1593operand,1594oldval,1595scratch1,1596scratch2,1597)1598}1599&Inst::AtomicCAS {1600rd, rs, rt, rn, ty, ..1601} => {1602let op = match ty {1603I8 => "casalb",1604I16 => "casalh",1605I32 | I64 => "casal",1606_ => panic!("Unsupported type: {ty}"),1607};1608let size = OperandSize::from_ty(ty);1609let rd = pretty_print_ireg(rd.to_reg(), size);1610let rs = pretty_print_ireg(rs, size);1611let rt = pretty_print_ireg(rt, size);1612let rn = pretty_print_ireg(rn, OperandSize::Size64);16131614format!("{op} {rd}, {rs}, {rt}, [{rn}]")1615}1616&Inst::AtomicCASLoop {1617ty,1618addr,1619expected,1620replacement,1621oldval,1622scratch,1623..1624} => {1625let addr = pretty_print_ireg(addr, OperandSize::Size64);1626let expected = pretty_print_ireg(expected, OperandSize::Size64);1627let replacement = pretty_print_ireg(replacement, OperandSize::Size64);1628let oldval = pretty_print_ireg(oldval.to_reg(), OperandSize::Size64);1629let scratch = pretty_print_ireg(scratch.to_reg(), OperandSize::Size64);1630format!(1631"atomic_cas_loop_{} addr={}, expect={}, replacement={}, oldval={}, scratch={}",1632ty.bits(),1633addr,1634expected,1635replacement,1636oldval,1637scratch,1638)1639}1640&Inst::LoadAcquire {1641access_ty, rt, rn, ..1642} => {1643let (op, ty) = match access_ty {1644I8 => ("ldarb", I32),1645I16 => ("ldarh", I32),1646I32 => ("ldar", I32),1647I64 => ("ldar", I64),1648_ => panic!("Unsupported type: {access_ty}"),1649};1650let size = OperandSize::from_ty(ty);1651let rn = pretty_print_ireg(rn, OperandSize::Size64);1652let rt = pretty_print_ireg(rt.to_reg(), size);1653format!("{op} {rt}, [{rn}]")1654}1655&Inst::StoreRelease {1656access_ty, rt, rn, ..1657} => {1658let (op, ty) = match access_ty {1659I8 => ("stlrb", I32),1660I16 => ("stlrh", I32),1661I32 => ("stlr", I32),1662I64 => ("stlr", I64),1663_ => panic!("Unsupported type: {access_ty}"),1664};1665let size = OperandSize::from_ty(ty);1666let rn = pretty_print_ireg(rn, OperandSize::Size64);1667let rt = pretty_print_ireg(rt, size);1668format!("{op} {rt}, [{rn}]")1669}1670&Inst::Fence {} => {1671format!("dmb ish")1672}1673&Inst::Csdb {} => {1674format!("csdb")1675}1676&Inst::FpuMove32 { rd, rn } => {1677let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size32);1678let rn = pretty_print_vreg_scalar(rn, ScalarSize::Size32);1679format!("fmov {rd}, {rn}")1680}1681&Inst::FpuMove64 { rd, rn } => {1682let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64);1683let rn = pretty_print_vreg_scalar(rn, ScalarSize::Size64);1684format!("fmov {rd}, {rn}")1685}1686&Inst::FpuMove128 { rd, rn } => {1687let rd = pretty_print_reg(rd.to_reg());1688let rn = pretty_print_reg(rn);1689format!("mov {rd}.16b, {rn}.16b")1690}1691&Inst::FpuMoveFromVec { rd, rn, idx, size } => {1692let rd = pretty_print_vreg_scalar(rd.to_reg(), size.lane_size());1693let rn = pretty_print_vreg_element(rn, idx as usize, size.lane_size());1694format!("mov {rd}, {rn}")1695}1696&Inst::FpuExtend { rd, rn, size } => {1697let rd = pretty_print_vreg_scalar(rd.to_reg(), size);1698let rn = pretty_print_vreg_scalar(rn, size);1699format!("fmov {rd}, {rn}")1700}1701&Inst::FpuRR {1702fpu_op,1703size,1704rd,1705rn,1706} => {1707let op = match fpu_op {1708FPUOp1::Abs => "fabs",1709FPUOp1::Neg => "fneg",1710FPUOp1::Sqrt => "fsqrt",1711FPUOp1::Cvt32To64 | FPUOp1::Cvt64To32 => "fcvt",1712};1713let dst_size = match fpu_op {1714FPUOp1::Cvt32To64 => ScalarSize::Size64,1715FPUOp1::Cvt64To32 => ScalarSize::Size32,1716_ => size,1717};1718let rd = pretty_print_vreg_scalar(rd.to_reg(), dst_size);1719let rn = pretty_print_vreg_scalar(rn, size);1720format!("{op} {rd}, {rn}")1721}1722&Inst::FpuRRR {1723fpu_op,1724size,1725rd,1726rn,1727rm,1728} => {1729let op = match fpu_op {1730FPUOp2::Add => "fadd",1731FPUOp2::Sub => "fsub",1732FPUOp2::Mul => "fmul",1733FPUOp2::Div => "fdiv",1734FPUOp2::Max => "fmax",1735FPUOp2::Min => "fmin",1736};1737let rd = pretty_print_vreg_scalar(rd.to_reg(), size);1738let rn = pretty_print_vreg_scalar(rn, size);1739let rm = pretty_print_vreg_scalar(rm, size);1740format!("{op} {rd}, {rn}, {rm}")1741}1742&Inst::FpuRRI { fpu_op, rd, rn } => {1743let (op, imm, vector) = match fpu_op {1744FPUOpRI::UShr32(imm) => ("ushr", imm.pretty_print(0), true),1745FPUOpRI::UShr64(imm) => ("ushr", imm.pretty_print(0), false),1746};17471748let (rd, rn) = if vector {1749(1750pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size32x2),1751pretty_print_vreg_vector(rn, VectorSize::Size32x2),1752)1753} else {1754(1755pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64),1756pretty_print_vreg_scalar(rn, ScalarSize::Size64),1757)1758};1759format!("{op} {rd}, {rn}, {imm}")1760}1761&Inst::FpuRRIMod { fpu_op, rd, ri, rn } => {1762let (op, imm, vector) = match fpu_op {1763FPUOpRIMod::Sli32(imm) => ("sli", imm.pretty_print(0), true),1764FPUOpRIMod::Sli64(imm) => ("sli", imm.pretty_print(0), false),1765};17661767let (rd, ri, rn) = if vector {1768(1769pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size32x2),1770pretty_print_vreg_vector(ri, VectorSize::Size32x2),1771pretty_print_vreg_vector(rn, VectorSize::Size32x2),1772)1773} else {1774(1775pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64),1776pretty_print_vreg_scalar(ri, ScalarSize::Size64),1777pretty_print_vreg_scalar(rn, ScalarSize::Size64),1778)1779};1780format!("{op} {rd}, {ri}, {rn}, {imm}")1781}1782&Inst::FpuRRRR {1783fpu_op,1784size,1785rd,1786rn,1787rm,1788ra,1789} => {1790let op = match fpu_op {1791FPUOp3::MAdd => "fmadd",1792FPUOp3::MSub => "fmsub",1793FPUOp3::NMAdd => "fnmadd",1794FPUOp3::NMSub => "fnmsub",1795};1796let rd = pretty_print_vreg_scalar(rd.to_reg(), size);1797let rn = pretty_print_vreg_scalar(rn, size);1798let rm = pretty_print_vreg_scalar(rm, size);1799let ra = pretty_print_vreg_scalar(ra, size);1800format!("{op} {rd}, {rn}, {rm}, {ra}")1801}1802&Inst::FpuCmp { size, rn, rm } => {1803let rn = pretty_print_vreg_scalar(rn, size);1804let rm = pretty_print_vreg_scalar(rm, size);1805format!("fcmp {rn}, {rm}")1806}1807&Inst::FpuLoad16 { rd, ref mem, .. } => {1808let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size16);1809let mem = mem.clone();1810let access_ty = self.mem_type().unwrap();1811let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1812format!("{mem_str}ldr {rd}, {mem}")1813}1814&Inst::FpuLoad32 { rd, ref mem, .. } => {1815let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size32);1816let mem = mem.clone();1817let access_ty = self.mem_type().unwrap();1818let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1819format!("{mem_str}ldr {rd}, {mem}")1820}1821&Inst::FpuLoad64 { rd, ref mem, .. } => {1822let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64);1823let mem = mem.clone();1824let access_ty = self.mem_type().unwrap();1825let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1826format!("{mem_str}ldr {rd}, {mem}")1827}1828&Inst::FpuLoad128 { rd, ref mem, .. } => {1829let rd = pretty_print_reg(rd.to_reg());1830let rd = "q".to_string() + &rd[1..];1831let mem = mem.clone();1832let access_ty = self.mem_type().unwrap();1833let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1834format!("{mem_str}ldr {rd}, {mem}")1835}1836&Inst::FpuStore16 { rd, ref mem, .. } => {1837let rd = pretty_print_vreg_scalar(rd, ScalarSize::Size16);1838let mem = mem.clone();1839let access_ty = self.mem_type().unwrap();1840let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1841format!("{mem_str}str {rd}, {mem}")1842}1843&Inst::FpuStore32 { rd, ref mem, .. } => {1844let rd = pretty_print_vreg_scalar(rd, ScalarSize::Size32);1845let mem = mem.clone();1846let access_ty = self.mem_type().unwrap();1847let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1848format!("{mem_str}str {rd}, {mem}")1849}1850&Inst::FpuStore64 { rd, ref mem, .. } => {1851let rd = pretty_print_vreg_scalar(rd, ScalarSize::Size64);1852let mem = mem.clone();1853let access_ty = self.mem_type().unwrap();1854let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1855format!("{mem_str}str {rd}, {mem}")1856}1857&Inst::FpuStore128 { rd, ref mem, .. } => {1858let rd = pretty_print_reg(rd);1859let rd = "q".to_string() + &rd[1..];1860let mem = mem.clone();1861let access_ty = self.mem_type().unwrap();1862let (mem_str, mem) = mem_finalize_for_show(&mem, access_ty, state);1863format!("{mem_str}str {rd}, {mem}")1864}1865&Inst::FpuLoadP64 {1866rt, rt2, ref mem, ..1867} => {1868let rt = pretty_print_vreg_scalar(rt.to_reg(), ScalarSize::Size64);1869let rt2 = pretty_print_vreg_scalar(rt2.to_reg(), ScalarSize::Size64);1870let mem = mem.clone();1871let mem = mem.pretty_print_default();18721873format!("ldp {rt}, {rt2}, {mem}")1874}1875&Inst::FpuStoreP64 {1876rt, rt2, ref mem, ..1877} => {1878let rt = pretty_print_vreg_scalar(rt, ScalarSize::Size64);1879let rt2 = pretty_print_vreg_scalar(rt2, ScalarSize::Size64);1880let mem = mem.clone();1881let mem = mem.pretty_print_default();18821883format!("stp {rt}, {rt2}, {mem}")1884}1885&Inst::FpuLoadP128 {1886rt, rt2, ref mem, ..1887} => {1888let rt = pretty_print_vreg_scalar(rt.to_reg(), ScalarSize::Size128);1889let rt2 = pretty_print_vreg_scalar(rt2.to_reg(), ScalarSize::Size128);1890let mem = mem.clone();1891let mem = mem.pretty_print_default();18921893format!("ldp {rt}, {rt2}, {mem}")1894}1895&Inst::FpuStoreP128 {1896rt, rt2, ref mem, ..1897} => {1898let rt = pretty_print_vreg_scalar(rt, ScalarSize::Size128);1899let rt2 = pretty_print_vreg_scalar(rt2, ScalarSize::Size128);1900let mem = mem.clone();1901let mem = mem.pretty_print_default();19021903format!("stp {rt}, {rt2}, {mem}")1904}1905&Inst::FpuToInt { op, rd, rn } => {1906let (op, sizesrc, sizedest) = match op {1907FpuToIntOp::F32ToI32 => ("fcvtzs", ScalarSize::Size32, OperandSize::Size32),1908FpuToIntOp::F32ToU32 => ("fcvtzu", ScalarSize::Size32, OperandSize::Size32),1909FpuToIntOp::F32ToI64 => ("fcvtzs", ScalarSize::Size32, OperandSize::Size64),1910FpuToIntOp::F32ToU64 => ("fcvtzu", ScalarSize::Size32, OperandSize::Size64),1911FpuToIntOp::F64ToI32 => ("fcvtzs", ScalarSize::Size64, OperandSize::Size32),1912FpuToIntOp::F64ToU32 => ("fcvtzu", ScalarSize::Size64, OperandSize::Size32),1913FpuToIntOp::F64ToI64 => ("fcvtzs", ScalarSize::Size64, OperandSize::Size64),1914FpuToIntOp::F64ToU64 => ("fcvtzu", ScalarSize::Size64, OperandSize::Size64),1915};1916let rd = pretty_print_ireg(rd.to_reg(), sizedest);1917let rn = pretty_print_vreg_scalar(rn, sizesrc);1918format!("{op} {rd}, {rn}")1919}1920&Inst::IntToFpu { op, rd, rn } => {1921let (op, sizesrc, sizedest) = match op {1922IntToFpuOp::I32ToF32 => ("scvtf", OperandSize::Size32, ScalarSize::Size32),1923IntToFpuOp::U32ToF32 => ("ucvtf", OperandSize::Size32, ScalarSize::Size32),1924IntToFpuOp::I64ToF32 => ("scvtf", OperandSize::Size64, ScalarSize::Size32),1925IntToFpuOp::U64ToF32 => ("ucvtf", OperandSize::Size64, ScalarSize::Size32),1926IntToFpuOp::I32ToF64 => ("scvtf", OperandSize::Size32, ScalarSize::Size64),1927IntToFpuOp::U32ToF64 => ("ucvtf", OperandSize::Size32, ScalarSize::Size64),1928IntToFpuOp::I64ToF64 => ("scvtf", OperandSize::Size64, ScalarSize::Size64),1929IntToFpuOp::U64ToF64 => ("ucvtf", OperandSize::Size64, ScalarSize::Size64),1930};1931let rd = pretty_print_vreg_scalar(rd.to_reg(), sizedest);1932let rn = pretty_print_ireg(rn, sizesrc);1933format!("{op} {rd}, {rn}")1934}1935&Inst::FpuCSel16 { rd, rn, rm, cond } => {1936let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size16);1937let rn = pretty_print_vreg_scalar(rn, ScalarSize::Size16);1938let rm = pretty_print_vreg_scalar(rm, ScalarSize::Size16);1939let cond = cond.pretty_print(0);1940format!("fcsel {rd}, {rn}, {rm}, {cond}")1941}1942&Inst::FpuCSel32 { rd, rn, rm, cond } => {1943let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size32);1944let rn = pretty_print_vreg_scalar(rn, ScalarSize::Size32);1945let rm = pretty_print_vreg_scalar(rm, ScalarSize::Size32);1946let cond = cond.pretty_print(0);1947format!("fcsel {rd}, {rn}, {rm}, {cond}")1948}1949&Inst::FpuCSel64 { rd, rn, rm, cond } => {1950let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64);1951let rn = pretty_print_vreg_scalar(rn, ScalarSize::Size64);1952let rm = pretty_print_vreg_scalar(rm, ScalarSize::Size64);1953let cond = cond.pretty_print(0);1954format!("fcsel {rd}, {rn}, {rm}, {cond}")1955}1956&Inst::FpuRound { op, rd, rn } => {1957let (inst, size) = match op {1958FpuRoundMode::Minus32 => ("frintm", ScalarSize::Size32),1959FpuRoundMode::Minus64 => ("frintm", ScalarSize::Size64),1960FpuRoundMode::Plus32 => ("frintp", ScalarSize::Size32),1961FpuRoundMode::Plus64 => ("frintp", ScalarSize::Size64),1962FpuRoundMode::Zero32 => ("frintz", ScalarSize::Size32),1963FpuRoundMode::Zero64 => ("frintz", ScalarSize::Size64),1964FpuRoundMode::Nearest32 => ("frintn", ScalarSize::Size32),1965FpuRoundMode::Nearest64 => ("frintn", ScalarSize::Size64),1966};1967let rd = pretty_print_vreg_scalar(rd.to_reg(), size);1968let rn = pretty_print_vreg_scalar(rn, size);1969format!("{inst} {rd}, {rn}")1970}1971&Inst::MovToFpu { rd, rn, size } => {1972let operand_size = size.operand_size();1973let rd = pretty_print_vreg_scalar(rd.to_reg(), size);1974let rn = pretty_print_ireg(rn, operand_size);1975format!("fmov {rd}, {rn}")1976}1977&Inst::FpuMoveFPImm { rd, imm, size } => {1978let imm = imm.pretty_print(0);1979let rd = pretty_print_vreg_scalar(rd.to_reg(), size);19801981format!("fmov {rd}, {imm}")1982}1983&Inst::MovToVec {1984rd,1985ri,1986rn,1987idx,1988size,1989} => {1990let rd = pretty_print_vreg_element(rd.to_reg(), idx as usize, size.lane_size());1991let ri = pretty_print_vreg_element(ri, idx as usize, size.lane_size());1992let rn = pretty_print_ireg(rn, size.operand_size());1993format!("mov {rd}, {ri}, {rn}")1994}1995&Inst::MovFromVec { rd, rn, idx, size } => {1996let op = match size {1997ScalarSize::Size8 => "umov",1998ScalarSize::Size16 => "umov",1999ScalarSize::Size32 => "mov",2000ScalarSize::Size64 => "mov",2001_ => unimplemented!(),2002};2003let rd = pretty_print_ireg(rd.to_reg(), size.operand_size());2004let rn = pretty_print_vreg_element(rn, idx as usize, size);2005format!("{op} {rd}, {rn}")2006}2007&Inst::MovFromVecSigned {2008rd,2009rn,2010idx,2011size,2012scalar_size,2013} => {2014let rd = pretty_print_ireg(rd.to_reg(), scalar_size);2015let rn = pretty_print_vreg_element(rn, idx as usize, size.lane_size());2016format!("smov {rd}, {rn}")2017}2018&Inst::VecDup { rd, rn, size } => {2019let rd = pretty_print_vreg_vector(rd.to_reg(), size);2020let rn = pretty_print_ireg(rn, size.operand_size());2021format!("dup {rd}, {rn}")2022}2023&Inst::VecDupFromFpu { rd, rn, size, lane } => {2024let rd = pretty_print_vreg_vector(rd.to_reg(), size);2025let rn = pretty_print_vreg_element(rn, lane.into(), size.lane_size());2026format!("dup {rd}, {rn}")2027}2028&Inst::VecDupFPImm { rd, imm, size } => {2029let imm = imm.pretty_print(0);2030let rd = pretty_print_vreg_vector(rd.to_reg(), size);20312032format!("fmov {rd}, {imm}")2033}2034&Inst::VecDupImm {2035rd,2036imm,2037invert,2038size,2039} => {2040let imm = imm.pretty_print(0);2041let op = if invert { "mvni" } else { "movi" };2042let rd = pretty_print_vreg_vector(rd.to_reg(), size);20432044format!("{op} {rd}, {imm}")2045}2046&Inst::VecExtend {2047t,2048rd,2049rn,2050high_half,2051lane_size,2052} => {2053let vec64 = VectorSize::from_lane_size(lane_size.narrow(), false);2054let vec128 = VectorSize::from_lane_size(lane_size.narrow(), true);2055let rd_size = VectorSize::from_lane_size(lane_size, true);2056let (op, rn_size) = match (t, high_half) {2057(VecExtendOp::Sxtl, false) => ("sxtl", vec64),2058(VecExtendOp::Sxtl, true) => ("sxtl2", vec128),2059(VecExtendOp::Uxtl, false) => ("uxtl", vec64),2060(VecExtendOp::Uxtl, true) => ("uxtl2", vec128),2061};2062let rd = pretty_print_vreg_vector(rd.to_reg(), rd_size);2063let rn = pretty_print_vreg_vector(rn, rn_size);2064format!("{op} {rd}, {rn}")2065}2066&Inst::VecMovElement {2067rd,2068ri,2069rn,2070dest_idx,2071src_idx,2072size,2073} => {2074let rd =2075pretty_print_vreg_element(rd.to_reg(), dest_idx as usize, size.lane_size());2076let ri = pretty_print_vreg_element(ri, dest_idx as usize, size.lane_size());2077let rn = pretty_print_vreg_element(rn, src_idx as usize, size.lane_size());2078format!("mov {rd}, {ri}, {rn}")2079}2080&Inst::VecRRLong {2081op,2082rd,2083rn,2084high_half,2085} => {2086let (op, rd_size, size, suffix) = match (op, high_half) {2087(VecRRLongOp::Fcvtl16, false) => {2088("fcvtl", VectorSize::Size32x4, VectorSize::Size16x4, "")2089}2090(VecRRLongOp::Fcvtl16, true) => {2091("fcvtl2", VectorSize::Size32x4, VectorSize::Size16x8, "")2092}2093(VecRRLongOp::Fcvtl32, false) => {2094("fcvtl", VectorSize::Size64x2, VectorSize::Size32x2, "")2095}2096(VecRRLongOp::Fcvtl32, true) => {2097("fcvtl2", VectorSize::Size64x2, VectorSize::Size32x4, "")2098}2099(VecRRLongOp::Shll8, false) => {2100("shll", VectorSize::Size16x8, VectorSize::Size8x8, ", #8")2101}2102(VecRRLongOp::Shll8, true) => {2103("shll2", VectorSize::Size16x8, VectorSize::Size8x16, ", #8")2104}2105(VecRRLongOp::Shll16, false) => {2106("shll", VectorSize::Size32x4, VectorSize::Size16x4, ", #16")2107}2108(VecRRLongOp::Shll16, true) => {2109("shll2", VectorSize::Size32x4, VectorSize::Size16x8, ", #16")2110}2111(VecRRLongOp::Shll32, false) => {2112("shll", VectorSize::Size64x2, VectorSize::Size32x2, ", #32")2113}2114(VecRRLongOp::Shll32, true) => {2115("shll2", VectorSize::Size64x2, VectorSize::Size32x4, ", #32")2116}2117};2118let rd = pretty_print_vreg_vector(rd.to_reg(), rd_size);2119let rn = pretty_print_vreg_vector(rn, size);21202121format!("{op} {rd}, {rn}{suffix}")2122}2123&Inst::VecRRNarrowLow {2124op,2125rd,2126rn,2127lane_size,2128..2129}2130| &Inst::VecRRNarrowHigh {2131op,2132rd,2133rn,2134lane_size,2135..2136} => {2137let vec64 = VectorSize::from_lane_size(lane_size, false);2138let vec128 = VectorSize::from_lane_size(lane_size, true);2139let rn_size = VectorSize::from_lane_size(lane_size.widen(), true);2140let high_half = match self {2141&Inst::VecRRNarrowLow { .. } => false,2142&Inst::VecRRNarrowHigh { .. } => true,2143_ => unreachable!(),2144};2145let (op, rd_size) = match (op, high_half) {2146(VecRRNarrowOp::Xtn, false) => ("xtn", vec64),2147(VecRRNarrowOp::Xtn, true) => ("xtn2", vec128),2148(VecRRNarrowOp::Sqxtn, false) => ("sqxtn", vec64),2149(VecRRNarrowOp::Sqxtn, true) => ("sqxtn2", vec128),2150(VecRRNarrowOp::Sqxtun, false) => ("sqxtun", vec64),2151(VecRRNarrowOp::Sqxtun, true) => ("sqxtun2", vec128),2152(VecRRNarrowOp::Uqxtn, false) => ("uqxtn", vec64),2153(VecRRNarrowOp::Uqxtn, true) => ("uqxtn2", vec128),2154(VecRRNarrowOp::Fcvtn, false) => ("fcvtn", vec64),2155(VecRRNarrowOp::Fcvtn, true) => ("fcvtn2", vec128),2156};2157let rn = pretty_print_vreg_vector(rn, rn_size);2158let rd = pretty_print_vreg_vector(rd.to_reg(), rd_size);2159let ri = match self {2160&Inst::VecRRNarrowLow { .. } => "".to_string(),2161&Inst::VecRRNarrowHigh { ri, .. } => {2162format!("{}, ", pretty_print_vreg_vector(ri, rd_size))2163}2164_ => unreachable!(),2165};21662167format!("{op} {rd}, {ri}{rn}")2168}2169&Inst::VecRRPair { op, rd, rn } => {2170let op = match op {2171VecPairOp::Addp => "addp",2172};2173let rd = pretty_print_vreg_scalar(rd.to_reg(), ScalarSize::Size64);2174let rn = pretty_print_vreg_vector(rn, VectorSize::Size64x2);21752176format!("{op} {rd}, {rn}")2177}2178&Inst::VecRRPairLong { op, rd, rn } => {2179let (op, dest, src) = match op {2180VecRRPairLongOp::Saddlp8 => {2181("saddlp", VectorSize::Size16x8, VectorSize::Size8x16)2182}2183VecRRPairLongOp::Saddlp16 => {2184("saddlp", VectorSize::Size32x4, VectorSize::Size16x8)2185}2186VecRRPairLongOp::Uaddlp8 => {2187("uaddlp", VectorSize::Size16x8, VectorSize::Size8x16)2188}2189VecRRPairLongOp::Uaddlp16 => {2190("uaddlp", VectorSize::Size32x4, VectorSize::Size16x8)2191}2192};2193let rd = pretty_print_vreg_vector(rd.to_reg(), dest);2194let rn = pretty_print_vreg_vector(rn, src);21952196format!("{op} {rd}, {rn}")2197}2198&Inst::VecRRR {2199rd,2200rn,2201rm,2202alu_op,2203size,2204} => {2205let (op, size) = match alu_op {2206VecALUOp::Sqadd => ("sqadd", size),2207VecALUOp::Uqadd => ("uqadd", size),2208VecALUOp::Sqsub => ("sqsub", size),2209VecALUOp::Uqsub => ("uqsub", size),2210VecALUOp::Cmeq => ("cmeq", size),2211VecALUOp::Cmge => ("cmge", size),2212VecALUOp::Cmgt => ("cmgt", size),2213VecALUOp::Cmhs => ("cmhs", size),2214VecALUOp::Cmhi => ("cmhi", size),2215VecALUOp::Fcmeq => ("fcmeq", size),2216VecALUOp::Fcmgt => ("fcmgt", size),2217VecALUOp::Fcmge => ("fcmge", size),2218VecALUOp::And => ("and", VectorSize::Size8x16),2219VecALUOp::Bic => ("bic", VectorSize::Size8x16),2220VecALUOp::Orr => ("orr", VectorSize::Size8x16),2221VecALUOp::Eor => ("eor", VectorSize::Size8x16),2222VecALUOp::Umaxp => ("umaxp", size),2223VecALUOp::Add => ("add", size),2224VecALUOp::Sub => ("sub", size),2225VecALUOp::Mul => ("mul", size),2226VecALUOp::Sshl => ("sshl", size),2227VecALUOp::Ushl => ("ushl", size),2228VecALUOp::Umin => ("umin", size),2229VecALUOp::Smin => ("smin", size),2230VecALUOp::Umax => ("umax", size),2231VecALUOp::Smax => ("smax", size),2232VecALUOp::Urhadd => ("urhadd", size),2233VecALUOp::Fadd => ("fadd", size),2234VecALUOp::Fsub => ("fsub", size),2235VecALUOp::Fdiv => ("fdiv", size),2236VecALUOp::Fmax => ("fmax", size),2237VecALUOp::Fmin => ("fmin", size),2238VecALUOp::Fmul => ("fmul", size),2239VecALUOp::Addp => ("addp", size),2240VecALUOp::Zip1 => ("zip1", size),2241VecALUOp::Zip2 => ("zip2", size),2242VecALUOp::Sqrdmulh => ("sqrdmulh", size),2243VecALUOp::Uzp1 => ("uzp1", size),2244VecALUOp::Uzp2 => ("uzp2", size),2245VecALUOp::Trn1 => ("trn1", size),2246VecALUOp::Trn2 => ("trn2", size),2247};2248let rd = pretty_print_vreg_vector(rd.to_reg(), size);2249let rn = pretty_print_vreg_vector(rn, size);2250let rm = pretty_print_vreg_vector(rm, size);2251format!("{op} {rd}, {rn}, {rm}")2252}2253&Inst::VecRRRMod {2254rd,2255ri,2256rn,2257rm,2258alu_op,2259size,2260} => {2261let (op, size) = match alu_op {2262VecALUModOp::Bsl => ("bsl", VectorSize::Size8x16),2263VecALUModOp::Fmla => ("fmla", size),2264VecALUModOp::Fmls => ("fmls", size),2265};2266let rd = pretty_print_vreg_vector(rd.to_reg(), size);2267let ri = pretty_print_vreg_vector(ri, size);2268let rn = pretty_print_vreg_vector(rn, size);2269let rm = pretty_print_vreg_vector(rm, size);2270format!("{op} {rd}, {ri}, {rn}, {rm}")2271}2272&Inst::VecFmlaElem {2273rd,2274ri,2275rn,2276rm,2277alu_op,2278size,2279idx,2280} => {2281let (op, size) = match alu_op {2282VecALUModOp::Fmla => ("fmla", size),2283VecALUModOp::Fmls => ("fmls", size),2284_ => unreachable!(),2285};2286let rd = pretty_print_vreg_vector(rd.to_reg(), size);2287let ri = pretty_print_vreg_vector(ri, size);2288let rn = pretty_print_vreg_vector(rn, size);2289let rm = pretty_print_vreg_element(rm, idx.into(), size.lane_size());2290format!("{op} {rd}, {ri}, {rn}, {rm}")2291}2292&Inst::VecRRRLong {2293rd,2294rn,2295rm,2296alu_op,2297high_half,2298} => {2299let (op, dest_size, src_size) = match (alu_op, high_half) {2300(VecRRRLongOp::Smull8, false) => {2301("smull", VectorSize::Size16x8, VectorSize::Size8x8)2302}2303(VecRRRLongOp::Smull8, true) => {2304("smull2", VectorSize::Size16x8, VectorSize::Size8x16)2305}2306(VecRRRLongOp::Smull16, false) => {2307("smull", VectorSize::Size32x4, VectorSize::Size16x4)2308}2309(VecRRRLongOp::Smull16, true) => {2310("smull2", VectorSize::Size32x4, VectorSize::Size16x8)2311}2312(VecRRRLongOp::Smull32, false) => {2313("smull", VectorSize::Size64x2, VectorSize::Size32x2)2314}2315(VecRRRLongOp::Smull32, true) => {2316("smull2", VectorSize::Size64x2, VectorSize::Size32x4)2317}2318(VecRRRLongOp::Umull8, false) => {2319("umull", VectorSize::Size16x8, VectorSize::Size8x8)2320}2321(VecRRRLongOp::Umull8, true) => {2322("umull2", VectorSize::Size16x8, VectorSize::Size8x16)2323}2324(VecRRRLongOp::Umull16, false) => {2325("umull", VectorSize::Size32x4, VectorSize::Size16x4)2326}2327(VecRRRLongOp::Umull16, true) => {2328("umull2", VectorSize::Size32x4, VectorSize::Size16x8)2329}2330(VecRRRLongOp::Umull32, false) => {2331("umull", VectorSize::Size64x2, VectorSize::Size32x2)2332}2333(VecRRRLongOp::Umull32, true) => {2334("umull2", VectorSize::Size64x2, VectorSize::Size32x4)2335}2336};2337let rd = pretty_print_vreg_vector(rd.to_reg(), dest_size);2338let rn = pretty_print_vreg_vector(rn, src_size);2339let rm = pretty_print_vreg_vector(rm, src_size);2340format!("{op} {rd}, {rn}, {rm}")2341}2342&Inst::VecRRRLongMod {2343rd,2344ri,2345rn,2346rm,2347alu_op,2348high_half,2349} => {2350let (op, dest_size, src_size) = match (alu_op, high_half) {2351(VecRRRLongModOp::Umlal8, false) => {2352("umlal", VectorSize::Size16x8, VectorSize::Size8x8)2353}2354(VecRRRLongModOp::Umlal8, true) => {2355("umlal2", VectorSize::Size16x8, VectorSize::Size8x16)2356}2357(VecRRRLongModOp::Umlal16, false) => {2358("umlal", VectorSize::Size32x4, VectorSize::Size16x4)2359}2360(VecRRRLongModOp::Umlal16, true) => {2361("umlal2", VectorSize::Size32x4, VectorSize::Size16x8)2362}2363(VecRRRLongModOp::Umlal32, false) => {2364("umlal", VectorSize::Size64x2, VectorSize::Size32x2)2365}2366(VecRRRLongModOp::Umlal32, true) => {2367("umlal2", VectorSize::Size64x2, VectorSize::Size32x4)2368}2369};2370let rd = pretty_print_vreg_vector(rd.to_reg(), dest_size);2371let ri = pretty_print_vreg_vector(ri, dest_size);2372let rn = pretty_print_vreg_vector(rn, src_size);2373let rm = pretty_print_vreg_vector(rm, src_size);2374format!("{op} {rd}, {ri}, {rn}, {rm}")2375}2376&Inst::VecMisc { op, rd, rn, size } => {2377let (op, size, suffix) = match op {2378VecMisc2::Not => (2379"mvn",2380if size.is_128bits() {2381VectorSize::Size8x162382} else {2383VectorSize::Size8x82384},2385"",2386),2387VecMisc2::Neg => ("neg", size, ""),2388VecMisc2::Abs => ("abs", size, ""),2389VecMisc2::Fabs => ("fabs", size, ""),2390VecMisc2::Fneg => ("fneg", size, ""),2391VecMisc2::Fsqrt => ("fsqrt", size, ""),2392VecMisc2::Rev16 => ("rev16", size, ""),2393VecMisc2::Rev32 => ("rev32", size, ""),2394VecMisc2::Rev64 => ("rev64", size, ""),2395VecMisc2::Fcvtzs => ("fcvtzs", size, ""),2396VecMisc2::Fcvtzu => ("fcvtzu", size, ""),2397VecMisc2::Scvtf => ("scvtf", size, ""),2398VecMisc2::Ucvtf => ("ucvtf", size, ""),2399VecMisc2::Frintn => ("frintn", size, ""),2400VecMisc2::Frintz => ("frintz", size, ""),2401VecMisc2::Frintm => ("frintm", size, ""),2402VecMisc2::Frintp => ("frintp", size, ""),2403VecMisc2::Cnt => ("cnt", size, ""),2404VecMisc2::Cmeq0 => ("cmeq", size, ", #0"),2405VecMisc2::Cmge0 => ("cmge", size, ", #0"),2406VecMisc2::Cmgt0 => ("cmgt", size, ", #0"),2407VecMisc2::Cmle0 => ("cmle", size, ", #0"),2408VecMisc2::Cmlt0 => ("cmlt", size, ", #0"),2409VecMisc2::Fcmeq0 => ("fcmeq", size, ", #0.0"),2410VecMisc2::Fcmge0 => ("fcmge", size, ", #0.0"),2411VecMisc2::Fcmgt0 => ("fcmgt", size, ", #0.0"),2412VecMisc2::Fcmle0 => ("fcmle", size, ", #0.0"),2413VecMisc2::Fcmlt0 => ("fcmlt", size, ", #0.0"),2414};2415let rd = pretty_print_vreg_vector(rd.to_reg(), size);2416let rn = pretty_print_vreg_vector(rn, size);2417format!("{op} {rd}, {rn}{suffix}")2418}2419&Inst::VecLanes { op, rd, rn, size } => {2420let op = match op {2421VecLanesOp::Uminv => "uminv",2422VecLanesOp::Addv => "addv",2423};2424let rd = pretty_print_vreg_scalar(rd.to_reg(), size.lane_size());2425let rn = pretty_print_vreg_vector(rn, size);2426format!("{op} {rd}, {rn}")2427}2428&Inst::VecShiftImm {2429op,2430rd,2431rn,2432size,2433imm,2434} => {2435let op = match op {2436VecShiftImmOp::Shl => "shl",2437VecShiftImmOp::Ushr => "ushr",2438VecShiftImmOp::Sshr => "sshr",2439};2440let rd = pretty_print_vreg_vector(rd.to_reg(), size);2441let rn = pretty_print_vreg_vector(rn, size);2442format!("{op} {rd}, {rn}, #{imm}")2443}2444&Inst::VecShiftImmMod {2445op,2446rd,2447ri,2448rn,2449size,2450imm,2451} => {2452let op = match op {2453VecShiftImmModOp::Sli => "sli",2454};2455let rd = pretty_print_vreg_vector(rd.to_reg(), size);2456let ri = pretty_print_vreg_vector(ri, size);2457let rn = pretty_print_vreg_vector(rn, size);2458format!("{op} {rd}, {ri}, {rn}, #{imm}")2459}2460&Inst::VecExtract { rd, rn, rm, imm4 } => {2461let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2462let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2463let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2464format!("ext {rd}, {rn}, {rm}, #{imm4}")2465}2466&Inst::VecTbl { rd, rn, rm } => {2467let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2468let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2469let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2470format!("tbl {rd}, {{ {rn} }}, {rm}")2471}2472&Inst::VecTblExt { rd, ri, rn, rm } => {2473let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2474let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2475let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2476let ri = pretty_print_vreg_vector(ri, VectorSize::Size8x16);2477format!("tbx {rd}, {ri}, {{ {rn} }}, {rm}")2478}2479&Inst::VecTbl2 { rd, rn, rn2, rm } => {2480let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2481let rn2 = pretty_print_vreg_vector(rn2, VectorSize::Size8x16);2482let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2483let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2484format!("tbl {rd}, {{ {rn}, {rn2} }}, {rm}")2485}2486&Inst::VecTbl2Ext {2487rd,2488ri,2489rn,2490rn2,2491rm,2492} => {2493let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2494let rn2 = pretty_print_vreg_vector(rn2, VectorSize::Size8x16);2495let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2496let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2497let ri = pretty_print_vreg_vector(ri, VectorSize::Size8x16);2498format!("tbx {rd}, {ri}, {{ {rn}, {rn2} }}, {rm}")2499}2500&Inst::VecLoadReplicate { rd, rn, size, .. } => {2501let rd = pretty_print_vreg_vector(rd.to_reg(), size);2502let rn = pretty_print_reg(rn);25032504format!("ld1r {{ {rd} }}, [{rn}]")2505}2506&Inst::VecCSel { rd, rn, rm, cond } => {2507let rd = pretty_print_vreg_vector(rd.to_reg(), VectorSize::Size8x16);2508let rn = pretty_print_vreg_vector(rn, VectorSize::Size8x16);2509let rm = pretty_print_vreg_vector(rm, VectorSize::Size8x16);2510let cond = cond.pretty_print(0);2511format!("vcsel {rd}, {rn}, {rm}, {cond} (if-then-else diamond)")2512}2513&Inst::MovToNZCV { rn } => {2514let rn = pretty_print_reg(rn);2515format!("msr nzcv, {rn}")2516}2517&Inst::MovFromNZCV { rd } => {2518let rd = pretty_print_reg(rd.to_reg());2519format!("mrs {rd}, nzcv")2520}2521&Inst::Extend {2522rd,2523rn,2524signed: false,2525from_bits: 1,2526..2527} => {2528let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size32);2529let rn = pretty_print_ireg(rn, OperandSize::Size32);2530format!("and {rd}, {rn}, #1")2531}2532&Inst::Extend {2533rd,2534rn,2535signed: false,2536from_bits: 32,2537to_bits: 64,2538} => {2539// The case of a zero extension from 32 to 64 bits, is implemented2540// with a "mov" to a 32-bit (W-reg) dest, because this zeroes2541// the top 32 bits.2542let rd = pretty_print_ireg(rd.to_reg(), OperandSize::Size32);2543let rn = pretty_print_ireg(rn, OperandSize::Size32);2544format!("mov {rd}, {rn}")2545}2546&Inst::Extend {2547rd,2548rn,2549signed,2550from_bits,2551to_bits,2552} => {2553assert!(from_bits <= to_bits);2554let op = match (signed, from_bits) {2555(false, 8) => "uxtb",2556(true, 8) => "sxtb",2557(false, 16) => "uxth",2558(true, 16) => "sxth",2559(true, 32) => "sxtw",2560(true, _) => "sbfx",2561(false, _) => "ubfx",2562};2563if op == "sbfx" || op == "ubfx" {2564let dest_size = OperandSize::from_bits(to_bits);2565let rd = pretty_print_ireg(rd.to_reg(), dest_size);2566let rn = pretty_print_ireg(rn, dest_size);2567format!("{op} {rd}, {rn}, #0, #{from_bits}")2568} else {2569let dest_size = if signed {2570OperandSize::from_bits(to_bits)2571} else {2572OperandSize::Size322573};2574let rd = pretty_print_ireg(rd.to_reg(), dest_size);2575let rn = pretty_print_ireg(rn, OperandSize::from_bits(from_bits));2576format!("{op} {rd}, {rn}")2577}2578}2579&Inst::Call { ref info } => {2580let try_call = info2581.try_call_info2582.as_ref()2583.map(|tci| pretty_print_try_call(tci))2584.unwrap_or_default();2585format!("bl 0{try_call}")2586}2587&Inst::CallInd { ref info } => {2588let rn = pretty_print_reg(info.dest);2589let try_call = info2590.try_call_info2591.as_ref()2592.map(|tci| pretty_print_try_call(tci))2593.unwrap_or_default();2594format!("blr {rn}{try_call}")2595}2596&Inst::ReturnCall { ref info } => {2597let mut s = format!(2598"return_call {:?} new_stack_arg_size:{}",2599info.dest, info.new_stack_arg_size2600);2601for ret in &info.uses {2602let preg = pretty_print_reg(ret.preg);2603let vreg = pretty_print_reg(ret.vreg);2604write!(&mut s, " {vreg}={preg}").unwrap();2605}2606s2607}2608&Inst::ReturnCallInd { ref info } => {2609let callee = pretty_print_reg(info.dest);2610let mut s = format!(2611"return_call_ind {callee} new_stack_arg_size:{}",2612info.new_stack_arg_size2613);2614for ret in &info.uses {2615let preg = pretty_print_reg(ret.preg);2616let vreg = pretty_print_reg(ret.vreg);2617write!(&mut s, " {vreg}={preg}").unwrap();2618}2619s2620}2621&Inst::Args { ref args } => {2622let mut s = "args".to_string();2623for arg in args {2624let preg = pretty_print_reg(arg.preg);2625let def = pretty_print_reg(arg.vreg.to_reg());2626write!(&mut s, " {def}={preg}").unwrap();2627}2628s2629}2630&Inst::Rets { ref rets } => {2631let mut s = "rets".to_string();2632for ret in rets {2633let preg = pretty_print_reg(ret.preg);2634let vreg = pretty_print_reg(ret.vreg);2635write!(&mut s, " {vreg}={preg}").unwrap();2636}2637s2638}2639&Inst::Ret {} => "ret".to_string(),2640&Inst::AuthenticatedRet { key, is_hint } => {2641let key = match key {2642APIKey::AZ => "az",2643APIKey::BZ => "bz",2644APIKey::ASP => "asp",2645APIKey::BSP => "bsp",2646};2647match is_hint {2648false => format!("reta{key}"),2649true => format!("auti{key} ; ret"),2650}2651}2652&Inst::Jump { ref dest } => {2653let dest = dest.pretty_print(0);2654format!("b {dest}")2655}2656&Inst::CondBr {2657ref taken,2658ref not_taken,2659ref kind,2660} => {2661let taken = taken.pretty_print(0);2662let not_taken = not_taken.pretty_print(0);2663match kind {2664&CondBrKind::Zero(reg, size) => {2665let reg = pretty_print_reg_sized(reg, size);2666format!("cbz {reg}, {taken} ; b {not_taken}")2667}2668&CondBrKind::NotZero(reg, size) => {2669let reg = pretty_print_reg_sized(reg, size);2670format!("cbnz {reg}, {taken} ; b {not_taken}")2671}2672&CondBrKind::Cond(c) => {2673let c = c.pretty_print(0);2674format!("b.{c} {taken} ; b {not_taken}")2675}2676}2677}2678&Inst::TestBitAndBranch {2679kind,2680ref taken,2681ref not_taken,2682rn,2683bit,2684} => {2685let cond = match kind {2686TestBitAndBranchKind::Z => "z",2687TestBitAndBranchKind::NZ => "nz",2688};2689let taken = taken.pretty_print(0);2690let not_taken = not_taken.pretty_print(0);2691let rn = pretty_print_reg(rn);2692format!("tb{cond} {rn}, #{bit}, {taken} ; b {not_taken}")2693}2694&Inst::IndirectBr { rn, .. } => {2695let rn = pretty_print_reg(rn);2696format!("br {rn}")2697}2698&Inst::Brk => "brk #0xf000".to_string(),2699&Inst::Udf { .. } => "udf #0xc11f".to_string(),2700&Inst::TrapIf {2701ref kind,2702trap_code,2703} => match kind {2704&CondBrKind::Zero(reg, size) => {2705let reg = pretty_print_reg_sized(reg, size);2706format!("cbz {reg}, #trap={trap_code}")2707}2708&CondBrKind::NotZero(reg, size) => {2709let reg = pretty_print_reg_sized(reg, size);2710format!("cbnz {reg}, #trap={trap_code}")2711}2712&CondBrKind::Cond(c) => {2713let c = c.pretty_print(0);2714format!("b.{c} #trap={trap_code}")2715}2716},2717&Inst::Adr { rd, off } => {2718let rd = pretty_print_reg(rd.to_reg());2719format!("adr {rd}, pc+{off}")2720}2721&Inst::Adrp { rd, off } => {2722let rd = pretty_print_reg(rd.to_reg());2723// This instruction addresses 4KiB pages, so multiply it by the page size.2724let byte_offset = off * 4096;2725format!("adrp {rd}, pc+{byte_offset}")2726}2727&Inst::Word4 { data } => format!("data.i32 {data}"),2728&Inst::Word8 { data } => format!("data.i64 {data}"),2729&Inst::JTSequence {2730default,2731ref targets,2732ridx,2733rtmp1,2734rtmp2,2735..2736} => {2737let ridx = pretty_print_reg(ridx);2738let rtmp1 = pretty_print_reg(rtmp1.to_reg());2739let rtmp2 = pretty_print_reg(rtmp2.to_reg());2740let default_target = BranchTarget::Label(default).pretty_print(0);2741format!(2742concat!(2743"b.hs {} ; ",2744"csel {}, xzr, {}, hs ; ",2745"csdb ; ",2746"adr {}, pc+16 ; ",2747"ldrsw {}, [{}, {}, uxtw #2] ; ",2748"add {}, {}, {} ; ",2749"br {} ; ",2750"jt_entries {:?}"2751),2752default_target,2753rtmp2,2754ridx,2755rtmp1,2756rtmp2,2757rtmp1,2758rtmp2,2759rtmp1,2760rtmp1,2761rtmp2,2762rtmp1,2763targets2764)2765}2766&Inst::LoadExtNameGot { rd, ref name } => {2767let rd = pretty_print_reg(rd.to_reg());2768format!("load_ext_name_got {rd}, {name:?}")2769}2770&Inst::LoadExtNameNear {2771rd,2772ref name,2773offset,2774} => {2775let rd = pretty_print_reg(rd.to_reg());2776format!("load_ext_name_near {rd}, {name:?}+{offset}")2777}2778&Inst::LoadExtNameFar {2779rd,2780ref name,2781offset,2782} => {2783let rd = pretty_print_reg(rd.to_reg());2784format!("load_ext_name_far {rd}, {name:?}+{offset}")2785}2786&Inst::LoadAddr { rd, ref mem } => {2787// TODO: we really should find a better way to avoid duplication of2788// this logic between `emit()` and `show_rru()` -- a separate 1-to-N2789// expansion stage (i.e., legalization, but without the slow edit-in-place2790// of the existing legalization framework).2791let mem = mem.clone();2792let (mem_insts, mem) = mem_finalize(None, &mem, I8, state);2793let mut ret = String::new();2794for inst in mem_insts.into_iter() {2795ret.push_str(&inst.print_with_state(&mut EmitState::default()));2796}2797let (reg, index_reg, offset) = match mem {2798AMode::RegExtended { rn, rm, extendop } => (rn, Some((rm, extendop)), 0),2799AMode::Unscaled { rn, simm9 } => (rn, None, simm9.value()),2800AMode::UnsignedOffset { rn, uimm12 } => (rn, None, uimm12.value() as i32),2801_ => panic!("Unsupported case for LoadAddr: {mem:?}"),2802};2803let abs_offset = if offset < 0 {2804-offset as u642805} else {2806offset as u642807};2808let alu_op = if offset < 0 { ALUOp::Sub } else { ALUOp::Add };28092810if let Some((idx, extendop)) = index_reg {2811let add = Inst::AluRRRExtend {2812alu_op: ALUOp::Add,2813size: OperandSize::Size64,2814rd,2815rn: reg,2816rm: idx,2817extendop,2818};28192820ret.push_str(&add.print_with_state(&mut EmitState::default()));2821} else if offset == 0 {2822let mov = Inst::gen_move(rd, reg, I64);2823ret.push_str(&mov.print_with_state(&mut EmitState::default()));2824} else if let Some(imm12) = Imm12::maybe_from_u64(abs_offset) {2825let add = Inst::AluRRImm12 {2826alu_op,2827size: OperandSize::Size64,2828rd,2829rn: reg,2830imm12,2831};2832ret.push_str(&add.print_with_state(&mut EmitState::default()));2833} else {2834let tmp = writable_spilltmp_reg();2835for inst in Inst::load_constant(tmp, abs_offset).into_iter() {2836ret.push_str(&inst.print_with_state(&mut EmitState::default()));2837}2838let add = Inst::AluRRR {2839alu_op,2840size: OperandSize::Size64,2841rd,2842rn: reg,2843rm: tmp.to_reg(),2844};2845ret.push_str(&add.print_with_state(&mut EmitState::default()));2846}2847ret2848}2849&Inst::Paci { key } => {2850let key = match key {2851APIKey::AZ => "az",2852APIKey::BZ => "bz",2853APIKey::ASP => "asp",2854APIKey::BSP => "bsp",2855};28562857"paci".to_string() + key2858}2859&Inst::Xpaclri => "xpaclri".to_string(),2860&Inst::Bti { targets } => {2861let targets = match targets {2862BranchTargetType::None => "",2863BranchTargetType::C => " c",2864BranchTargetType::J => " j",2865BranchTargetType::JC => " jc",2866};28672868"bti".to_string() + targets2869}2870&Inst::EmitIsland { needed_space } => format!("emit_island {needed_space}"),28712872&Inst::ElfTlsGetAddr {2873ref symbol,2874rd,2875tmp,2876} => {2877let rd = pretty_print_reg(rd.to_reg());2878let tmp = pretty_print_reg(tmp.to_reg());2879format!("elf_tls_get_addr {}, {}, {}", rd, tmp, symbol.display(None))2880}2881&Inst::MachOTlsGetAddr { ref symbol, rd } => {2882let rd = pretty_print_reg(rd.to_reg());2883format!("macho_tls_get_addr {}, {}", rd, symbol.display(None))2884}2885&Inst::Unwind { ref inst } => {2886format!("unwind {inst:?}")2887}2888&Inst::DummyUse { reg } => {2889let reg = pretty_print_reg(reg);2890format!("dummy_use {reg}")2891}2892&Inst::LabelAddress { dst, label } => {2893let dst = pretty_print_reg(dst.to_reg());2894format!("label_address {dst}, {label:?}")2895}2896&Inst::StackProbeLoop { start, end, step } => {2897let start = pretty_print_reg(start.to_reg());2898let end = pretty_print_reg(end);2899let step = step.pretty_print(0);2900format!("stack_probe_loop {start}, {end}, {step}")2901}2902}2903}2904}29052906//=============================================================================2907// Label fixups and jump veneers.29082909/// Different forms of label references for different instruction formats.2910#[derive(Clone, Copy, Debug, PartialEq, Eq)]2911pub enum LabelUse {2912/// 14-bit branch offset (conditional branches). PC-rel, offset is imm <<2913/// 2. Immediate is 14 signed bits, in bits 18:5. Used by tbz and tbnz.2914Branch14,2915/// 19-bit branch offset (conditional branches). PC-rel, offset is imm << 2. Immediate is 192916/// signed bits, in bits 23:5. Used by cbz, cbnz, b.cond.2917Branch19,2918/// 26-bit branch offset (unconditional branches). PC-rel, offset is imm << 2. Immediate is 262919/// signed bits, in bits 25:0. Used by b, bl.2920Branch26,2921/// 19-bit offset for LDR (load literal). PC-rel, offset is imm << 2. Immediate is 19 signed bits,2922/// in bits 23:5.2923Ldr19,2924/// 21-bit offset for ADR (get address of label). PC-rel, offset is not shifted. Immediate is2925/// 21 signed bits, with high 19 bits in bits 23:5 and low 2 bits in bits 30:29.2926Adr21,2927/// 32-bit PC relative constant offset (from address of constant itself),2928/// signed. Used in jump tables.2929PCRel32,2930}29312932impl MachInstLabelUse for LabelUse {2933/// Alignment for veneer code. Every AArch64 instruction must be 4-byte-aligned.2934const ALIGN: CodeOffset = 4;29352936/// Maximum PC-relative range (positive), inclusive.2937fn max_pos_range(self) -> CodeOffset {2938match self {2939// N-bit immediate, left-shifted by 2, for (N+2) bits of total2940// range. Signed, so +2^(N+1) from zero. Likewise for two other2941// shifted cases below.2942LabelUse::Branch14 => (1 << 15) - 1,2943LabelUse::Branch19 => (1 << 20) - 1,2944LabelUse::Branch26 => (1 << 27) - 1,2945LabelUse::Ldr19 => (1 << 20) - 1,2946// Adr does not shift its immediate, so the 21-bit immediate gives 21 bits of total2947// range.2948LabelUse::Adr21 => (1 << 20) - 1,2949LabelUse::PCRel32 => 0x7fffffff,2950}2951}29522953/// Maximum PC-relative range (negative).2954fn max_neg_range(self) -> CodeOffset {2955// All forms are twos-complement signed offsets, so negative limit is one more than2956// positive limit.2957self.max_pos_range() + 12958}29592960/// Size of window into code needed to do the patch.2961fn patch_size(self) -> CodeOffset {2962// Patch is on one instruction only for all of these label reference types.296342964}29652966/// Perform the patch.2967fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {2968let pc_rel = (label_offset as i64) - (use_offset as i64);2969debug_assert!(pc_rel <= self.max_pos_range() as i64);2970debug_assert!(pc_rel >= -(self.max_neg_range() as i64));2971let pc_rel = pc_rel as u32;2972let insn_word = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);2973let mask = match self {2974LabelUse::Branch14 => 0x0007ffe0, // bits 18..5 inclusive2975LabelUse::Branch19 => 0x00ffffe0, // bits 23..5 inclusive2976LabelUse::Branch26 => 0x03ffffff, // bits 25..0 inclusive2977LabelUse::Ldr19 => 0x00ffffe0, // bits 23..5 inclusive2978LabelUse::Adr21 => 0x60ffffe0, // bits 30..29, 25..5 inclusive2979LabelUse::PCRel32 => 0xffffffff,2980};2981let pc_rel_shifted = match self {2982LabelUse::Adr21 | LabelUse::PCRel32 => pc_rel,2983_ => {2984debug_assert!(pc_rel & 3 == 0);2985pc_rel >> 22986}2987};2988let pc_rel_inserted = match self {2989LabelUse::Branch14 => (pc_rel_shifted & 0x3fff) << 5,2990LabelUse::Branch19 | LabelUse::Ldr19 => (pc_rel_shifted & 0x7ffff) << 5,2991LabelUse::Branch26 => pc_rel_shifted & 0x3ffffff,2992// Note: the *low* two bits of offset are put in the2993// *high* bits (30, 29).2994LabelUse::Adr21 => (pc_rel_shifted & 0x1ffffc) << 3 | (pc_rel_shifted & 3) << 29,2995LabelUse::PCRel32 => pc_rel_shifted,2996};2997let is_add = match self {2998LabelUse::PCRel32 => true,2999_ => false,3000};3001let insn_word = if is_add {3002insn_word.wrapping_add(pc_rel_inserted)3003} else {3004(insn_word & !mask) | pc_rel_inserted3005};3006buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn_word));3007}30083009/// Is a veneer supported for this label reference type?3010fn supports_veneer(self) -> bool {3011match self {3012LabelUse::Branch14 | LabelUse::Branch19 => true, // veneer is a Branch263013LabelUse::Branch26 => true, // veneer is a PCRel323014_ => false,3015}3016}30173018/// How large is the veneer, if supported?3019fn veneer_size(self) -> CodeOffset {3020match self {3021LabelUse::Branch14 | LabelUse::Branch19 => 4,3022LabelUse::Branch26 => 20,3023_ => unreachable!(),3024}3025}30263027fn worst_case_veneer_size() -> CodeOffset {3028203029}30303031/// Generate a veneer into the buffer, given that this veneer is at `veneer_offset`, and return3032/// an offset and label-use for the veneer's use of the original label.3033fn generate_veneer(3034self,3035buffer: &mut [u8],3036veneer_offset: CodeOffset,3037) -> (CodeOffset, LabelUse) {3038match self {3039LabelUse::Branch14 | LabelUse::Branch19 => {3040// veneer is a Branch26 (unconditional branch). Just encode directly here -- don't3041// bother with constructing an Inst.3042let insn_word = 0b000101 << 26;3043buffer[0..4].clone_from_slice(&u32::to_le_bytes(insn_word));3044(veneer_offset, LabelUse::Branch26)3045}30463047// This is promoting a 26-bit call/jump to a 32-bit call/jump to3048// get a further range. This jump translates to a jump to a3049// relative location based on the address of the constant loaded3050// from here.3051//3052// If this path is taken from a call instruction then caller-saved3053// registers are available (minus arguments), so x16/x17 are3054// available. Otherwise for intra-function jumps we also reserve3055// x16/x17 as spill-style registers. In both cases these are3056// available for us to use.3057LabelUse::Branch26 => {3058let tmp1 = regs::spilltmp_reg();3059let tmp1_w = regs::writable_spilltmp_reg();3060let tmp2 = regs::tmp2_reg();3061let tmp2_w = regs::writable_tmp2_reg();3062// ldrsw x16, 163063let ldr = emit::enc_ldst_imm19(0b1001_1000, 16 / 4, tmp1);3064// adr x17, 123065let adr = emit::enc_adr(12, tmp2_w);3066// add x16, x16, x173067let add = emit::enc_arith_rrr(0b10001011_000, 0, tmp1_w, tmp1, tmp2);3068// br x163069let br = emit::enc_br(tmp1);3070buffer[0..4].clone_from_slice(&u32::to_le_bytes(ldr));3071buffer[4..8].clone_from_slice(&u32::to_le_bytes(adr));3072buffer[8..12].clone_from_slice(&u32::to_le_bytes(add));3073buffer[12..16].clone_from_slice(&u32::to_le_bytes(br));3074// the 4-byte signed immediate we'll load is after these3075// instructions, 16-bytes in.3076(veneer_offset + 16, LabelUse::PCRel32)3077}30783079_ => panic!("Unsupported label-reference type for veneer generation!"),3080}3081}30823083fn from_reloc(reloc: Reloc, addend: Addend) -> Option<LabelUse> {3084match (reloc, addend) {3085(Reloc::Arm64Call, 0) => Some(LabelUse::Branch26),3086_ => None,3087}3088}3089}30903091#[cfg(test)]3092mod tests {3093use super::*;30943095#[test]3096fn inst_size_test() {3097// This test will help with unintentionally growing the size3098// of the Inst enum.3099let expected = if cfg!(target_pointer_width = "32") && !cfg!(target_arch = "arm") {3100283101} else {3102323103};3104assert_eq!(expected, std::mem::size_of::<Inst>());3105}3106}310731083109