cranelift_codegen/isa/x64/inst/
args.rs

1//! Instruction operand sub-components (aka "parts"): definitions and printing.
2
3use super::regs::{self};
4use crate::ir::MemFlags;
5use crate::ir::condcodes::{FloatCC, IntCC};
6use crate::ir::types::*;
7use crate::isa::x64::inst::Inst;
8use crate::isa::x64::inst::regs::pretty_print_reg;
9use crate::machinst::*;
10use std::fmt;
11use std::string::String;
12
13/// An extension trait for converting `Writable{Xmm,Gpr}` to `Writable<Reg>`.
14pub trait ToWritableReg {
15    /// Convert `Writable{Xmm,Gpr}` to `Writable<Reg>`.
16    fn to_writable_reg(&self) -> Writable<Reg>;
17}
18
19/// An extension trait for converting `Writable<Reg>` to `Writable{Xmm,Gpr}`.
20pub trait FromWritableReg: Sized {
21    /// Convert `Writable<Reg>` to `Writable{Xmm,Gpr}`.
22    fn from_writable_reg(w: Writable<Reg>) -> Option<Self>;
23}
24
25/// A macro for defining a newtype of `Reg` that enforces some invariant about
26/// the wrapped `Reg` (such as that it is of a particular register class).
27macro_rules! newtype_of_reg {
28    (
29        $newtype_reg:ident,
30        $newtype_writable_reg:ident,
31        $newtype_option_writable_reg:ident,
32        reg_mem: ($($newtype_reg_mem:ident $(aligned:$aligned:ident)?),*),
33        reg_mem_imm: ($($newtype_reg_mem_imm:ident $(aligned:$aligned_imm:ident)?),*),
34        |$check_reg:ident| $check:expr
35    ) => {
36        /// A newtype wrapper around `Reg`.
37        #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38        pub struct $newtype_reg(Reg);
39
40        impl PartialEq<Reg> for $newtype_reg {
41            fn eq(&self, other: &Reg) -> bool {
42                self.0 == *other
43            }
44        }
45
46        impl From<$newtype_reg> for Reg {
47            fn from(r: $newtype_reg) -> Self {
48                r.0
49            }
50        }
51
52        impl $newtype_reg {
53            /// Create this newtype from the given register, or return `None` if the register
54            /// is not a valid instance of this newtype.
55            pub fn new($check_reg: Reg) -> Option<Self> {
56                if $check {
57                    Some(Self($check_reg))
58                } else {
59                    None
60                }
61            }
62
63            /// Like `Self::new(r).unwrap()` but with a better panic message on
64            /// failure.
65            pub fn unwrap_new($check_reg: Reg) -> Self {
66                if $check {
67                    Self($check_reg)
68                } else {
69                    panic!(
70                        "cannot construct {} from register {:?} with register class {:?}",
71                        stringify!($newtype_reg),
72                        $check_reg,
73                        $check_reg.class(),
74                    )
75                }
76            }
77
78            /// Get this newtype's underlying `Reg`.
79            pub fn to_reg(self) -> Reg {
80                self.0
81            }
82        }
83
84        // Convenience impl so that people working with this newtype can use it
85        // "just like" a plain `Reg`.
86        //
87        // NB: We cannot implement `DerefMut` because that would let people do
88        // nasty stuff like `*my_gpr.deref_mut() = some_xmm_reg`, breaking the
89        // invariants that `Gpr` provides.
90        impl std::ops::Deref for $newtype_reg {
91            type Target = Reg;
92
93            fn deref(&self) -> &Reg {
94                &self.0
95            }
96        }
97
98        /// If you know what you're doing, you can explicitly mutably borrow the
99        /// underlying `Reg`. Don't make it point to the wrong type of register
100        /// please.
101        impl AsMut<Reg> for $newtype_reg {
102            fn as_mut(&mut self) -> &mut Reg {
103                &mut self.0
104            }
105        }
106
107        /// Writable Gpr.
108        pub type $newtype_writable_reg = Writable<$newtype_reg>;
109
110        #[allow(dead_code, reason = "Used by some newtypes and not others")]
111        /// Optional writable Gpr.
112        pub type $newtype_option_writable_reg = Option<Writable<$newtype_reg>>;
113
114        impl ToWritableReg for $newtype_writable_reg {
115            fn to_writable_reg(&self) -> Writable<Reg> {
116                Writable::from_reg(self.to_reg().to_reg())
117            }
118        }
119
120        impl FromWritableReg for $newtype_writable_reg {
121            fn from_writable_reg(w: Writable<Reg>) -> Option<Self> {
122                Some(Writable::from_reg($newtype_reg::new(w.to_reg())?))
123            }
124        }
125
126        $(
127            /// A newtype wrapper around `RegMem` for general-purpose registers.
128            #[derive(Clone, Debug)]
129            pub struct $newtype_reg_mem(RegMem);
130
131            impl From<$newtype_reg_mem> for RegMem {
132                fn from(rm: $newtype_reg_mem) -> Self {
133                    rm.0
134                }
135            }
136            impl<'a> From<&'a $newtype_reg_mem> for &'a RegMem {
137                fn from(rm: &'a $newtype_reg_mem) -> &'a RegMem {
138                    &rm.0
139                }
140            }
141
142            impl From<$newtype_reg> for $newtype_reg_mem {
143                fn from(r: $newtype_reg) -> Self {
144                    $newtype_reg_mem(RegMem::reg(r.into()))
145                }
146            }
147
148            impl $newtype_reg_mem {
149                /// Construct a `RegMem` newtype from the given `RegMem`, or return
150                /// `None` if the `RegMem` is not a valid instance of this `RegMem`
151                /// newtype.
152                pub fn new(rm: RegMem) -> Option<Self> {
153                    match rm {
154                        RegMem::Mem { addr } => {
155                            let mut _allow = true;
156                            $(
157                                if $aligned {
158                                    _allow = addr.aligned();
159                                }
160                            )?
161                            if _allow {
162                                Some(Self(RegMem::Mem { addr }))
163                            } else {
164                                None
165                            }
166                        }
167                        RegMem::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
168                    }
169                }
170
171                /// Like `Self::new(rm).unwrap()` but with better panic messages
172                /// in case of failure.
173                pub fn unwrap_new(rm: RegMem) -> Self {
174                    match rm {
175                        RegMem::Mem { addr } => {
176                            $(
177                                if $aligned && !addr.aligned() {
178                                    panic!(
179                                        "cannot create {} from an unaligned memory address: {addr:?}",
180                                        stringify!($newtype_reg_mem),
181                                    );
182                                }
183                            )?
184                            Self(RegMem::Mem { addr })
185                        }
186                        RegMem::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
187                    }
188                }
189
190                /// Convert this newtype into its underlying `RegMem`.
191                pub fn to_reg_mem(self) -> RegMem {
192                    self.0
193                }
194
195                #[allow(dead_code, reason = "Used by some newtypes and not others")]
196                pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
197                    self.0.get_operands(collector);
198                }
199            }
200            impl PrettyPrint for $newtype_reg_mem {
201                fn pretty_print(&self, size: u8) -> String {
202                    self.0.pretty_print(size)
203                }
204            }
205        )*
206
207        $(
208            /// A newtype wrapper around `RegMemImm`.
209            #[derive(Clone, Debug)]
210            pub struct $newtype_reg_mem_imm(RegMemImm);
211
212            impl From<$newtype_reg_mem_imm> for RegMemImm {
213                fn from(rmi: $newtype_reg_mem_imm) -> RegMemImm {
214                    rmi.0
215                }
216            }
217            impl<'a> From<&'a $newtype_reg_mem_imm> for &'a RegMemImm {
218                fn from(rmi: &'a $newtype_reg_mem_imm) -> &'a RegMemImm {
219                    &rmi.0
220                }
221            }
222
223            impl From<$newtype_reg> for $newtype_reg_mem_imm {
224                fn from(r: $newtype_reg) -> Self {
225                    $newtype_reg_mem_imm(RegMemImm::reg(r.into()))
226                }
227            }
228
229            impl $newtype_reg_mem_imm {
230                /// Construct this newtype from the given `RegMemImm`, or return
231                /// `None` if the `RegMemImm` is not a valid instance of this
232                /// newtype.
233                pub fn new(rmi: RegMemImm) -> Option<Self> {
234                    match rmi {
235                        RegMemImm::Imm { .. } => Some(Self(rmi)),
236                        RegMemImm::Mem { addr } => {
237                            let mut _allow = true;
238                            $(
239                                if $aligned_imm {
240                                    _allow = addr.aligned();
241                                }
242                            )?
243                            if _allow {
244                                Some(Self(RegMemImm::Mem { addr }))
245                            } else {
246                                None
247                            }
248                        }
249                        RegMemImm::Reg { reg } => Some($newtype_reg::new(reg)?.into()),
250                    }
251                }
252
253                /// Like `Self::new(rmi).unwrap()` but with better panic
254                /// messages in case of failure.
255                pub fn unwrap_new(rmi: RegMemImm) -> Self {
256                    match rmi {
257                        RegMemImm::Imm { .. } => Self(rmi),
258                        RegMemImm::Mem { addr } => {
259                            $(
260                                if $aligned_imm && !addr.aligned() {
261                                    panic!(
262                                        "cannot construct {} from unaligned memory address: {:?}",
263                                        stringify!($newtype_reg_mem_imm),
264                                        addr,
265                                    );
266                                }
267                            )?
268                            Self(RegMemImm::Mem { addr })
269
270                        }
271                        RegMemImm::Reg { reg } => $newtype_reg::unwrap_new(reg).into(),
272                    }
273                }
274
275                /// Convert this newtype into its underlying `RegMemImm`.
276                #[allow(dead_code, reason = "Used by some newtypes and not others")]
277                pub fn to_reg_mem_imm(self) -> RegMemImm {
278                    self.0
279                }
280
281                #[allow(dead_code, reason = "Used by some newtypes and not others")]
282                pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
283                    self.0.get_operands(collector);
284                }
285            }
286
287            impl PrettyPrint for $newtype_reg_mem_imm {
288                fn pretty_print(&self, size: u8) -> String {
289                    self.0.pretty_print(size)
290                }
291            }
292        )*
293    };
294}
295
296// Define a newtype of `Reg` for general-purpose registers.
297newtype_of_reg!(
298    Gpr,
299    WritableGpr,
300    OptionWritableGpr,
301    reg_mem: (GprMem),
302    reg_mem_imm: (GprMemImm),
303    |reg| reg.class() == RegClass::Int
304);
305
306#[expect(missing_docs, reason = "self-describing fields")]
307impl Gpr {
308    pub const RAX: Gpr = Gpr(regs::rax());
309    pub const RBX: Gpr = Gpr(regs::rbx());
310    pub const RCX: Gpr = Gpr(regs::rcx());
311    pub const RDX: Gpr = Gpr(regs::rdx());
312    pub const RSI: Gpr = Gpr(regs::rsi());
313    pub const RDI: Gpr = Gpr(regs::rdi());
314    pub const RSP: Gpr = Gpr(regs::rsp());
315    pub const RBP: Gpr = Gpr(regs::rbp());
316    pub const R8: Gpr = Gpr(regs::r8());
317    pub const R9: Gpr = Gpr(regs::r9());
318    pub const R10: Gpr = Gpr(regs::r10());
319    pub const R11: Gpr = Gpr(regs::r11());
320    pub const R12: Gpr = Gpr(regs::r12());
321    pub const R13: Gpr = Gpr(regs::r13());
322    pub const R14: Gpr = Gpr(regs::r14());
323    pub const R15: Gpr = Gpr(regs::r15());
324}
325
326// Define a newtype of `Reg` for XMM registers.
327newtype_of_reg!(
328    Xmm,
329    WritableXmm,
330    OptionWritableXmm,
331    reg_mem: (XmmMem, XmmMemAligned aligned:true),
332    reg_mem_imm: (XmmMemImm, XmmMemAlignedImm aligned:true),
333    |reg| reg.class() == RegClass::Float
334);
335
336// N.B.: `Amode` is defined in `inst.isle`. We add some convenience
337// constructors here.
338
339// Re-export the type from the ISLE generated code.
340pub use crate::isa::x64::lower::isle::generated_code::Amode;
341
342impl Amode {
343    /// Create an immediate sign-extended and register addressing mode.
344    pub fn imm_reg(simm32: i32, base: Reg) -> Self {
345        debug_assert!(base.class() == RegClass::Int);
346        Self::ImmReg {
347            simm32,
348            base,
349            flags: MemFlags::trusted(),
350        }
351    }
352
353    /// Create a sign-extended-32-to-64 with register and shift addressing mode.
354    pub fn imm_reg_reg_shift(simm32: i32, base: Gpr, index: Gpr, shift: u8) -> Self {
355        debug_assert!(base.class() == RegClass::Int);
356        debug_assert!(index.class() == RegClass::Int);
357        debug_assert!(shift <= 3);
358        Self::ImmRegRegShift {
359            simm32,
360            base,
361            index,
362            shift,
363            flags: MemFlags::trusted(),
364        }
365    }
366
367    pub(crate) fn rip_relative(target: MachLabel) -> Self {
368        Self::RipRelative { target }
369    }
370
371    /// Set the specified [MemFlags] to the [Amode].
372    pub fn with_flags(&self, flags: MemFlags) -> Self {
373        match self {
374            &Self::ImmReg { simm32, base, .. } => Self::ImmReg {
375                simm32,
376                base,
377                flags,
378            },
379            &Self::ImmRegRegShift {
380                simm32,
381                base,
382                index,
383                shift,
384                ..
385            } => Self::ImmRegRegShift {
386                simm32,
387                base,
388                index,
389                shift,
390                flags,
391            },
392            _ => panic!("Amode {self:?} cannot take memflags"),
393        }
394    }
395
396    /// Add the registers mentioned by `self` to `collector`.
397    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
398        match self {
399            Amode::ImmReg { base, .. } => {
400                if *base != regs::rbp() && *base != regs::rsp() {
401                    collector.reg_use(base);
402                }
403            }
404            Amode::ImmRegRegShift { base, index, .. } => {
405                debug_assert_ne!(base.to_reg(), regs::rbp());
406                debug_assert_ne!(base.to_reg(), regs::rsp());
407                collector.reg_use(base);
408                debug_assert_ne!(index.to_reg(), regs::rbp());
409                debug_assert_ne!(index.to_reg(), regs::rsp());
410                collector.reg_use(index);
411            }
412            Amode::RipRelative { .. } => {
413                // RIP isn't involved in regalloc.
414            }
415        }
416    }
417
418    /// Same as `get_operands`, but add the registers in the "late" phase.
419    pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
420        match self {
421            Amode::ImmReg { base, .. } => {
422                collector.reg_late_use(base);
423            }
424            Amode::ImmRegRegShift { base, index, .. } => {
425                collector.reg_late_use(base);
426                collector.reg_late_use(index);
427            }
428            Amode::RipRelative { .. } => {
429                // RIP isn't involved in regalloc.
430            }
431        }
432    }
433
434    pub(crate) fn get_flags(&self) -> MemFlags {
435        match self {
436            Amode::ImmReg { flags, .. } | Amode::ImmRegRegShift { flags, .. } => *flags,
437            Amode::RipRelative { .. } => MemFlags::trusted(),
438        }
439    }
440
441    /// Offset the amode by a fixed offset.
442    pub(crate) fn offset(&self, offset: i32) -> Self {
443        let mut ret = self.clone();
444        match &mut ret {
445            &mut Amode::ImmReg { ref mut simm32, .. } => *simm32 += offset,
446            &mut Amode::ImmRegRegShift { ref mut simm32, .. } => *simm32 += offset,
447            _ => panic!("Cannot offset amode: {self:?}"),
448        }
449        ret
450    }
451
452    pub(crate) fn aligned(&self) -> bool {
453        self.get_flags().aligned()
454    }
455}
456
457impl PrettyPrint for Amode {
458    fn pretty_print(&self, _size: u8) -> String {
459        match self {
460            Amode::ImmReg { simm32, base, .. } => {
461                // Note: size is always 8; the address is 64 bits,
462                // even if the addressed operand is smaller.
463                format!("{}({})", *simm32, pretty_print_reg(*base, 8))
464            }
465            Amode::ImmRegRegShift {
466                simm32,
467                base,
468                index,
469                shift,
470                ..
471            } => format!(
472                "{}({},{},{})",
473                *simm32,
474                pretty_print_reg(base.to_reg(), 8),
475                pretty_print_reg(index.to_reg(), 8),
476                1 << shift
477            ),
478            Amode::RipRelative { target } => format!("label{}(%rip)", target.as_u32()),
479        }
480    }
481}
482
483/// A Memory Address. These denote a 64-bit value only.
484/// Used for usual addressing modes as well as addressing modes used during compilation, when the
485/// moving SP offset is not known.
486#[derive(Clone, Debug)]
487pub enum SyntheticAmode {
488    /// A real amode.
489    Real(Amode),
490
491    /// A (virtual) offset into the incoming argument area.
492    IncomingArg {
493        /// The downward offset from the start of the incoming argument area.
494        offset: u32,
495    },
496
497    /// A (virtual) offset to the slot area of the function frame, which lies just above the
498    /// outgoing arguments.
499    SlotOffset {
500        /// The offset into the slot area.
501        simm32: i32,
502    },
503
504    /// A virtual offset to a constant that will be emitted in the constant section of the buffer.
505    ConstantOffset(VCodeConstant),
506}
507
508impl SyntheticAmode {
509    /// Create a real addressing mode.
510    pub fn real(amode: Amode) -> Self {
511        Self::Real(amode)
512    }
513
514    pub(crate) fn slot_offset(simm32: i32) -> Self {
515        SyntheticAmode::SlotOffset { simm32 }
516    }
517
518    /// Add the registers mentioned by `self` to `collector`.
519    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
520        match self {
521            SyntheticAmode::Real(addr) => addr.get_operands(collector),
522            SyntheticAmode::IncomingArg { .. } => {
523                // Nothing to do; the base is known and isn't involved in regalloc.
524            }
525            SyntheticAmode::SlotOffset { .. } => {
526                // Nothing to do; the base is SP and isn't involved in regalloc.
527            }
528            SyntheticAmode::ConstantOffset(_) => {}
529        }
530    }
531
532    /// Same as `get_operands`, but add the register in the "late" phase.
533    pub(crate) fn get_operands_late(&mut self, collector: &mut impl OperandVisitor) {
534        match self {
535            SyntheticAmode::Real(addr) => addr.get_operands_late(collector),
536            SyntheticAmode::IncomingArg { .. } => {
537                // Nothing to do; the base is known and isn't involved in regalloc.
538            }
539            SyntheticAmode::SlotOffset { .. } => {
540                // Nothing to do; the base is SP and isn't involved in regalloc.
541            }
542            SyntheticAmode::ConstantOffset(_) => {}
543        }
544    }
545
546    pub(crate) fn finalize(&self, frame: &FrameLayout, buffer: &mut MachBuffer<Inst>) -> Amode {
547        match self {
548            SyntheticAmode::Real(addr) => addr.clone(),
549            SyntheticAmode::IncomingArg { offset } => {
550                // NOTE: this could be made relative to RSP by adding additional
551                // offsets from the frame_layout.
552                let args_max_fp_offset = frame.tail_args_size + frame.setup_area_size;
553                Amode::imm_reg(
554                    i32::try_from(args_max_fp_offset - offset).unwrap(),
555                    regs::rbp(),
556                )
557            }
558            SyntheticAmode::SlotOffset { simm32 } => {
559                let off = *simm32 as i64 + i64::from(frame.outgoing_args_size);
560                Amode::imm_reg(off.try_into().expect("invalid sp offset"), regs::rsp())
561            }
562            SyntheticAmode::ConstantOffset(c) => {
563                Amode::rip_relative(buffer.get_label_for_constant(*c))
564            }
565        }
566    }
567
568    pub(crate) fn aligned(&self) -> bool {
569        match self {
570            SyntheticAmode::Real(addr) => addr.aligned(),
571            &SyntheticAmode::IncomingArg { .. }
572            | SyntheticAmode::SlotOffset { .. }
573            | SyntheticAmode::ConstantOffset { .. } => true,
574        }
575    }
576}
577
578impl From<Amode> for SyntheticAmode {
579    fn from(amode: Amode) -> SyntheticAmode {
580        SyntheticAmode::Real(amode)
581    }
582}
583
584impl From<VCodeConstant> for SyntheticAmode {
585    fn from(c: VCodeConstant) -> SyntheticAmode {
586        SyntheticAmode::ConstantOffset(c)
587    }
588}
589
590impl PrettyPrint for SyntheticAmode {
591    fn pretty_print(&self, _size: u8) -> String {
592        match self {
593            // See note in `Amode` regarding constant size of `8`.
594            SyntheticAmode::Real(addr) => addr.pretty_print(8),
595            &SyntheticAmode::IncomingArg { offset } => {
596                format!("rbp(stack args max - {offset})")
597            }
598            SyntheticAmode::SlotOffset { simm32 } => {
599                format!("rsp({} + virtual offset)", *simm32)
600            }
601            SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),
602        }
603    }
604}
605
606/// An operand which is either an integer Register, a value in Memory or an Immediate.  This can
607/// denote an 8, 16, 32 or 64 bit value.  For the Immediate form, in the 8- and 16-bit case, only
608/// the lower 8 or 16 bits of `simm32` is relevant.  In the 64-bit case, the value denoted by
609/// `simm32` is its sign-extension out to 64 bits.
610#[derive(Clone, Debug)]
611pub enum RegMemImm {
612    /// A register operand.
613    Reg {
614        /// The underlying register.
615        reg: Reg,
616    },
617    /// A memory operand.
618    Mem {
619        /// The memory address.
620        addr: SyntheticAmode,
621    },
622    /// An immediate operand.
623    Imm {
624        /// The immediate value.
625        simm32: u32,
626    },
627}
628
629impl RegMemImm {
630    /// Create a register operand.
631    pub fn reg(reg: Reg) -> Self {
632        debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
633        Self::Reg { reg }
634    }
635
636    /// Create a memory operand.
637    pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
638        Self::Mem { addr: addr.into() }
639    }
640
641    /// Create an immediate operand.
642    pub fn imm(simm32: u32) -> Self {
643        Self::Imm { simm32 }
644    }
645
646    /// Add the regs mentioned by `self` to `collector`.
647    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
648        match self {
649            Self::Reg { reg } => collector.reg_use(reg),
650            Self::Mem { addr } => addr.get_operands(collector),
651            Self::Imm { .. } => {}
652        }
653    }
654}
655
656impl From<RegMem> for RegMemImm {
657    fn from(rm: RegMem) -> RegMemImm {
658        match rm {
659            RegMem::Reg { reg } => RegMemImm::Reg { reg },
660            RegMem::Mem { addr } => RegMemImm::Mem { addr },
661        }
662    }
663}
664
665impl From<Reg> for RegMemImm {
666    fn from(reg: Reg) -> Self {
667        RegMemImm::Reg { reg }
668    }
669}
670
671impl PrettyPrint for RegMemImm {
672    fn pretty_print(&self, size: u8) -> String {
673        match self {
674            Self::Reg { reg } => pretty_print_reg(*reg, size),
675            Self::Mem { addr } => addr.pretty_print(size),
676            Self::Imm { simm32 } => format!("${}", *simm32 as i32),
677        }
678    }
679}
680
681/// An operand which is either an integer Register or a value in Memory.  This can denote an 8, 16,
682/// 32, 64, or 128 bit value.
683#[derive(Clone, Debug)]
684pub enum RegMem {
685    /// A register operand.
686    Reg {
687        /// The underlying register.
688        reg: Reg,
689    },
690    /// A memory operand.
691    Mem {
692        /// The memory address.
693        addr: SyntheticAmode,
694    },
695}
696
697impl RegMem {
698    /// Create a register operand.
699    pub fn reg(reg: Reg) -> Self {
700        debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
701        Self::Reg { reg }
702    }
703
704    /// Create a memory operand.
705    pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
706        Self::Mem { addr: addr.into() }
707    }
708    /// Asserts that in register mode, the reg class is the one that's expected.
709    pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {
710        if let Self::Reg { reg } = self {
711            debug_assert_eq!(reg.class(), expected_reg_class);
712        }
713    }
714    /// Add the regs mentioned by `self` to `collector`.
715    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
716        match self {
717            RegMem::Reg { reg } => collector.reg_use(reg),
718            RegMem::Mem { addr, .. } => addr.get_operands(collector),
719        }
720    }
721}
722
723impl From<Reg> for RegMem {
724    fn from(reg: Reg) -> RegMem {
725        RegMem::Reg { reg }
726    }
727}
728
729impl From<Writable<Reg>> for RegMem {
730    fn from(r: Writable<Reg>) -> Self {
731        RegMem::reg(r.to_reg())
732    }
733}
734
735impl PrettyPrint for RegMem {
736    fn pretty_print(&self, size: u8) -> String {
737        match self {
738            RegMem::Reg { reg } => pretty_print_reg(*reg, size),
739            RegMem::Mem { addr, .. } => addr.pretty_print(size),
740        }
741    }
742}
743
744#[derive(Debug)]
745pub(crate) enum InstructionSet {
746    SSE,
747    SSE2,
748    CMPXCHG16b,
749    SSE3,
750    SSSE3,
751    SSE41,
752    SSE42,
753    Popcnt,
754    Lzcnt,
755    BMI1,
756    BMI2,
757    FMA,
758    AVX,
759    AVX2,
760    AVX512BITALG,
761    AVX512DQ,
762    AVX512F,
763    AVX512VBMI,
764    AVX512VL,
765}
766
767/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
768/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
769/// values can be extended.
770#[derive(Clone, PartialEq)]
771pub enum ExtKind {
772    /// No extension.
773    None,
774    /// Sign-extend.
775    SignExtend,
776    /// Zero-extend.
777    ZeroExtend,
778}
779
780/// These indicate ways of extending (widening) a value, using the Intel
781/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
782#[derive(Clone, PartialEq)]
783pub enum ExtMode {
784    /// Byte -> Longword.
785    BL,
786    /// Byte -> Quadword.
787    BQ,
788    /// Word -> Longword.
789    WL,
790    /// Word -> Quadword.
791    WQ,
792    /// Longword -> Quadword.
793    LQ,
794}
795
796impl ExtMode {
797    /// Calculate the `ExtMode` from passed bit lengths of the from/to types.
798    pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {
799        match (from_bits, to_bits) {
800            (1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),
801            (1, 64) | (8, 64) => Some(ExtMode::BQ),
802            (16, 32) => Some(ExtMode::WL),
803            (16, 64) => Some(ExtMode::WQ),
804            (32, 64) => Some(ExtMode::LQ),
805            _ => None,
806        }
807    }
808}
809
810impl fmt::Debug for ExtMode {
811    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
812        let name = match self {
813            ExtMode::BL => "bl",
814            ExtMode::BQ => "bq",
815            ExtMode::WL => "wl",
816            ExtMode::WQ => "wq",
817            ExtMode::LQ => "lq",
818        };
819        write!(fmt, "{name}")
820    }
821}
822
823impl fmt::Display for ExtMode {
824    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
825        fmt::Debug::fmt(self, f)
826    }
827}
828
829/// These indicate condition code tests.  Not all are represented since not all are useful in
830/// compiler-generated code.
831#[derive(Copy, Clone, PartialEq, Eq)]
832#[repr(u8)]
833pub enum CC {
834    ///  overflow
835    O = 0,
836    /// no overflow
837    NO = 1,
838
839    /// < unsigned
840    B = 2,
841    /// >= unsigned
842    NB = 3,
843
844    /// zero
845    Z = 4,
846    /// not-zero
847    NZ = 5,
848
849    /// <= unsigned
850    BE = 6,
851    /// > unsigned
852    NBE = 7,
853
854    /// negative
855    S = 8,
856    /// not-negative
857    NS = 9,
858
859    /// < signed
860    L = 12,
861    /// >= signed
862    NL = 13,
863
864    /// <= signed
865    LE = 14,
866    /// > signed
867    NLE = 15,
868
869    /// parity
870    P = 10,
871
872    /// not parity
873    NP = 11,
874}
875
876impl CC {
877    pub(crate) fn from_intcc(intcc: IntCC) -> Self {
878        match intcc {
879            IntCC::Equal => CC::Z,
880            IntCC::NotEqual => CC::NZ,
881            IntCC::SignedGreaterThanOrEqual => CC::NL,
882            IntCC::SignedGreaterThan => CC::NLE,
883            IntCC::SignedLessThanOrEqual => CC::LE,
884            IntCC::SignedLessThan => CC::L,
885            IntCC::UnsignedGreaterThanOrEqual => CC::NB,
886            IntCC::UnsignedGreaterThan => CC::NBE,
887            IntCC::UnsignedLessThanOrEqual => CC::BE,
888            IntCC::UnsignedLessThan => CC::B,
889        }
890    }
891
892    pub(crate) fn invert(&self) -> Self {
893        match self {
894            CC::O => CC::NO,
895            CC::NO => CC::O,
896
897            CC::B => CC::NB,
898            CC::NB => CC::B,
899
900            CC::Z => CC::NZ,
901            CC::NZ => CC::Z,
902
903            CC::BE => CC::NBE,
904            CC::NBE => CC::BE,
905
906            CC::S => CC::NS,
907            CC::NS => CC::S,
908
909            CC::L => CC::NL,
910            CC::NL => CC::L,
911
912            CC::LE => CC::NLE,
913            CC::NLE => CC::LE,
914
915            CC::P => CC::NP,
916            CC::NP => CC::P,
917        }
918    }
919
920    pub(crate) fn get_enc(self) -> u8 {
921        self as u8
922    }
923}
924
925impl fmt::Debug for CC {
926    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
927        let name = match self {
928            CC::O => "o",
929            CC::NO => "no",
930            CC::B => "b",
931            CC::NB => "nb",
932            CC::Z => "z",
933            CC::NZ => "nz",
934            CC::BE => "be",
935            CC::NBE => "nbe",
936            CC::S => "s",
937            CC::NS => "ns",
938            CC::L => "l",
939            CC::NL => "nl",
940            CC::LE => "le",
941            CC::NLE => "nle",
942            CC::P => "p",
943            CC::NP => "np",
944        };
945        write!(fmt, "{name}")
946    }
947}
948
949impl fmt::Display for CC {
950    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
951        fmt::Debug::fmt(self, f)
952    }
953}
954
955/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,
956/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS
957/// whereas [FcmpImm] is used as an immediate.
958#[derive(Clone, Copy)]
959pub enum FcmpImm {
960    /// Equal comparison.
961    Equal = 0x00,
962    /// Less than comparison.
963    LessThan = 0x01,
964    /// Less than or equal comparison.
965    LessThanOrEqual = 0x02,
966    /// Unordered.
967    Unordered = 0x03,
968    /// Not equal comparison.
969    NotEqual = 0x04,
970    /// Unordered of greater than or equal comparison.
971    UnorderedOrGreaterThanOrEqual = 0x05,
972    /// Unordered or greater than comparison.
973    UnorderedOrGreaterThan = 0x06,
974    /// Ordered.
975    Ordered = 0x07,
976}
977
978impl FcmpImm {
979    pub(crate) fn encode(self) -> u8 {
980        self as u8
981    }
982}
983
984impl From<FloatCC> for FcmpImm {
985    fn from(cond: FloatCC) -> Self {
986        match cond {
987            FloatCC::Equal => FcmpImm::Equal,
988            FloatCC::LessThan => FcmpImm::LessThan,
989            FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,
990            FloatCC::Unordered => FcmpImm::Unordered,
991            FloatCC::NotEqual => FcmpImm::NotEqual,
992            FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,
993            FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,
994            FloatCC::Ordered => FcmpImm::Ordered,
995            _ => panic!("unable to create comparison predicate for {cond}"),
996        }
997    }
998}
999
1000/// Encode the rounding modes used as part of the Rounding Control field.
1001/// Note, these rounding immediates only consider the rounding control field
1002/// (i.e. the rounding mode) which only take up the first two bits when encoded.
1003/// However the rounding immediate which this field helps make up, also includes
1004/// bits 3 and 4 which define the rounding select and precision mask respectively.
1005/// These two bits are not defined here and are implicitly set to zero when encoded.
1006#[derive(Clone, Copy)]
1007pub enum RoundImm {
1008    /// Round to nearest mode.
1009    RoundNearest = 0x00,
1010    /// Round down mode.
1011    RoundDown = 0x01,
1012    /// Round up mode.
1013    RoundUp = 0x02,
1014    /// Round to zero mode.
1015    RoundZero = 0x03,
1016}
1017
1018impl RoundImm {
1019    pub(crate) fn encode(self) -> u8 {
1020        self as u8
1021    }
1022}
1023
1024/// An operand's size in bits.
1025#[derive(Clone, Copy, PartialEq)]
1026pub enum OperandSize {
1027    /// 8-bit.
1028    Size8,
1029    /// 16-bit.
1030    Size16,
1031    /// 32-bit.
1032    Size32,
1033    /// 64-bit.
1034    Size64,
1035}
1036
1037impl OperandSize {
1038    pub(crate) fn from_bytes(num_bytes: u32) -> Self {
1039        match num_bytes {
1040            1 => OperandSize::Size8,
1041            2 => OperandSize::Size16,
1042            4 => OperandSize::Size32,
1043            8 => OperandSize::Size64,
1044            _ => unreachable!("Invalid OperandSize: {}", num_bytes),
1045        }
1046    }
1047
1048    // Computes the OperandSize for a given type.
1049    // For vectors, the OperandSize of the lanes is returned.
1050    pub(crate) fn from_ty(ty: Type) -> Self {
1051        Self::from_bytes(ty.lane_type().bytes())
1052    }
1053
1054    // Check that the value of self is one of the allowed sizes.
1055    pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {
1056        sizes.iter().any(|val| *self == *val)
1057    }
1058
1059    pub(crate) fn to_bytes(&self) -> u8 {
1060        match self {
1061            Self::Size8 => 1,
1062            Self::Size16 => 2,
1063            Self::Size32 => 4,
1064            Self::Size64 => 8,
1065        }
1066    }
1067
1068    pub(crate) fn to_bits(&self) -> u8 {
1069        self.to_bytes() * 8
1070    }
1071}