Skip to main content

asmkit/x86/
assembler.rs

1#![allow(dead_code)]
2use super::emit::{self, PendingPrefixes};
3use super::emitter::{CallEmitter, JmpEmitter, MovEmitter};
4use super::operands::*;
5use crate::{
6    X86Error,
7    core::{
8        arch_traits::Arch,
9        buffer::{CodeBuffer, CodeOffset, ConstantData, LabelUse},
10        globals::InstOptions,
11        operand::*,
12        patch::{PatchBlockId, PatchSiteId},
13        target::Environment,
14    },
15};
16
17/// X86/X64 Assembler implementation.
18pub struct Assembler<'a> {
19    pub(crate) buffer: &'a mut CodeBuffer,
20    flags: u64,
21    extra_reg: Reg,
22}
23
24const RC_RN: u64 = 0x0000000;
25const RC_RD: u64 = 0x0800000;
26const RC_RU: u64 = 0x1000000;
27const RC_RZ: u64 = 0x1800000;
28const RC_MASK: u64 = RC_RD | RC_RU;
29const RC_ENABLED: u64 = 0x4000000;
30const SEG_MASK: u64 = 0xe0000000;
31const LONG: u64 = 0x100000000;
32
33/// Bit 37: LOCK prefix (bits 23/24 are the rounding-mode RC bits, bits 20/21
34/// REP/REPNE, bit 26 SAE/ER enable, bits 29..=31 segment, bit 32 long-form,
35/// bits 33..=35 mask id, bit 36 zeroing mask).
36const OPC_LOCK: u64 = 0x2000000000;
37/// Bit 36: AVX-512 zeroing mask `{z}` (bits 33..=35 carry the mask register id).
38const OPC_Z: u64 = 0x1000000000;
39
40impl crate::core::builder::InstSink for Assembler<'_> {
41    fn arch(&self) -> Arch {
42        self.environment().arch()
43    }
44
45    fn emit_inst(&mut self, inst: &crate::core::inst::Inst) -> Result<(), crate::AsmError> {
46        let ops = inst.operands();
47        let mut refs: smallvec::SmallVec<[&Operand; 6]> = smallvec::SmallVec::new();
48        refs.extend(ops.iter());
49
50        let extra_reg = inst.extra_reg();
51        let mask_id = match extra_reg.signature.try_op_type() {
52            Some(OperandType::None) if extra_reg.signature.bits() == 0 => 0,
53            Some(OperandType::Reg) if extra_reg.signature.try_reg_type() == Some(RegType::Mask) => {
54                extra_reg.id()
55            }
56            _ => {
57                return Err(X86Error::InvalidMasking {
58                    mask_reg: extra_reg.id(),
59                    reason: "x86 extra register must be a mask register",
60                }
61                .into());
62            }
63        };
64        self.try_emit_n_with_prefixes(
65            inst.id(),
66            &refs,
67            PendingPrefixes {
68                options: inst.options(),
69                segment_id: 0,
70                mask_id,
71            },
72        )
73    }
74
75    fn bind_label(&mut self, label: Label) -> Result<(), crate::AsmError> {
76        self.try_bind_label(label)
77    }
78}
79
80impl<'a> Assembler<'a> {
81    /// Collects the pending prefix flags set by the prefix setters (`rep`, `lock`,
82    /// `seg`, `k`, sae/rounding, `long`) and resets them, mapping them onto the
83    /// asmjit-style [`InstOptions`]/extra-reg model consumed by [`Assembler::emit_n`].
84    fn take_pending_prefixes(&mut self) -> PendingPrefixes {
85        let flags = self.flags;
86        self.flags = 0;
87
88        let mut prefixes = PendingPrefixes::default();
89        if flags & OPC_LOCK != 0 {
90            prefixes.options |= InstOptions::X86_LOCK;
91        }
92        if flags & 0x200000 != 0 {
93            prefixes.options |= InstOptions::X86_REP;
94        }
95        if flags & 0x100000 != 0 {
96            prefixes.options |= InstOptions::X86_REPNE;
97        }
98        if flags & LONG != 0 {
99            prefixes.options |= InstOptions::LONG_FORM;
100        }
101        if flags & OPC_Z != 0 {
102            prefixes.options |= InstOptions::X86_ZMASK;
103        }
104        if flags & RC_ENABLED != 0 {
105            let rc = flags & (RC_RD | RC_RU);
106            prefixes.options |= if rc == RC_RD {
107                InstOptions::X86_ER | InstOptions::X86_RD_SAE
108            } else if rc == RC_RU {
109                InstOptions::X86_ER | InstOptions::X86_RU_SAE
110            } else if rc == RC_RZ {
111                InstOptions::X86_ER | InstOptions::X86_RZ_SAE
112            } else {
113                // `sae()` and `rn_sae()` share the same flag bits (RC_RN is zero);
114                // both encode identically (EVEX.b with LL=00).
115                InstOptions::X86_SAE
116            };
117        }
118        prefixes.segment_id = ((flags & SEG_MASK) >> 29) as u32;
119        prefixes.mask_id = ((flags >> 33) & 0x7) as u32;
120        prefixes
121    }
122
123    /// Emits one instruction by id with explicit operands, using the asmjit-style
124    /// InstInfo → signature match → emit-handler pipeline (the new primary emit
125    /// path). Pending prefixes set by the prefix setters apply.
126    pub fn emit_n(&mut self, id: impl Into<u32>, ops: &[&Operand]) {
127        if let Err(error) = self.try_emit_n(id, ops) {
128            self.buffer.record_error(error);
129        }
130    }
131
132    pub fn try_emit_n(
133        &mut self,
134        id: impl Into<u32>,
135        ops: &[&Operand],
136    ) -> Result<(), crate::AsmError> {
137        if let Some(error) = self.buffer.error().cloned() {
138            return Err(error);
139        }
140        let prefixes = self.take_pending_prefixes();
141        self.try_emit_n_with_prefixes(id, ops, prefixes)
142    }
143
144    fn try_emit_n_with_prefixes(
145        &mut self,
146        id: impl Into<u32>,
147        ops: &[&Operand],
148        prefixes: PendingPrefixes,
149    ) -> Result<(), crate::AsmError> {
150        if let Some(error) = self.buffer.error().cloned() {
151            return Err(error);
152        }
153        let checkpoint = self.buffer.checkpoint();
154        if let Err(error) = emit::emit_n(self.buffer, id.into(), ops, prefixes, self.is_32bit()) {
155            self.buffer.rollback(checkpoint);
156            return Err(error);
157        }
158        Ok(())
159    }
160
161    pub fn new(buf: &'a mut CodeBuffer) -> Self {
162        if !matches!(buf.env().arch(), Arch::X86 | Arch::X64) {
163            return Self::poisoned(buf, crate::AsmError::InvalidArch);
164        }
165        Self::unchecked(buf)
166    }
167
168    pub fn try_new(buf: &'a mut CodeBuffer) -> Result<Self, crate::AsmError> {
169        if !matches!(buf.env().arch(), Arch::X86 | Arch::X64) {
170            return Err(crate::AsmError::InvalidArch);
171        }
172        Ok(Self::unchecked(buf))
173    }
174
175    fn unchecked(buf: &'a mut CodeBuffer) -> Self {
176        Self {
177            buffer: buf,
178            extra_reg: Reg::new(),
179            flags: 0,
180        }
181    }
182
183    fn poisoned(buf: &'a mut CodeBuffer, error: crate::AsmError) -> Self {
184        buf.record_error(error);
185        Self {
186            buffer: buf,
187            extra_reg: Reg::new(),
188            flags: 0,
189        }
190    }
191
192    /// Returns the environment (arch/mode) this assembler targets.
193    pub fn environment(&self) -> &Environment {
194        self.buffer.env()
195    }
196
197    /// Tests whether the assembler targets 32-bit X86 mode.
198    pub fn is_32bit(&self) -> bool {
199        self.buffer.env().is_32bit()
200    }
201
202    /// Tests whether the assembler targets 64-bit X64 mode.
203    pub fn is_64bit(&self) -> bool {
204        self.buffer.env().is_64bit()
205    }
206
207    #[cfg(test)]
208    fn last_error(&self) -> Option<X86Error> {
209        match self.buffer.error() {
210            Some(crate::AsmError::X86(error)) => Some(error.clone()),
211            _ => None,
212        }
213    }
214
215    pub fn sae(&mut self) -> &mut Self {
216        self.set_rounding(RC_RN)
217    }
218
219    pub fn rn_sae(&mut self) -> &mut Self {
220        self.set_rounding(RC_RN)
221    }
222
223    pub fn rd_sae(&mut self) -> &mut Self {
224        self.set_rounding(RC_RD)
225    }
226    pub fn ru_sae(&mut self) -> &mut Self {
227        self.set_rounding(RC_RU)
228    }
229
230    pub fn rz_sae(&mut self) -> &mut Self {
231        self.set_rounding(RC_RZ)
232    }
233
234    fn set_rounding(&mut self, rounding: u64) -> &mut Self {
235        let mask = RC_ENABLED | RC_MASK;
236        let pending = self.flags & mask;
237        let requested = RC_ENABLED | rounding;
238        if pending != 0 && pending != requested {
239            self.buffer
240                .record_error(crate::AsmError::X86(X86Error::InvalidRoundingControl {
241                    rc: requested,
242                    reason: "conflicting pending rounding modes",
243                }));
244            return self;
245        }
246        self.flags = (self.flags & !mask) | requested;
247        self
248    }
249
250    pub fn seg(&mut self, sreg: SReg) -> &mut Self {
251        let segment_id = sreg.id();
252        if !(SReg::ES..=SReg::GS).contains(&segment_id) {
253            self.buffer
254                .record_error(crate::AsmError::X86(X86Error::InvalidPrefix {
255                    prefix: segment_id as u64,
256                    reason: "invalid segment override",
257                }));
258            return self;
259        }
260        let pending = (self.flags & SEG_MASK) >> 29;
261        if pending != 0 && pending != segment_id as u64 {
262            self.buffer
263                .record_error(crate::AsmError::X86(X86Error::InvalidPrefix {
264                    prefix: segment_id as u64,
265                    reason: "conflicting pending segment overrides",
266                }));
267            return self;
268        }
269        self.flags = (self.flags & !SEG_MASK) | (segment_id as u64) << 29;
270        self
271    }
272
273    pub fn fs(&mut self) -> &mut Self {
274        self.seg(FS)
275    }
276
277    pub fn gs(&mut self) -> &mut Self {
278        self.seg(GS)
279    }
280
281    pub fn k(&mut self, k: KReg) -> &mut Self {
282        let mask_id = k.id();
283        if !(1..=7).contains(&mask_id) {
284            self.buffer
285                .record_error(crate::AsmError::X86(X86Error::InvalidMasking {
286                    mask_reg: mask_id,
287                    reason: "mask register must be k1..k7",
288                }));
289            return self;
290        }
291        let pending = (self.flags >> 33) & 0x7;
292        if pending != 0 && pending != mask_id as u64 {
293            self.buffer
294                .record_error(crate::AsmError::X86(X86Error::InvalidMasking {
295                    mask_reg: mask_id,
296                    reason: "conflicting pending mask registers",
297                }));
298            return self;
299        }
300        self.flags = (self.flags & !(0x7 << 33)) | (mask_id as u64) << 33;
301
302        self
303    }
304
305    /// AVX-512 zeroing mask `{z}` for the next instruction (requires `k()`).
306    pub fn z(&mut self) -> &mut Self {
307        self.flags |= OPC_Z;
308        self
309    }
310
311    pub fn rep(&mut self) -> &mut Self {
312        self.flags |= 0x200000;
313        self
314    }
315
316    pub fn repnz(&mut self) -> &mut Self {
317        self.flags |= 0x100000;
318        self
319    }
320
321    pub fn repz(&mut self) -> &mut Self {
322        self.rep()
323    }
324
325    pub fn lock(&mut self) -> &mut Self {
326        self.flags |= OPC_LOCK;
327        self
328    }
329
330    pub fn long(&mut self) -> &mut Self {
331        self.flags |= LONG;
332        self
333    }
334
335    pub fn get_label(&mut self) -> Label {
336        self.buffer.get_label()
337    }
338
339    pub fn bind_label(&mut self, label: Label) {
340        if let Err(error) = self.try_bind_label(label) {
341            self.buffer.record_error(error);
342        }
343    }
344
345    pub fn try_bind_label(&mut self, label: Label) -> Result<(), crate::AsmError> {
346        self.buffer.try_bind_label(label)
347    }
348
349    pub fn add_constant(&mut self, c: impl Into<ConstantData>) -> Label {
350        let c = self.buffer.add_constant(c);
351        self.buffer.get_label_for_constant(c)
352    }
353
354    pub fn label_offset(&self, label: Label) -> CodeOffset {
355        self.buffer.label_offset(label)
356    }
357
358    pub fn data(&self) -> &[u8] {
359        self.buffer.data()
360    }
361
362    pub fn error(&self) -> Option<&crate::AsmError> {
363        self.buffer.error()
364    }
365
366    pub fn patchable_jmp(&mut self, label: Label) -> PatchSiteId {
367        self.long();
368        self.jmp(label);
369        let offset = self
370            .buffer
371            .cur_offset()
372            .saturating_sub(LabelUse::X86JmpRel32.patch_size() as u32);
373        self.buffer
374            .record_label_patch_site(offset, label, LabelUse::X86JmpRel32)
375    }
376
377    pub fn patchable_call(&mut self, label: Label) -> PatchSiteId {
378        self.long();
379        self.call(label);
380        let offset = self
381            .buffer
382            .cur_offset()
383            .saturating_sub(LabelUse::X86JmpRel32.patch_size() as u32);
384        self.buffer
385            .record_label_patch_site(offset, label, LabelUse::X86JmpRel32)
386    }
387
388    pub fn patchable_mov<A, B>(&mut self, dst: A, src: B) -> PatchBlockId
389    where
390        A: OperandCast + Copy,
391        B: OperandCast + Copy,
392        Self: MovEmitter<A, B>,
393    {
394        let dst_op = *dst.as_operand();
395        let src_op = *src.as_operand();
396        let size = if dst_op.is_reg_type_of(RegType::Gp64) {
397            8
398        } else if dst_op.is_reg_type_of(RegType::Gp32) {
399            4
400        } else {
401            self.buffer
402                .record_error(crate::AsmError::X86(X86Error::InvalidOperand {
403                    operand_index: 0,
404                    reason: "patchable_mov requires a Gp32 or Gp64 destination",
405                }));
406            return PatchBlockId::from_index(usize::MAX);
407        };
408
409        if !src_op.is_imm() {
410            self.buffer
411                .record_error(crate::AsmError::X86(X86Error::InvalidOperand {
412                    operand_index: 1,
413                    reason: "patchable_mov requires an immediate source",
414                }));
415            return PatchBlockId::from_index(usize::MAX);
416        }
417
418        let offset = self.buffer.cur_offset();
419        let previous_error = self.buffer.error().cloned();
420        self.long();
421        MovEmitter::mov(self, dst, src);
422        if self.buffer.error().cloned() != previous_error
423            || self.buffer.cur_offset() < offset + size
424        {
425            return PatchBlockId::from_index(usize::MAX);
426        }
427
428        let offset = self.buffer.cur_offset() - size;
429        self.buffer.record_patch_block(offset, size, 1)
430    }
431}
432
433#[derive(Debug, Clone, Copy, PartialEq, Eq)]
434pub enum CondCode {
435    O = 0x0,
436    NO = 0x1,
437    C = 0x2,
438    NC = 0x3,
439    Z = 0x4,
440    NZ = 0x5,
441    BE = 0x6,
442    A = 0x7,
443    S = 0x8,
444    NS = 0x9,
445    P = 0xa,
446
447    NP = 0xb,
448    L = 0xc,
449    GE = 0xd,
450    LE = 0xe,
451    G = 0xf,
452}
453
454impl CondCode {
455    pub const B: Self = Self::C;
456    pub const NAE: Self = Self::C;
457    pub const AE: Self = Self::NC;
458    pub const NB: Self = Self::NC;
459    pub const E: Self = Self::Z;
460    pub const NE: Self = Self::NZ;
461    pub const NA: Self = Self::BE;
462    pub const NBE: Self = Self::A;
463    pub const PO: Self = Self::NP;
464    pub const NGE: Self = Self::L;
465    pub const NL: Self = Self::GE;
466    pub const NG: Self = Self::LE;
467    pub const NLE: Self = Self::G;
468    pub const PE: Self = Self::P;
469
470    pub const fn code(self) -> u8 {
471        self as u8
472    }
473
474    pub fn invert(self) -> Self {
475        match self {
476            Self::O => Self::NO,
477            Self::NO => Self::O,
478            Self::C => Self::NC,
479            Self::NC => Self::C,
480            Self::Z => Self::NZ,
481            Self::NZ => Self::Z,
482            Self::BE => Self::A,
483            Self::A => Self::BE,
484            Self::S => Self::NS,
485            Self::NS => Self::S,
486            Self::P => Self::NP,
487            Self::NP => Self::P,
488            Self::L => Self::GE,
489            Self::GE => Self::L,
490            Self::LE => Self::G,
491            Self::G => Self::LE,
492        }
493    }
494}
495
496#[cfg(test)]
497mod tests {
498    use super::*;
499    use crate::core::builder::Builder;
500    use crate::core::inst::Inst;
501    use crate::x86::instdb::InstId;
502    use crate::x86::operands::regs::*;
503
504    /// Assembles via `emit_n`, asserting no error, and finalizes label fixups.
505    fn asm(f: impl FnOnce(&mut Assembler)) -> std::vec::Vec<u8> {
506        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
507        {
508            let mut a = Assembler::new(&mut buf);
509            f(&mut a);
510            assert!(a.last_error().is_none(), "{:?}", a.last_error());
511        }
512        buf.finish().unwrap().data().to_vec()
513    }
514
515    #[test]
516    fn emit_n_integer_forms() {
517        // mov rax, 1 — sign-extended C7 /0 form (AsmJit default, no long_form).
518        assert_eq!(
519            asm(|a| a.emit_n(InstId::Mov as u32, &[RAX.as_operand(), imm(1).as_operand()])),
520            [0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00]
521        );
522        // mov rax, rbx.
523        assert_eq!(
524            asm(|a| a.emit_n(InstId::Mov as u32, &[RAX.as_operand(), RBX.as_operand()])),
525            [0x48, 0x89, 0xD8]
526        );
527        // add rax, rbx.
528        assert_eq!(
529            asm(|a| a.emit_n(InstId::Add as u32, &[RAX.as_operand(), RBX.as_operand()])),
530            [0x48, 0x01, 0xD8]
531        );
532        // push rax / pop rax.
533        assert_eq!(
534            asm(|a| a.emit_n(InstId::Push as u32, &[RAX.as_operand()])),
535            [0x50]
536        );
537        assert_eq!(
538            asm(|a| a.emit_n(InstId::Pop as u32, &[RAX.as_operand()])),
539            [0x58]
540        );
541        // cmovz rax, rbx.
542        assert_eq!(
543            asm(|a| a.emit_n(InstId::Cmovz as u32, &[RAX.as_operand(), RBX.as_operand()])),
544            [0x48, 0x0F, 0x44, 0xC3]
545        );
546        // ret / syscall.
547        assert_eq!(asm(|a| a.emit_n(InstId::Ret as u32, &[])), [0xC3]);
548        assert_eq!(asm(|a| a.emit_n(InstId::Syscall as u32, &[])), [0x0F, 0x05]);
549        // mov rax, 0x123456789 — 64-bit immediate uses the B8 (movabs) form.
550        assert_eq!(
551            asm(|a| a.emit_n(
552                InstId::Mov as u32,
553                &[RAX.as_operand(), imm(0x1_2345_6789i64).as_operand()]
554            )),
555            [0x48, 0xB8, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00]
556        );
557    }
558
559    #[test]
560    fn emit_n_invalid_sets_last_error() {
561        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
562        let mut a = Assembler::new(&mut buf);
563        // add rax, xmm0 — no signature matches; nothing is emitted.
564        a.emit_n(InstId::Add as u32, &[RAX.as_operand(), XMM0.as_operand()]);
565        assert!(matches!(
566            a.last_error(),
567            Some(X86Error::InvalidInstruction { .. })
568        ));
569        assert!(a.buffer.data().is_empty());
570        // Unknown instruction id.
571        a.buffer.clear();
572        a.emit_n(u32::MAX, &[]);
573        assert!(a.last_error().is_some());
574    }
575
576    #[test]
577    fn raw_invalid_symbol_id_is_rejected() {
578        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
579        let mut a = Assembler::new(&mut buf);
580        let mut operand = Operand::new();
581        operand.set_signature(OperandSignature::from(0x001A_0012));
582
583        assert_eq!(
584            a.try_emit_n(727u32, &[&operand]),
585            Err(crate::AsmError::X86(X86Error::InvalidOperand {
586                operand_index: 0,
587                reason: "symbol is not declared in this buffer",
588            }))
589        );
590        assert!(a.buffer.data().is_empty());
591
592        a.emit_n(727u32, &[&operand]);
593        assert_eq!(
594            a.buffer.error(),
595            Some(&crate::AsmError::X86(X86Error::InvalidOperand {
596                operand_index: 0,
597                reason: "symbol is not declared in this buffer",
598            }))
599        );
600        assert!(a.buffer.data().is_empty());
601    }
602
603    #[test]
604    fn emit_n_rejects_more_than_six_operands() {
605        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
606        let mut a = Assembler::new(&mut buf);
607        let ops = [RAX.as_operand(); 7];
608
609        a.emit_n(InstId::Ret as u32, &ops);
610
611        assert!(a.last_error().is_some());
612        assert!(a.buffer.data().is_empty());
613    }
614
615    #[test]
616    fn patchable_mov_emits_once_and_rejects_unsupported_operands() {
617        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
618        {
619            let mut a = Assembler::new(&mut buf);
620            a.patchable_mov(EAX, imm(42));
621            assert_eq!(a.buffer.data(), &[0xB8, 42, 0, 0, 0]);
622            assert!(a.last_error().is_none());
623
624            a.patchable_mov(RAX, imm(42));
625            assert_eq!(
626                a.buffer.data(),
627                &[0xB8, 42, 0, 0, 0, 0x48, 0xB8, 42, 0, 0, 0, 0, 0, 0, 0]
628            );
629
630            a.patchable_mov(RAX, RBX);
631            assert!(matches!(
632                a.last_error(),
633                Some(X86Error::InvalidOperand {
634                    operand_index: 1,
635                    ..
636                })
637            ));
638            assert_eq!(
639                a.buffer.data(),
640                &[0xB8, 42, 0, 0, 0, 0x48, 0xB8, 42, 0, 0, 0, 0, 0, 0, 0]
641            );
642        }
643
644        assert!(matches!(
645            buf.finish_patched(),
646            Err(crate::AsmError::X86(X86Error::InvalidOperand {
647                operand_index: 1,
648                ..
649            }))
650        ));
651    }
652
653    #[test]
654    fn emit_n_memory_forms() {
655        // mov rax, [rip + 0x1234].
656        assert_eq!(
657            asm(|a| a.emit_n(
658                InstId::Mov as u32,
659                &[RAX.as_operand(), qword_ptr_rip(0x1234).as_operand()]
660            )),
661            [0x48, 0x8B, 0x05, 0x34, 0x12, 0x00, 0x00]
662        );
663        // mov [rip + label], eax — label bound right after the instruction.
664        assert_eq!(
665            asm(|a| {
666                let label = a.get_label();
667                a.emit_n(
668                    InstId::Mov as u32,
669                    &[dword_ptr_label(label, 0).as_operand(), EAX.as_operand()],
670                );
671                a.bind_label(label);
672                a.emit_n(InstId::Ret as u32, &[]);
673            }),
674            [0x89, 0x05, 0x00, 0x00, 0x00, 0x00, 0xC3]
675        );
676        // fs prefix from the prefix setter applies to the mem operand.
677        assert_eq!(
678            asm(|a| {
679                a.fs();
680                a.emit_n(
681                    InstId::Mov as u32,
682                    &[RAX.as_operand(), qword_ptr_u64(0x40).as_operand()],
683                );
684            }),
685            [0x64, 0x48, 0x8B, 0x04, 0x25, 0x40, 0x00, 0x00, 0x00]
686        );
687    }
688
689    #[test]
690    fn emit_n_branch_forms() {
691        // Backward jmp to a bound label uses rel8 by default.
692        assert_eq!(
693            asm(|a| {
694                let label = a.get_label();
695                a.bind_label(label);
696                a.emit_n(
697                    InstId::Jmp as u32,
698                    &[Label::from_id(label.id()).as_operand()],
699                );
700            }),
701            [0xEB, 0xFE]
702        );
703        // Forward jmp to an unbound label is relaxed at finalization.
704        assert_eq!(
705            asm(|a| {
706                let label = a.get_label();
707                a.emit_n(
708                    InstId::Jmp as u32,
709                    &[Label::from_id(label.id()).as_operand()],
710                );
711                a.bind_label(label);
712            }),
713            [0xEB, 0x00]
714        );
715        // call rel32 to an unbound label, bound right after.
716        assert_eq!(
717            asm(|a| {
718                let label = a.get_label();
719                a.emit_n(
720                    InstId::Call as u32,
721                    &[Label::from_id(label.id()).as_operand()],
722                );
723                a.bind_label(label);
724            }),
725            [0xE8, 0x00, 0x00, 0x00, 0x00]
726        );
727    }
728
729    #[test]
730    fn patchable_branches_keep_the_near_form() {
731        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
732        let target = buf.get_label();
733        let site = {
734            let mut asm = Assembler::new(&mut buf);
735            asm.patchable_jmp(target)
736        };
737        buf.bind_label(target);
738
739        let code = buf.finish_patched().unwrap();
740        assert_eq!(code.data(), &[0xE9, 0, 0, 0, 0]);
741        let site = code.patch_catalog().site(site).unwrap();
742        assert_eq!(site.offset, 1);
743        assert_eq!(site.current_target, 5);
744    }
745
746    #[test]
747    fn emit_n_vex_evex_forms() {
748        // vaddps ymm1, ymm2, ymm3 — AsmJit golden "C5EC58CB".
749        assert_eq!(
750            asm(|a| a.emit_n(
751                InstId::Vaddps as u32,
752                &[YMM1.as_operand(), YMM2.as_operand(), YMM3.as_operand()]
753            )),
754            [0xC5, 0xEC, 0x58, 0xCB]
755        );
756        // vmovdqu ymm0, ymm1.
757        assert_eq!(
758            asm(|a| a.emit_n(
759                InstId::Vmovdqu as u32,
760                &[YMM0.as_operand(), YMM1.as_operand()]
761            )),
762            [0xC5, 0xFE, 0x6F, 0xC1]
763        );
764        // vaddpd zmm1, zmm2, zmm3 (unmasked EVEX).
765        assert_eq!(
766            asm(|a| a.emit_n(
767                InstId::Vaddpd as u32,
768                &[ZMM1.as_operand(), ZMM2.as_operand(), ZMM3.as_operand()]
769            )),
770            [0x62, 0xF1, 0xED, 0x48, 0x58, 0xCB]
771        );
772        // {k1} mask from the prefix setter: EVEX P2 aaa=001.
773        assert_eq!(
774            asm(|a| {
775                a.k(K1);
776                a.emit_n(
777                    InstId::Vaddpd as u32,
778                    &[ZMM1.as_operand(), ZMM2.as_operand(), ZMM3.as_operand()],
779                );
780            }),
781            [0x62, 0xF1, 0xED, 0x49, 0x58, 0xCB]
782        );
783    }
784
785    #[test]
786    fn emitter_traits_match_emit_n() {
787        // The generated emitter traits (src/x86/emitter.rs) must produce the
788        // same bytes as the direct `emit_n` calls above.
789        use crate::x86::emitter::{AddEmitter, JmpEmitter, MovEmitter, VaddpsEmitter};
790
791        // mov rax, 1.
792        assert_eq!(
793            asm(|a| MovEmitter::mov(a, RAX, imm(1))),
794            [0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00]
795        );
796        // mov rax, rbx.
797        assert_eq!(asm(|a| MovEmitter::mov(a, RAX, RBX)), [0x48, 0x89, 0xD8]);
798        // add rax, rbx.
799        assert_eq!(asm(|a| AddEmitter::add(a, RAX, RBX)), [0x48, 0x01, 0xD8]);
800        // vaddps ymm1, ymm2, ymm3.
801        assert_eq!(
802            asm(|a| VaddpsEmitter::vaddps(a, YMM1, YMM2, YMM3)),
803            [0xC5, 0xEC, 0x58, 0xCB]
804        );
805        // Backward jmp to a bound label through the trait.
806        assert_eq!(
807            asm(|a| {
808                let label = a.get_label();
809                a.bind_label(label);
810                JmpEmitter::jmp(a, label);
811            }),
812            [0xEB, 0xFE]
813        );
814    }
815
816    #[test]
817    fn emitter_typed_call_sites() {
818        // Sized register constants and integer literals must work directly,
819        // producing the same bytes as the abstract forms above.
820        use crate::x86::emitter::{AddEmitter, MovEmitter, PaddwEmitter, VaddpsEmitter};
821
822        // mov rax, 42 (integer literal via Into<Imm>).
823        assert_eq!(
824            asm(|a| MovEmitter::mov(a, RAX, 42)),
825            [0x48, 0xC7, 0xC0, 0x2A, 0x00, 0x00, 0x00]
826        );
827        // mov eax, 42 (no REX prefix).
828        assert_eq!(
829            asm(|a| MovEmitter::mov(a, EAX, 42)),
830            [0xB8, 0x2A, 0x00, 0x00, 0x00]
831        );
832        // add rax, rbx.
833        assert_eq!(asm(|a| AddEmitter::add(a, RAX, RBX)), [0x48, 0x01, 0xD8]);
834        // paddw xmm0, xmm1 (legacy SSE form).
835        assert_eq!(
836            asm(|a| PaddwEmitter::paddw(a, XMM0, XMM1)),
837            [0x66, 0x0F, 0xFD, 0xC1]
838        );
839        // vaddps ymm1, ymm2, ymm3 (VEX form).
840        assert_eq!(
841            asm(|a| VaddpsEmitter::vaddps(a, YMM1, YMM2, YMM3)),
842            [0xC5, 0xEC, 0x58, 0xCB]
843        );
844    }
845
846    #[test]
847    fn emit_n_prefix_forms() {
848        // rep movsq (explicit implicit-mem form): F3 48 A5.
849        assert_eq!(
850            asm(|a| {
851                a.rep();
852                a.emit_n(
853                    InstId::Movs as u32,
854                    &[
855                        qword_ptr(RDI, 0).as_operand(),
856                        qword_ptr(RSI, 0).as_operand(),
857                    ],
858                );
859            }),
860            [0xF3, 0x48, 0xA5]
861        );
862        // lock add dword ptr [rbx], eax: F0 01 03.
863        assert_eq!(
864            asm(|a| {
865                a.lock();
866                a.emit_n(
867                    InstId::Add as u32,
868                    &[dword_ptr(RBX, 0).as_operand(), EAX.as_operand()],
869                );
870            }),
871            [0xF0, 0x01, 0x03]
872        );
873    }
874
875    #[test]
876    fn conflicting_prefix_setters_poison_without_emitting() {
877        let cases: &[fn(&mut Assembler<'_>)] = &[
878            |a| {
879                a.fs().gs();
880            },
881            |a| {
882                a.k(K1).k(K2);
883            },
884            |a| {
885                a.rd_sae().ru_sae();
886            },
887            |a| {
888                a.seg(sreg(7));
889            },
890            |a| {
891                a.k(k(8));
892            },
893            |a| {
894                a.k(K0);
895            },
896        ];
897
898        for set_prefixes in cases {
899            let mut buffer = CodeBuffer::new(Environment::new(Arch::X64));
900            let mut assembler = Assembler::new(&mut buffer);
901            set_prefixes(&mut assembler);
902            assembler.emit_n(InstId::Ret as u32, &[]);
903            assert!(assembler.buffer.error().is_some());
904            assert!(assembler.buffer.data().is_empty());
905        }
906    }
907
908    #[test]
909    fn emit_n_builder_replay_matches_direct() {
910        fn direct() -> std::vec::Vec<u8> {
911            asm(|a| {
912                let done = a.get_label();
913                a.emit_n(InstId::Mov as u32, &[RAX.as_operand(), imm(1).as_operand()]);
914                a.emit_n(InstId::Add as u32, &[RAX.as_operand(), RBX.as_operand()]);
915                a.emit_n(
916                    InstId::Jmp as u32,
917                    &[Label::from_id(done.id()).as_operand()],
918                );
919                a.emit_n(InstId::Ret as u32, &[]);
920                a.bind_label(done);
921                a.emit_n(InstId::Ret as u32, &[]);
922            })
923        }
924
925        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
926        let mut builder = Builder::new();
927        let done = buf.get_label();
928        builder
929            .push_inst(Inst::with_operands(
930                InstId::Mov as u32,
931                &[*RAX.as_operand(), *imm(1).as_operand()],
932            ))
933            .unwrap();
934        builder
935            .push_inst(Inst::with_operands(
936                InstId::Add as u32,
937                &[*RAX.as_operand(), *RBX.as_operand()],
938            ))
939            .unwrap();
940        builder
941            .push_inst(Inst::with_operands(
942                InstId::Jmp as u32,
943                &[*Label::from_id(done.id()).as_operand()],
944            ))
945            .unwrap();
946        builder
947            .push_inst(Inst::with_operands(InstId::Ret as u32, &[]))
948            .unwrap();
949        builder.push_label(done);
950        builder
951            .push_inst(Inst::with_operands(InstId::Ret as u32, &[]))
952            .unwrap();
953        {
954            let mut a = Assembler::new(&mut buf);
955            builder.emit_into(&mut a).unwrap();
956            assert!(a.last_error().is_none(), "{:?}", a.last_error());
957        }
958        assert_eq!(buf.finish().unwrap().data().to_vec(), direct());
959    }
960
961    #[test]
962    fn builder_replays_options_and_mask_register() {
963        let direct = asm(|a| {
964            a.k(K1).z();
965            a.emit_n(
966                InstId::Vaddpd as u32,
967                &[ZMM1.as_operand(), ZMM2.as_operand(), ZMM3.as_operand()],
968            );
969        });
970
971        let mut inst = Inst::with_arch_operands(
972            Arch::X64,
973            InstId::Vaddpd as u32,
974            &[*ZMM1.as_operand(), *ZMM2.as_operand(), *ZMM3.as_operand()],
975        )
976        .unwrap();
977        inst.set_options(InstOptions::X86_ZMASK);
978        inst.set_extra_reg(*K1.as_operand());
979        let mut builder = Builder::for_arch(Arch::X64);
980        builder.push_inst(inst).unwrap();
981
982        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
983        builder.emit_into(&mut Assembler::new(&mut buf)).unwrap();
984        assert_eq!(buf.finish().unwrap().data(), direct);
985    }
986
987    /// Assembles in 32-bit mode via `emit_n`, asserting no error.
988    fn asm32(f: impl FnOnce(&mut Assembler)) -> std::vec::Vec<u8> {
989        let mut buf = CodeBuffer::new(Environment::new(Arch::X86));
990        {
991            let mut a = Assembler::new(&mut buf);
992            f(&mut a);
993            assert!(a.last_error().is_none(), "{:?}", a.last_error());
994        }
995        buf.finish().unwrap().data().to_vec()
996    }
997
998    /// Assembles in 32-bit mode, expecting an error.
999    fn asm32_err(f: impl FnOnce(&mut Assembler)) -> X86Error {
1000        let mut buf = CodeBuffer::new(Environment::new(Arch::X86));
1001        let mut a = Assembler::new(&mut buf);
1002        f(&mut a);
1003        a.last_error().expect("expected an emit error")
1004    }
1005
1006    /// Assembles in 64-bit mode, expecting an error.
1007    fn asm64_err(f: impl FnOnce(&mut Assembler)) -> X86Error {
1008        let mut buf = CodeBuffer::new(Environment::new(Arch::X64));
1009        let mut a = Assembler::new(&mut buf);
1010        f(&mut a);
1011        a.last_error().expect("expected an emit error")
1012    }
1013
1014    #[test]
1015    fn emit_n_32bit_gp_forms() {
1016        use crate::x86::emitter::{AddEmitter, MovEmitter};
1017
1018        // mov eax, ebx — no REX in 32-bit mode.
1019        assert_eq!(asm32(|a| MovEmitter::mov(a, EAX, EBX)), [0x89, 0xD8]);
1020        // mov ax, bx / mov al, bl.
1021        assert_eq!(asm32(|a| MovEmitter::mov(a, AX, BX)), [0x66, 0x89, 0xD8]);
1022        assert_eq!(asm32(|a| MovEmitter::mov(a, AL, BL)), [0x88, 0xD8]);
1023        // add ecx, 1 (imm8 form); add eax, 0x12345678 (accumulator short form).
1024        assert_eq!(
1025            asm32(|a| a.emit_n(InstId::Add as u32, &[ECX.as_operand(), imm(1).as_operand()])),
1026            [0x83, 0xC1, 0x01]
1027        );
1028        assert_eq!(
1029            asm32(|a| AddEmitter::add(a, EAX, 0x1234_5678i32)),
1030            [0x05, 0x78, 0x56, 0x34, 0x12]
1031        );
1032        // mov eax, 0x12345678 / mov ax, 0x1234 / mov cl, 0x12.
1033        assert_eq!(
1034            asm32(|a| MovEmitter::mov(a, EAX, 0x1234_5678i32)),
1035            [0xB8, 0x78, 0x56, 0x34, 0x12]
1036        );
1037        assert_eq!(
1038            asm32(|a| MovEmitter::mov(a, AX, 0x1234)),
1039            [0x66, 0xB8, 0x34, 0x12]
1040        );
1041        assert_eq!(asm32(|a| MovEmitter::mov(a, CL, 0x12)), [0xB1, 0x12]);
1042    }
1043
1044    #[test]
1045    fn emit_n_32bit_inc_dec_short_forms() {
1046        use crate::x86::emitter::{DecEmitter, IncEmitter};
1047
1048        // INC/DEC r16|r32 short forms exist only in 32-bit mode.
1049        assert_eq!(asm32(|a| IncEmitter::inc(a, EAX)), [0x40]);
1050        assert_eq!(asm32(|a| IncEmitter::inc(a, ECX)), [0x41]);
1051        assert_eq!(asm32(|a| IncEmitter::inc(a, AX)), [0x66, 0x40]);
1052        assert_eq!(asm32(|a| DecEmitter::dec(a, EDX)), [0x4A]);
1053        assert_eq!(asm32(|a| DecEmitter::dec(a, DX)), [0x66, 0x4A]);
1054        // 8-bit and memory forms are shared with 64-bit.
1055        assert_eq!(asm32(|a| IncEmitter::inc(a, AL)), [0xFE, 0xC0]);
1056        assert_eq!(
1057            asm32(|a| IncEmitter::inc(a, dword_ptr(ECX, 0))),
1058            [0xFF, 0x01]
1059        );
1060        // In 64-bit mode the same instructions use the FF /0|/1 forms.
1061        assert_eq!(
1062            asm(|a| a.emit_n(InstId::Inc as u32, &[EAX.as_operand()])),
1063            [0xFF, 0xC0]
1064        );
1065    }
1066
1067    #[test]
1068    fn emit_n_32bit_push_pop() {
1069        use crate::x86::emitter::{PopEmitter, PushEmitter};
1070
1071        assert_eq!(asm32(|a| PushEmitter::push(a, EAX)), [0x50]);
1072        assert_eq!(asm32(|a| PushEmitter::push(a, AX)), [0x66, 0x50]);
1073        assert_eq!(asm32(|a| PopEmitter::pop(a, ECX)), [0x59]);
1074        assert_eq!(
1075            asm32(|a| PushEmitter::push(a, 0x1234_5678i32)),
1076            [0x68, 0x78, 0x56, 0x34, 0x12]
1077        );
1078        assert_eq!(
1079            asm32(|a| PushEmitter::push(a, dword_ptr(ECX, 0))),
1080            [0xFF, 0x31]
1081        );
1082        assert_eq!(
1083            asm32(|a| PushEmitter::push(a, word_ptr(ECX, 0))),
1084            [0x66, 0xFF, 0x31]
1085        );
1086        // push/pop m64 is not encodable in 32-bit mode.
1087        asm32_err(|a| PushEmitter::push(a, qword_ptr(ECX, 0)));
1088        asm32_err(|a| PopEmitter::pop(a, qword_ptr(ECX, 0)));
1089    }
1090
1091    #[test]
1092    fn emit_n_32bit_far_pointer_forms() {
1093        // lcall/ljmp imm16, imm32 — 32-bit only.
1094        assert_eq!(
1095            asm32(|a| a.emit_n(
1096                InstId::Lcall as u32,
1097                &[imm(0x1234).as_operand(), imm(0x1234_5678).as_operand()]
1098            )),
1099            [0x9A, 0x78, 0x56, 0x34, 0x12, 0x34, 0x12]
1100        );
1101        assert_eq!(
1102            asm32(|a| a.emit_n(
1103                InstId::Ljmp as u32,
1104                &[imm(0x10).as_operand(), imm(0x20).as_operand()]
1105            )),
1106            [0xEA, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00]
1107        );
1108        // Selector above 0xFFFF is rejected.
1109        asm32_err(|a| {
1110            a.emit_n(
1111                InstId::Lcall as u32,
1112                &[imm(0x1_0000).as_operand(), imm(0).as_operand()],
1113            )
1114        });
1115        // The imm,imm form is rejected in 64-bit mode.
1116        asm64_err(|a| {
1117            a.emit_n(
1118                InstId::Lcall as u32,
1119                &[imm(0x1234).as_operand(), imm(0x1234_5678).as_operand()],
1120            )
1121        });
1122        // lcall fword [ecx] — m16:32.
1123        assert_eq!(
1124            asm32(|a| a.emit_n(InstId::Lcall as u32, &[fword_ptr(ECX, 0).as_operand()])),
1125            [0xFF, 0x19]
1126        );
1127    }
1128
1129    #[test]
1130    fn emit_n_32bit_movabs_and_xchg() {
1131        use crate::x86::emitter::{MovEmitter, XchgEmitter};
1132
1133        // mov eax, [abs] uses the moffs A1 form in 32-bit mode.
1134        assert_eq!(
1135            asm32(|a| MovEmitter::mov(a, EAX, dword_ptr_u64(0x1234_5678))),
1136            [0xA1, 0x78, 0x56, 0x34, 0x12]
1137        );
1138        // mov [abs], eax — moffs A3 form.
1139        assert_eq!(
1140            asm32(|a| MovEmitter::mov(a, dword_ptr_u64(0x1234_5678), EAX)),
1141            [0xA3, 0x78, 0x56, 0x34, 0x12]
1142        );
1143        // xchg eax, eax is encoded as 90 in 32-bit mode (generic path in 64-bit).
1144        assert_eq!(asm32(|a| XchgEmitter::xchg(a, EAX, EAX)), [0x90]);
1145        assert_eq!(
1146            asm(|a| a.emit_n(InstId::Xchg as u32, &[EAX.as_operand(), EAX.as_operand()])),
1147            [0x87, 0xC0]
1148        );
1149    }
1150
1151    #[test]
1152    fn emit_n_32bit_creg_lock_extension() {
1153        // mov eax, cr8 / mov cr8, eax use the LOCK prefix in 32-bit mode (AMD ext).
1154        assert_eq!(
1155            asm32(|a| a.emit_n(InstId::Mov as u32, &[EAX.as_operand(), CR8.as_operand()])),
1156            [0xF0, 0x0F, 0x20, 0xC0]
1157        );
1158        assert_eq!(
1159            asm32(|a| a.emit_n(InstId::Mov as u32, &[CR8.as_operand(), EAX.as_operand()])),
1160            [0xF0, 0x0F, 0x22, 0xC0]
1161        );
1162        // cr0 needs no LOCK.
1163        assert_eq!(
1164            asm32(|a| a.emit_n(InstId::Mov as u32, &[EAX.as_operand(), CR0.as_operand()])),
1165            [0x0F, 0x20, 0xC0]
1166        );
1167    }
1168
1169    #[test]
1170    fn emit_n_32bit_mode_gating() {
1171        // 64-bit registers are not available in 32-bit mode.
1172        asm32_err(|a| a.emit_n(InstId::Mov as u32, &[RAX.as_operand(), RBX.as_operand()]));
1173        asm32_err(|a| a.emit_n(InstId::Push as u32, &[RAX.as_operand()]));
1174        // Register ids above 7 are not available in 32-bit mode.
1175        asm32_err(|a| a.emit_n(InstId::Mov as u32, &[EAX.as_operand(), R8D.as_operand()]));
1176        asm32_err(|a| {
1177            a.emit_n(
1178                InstId::Paddw as u32,
1179                &[XMM0.as_operand(), XMM8.as_operand()],
1180            )
1181        });
1182        // X64-only instructions are rejected in 32-bit mode.
1183        asm32_err(|a| a.emit_n(InstId::Syscall as u32, &[]));
1184        asm32_err(|a| a.emit_n(InstId::Movsxd as u32, &[EAX.as_operand(), EBX.as_operand()]));
1185        // 64-bit addressing registers are rejected in 32-bit mode.
1186        asm32_err(|a| {
1187            a.emit_n(
1188                InstId::Mov as u32,
1189                &[EAX.as_operand(), dword_ptr(RBX, 0).as_operand()],
1190            )
1191        });
1192        // A 64-bit absolute address is not encodable in 32-bit mode.
1193        asm32_err(|a| {
1194            a.emit_n(
1195                InstId::Mov as u32,
1196                &[EAX.as_operand(), dword_ptr_u64(0x1_0000_0000).as_operand()],
1197            )
1198        });
1199    }
1200
1201    #[test]
1202    fn emit_n_32bit_string_ops() {
1203        // movsd with implicit [edi]/[esi] operands: no 67h in 32-bit mode.
1204        assert_eq!(
1205            asm32(|a| a.emit_n(
1206                InstId::Movs as u32,
1207                &[
1208                    dword_ptr(EDI, 0).as_operand(),
1209                    dword_ptr(ESI, 0).as_operand()
1210                ]
1211            )),
1212            [0xA5]
1213        );
1214        // jecxz with an explicit cx: 67h in 32-bit mode (immediate raw-disp8 form).
1215        assert_eq!(
1216            asm32(|a| a.emit_n(
1217                InstId::Jecxz as u32,
1218                &[CX.as_operand(), imm(-2).as_operand()],
1219            )),
1220            [0x67, 0xE3, 0xFE]
1221        );
1222    }
1223}