1 //! Instruction operand sub-components (aka "parts"): definitions and printing.
2 
3 use super::regs::{self};
4 use crate::ir::MemFlags;
5 use crate::ir::condcodes::{FloatCC, IntCC};
6 use crate::ir::types::*;
7 use crate::isa::x64::inst::Inst;
8 use crate::isa::x64::inst::regs::pretty_print_reg;
9 use crate::machinst::*;
10 use std::fmt;
11 use std::string::String;
12 
13 /// An extension trait for converting `Writable{Xmm,Gpr}` to `Writable<Reg>`.
14 pub 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}`.
20 pub 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 macro_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.
297 newtype_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")]
307 impl 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.
327 newtype_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.
340 pub use crate::isa::x64::lower::isle::generated_code::Amode;
341 
342 impl 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 
457 impl 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)]
487 pub 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 
508 impl 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 
593 impl From<Amode> for SyntheticAmode {
594     fn from(amode: Amode) -> SyntheticAmode {
595         SyntheticAmode::Real(amode)
596     }
597 }
598 
599 impl From<VCodeConstant> for SyntheticAmode {
600     fn from(c: VCodeConstant) -> SyntheticAmode {
601         SyntheticAmode::ConstantOffset(c)
602     }
603 }
604 
605 impl 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)]
626 pub 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 
644 impl 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 
671 impl 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 
680 impl From<Reg> for RegMemImm {
681     fn from(reg: Reg) -> Self {
682         RegMemImm::Reg { reg }
683     }
684 }
685 
686 impl 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)]
699 pub 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 
712 impl 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 
738 impl From<Reg> for RegMem {
739     fn from(reg: Reg) -> RegMem {
740         RegMem::Reg { reg }
741     }
742 }
743 
744 impl From<Writable<Reg>> for RegMem {
745     fn from(r: Writable<Reg>) -> Self {
746         RegMem::reg(r.to_reg())
747     }
748 }
749 
750 impl 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)]
763 pub 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)]
775 pub 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 
788 impl 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 
802 impl 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 
815 impl 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)]
825 pub 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 
868 impl 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 
917 impl 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 
941 impl 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)]
951 pub 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 
970 impl FcmpImm {
971     pub(crate) fn encode(self) -> u8 {
972         self as u8
973     }
974 }
975 
976 impl 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)]
999 pub 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 
1010 impl 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)]
1018 pub enum OperandSize {
1019     /// 8-bit.
1020     Size8,
1021     /// 16-bit.
1022     Size16,
1023     /// 32-bit.
1024     Size32,
1025     /// 64-bit.
1026     Size64,
1027 }
1028 
1029 impl 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 }
1064