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    /// Offset the synthetic amode by a fixed offset.
578    pub(crate) fn offset(&self, offset: i32) -> Self {
579        let mut ret = self.clone();
580        match &mut ret {
581            SyntheticAmode::Real(amode) => *amode = amode.offset(offset),
582            SyntheticAmode::SlotOffset { simm32 } => *simm32 += offset,
583            // `amode_offset` is used only in i128.load/store which
584            // takes a synthetic amode from `to_amode`; `to_amode` can
585            // only produce Real or SlotOffset amodes, never
586            // IncomingArg or ConstantOffset.
587            _ => panic!("Cannot offset SyntheticAmode: {self:?}"),
588        }
589        ret
590    }
591}
592
593impl From<Amode> for SyntheticAmode {
594    fn from(amode: Amode) -> SyntheticAmode {
595        SyntheticAmode::Real(amode)
596    }
597}
598
599impl From<VCodeConstant> for SyntheticAmode {
600    fn from(c: VCodeConstant) -> SyntheticAmode {
601        SyntheticAmode::ConstantOffset(c)
602    }
603}
604
605impl PrettyPrint for SyntheticAmode {
606    fn pretty_print(&self, _size: u8) -> String {
607        match self {
608            // See note in `Amode` regarding constant size of `8`.
609            SyntheticAmode::Real(addr) => addr.pretty_print(8),
610            &SyntheticAmode::IncomingArg { offset } => {
611                format!("rbp(stack args max - {offset})")
612            }
613            SyntheticAmode::SlotOffset { simm32 } => {
614                format!("rsp({} + virtual offset)", *simm32)
615            }
616            SyntheticAmode::ConstantOffset(c) => format!("const({})", c.as_u32()),
617        }
618    }
619}
620
621/// An operand which is either an integer Register, a value in Memory or an Immediate.  This can
622/// denote an 8, 16, 32 or 64 bit value.  For the Immediate form, in the 8- and 16-bit case, only
623/// the lower 8 or 16 bits of `simm32` is relevant.  In the 64-bit case, the value denoted by
624/// `simm32` is its sign-extension out to 64 bits.
625#[derive(Clone, Debug)]
626pub enum RegMemImm {
627    /// A register operand.
628    Reg {
629        /// The underlying register.
630        reg: Reg,
631    },
632    /// A memory operand.
633    Mem {
634        /// The memory address.
635        addr: SyntheticAmode,
636    },
637    /// An immediate operand.
638    Imm {
639        /// The immediate value.
640        simm32: u32,
641    },
642}
643
644impl RegMemImm {
645    /// Create a register operand.
646    pub fn reg(reg: Reg) -> Self {
647        debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
648        Self::Reg { reg }
649    }
650
651    /// Create a memory operand.
652    pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
653        Self::Mem { addr: addr.into() }
654    }
655
656    /// Create an immediate operand.
657    pub fn imm(simm32: u32) -> Self {
658        Self::Imm { simm32 }
659    }
660
661    /// Add the regs mentioned by `self` to `collector`.
662    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
663        match self {
664            Self::Reg { reg } => collector.reg_use(reg),
665            Self::Mem { addr } => addr.get_operands(collector),
666            Self::Imm { .. } => {}
667        }
668    }
669}
670
671impl From<RegMem> for RegMemImm {
672    fn from(rm: RegMem) -> RegMemImm {
673        match rm {
674            RegMem::Reg { reg } => RegMemImm::Reg { reg },
675            RegMem::Mem { addr } => RegMemImm::Mem { addr },
676        }
677    }
678}
679
680impl From<Reg> for RegMemImm {
681    fn from(reg: Reg) -> Self {
682        RegMemImm::Reg { reg }
683    }
684}
685
686impl PrettyPrint for RegMemImm {
687    fn pretty_print(&self, size: u8) -> String {
688        match self {
689            Self::Reg { reg } => pretty_print_reg(*reg, size),
690            Self::Mem { addr } => addr.pretty_print(size),
691            Self::Imm { simm32 } => format!("${}", *simm32 as i32),
692        }
693    }
694}
695
696/// An operand which is either an integer Register or a value in Memory.  This can denote an 8, 16,
697/// 32, 64, or 128 bit value.
698#[derive(Clone, Debug)]
699pub enum RegMem {
700    /// A register operand.
701    Reg {
702        /// The underlying register.
703        reg: Reg,
704    },
705    /// A memory operand.
706    Mem {
707        /// The memory address.
708        addr: SyntheticAmode,
709    },
710}
711
712impl RegMem {
713    /// Create a register operand.
714    pub fn reg(reg: Reg) -> Self {
715        debug_assert!(reg.class() == RegClass::Int || reg.class() == RegClass::Float);
716        Self::Reg { reg }
717    }
718
719    /// Create a memory operand.
720    pub fn mem(addr: impl Into<SyntheticAmode>) -> Self {
721        Self::Mem { addr: addr.into() }
722    }
723    /// Asserts that in register mode, the reg class is the one that's expected.
724    pub(crate) fn assert_regclass_is(&self, expected_reg_class: RegClass) {
725        if let Self::Reg { reg } = self {
726            debug_assert_eq!(reg.class(), expected_reg_class);
727        }
728    }
729    /// Add the regs mentioned by `self` to `collector`.
730    pub(crate) fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
731        match self {
732            RegMem::Reg { reg } => collector.reg_use(reg),
733            RegMem::Mem { addr, .. } => addr.get_operands(collector),
734        }
735    }
736}
737
738impl From<Reg> for RegMem {
739    fn from(reg: Reg) -> RegMem {
740        RegMem::Reg { reg }
741    }
742}
743
744impl From<Writable<Reg>> for RegMem {
745    fn from(r: Writable<Reg>) -> Self {
746        RegMem::reg(r.to_reg())
747    }
748}
749
750impl PrettyPrint for RegMem {
751    fn pretty_print(&self, size: u8) -> String {
752        match self {
753            RegMem::Reg { reg } => pretty_print_reg(*reg, size),
754            RegMem::Mem { addr, .. } => addr.pretty_print(size),
755        }
756    }
757}
758
759/// This defines the ways a value can be extended: either signed- or zero-extension, or none for
760/// types that are not extended. Contrast with [ExtMode], which defines the widths from and to which
761/// values can be extended.
762#[derive(Clone, PartialEq)]
763pub enum ExtKind {
764    /// No extension.
765    None,
766    /// Sign-extend.
767    SignExtend,
768    /// Zero-extend.
769    ZeroExtend,
770}
771
772/// These indicate ways of extending (widening) a value, using the Intel
773/// naming: B(yte) = u8, W(ord) = u16, L(ong)word = u32, Q(uad)word = u64
774#[derive(Clone, PartialEq)]
775pub enum ExtMode {
776    /// Byte -> Longword.
777    BL,
778    /// Byte -> Quadword.
779    BQ,
780    /// Word -> Longword.
781    WL,
782    /// Word -> Quadword.
783    WQ,
784    /// Longword -> Quadword.
785    LQ,
786}
787
788impl ExtMode {
789    /// Calculate the `ExtMode` from passed bit lengths of the from/to types.
790    pub(crate) fn new(from_bits: u16, to_bits: u16) -> Option<ExtMode> {
791        match (from_bits, to_bits) {
792            (1, 8) | (1, 16) | (1, 32) | (8, 16) | (8, 32) => Some(ExtMode::BL),
793            (1, 64) | (8, 64) => Some(ExtMode::BQ),
794            (16, 32) => Some(ExtMode::WL),
795            (16, 64) => Some(ExtMode::WQ),
796            (32, 64) => Some(ExtMode::LQ),
797            _ => None,
798        }
799    }
800}
801
802impl fmt::Debug for ExtMode {
803    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
804        let name = match self {
805            ExtMode::BL => "bl",
806            ExtMode::BQ => "bq",
807            ExtMode::WL => "wl",
808            ExtMode::WQ => "wq",
809            ExtMode::LQ => "lq",
810        };
811        write!(fmt, "{name}")
812    }
813}
814
815impl fmt::Display for ExtMode {
816    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
817        fmt::Debug::fmt(self, f)
818    }
819}
820
821/// These indicate condition code tests.  Not all are represented since not all are useful in
822/// compiler-generated code.
823#[derive(Copy, Clone, PartialEq, Eq)]
824#[repr(u8)]
825pub enum CC {
826    ///  overflow
827    O = 0,
828    /// no overflow
829    NO = 1,
830
831    /// < unsigned
832    B = 2,
833    /// >= unsigned
834    NB = 3,
835
836    /// zero
837    Z = 4,
838    /// not-zero
839    NZ = 5,
840
841    /// <= unsigned
842    BE = 6,
843    /// > unsigned
844    NBE = 7,
845
846    /// negative
847    S = 8,
848    /// not-negative
849    NS = 9,
850
851    /// < signed
852    L = 12,
853    /// >= signed
854    NL = 13,
855
856    /// <= signed
857    LE = 14,
858    /// > signed
859    NLE = 15,
860
861    /// parity
862    P = 10,
863
864    /// not parity
865    NP = 11,
866}
867
868impl CC {
869    pub(crate) fn from_intcc(intcc: IntCC) -> Self {
870        match intcc {
871            IntCC::Equal => CC::Z,
872            IntCC::NotEqual => CC::NZ,
873            IntCC::SignedGreaterThanOrEqual => CC::NL,
874            IntCC::SignedGreaterThan => CC::NLE,
875            IntCC::SignedLessThanOrEqual => CC::LE,
876            IntCC::SignedLessThan => CC::L,
877            IntCC::UnsignedGreaterThanOrEqual => CC::NB,
878            IntCC::UnsignedGreaterThan => CC::NBE,
879            IntCC::UnsignedLessThanOrEqual => CC::BE,
880            IntCC::UnsignedLessThan => CC::B,
881        }
882    }
883
884    pub(crate) fn invert(&self) -> Self {
885        match self {
886            CC::O => CC::NO,
887            CC::NO => CC::O,
888
889            CC::B => CC::NB,
890            CC::NB => CC::B,
891
892            CC::Z => CC::NZ,
893            CC::NZ => CC::Z,
894
895            CC::BE => CC::NBE,
896            CC::NBE => CC::BE,
897
898            CC::S => CC::NS,
899            CC::NS => CC::S,
900
901            CC::L => CC::NL,
902            CC::NL => CC::L,
903
904            CC::LE => CC::NLE,
905            CC::NLE => CC::LE,
906
907            CC::P => CC::NP,
908            CC::NP => CC::P,
909        }
910    }
911
912    pub(crate) fn get_enc(self) -> u8 {
913        self as u8
914    }
915}
916
917impl fmt::Debug for CC {
918    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
919        let name = match self {
920            CC::O => "o",
921            CC::NO => "no",
922            CC::B => "b",
923            CC::NB => "nb",
924            CC::Z => "z",
925            CC::NZ => "nz",
926            CC::BE => "be",
927            CC::NBE => "nbe",
928            CC::S => "s",
929            CC::NS => "ns",
930            CC::L => "l",
931            CC::NL => "nl",
932            CC::LE => "le",
933            CC::NLE => "nle",
934            CC::P => "p",
935            CC::NP => "np",
936        };
937        write!(fmt, "{name}")
938    }
939}
940
941impl fmt::Display for CC {
942    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
943        fmt::Debug::fmt(self, f)
944    }
945}
946
947/// Encode the ways that floats can be compared. This is used in float comparisons such as `cmpps`,
948/// e.g.; it is distinguished from other float comparisons (e.g. `ucomiss`) in that those use EFLAGS
949/// whereas [FcmpImm] is used as an immediate.
950#[derive(Clone, Copy)]
951pub enum FcmpImm {
952    /// Equal comparison.
953    Equal = 0x00,
954    /// Less than comparison.
955    LessThan = 0x01,
956    /// Less than or equal comparison.
957    LessThanOrEqual = 0x02,
958    /// Unordered.
959    Unordered = 0x03,
960    /// Not equal comparison.
961    NotEqual = 0x04,
962    /// Unordered of greater than or equal comparison.
963    UnorderedOrGreaterThanOrEqual = 0x05,
964    /// Unordered or greater than comparison.
965    UnorderedOrGreaterThan = 0x06,
966    /// Ordered.
967    Ordered = 0x07,
968}
969
970impl FcmpImm {
971    pub(crate) fn encode(self) -> u8 {
972        self as u8
973    }
974}
975
976impl From<FloatCC> for FcmpImm {
977    fn from(cond: FloatCC) -> Self {
978        match cond {
979            FloatCC::Equal => FcmpImm::Equal,
980            FloatCC::LessThan => FcmpImm::LessThan,
981            FloatCC::LessThanOrEqual => FcmpImm::LessThanOrEqual,
982            FloatCC::Unordered => FcmpImm::Unordered,
983            FloatCC::NotEqual => FcmpImm::NotEqual,
984            FloatCC::UnorderedOrGreaterThanOrEqual => FcmpImm::UnorderedOrGreaterThanOrEqual,
985            FloatCC::UnorderedOrGreaterThan => FcmpImm::UnorderedOrGreaterThan,
986            FloatCC::Ordered => FcmpImm::Ordered,
987            _ => panic!("unable to create comparison predicate for {cond}"),
988        }
989    }
990}
991
992/// Encode the rounding modes used as part of the Rounding Control field.
993/// Note, these rounding immediates only consider the rounding control field
994/// (i.e. the rounding mode) which only take up the first two bits when encoded.
995/// However the rounding immediate which this field helps make up, also includes
996/// bits 3 and 4 which define the rounding select and precision mask respectively.
997/// These two bits are not defined here and are implicitly set to zero when encoded.
998#[derive(Clone, Copy)]
999pub enum RoundImm {
1000    /// Round to nearest mode.
1001    RoundNearest = 0x00,
1002    /// Round down mode.
1003    RoundDown = 0x01,
1004    /// Round up mode.
1005    RoundUp = 0x02,
1006    /// Round to zero mode.
1007    RoundZero = 0x03,
1008}
1009
1010impl RoundImm {
1011    pub(crate) fn encode(self) -> u8 {
1012        self as u8
1013    }
1014}
1015
1016/// An operand's size in bits.
1017#[derive(Clone, Copy, PartialEq)]
1018pub enum OperandSize {
1019    /// 8-bit.
1020    Size8,
1021    /// 16-bit.
1022    Size16,
1023    /// 32-bit.
1024    Size32,
1025    /// 64-bit.
1026    Size64,
1027}
1028
1029impl OperandSize {
1030    pub(crate) fn from_bytes(num_bytes: u32) -> Self {
1031        match num_bytes {
1032            1 => OperandSize::Size8,
1033            2 => OperandSize::Size16,
1034            4 => OperandSize::Size32,
1035            8 => OperandSize::Size64,
1036            _ => unreachable!("Invalid OperandSize: {}", num_bytes),
1037        }
1038    }
1039
1040    // Computes the OperandSize for a given type.
1041    // For vectors, the OperandSize of the lanes is returned.
1042    pub(crate) fn from_ty(ty: Type) -> Self {
1043        Self::from_bytes(ty.lane_type().bytes())
1044    }
1045
1046    // Check that the value of self is one of the allowed sizes.
1047    pub(crate) fn is_one_of(&self, sizes: &[Self]) -> bool {
1048        sizes.iter().any(|val| *self == *val)
1049    }
1050
1051    pub(crate) fn to_bytes(&self) -> u8 {
1052        match self {
1053            Self::Size8 => 1,
1054            Self::Size16 => 2,
1055            Self::Size32 => 4,
1056            Self::Size64 => 8,
1057        }
1058    }
1059
1060    pub(crate) fn to_bits(&self) -> u8 {
1061        self.to_bytes() * 8
1062    }
1063}