polkavm_assembler/
amd64.rs

1#![allow(non_camel_case_types)]
2
3use crate::assembler::{InstBuf, InstFixup, Instruction, Label};
4
5/// The REX prefix.
6const REX: u8 = 0x40;
7const REX_64B_OP: u8 = REX | (1 << 3);
8const REX_EXT_MODRM_REG: u8 = REX | (1 << 2);
9const REX_EXT_MODRM_SIB_INDEX: u8 = REX | (1 << 1);
10const REX_EXT_MODRM_RM: u8 = REX | (1 << 0);
11
12const PREFIX_OVERRIDE_SEGMENT_FS: u8 = 0x64;
13const PREFIX_OVERRIDE_SEGMENT_GS: u8 = 0x65;
14const PREFIX_OVERRIDE_OP_SIZE: u8 = 0x66;
15const PREFIX_OVERRIDE_ADDR_SIZE: u8 = 0x67;
16
17#[derive(Copy, Clone, PartialEq, Eq, Debug)]
18pub enum Reg {
19    rax = 0,
20    rcx = 1,
21    rdx = 2,
22    rbx = 3,
23    rsp = 4,
24    rbp = 5,
25    rsi = 6,
26    rdi = 7,
27    r8 = 8,
28    r9 = 9,
29    r10 = 10,
30    r11 = 11,
31    r12 = 12,
32    r13 = 13,
33    r14 = 14,
34    r15 = 15,
35}
36
37impl Reg {
38    pub const fn is_reg_preserved(self) -> bool {
39        // See page 23 from: https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf
40        use Reg::*;
41        match self {
42            rbx | rsp | rbp | r12 | r13 | r14 | r15 => true,
43            rax | rcx | rdx | rsi | rdi | r8 | r9 | r10 | r11 => false,
44        }
45    }
46
47    #[inline]
48    pub const fn needs_rex(self) -> bool {
49        self as usize >= Reg::r8 as usize
50    }
51
52    #[inline]
53    pub const fn modrm_rm_bits(self) -> u8 {
54        (self as usize & 0b111) as u8
55    }
56
57    #[inline]
58    pub const fn modrm_reg_bits(self) -> u8 {
59        (((self as usize) << 3) & 0b111000) as u8
60    }
61
62    #[inline]
63    pub const fn rex_bit(self) -> u8 {
64        if self as usize >= Reg::r8 as usize {
65            REX_EXT_MODRM_RM
66        } else {
67            0
68        }
69    }
70
71    #[inline]
72    pub const fn rex_modrm_reg(self) -> u8 {
73        if self as usize >= Reg::r8 as usize {
74            REX_EXT_MODRM_REG
75        } else {
76            0
77        }
78    }
79
80    pub const fn name_from(self, size: RegSize) -> &'static str {
81        match size {
82            RegSize::R64 => self.name(),
83            RegSize::R32 => self.name32(),
84        }
85    }
86
87    pub const fn name_from_size(self, kind: Size) -> &'static str {
88        match kind {
89            Size::U64 => self.name(),
90            Size::U32 => self.name32(),
91            Size::U16 => self.name16(),
92            Size::U8 => self.name8(),
93        }
94    }
95}
96
97impl core::fmt::Display for Reg {
98    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
99        fmt.write_str(self.name())
100    }
101}
102
103macro_rules! impl_regs {
104    ($(($r64:ident, $r32:ident, $r16:ident, $r8:ident)),+) => {
105        impl Reg {
106            pub const fn name(self) -> &'static str {
107                match self {
108                    $(
109                        Reg::$r64 => stringify!($r64),
110                    )+
111                }
112            }
113
114            pub const fn name32(self) -> &'static str {
115                match self {
116                    $(
117                        Reg::$r64 => stringify!($r32),
118                    )+
119                }
120            }
121
122            pub const fn name16(self) -> &'static str {
123                match self {
124                    $(
125                        Reg::$r64 => stringify!($r16),
126                    )+
127                }
128            }
129
130            pub const fn name8(self) -> &'static str {
131                match self {
132                    $(
133                        Reg::$r64 => stringify!($r8),
134                    )+
135                }
136            }
137        }
138    };
139}
140
141impl_regs! {
142    (rax, eax, ax, al),
143    (rcx, ecx, cx, cl),
144    (rdx, edx, dx, dl),
145    (rbx, ebx, bx, bl),
146    (rsp, esp, sp, spl),
147    (rbp, ebp, bp, bpl),
148    (rsi, esi, si, sil),
149    (rdi, edi, di, dil),
150    (r8, r8d, r8w, r8b),
151    (r9, r9d, r9w, r9b),
152    (r10, r10d, r10w, r10b),
153    (r11, r11d, r11w, r11b),
154    (r12, r12d, r12w, r12b),
155    (r13, r13d, r13w, r13b),
156    (r14, r14d, r14w, r14b),
157    (r15, r15d, r15w, r15b)
158}
159
160#[derive(Copy, Clone, PartialEq, Eq, Debug)]
161pub enum RegIndex {
162    rax = 0,
163    rcx = 1,
164    rdx = 2,
165    rbx = 3,
166    // No `rsp`.
167    rbp = 5,
168    rsi = 6,
169    rdi = 7,
170    r8 = 8,
171    r9 = 9,
172    r10 = 10,
173    r11 = 11,
174    r12 = 12,
175    r13 = 13,
176    r14 = 14,
177    r15 = 15,
178}
179
180impl From<RegIndex> for Reg {
181    #[inline]
182    fn from(reg: RegIndex) -> Reg {
183        reg.into_reg()
184    }
185}
186
187impl RegIndex {
188    #[inline]
189    pub const fn into_reg(self) -> Reg {
190        match self {
191            RegIndex::rax => Reg::rax,
192            RegIndex::rcx => Reg::rcx,
193            RegIndex::rdx => Reg::rdx,
194            RegIndex::rbx => Reg::rbx,
195            RegIndex::rbp => Reg::rbp,
196            RegIndex::rsi => Reg::rsi,
197            RegIndex::rdi => Reg::rdi,
198            RegIndex::r8 => Reg::r8,
199            RegIndex::r9 => Reg::r9,
200            RegIndex::r10 => Reg::r10,
201            RegIndex::r11 => Reg::r11,
202            RegIndex::r12 => Reg::r12,
203            RegIndex::r13 => Reg::r13,
204            RegIndex::r14 => Reg::r14,
205            RegIndex::r15 => Reg::r15,
206        }
207    }
208    pub const fn name(self) -> &'static str {
209        self.into_reg().name()
210    }
211
212    pub const fn name32(self) -> &'static str {
213        self.into_reg().name32()
214    }
215
216    pub const fn name16(self) -> &'static str {
217        self.into_reg().name16()
218    }
219
220    pub const fn name8(self) -> &'static str {
221        self.into_reg().name8()
222    }
223
224    pub const fn name_from(self, size: RegSize) -> &'static str {
225        match size {
226            RegSize::R64 => self.name(),
227            RegSize::R32 => self.name32(),
228        }
229    }
230}
231
232impl core::fmt::Display for RegIndex {
233    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
234        let reg: Reg = (*self).into();
235        reg.fmt(fmt)
236    }
237}
238
239#[derive(Copy, Clone, PartialEq, Eq, Debug)]
240pub enum SegReg {
241    fs,
242    gs,
243}
244
245impl core::fmt::Display for SegReg {
246    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
247        let name = match *self {
248            Self::fs => "fs",
249            Self::gs => "gs",
250        };
251        fmt.write_str(name)
252    }
253}
254
255#[derive(Copy, Clone, PartialEq, Eq, Debug)]
256pub enum Scale {
257    x1 = 0,
258    x2 = 1,
259    x4 = 2,
260    x8 = 3,
261}
262
263#[derive(Copy, Clone)]
264enum RawImm {
265    Imm8(u8),
266    Imm16(u16),
267    Imm32(u32),
268}
269
270impl RawImm {
271    #[inline(always)]
272    fn append_into(self, buf: &mut InstBuf) {
273        match self {
274            RawImm::Imm8(value) => buf.append(value),
275            RawImm::Imm16(value) => buf.append2(value.to_le_bytes()),
276            RawImm::Imm32(value) => buf.append4(value.to_le_bytes()),
277        }
278    }
279}
280
281#[derive(Copy, Clone, PartialEq, Eq, Debug)]
282pub enum MemOp {
283    /// segment:base + offset
284    BaseOffset(Option<SegReg>, RegSize, Reg, i32),
285    /// segment:base + index * scale + offset
286    BaseIndexScaleOffset(Option<SegReg>, RegSize, Reg, RegIndex, Scale, i32),
287    /// segment:base * scale + offset
288    IndexScaleOffset(Option<SegReg>, RegSize, RegIndex, Scale, i32),
289    /// segment:offset
290    Offset(Option<SegReg>, RegSize, i32),
291    /// segment:rip + offset
292    RipRelative(Option<SegReg>, i32),
293}
294
295#[derive(Copy, Clone, PartialEq, Eq, Debug)]
296pub enum RegMem {
297    Reg(Reg),
298    Mem(MemOp),
299}
300
301#[derive(Copy, Clone, PartialEq, Eq, Debug)]
302pub enum Operands {
303    RegMem_Reg(Size, RegMem, Reg),
304    Reg_RegMem(Size, Reg, RegMem),
305    RegMem_Imm(RegMem, ImmKind),
306}
307
308impl MemOp {
309    #[inline]
310    const fn needs_rex(self) -> bool {
311        match self {
312            MemOp::BaseOffset(_, _, base, _) => base.needs_rex(),
313            MemOp::BaseIndexScaleOffset(_, _, base, index, _, _) => base.needs_rex() || index.into_reg().needs_rex(),
314            MemOp::IndexScaleOffset(_, _, index, _, _) => index.into_reg().needs_rex(),
315            MemOp::Offset(..) => false,
316            MemOp::RipRelative(..) => false,
317        }
318    }
319
320    #[inline]
321    const fn simplify(self) -> Self {
322        match self {
323            // Use a more compact encoding if possible.
324            MemOp::IndexScaleOffset(segment, reg_size, index, Scale::x1, offset) => {
325                MemOp::BaseOffset(segment, reg_size, index.into_reg(), offset)
326            }
327            operand => operand,
328        }
329    }
330}
331
332impl core::fmt::Display for MemOp {
333    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
334        let (segment, base, index, offset_reg_size, offset) = match self.simplify() {
335            MemOp::BaseOffset(segment, reg_size, base, offset) => (segment, Some((reg_size, base)), None, reg_size, offset),
336            MemOp::BaseIndexScaleOffset(segment, reg_size, base, index, scale, offset) => {
337                (segment, Some((reg_size, base)), Some((reg_size, index, scale)), reg_size, offset)
338            }
339            MemOp::IndexScaleOffset(segment, reg_size, index, scale, offset) => {
340                (segment, None, Some((reg_size, index, scale)), reg_size, offset)
341            }
342            MemOp::Offset(segment, reg_size, offset) => (segment, None, None, reg_size, offset),
343            MemOp::RipRelative(segment, offset) => {
344                fmt.write_str("[")?;
345                if let Some(segment) = segment {
346                    fmt.write_fmt(core::format_args!("{}:", segment))?;
347                }
348
349                fmt.write_str("rip")?;
350                if offset != 0 {
351                    if offset > 0 {
352                        fmt.write_fmt(core::format_args!("+0x{:x}", offset))?;
353                    } else {
354                        fmt.write_fmt(core::format_args!("-0x{:x}", -i64::from(offset)))?;
355                    }
356                }
357
358                return fmt.write_str("]");
359            }
360        };
361
362        fmt.write_str("[")?;
363        if let Some(segment) = segment {
364            fmt.write_fmt(core::format_args!("{}:", segment))?;
365        }
366
367        if let Some((reg_size, base)) = base {
368            base.name_from(reg_size).fmt(fmt)?;
369        }
370
371        if let Some((reg_size, index, scale)) = index {
372            if base.is_some() {
373                fmt.write_str("+")?;
374            }
375
376            index.name_from(reg_size).fmt(fmt)?;
377            match scale {
378                Scale::x1 if base.is_some() => {}
379                Scale::x1 => fmt.write_str("*1")?,
380                Scale::x2 => fmt.write_str("*2")?,
381                Scale::x4 => fmt.write_str("*4")?,
382                Scale::x8 => fmt.write_str("*8")?,
383            }
384        }
385
386        if offset != 0 || (base.is_none() && index.is_none()) {
387            if base.is_some() || index.is_some() {
388                if offset > 0 {
389                    fmt.write_fmt(core::format_args!("+0x{:x}", offset))?;
390                } else if offset_reg_size == RegSize::R32 {
391                    if let Some(offset) = offset.checked_neg() {
392                        fmt.write_fmt(core::format_args!("-0x{:x}", offset))?;
393                    } else {
394                        fmt.write_fmt(core::format_args!("-0x{:x}", offset as u32))?;
395                    }
396                } else {
397                    fmt.write_fmt(core::format_args!("-0x{:x}", -i64::from(offset)))?;
398                }
399            } else if offset_reg_size == RegSize::R32 {
400                fmt.write_fmt(core::format_args!("0x{:x}", offset))?;
401            } else {
402                fmt.write_fmt(core::format_args!("0x{:x}", i64::from(offset)))?;
403            }
404        }
405
406        fmt.write_str("]")
407    }
408}
409
410impl RegMem {
411    fn display_without_prefix(self, size: Size) -> impl core::fmt::Display {
412        struct Impl(Size, RegMem);
413
414        impl core::fmt::Display for Impl {
415            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
416                match self.1 {
417                    RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("{}", reg.name_from_size(self.0))),
418                    RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("{}", mem)),
419                }
420            }
421        }
422
423        Impl(size, self)
424    }
425
426    fn display(self, size: Size) -> impl core::fmt::Display {
427        struct Impl(Size, RegMem);
428
429        impl core::fmt::Display for Impl {
430            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
431                match self.1 {
432                    RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("{}", reg.name_from_size(self.0))),
433                    RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("{} {}", self.0.name(), mem)),
434                }
435            }
436        }
437
438        Impl(size, self)
439    }
440}
441
442impl From<Reg> for RegMem {
443    #[inline]
444    fn from(reg: Reg) -> Self {
445        RegMem::Reg(reg)
446    }
447}
448
449impl From<RegIndex> for RegMem {
450    #[inline]
451    fn from(reg: RegIndex) -> Self {
452        RegMem::Reg(reg.into())
453    }
454}
455
456impl From<MemOp> for RegMem {
457    #[inline]
458    fn from(mem: MemOp) -> Self {
459        RegMem::Mem(mem)
460    }
461}
462
463struct Inst {
464    override_op_size: bool,
465    override_addr_size: bool,
466    op_alt: bool,
467    force_enable_modrm: bool,
468    rex: u8,
469    opcode: u8,
470    modrm: u8,
471    sib: u8,
472    displacement: Option<RawImm>,
473    immediate: Option<RawImm>,
474    override_segment: Option<SegReg>,
475}
476
477// See: https://www-user.tu-chemnitz.de/~heha/hsn/chm/x86.chm/x64.htm
478impl Inst {
479    #[inline]
480    const fn new(opcode: u8) -> Self {
481        Inst {
482            override_op_size: false,
483            override_addr_size: false,
484            op_alt: false,
485            force_enable_modrm: false,
486            rex: 0,
487            opcode,
488            modrm: 0,
489            sib: 0,
490            displacement: None,
491            immediate: None,
492            override_segment: None,
493        }
494    }
495
496    #[inline]
497    const fn with_reg_in_op(opcode: u8, reg: Reg) -> Self {
498        Inst::new(opcode | reg.modrm_rm_bits()).rex_from_reg(reg)
499    }
500
501    #[inline]
502    const fn override_op_size(mut self) -> Self {
503        self.override_op_size = true;
504        self
505    }
506
507    #[inline]
508    const fn override_addr_size_if(mut self, cond: bool) -> Self {
509        if cond {
510            self.override_addr_size = true;
511        }
512        self
513    }
514
515    #[inline]
516    const fn op_alt(mut self) -> Self {
517        self.op_alt = true;
518        self
519    }
520
521    #[inline]
522    const fn rex(mut self) -> Self {
523        self.rex |= REX;
524        self
525    }
526
527    #[inline]
528    const fn rex_if(mut self, cond: bool) -> Self {
529        if cond {
530            self = self.rex();
531        }
532        self
533    }
534
535    #[inline]
536    const fn rex_from_reg(mut self, reg: Reg) -> Self {
537        if reg.needs_rex() {
538            self.rex |= REX_EXT_MODRM_RM;
539        }
540        self
541    }
542
543    #[inline]
544    const fn rex_64b(mut self) -> Self {
545        self.rex |= REX_64B_OP;
546        self
547    }
548
549    #[inline]
550    const fn rex_64b_if(mut self, cond: bool) -> Self {
551        if cond {
552            self.rex |= REX_64B_OP;
553        }
554        self
555    }
556
557    #[inline]
558    const fn modrm_rm_direct(mut self, value: Reg) -> Self {
559        if value.needs_rex() {
560            self.rex |= REX_EXT_MODRM_RM;
561        }
562        self.modrm |= value.modrm_rm_bits() | 0b11000000;
563        self
564    }
565
566    #[inline(always)]
567    const fn regmem(self, operand: RegMem) -> Self {
568        match operand {
569            RegMem::Reg(reg) => self.modrm_rm_direct(reg),
570            RegMem::Mem(mem) => self.mem(mem),
571        }
572    }
573
574    #[cfg_attr(not(debug_assertions), inline(always))]
575    const fn mem(mut self, operand: MemOp) -> Self {
576        match operand.simplify() {
577            MemOp::BaseOffset(segment, reg_size, base, offset) => {
578                self.force_enable_modrm = true;
579
580                if base.needs_rex() {
581                    self.rex |= REX_EXT_MODRM_RM;
582                }
583
584                if matches!(base, Reg::rsp | Reg::r12) {
585                    self.sib = 0b00100100;
586                }
587
588                self.modrm |= base.modrm_rm_bits();
589
590                if offset != 0 || matches!(base, Reg::rbp | Reg::r13) {
591                    if offset <= i8::MAX as i32 && offset >= i8::MIN as i32 {
592                        self.modrm |= 0b01000000;
593                        self.displacement = Some(RawImm::Imm8(offset as u8));
594                    } else {
595                        self.modrm |= 0b10000000;
596                        self.displacement = Some(RawImm::Imm32(offset as u32));
597                    }
598                }
599
600                self.override_segment = segment;
601                self.override_addr_size_if(matches!(reg_size, RegSize::R32))
602            }
603            MemOp::BaseIndexScaleOffset(segment, reg_size, base, index, scale, offset) => {
604                if base.needs_rex() {
605                    self.rex |= REX_EXT_MODRM_RM;
606                }
607
608                if index.into_reg().needs_rex() {
609                    self.rex |= REX_EXT_MODRM_SIB_INDEX;
610                }
611
612                self.modrm |= 0b00000100;
613                self.sib |= index.into_reg().modrm_reg_bits();
614                self.sib |= base.modrm_rm_bits();
615                self.sib |= ((scale as usize) << 6) as u8;
616
617                if offset != 0 || matches!(base, Reg::rbp | Reg::r13) {
618                    if offset <= i8::MAX as i32 && offset >= i8::MIN as i32 {
619                        self.modrm |= 0b01000000;
620                        self.displacement = Some(RawImm::Imm8(offset as u8));
621                    } else {
622                        self.modrm |= 0b10000000;
623                        self.displacement = Some(RawImm::Imm32(offset as u32));
624                    }
625                }
626
627                self.override_segment = segment;
628                self.override_addr_size_if(matches!(reg_size, RegSize::R32))
629            }
630            MemOp::IndexScaleOffset(segment, reg_size, index, scale, offset) => {
631                if index.into_reg().needs_rex() {
632                    self.rex |= REX_EXT_MODRM_SIB_INDEX;
633                }
634
635                self.modrm |= 0b00000100;
636                self.sib |= index.into_reg().modrm_reg_bits();
637                self.sib |= 0b00000101;
638                self.sib |= ((scale as usize) << 6) as u8;
639                self.displacement = Some(RawImm::Imm32(offset as u32));
640                self.override_segment = segment;
641                self.override_addr_size_if(matches!(reg_size, RegSize::R32))
642            }
643            MemOp::Offset(segment, reg_size, offset) => {
644                self.modrm |= 0b00000100;
645                self.sib |= 0b00100101;
646                self.displacement = Some(RawImm::Imm32(offset as u32));
647                self.override_segment = segment;
648                self.override_addr_size_if(matches!(reg_size, RegSize::R32) && offset < 0)
649            }
650            MemOp::RipRelative(segment, offset) => {
651                self.modrm |= 0b00000101;
652                self.displacement = Some(RawImm::Imm32(offset as u32));
653                self.override_segment = segment;
654                self
655            }
656        }
657    }
658
659    #[inline]
660    const fn modrm_reg(mut self, value: Reg) -> Self {
661        if value.needs_rex() {
662            self.rex |= REX_EXT_MODRM_REG;
663        }
664        self.modrm |= value.modrm_reg_bits();
665        self.force_enable_modrm = true;
666        self
667    }
668
669    #[inline]
670    const fn modrm_opext(mut self, ext: u8) -> Self {
671        self.modrm |= ext << 3;
672        self.force_enable_modrm = true;
673        self
674    }
675
676    #[inline]
677    const fn imm8(mut self, value: u8) -> Self {
678        self.immediate = Some(RawImm::Imm8(value));
679        self
680    }
681
682    #[inline]
683    const fn imm16(mut self, value: u16) -> Self {
684        self.immediate = Some(RawImm::Imm16(value));
685        self
686    }
687
688    #[inline]
689    const fn imm32(mut self, value: u32) -> Self {
690        self.immediate = Some(RawImm::Imm32(value));
691        self
692    }
693
694    #[inline]
695    fn encode(self) -> InstBuf {
696        let mut enc = InstBuf::new();
697        self.encode_into(&mut enc);
698        enc
699    }
700
701    #[inline(always)]
702    fn encode_into(self, buf: &mut InstBuf) {
703        match self.override_segment {
704            Some(SegReg::fs) => buf.append(PREFIX_OVERRIDE_SEGMENT_FS),
705            Some(SegReg::gs) => buf.append(PREFIX_OVERRIDE_SEGMENT_GS),
706            None => {}
707        }
708
709        if self.override_op_size {
710            buf.append(PREFIX_OVERRIDE_OP_SIZE);
711        }
712
713        if self.override_addr_size {
714            buf.append(PREFIX_OVERRIDE_ADDR_SIZE);
715        }
716
717        if self.rex != 0 {
718            buf.append(self.rex);
719        }
720
721        if self.op_alt {
722            buf.append(0x0f);
723        }
724
725        buf.append(self.opcode);
726
727        if self.modrm != 0 || self.force_enable_modrm {
728            buf.append(self.modrm);
729            if self.modrm & 0b11000000 != 0b11000000 && self.modrm & 0b111 == 0b100 {
730                buf.append(self.sib);
731            }
732        }
733
734        if let Some(displacement) = self.displacement {
735            displacement.append_into(buf);
736        }
737
738        if let Some(immediate) = self.immediate {
739            immediate.append_into(buf);
740        }
741    }
742}
743
744macro_rules! impl_inst {
745    (@generate_test_values $cb:expr, $name:ident, $arg0:ty, $arg1:ty, $arg2:ty, $arg3:ty, $arg4:ty, $arg5:ty) => {
746        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
747            <$arg1 as super::tests::GenerateTestValues>::generate_test_values(|arg1|
748                <$arg2 as super::tests::GenerateTestValues>::generate_test_values(|arg2|
749                    <$arg3 as super::tests::GenerateTestValues>::generate_test_values(|arg3|
750                        <$arg4 as super::tests::GenerateTestValues>::generate_test_values(|arg4|
751                            <$arg5 as super::tests::GenerateTestValues>::generate_test_values(|arg5|
752                                $cb($name(arg0, arg1, arg2, arg3, arg4, arg5))
753                            )
754                        )
755                    )
756                )
757            )
758        )
759    };
760
761    (@generate_test_values $cb:expr, $name:ident, $arg0:ty, $arg1:ty, $arg2:ty, $arg3:ty, $arg4:ty) => {
762        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
763            <$arg1 as super::tests::GenerateTestValues>::generate_test_values(|arg1|
764                <$arg2 as super::tests::GenerateTestValues>::generate_test_values(|arg2|
765                    <$arg3 as super::tests::GenerateTestValues>::generate_test_values(|arg3|
766                        <$arg4 as super::tests::GenerateTestValues>::generate_test_values(|arg4|
767                            $cb($name(arg0, arg1, arg2, arg3, arg4))
768                        )
769                    )
770                )
771            )
772        )
773    };
774
775    (@generate_test_values $cb:expr, $name:ident, $arg0:ty, $arg1:ty, $arg2:ty, $arg3:ty) => {
776        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
777            <$arg1 as super::tests::GenerateTestValues>::generate_test_values(|arg1|
778                <$arg2 as super::tests::GenerateTestValues>::generate_test_values(|arg2|
779                    <$arg3 as super::tests::GenerateTestValues>::generate_test_values(|arg3|
780                        $cb($name(arg0, arg1, arg2, arg3))
781                    )
782                )
783            )
784        )
785    };
786
787    (@generate_test_values $cb:expr, $name:ident, $arg0:ty, $arg1:ty, $arg2:ty) => {
788        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
789            <$arg1 as super::tests::GenerateTestValues>::generate_test_values(|arg1|
790                <$arg2 as super::tests::GenerateTestValues>::generate_test_values(|arg2|
791                    $cb($name(arg0, arg1, arg2))
792                )
793            )
794        )
795    };
796
797    (@generate_test_values $cb:expr, $name:ident, $arg0:ty, $arg1:ty) => {
798        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
799            <$arg1 as super::tests::GenerateTestValues>::generate_test_values(|arg1|
800                $cb($name(arg0, arg1))
801            )
802        )
803    };
804
805    (@generate_test_values $cb:expr, $name:ident, $arg0:ty) => {
806        <$arg0 as super::tests::GenerateTestValues>::generate_test_values(|arg0|
807            $cb($name(arg0))
808        )
809    };
810
811    (@generate_test_values $cb:expr, $name:ident,) => {
812        $cb($name())
813    };
814
815    (@impl |$self:ident, $fmt:ident| $($name:ident($($arg:ty),*) => $body:expr, $fixup:expr, ($fmt_body:expr),)+) => {
816        pub(crate) mod types {
817            use super::*;
818            $(
819                #[derive(Copy, Clone, PartialEq, Eq, Debug)]
820                pub struct $name($(pub $arg),*);
821
822                impl core::fmt::Display for $name {
823                    fn fmt(&$self, $fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
824                        $fmt_body
825                    }
826                }
827
828                impl $name {
829                    #[inline(always)]
830                    pub fn encode($self) -> InstBuf {
831                        $body
832                    }
833
834                    #[inline(always)]
835                    pub fn fixup($self) -> Option<InstFixup> {
836                        let value: Option<(Label, u8, u8)> = $fixup;
837                        value.map(|(target_label, fixup_offset, fixup_length)| InstFixup { target_label, fixup_offset, fixup_length})
838                    }
839                }
840
841                #[cfg(test)]
842                impl super::tests::GenerateTestValues for $name {
843                    fn generate_test_values(mut cb: impl FnMut(Self)) {
844                        impl_inst!(@generate_test_values cb, $name, $($arg),*);
845                    }
846                }
847            )+
848        }
849    };
850
851    (@conv_ty i8) => {
852        i8
853    };
854
855    (@conv_ty i32) => {
856        i32
857    };
858
859    (@conv_ty Label) => {
860        Label
861    };
862
863    (@conv_ty $type:ty) => {
864        impl Into<$type>
865    };
866
867    (@ctor_impl $name:ident, $(($arg_name:ident: $arg_ty:tt)),*) => {
868        #[inline(always)]
869        pub fn $name($($arg_name: impl_inst!(@conv_ty $arg_ty)),*) -> Instruction<types::$name> {
870            let instruction = self::types::$name($($arg_name.into()),*);
871            Instruction {
872                instruction,
873                bytes: instruction.encode(),
874                fixup: instruction.fixup(),
875            }
876        }
877    };
878
879    (@ctor $name:ident,) => {
880        impl_inst!(@ctor_impl $name,);
881    };
882
883    (@ctor $name:ident, $a0:tt) => {
884        impl_inst!(@ctor_impl $name, (a0: $a0));
885    };
886
887    (@ctor $name:ident, $a0:tt, $a1:tt) => {
888        impl_inst!(@ctor_impl $name, (a0: $a0), (a1: $a1));
889    };
890
891    (@ctor $name:ident, $a0:tt, $a1:tt, $a2:tt) => {
892        impl_inst!(@ctor_impl $name, (a0: $a0), (a1: $a1), (a2: $a2));
893    };
894
895    (@ctor $name:ident, $a0:tt, $a1:tt, $a2:tt, $a3:tt) => {
896        impl_inst!(@ctor_impl $name, (a0: $a0), (a1: $a1), (a2: $a2), (a3: $a3));
897    };
898
899    (|$self:ident, $fmt:ident| $($name:ident($($arg:tt),*) => $body:expr, $fixup:expr, ($fmt_body:expr),)+) => {
900        impl_inst!(@impl |$self, $fmt| $($name($($arg),*) => $body, $fixup, ($fmt_body),)+);
901        $(
902            impl_inst!(@ctor $name, $($arg),*);
903        )+
904    };
905}
906
907pub mod addr {
908    use super::*;
909
910    impl core::ops::Add<i32> for Reg {
911        type Output = (Reg, i32);
912
913        #[inline]
914        fn add(self, offset: i32) -> Self::Output {
915            (self, offset)
916        }
917    }
918
919    impl core::ops::Add<i32> for RegIndex {
920        type Output = (RegIndex, i32);
921
922        #[inline]
923        fn add(self, offset: i32) -> Self::Output {
924            (self, offset)
925        }
926    }
927
928    impl core::ops::Sub<i32> for Reg {
929        type Output = (Reg, i32);
930
931        #[inline]
932        fn sub(self, offset: i32) -> Self::Output {
933            (self, -offset)
934        }
935    }
936
937    impl core::ops::Sub<i32> for RegIndex {
938        type Output = (RegIndex, i32);
939
940        #[inline]
941        fn sub(self, offset: i32) -> Self::Output {
942            (self, -offset)
943        }
944    }
945
946    pub trait IntoMemOp {
947        #[doc(hidden)]
948        fn into_mem_op(self, segment: Option<SegReg>, reg_size: RegSize) -> MemOp;
949    }
950
951    impl IntoMemOp for Reg {
952        #[doc(hidden)]
953        #[inline]
954        fn into_mem_op(self, segment: Option<SegReg>, reg_size: RegSize) -> MemOp {
955            MemOp::BaseOffset(segment, reg_size, self, 0)
956        }
957    }
958
959    impl IntoMemOp for RegIndex {
960        #[doc(hidden)]
961        #[inline]
962        fn into_mem_op(self, segment: Option<SegReg>, reg_size: RegSize) -> MemOp {
963            MemOp::BaseOffset(segment, reg_size, self.into(), 0)
964        }
965    }
966
967    impl IntoMemOp for (Reg, i32) {
968        #[doc(hidden)]
969        #[inline]
970        fn into_mem_op(self, segment: Option<SegReg>, reg_size: RegSize) -> MemOp {
971            MemOp::BaseOffset(segment, reg_size, self.0, self.1)
972        }
973    }
974
975    impl IntoMemOp for (RegIndex, i32) {
976        #[doc(hidden)]
977        #[inline]
978        fn into_mem_op(self, segment: Option<SegReg>, reg_size: RegSize) -> MemOp {
979            MemOp::BaseOffset(segment, reg_size, self.0.into(), self.1)
980        }
981    }
982
983    #[inline]
984    pub fn reg_indirect(reg_size: RegSize, op: impl IntoMemOp) -> MemOp {
985        op.into_mem_op(None, reg_size)
986    }
987
988    #[inline]
989    pub fn abs(reg_size: RegSize, offset: i32) -> MemOp {
990        MemOp::Offset(None, reg_size, offset)
991    }
992
993    #[inline]
994    pub fn base_index(reg_size: RegSize, base: impl Into<Reg>, index: RegIndex) -> MemOp {
995        MemOp::BaseIndexScaleOffset(None, reg_size, base.into(), index, Scale::x1, 0)
996    }
997
998    impl From<(RegSize, Reg, Reg)> for Operands {
999        #[inline]
1000        fn from((reg_size, dst, src): (RegSize, Reg, Reg)) -> Self {
1001            Self::RegMem_Reg(reg_size.into(), RegMem::Reg(dst), src)
1002        }
1003    }
1004
1005    impl From<(RegSize, RegIndex, RegIndex)> for Operands {
1006        #[inline]
1007        fn from((reg_size, dst, src): (RegSize, RegIndex, RegIndex)) -> Self {
1008            Self::RegMem_Reg(reg_size.into(), RegMem::Reg(dst.into()), src.into())
1009        }
1010    }
1011
1012    impl From<(RegSize, Reg, MemOp)> for Operands {
1013        #[inline]
1014        fn from((reg_size, dst, src): (RegSize, Reg, MemOp)) -> Self {
1015            Self::Reg_RegMem(reg_size.into(), dst, src.into())
1016        }
1017    }
1018
1019    impl From<(RegSize, RegIndex, MemOp)> for Operands {
1020        #[inline]
1021        fn from((reg_size, dst, src): (RegSize, RegIndex, MemOp)) -> Self {
1022            Self::Reg_RegMem(reg_size.into(), dst.into(), src.into())
1023        }
1024    }
1025
1026    impl From<(Reg, ImmKind)> for Operands {
1027        #[inline]
1028        fn from((dst, imm): (Reg, ImmKind)) -> Self {
1029            Self::RegMem_Imm(RegMem::Reg(dst), imm)
1030        }
1031    }
1032
1033    impl From<(RegIndex, ImmKind)> for Operands {
1034        #[inline]
1035        fn from((dst, imm): (RegIndex, ImmKind)) -> Self {
1036            Self::RegMem_Imm(RegMem::Reg(dst.into()), imm)
1037        }
1038    }
1039
1040    impl From<(MemOp, ImmKind)> for Operands {
1041        #[inline]
1042        fn from((dst, imm): (MemOp, ImmKind)) -> Self {
1043            Self::RegMem_Imm(RegMem::Mem(dst), imm)
1044        }
1045    }
1046
1047    #[inline]
1048    pub fn imm8(value: u8) -> ImmKind {
1049        ImmKind::I8(value)
1050    }
1051
1052    #[inline]
1053    pub fn imm16(value: u16) -> ImmKind {
1054        ImmKind::I16(value)
1055    }
1056
1057    #[inline]
1058    pub fn imm32(value: u32) -> ImmKind {
1059        ImmKind::I32(value)
1060    }
1061
1062    #[inline]
1063    pub fn imm64(value: i32) -> ImmKind {
1064        ImmKind::I64(value)
1065    }
1066}
1067
1068pub mod inst {
1069    use super::*;
1070    use crate::assembler::InstBuf;
1071
1072    #[inline(always)]
1073    const fn new_rm(op: u8, size: Size, regmem: RegMem, reg: Option<Reg>) -> Inst {
1074        let inst = match size {
1075            Size::U8 => {
1076                let force_rex = (match regmem {
1077                    RegMem::Mem(_) => false,
1078                    RegMem::Reg(reg) => !matches!(reg, Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx),
1079                }) || (if let Some(reg) = reg {
1080                    !matches!(reg, Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)
1081                } else {
1082                    false
1083                });
1084                Inst::new(op).rex_if(force_rex)
1085            }
1086            Size::U16 => Inst::new(op + 1).override_op_size(),
1087            Size::U32 => Inst::new(op + 1),
1088            Size::U64 => Inst::new(op + 1).rex_64b(),
1089        }
1090        .regmem(regmem);
1091
1092        if let Some(reg) = reg {
1093            inst.modrm_reg(reg)
1094        } else {
1095            inst
1096        }
1097    }
1098
1099    #[inline(always)]
1100    const fn new_rm_imm(op: u8, regmem: RegMem, imm: ImmKind) -> Inst {
1101        let inst = new_rm(op, imm.size(), regmem, None);
1102        match imm {
1103            ImmKind::I8(imm) => inst.imm8(imm),
1104            ImmKind::I16(imm) => inst.imm16(imm),
1105            ImmKind::I32(imm) => inst.imm32(imm),
1106            ImmKind::I64(imm) => inst.imm32(imm as u32),
1107        }
1108    }
1109
1110    #[inline(always)]
1111    fn alu_impl(op_reg2rm: u8, op_rm2reg: u8, opext: u8, operands: Operands) -> InstBuf {
1112        match operands {
1113            Operands::RegMem_Reg(size, dst, src) => new_rm(op_reg2rm, size, dst, Some(src)).encode(),
1114            Operands::Reg_RegMem(size, dst, src) => new_rm(op_rm2reg, size, src, Some(dst)).encode(),
1115            Operands::RegMem_Imm(dst, imm) => match imm {
1116                ImmKind::I8(imm) => Inst::new(0x80)
1117                    .rex_if(!matches!(dst, RegMem::Reg(Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)))
1118                    .imm8(imm),
1119
1120                ImmKind::I16(value) => {
1121                    // These instructions have a special variant which sign extends the immediate,
1122                    // so we can get away with using a shorter immediate if possible.
1123                    if value as i16 <= i16::from(i8::MAX) && value as i16 >= i16::from(i8::MIN) {
1124                        Inst::new(0x83).imm8(value as u8)
1125                    } else {
1126                        Inst::new(0x81).imm16(value)
1127                    }
1128                    .override_op_size()
1129                }
1130                ImmKind::I32(value) => {
1131                    if value as i32 <= i32::from(i8::MAX) && value as i32 >= i32::from(i8::MIN) {
1132                        Inst::new(0x83).imm8(value as u8)
1133                    } else {
1134                        Inst::new(0x81).imm32(value)
1135                    }
1136                }
1137                ImmKind::I64(value) => if value <= i32::from(i8::MAX) && value >= i32::from(i8::MIN) {
1138                    Inst::new(0x83).imm8(value as u8)
1139                } else {
1140                    Inst::new(0x81).imm32(value as u32)
1141                }
1142                .rex_64b(),
1143            }
1144            .modrm_opext(opext)
1145            .regmem(dst)
1146            .encode(),
1147        }
1148    }
1149
1150    fn display_with_operands(fmt: &mut core::fmt::Formatter, inst_name: &str, operands: Operands) -> core::fmt::Result {
1151        fmt.write_str(inst_name)?;
1152        fmt.write_str(" ")?;
1153
1154        match operands {
1155            Operands::RegMem_Reg(reg_size, dst, src) => match dst {
1156                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("{}, {}", mem, src.name_from_size(reg_size))),
1157                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!(
1158                    "{}, {}",
1159                    reg.name_from_size(reg_size),
1160                    src.name_from_size(reg_size)
1161                )),
1162            },
1163            Operands::Reg_RegMem(reg_size, dst, src) => match src {
1164                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("{}, {}", dst.name_from_size(reg_size), mem)),
1165                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!(
1166                    "{}, {}",
1167                    dst.name_from_size(reg_size),
1168                    reg.name_from_size(reg_size)
1169                )),
1170            },
1171            Operands::RegMem_Imm(dst, imm) => {
1172                if matches!(dst, RegMem::Mem(..)) {
1173                    fmt.write_str(imm.size().name())?;
1174                    fmt.write_str(" ")?;
1175                }
1176
1177                fmt.write_fmt(core::format_args!("{}, {imm}", dst.display_without_prefix(imm.size())))
1178            }
1179        }
1180    }
1181
1182    impl_inst! { |self, fmt|
1183        ud2() =>
1184            InstBuf::from_array([0x0f, 0x0b]),
1185            None,
1186            (fmt.write_str("ud2")),
1187
1188        // https://www.felixcloutier.com/x86/endbr64
1189        endbr64() =>
1190            InstBuf::from_array([0xf3, 0x0f, 0x1e, 0xfa]),
1191            None,
1192            (fmt.write_str("endbr64")),
1193
1194        // https://www.felixcloutier.com/x86/syscall
1195        syscall() =>
1196            InstBuf::from_array([0x0f, 0x05]),
1197            None,
1198            (fmt.write_str("syscall")),
1199
1200        // https://www.felixcloutier.com/x86/push
1201        push(Reg) =>
1202            Inst::with_reg_in_op(0x50, self.0).encode(),
1203            None,
1204            (fmt.write_fmt(core::format_args!("push {}", self.0))),
1205
1206        // https://www.felixcloutier.com/x86/pop
1207        pop(Reg) =>
1208            Inst::with_reg_in_op(0x58, self.0).encode(),
1209            None,
1210            (fmt.write_fmt(core::format_args!("pop {}", self.0))),
1211
1212        // https://www.felixcloutier.com/x86/nop
1213        nop() =>
1214            InstBuf::from_array([0x90]),
1215            None,
1216            (fmt.write_str("nop")),
1217
1218        nop2() =>
1219            InstBuf::from_array([0x66, 0x90]),
1220            None,
1221            (fmt.write_str("xchg ax, ax")),
1222
1223        nop3() =>
1224            InstBuf::from_array([0x0f, 0x1f, 0x00]),
1225            None,
1226            (fmt.write_str("nop dword [rax]")),
1227
1228        nop4() =>
1229            InstBuf::from_array([0x0f, 0x1f, 0x40, 0x00]),
1230            None,
1231            (fmt.write_str("nop dword [rax]")),
1232
1233        nop5() =>
1234            InstBuf::from_array([0x0f, 0x1f, 0x44, 0x00, 0x00]),
1235            None,
1236            (fmt.write_str("nop dword [rax+rax]")),
1237
1238        nop6() =>
1239            InstBuf::from_array([0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00]),
1240            None,
1241            (fmt.write_str("nop word [rax+rax]")),
1242
1243        nop7() =>
1244            InstBuf::from_array([0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00]),
1245            None,
1246            (fmt.write_str("nop dword [rax]")), //
1247
1248        nop8() =>
1249            InstBuf::from_array([0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1250            None,
1251            (fmt.write_str("nop dword [rax+rax]")),
1252
1253        nop9() =>
1254            InstBuf::from_array([0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1255            None,
1256            (fmt.write_str("nop word [rax+rax]")), //
1257
1258        nop10() =>
1259            InstBuf::from_array([0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1260            None,
1261            (fmt.write_str("nop word [cs:rax+rax]")),
1262
1263        nop11() =>
1264            InstBuf::from_array([0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1265            None,
1266            (fmt.write_str("nop word [cs:rax+rax]")),
1267
1268        // https://www.felixcloutier.com/x86/ret
1269        ret() =>
1270            InstBuf::from_array([0xc3]),
1271            None,
1272            (fmt.write_str("ret")),
1273
1274        // https://www.felixcloutier.com/x86/mov
1275        // https://www.felixcloutier.com/x86/movzx
1276        // https://www.felixcloutier.com/x86/movsx:movsxd
1277        mov(RegSize, Reg, Reg) =>
1278            Inst::new(0x89).rex_64b_if(matches!(self.0, RegSize::R64)).modrm_rm_direct(self.1).modrm_reg(self.2).encode(),
1279            None,
1280            (fmt.write_fmt(core::format_args!("mov {}, {}", self.1.name_from(self.0), self.2.name_from(self.0)))),
1281
1282        movsxd_32_to_64(Reg, Reg) =>
1283            Inst::new(0x63).rex_64b().modrm_rm_direct(self.1).modrm_reg(self.0).encode(),
1284            None,
1285            (fmt.write_fmt(core::format_args!("movsxd {}, {}", self.0.name(), self.1.name32()))),
1286
1287        mov_imm64(Reg, u64) =>
1288            {
1289                if self.1 <= 0x7fffffff {
1290                    mov_imm(RegMem::Reg(self.0), ImmKind::I32(self.1 as u32)).encode()
1291                } else {
1292                    let xs = self.1.to_le_bytes();
1293                    InstBuf::from_array([
1294                        REX_64B_OP | self.0.rex_bit(),
1295                        0xb8 | self.0.modrm_rm_bits(),
1296                        xs[0], xs[1], xs[2], xs[3], xs[4], xs[5], xs[6], xs[7]
1297                    ])
1298                }
1299            },
1300            None,
1301            ({
1302                if self.1 <= 0x7fffffff {
1303                    mov_imm(RegMem::Reg(self.0), ImmKind::I32(self.1 as u32)).fmt(fmt)
1304                } else {
1305                    fmt.write_fmt(core::format_args!("mov {}, 0x{:x}", self.0, self.1))
1306                }
1307            }),
1308
1309        mov_imm(RegMem, ImmKind) =>
1310            {
1311                match self.0 {
1312                    RegMem::Mem(..) => new_rm_imm(0xc6, self.0, self.1).encode(),
1313                    RegMem::Reg(reg) => {
1314                        match self.1 {
1315                            ImmKind::I8(value) => Inst::with_reg_in_op(0xb0, reg).imm8(value).rex_if(!matches!(reg, Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)),
1316                            ImmKind::I16(value) => Inst::with_reg_in_op(0xb8, reg).imm16(value).override_op_size(),
1317                            ImmKind::I32(value) => Inst::with_reg_in_op(0xb8, reg).imm32(value),
1318                            ImmKind::I64(..) => new_rm_imm(0xc6, self.0, self.1),
1319                        }.encode()
1320                    }
1321                }
1322            },
1323            None,
1324            (display_with_operands(fmt, "mov", Operands::RegMem_Imm(self.0, self.1))),
1325
1326        store(Size, MemOp, Reg) =>
1327            new_rm(0x88, self.0, RegMem::Mem(self.1), Some(self.2)).encode(),
1328            None,
1329            (fmt.write_fmt(core::format_args!("mov {}, {}", self.1, self.2.name_from_size(self.0)))),
1330
1331        load(LoadKind, Reg, MemOp) =>
1332            {
1333                let inst = match self.0 {
1334                    LoadKind::U8 | LoadKind::U16 | LoadKind::I8 | LoadKind::I16 => {
1335                        let op = match self.0 {
1336                            LoadKind::U8 => 0xb6,
1337                            LoadKind::I8 => 0xbe,
1338                            LoadKind::U16 => 0xb7,
1339                            LoadKind::I16 => 0xbf,
1340                            | LoadKind::I32
1341                            | LoadKind::U32
1342                            | LoadKind::U64
1343                                => unreachable!()
1344                        };
1345
1346                        Inst::new(op)
1347                            .op_alt()
1348                            // Use a 32-bit register as that's 1 byte shorter if we don't need the REX prefix.
1349                            .rex_64b_if(!(matches!(self.0, LoadKind::U8 | LoadKind::U16) && !self.1.needs_rex() && !self.2.needs_rex()))
1350                    },
1351                    LoadKind::I32 => Inst::new(0x63).rex_64b(),
1352                    LoadKind::U32 => Inst::new(0x8b),
1353                    LoadKind::U64 => Inst::new(0x8b).rex_64b()
1354                };
1355
1356                inst
1357                    .modrm_reg(self.1)
1358                    .mem(self.2)
1359                    .encode()
1360            },
1361            None,
1362            ({
1363                let (name, kind, size) = match self.0 {
1364                    LoadKind::U8 if !self.1.needs_rex() && !self.2.needs_rex() => (self.1.name32(), "zx", "byte "),
1365                    LoadKind::U16 if !self.1.needs_rex() && !self.2.needs_rex() => (self.1.name32(), "zx", "word "),
1366                    LoadKind::U8 => (self.1.name(), "zx", "byte "),
1367                    LoadKind::I8 => (self.1.name(), "sx", "byte "),
1368                    LoadKind::U16 => (self.1.name(), "zx", "word "),
1369                    LoadKind::U32 => (self.1.name32(), "", ""),
1370                    LoadKind::I16 => (self.1.name(), "sx", "word "),
1371                    LoadKind::I32 => (self.1.name(), "sxd", ""),
1372                    LoadKind::U64 => (self.1.name(), "", ""),
1373                };
1374
1375                fmt.write_fmt(core::format_args!("mov{} {}, {}{}", kind, name, size, self.2))
1376            }),
1377
1378        // https://www.felixcloutier.com/x86/cmovcc
1379        cmov(Condition, RegSize, Reg, RegMem) =>
1380            {
1381                Inst::new(0x40 | self.0 as u8)
1382                    .op_alt()
1383                    .rex_64b_if(matches!(self.1, RegSize::R64))
1384                    .modrm_reg(self.2)
1385                    .regmem(self.3)
1386                    .encode()
1387            },
1388            None,
1389            (fmt.write_fmt(core::format_args!("cmov{} {}, {}", self.0.suffix(), self.2.name_from(self.1), self.3.display_without_prefix(Size::from(self.1))))),
1390
1391        // https://www.felixcloutier.com/x86/add
1392        add(Operands) =>
1393            alu_impl(0x00, 0x02, 0b000, self.0),
1394            None,
1395            (display_with_operands(fmt, "add", self.0)),
1396
1397        // https://www.felixcloutier.com/x86/inc
1398        inc(Size, RegMem) =>
1399            new_rm(0xfe, self.0, self.1, None).encode(),
1400            None,
1401            (fmt.write_fmt(core::format_args!("inc {}", self.1.display(self.0)))),
1402
1403        // https://www.felixcloutier.com/x86/sub
1404        sub(Operands) =>
1405            alu_impl(0x28, 0x2a, 0b101, self.0),
1406            None,
1407            (display_with_operands(fmt, "sub", self.0)),
1408
1409        // https://www.felixcloutier.com/x86/or
1410        or(Operands) =>
1411            alu_impl(0x08, 0x0a, 0b001, self.0),
1412            None,
1413            (display_with_operands(fmt, "or", self.0)),
1414
1415        // https://www.felixcloutier.com/x86/and
1416        and(Operands) =>
1417            alu_impl(0x20, 0x22, 0b100, self.0),
1418            None,
1419            (display_with_operands(fmt, "and", self.0)),
1420
1421        // https://www.felixcloutier.com/x86/xor
1422        xor(Operands) =>
1423            alu_impl(0x30, 0x32, 0b110, self.0),
1424            None,
1425            (display_with_operands(fmt, "xor", self.0)),
1426
1427        // https://www.felixcloutier.com/x86/bts
1428        bts(RegSize, RegMem, u8) =>
1429            Inst::new(0xba).op_alt().modrm_opext(0b101).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).imm8(self.2).encode(),
1430            None,
1431            ({
1432                match self.0 {
1433                    RegSize::R64 => fmt.write_fmt(core::format_args!("bts {}, 0x{:x}", self.1.display(Size::from(self.0)), i64::from(self.2))),
1434                    RegSize::R32 => fmt.write_fmt(core::format_args!("bts {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2)),
1435                }
1436            }),
1437
1438        // https://www.felixcloutier.com/x86/neg
1439        neg(Size, RegMem) =>
1440            new_rm(0xf6, self.0, self.1, None).modrm_opext(0b011).encode(),
1441            None,
1442            (fmt.write_fmt(core::format_args!("neg {}", self.1.display(self.0)))),
1443
1444        // https://www.felixcloutier.com/x86/not
1445        not(Size, RegMem) =>
1446            new_rm(0xf6, self.0, self.1, None).modrm_opext(0b010).encode(),
1447            None,
1448            (fmt.write_fmt(core::format_args!("not {}", self.1.display(self.0)))),
1449
1450        // https://www.felixcloutier.com/x86/cmp
1451        cmp(Operands) =>
1452            alu_impl(0x38, 0x3a, 0b111, self.0),
1453            None,
1454            (display_with_operands(fmt, "cmp", self.0)),
1455
1456        // https://www.felixcloutier.com/x86/sal:sar:shl:shr
1457        sar_cl(RegSize, RegMem) =>
1458            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b111).encode(),
1459            None,
1460            (fmt.write_fmt(core::format_args!("sar {}, cl", self.1.display(Size::from(self.0))))),
1461
1462        sar_imm(RegSize, RegMem, u8) =>
1463            {
1464                if self.2 == 1 {
1465                    Inst::new(0xd1)
1466                } else {
1467                    Inst::new(0xc1).imm8(self.2)
1468                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b111).encode()
1469            },
1470            None,
1471            (fmt.write_fmt(core::format_args!("sar {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1472
1473        shl_cl(RegSize, RegMem) =>
1474            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b100).encode(),
1475            None,
1476            (fmt.write_fmt(core::format_args!("shl {}, cl", self.1.display(Size::from(self.0))))),
1477
1478        shl_imm(RegSize, RegMem, u8) =>
1479            {
1480                if self.2 == 1 {
1481                    Inst::new(0xd1)
1482                } else {
1483                    Inst::new(0xc1).imm8(self.2)
1484                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b100).encode()
1485            },
1486            None,
1487            (fmt.write_fmt(core::format_args!("shl {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1488
1489        shr_cl(RegSize, RegMem) =>
1490            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b101).encode(),
1491            None,
1492            (fmt.write_fmt(core::format_args!("shr {}, cl", self.1.display(Size::from(self.0))))),
1493
1494        shr_imm(RegSize, RegMem, u8) =>
1495            {
1496                if self.2 == 1 {
1497                    Inst::new(0xd1)
1498                } else {
1499                    Inst::new(0xc1).imm8(self.2)
1500                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b101).encode()
1501            },
1502            None,
1503            (fmt.write_fmt(core::format_args!("shr {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1504
1505        // https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
1506        ror_imm(RegSize, RegMem, u8) =>
1507            {
1508                if self.2 == 1 {
1509                    Inst::new(0xd1)
1510                } else {
1511                    Inst::new(0xc1).imm8(self.2)
1512                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b001).encode()
1513            },
1514            None,
1515            (fmt.write_fmt(core::format_args!("ror {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1516
1517        // https://www.felixcloutier.com/x86/test
1518        test(Operands) =>
1519            {
1520                match self.0 {
1521                    Operands::RegMem_Reg(size, regmem, reg) |
1522                    Operands::Reg_RegMem(size, reg, regmem) => new_rm(0x84, size, regmem, Some(reg)).encode(),
1523                    Operands::RegMem_Imm(regmem, imm) => new_rm_imm(0xf6, regmem, imm).encode(),
1524                }
1525            },
1526            None,
1527            ({
1528                let operands = match self.0 {
1529                    Operands::Reg_RegMem(size, reg, regmem) => Operands::RegMem_Reg(size, regmem, reg),
1530                    operands => operands
1531                };
1532
1533                display_with_operands(fmt, "test", operands)
1534            }),
1535
1536        // https://www.felixcloutier.com/x86/imul
1537        imul(RegSize, Reg, RegMem) =>
1538            Inst::new(0xaf).op_alt().rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode(),
1539            None,
1540            (fmt.write_fmt(core::format_args!("imul {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0))))),
1541
1542        imul_imm(RegSize, Reg, RegMem, i32) =>
1543            {
1544                let value = self.3;
1545                if value <= i32::from(i8::MAX) && value >= i32::from(i8::MIN) {
1546                    Inst::new(0x6b).imm8(value as u8)
1547                } else {
1548                    Inst::new(0x69).imm32(value as u32)
1549                }.rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode()
1550            },
1551            None,
1552            ({
1553                struct DisplaySignExtend(RegSize, i32);
1554                impl core::fmt::Display for DisplaySignExtend {
1555                    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1556                        let value = match self.0 {
1557                            RegSize::R64 => i64::from(self.1) as u64,
1558                            RegSize::R32 => u64::from(self.1 as u32),
1559                        };
1560
1561                        fmt.write_fmt(core::format_args!("0x{:x}", value))
1562                    }
1563
1564                }
1565
1566                if RegMem::Reg(self.1) == self.2 {
1567                    fmt.write_fmt(core::format_args!("imul {}, {}", self.1.name_from(self.0), DisplaySignExtend(self.0, self.3)))
1568                } else {
1569                    fmt.write_fmt(core::format_args!("imul {}, {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0)), DisplaySignExtend(self.0, self.3)))
1570                }
1571            }),
1572
1573        // https://www.felixcloutier.com/x86/div
1574        div(RegSize, RegMem) =>
1575            Inst::new(0xf7).modrm_opext(0b110).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1576            None,
1577            (fmt.write_fmt(core::format_args!("div {}", self.1.display(Size::from(self.0))))),
1578
1579        // https://www.felixcloutier.com/x86/idiv
1580        idiv(RegSize, RegMem) =>
1581            Inst::new(0xf7).modrm_opext(0b111).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1582            None,
1583            (fmt.write_fmt(core::format_args!("idiv {}", self.1.display(Size::from(self.0))))),
1584
1585        // https://www.felixcloutier.com/x86/cwd:cdq:cqo
1586        cdq() =>
1587            Inst::new(0x99).encode(),
1588            None,
1589            (fmt.write_str("cdq")),
1590
1591        // https://www.felixcloutier.com/x86/setcc
1592        setcc(Condition, RegMem) =>
1593            {
1594                Inst::new(0x90 | self.0 as u8)
1595                    .rex_if(!matches!(self.1, RegMem::Reg(Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)))
1596                    .op_alt()
1597                    .regmem(self.1)
1598                    .encode()
1599            },
1600            None,
1601            (fmt.write_fmt(core::format_args!("set{} {}", self.0.suffix(), self.1.display_without_prefix(Size::U8)))),
1602
1603        // https://www.felixcloutier.com/x86/lea
1604        lea(RegSize, Reg, MemOp) =>
1605            Inst::new(0x8d)
1606                .rex_64b_if(matches!(self.0, RegSize::R64))
1607                .modrm_reg(self.1)
1608                .mem(self.2).encode(),
1609            None,
1610            (fmt.write_fmt(core::format_args!("lea {}, {}", self.1.name_from(self.0), self.2))),
1611
1612        // https://www.felixcloutier.com/x86/call
1613        call(RegMem) => {
1614            Inst::new(0xff).modrm_opext(0b010).regmem(self.0).encode()
1615        },
1616        None,
1617        ({
1618            match self.0 {
1619                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("call {}", reg)),
1620                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("call qword {}", mem)),
1621            }
1622        }),
1623
1624        call_rel32(i32) =>
1625            Inst::new(0xe8).imm32(self.0 as u32).encode(),
1626            None,
1627            (fmt.write_fmt(core::format_args!("call 0x{:x}", i64::from(self.0).wrapping_add(5)))),
1628
1629        // https://www.felixcloutier.com/x86/jmp
1630        jmp(RegMem) => {
1631            Inst::new(0xff).modrm_opext(0b100).regmem(self.0).encode()
1632        },
1633        None,
1634        ({
1635            match self.0 {
1636                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("jmp {}", reg)),
1637                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("jmp qword {}", mem)),
1638            }
1639        }),
1640
1641        jmp_rel8(i8) =>
1642            Inst::new(0xeb).imm8(self.0 as u8).encode(),
1643            None,
1644            (fmt.write_fmt(core::format_args!("jmp short 0x{:x}", i64::from(self.0).wrapping_add(2)))),
1645
1646        jmp_rel32(i32) =>
1647            Inst::new(0xe9).imm32(self.0 as u32).encode(),
1648            None,
1649            (fmt.write_fmt(core::format_args!("jmp 0x{:x}", i64::from(self.0).wrapping_add(5)))),
1650
1651        // https://www.felixcloutier.com/x86/jcc
1652        jcc_rel8(Condition, i8) =>
1653            Inst::new(0x70 | self.0 as u8).imm8(self.1 as u8).encode(),
1654            None,
1655            (fmt.write_fmt(core::format_args!("j{} short 0x{:x}", self.0.suffix(), i64::from(self.1).wrapping_add(2)))),
1656
1657        jcc_rel32(Condition, i32) =>
1658            Inst::new(0x80 | self.0 as u8).op_alt().imm32(self.1 as u32).encode(),
1659            None,
1660            (fmt.write_fmt(core::format_args!("j{} near 0x{:x}", self.0.suffix(), i64::from(self.1).wrapping_add(6)))),
1661
1662        // (label instructions)
1663        jmp_label8(Label) =>
1664            jmp_rel8(0).encode(),
1665            Some((self.0, 1, 1)),
1666            (fmt.write_fmt(core::format_args!("jmp {}", self.0))),
1667
1668        jmp_label32(Label) =>
1669            jmp_rel32(0).encode(),
1670            Some((self.0, 1, 4)),
1671            (fmt.write_fmt(core::format_args!("jmp {}", self.0))),
1672
1673        call_label32(Label) =>
1674            call_rel32(0).encode(),
1675            Some((self.0, 1, 4)),
1676            (fmt.write_fmt(core::format_args!("call {}", self.0))),
1677
1678        jcc_label8(Condition, Label) =>
1679            jcc_rel8(self.0, 0).encode(),
1680            Some((self.1, 1, 1)),
1681            (fmt.write_fmt(core::format_args!("j{} {}", self.0.suffix(), self.1))),
1682
1683        jcc_label32(Condition, Label) =>
1684            jcc_rel32(self.0, 0).encode(),
1685            Some((self.1, 2, 4)),
1686            (fmt.write_fmt(core::format_args!("j{} {}", self.0.suffix(), self.1))),
1687
1688        lea_rip_label(Reg, Label) =>
1689            lea(RegSize::R64, self.0, MemOp::RipRelative(None, 0)).encode(),
1690            Some((self.1, 3, 4)),
1691            (fmt.write_fmt(core::format_args!("lea {}, [{}]", self.0, self.1))),
1692    }
1693}
1694
1695#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1696pub enum Condition {
1697    Overflow = 0,
1698    NotOverflow = 1,
1699    Below = 2,        // For unsigned values.
1700    AboveOrEqual = 3, // For unsigned values.
1701    Equal = 4,
1702    NotEqual = 5,
1703    BelowOrEqual = 6, // For unsigned values.
1704    Above = 7,        // For unsigned values.
1705    Sign = 8,
1706    NotSign = 9,
1707    Parity = 10,
1708    NotParity = 11,
1709    Less = 12,           // For signed values.
1710    GreaterOrEqual = 13, // For signed values.
1711    LessOrEqual = 14,    // For signed values.
1712    Greater = 15,        // For signed values.
1713}
1714
1715impl Condition {
1716    const fn suffix(self) -> &'static str {
1717        use Condition::*;
1718        match self {
1719            Overflow => "o",
1720            NotOverflow => "no",
1721            Below => "b",
1722            AboveOrEqual => "ae",
1723            Equal => "e",
1724            NotEqual => "ne",
1725            BelowOrEqual => "be",
1726            Above => "a",
1727            Sign => "s",
1728            NotSign => "ns",
1729            Parity => "p",
1730            NotParity => "np",
1731            Less => "l",
1732            GreaterOrEqual => "ge",
1733            LessOrEqual => "le",
1734            Greater => "g",
1735        }
1736    }
1737}
1738
1739#[cfg(test)]
1740impl tests::GenerateTestValues for Condition {
1741    fn generate_test_values(cb: impl FnMut(Self)) {
1742        use Condition::*;
1743        [
1744            Overflow,
1745            NotOverflow,
1746            Below,
1747            AboveOrEqual,
1748            Equal,
1749            NotEqual,
1750            BelowOrEqual,
1751            Above,
1752            Sign,
1753            NotSign,
1754            Parity,
1755            NotParity,
1756            Less,
1757            GreaterOrEqual,
1758            LessOrEqual,
1759            Greater,
1760        ]
1761        .into_iter()
1762        .for_each(cb);
1763    }
1764}
1765
1766#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1767pub enum RegSize {
1768    R32,
1769    R64,
1770}
1771
1772#[cfg(test)]
1773impl tests::GenerateTestValues for RegSize {
1774    fn generate_test_values(cb: impl FnMut(Self)) {
1775        use RegSize::*;
1776        [R32, R64].into_iter().for_each(cb);
1777    }
1778}
1779
1780#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1781pub enum LoadKind {
1782    U8,
1783    U16,
1784    U32,
1785    #[default]
1786    U64,
1787    I8,
1788    I16,
1789    I32,
1790}
1791
1792#[cfg(test)]
1793impl tests::GenerateTestValues for LoadKind {
1794    fn generate_test_values(cb: impl FnMut(Self)) {
1795        use LoadKind::*;
1796        [U8, U16, U32, U64, I8, I16, I32].into_iter().for_each(cb);
1797    }
1798}
1799
1800#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1801pub enum Size {
1802    U8,
1803    U16,
1804    U32,
1805    #[default]
1806    U64,
1807}
1808
1809impl Size {
1810    fn name(self) -> &'static str {
1811        match self {
1812            Size::U8 => "byte",
1813            Size::U16 => "word",
1814            Size::U32 => "dword",
1815            Size::U64 => "qword",
1816        }
1817    }
1818}
1819
1820impl From<RegSize> for Size {
1821    #[inline]
1822    fn from(reg_size: RegSize) -> Size {
1823        match reg_size {
1824            RegSize::R32 => Size::U32,
1825            RegSize::R64 => Size::U64,
1826        }
1827    }
1828}
1829
1830#[cfg(test)]
1831impl tests::GenerateTestValues for Size {
1832    fn generate_test_values(cb: impl FnMut(Self)) {
1833        use Size::*;
1834        [U8, U16, U32, U64].into_iter().for_each(cb);
1835    }
1836}
1837
1838#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1839pub enum ImmKind {
1840    I8(u8),
1841    I16(u16),
1842    I32(u32),
1843    I64(i32),
1844}
1845
1846impl core::fmt::Display for ImmKind {
1847    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1848        match *self {
1849            ImmKind::I64(value) => fmt.write_fmt(core::format_args!("0x{:x}", i64::from(value))),
1850            ImmKind::I32(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
1851            ImmKind::I16(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
1852            ImmKind::I8(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
1853        }
1854    }
1855}
1856
1857impl ImmKind {
1858    #[inline]
1859    const fn size(self) -> Size {
1860        match self {
1861            ImmKind::I8(..) => Size::U8,
1862            ImmKind::I16(..) => Size::U16,
1863            ImmKind::I32(..) => Size::U32,
1864            ImmKind::I64(..) => Size::U64,
1865        }
1866    }
1867}
1868
1869#[cfg(test)]
1870impl tests::GenerateTestValues for ImmKind {
1871    fn generate_test_values(mut cb: impl FnMut(Self)) {
1872        use ImmKind::*;
1873        u8::generate_test_values(|imm| cb(I8(imm)));
1874        u16::generate_test_values(|imm| cb(I16(imm)));
1875        u32::generate_test_values(|imm| cb(I32(imm)));
1876        i32::generate_test_values(|imm| cb(I64(imm)));
1877    }
1878}
1879
1880#[cfg(test)]
1881mod tests {
1882    use alloc::format;
1883    use alloc::string::String;
1884
1885    #[cfg(test)]
1886    pub trait GenerateTestValues: Copy {
1887        fn generate_test_values(cb: impl FnMut(Self));
1888    }
1889
1890    #[cfg(test)]
1891    impl GenerateTestValues for super::Reg {
1892        fn generate_test_values(cb: impl FnMut(Self)) {
1893            use super::Reg::*;
1894            [rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15]
1895                .into_iter()
1896                .for_each(cb);
1897        }
1898    }
1899
1900    impl GenerateTestValues for super::RegIndex {
1901        fn generate_test_values(cb: impl FnMut(Self)) {
1902            use super::RegIndex::*;
1903            [rax, rcx, rdx, rbx, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15]
1904                .into_iter()
1905                .for_each(cb);
1906        }
1907    }
1908
1909    impl GenerateTestValues for super::SegReg {
1910        fn generate_test_values(cb: impl FnMut(Self)) {
1911            use super::SegReg::*;
1912            [fs, gs].into_iter().for_each(cb);
1913        }
1914    }
1915
1916    impl GenerateTestValues for super::Scale {
1917        fn generate_test_values(cb: impl FnMut(Self)) {
1918            use super::Scale::*;
1919            [x1, x2, x4, x8].into_iter().for_each(cb);
1920        }
1921    }
1922
1923    impl GenerateTestValues for super::MemOp {
1924        fn generate_test_values(mut cb: impl FnMut(Self)) {
1925            Option::<super::SegReg>::generate_test_values(|seg_reg| {
1926                super::RegSize::generate_test_values(|reg_size| {
1927                    super::Reg::generate_test_values(|base| {
1928                        i32::generate_test_values(|offset| cb(super::MemOp::BaseOffset(seg_reg, reg_size, base, offset)))
1929                    })
1930                })
1931            });
1932
1933            Option::<super::SegReg>::generate_test_values(|seg_reg| {
1934                super::RegSize::generate_test_values(|reg_size| {
1935                    super::Reg::generate_test_values(|base| {
1936                        super::RegIndex::generate_test_values(|index| {
1937                            super::Scale::generate_test_values(|scale| {
1938                                i32::generate_test_values(|offset| {
1939                                    cb(super::MemOp::BaseIndexScaleOffset(seg_reg, reg_size, base, index, scale, offset))
1940                                })
1941                            })
1942                        })
1943                    })
1944                })
1945            });
1946
1947            Option::<super::SegReg>::generate_test_values(|seg_reg| {
1948                super::RegSize::generate_test_values(|reg_size| {
1949                    super::RegIndex::generate_test_values(|base| {
1950                        super::Scale::generate_test_values(|scale| {
1951                            i32::generate_test_values(|offset| cb(super::MemOp::IndexScaleOffset(seg_reg, reg_size, base, scale, offset)))
1952                        })
1953                    })
1954                })
1955            });
1956
1957            Option::<super::SegReg>::generate_test_values(|seg_reg| {
1958                super::RegSize::generate_test_values(|reg_size| {
1959                    i32::generate_test_values(|offset| cb(super::MemOp::Offset(seg_reg, reg_size, offset)))
1960                })
1961            });
1962
1963            Option::<super::SegReg>::generate_test_values(|seg_reg| {
1964                i32::generate_test_values(|offset| cb(super::MemOp::RipRelative(seg_reg, offset)))
1965            });
1966        }
1967    }
1968
1969    impl GenerateTestValues for super::RegMem {
1970        fn generate_test_values(mut cb: impl FnMut(Self)) {
1971            super::Reg::generate_test_values(|reg| cb(super::RegMem::Reg(reg)));
1972            super::MemOp::generate_test_values(|mem| cb(super::RegMem::Mem(mem)));
1973        }
1974    }
1975
1976    impl GenerateTestValues for super::Operands {
1977        fn generate_test_values(mut cb: impl FnMut(Self)) {
1978            super::RegMem::generate_test_values(|regmem| {
1979                super::Size::generate_test_values(|size| {
1980                    super::Reg::generate_test_values(|reg| {
1981                        cb(super::Operands::RegMem_Reg(size, regmem, reg));
1982                        cb(super::Operands::Reg_RegMem(size, reg, regmem));
1983                    });
1984                });
1985
1986                super::ImmKind::generate_test_values(|imm| {
1987                    cb(super::Operands::RegMem_Imm(regmem, imm));
1988                });
1989            });
1990        }
1991    }
1992
1993    impl GenerateTestValues for crate::Label {
1994        fn generate_test_values(_: impl FnMut(Self)) {
1995            unimplemented!();
1996        }
1997    }
1998
1999    impl GenerateTestValues for () {
2000        fn generate_test_values(mut cb: impl FnMut(Self)) {
2001            cb(())
2002        }
2003    }
2004
2005    #[cfg(test)]
2006    impl<T> GenerateTestValues for Option<T>
2007    where
2008        T: GenerateTestValues,
2009    {
2010        fn generate_test_values(mut cb: impl FnMut(Self)) {
2011            cb(None);
2012            T::generate_test_values(move |value| cb(Some(value)))
2013        }
2014    }
2015
2016    #[cfg(test)]
2017    impl GenerateTestValues for u8 {
2018        fn generate_test_values(cb: impl FnMut(Self)) {
2019            [0, 1, 31, 0x7f, 0x80, 0x81, 0xfe, 0xff].into_iter().for_each(cb);
2020        }
2021    }
2022
2023    #[cfg(test)]
2024    impl GenerateTestValues for i8 {
2025        fn generate_test_values(mut cb: impl FnMut(Self)) {
2026            u8::generate_test_values(|value| cb(value as i8))
2027        }
2028    }
2029
2030    #[cfg(test)]
2031    impl GenerateTestValues for u16 {
2032        fn generate_test_values(cb: impl FnMut(Self)) {
2033            [
2034                0, 0x7f, 0x80, 0x81, 0xfe, 0xff, 0x100, 0x101, 0x7fff, 0x8000, 0x8001, 0xfffe, 0xffff,
2035            ]
2036            .into_iter()
2037            .for_each(cb);
2038        }
2039    }
2040
2041    #[cfg(test)]
2042    impl GenerateTestValues for i16 {
2043        fn generate_test_values(mut cb: impl FnMut(Self)) {
2044            u16::generate_test_values(|value| cb(value as i16))
2045        }
2046    }
2047
2048    #[cfg(test)]
2049    impl GenerateTestValues for u32 {
2050        fn generate_test_values(cb: impl FnMut(Self)) {
2051            [
2052                0, 0x7f, 0x80, 0x81, 0xfe, 0xff, 0x100, 0x101, 0x7fff, 0x8000, 0x8001, 0xfffe, 0xffff, 0x10000, 0x10001, 0x7fffffff,
2053                0x80000000, 0x80000001, 0xfffffffe, 0xffffffff,
2054            ]
2055            .into_iter()
2056            .for_each(cb);
2057        }
2058    }
2059
2060    #[cfg(test)]
2061    impl GenerateTestValues for i32 {
2062        fn generate_test_values(mut cb: impl FnMut(Self)) {
2063            u32::generate_test_values(|value| cb(value as i32))
2064        }
2065    }
2066
2067    #[cfg(test)]
2068    impl GenerateTestValues for u64 {
2069        fn generate_test_values(cb: impl FnMut(Self)) {
2070            [
2071                0,
2072                0x7f,
2073                0x80,
2074                0x81,
2075                0xfe,
2076                0xff,
2077                0x100,
2078                0x101,
2079                0x7fff,
2080                0x8000,
2081                0x8001,
2082                0xfffe,
2083                0xffff,
2084                0x10000,
2085                0x10001,
2086                0x7fffffff,
2087                0x80000000,
2088                0x80000001,
2089                0xfffffffe,
2090                0xffffffff,
2091                0x100000000,
2092                0x100000001,
2093                0x7fffffffffffffff,
2094                0x8000000000000000,
2095                0x8000000000000001,
2096                0xfffffffffffffffe,
2097                0xffffffffffffffff,
2098            ]
2099            .into_iter()
2100            .for_each(cb);
2101        }
2102    }
2103
2104    fn disassemble(code: &[u8]) -> String {
2105        let mut output = String::new();
2106        disassemble_into(code, &mut output);
2107        output
2108    }
2109
2110    fn disassemble_into(mut code: &[u8], output: &mut String) {
2111        use core::fmt::Write;
2112        use iced_x86::Formatter;
2113
2114        let mut formatter = iced_x86::NasmFormatter::new();
2115        formatter.options_mut().set_space_after_operand_separator(true);
2116        formatter.options_mut().set_hex_prefix("0x");
2117        formatter.options_mut().set_hex_suffix("");
2118        formatter.options_mut().set_uppercase_hex(false);
2119        formatter.options_mut().set_small_hex_numbers_in_decimal(false);
2120        formatter.options_mut().set_show_useless_prefixes(true);
2121        formatter.options_mut().set_branch_leading_zeros(false);
2122        formatter.options_mut().set_rip_relative_addresses(true);
2123        let code_origin = 0;
2124        let mut position = 0;
2125        loop {
2126            let mut decoder = iced_x86::Decoder::with_ip(64, code, code_origin, iced_x86::DecoderOptions::NONE);
2127            if !decoder.can_decode() {
2128                break;
2129            }
2130            let mut instruction = iced_x86::Instruction::default();
2131            decoder.decode_out(&mut instruction);
2132
2133            write!(output, "{:08x} ", position).unwrap();
2134            let start_index = (instruction.ip() - code_origin) as usize;
2135            let instr_bytes = &code[start_index..start_index + instruction.len()];
2136            for b in instr_bytes.iter() {
2137                write!(output, "{:02x}", b).unwrap();
2138            }
2139
2140            output.push(' ');
2141            formatter.format(&instruction, output);
2142            output.push('\n');
2143            code = &code[instruction.len()..];
2144            position += instruction.len();
2145        }
2146
2147        output.pop();
2148    }
2149
2150    struct TestAsm {
2151        asm: crate::Assembler,
2152        disassembly_1: String,
2153        disassembly_2: String,
2154    }
2155
2156    impl TestAsm {
2157        fn new() -> Self {
2158            Self {
2159                asm: crate::Assembler::new(),
2160                disassembly_1: String::new(),
2161                disassembly_2: String::new(),
2162            }
2163        }
2164
2165        fn run<T>(&mut self, inst: crate::Instruction<T>)
2166        where
2167            T: Copy + core::fmt::Display + core::fmt::Debug,
2168        {
2169            use core::fmt::Write;
2170
2171            self.asm.clear();
2172            self.disassembly_1.clear();
2173            self.disassembly_2.clear();
2174
2175            let position = self.asm.len();
2176            self.asm.push(inst);
2177            let ranges = [(inst, position..self.asm.len())];
2178
2179            let code = self.asm.finalize();
2180            let mut position = 0;
2181            for (inst, range) in ranges {
2182                write!(&mut self.disassembly_1, "{:08x} ", position).unwrap();
2183                for &b in &code[range.clone()] {
2184                    write!(&mut self.disassembly_1, "{:02x}", b).unwrap();
2185                }
2186                position += range.len();
2187                writeln!(&mut self.disassembly_1, " {}", inst).unwrap();
2188            }
2189
2190            self.disassembly_1.pop();
2191            disassemble_into(&code, &mut self.disassembly_2);
2192            assert_eq!(self.disassembly_1, self.disassembly_2, "broken encoding for: {inst:?}");
2193        }
2194    }
2195
2196    macro_rules! generate_tests {
2197        ($($inst_name:ident,)+) => {
2198            $(
2199                #[test]
2200                fn $inst_name() {
2201                    let mut test = TestAsm::new();
2202                    <super::inst::types::$inst_name as GenerateTestValues>::generate_test_values(|instruction| {
2203                        test.run(crate::Instruction {
2204                            bytes: instruction.encode(),
2205                            fixup: None,
2206                            instruction
2207                        })
2208                    });
2209                }
2210            )+
2211        }
2212    }
2213
2214    generate_tests! {
2215        add,
2216        and,
2217        bts,
2218        call_rel32,
2219        call,
2220        cdq,
2221        cmov,
2222        cmp,
2223        div,
2224        endbr64,
2225        idiv,
2226        imul_imm,
2227        imul,
2228        inc,
2229        jcc_rel32,
2230        jcc_rel8,
2231        jmp_rel32,
2232        jmp_rel8,
2233        jmp,
2234        lea,
2235        load,
2236        mov_imm,
2237        mov_imm64,
2238        mov,
2239        movsxd_32_to_64,
2240        neg,
2241        nop,
2242        nop10,
2243        nop11,
2244        nop2,
2245        nop3,
2246        nop4,
2247        nop5,
2248        nop6,
2249        nop7,
2250        nop8,
2251        nop9,
2252        not,
2253        or,
2254        pop,
2255        push,
2256        ret,
2257        ror_imm,
2258        sar_cl,
2259        sar_imm,
2260        setcc,
2261        shl_cl,
2262        shl_imm,
2263        shr_cl,
2264        shr_imm,
2265        store,
2266        sub,
2267        syscall,
2268        test,
2269        ud2,
2270        xor,
2271    }
2272
2273    #[test]
2274    fn jmp_label8_infinite_loop() {
2275        use super::inst::*;
2276        let mut asm = crate::Assembler::new();
2277        let label = asm.forward_declare_label();
2278        asm.push_with_label(label, jmp_label8(label));
2279        let disassembly = disassemble(&asm.finalize());
2280        assert_eq!(disassembly, "00000000 ebfe jmp short 0x0");
2281    }
2282
2283    #[test]
2284    fn jmp_label32_infinite_loop() {
2285        use super::inst::*;
2286        let mut asm = crate::Assembler::new();
2287        let label = asm.forward_declare_label();
2288        asm.push_with_label(label, jmp_label32(label));
2289        let disassembly = disassemble(&asm.finalize());
2290        assert_eq!(disassembly, "00000000 e9fbffffff jmp 0x0");
2291    }
2292
2293    #[test]
2294    fn call_label32_infinite_loop() {
2295        use super::inst::*;
2296        let mut asm = crate::Assembler::new();
2297        let label = asm.forward_declare_label();
2298        asm.push_with_label(label, call_label32(label));
2299        let disassembly = disassemble(&asm.finalize());
2300        assert_eq!(disassembly, "00000000 e8fbffffff call 0x0");
2301    }
2302
2303    #[test]
2304    fn jcc_label8_infinite_loop() {
2305        use super::inst::*;
2306        super::Condition::generate_test_values(|cond| {
2307            let mut asm = crate::Assembler::new();
2308            let label = asm.forward_declare_label();
2309            asm.push_with_label(label, jcc_label8(cond, label));
2310            let disassembly = disassemble(&asm.finalize());
2311            assert_eq!(
2312                disassembly,
2313                format!("00000000 {:02x}fe j{} short 0x0", 0x70 + cond as u8, cond.suffix())
2314            );
2315        });
2316    }
2317
2318    #[test]
2319    fn jcc_label8_jump_forward() {
2320        use super::inst::*;
2321        super::Condition::generate_test_values(|cond| {
2322            let mut asm = crate::Assembler::new();
2323            let label = asm.forward_declare_label();
2324            asm.push(jcc_label8(cond, label));
2325            asm.push_with_label(label, nop());
2326            let disassembly = disassemble(&asm.finalize());
2327            assert_eq!(
2328                disassembly,
2329                format!(
2330                    concat!("00000000 {:02x}00 j{} short 0x2\n", "00000002 90 nop",),
2331                    0x70 + cond as u8,
2332                    cond.suffix()
2333                )
2334            );
2335        })
2336    }
2337
2338    #[test]
2339    fn jcc_label8_jump_backward() {
2340        use super::inst::*;
2341        super::Condition::generate_test_values(|cond| {
2342            let mut asm = crate::Assembler::new();
2343            let label = asm.forward_declare_label();
2344            asm.push_with_label(label, nop());
2345            asm.push(jcc_label8(cond, label));
2346            let disassembly = disassemble(&asm.finalize());
2347            assert_eq!(
2348                disassembly,
2349                format!(
2350                    concat!("00000000 90 nop\n", "00000001 {:02x}fd j{} short 0xffffffffffffffff",),
2351                    0x70 + cond as u8,
2352                    cond.suffix()
2353                )
2354            );
2355        });
2356    }
2357
2358    #[test]
2359    fn jcc_label32_jump_forward() {
2360        use super::inst::*;
2361        super::Condition::generate_test_values(|cond| {
2362            let mut asm = crate::Assembler::new();
2363            let label = asm.forward_declare_label();
2364            asm.push(jcc_label32(cond, label));
2365            asm.push_with_label(label, nop());
2366            let disassembly = disassemble(&asm.finalize());
2367            assert_eq!(
2368                disassembly,
2369                format!(
2370                    concat!("00000000 0f{:02x}00000000 j{} near 0x6\n", "00000006 90 nop",),
2371                    0x80 + cond as u8,
2372                    cond.suffix()
2373                )
2374            );
2375        });
2376    }
2377
2378    #[test]
2379    fn lea_rip_label_infinite_loop() {
2380        use super::inst::*;
2381        let mut asm = crate::Assembler::new();
2382        let label = asm.forward_declare_label();
2383        asm.push_with_label(label, lea_rip_label(super::Reg::rax, label));
2384        let disassembly = disassemble(&asm.finalize());
2385        assert_eq!(disassembly, "00000000 488d05f9ffffff lea rax, [rip-0x7]");
2386    }
2387
2388    #[test]
2389    fn lea_rip_label_next_instruction() {
2390        use super::inst::*;
2391        let mut asm = crate::Assembler::new();
2392        let label = asm.forward_declare_label();
2393        asm.push(lea_rip_label(super::Reg::rax, label));
2394        asm.push_with_label(label, nop());
2395        let disassembly = disassemble(&asm.finalize());
2396        assert_eq!(disassembly, "00000000 488d0500000000 lea rax, [rip]\n00000007 90 nop");
2397    }
2398}