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