Path: blob/main/cranelift/codegen/src/isa/x64/inst/args.rs
3073 views
//! Instruction operand sub-components (aka "parts"): definitions and printing.12use super::regs::{self};3use crate::ir::MemFlags;4use crate::ir::condcodes::{FloatCC, IntCC};5use crate::ir::types::*;6use crate::isa::x64::inst::Inst;7use crate::isa::x64::inst::regs::pretty_print_reg;8use crate::machinst::*;9use alloc::string::String;10use core::fmt;1112/// An extension trait for converting `Writable{Xmm,Gpr}` to `Writable<Reg>`.13pub trait ToWritableReg {14/// Convert `Writable{Xmm,Gpr}` to `Writable<Reg>`.15fn to_writable_reg(&self) -> Writable<Reg>;16}1718/// An extension trait for converting `Writable<Reg>` to `Writable{Xmm,Gpr}`.19pub trait FromWritableReg: Sized {20/// Convert `Writable<Reg>` to `Writable{Xmm,Gpr}`.21fn from_writable_reg(w: Writable<Reg>) -> Option<Self>;22}2324/// A macro for defining a newtype of `Reg` that enforces some invariant about25/// the wrapped `Reg` (such as that it is of a particular register class).26macro_rules! newtype_of_reg {27(28$newtype_reg:ident,29$newtype_writable_reg:ident,30$newtype_option_writable_reg:ident,31reg_mem: ($($newtype_reg_mem:ident $(aligned:$aligned:ident)?),*),32reg_mem_imm: ($($newtype_reg_mem_imm:ident $(aligned:$aligned_imm:ident)?),*),33|$check_reg:ident| $check:expr34) => {35/// A newtype wrapper around `Reg`.36#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]37pub struct $newtype_reg(Reg);3839impl PartialEq<Reg> for $newtype_reg {40fn eq(&self, other: &Reg) -> bool {41self.0 == *other42}43}4445impl From<$newtype_reg> for Reg {46fn from(r: $newtype_reg) -> Self {47r.048}49}5051impl $newtype_reg {52/// Create this newtype from the given register, or return `None` if the register53/// is not a valid instance of this newtype.54pub fn new($check_reg: Reg) -> Option<Self> {55if $check {56Some(Self($check_reg))57} else {58None59}60}6162/// Like `Self::new(r).unwrap()` but with a better panic message on63/// failure.64pub fn unwrap_new($check_reg: Reg) -> Self {65if $check {66Self($check_reg)67} else {68panic!(69"cannot construct {} from register {:?} with register class {:?}",70stringify!($newtype_reg),71$check_reg,72$check_reg.class(),73)74}75}7677/// Get this newtype's underlying `Reg`.78pub fn to_reg(self) -> Reg {79self.080}81}8283// Convenience impl so that people working with this newtype can use it84// "just like" a plain `Reg`.85//86// NB: We cannot implement `DerefMut` because that would let people do87// nasty stuff like `*my_gpr.deref_mut() = some_xmm_reg`, breaking the88// invariants that `Gpr` provides.89impl core::ops::Deref for $newtype_reg {90type Target = Reg;9192fn deref(&self) -> &Reg {93&self.094}95}9697/// If you know what you're doing, you can explicitly mutably borrow the98/// underlying `Reg`. Don't make it point to the wrong type of register99/// please.100impl AsMut<Reg> for $newtype_reg {101fn as_mut(&mut self) -> &mut Reg {102&mut self.0103}104}105106/// Writable Gpr.107pub type $newtype_writable_reg = Writable<$newtype_reg>;108109#[allow(dead_code, reason = "Used by some newtypes and not others")]110/// Optional writable Gpr.111pub type $newtype_option_writable_reg = Option<Writable<$newtype_reg>>;112113impl ToWritableReg for $newtype_writable_reg {114fn to_writable_reg(&self) -> Writable<Reg> {115Writable::from_reg(self.to_reg().to_reg())116}117}118119impl FromWritableReg for $newtype_writable_reg {120fn from_writable_reg(w: Writable<Reg>) -> Option<Self> {121Some(Writable::from_reg($newtype_reg::new(w.to_reg())?))122}123}124125$(126/// A newtype wrapper around `RegMem` for general-purpose registers.127#[derive(Clone, Debug)]128pub struct $newtype_reg_mem(RegMem);129130impl From<$newtype_reg_mem> for RegMem {131fn from(rm: $newtype_reg_mem) -> Self {132rm.0133}134}135impl<'a> From<&'a $newtype_reg_mem> for &'a RegMem {136fn from(rm: &'a $newtype_reg_mem) -> &'a RegMem {137&rm.0138}139}140141impl From<$newtype_reg> for $newtype_reg_mem {142fn from(r: $newtype_reg) -> Self {143$newtype_reg_mem(RegMem::reg(r.into()))144}145}146147impl $newtype_reg_mem {148/// Construct a `RegMem` newtype from the given `RegMem`, or return149/// `None` if the `RegMem` is not a valid instance of this `RegMem`150/// newtype.151pub fn new(rm: RegMem) -> Option<Self> {152match rm {153RegMem::Mem { addr } => {154let mut _allow = true;155$(156if $aligned {157_allow = addr.aligned();158}159)?160if _allow {161Some(Self(RegMem::Mem { addr }))162} else {163None164}165}166RegMem::Reg { reg } => Some($newtype_reg::new(reg)?.into()),167}168}169170/// Like `Self::new(rm).unwrap()` but with better panic messages171/// in case of failure.172pub fn unwrap_new(rm: RegMem) -> Self {173match rm {174RegMem::Mem { addr } => {175$(176if $aligned && !addr.aligned() {177panic!(178"cannot create {} from an unaligned memory address: {addr:?}",179stringify!($newtype_reg_mem),180);181}182)?183Self(RegMem::Mem { addr })184}185RegMem::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),186}187}188189/// Convert this newtype into its underlying `RegMem`.190pub fn to_reg_mem(self) -> RegMem {191self.0192}193194#[allow(dead_code, reason = "Used by some newtypes and not others")]195pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {196self.0.get_operands(collector);197}198}199impl PrettyPrint for $newtype_reg_mem {200fn pretty_print(&self, size: u8) -> String {201self.0.pretty_print(size)202}203}204)*205206$(207/// A newtype wrapper around `RegMemImm`.208#[derive(Clone, Debug)]209pub struct $newtype_reg_mem_imm(RegMemImm);210211impl From<$newtype_reg_mem_imm> for RegMemImm {212fn from(rmi: $newtype_reg_mem_imm) -> RegMemImm {213rmi.0214}215}216impl<'a> From<&'a $newtype_reg_mem_imm> for &'a RegMemImm {217fn from(rmi: &'a $newtype_reg_mem_imm) -> &'a RegMemImm {218&rmi.0219}220}221222impl From<$newtype_reg> for $newtype_reg_mem_imm {223fn from(r: $newtype_reg) -> Self {224$newtype_reg_mem_imm(RegMemImm::reg(r.into()))225}226}227228impl $newtype_reg_mem_imm {229/// Construct this newtype from the given `RegMemImm`, or return230/// `None` if the `RegMemImm` is not a valid instance of this231/// newtype.232pub fn new(rmi: RegMemImm) -> Option<Self> {233match rmi {234RegMemImm::Imm { .. } => Some(Self(rmi)),235RegMemImm::Mem { addr } => {236let mut _allow = true;237$(238if $aligned_imm {239_allow = addr.aligned();240}241)?242if _allow {243Some(Self(RegMemImm::Mem { addr }))244} else {245None246}247}248RegMemImm::Reg { reg } => Some($newtype_reg::new(reg)?.into()),249}250}251252/// Like `Self::new(rmi).unwrap()` but with better panic253/// messages in case of failure.254pub fn unwrap_new(rmi: RegMemImm) -> Self {255match rmi {256RegMemImm::Imm { .. } => Self(rmi),257RegMemImm::Mem { addr } => {258$(259if $aligned_imm && !addr.aligned() {260panic!(261"cannot construct {} from unaligned memory address: {:?}",262stringify!($newtype_reg_mem_imm),263addr,264);265}266)?267Self(RegMemImm::Mem { addr })268269}270RegMemImm::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),271}272}273274/// Convert this newtype into its underlying `RegMemImm`.275#[allow(dead_code, reason = "Used by some newtypes and not others")]276pub fn to_reg_mem_imm(self) -> RegMemImm {277self.0278}279280#[allow(dead_code, reason = "Used by some newtypes and not others")]281pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {282self.0.get_operands(collector);283}284}285286impl PrettyPrint for $newtype_reg_mem_imm {287fn pretty_print(&self, size: u8) -> String {288self.0.pretty_print(size)289}290}291)*292};293}294295// Define a newtype of `Reg` for general-purpose registers.296newtype_of_reg!(297Gpr,298WritableGpr,299OptionWritableGpr,300reg_mem: (GprMem),301reg_mem_imm: (GprMemImm),302|reg| reg.class() == RegClass::Int303);304305#[expect(missing_docs, reason = "self-describing fields")]306impl Gpr {307pub const RAX: Gpr = Gpr(regs::rax());308pub const RBX: Gpr = Gpr(regs::rbx());309pub const RCX: Gpr = Gpr(regs::rcx());310pub const RDX: Gpr = Gpr(regs::rdx());311pub const RSI: Gpr = Gpr(regs::rsi());312pub const RDI: Gpr = Gpr(regs::rdi());313pub const RSP: Gpr = Gpr(regs::rsp());314pub const RBP: Gpr = Gpr(regs::rbp());315pub const R8: Gpr = Gpr(regs::r8());316pub const R9: Gpr = Gpr(regs::r9());317pub const R10: Gpr = Gpr(regs::r10());318pub const R11: Gpr = Gpr(regs::r11());319pub const R12: Gpr = Gpr(regs::r12());320pub const R13: Gpr = Gpr(regs::r13());321pub const R14: Gpr = Gpr(regs::r14());322pub const R15: Gpr = Gpr(regs::r15());323}324325// Define a newtype of `Reg` for XMM registers.326newtype_of_reg!(327Xmm,328WritableXmm,329OptionWritableXmm,330reg_mem: (XmmMem, XmmMemAligned aligned:true),331reg_mem_imm: (XmmMemImm, XmmMemAlignedImm aligned:true),332|reg| reg.class() == RegClass::Float333);334335// N.B.: `Amode` is defined in `inst.isle`. We add some convenience336// constructors here.337338// Re-export the type from the ISLE generated code.339pub use crate::isa::x64::lower::isle::generated_code::Amode;340341impl Amode {342/// Create an immediate sign-extended and register addressing mode.343pub fn imm_reg(simm32: i32, base: Reg) -> Self {344debug_assert!(base.class() == RegClass::Int);345Self::ImmReg {346simm32,347base,348flags: MemFlags::trusted(),349}350}351352/// Create a sign-extended-32-to-64 with register and shift addressing mode.353pub fn imm_reg_reg_shift(simm32: i32, base: Gpr, index: Gpr, shift: u8) -> Self {354debug_assert!(base.class() == RegClass::Int);355debug_assert!(index.class() == RegClass::Int);356debug_assert!(shift <= 3);357Self::ImmRegRegShift {358simm32,359base,360index,361shift,362flags: MemFlags::trusted(),363}364}365366pub(crate) fn rip_relative(target: MachLabel) -> Self {367Self::RipRelative { target }368}369370/// Set the specified [MemFlags] to the [Amode].371pub fn with_flags(&self, flags: MemFlags) -> Self {372match self {373&Self::ImmReg { simm32, base, .. } => Self::ImmReg {374simm32,375base,376flags,377},378&Self::ImmRegRegShift {379simm32,380base,381index,382shift,383..384} => Self::ImmRegRegShift {385simm32,386base,387index,388shift,389flags,390},391_ => panic!("Amode {self:?} cannot take memflags"),392}393}394395/// Add the registers mentioned by `self` to `collector`.396pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {397match self {398Amode::ImmReg { base, .. } => {399if *base != regs::rbp() && *base != regs::rsp() {400collector.reg_use(base);401}402}403Amode::ImmRegRegShift { base, index, .. } => {404debug_assert_ne!(base.to_reg(), regs::rbp());405debug_assert_ne!(base.to_reg(), regs::rsp());406collector.reg_use(base);407debug_assert_ne!(index.to_reg(), regs::rbp());408debug_assert_ne!(index.to_reg(), regs::rsp());409collector.reg_use(index);410}411Amode::RipRelative { .. } => {412// RIP isn't involved in regalloc.413}414}415}416417/// Same as `get_operands`, but add the registers in the "late" phase.418pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {419match self {420Amode::ImmReg { base, .. } => {421collector.reg_late_use(base);422}423Amode::ImmRegRegShift { base, index, .. } => {424collector.reg_late_use(base);425collector.reg_late_use(index);426}427Amode::RipRelative { .. } => {428// RIP isn't involved in regalloc.429}430}431}432433pub(crate) fn get_flags(&self) -> MemFlags {434match self {435Amode::ImmReg { flags, .. } | Amode::ImmRegRegShift { flags, .. } => *flags,436Amode::RipRelative { .. } => MemFlags::trusted(),437}438}439440/// Offset the amode by a fixed offset.441pub(crate) fn offset(&self, offset: i32) -> Self {442let mut ret = self.clone();443match &mut ret {444&mut Amode::ImmReg { ref mut simm32, .. } => *simm32 += offset,445&mut Amode::ImmRegRegShift { ref mut simm32, .. } => *simm32 += offset,446_ => panic!("Cannot offset amode: {self:?}"),447}448ret449}450451pub(crate) fn aligned(&self) -> bool {452self.get_flags().aligned()453}454}455456impl PrettyPrint for Amode {457fn pretty_print(&self, _size: u8) -> String {458match self {459Amode::ImmReg { simm32, base, .. } => {460// Note: size is always 8; the address is 64 bits,461// even if the addressed operand is smaller.462format!("{}({})", *simm32, pretty_print_reg(*base, 8))463}464Amode::ImmRegRegShift {465simm32,466base,467index,468shift,469..470} => format!(471"{}({},{},{})",472*simm32,473pretty_print_reg(base.to_reg(), 8),474pretty_print_reg(index.to_reg(), 8),4751 << shift476),477Amode::RipRelative { target } => format!("label{}(%rip)", target.as_u32()),478}479}480}481482/// A Memory Address. These denote a 64-bit value only.483/// Used for usual addressing modes as well as addressing modes used during compilation, when the484/// moving SP offset is not known.485#[derive(Clone, Debug)]486pub enum SyntheticAmode {487/// A real amode.488Real(Amode),489490/// A (virtual) offset into the incoming argument area.491IncomingArg {492/// The downward offset from the start of the incoming argument area.493offset: u32,494},495496/// A (virtual) offset to the slot area of the function frame, which lies just above the497/// outgoing arguments.498SlotOffset {499/// The offset into the slot area.500simm32: i32,501},502503/// A virtual offset to a constant that will be emitted in the constant section of the buffer.504ConstantOffset(VCodeConstant),505}506507impl SyntheticAmode {508/// Create a real addressing mode.509pub fn real(amode: Amode) -> Self {510Self::Real(amode)511}512513pub(crate) fn slot_offset(simm32: i32) -> Self {514SyntheticAmode::SlotOffset { simm32 }515}516517/// Add the registers mentioned by `self` to `collector`.518pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {519match self {520SyntheticAmode::Real(addr) => addr.get_operands(collector),521SyntheticAmode::IncomingArg { .. } => {522// Nothing to do; the base is known and isn't involved in regalloc.523}524SyntheticAmode::SlotOffset { .. } => {525// Nothing to do; the base is SP and isn't involved in regalloc.526}527SyntheticAmode::ConstantOffset(_) => {}528}529}530531/// Same as `get_operands`, but add the register in the "late" phase.532pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {533match self {534SyntheticAmode::Real(addr) => addr.get_operands_late(collector),535SyntheticAmode::IncomingArg { .. } => {536// Nothing to do; the base is known and isn't involved in regalloc.537}538SyntheticAmode::SlotOffset { .. } => {539// Nothing to do; the base is SP and isn't involved in regalloc.540}541SyntheticAmode::ConstantOffset(_) => {}542}543}544545pub(crate) fn finalize(&self, frame: &FrameLayout, buffer: &mut MachBuffer<Inst>) -> Amode {546match self {547SyntheticAmode::Real(addr) => addr.clone(),548SyntheticAmode::IncomingArg { offset } => {549// NOTE: this could be made relative to RSP by adding additional550// offsets from the frame_layout.551let args_max_fp_offset = frame.tail_args_size + frame.setup_area_size;552Amode::imm_reg(553i32::try_from(args_max_fp_offset - offset).unwrap(),554regs::rbp(),555)556}557SyntheticAmode::SlotOffset { simm32 } => {558let off = *simm32 as i64 + i64::from(frame.outgoing_args_size);559Amode::imm_reg(off.try_into().expect("invalid sp offset"), regs::rsp())560}561SyntheticAmode::ConstantOffset(c) => {562Amode::rip_relative(buffer.get_label_for_constant(*c))563}564}565}566567pub(crate) fn aligned(&self) -> bool {568match self {569SyntheticAmode::Real(addr) => addr.aligned(),570&SyntheticAmode::IncomingArg { .. }571| SyntheticAmode::SlotOffset { .. }572| SyntheticAmode::ConstantOffset { .. } => true,573}574}575576/// Offset the synthetic amode by a fixed offset.577pub(crate) fn offset(&self, offset: i32) -> Self {578let mut ret = self.clone();579match &mut ret {580SyntheticAmode::Real(amode) => *amode = amode.offset(offset),581SyntheticAmode::SlotOffset { simm32 } => *simm32 += offset,582// `amode_offset` is used only in i128.load/store which583// takes a synthetic amode from `to_amode`; `to_amode` can584// only produce Real or SlotOffset amodes, never585// IncomingArg or ConstantOffset.586_ => panic!("Cannot offset SyntheticAmode: {self:?}"),587}588ret589}590}591592impl From<Amode> for SyntheticAmode {593fn from(amode: Amode) -> SyntheticAmode {594SyntheticAmode::Real(amode)595}596}597598impl From<VCodeConstant> for SyntheticAmode {599fn from(c: VCodeConstant) -> SyntheticAmode {600SyntheticAmode::ConstantOffset(c)601}602}603604impl PrettyPrint for SyntheticAmode {605fn pretty_print(&self, _size: u8) -> String {606match self {607// See note in `Amode` regarding constant size of `8`.608SyntheticAmode::Real(addr) => addr.pretty_print(8),609&SyntheticAmode::IncomingArg { offset } => {610format!("rbp(stack args max - {offset})")611}612SyntheticAmode::SlotOffset { simm32 } => {613format!("rsp({} + virtual offset)", *simm32)614}615SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),616}617}618}619620/// An operand which is either an integer Register, a value in Memory or an Immediate. This can621/// denote an 8, 16, 32 or 64 bit value. For the Immediate form, in the 8- and 16-bit case, only622/// the lower 8 or 16 bits of `simm32` is relevant. In the 64-bit case, the value denoted by623/// `simm32` is its sign-extension out to 64 bits.624#[derive(Clone, Debug)]625pub enum RegMemImm {626/// A register operand.627Reg {628/// The underlying register.629reg: Reg,630},631/// A memory operand.632Mem {633/// The memory address.634addr: SyntheticAmode,635},636/// An immediate operand.637Imm {638/// The immediate value.639simm32: u32,640},641}642643impl RegMemImm {644/// Create a register operand.645pub fn reg(reg: Reg) -> Self {646debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);647Self::Reg { reg }648}649650/// Create a memory operand.651pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {652Self::Mem { addr: addr.into() }653}654655/// Create an immediate operand.656pub fn imm(simm32: u32) -> Self {657Self::Imm { simm32 }658}659660/// Add the regs mentioned by `self` to `collector`.661pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {662match self {663Self::Reg { reg } => collector.reg_use(reg),664Self::Mem { addr } => addr.get_operands(collector),665Self::Imm { .. } => {}666}667}668}669670impl From<RegMem> for RegMemImm {671fn from(rm: RegMem) -> RegMemImm {672match rm {673RegMem::Reg { reg } => RegMemImm::Reg { reg },674RegMem::Mem { addr } => RegMemImm::Mem { addr },675}676}677}678679impl From<Reg> for RegMemImm {680fn from(reg: Reg) -> Self {681RegMemImm::Reg { reg }682}683}684685impl PrettyPrint for RegMemImm {686fn pretty_print(&self, size: u8) -> String {687match self {688Self::Reg { reg } => pretty_print_reg(*reg, size),689Self::Mem { addr } => addr.pretty_print(size),690Self::Imm { simm32 } => format!("${}", *simm32 as i32),691}692}693}694695/// An operand which is either an integer Register or a value in Memory. This can denote an 8, 16,696/// 32, 64, or 128 bit value.697#[derive(Clone, Debug)]698pub enum RegMem {699/// A register operand.700Reg {701/// The underlying register.702reg: Reg,703},704/// A memory operand.705Mem {706/// The memory address.707addr: SyntheticAmode,708},709}710711impl RegMem {712/// Create a register operand.713pub fn reg(reg: Reg) -> Self {714debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);715Self::Reg { reg }716}717718/// Create a memory operand.719pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {720Self::Mem { addr: addr.into() }721}722/// Asserts that in register mode, the reg class is the one that's expected.723pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {724if let Self::Reg { reg } = self {725debug_assert_eq!(reg.class(), expected_reg_class);726}727}728/// Add the regs mentioned by `self` to `collector`.729pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {730match self {731RegMem::Reg { reg } => collector.reg_use(reg),732RegMem::Mem { addr, .. } => addr.get_operands(collector),733}734}735}736737impl From<Reg> for RegMem {738fn from(reg: Reg) -> RegMem {739RegMem::Reg { reg }740}741}742743impl From<Writable<Reg>> for RegMem {744fn from(r: Writable<Reg>) -> Self {745RegMem::reg(r.to_reg())746}747}748749impl PrettyPrint for RegMem {750fn pretty_print(&self, size: u8) -> String {751match self {752RegMem::Reg { reg } => pretty_print_reg(*reg, size),753RegMem::Mem { addr, .. } => addr.pretty_print(size),754}755}756}757758/// This defines the ways a value can be extended: either signed- or zero-extension, or none for759/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which760/// values can be extended.761#[derive(Clone, PartialEq)]762pub enum ExtKind {763/// No extension.764None,765/// Sign-extend.766SignExtend,767/// Zero-extend.768ZeroExtend,769}770771/// These indicate ways of extending (widening) a value, using the Intel772/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64773#[derive(Clone, PartialEq)]774pub enum ExtMode {775/// Byte -> Longword.776BL,777/// Byte -> Quadword.778BQ,779/// Word -> Longword.780WL,781/// Word -> Quadword.782WQ,783/// Longword -> Quadword.784LQ,785}786787impl ExtMode {788/// Calculate the `ExtMode` from passed bit lengths of the from/to types.789pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {790match (from_bits, to_bits) {791(1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),792(1, 64) | (8, 64) => Some(ExtMode::BQ),793(16, 32) => Some(ExtMode::WL),794(16, 64) => Some(ExtMode::WQ),795(32, 64) => Some(ExtMode::LQ),796_ => None,797}798}799}800801impl fmt::Debug for ExtMode {802fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {803let name = match self {804ExtMode::BL => "bl",805ExtMode::BQ => "bq",806ExtMode::WL => "wl",807ExtMode::WQ => "wq",808ExtMode::LQ => "lq",809};810write!(fmt, "{name}")811}812}813814impl fmt::Display for ExtMode {815fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {816fmt::Debug::fmt(self, f)817}818}819820/// These indicate condition code tests. Not all are represented since not all are useful in821/// compiler-generated code.822#[derive(Copy, Clone, PartialEq, Eq)]823#[repr(u8)]824pub enum CC {825/// overflow826O = 0,827/// no overflow828NO = 1,829830/// < unsigned831B = 2,832/// >= unsigned833NB = 3,834835/// zero836Z = 4,837/// not-zero838NZ = 5,839840/// <= unsigned841BE = 6,842/// > unsigned843NBE = 7,844845/// negative846S = 8,847/// not-negative848NS = 9,849850/// < signed851L = 12,852/// >= signed853NL = 13,854855/// <= signed856LE = 14,857/// > signed858NLE = 15,859860/// parity861P = 10,862863/// not parity864NP = 11,865}866867impl CC {868pub(crate) fn from_intcc(intcc: IntCC) -> Self {869match intcc {870IntCC::Equal => CC::Z,871IntCC::NotEqual => CC::NZ,872IntCC::SignedGreaterThanOrEqual => CC::NL,873IntCC::SignedGreaterThan => CC::NLE,874IntCC::SignedLessThanOrEqual => CC::LE,875IntCC::SignedLessThan => CC::L,876IntCC::UnsignedGreaterThanOrEqual => CC::NB,877IntCC::UnsignedGreaterThan => CC::NBE,878IntCC::UnsignedLessThanOrEqual => CC::BE,879IntCC::UnsignedLessThan => CC::B,880}881}882883pub(crate) fn invert(&self) -> Self {884match self {885CC::O => CC::NO,886CC::NO => CC::O,887888CC::B => CC::NB,889CC::NB => CC::B,890891CC::Z => CC::NZ,892CC::NZ => CC::Z,893894CC::BE => CC::NBE,895CC::NBE => CC::BE,896897CC::S => CC::NS,898CC::NS => CC::S,899900CC::L => CC::NL,901CC::NL => CC::L,902903CC::LE => CC::NLE,904CC::NLE => CC::LE,905906CC::P => CC::NP,907CC::NP => CC::P,908}909}910911pub(crate) fn get_enc(self) -> u8 {912self as u8913}914}915916impl fmt::Debug for CC {917fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {918let name = match self {919CC::O => "o",920CC::NO => "no",921CC::B => "b",922CC::NB => "nb",923CC::Z => "z",924CC::NZ => "nz",925CC::BE => "be",926CC::NBE => "nbe",927CC::S => "s",928CC::NS => "ns",929CC::L => "l",930CC::NL => "nl",931CC::LE => "le",932CC::NLE => "nle",933CC::P => "p",934CC::NP => "np",935};936write!(fmt, "{name}")937}938}939940impl fmt::Display for CC {941fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {942fmt::Debug::fmt(self, f)943}944}945946/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,947/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS948/// whereas [FcmpImm] is used as an immediate.949#[derive(Clone, Copy)]950pub enum FcmpImm {951/// Equal comparison.952Equal = 0x00,953/// Less than comparison.954LessThan = 0x01,955/// Less than or equal comparison.956LessThanOrEqual = 0x02,957/// Unordered.958Unordered = 0x03,959/// Not equal comparison.960NotEqual = 0x04,961/// Unordered of greater than or equal comparison.962UnorderedOrGreaterThanOrEqual = 0x05,963/// Unordered or greater than comparison.964UnorderedOrGreaterThan = 0x06,965/// Ordered.966Ordered = 0x07,967}968969impl FcmpImm {970pub(crate) fn encode(self) -> u8 {971self as u8972}973}974975impl From<FloatCC> for FcmpImm {976fn from(cond: FloatCC) -> Self {977match cond {978FloatCC::Equal => FcmpImm::Equal,979FloatCC::LessThan => FcmpImm::LessThan,980FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,981FloatCC::Unordered => FcmpImm::Unordered,982FloatCC::NotEqual => FcmpImm::NotEqual,983FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,984FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,985FloatCC::Ordered => FcmpImm::Ordered,986_ => panic!("unable to create comparison predicate for {cond}"),987}988}989}990991/// Encode the rounding modes used as part of the Rounding Control field.992/// Note, these rounding immediates only consider the rounding control field993/// (i.e. the rounding mode) which only take up the first two bits when encoded.994/// However the rounding immediate which this field helps make up, also includes995/// bits 3 and 4 which define the rounding select and precision mask respectively.996/// These two bits are not defined here and are implicitly set to zero when encoded.997#[derive(Clone, Copy)]998pub enum RoundImm {999/// Round to nearest mode.1000RoundNearest = 0x00,1001/// Round down mode.1002RoundDown = 0x01,1003/// Round up mode.1004RoundUp = 0x02,1005/// Round to zero mode.1006RoundZero = 0x03,1007}10081009impl RoundImm {1010pub(crate) fn encode(self) -> u8 {1011self as u81012}1013}10141015/// An operand's size in bits.1016#[derive(Clone, Copy, PartialEq)]1017pub enum OperandSize {1018/// 8-bit.1019Size8,1020/// 16-bit.1021Size16,1022/// 32-bit.1023Size32,1024/// 64-bit.1025Size64,1026}10271028impl OperandSize {1029pub(crate) fn from_bytes(num_bytes: u32) -> Self {1030match num_bytes {10311 => OperandSize::Size8,10322 => OperandSize::Size16,10334 => OperandSize::Size32,10348 => OperandSize::Size64,1035_ => unreachable!("Invalid OperandSize: {}", num_bytes),1036}1037}10381039// Computes the OperandSize for a given type.1040// For vectors, the OperandSize of the lanes is returned.1041pub(crate) fn from_ty(ty: Type) -> Self {1042Self::from_bytes(ty.lane_type().bytes())1043}10441045// Check that the value of self is one of the allowed sizes.1046pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {1047sizes.iter().any(|val| *self == *val)1048}10491050pub(crate) fn to_bytes(&self) -> u8 {1051match self {1052Self::Size8 => 1,1053Self::Size16 => 2,1054Self::Size32 => 4,1055Self::Size64 => 8,1056}1057}10581059pub(crate) fn to_bits(&self) -> u8 {1060self.to_bytes() * 81061}1062}106310641065