polkavm_assembler/
amd64.rs

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