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/pause
1241        pause() =>
1242            InstBuf::from_array([0xf3, 0x90]),
1243            None,
1244            (fmt.write_str("pause")),
1245
1246        // https://www.felixcloutier.com/x86/nop
1247        nop() =>
1248            InstBuf::from_array([0x90]),
1249            None,
1250            (fmt.write_str("nop")),
1251
1252        nop2() =>
1253            InstBuf::from_array([0x66, 0x90]),
1254            None,
1255            (fmt.write_str("xchg ax, ax")),
1256
1257        nop3() =>
1258            InstBuf::from_array([0x0f, 0x1f, 0x00]),
1259            None,
1260            (fmt.write_str("nop dword [rax]")),
1261
1262        nop4() =>
1263            InstBuf::from_array([0x0f, 0x1f, 0x40, 0x00]),
1264            None,
1265            (fmt.write_str("nop dword [rax]")),
1266
1267        nop5() =>
1268            InstBuf::from_array([0x0f, 0x1f, 0x44, 0x00, 0x00]),
1269            None,
1270            (fmt.write_str("nop dword [rax+rax]")),
1271
1272        nop6() =>
1273            InstBuf::from_array([0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00]),
1274            None,
1275            (fmt.write_str("nop word [rax+rax]")),
1276
1277        nop7() =>
1278            InstBuf::from_array([0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00]),
1279            None,
1280            (fmt.write_str("nop dword [rax]")), //
1281
1282        nop8() =>
1283            InstBuf::from_array([0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1284            None,
1285            (fmt.write_str("nop dword [rax+rax]")),
1286
1287        nop9() =>
1288            InstBuf::from_array([0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1289            None,
1290            (fmt.write_str("nop word [rax+rax]")), //
1291
1292        nop10() =>
1293            InstBuf::from_array([0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1294            None,
1295            (fmt.write_str("nop word [cs:rax+rax]")),
1296
1297        nop11() =>
1298            InstBuf::from_array([0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00]),
1299            None,
1300            (fmt.write_str("nop word [cs:rax+rax]")),
1301
1302        // https://www.felixcloutier.com/x86/clflushopt
1303        clflushopt(MemOp) =>
1304            Inst::new(0xae).override_op_size().op_alt().modrm_opext(0b111).mem(self.0).encode(),
1305            None,
1306            (fmt.write_fmt(core::format_args!("clflushopt {}", self.0))),
1307
1308        // https://www.felixcloutier.com/x86/ret
1309        ret() =>
1310            InstBuf::from_array([0xc3]),
1311            None,
1312            (fmt.write_str("ret")),
1313
1314        // https://www.felixcloutier.com/x86/mov
1315        // https://www.felixcloutier.com/x86/movzx
1316        // https://www.felixcloutier.com/x86/movsx:movsxd
1317        mov(RegSize, Reg, Reg) =>
1318            Inst::new(0x89).rex_64b_if(matches!(self.0, RegSize::R64)).modrm_rm_direct(self.1).modrm_reg(self.2).encode(),
1319            None,
1320            (fmt.write_fmt(core::format_args!("mov {}, {}", self.1.name_from(self.0), self.2.name_from(self.0)))),
1321
1322        movsx_8_to_64(RegSize, Reg, Reg) =>
1323            Inst::new(0xbe).op_alt().rex_64b().modrm_rm_direct(self.2).modrm_reg(self.1).encode(),
1324            None,
1325            (fmt.write_fmt(core::format_args!("movsx {}, {}", self.1.name(), self.2.name8()))),
1326
1327        movsx_16_to_64(RegSize, Reg, Reg) =>
1328            Inst::new(0xbf).op_alt().rex_64b().modrm_rm_direct(self.2).modrm_reg(self.1).encode(),
1329            None,
1330            (fmt.write_fmt(core::format_args!("movsx {}, {}", self.1.name(), self.2.name16()))),
1331
1332        movzx_16_to_64(RegSize, Reg, Reg) =>
1333            Inst::new(0xb7).op_alt().rex_64b().modrm_rm_direct(self.2).modrm_reg(self.1).encode(),
1334            None,
1335            (fmt.write_fmt(core::format_args!("movzx {}, {}", self.1.name(), self.2.name16()))),
1336
1337        movsxd_32_to_64(Reg, Reg) =>
1338            Inst::new(0x63).rex_64b().modrm_rm_direct(self.1).modrm_reg(self.0).encode(),
1339            None,
1340            (fmt.write_fmt(core::format_args!("movsxd {}, {}", self.0.name(), self.1.name32()))),
1341
1342        mov_imm64(Reg, u64) =>
1343            {
1344                if self.1 <= 0x7fffffff {
1345                    mov_imm(RegMem::Reg(self.0), ImmKind::I32(self.1 as u32)).encode()
1346                } else {
1347                    let xs = self.1.to_le_bytes();
1348                    InstBuf::from_array([
1349                        REX_64B_OP | self.0.rex_bit(),
1350                        0xb8 | self.0.modrm_rm_bits(),
1351                        xs[0], xs[1], xs[2], xs[3], xs[4], xs[5], xs[6], xs[7]
1352                    ])
1353                }
1354            },
1355            None,
1356            ({
1357                if self.1 <= 0x7fffffff {
1358                    mov_imm(RegMem::Reg(self.0), ImmKind::I32(self.1 as u32)).fmt(fmt)
1359                } else {
1360                    fmt.write_fmt(core::format_args!("mov {}, 0x{:x}", self.0, self.1))
1361                }
1362            }),
1363
1364        mov_imm(RegMem, ImmKind) =>
1365            {
1366                match self.0 {
1367                    RegMem::Mem(..) => new_rm_imm(0xc6, self.0, self.1).encode(),
1368                    RegMem::Reg(reg) => {
1369                        match self.1 {
1370                            ImmKind::I8(value) => Inst::with_reg_in_op(0xb0, reg).imm8(value).rex_if(!matches!(reg, Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)),
1371                            ImmKind::I16(value) => Inst::with_reg_in_op(0xb8, reg).imm16(value).override_op_size(),
1372                            ImmKind::I32(value) => Inst::with_reg_in_op(0xb8, reg).imm32(value),
1373                            ImmKind::I64(..) => new_rm_imm(0xc6, self.0, self.1),
1374                        }.encode()
1375                    }
1376                }
1377            },
1378            None,
1379            (display_with_operands(fmt, "mov", Operands::RegMem_Imm(self.0, self.1))),
1380
1381        store(Size, MemOp, Reg) =>
1382            new_rm(0x88, self.0, RegMem::Mem(self.1), Some(self.2)).encode(),
1383            None,
1384            (fmt.write_fmt(core::format_args!("mov {}, {}", self.1, self.2.name_from_size(self.0)))),
1385
1386        load(LoadKind, Reg, MemOp) =>
1387            {
1388                let inst = match self.0 {
1389                    LoadKind::U8 | LoadKind::U16 | LoadKind::I8 | LoadKind::I16 => {
1390                        let op = match self.0 {
1391                            LoadKind::U8 => 0xb6,
1392                            LoadKind::I8 => 0xbe,
1393                            LoadKind::U16 => 0xb7,
1394                            LoadKind::I16 => 0xbf,
1395                            | LoadKind::I32
1396                            | LoadKind::U32
1397                            | LoadKind::U64
1398                                => unreachable!()
1399                        };
1400
1401                        Inst::new(op)
1402                            .op_alt()
1403                            // Use a 32-bit register as that's 1 byte shorter if we don't need the REX prefix.
1404                            .rex_64b_if(!(matches!(self.0, LoadKind::U8 | LoadKind::U16) && !self.1.needs_rex() && !self.2.needs_rex()))
1405                    },
1406                    LoadKind::I32 => Inst::new(0x63).rex_64b(),
1407                    LoadKind::U32 => Inst::new(0x8b),
1408                    LoadKind::U64 => Inst::new(0x8b).rex_64b()
1409                };
1410
1411                inst
1412                    .modrm_reg(self.1)
1413                    .mem(self.2)
1414                    .encode()
1415            },
1416            None,
1417            ({
1418                let (name, kind, size) = match self.0 {
1419                    LoadKind::U8 if !self.1.needs_rex() && !self.2.needs_rex() => (self.1.name32(), "zx", "byte "),
1420                    LoadKind::U16 if !self.1.needs_rex() && !self.2.needs_rex() => (self.1.name32(), "zx", "word "),
1421                    LoadKind::U8 => (self.1.name(), "zx", "byte "),
1422                    LoadKind::I8 => (self.1.name(), "sx", "byte "),
1423                    LoadKind::U16 => (self.1.name(), "zx", "word "),
1424                    LoadKind::U32 => (self.1.name32(), "", ""),
1425                    LoadKind::I16 => (self.1.name(), "sx", "word "),
1426                    LoadKind::I32 => (self.1.name(), "sxd", ""),
1427                    LoadKind::U64 => (self.1.name(), "", ""),
1428                };
1429
1430                fmt.write_fmt(core::format_args!("mov{} {}, {}{}", kind, name, size, self.2))
1431            }),
1432
1433        // https://www.felixcloutier.com/x86/cmovcc
1434        cmov(Condition, RegSize, Reg, RegMem) =>
1435            {
1436                Inst::new(0x40 | self.0 as u8)
1437                    .op_alt()
1438                    .rex_64b_if(matches!(self.1, RegSize::R64))
1439                    .modrm_reg(self.2)
1440                    .regmem(self.3)
1441                    .encode()
1442            },
1443            None,
1444            (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))))),
1445
1446        // https://www.felixcloutier.com/x86/xchg
1447        xchg_mem(RegSize, Reg, MemOp) =>
1448            Inst::new(0x87).rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).mem(self.2).encode(),
1449            None,
1450            (fmt.write_fmt(core::format_args!("xchg {}, {}", self.1.name_from(self.0), self.2))),
1451
1452        // https://www.felixcloutier.com/x86/add
1453        add(Operands) =>
1454            alu_impl(0x00, 0x02, 0b000, self.0),
1455            None,
1456            (display_with_operands(fmt, "add", self.0)),
1457
1458        // https://www.felixcloutier.com/x86/inc
1459        inc(Size, RegMem) =>
1460            new_rm(0xfe, self.0, self.1, None).encode(),
1461            None,
1462            (fmt.write_fmt(core::format_args!("inc {}", self.1.display(self.0)))),
1463
1464        // https://www.felixcloutier.com/x86/dec
1465        dec(Size, RegMem) =>
1466            new_rm(0xfe, self.0, self.1, None).modrm_opext(0b001).encode(),
1467            None,
1468            (fmt.write_fmt(core::format_args!("dec {}", self.1.display(self.0)))),
1469
1470        // https://www.felixcloutier.com/x86/sub
1471        sub(Operands) =>
1472            alu_impl(0x28, 0x2a, 0b101, self.0),
1473            None,
1474            (display_with_operands(fmt, "sub", self.0)),
1475
1476        // https://www.felixcloutier.com/x86/or
1477        or(Operands) =>
1478            alu_impl(0x08, 0x0a, 0b001, self.0),
1479            None,
1480            (display_with_operands(fmt, "or", self.0)),
1481
1482        // https://www.felixcloutier.com/x86/and
1483        and(Operands) =>
1484            alu_impl(0x20, 0x22, 0b100, self.0),
1485            None,
1486            (display_with_operands(fmt, "and", self.0)),
1487
1488        // https://www.felixcloutier.com/x86/xor
1489        xor(Operands) =>
1490            alu_impl(0x30, 0x32, 0b110, self.0),
1491            None,
1492            (display_with_operands(fmt, "xor", self.0)),
1493
1494        // https://www.felixcloutier.com/x86/bts
1495        bts(RegSize, RegMem, u8) =>
1496            Inst::new(0xba).op_alt().modrm_opext(0b101).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).imm8(self.2).encode(),
1497            None,
1498            ({
1499                match self.0 {
1500                    RegSize::R64 => fmt.write_fmt(core::format_args!("bts {}, 0x{:x}", self.1.display(Size::from(self.0)), i64::from(self.2))),
1501                    RegSize::R32 => fmt.write_fmt(core::format_args!("bts {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2)),
1502                }
1503            }),
1504
1505        // https://www.felixcloutier.com/x86/neg
1506        neg(Size, RegMem) =>
1507            new_rm(0xf6, self.0, self.1, None).modrm_opext(0b011).encode(),
1508            None,
1509            (fmt.write_fmt(core::format_args!("neg {}", self.1.display(self.0)))),
1510
1511        // https://www.felixcloutier.com/x86/not
1512        not(Size, RegMem) =>
1513            new_rm(0xf6, self.0, self.1, None).modrm_opext(0b010).encode(),
1514            None,
1515            (fmt.write_fmt(core::format_args!("not {}", self.1.display(self.0)))),
1516
1517        // https://www.felixcloutier.com/x86/cmp
1518        cmp(Operands) =>
1519            alu_impl(0x38, 0x3a, 0b111, self.0),
1520            None,
1521            (display_with_operands(fmt, "cmp", self.0)),
1522
1523        // https://www.felixcloutier.com/x86/sal:sar:shl:shr
1524        sar_cl(RegSize, RegMem) =>
1525            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b111).encode(),
1526            None,
1527            (fmt.write_fmt(core::format_args!("sar {}, cl", self.1.display(Size::from(self.0))))),
1528
1529        sar_imm(RegSize, RegMem, u8) =>
1530            {
1531                if self.2 == 1 {
1532                    Inst::new(0xd1)
1533                } else {
1534                    Inst::new(0xc1).imm8(self.2)
1535                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b111).encode()
1536            },
1537            None,
1538            (fmt.write_fmt(core::format_args!("sar {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1539
1540        shl_cl(RegSize, RegMem) =>
1541            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b100).encode(),
1542            None,
1543            (fmt.write_fmt(core::format_args!("shl {}, cl", self.1.display(Size::from(self.0))))),
1544
1545        shl_imm(RegSize, RegMem, u8) =>
1546            {
1547                if self.2 == 1 {
1548                    Inst::new(0xd1)
1549                } else {
1550                    Inst::new(0xc1).imm8(self.2)
1551                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b100).encode()
1552            },
1553            None,
1554            (fmt.write_fmt(core::format_args!("shl {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1555
1556        shr_cl(RegSize, RegMem) =>
1557            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b101).encode(),
1558            None,
1559            (fmt.write_fmt(core::format_args!("shr {}, cl", self.1.display(Size::from(self.0))))),
1560
1561        shr_imm(RegSize, RegMem, u8) =>
1562            {
1563                if self.2 == 1 {
1564                    Inst::new(0xd1)
1565                } else {
1566                    Inst::new(0xc1).imm8(self.2)
1567                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b101).encode()
1568            },
1569            None,
1570            (fmt.write_fmt(core::format_args!("shr {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1571
1572        // https://www.felixcloutier.com/x86/rcl:rcr:rol:ror
1573        ror_imm(RegSize, RegMem, u8) =>
1574            {
1575                if self.2 == 1 {
1576                    Inst::new(0xd1)
1577                } else {
1578                    Inst::new(0xc1).imm8(self.2)
1579                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b001).encode()
1580            },
1581            None,
1582            (fmt.write_fmt(core::format_args!("ror {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1583
1584        rol_cl(RegSize, RegMem) =>
1585            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b000).encode(),
1586            None,
1587            (fmt.write_fmt(core::format_args!("rol {}, cl", self.1.display(Size::from(self.0))))),
1588
1589        ror_cl(RegSize, RegMem) =>
1590            Inst::new(0xd3).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b001).encode(),
1591            None,
1592            (fmt.write_fmt(core::format_args!("ror {}, cl", self.1.display(Size::from(self.0))))),
1593
1594        rcr_imm(RegSize, RegMem, u8) =>
1595            {
1596                if self.2 == 1 {
1597                    Inst::new(0xd1)
1598                } else {
1599                    Inst::new(0xc1).imm8(self.2)
1600                }.rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).modrm_opext(0b011).encode()
1601            },
1602            None,
1603            (fmt.write_fmt(core::format_args!("rcr {}, 0x{:x}", self.1.display(Size::from(self.0)), self.2))),
1604
1605        // https://www.felixcloutier.com/x86/popcnt
1606        popcnt(RegSize, Reg, RegMem) =>
1607            {
1608                Inst::new(0xb8)
1609                    .op_rep_prefix()
1610                    .op_alt()
1611                    .rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode()
1612            },
1613            None,
1614            (fmt.write_fmt(core::format_args!("popcnt {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0))))),
1615
1616        // https://www.felixcloutier.com/x86/lzcnt
1617        lzcnt(RegSize, Reg, RegMem) =>
1618        {
1619            Inst::new(0xbd)
1620                .op_rep_prefix()
1621                .op_alt()
1622                .rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode()
1623        },
1624        None,
1625        (fmt.write_fmt(core::format_args!("lzcnt {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0))))),
1626
1627        // https://www.felixcloutier.com/x86/tzcnt
1628        tzcnt(RegSize, Reg, RegMem) =>
1629        {
1630            Inst::new(0xbc)
1631                .op_rep_prefix()
1632                .op_alt()
1633                .rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode()
1634        },
1635        None,
1636        (fmt.write_fmt(core::format_args!("tzcnt {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0))))),
1637
1638        // https://www.felixcloutier.com/x86/bswap
1639        bswap(RegSize, Reg) =>
1640        {
1641            Inst::with_reg_in_op(0xc8, self.1)
1642                .op_alt()
1643                .rex_64b_if(matches!(self.0, RegSize::R64)).encode()
1644        },
1645        None,
1646        (fmt.write_fmt(core::format_args!("bswap {}", self.1.name_from(self.0)))),
1647
1648        // https://www.felixcloutier.com/x86/test
1649        test(Operands) =>
1650            {
1651                match self.0 {
1652                    Operands::RegMem_Reg(size, regmem, reg) |
1653                    Operands::Reg_RegMem(size, reg, regmem) => new_rm(0x84, size, regmem, Some(reg)).encode(),
1654                    Operands::RegMem_Imm(regmem, imm) => new_rm_imm(0xf6, regmem, imm).encode(),
1655                }
1656            },
1657            None,
1658            ({
1659                let operands = match self.0 {
1660                    Operands::Reg_RegMem(size, reg, regmem) => Operands::RegMem_Reg(size, regmem, reg),
1661                    operands => operands
1662                };
1663
1664                display_with_operands(fmt, "test", operands)
1665            }),
1666
1667        // https://www.felixcloutier.com/x86/imul
1668        imul(RegSize, Reg, RegMem) =>
1669            Inst::new(0xaf).op_alt().rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode(),
1670            None,
1671            (fmt.write_fmt(core::format_args!("imul {}, {}", self.1.name_from(self.0), self.2.display_without_prefix(Size::from(self.0))))),
1672
1673        imul_imm(RegSize, Reg, RegMem, i32) =>
1674            {
1675                let value = self.3;
1676                if value <= i32::from(i8::MAX) && value >= i32::from(i8::MIN) {
1677                    Inst::new(0x6b).imm8(value as u8)
1678                } else {
1679                    Inst::new(0x69).imm32(value as u32)
1680                }.rex_64b_if(matches!(self.0, RegSize::R64)).modrm_reg(self.1).regmem(self.2).encode()
1681            },
1682            None,
1683            ({
1684                struct DisplaySignExtend(RegSize, i32);
1685                impl core::fmt::Display for DisplaySignExtend {
1686                    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1687                        let value = match self.0 {
1688                            RegSize::R64 => i64::from(self.1) as u64,
1689                            RegSize::R32 => u64::from(self.1 as u32),
1690                        };
1691
1692                        fmt.write_fmt(core::format_args!("0x{:x}", value))
1693                    }
1694
1695                }
1696
1697                if RegMem::Reg(self.1) == self.2 {
1698                    fmt.write_fmt(core::format_args!("imul {}, {}", self.1.name_from(self.0), DisplaySignExtend(self.0, self.3)))
1699                } else {
1700                    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)))
1701                }
1702            }),
1703
1704        imul_dx_ax(RegSize, RegMem) =>
1705            Inst::new(0xf7).modrm_opext(0b101).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1706            None,
1707            (fmt.write_fmt(core::format_args!("imul {}", self.1.display(Size::from(self.0))))),
1708
1709        // https://www.felixcloutier.com/x86/mul
1710        mul(RegSize, RegMem) =>
1711            Inst::new(0xf7).modrm_opext(0b100).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1712            None,
1713            (fmt.write_fmt(core::format_args!("mul {}", self.1.display(Size::from(self.0))))),
1714
1715        mul_dx_ax(RegSize, RegMem) =>
1716            Inst::new(0xf7).modrm_opext(0b100).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1717            None,
1718            (fmt.write_fmt(core::format_args!("mul {}", self.1.display(Size::from(self.0))))),
1719
1720        // https://www.felixcloutier.com/x86/div
1721        div(RegSize, RegMem) =>
1722            Inst::new(0xf7).modrm_opext(0b110).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1723            None,
1724            (fmt.write_fmt(core::format_args!("div {}", self.1.display(Size::from(self.0))))),
1725
1726        // https://www.felixcloutier.com/x86/idiv
1727        idiv(RegSize, RegMem) =>
1728            Inst::new(0xf7).modrm_opext(0b111).rex_64b_if(matches!(self.0, RegSize::R64)).regmem(self.1).encode(),
1729            None,
1730            (fmt.write_fmt(core::format_args!("idiv {}", self.1.display(Size::from(self.0))))),
1731
1732        // https://www.felixcloutier.com/x86/cwd:cdq:cqo
1733        cdq() =>
1734            Inst::new(0x99).encode(),
1735            None,
1736            (fmt.write_str("cdq")),
1737
1738        cqo() =>
1739            Inst::new(0x99).rex_64b().encode(),
1740            None,
1741            (fmt.write_str("cqo")),
1742
1743        // https://www.felixcloutier.com/x86/setcc
1744        setcc(Condition, RegMem) =>
1745            {
1746                Inst::new(0x90 | self.0 as u8)
1747                    .rex_if(!matches!(self.1, RegMem::Reg(Reg::rax | Reg::rcx | Reg::rdx | Reg::rbx)))
1748                    .op_alt()
1749                    .regmem(self.1)
1750                    .encode()
1751            },
1752            None,
1753            (fmt.write_fmt(core::format_args!("set{} {}", self.0.suffix(), self.1.display_without_prefix(Size::U8)))),
1754
1755        // https://www.felixcloutier.com/x86/lea
1756        lea(RegSize, Reg, MemOp) =>
1757            Inst::new(0x8d)
1758                .rex_64b_if(matches!(self.0, RegSize::R64))
1759                .modrm_reg(self.1)
1760                .mem(self.2).encode(),
1761            None,
1762            (fmt.write_fmt(core::format_args!("lea {}, {}", self.1.name_from(self.0), self.2))),
1763
1764        // https://www.felixcloutier.com/x86/mfence
1765        mfence() =>
1766            InstBuf::from_array([0x0f, 0xae, 0xf0]),
1767            None,
1768            (fmt.write_str("mfence")),
1769
1770        // https://www.felixcloutier.com/x86/lfence
1771        lfence() =>
1772            InstBuf::from_array([0x0f, 0xae, 0xe8]),
1773            None,
1774            (fmt.write_str("lfence")),
1775
1776        // https://www.felixcloutier.com/x86/rdtscp
1777        rdtscp() =>
1778            InstBuf::from_array([0x0f, 0x01, 0xf9]),
1779            None,
1780            (fmt.write_str("rdtscp")),
1781
1782        // https://www.felixcloutier.com/x86/rdpmc
1783        rdpmc() =>
1784            InstBuf::from_array([0x0f, 0x33]),
1785            None,
1786            (fmt.write_str("rdpmc")),
1787
1788        // https://www.felixcloutier.com/x86/cpuid
1789        cpuid() =>
1790            InstBuf::from_array([0x0f, 0xa2]),
1791            None,
1792            (fmt.write_str("cpuid")),
1793
1794        // https://www.felixcloutier.com/x86/call
1795        call(RegMem) => {
1796            Inst::new(0xff).modrm_opext(0b010).regmem(self.0).encode()
1797        },
1798        None,
1799        ({
1800            match self.0 {
1801                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("call {}", reg)),
1802                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("call qword {}", mem)),
1803            }
1804        }),
1805
1806        call_rel32(i32) =>
1807            Inst::new(0xe8).imm32(self.0 as u32).encode(),
1808            None,
1809            (fmt.write_fmt(core::format_args!("call 0x{:x}", i64::from(self.0).wrapping_add(5)))),
1810
1811        // https://www.felixcloutier.com/x86/jmp
1812        jmp(RegMem) => {
1813            Inst::new(0xff).modrm_opext(0b100).regmem(self.0).encode()
1814        },
1815        None,
1816        ({
1817            match self.0 {
1818                RegMem::Reg(reg) => fmt.write_fmt(core::format_args!("jmp {}", reg)),
1819                RegMem::Mem(mem) => fmt.write_fmt(core::format_args!("jmp qword {}", mem)),
1820            }
1821        }),
1822
1823        jmp_rel8(i8) =>
1824            Inst::new(0xeb).imm8(self.0 as u8).encode(),
1825            None,
1826            (fmt.write_fmt(core::format_args!("jmp short 0x{:x}", i64::from(self.0).wrapping_add(2)))),
1827
1828        jmp_rel32(i32) =>
1829            Inst::new(0xe9).imm32(self.0 as u32).encode(),
1830            None,
1831            (fmt.write_fmt(core::format_args!("jmp 0x{:x}", i64::from(self.0).wrapping_add(5)))),
1832
1833        // https://www.felixcloutier.com/x86/jcc
1834        jcc_rel8(Condition, i8) =>
1835            Inst::new(0x70 | self.0 as u8).imm8(self.1 as u8).encode(),
1836            None,
1837            (fmt.write_fmt(core::format_args!("j{} short 0x{:x}", self.0.suffix(), i64::from(self.1).wrapping_add(2)))),
1838
1839        jcc_rel32(Condition, i32) =>
1840            Inst::new(0x80 | self.0 as u8).op_alt().imm32(self.1 as u32).encode(),
1841            None,
1842            (fmt.write_fmt(core::format_args!("j{} near 0x{:x}", self.0.suffix(), i64::from(self.1).wrapping_add(6)))),
1843
1844        // (label instructions)
1845        jmp_label8(Label) =>
1846            ud2().encode(),
1847            Some((self.0, FixupKind::new_1(0xeb, 1))),
1848            (fmt.write_fmt(core::format_args!("jmp {}", self.0))),
1849
1850        jmp_label32(Label) =>
1851            InstBuf::from_array([0x0f, 0x0b, 0x90, 0x0f, 0x0b]),
1852            Some((self.0, FixupKind::new_1(0xe9, 4))),
1853            (fmt.write_fmt(core::format_args!("jmp {}", self.0))),
1854
1855        call_label32(Label) =>
1856            InstBuf::from_array([0x0f, 0x0b, 0x90, 0x0f, 0x0b]),
1857            Some((self.0, FixupKind::new_1(0xe8, 4))),
1858            (fmt.write_fmt(core::format_args!("call {}", self.0))),
1859
1860        jcc_label8(Condition, Label) =>
1861            ud2().encode(),
1862            Some((self.1, FixupKind::new_1(0x70 | self.0 as u32, 1))),
1863            (fmt.write_fmt(core::format_args!("j{} {}", self.0.suffix(), self.1))),
1864
1865        jcc_label32(Condition, Label) =>
1866            InstBuf::from_array([0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b]),
1867            Some((self.1, FixupKind::new_2([0x0f, 0x80 | self.0 as u32], 4))),
1868            (fmt.write_fmt(core::format_args!("j{} {}", self.0.suffix(), self.1))),
1869
1870        jcc_label32_default(Condition, Label, i32) =>
1871            Inst::new(0x80 | self.0 as u8).op_alt().imm32(self.2 as u32).encode(),
1872            Some((self.1, FixupKind::new_2([0x0f, 0x80 | self.0 as u32], 4))),
1873            (fmt.write_fmt(core::format_args!("j{} {} or near 0x{:x}", self.0.suffix(), self.1, i64::from(self.2).wrapping_add(6)))),
1874
1875        lea_rip_label(Reg, Label) =>
1876            InstBuf::from_array([0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x90]),
1877            {
1878                let inst = Inst::new(0x8d).rex_64b().modrm_reg(self.0).mem(MemOp::RipRelative(None, 0));
1879                Some((self.1, FixupKind::new_3([u32::from(inst.rex), u32::from(inst.opcode), u32::from(inst.modrm)], 4)))
1880            },
1881            (fmt.write_fmt(core::format_args!("lea {}, [{}]", self.0, self.1))),
1882    }
1883}
1884
1885#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1886pub enum Condition {
1887    Overflow = 0,
1888    NotOverflow = 1,
1889    Below = 2,        // For unsigned values.
1890    AboveOrEqual = 3, // For unsigned values.
1891    Equal = 4,
1892    NotEqual = 5,
1893    BelowOrEqual = 6, // For unsigned values.
1894    Above = 7,        // For unsigned values.
1895    Sign = 8,
1896    NotSign = 9,
1897    Parity = 10,
1898    NotParity = 11,
1899    Less = 12,           // For signed values.
1900    GreaterOrEqual = 13, // For signed values.
1901    LessOrEqual = 14,    // For signed values.
1902    Greater = 15,        // For signed values.
1903}
1904
1905impl Condition {
1906    const fn suffix(self) -> &'static str {
1907        use Condition::*;
1908        match self {
1909            Overflow => "o",
1910            NotOverflow => "no",
1911            Below => "b",
1912            AboveOrEqual => "ae",
1913            Equal => "e",
1914            NotEqual => "ne",
1915            BelowOrEqual => "be",
1916            Above => "a",
1917            Sign => "s",
1918            NotSign => "ns",
1919            Parity => "p",
1920            NotParity => "np",
1921            Less => "l",
1922            GreaterOrEqual => "ge",
1923            LessOrEqual => "le",
1924            Greater => "g",
1925        }
1926    }
1927}
1928
1929#[cfg(feature = "alloc")]
1930#[cfg(test)]
1931impl tests::GenerateTestValues for Condition {
1932    fn generate_test_values(cb: impl FnMut(Self)) {
1933        use Condition::*;
1934        [
1935            Overflow,
1936            NotOverflow,
1937            Below,
1938            AboveOrEqual,
1939            Equal,
1940            NotEqual,
1941            BelowOrEqual,
1942            Above,
1943            Sign,
1944            NotSign,
1945            Parity,
1946            NotParity,
1947            Less,
1948            GreaterOrEqual,
1949            LessOrEqual,
1950            Greater,
1951        ]
1952        .into_iter()
1953        .for_each(cb);
1954    }
1955}
1956
1957#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1958pub enum RegSize {
1959    R32,
1960    R64,
1961}
1962
1963#[cfg(feature = "alloc")]
1964#[cfg(test)]
1965impl tests::GenerateTestValues for RegSize {
1966    fn generate_test_values(cb: impl FnMut(Self)) {
1967        use RegSize::*;
1968        [R32, R64].into_iter().for_each(cb);
1969    }
1970}
1971
1972#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1973pub enum LoadKind {
1974    U8,
1975    U16,
1976    U32,
1977    #[default]
1978    U64,
1979    I8,
1980    I16,
1981    I32,
1982}
1983
1984#[cfg(feature = "alloc")]
1985#[cfg(test)]
1986impl tests::GenerateTestValues for LoadKind {
1987    fn generate_test_values(cb: impl FnMut(Self)) {
1988        use LoadKind::*;
1989        [U8, U16, U32, U64, I8, I16, I32].into_iter().for_each(cb);
1990    }
1991}
1992
1993#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
1994pub enum Size {
1995    U8,
1996    U16,
1997    U32,
1998    #[default]
1999    U64,
2000}
2001
2002impl Size {
2003    fn name(self) -> &'static str {
2004        match self {
2005            Size::U8 => "byte",
2006            Size::U16 => "word",
2007            Size::U32 => "dword",
2008            Size::U64 => "qword",
2009        }
2010    }
2011}
2012
2013impl From<RegSize> for Size {
2014    #[inline]
2015    fn from(reg_size: RegSize) -> Size {
2016        match reg_size {
2017            RegSize::R32 => Size::U32,
2018            RegSize::R64 => Size::U64,
2019        }
2020    }
2021}
2022
2023#[cfg(feature = "alloc")]
2024#[cfg(test)]
2025impl tests::GenerateTestValues for Size {
2026    fn generate_test_values(cb: impl FnMut(Self)) {
2027        use Size::*;
2028        [U8, U16, U32, U64].into_iter().for_each(cb);
2029    }
2030}
2031
2032#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2033pub enum ImmKind {
2034    I8(u8),
2035    I16(u16),
2036    I32(u32),
2037    I64(i32),
2038}
2039
2040impl core::fmt::Display for ImmKind {
2041    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
2042        match *self {
2043            ImmKind::I64(value) => fmt.write_fmt(core::format_args!("0x{:x}", i64::from(value))),
2044            ImmKind::I32(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
2045            ImmKind::I16(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
2046            ImmKind::I8(value) => fmt.write_fmt(core::format_args!("0x{:x}", value)),
2047        }
2048    }
2049}
2050
2051impl ImmKind {
2052    #[inline]
2053    const fn size(self) -> Size {
2054        match self {
2055            ImmKind::I8(..) => Size::U8,
2056            ImmKind::I16(..) => Size::U16,
2057            ImmKind::I32(..) => Size::U32,
2058            ImmKind::I64(..) => Size::U64,
2059        }
2060    }
2061}
2062
2063#[cfg(feature = "alloc")]
2064#[cfg(test)]
2065impl tests::GenerateTestValues for ImmKind {
2066    fn generate_test_values(mut cb: impl FnMut(Self)) {
2067        use ImmKind::*;
2068        u8::generate_test_values(|imm| cb(I8(imm)));
2069        u16::generate_test_values(|imm| cb(I16(imm)));
2070        u32::generate_test_values(|imm| cb(I32(imm)));
2071        i32::generate_test_values(|imm| cb(I64(imm)));
2072    }
2073}
2074
2075#[cfg(feature = "alloc")]
2076#[cfg(test)]
2077mod tests {
2078    pub trait GenerateTestValues: Copy {
2079        fn generate_test_values(cb: impl FnMut(Self));
2080    }
2081
2082    impl GenerateTestValues for super::Reg {
2083        fn generate_test_values(cb: impl FnMut(Self)) {
2084            use super::Reg::*;
2085            [rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15]
2086                .into_iter()
2087                .for_each(cb);
2088        }
2089    }
2090
2091    impl GenerateTestValues for super::RegIndex {
2092        fn generate_test_values(cb: impl FnMut(Self)) {
2093            use super::RegIndex::*;
2094            [rax, rcx, rdx, rbx, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15]
2095                .into_iter()
2096                .for_each(cb);
2097        }
2098    }
2099
2100    impl GenerateTestValues for super::SegReg {
2101        fn generate_test_values(cb: impl FnMut(Self)) {
2102            use super::SegReg::*;
2103            [fs, gs].into_iter().for_each(cb);
2104        }
2105    }
2106
2107    impl GenerateTestValues for super::Scale {
2108        fn generate_test_values(cb: impl FnMut(Self)) {
2109            use super::Scale::*;
2110            [x1, x2, x4, x8].into_iter().for_each(cb);
2111        }
2112    }
2113
2114    impl GenerateTestValues for super::MemOp {
2115        fn generate_test_values(mut cb: impl FnMut(Self)) {
2116            Option::<super::SegReg>::generate_test_values(|seg_reg| {
2117                super::RegSize::generate_test_values(|reg_size| {
2118                    super::Reg::generate_test_values(|base| {
2119                        i32::generate_test_values(|offset| cb(super::MemOp::BaseOffset(seg_reg, reg_size, base, offset)))
2120                    })
2121                })
2122            });
2123
2124            Option::<super::SegReg>::generate_test_values(|seg_reg| {
2125                super::RegSize::generate_test_values(|reg_size| {
2126                    super::Reg::generate_test_values(|base| {
2127                        super::RegIndex::generate_test_values(|index| {
2128                            super::Scale::generate_test_values(|scale| {
2129                                i32::generate_test_values(|offset| {
2130                                    cb(super::MemOp::BaseIndexScaleOffset(seg_reg, reg_size, base, index, scale, offset))
2131                                })
2132                            })
2133                        })
2134                    })
2135                })
2136            });
2137
2138            Option::<super::SegReg>::generate_test_values(|seg_reg| {
2139                super::RegSize::generate_test_values(|reg_size| {
2140                    super::RegIndex::generate_test_values(|base| {
2141                        super::Scale::generate_test_values(|scale| {
2142                            i32::generate_test_values(|offset| cb(super::MemOp::IndexScaleOffset(seg_reg, reg_size, base, scale, offset)))
2143                        })
2144                    })
2145                })
2146            });
2147
2148            Option::<super::SegReg>::generate_test_values(|seg_reg| {
2149                super::RegSize::generate_test_values(|reg_size| {
2150                    i32::generate_test_values(|offset| cb(super::MemOp::Offset(seg_reg, reg_size, offset)))
2151                })
2152            });
2153
2154            Option::<super::SegReg>::generate_test_values(|seg_reg| {
2155                i32::generate_test_values(|offset| cb(super::MemOp::RipRelative(seg_reg, offset)))
2156            });
2157        }
2158    }
2159
2160    impl GenerateTestValues for super::RegMem {
2161        fn generate_test_values(mut cb: impl FnMut(Self)) {
2162            super::Reg::generate_test_values(|reg| cb(super::RegMem::Reg(reg)));
2163            super::MemOp::generate_test_values(|mem| cb(super::RegMem::Mem(mem)));
2164        }
2165    }
2166
2167    impl GenerateTestValues for super::Operands {
2168        fn generate_test_values(mut cb: impl FnMut(Self)) {
2169            super::RegMem::generate_test_values(|regmem| {
2170                super::Size::generate_test_values(|size| {
2171                    super::Reg::generate_test_values(|reg| {
2172                        cb(super::Operands::RegMem_Reg(size, regmem, reg));
2173                        cb(super::Operands::Reg_RegMem(size, reg, regmem));
2174                    });
2175                });
2176
2177                super::ImmKind::generate_test_values(|imm| {
2178                    cb(super::Operands::RegMem_Imm(regmem, imm));
2179                });
2180            });
2181        }
2182    }
2183
2184    impl GenerateTestValues for crate::Label {
2185        fn generate_test_values(_: impl FnMut(Self)) {
2186            unimplemented!();
2187        }
2188    }
2189
2190    impl GenerateTestValues for () {
2191        fn generate_test_values(mut cb: impl FnMut(Self)) {
2192            cb(())
2193        }
2194    }
2195
2196    impl<T> GenerateTestValues for Option<T>
2197    where
2198        T: GenerateTestValues,
2199    {
2200        fn generate_test_values(mut cb: impl FnMut(Self)) {
2201            cb(None);
2202            T::generate_test_values(move |value| cb(Some(value)))
2203        }
2204    }
2205
2206    impl GenerateTestValues for u8 {
2207        fn generate_test_values(cb: impl FnMut(Self)) {
2208            [0, 1, 31, 0x7f, 0x80, 0x81, 0xfe, 0xff].into_iter().for_each(cb);
2209        }
2210    }
2211
2212    impl GenerateTestValues for i8 {
2213        fn generate_test_values(mut cb: impl FnMut(Self)) {
2214            u8::generate_test_values(|value| cb(value as i8))
2215        }
2216    }
2217
2218    impl GenerateTestValues for u16 {
2219        fn generate_test_values(cb: impl FnMut(Self)) {
2220            [
2221                0, 1, 0x7f, 0x80, 0x81, 0xfe, 0xff, 0x100, 0x101, 0x7fff, 0x8000, 0x8001, 0xfffe, 0xffff,
2222            ]
2223            .into_iter()
2224            .for_each(cb);
2225        }
2226    }
2227
2228    impl GenerateTestValues for i16 {
2229        fn generate_test_values(mut cb: impl FnMut(Self)) {
2230            u16::generate_test_values(|value| cb(value as i16))
2231        }
2232    }
2233
2234    impl GenerateTestValues for u32 {
2235        fn generate_test_values(cb: impl FnMut(Self)) {
2236            [
2237                0, 1, 0x7f, 0x80, 0x81, 0xfe, 0xff, 0x100, 0x101, 0x7fff, 0x8000, 0x8001, 0xfffe, 0xffff, 0x10000, 0x10001, 0x7fffffff,
2238                0x80000000, 0x80000001, 0xfffffffe, 0xffffffff,
2239            ]
2240            .into_iter()
2241            .for_each(cb);
2242        }
2243    }
2244
2245    impl GenerateTestValues for i32 {
2246        fn generate_test_values(mut cb: impl FnMut(Self)) {
2247            u32::generate_test_values(|value| cb(value as i32))
2248        }
2249    }
2250
2251    impl GenerateTestValues for u64 {
2252        fn generate_test_values(cb: impl FnMut(Self)) {
2253            [
2254                0,
2255                1,
2256                0x7f,
2257                0x80,
2258                0x81,
2259                0xfe,
2260                0xff,
2261                0x100,
2262                0x101,
2263                0x7fff,
2264                0x8000,
2265                0x8001,
2266                0xfffe,
2267                0xffff,
2268                0x10000,
2269                0x10001,
2270                0x7fffffff,
2271                0x80000000,
2272                0x80000001,
2273                0xfffffffe,
2274                0xffffffff,
2275                0x100000000,
2276                0x100000001,
2277                0x7fffffffffffffff,
2278                0x8000000000000000,
2279                0x8000000000000001,
2280                0xfffffffffffffffe,
2281                0xffffffffffffffff,
2282            ]
2283            .into_iter()
2284            .for_each(cb);
2285        }
2286    }
2287
2288    use alloc::format;
2289    use alloc::string::String;
2290
2291    fn disassemble(code: &[u8]) -> String {
2292        let mut output = String::new();
2293        disassemble_into(code, &mut output);
2294        output
2295    }
2296
2297    fn disassemble_into(mut code: &[u8], output: &mut String) {
2298        use core::fmt::Write;
2299        use iced_x86::Formatter;
2300
2301        let mut formatter = iced_x86::NasmFormatter::new();
2302        formatter.options_mut().set_space_after_operand_separator(true);
2303        formatter.options_mut().set_hex_prefix("0x");
2304        formatter.options_mut().set_hex_suffix("");
2305        formatter.options_mut().set_uppercase_hex(false);
2306        formatter.options_mut().set_small_hex_numbers_in_decimal(false);
2307        formatter.options_mut().set_show_useless_prefixes(true);
2308        formatter.options_mut().set_branch_leading_zeros(false);
2309        formatter.options_mut().set_rip_relative_addresses(true);
2310        let code_origin = 0;
2311        let mut position = 0;
2312        loop {
2313            let mut decoder = iced_x86::Decoder::with_ip(64, code, code_origin, iced_x86::DecoderOptions::NONE);
2314            if !decoder.can_decode() {
2315                break;
2316            }
2317            let mut instruction = iced_x86::Instruction::default();
2318            decoder.decode_out(&mut instruction);
2319
2320            write!(output, "{:08x} ", position).unwrap();
2321            let start_index = (instruction.ip() - code_origin) as usize;
2322            let instr_bytes = &code[start_index..start_index + instruction.len()];
2323            for b in instr_bytes.iter() {
2324                write!(output, "{:02x}", b).unwrap();
2325            }
2326
2327            output.push(' ');
2328            formatter.format(&instruction, output);
2329            output.push('\n');
2330            code = &code[instruction.len()..];
2331            position += instruction.len();
2332        }
2333
2334        output.pop();
2335    }
2336
2337    struct TestAsm {
2338        asm: crate::Assembler,
2339        disassembly_1: String,
2340        disassembly_2: String,
2341    }
2342
2343    impl TestAsm {
2344        fn new() -> Self {
2345            Self {
2346                asm: crate::Assembler::new(),
2347                disassembly_1: String::new(),
2348                disassembly_2: String::new(),
2349            }
2350        }
2351
2352        fn run<T>(&mut self, inst: crate::Instruction<T>)
2353        where
2354            T: Copy + core::fmt::Display + core::fmt::Debug,
2355        {
2356            use core::fmt::Write;
2357
2358            self.asm.clear();
2359            self.disassembly_1.clear();
2360            self.disassembly_2.clear();
2361
2362            let position = self.asm.len();
2363            self.asm.push(inst);
2364            let ranges = [(inst, position..self.asm.len())];
2365
2366            let code = self.asm.finalize();
2367            let mut position = 0;
2368            for (inst, range) in ranges {
2369                write!(&mut self.disassembly_1, "{:08x} ", position).unwrap();
2370                for &b in &code[range.clone()] {
2371                    write!(&mut self.disassembly_1, "{:02x}", b).unwrap();
2372                }
2373                position += range.len();
2374                writeln!(&mut self.disassembly_1, " {}", inst).unwrap();
2375            }
2376
2377            self.disassembly_1.pop();
2378            disassemble_into(&code, &mut self.disassembly_2);
2379            assert_eq!(self.disassembly_1, self.disassembly_2, "broken encoding for: {inst:?}");
2380        }
2381    }
2382
2383    macro_rules! generate_tests {
2384        ($($inst_name:ident,)+) => {
2385            $(
2386                #[test]
2387                fn $inst_name() {
2388                    let mut test = TestAsm::new();
2389                    <super::inst::types::$inst_name as GenerateTestValues>::generate_test_values(|instruction| {
2390                        test.run(crate::Instruction {
2391                            bytes: instruction.encode(),
2392                            fixup: None,
2393                            instruction
2394                        })
2395                    });
2396                }
2397            )+
2398        }
2399    }
2400
2401    generate_tests! {
2402        add,
2403        and,
2404        bts,
2405        call_rel32,
2406        call,
2407        cdq,
2408        clflushopt,
2409        cmov,
2410        cmp,
2411        cpuid,
2412        cqo,
2413        dec,
2414        div,
2415        endbr64,
2416        idiv,
2417        imul_dx_ax,
2418        imul_imm,
2419        imul,
2420        inc,
2421        jcc_rel32,
2422        jcc_rel8,
2423        jmp_rel32,
2424        jmp_rel8,
2425        jmp,
2426        lea,
2427        lfence,
2428        load,
2429        mfence,
2430        mov_imm,
2431        mov_imm64,
2432        mov,
2433        movsx_8_to_64,
2434        movsx_16_to_64,
2435        movzx_16_to_64,
2436        movsxd_32_to_64,
2437        mul,
2438        mul_dx_ax,
2439        neg,
2440        nop,
2441        nop10,
2442        nop11,
2443        nop2,
2444        nop3,
2445        nop4,
2446        nop5,
2447        nop6,
2448        nop7,
2449        nop8,
2450        nop9,
2451        not,
2452        or,
2453        pause,
2454        pop,
2455        push,
2456        push_imm,
2457        rcr_imm,
2458        rdpmc,
2459        rdtscp,
2460        ret,
2461        ror_imm,
2462        rol_cl,
2463        ror_cl,
2464        sar_cl,
2465        sar_imm,
2466        popcnt,
2467        lzcnt,
2468        tzcnt,
2469        setcc,
2470        bswap,
2471        shl_cl,
2472        shl_imm,
2473        shr_cl,
2474        shr_imm,
2475        store,
2476        sub,
2477        syscall,
2478        test,
2479        ud2,
2480        xchg_mem,
2481        xor,
2482    }
2483
2484    #[test]
2485    fn jmp_label8_infinite_loop() {
2486        use super::inst::*;
2487        let mut asm = crate::Assembler::new();
2488        let label = asm.forward_declare_label();
2489        asm.push_with_label(label, jmp_label8(label));
2490        let disassembly = disassemble(&asm.finalize());
2491        assert_eq!(disassembly, "00000000 ebfe jmp short 0x0");
2492    }
2493
2494    #[test]
2495    fn jmp_label8_undefined() {
2496        use super::inst::*;
2497        let mut asm = crate::Assembler::new();
2498        let label = asm.forward_declare_label();
2499        asm.push(jmp_label8(label));
2500        let disassembly = disassemble(&asm.finalize());
2501        assert_eq!(disassembly, "00000000 0f0b ud2");
2502    }
2503
2504    #[test]
2505    fn jmp_label32_infinite_loop() {
2506        use super::inst::*;
2507        let mut asm = crate::Assembler::new();
2508        let label = asm.forward_declare_label();
2509        asm.push_with_label(label, jmp_label32(label));
2510        let disassembly = disassemble(&asm.finalize());
2511        assert_eq!(disassembly, "00000000 e9fbffffff jmp 0x0");
2512    }
2513
2514    #[test]
2515    fn jmp_label32_undefined() {
2516        use super::inst::*;
2517        let mut asm = crate::Assembler::new();
2518        let label = asm.forward_declare_label();
2519        asm.push(jmp_label32(label));
2520        let disassembly = disassemble(&asm.finalize());
2521        assert_eq!(disassembly, "00000000 0f0b ud2\n00000002 90 nop\n00000003 0f0b ud2");
2522    }
2523
2524    #[test]
2525    fn call_label32_infinite_loop() {
2526        use super::inst::*;
2527        let mut asm = crate::Assembler::new();
2528        let label = asm.forward_declare_label();
2529        asm.push_with_label(label, call_label32(label));
2530        let disassembly = disassemble(&asm.finalize());
2531        assert_eq!(disassembly, "00000000 e8fbffffff call 0x0");
2532    }
2533
2534    #[test]
2535    fn call_label32_undefined() {
2536        use super::inst::*;
2537        let mut asm = crate::Assembler::new();
2538        let label = asm.forward_declare_label();
2539        asm.push(call_label32(label));
2540        let disassembly = disassemble(&asm.finalize());
2541        assert_eq!(disassembly, "00000000 0f0b ud2\n00000002 90 nop\n00000003 0f0b ud2");
2542    }
2543
2544    #[test]
2545    fn jcc_label8_infinite_loop() {
2546        use super::inst::*;
2547        super::Condition::generate_test_values(|cond| {
2548            let mut asm = crate::Assembler::new();
2549            let label = asm.forward_declare_label();
2550            asm.push_with_label(label, jcc_label8(cond, label));
2551            let disassembly = disassemble(&asm.finalize());
2552            assert_eq!(
2553                disassembly,
2554                format!("00000000 {:02x}fe j{} short 0x0", 0x70 + cond as u8, cond.suffix())
2555            );
2556        });
2557    }
2558
2559    #[test]
2560    fn jcc_label8_undefined() {
2561        use super::inst::*;
2562        super::Condition::generate_test_values(|cond| {
2563            let mut asm = crate::Assembler::new();
2564            let label = asm.forward_declare_label();
2565            asm.push(jcc_label8(cond, label));
2566            let disassembly = disassemble(&asm.finalize());
2567            assert_eq!(disassembly, "00000000 0f0b ud2");
2568        });
2569    }
2570
2571    #[test]
2572    fn jcc_label8_jump_forward() {
2573        use super::inst::*;
2574        super::Condition::generate_test_values(|cond| {
2575            let mut asm = crate::Assembler::new();
2576            let label = asm.forward_declare_label();
2577            asm.push(jcc_label8(cond, label));
2578            asm.push_with_label(label, nop());
2579            let disassembly = disassemble(&asm.finalize());
2580            assert_eq!(
2581                disassembly,
2582                format!(
2583                    concat!("00000000 {:02x}00 j{} short 0x2\n", "00000002 90 nop",),
2584                    0x70 + cond as u8,
2585                    cond.suffix()
2586                )
2587            );
2588        })
2589    }
2590
2591    #[test]
2592    fn jcc_label8_jump_backward() {
2593        use super::inst::*;
2594        super::Condition::generate_test_values(|cond| {
2595            let mut asm = crate::Assembler::new();
2596            let label = asm.forward_declare_label();
2597            asm.push_with_label(label, nop());
2598            asm.push(jcc_label8(cond, label));
2599            let disassembly = disassemble(&asm.finalize());
2600            assert_eq!(
2601                disassembly,
2602                format!(
2603                    concat!("00000000 90 nop\n", "00000001 {:02x}fd j{} short 0xffffffffffffffff",),
2604                    0x70 + cond as u8,
2605                    cond.suffix()
2606                )
2607            );
2608        });
2609    }
2610
2611    #[test]
2612    fn jcc_label32_jump_forward() {
2613        use super::inst::*;
2614        super::Condition::generate_test_values(|cond| {
2615            let mut asm = crate::Assembler::new();
2616            let label = asm.forward_declare_label();
2617            asm.push(jcc_label32(cond, label));
2618            asm.push_with_label(label, nop());
2619            let disassembly = disassemble(&asm.finalize());
2620            assert_eq!(
2621                disassembly,
2622                format!(
2623                    concat!("00000000 0f{:02x}00000000 j{} near 0x6\n", "00000006 90 nop",),
2624                    0x80 + cond as u8,
2625                    cond.suffix()
2626                )
2627            );
2628        });
2629    }
2630
2631    #[test]
2632    fn jcc_label32_undefined() {
2633        use super::inst::*;
2634        super::Condition::generate_test_values(|cond| {
2635            let mut asm = crate::Assembler::new();
2636            let label = asm.forward_declare_label();
2637            asm.push(jcc_label32(cond, label));
2638            let disassembly = disassemble(&asm.finalize());
2639            assert_eq!(disassembly, "00000000 0f0b ud2\n00000002 0f0b ud2\n00000004 0f0b ud2");
2640        });
2641    }
2642
2643    #[test]
2644    fn lea_rip_label_infinite_loop() {
2645        use super::inst::*;
2646        let mut asm = crate::Assembler::new();
2647        let label = asm.forward_declare_label();
2648        asm.push_with_label(label, lea_rip_label(super::Reg::rax, label));
2649        let disassembly = disassemble(&asm.finalize());
2650        assert_eq!(disassembly, "00000000 488d05f9ffffff lea rax, [rip-0x7]");
2651    }
2652
2653    #[test]
2654    fn lea_rip_label_undefined() {
2655        use super::inst::*;
2656        super::Reg::generate_test_values(|reg| {
2657            let mut asm = crate::Assembler::new();
2658            let label = asm.forward_declare_label();
2659            asm.push(lea_rip_label(reg, label));
2660            let disassembly = disassemble(&asm.finalize());
2661            assert_eq!(
2662                disassembly,
2663                "00000000 0f0b ud2\n00000002 0f0b ud2\n00000004 0f0b ud2\n00000006 90 nop"
2664            );
2665        });
2666    }
2667
2668    #[test]
2669    fn lea_rip_label_next_instruction() {
2670        use super::inst::*;
2671        let mut asm = crate::Assembler::new();
2672        let label = asm.forward_declare_label();
2673        asm.push(lea_rip_label(super::Reg::rax, label));
2674        asm.push_with_label(label, nop());
2675        let disassembly = disassemble(&asm.finalize());
2676        assert_eq!(disassembly, "00000000 488d0500000000 lea rax, [rip]\n00000007 90 nop");
2677    }
2678}