Skip to main content

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