Path: blob/main/cranelift/codegen/src/isa/x64/inst/args.rs
1693 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 std::fmt;10use std::string::String;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 std::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}575}576577impl From<Amode> for SyntheticAmode {578fn from(amode: Amode) -> SyntheticAmode {579SyntheticAmode::Real(amode)580}581}582583impl From<VCodeConstant> for SyntheticAmode {584fn from(c: VCodeConstant) -> SyntheticAmode {585SyntheticAmode::ConstantOffset(c)586}587}588589impl PrettyPrint for SyntheticAmode {590fn pretty_print(&self, _size: u8) -> String {591match self {592// See note in `Amode` regarding constant size of `8`.593SyntheticAmode::Real(addr) => addr.pretty_print(8),594&SyntheticAmode::IncomingArg { offset } => {595format!("rbp(stack args max - {offset})")596}597SyntheticAmode::SlotOffset { simm32 } => {598format!("rsp({} + virtual offset)", *simm32)599}600SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),601}602}603}604605/// An operand which is either an integer Register, a value in Memory or an Immediate. This can606/// denote an 8, 16, 32 or 64 bit value. For the Immediate form, in the 8- and 16-bit case, only607/// the lower 8 or 16 bits of `simm32` is relevant. In the 64-bit case, the value denoted by608/// `simm32` is its sign-extension out to 64 bits.609#[derive(Clone, Debug)]610pub enum RegMemImm {611/// A register operand.612Reg {613/// The underlying register.614reg: Reg,615},616/// A memory operand.617Mem {618/// The memory address.619addr: SyntheticAmode,620},621/// An immediate operand.622Imm {623/// The immediate value.624simm32: u32,625},626}627628impl RegMemImm {629/// Create a register operand.630pub fn reg(reg: Reg) -> Self {631debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);632Self::Reg { reg }633}634635/// Create a memory operand.636pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {637Self::Mem { addr: addr.into() }638}639640/// Create an immediate operand.641pub fn imm(simm32: u32) -> Self {642Self::Imm { simm32 }643}644645/// Add the regs mentioned by `self` to `collector`.646pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {647match self {648Self::Reg { reg } => collector.reg_use(reg),649Self::Mem { addr } => addr.get_operands(collector),650Self::Imm { .. } => {}651}652}653}654655impl From<RegMem> for RegMemImm {656fn from(rm: RegMem) -> RegMemImm {657match rm {658RegMem::Reg { reg } => RegMemImm::Reg { reg },659RegMem::Mem { addr } => RegMemImm::Mem { addr },660}661}662}663664impl From<Reg> for RegMemImm {665fn from(reg: Reg) -> Self {666RegMemImm::Reg { reg }667}668}669670impl PrettyPrint for RegMemImm {671fn pretty_print(&self, size: u8) -> String {672match self {673Self::Reg { reg } => pretty_print_reg(*reg, size),674Self::Mem { addr } => addr.pretty_print(size),675Self::Imm { simm32 } => format!("${}", *simm32 as i32),676}677}678}679680/// An operand which is either an integer Register or a value in Memory. This can denote an 8, 16,681/// 32, 64, or 128 bit value.682#[derive(Clone, Debug)]683pub enum RegMem {684/// A register operand.685Reg {686/// The underlying register.687reg: Reg,688},689/// A memory operand.690Mem {691/// The memory address.692addr: SyntheticAmode,693},694}695696impl RegMem {697/// Create a register operand.698pub fn reg(reg: Reg) -> Self {699debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);700Self::Reg { reg }701}702703/// Create a memory operand.704pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {705Self::Mem { addr: addr.into() }706}707/// Asserts that in register mode, the reg class is the one that's expected.708pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {709if let Self::Reg { reg } = self {710debug_assert_eq!(reg.class(), expected_reg_class);711}712}713/// Add the regs mentioned by `self` to `collector`.714pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {715match self {716RegMem::Reg { reg } => collector.reg_use(reg),717RegMem::Mem { addr, .. } => addr.get_operands(collector),718}719}720}721722impl From<Reg> for RegMem {723fn from(reg: Reg) -> RegMem {724RegMem::Reg { reg }725}726}727728impl From<Writable<Reg>> for RegMem {729fn from(r: Writable<Reg>) -> Self {730RegMem::reg(r.to_reg())731}732}733734impl PrettyPrint for RegMem {735fn pretty_print(&self, size: u8) -> String {736match self {737RegMem::Reg { reg } => pretty_print_reg(*reg, size),738RegMem::Mem { addr, .. } => addr.pretty_print(size),739}740}741}742743/// This defines the ways a value can be extended: either signed- or zero-extension, or none for744/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which745/// values can be extended.746#[derive(Clone, PartialEq)]747pub enum ExtKind {748/// No extension.749None,750/// Sign-extend.751SignExtend,752/// Zero-extend.753ZeroExtend,754}755756/// These indicate ways of extending (widening) a value, using the Intel757/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64758#[derive(Clone, PartialEq)]759pub enum ExtMode {760/// Byte -> Longword.761BL,762/// Byte -> Quadword.763BQ,764/// Word -> Longword.765WL,766/// Word -> Quadword.767WQ,768/// Longword -> Quadword.769LQ,770}771772impl ExtMode {773/// Calculate the `ExtMode` from passed bit lengths of the from/to types.774pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {775match (from_bits, to_bits) {776(1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),777(1, 64) | (8, 64) => Some(ExtMode::BQ),778(16, 32) => Some(ExtMode::WL),779(16, 64) => Some(ExtMode::WQ),780(32, 64) => Some(ExtMode::LQ),781_ => None,782}783}784}785786impl fmt::Debug for ExtMode {787fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {788let name = match self {789ExtMode::BL => "bl",790ExtMode::BQ => "bq",791ExtMode::WL => "wl",792ExtMode::WQ => "wq",793ExtMode::LQ => "lq",794};795write!(fmt, "{name}")796}797}798799impl fmt::Display for ExtMode {800fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {801fmt::Debug::fmt(self, f)802}803}804805/// These indicate condition code tests. Not all are represented since not all are useful in806/// compiler-generated code.807#[derive(Copy, Clone, PartialEq, Eq)]808#[repr(u8)]809pub enum CC {810/// overflow811O = 0,812/// no overflow813NO = 1,814815/// < unsigned816B = 2,817/// >= unsigned818NB = 3,819820/// zero821Z = 4,822/// not-zero823NZ = 5,824825/// <= unsigned826BE = 6,827/// > unsigned828NBE = 7,829830/// negative831S = 8,832/// not-negative833NS = 9,834835/// < signed836L = 12,837/// >= signed838NL = 13,839840/// <= signed841LE = 14,842/// > signed843NLE = 15,844845/// parity846P = 10,847848/// not parity849NP = 11,850}851852impl CC {853pub(crate) fn from_intcc(intcc: IntCC) -> Self {854match intcc {855IntCC::Equal => CC::Z,856IntCC::NotEqual => CC::NZ,857IntCC::SignedGreaterThanOrEqual => CC::NL,858IntCC::SignedGreaterThan => CC::NLE,859IntCC::SignedLessThanOrEqual => CC::LE,860IntCC::SignedLessThan => CC::L,861IntCC::UnsignedGreaterThanOrEqual => CC::NB,862IntCC::UnsignedGreaterThan => CC::NBE,863IntCC::UnsignedLessThanOrEqual => CC::BE,864IntCC::UnsignedLessThan => CC::B,865}866}867868pub(crate) fn invert(&self) -> Self {869match self {870CC::O => CC::NO,871CC::NO => CC::O,872873CC::B => CC::NB,874CC::NB => CC::B,875876CC::Z => CC::NZ,877CC::NZ => CC::Z,878879CC::BE => CC::NBE,880CC::NBE => CC::BE,881882CC::S => CC::NS,883CC::NS => CC::S,884885CC::L => CC::NL,886CC::NL => CC::L,887888CC::LE => CC::NLE,889CC::NLE => CC::LE,890891CC::P => CC::NP,892CC::NP => CC::P,893}894}895896pub(crate) fn get_enc(self) -> u8 {897self as u8898}899}900901impl fmt::Debug for CC {902fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {903let name = match self {904CC::O => "o",905CC::NO => "no",906CC::B => "b",907CC::NB => "nb",908CC::Z => "z",909CC::NZ => "nz",910CC::BE => "be",911CC::NBE => "nbe",912CC::S => "s",913CC::NS => "ns",914CC::L => "l",915CC::NL => "nl",916CC::LE => "le",917CC::NLE => "nle",918CC::P => "p",919CC::NP => "np",920};921write!(fmt, "{name}")922}923}924925impl fmt::Display for CC {926fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {927fmt::Debug::fmt(self, f)928}929}930931/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,932/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS933/// whereas [FcmpImm] is used as an immediate.934#[derive(Clone, Copy)]935pub enum FcmpImm {936/// Equal comparison.937Equal = 0x00,938/// Less than comparison.939LessThan = 0x01,940/// Less than or equal comparison.941LessThanOrEqual = 0x02,942/// Unordered.943Unordered = 0x03,944/// Not equal comparison.945NotEqual = 0x04,946/// Unordered of greater than or equal comparison.947UnorderedOrGreaterThanOrEqual = 0x05,948/// Unordered or greater than comparison.949UnorderedOrGreaterThan = 0x06,950/// Ordered.951Ordered = 0x07,952}953954impl FcmpImm {955pub(crate) fn encode(self) -> u8 {956self as u8957}958}959960impl From<FloatCC> for FcmpImm {961fn from(cond: FloatCC) -> Self {962match cond {963FloatCC::Equal => FcmpImm::Equal,964FloatCC::LessThan => FcmpImm::LessThan,965FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,966FloatCC::Unordered => FcmpImm::Unordered,967FloatCC::NotEqual => FcmpImm::NotEqual,968FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,969FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,970FloatCC::Ordered => FcmpImm::Ordered,971_ => panic!("unable to create comparison predicate for {cond}"),972}973}974}975976/// Encode the rounding modes used as part of the Rounding Control field.977/// Note, these rounding immediates only consider the rounding control field978/// (i.e. the rounding mode) which only take up the first two bits when encoded.979/// However the rounding immediate which this field helps make up, also includes980/// bits 3 and 4 which define the rounding select and precision mask respectively.981/// These two bits are not defined here and are implicitly set to zero when encoded.982#[derive(Clone, Copy)]983pub enum RoundImm {984/// Round to nearest mode.985RoundNearest = 0x00,986/// Round down mode.987RoundDown = 0x01,988/// Round up mode.989RoundUp = 0x02,990/// Round to zero mode.991RoundZero = 0x03,992}993994impl RoundImm {995pub(crate) fn encode(self) -> u8 {996self as u8997}998}9991000/// An operand's size in bits.1001#[derive(Clone, Copy, PartialEq)]1002pub enum OperandSize {1003/// 8-bit.1004Size8,1005/// 16-bit.1006Size16,1007/// 32-bit.1008Size32,1009/// 64-bit.1010Size64,1011}10121013impl OperandSize {1014pub(crate) fn from_bytes(num_bytes: u32) -> Self {1015match num_bytes {10161 => OperandSize::Size8,10172 => OperandSize::Size16,10184 => OperandSize::Size32,10198 => OperandSize::Size64,1020_ => unreachable!("Invalid OperandSize: {}", num_bytes),1021}1022}10231024// Computes the OperandSize for a given type.1025// For vectors, the OperandSize of the lanes is returned.1026pub(crate) fn from_ty(ty: Type) -> Self {1027Self::from_bytes(ty.lane_type().bytes())1028}10291030// Check that the value of self is one of the allowed sizes.1031pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {1032sizes.iter().any(|val| *self == *val)1033}10341035pub(crate) fn to_bytes(&self) -> u8 {1036match self {1037Self::Size8 => 1,1038Self::Size16 => 2,1039Self::Size32 => 4,1040Self::Size64 => 8,1041}1042}10431044pub(crate) fn to_bits(&self) -> u8 {1045self.to_bytes() * 81046}1047}104810491050