Skip to main content

isa_lang/x86_64/
encode.rs

1//! The encoder: interprets form records to turn an [`Inst`] into bytes.
2//!
3//! There is no per-instruction code here. For each candidate form of the
4//! mnemonic, in table order, the operands are matched against the form's
5//! operand kinds and placed into the fields its slots name (ModRM.reg,
6//! ModRM.rm/SIB/displacement, the opcode's low bits, the immediate); the
7//! first form that fits is emitted. Memory operands always take the shortest
8//! ModRM/SIB/displacement encoding, so equal instructions encode equally.
9
10use core::fmt;
11use core::ops::Deref;
12
13use super::error::EncodeError;
14use super::flags::FlagEffects;
15use super::form::{ATTR_NOT_R0, Form, ModRmSpec, OpcodeField, OperandKind, Slot};
16use super::inst::Inst;
17use super::operand::{Cond, Mem, Operand};
18use super::reg::{Reg, Size};
19use super::table;
20
21/// The longest x86-64 instruction, in bytes.
22pub const MAX_INST_LEN: usize = 15;
23
24/// An encoded instruction: up to 15 bytes on the stack, plus the form that
25/// produced them. Dereferences to `[u8]`.
26///
27/// # Examples
28///
29/// ```
30/// use isa_lang::x86_64::{encode, Inst, Mnemonic, Reg};
31///
32/// let enc = encode(&Inst::new(Mnemonic::Push, [Reg::R12.into()]))?;
33/// assert_eq!(&*enc, [0x41, 0x54]);
34/// assert_eq!(enc.len(), 2);
35/// let mut code = Vec::new();
36/// code.extend_from_slice(&enc);
37/// # Ok::<(), isa_lang::x86_64::EncodeError>(())
38/// ```
39#[derive(Clone, Copy, PartialEq, Eq, Hash)]
40pub struct Encoded {
41    bytes: [u8; MAX_INST_LEN],
42    flags: FlagEffects,
43    form: u16,
44    len: u8,
45    disp_at: u8,
46    imm_at: u8,
47}
48
49impl Encoded {
50    /// The encoded bytes.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use isa_lang::x86_64::{encode, Inst, Mnemonic};
56    ///
57    /// assert_eq!(encode(&Inst::new(Mnemonic::Ret, []))?.as_bytes(), [0xC3]);
58    /// # Ok::<(), isa_lang::x86_64::EncodeError>(())
59    /// ```
60    #[inline]
61    #[must_use]
62    pub fn as_bytes(&self) -> &[u8] {
63        &self.bytes[..usize::from(self.len).min(MAX_INST_LEN)]
64    }
65
66    /// The table row the instruction was encoded with.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use isa_lang::x86_64::{encode, Inst, Mnemonic, Reg};
72    ///
73    /// let enc = encode(&Inst::new(Mnemonic::Mov, [Reg::EAX.into(), Reg::ECX.into()]))?;
74    /// assert_eq!(enc.form().syntax(), "MOV r/m32, r32");
75    /// # Ok::<(), isa_lang::x86_64::EncodeError>(())
76    /// ```
77    #[must_use]
78    pub fn form(&self) -> &'static Form {
79        // Every `Encoded` is built from a table form, so the lookup succeeds;
80        // the fallback keeps this total without a panic path.
81        table::form(self.form).unwrap_or(&table::FORMS[0])
82    }
83
84    /// What the instruction does to the flags, with a condition code's reads
85    /// filled in.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use isa_lang::x86_64::{encode, Cond, Flags, Inst, Mnemonic, Operand};
91    ///
92    /// let jl = encode(&Inst::new(Mnemonic::Jcc(Cond::L), [Operand::Rel(0)]))?;
93    /// assert_eq!(jl.flags().read(), Flags::SF | Flags::OF);
94    /// # Ok::<(), isa_lang::x86_64::EncodeError>(())
95    /// ```
96    #[inline]
97    #[must_use]
98    pub const fn flags(&self) -> FlagEffects {
99        self.flags
100    }
101
102    /// Offset of the 32-bit displacement of a RIP-relative operand, if any.
103    pub(crate) const fn rip_disp_at(&self) -> Option<usize> {
104        if self.disp_at == 0 {
105            None
106        } else {
107            Some(self.disp_at as usize)
108        }
109    }
110
111    /// Offset of the immediate field, if any.
112    pub(crate) const fn imm_at(&self) -> Option<usize> {
113        if self.imm_at == 0 {
114            None
115        } else {
116            Some(self.imm_at as usize)
117        }
118    }
119}
120
121impl Deref for Encoded {
122    type Target = [u8];
123
124    #[inline]
125    fn deref(&self) -> &[u8] {
126        self.as_bytes()
127    }
128}
129
130impl AsRef<[u8]> for Encoded {
131    #[inline]
132    fn as_ref(&self) -> &[u8] {
133        self.as_bytes()
134    }
135}
136
137impl fmt::Debug for Encoded {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        f.write_str("Encoded(")?;
140        for (i, b) in self.as_bytes().iter().enumerate() {
141            if i > 0 {
142                f.write_str(" ")?;
143            }
144            write!(f, "{b:02x}")?;
145        }
146        f.write_str(")")
147    }
148}
149
150/// Encodes one instruction, choosing the shortest form that fits.
151///
152/// Forms are tried in table order, which puts shorter forms first: a small
153/// immediate takes the sign-extended 8-bit form, the accumulator forms are
154/// used where they are shorter, a near branch takes `rel8`.
155///
156/// # Errors
157///
158/// [`EncodeError`] when no form takes the operands, an immediate or branch
159/// target is out of range, a memory operand is malformed, or a high-byte
160/// register meets a REX prefix.
161///
162/// # Examples
163///
164/// ```
165/// use isa_lang::x86_64::{encode, Inst, Mem, Mnemonic, Operand, Reg, Size};
166///
167/// let i = Inst::new(Mnemonic::Add, [Reg::RAX.into(), Operand::Imm(8)]);
168/// assert_eq!(&*encode(&i)?, [0x48, 0x83, 0xC0, 0x08]);
169///
170/// let store = Inst::new(Mnemonic::Mov, [Mem::new(Size::Dword, Reg::RSP).with_disp(4).into(), Reg::R9D.into()]);
171/// assert_eq!(&*encode(&store)?, [0x44, 0x89, 0x4C, 0x24, 0x04]);
172/// # Ok::<(), isa_lang::x86_64::EncodeError>(())
173/// ```
174pub fn encode(inst: &Inst) -> Result<Encoded, EncodeError> {
175    check_memory(inst)?;
176    let (op, cond) = inst.mnemonic().parts();
177    let mut best = Miss::Kind;
178    let ops = inst.operands();
179    for &id in table::by_op(op) {
180        let Some(form) = table::form(id) else {
181            continue;
182        };
183        // Most candidates fail on operand count or kinds; skip those before
184        // any layout work.
185        if ops.len() != usize::from(form.count)
186            || !ops
187                .iter()
188                .zip(form.kinds.iter())
189                .all(|(&o, &k)| kind_fits(k, o))
190        {
191            continue;
192        }
193        match try_form(form, inst, cond) {
194            Ok(encoded) => return Ok(encoded),
195            Err(miss) => best = best.closer(miss),
196        }
197    }
198    Err(best.into_error(inst))
199}
200
201/// Encodes one instruction with exactly the given form.
202///
203/// This is the instruction selector's door: a pattern that names a form gets
204/// that form's bytes, or an error if the operands do not fit it. The
205/// `rel8`/`rel32` and register-or-memory choices are the caller's.
206///
207/// # Errors
208///
209/// [`EncodeError::NoForm`] if the form belongs to another mnemonic or does
210/// not take these operands; the other variants as for [`encode`].
211///
212/// # Examples
213///
214/// ```
215/// use isa_lang::x86_64::{encode_form, forms, Inst, Mnemonic, Operand, Reg};
216///
217/// // Force the 32-bit immediate form even though 8 bits would do.
218/// let wide = forms().iter().find(|f| f.syntax() == "ADD r/m32, imm32").unwrap();
219/// let i = Inst::new(Mnemonic::Add, [Reg::ECX.into(), Operand::Imm(1)]);
220/// assert_eq!(&*encode_form(wide, &i)?, [0x81, 0xC1, 0x01, 0x00, 0x00, 0x00]);
221/// # Ok::<(), isa_lang::x86_64::EncodeError>(())
222/// ```
223pub fn encode_form(form: &Form, inst: &Inst) -> Result<Encoded, EncodeError> {
224    check_memory(inst)?;
225    let (op, cond) = inst.mnemonic().parts();
226    // Use the table's own record: `form` may be a copy, but its id is real.
227    let form = table::form(form.id).unwrap_or(form);
228    if form.op != op {
229        return Err(EncodeError::NoForm {
230            mnemonic: inst.mnemonic(),
231        });
232    }
233    try_form(form, inst, cond).map_err(|miss| miss.into_error(inst))
234}
235
236/// Why one form did not fit, ordered from farthest to closest.
237#[derive(Clone, Copy, Debug)]
238enum Miss {
239    Kind,
240    HighByte,
241    Imm(i64),
242    Target(i64),
243}
244
245impl Miss {
246    const fn rank(self) -> u8 {
247        match self {
248            Miss::Kind => 0,
249            Miss::HighByte => 1,
250            Miss::Imm(_) => 2,
251            Miss::Target(_) => 3,
252        }
253    }
254
255    fn closer(self, other: Miss) -> Miss {
256        if other.rank() > self.rank() {
257            other
258        } else {
259            self
260        }
261    }
262
263    fn into_error(self, inst: &Inst) -> EncodeError {
264        match self {
265            Miss::Kind => EncodeError::NoForm {
266                mnemonic: inst.mnemonic(),
267            },
268            Miss::HighByte => EncodeError::HighByteWithRex,
269            Miss::Imm(value) => EncodeError::ImmediateOutOfRange { value },
270            Miss::Target(offset) => EncodeError::TargetOutOfRange { offset },
271        }
272    }
273}
274
275/// Rejects memory operands no form can encode, before any form is tried.
276fn check_memory(inst: &Inst) -> Result<(), EncodeError> {
277    for op in inst.operands() {
278        let Operand::Mem(m) = op else { continue };
279        if let Some(base) = m.base() {
280            if base.size() != Size::Qword {
281                return Err(EncodeError::BaseNotQword { base });
282            }
283        }
284        if let Some(index) = m.index() {
285            if m.is_rip_relative() {
286                return Err(EncodeError::RipWithIndex);
287            }
288            if index.size() != Size::Qword {
289                return Err(EncodeError::IndexNotQword { index });
290            }
291            if index.num() == 4 {
292                return Err(EncodeError::RspIndex);
293            }
294        }
295    }
296    Ok(())
297}
298
299/// Whether `op` has the kind and width `kind` asks for. Immediate and branch
300/// ranges are checked separately, because a range miss is a closer miss.
301fn kind_fits(kind: OperandKind, op: Operand) -> bool {
302    match (kind, op) {
303        (OperandKind::Reg(size), Operand::Reg(r)) => r.size() == size,
304        (OperandKind::RegMem(size), Operand::Reg(r)) => r.size() == size,
305        (OperandKind::RegMem(size) | OperandKind::Mem(size), Operand::Mem(m)) => m.size() == size,
306        (OperandKind::Fixed(reg), Operand::Reg(r)) => r == reg,
307        (OperandKind::One, Operand::Imm(v)) => v == 1,
308        (OperandKind::Imm { .. }, Operand::Imm(_)) => true,
309        (OperandKind::Rel { .. }, Operand::Rel(_)) => true,
310        _ => false,
311    }
312}
313
314/// Whether `value` fits an immediate field of `bits` bits.
315pub(crate) const fn imm_fits(value: i64, bits: u8, signed: bool) -> bool {
316    if bits >= 64 {
317        return signed || value >= 0;
318    }
319    if signed {
320        let half = 1i64 << (bits - 1);
321        value >= -half && value < half
322    } else {
323        value >= 0 && value < (1i64 << bits)
324    }
325}
326
327/// The ModRM/SIB/displacement part of an encoding.
328#[derive(Clone, Copy)]
329enum Disp {
330    None,
331    Byte(i8),
332    Dword(i32),
333}
334
335impl Disp {
336    const fn len(self) -> usize {
337        match self {
338            Disp::None => 0,
339            Disp::Byte(_) => 1,
340            Disp::Dword(_) => 4,
341        }
342    }
343}
344
345/// An instruction being laid out against one form.
346struct Layout<'f> {
347    form: &'f Form,
348    rex: u8,
349    force_rex: bool,
350    high_byte: bool,
351    opcode: u8,
352    reg_field: u8,
353    md: u8,
354    rm_field: u8,
355    sib: Option<u8>,
356    disp: Disp,
357    rip: bool,
358}
359
360impl<'f> Layout<'f> {
361    fn new(form: &'f Form) -> Layout<'f> {
362        Layout {
363            form,
364            rex: if form.rex_w { 0b1000 } else { 0 },
365            force_rex: false,
366            high_byte: false,
367            opcode: form.opcode,
368            reg_field: match form.modrm {
369                ModRmSpec::Ext(n) => n,
370                ModRmSpec::Reg | ModRmSpec::Absent => 0,
371            },
372            md: 0,
373            rm_field: 0,
374            sib: None,
375            disp: Disp::None,
376            rip: false,
377        }
378    }
379
380    /// Records REX needs of a register placed in an encoded field.
381    fn note_byte_reg(&mut self, r: Reg) {
382        if r.is_high_byte() {
383            self.high_byte = true;
384        } else if r.needs_rex() {
385            self.force_rex = true;
386        }
387    }
388
389    fn reg(&mut self, r: Reg) {
390        self.note_byte_reg(r);
391        self.reg_field = r.num() & 7;
392        self.rex |= (r.num() >> 3) << 2;
393    }
394
395    fn opcode_reg(&mut self, r: Reg) {
396        self.note_byte_reg(r);
397        self.opcode |= r.num() & 7;
398        self.rex |= r.num() >> 3;
399    }
400
401    fn rm_reg(&mut self, r: Reg) {
402        self.note_byte_reg(r);
403        self.md = 0b11;
404        self.rm_field = r.num() & 7;
405        self.rex |= r.num() >> 3;
406    }
407
408    fn rm_mem(&mut self, m: Mem) {
409        let disp = m.disp();
410        if m.is_rip_relative() {
411            // mod=00 rm=101 is [rip+disp32] in 64-bit mode.
412            self.md = 0b00;
413            self.rm_field = 0b101;
414            self.disp = Disp::Dword(disp);
415            self.rip = true;
416            return;
417        }
418        let index_bits = match m.index() {
419            Some(index) => {
420                self.rex |= (index.num() >> 3) << 1;
421                index.num() & 7
422            }
423            None => 0b100,
424        };
425        let scale = m.scale().bits() << 6;
426        match m.base() {
427            None => {
428                // No base: SIB with base=101 and mod=00 means disp32 only.
429                self.md = 0b00;
430                self.rm_field = 0b100;
431                self.sib = Some(scale | (index_bits << 3) | 0b101);
432                self.disp = Disp::Dword(disp);
433            }
434            Some(base) => {
435                self.rex |= base.num() >> 3;
436                let low = base.num() & 7;
437                // RBP/R13 have no mod=00 form (that encoding is RIP or
438                // disp32), so they take a zero 8-bit displacement.
439                (self.md, self.disp) = if disp == 0 && low != 0b101 {
440                    (0b00, Disp::None)
441                } else if let Ok(d) = i8::try_from(disp) {
442                    (0b01, Disp::Byte(d))
443                } else {
444                    (0b10, Disp::Dword(disp))
445                };
446                // RSP/R12 as base, or any index, need a SIB byte.
447                if m.index().is_some() || low == 0b100 {
448                    self.rm_field = 0b100;
449                    self.sib = Some(scale | (index_bits << 3) | low);
450                } else {
451                    self.rm_field = low;
452                }
453            }
454        }
455    }
456
457    fn needs_rex(&self) -> bool {
458        self.rex != 0 || self.force_rex
459    }
460
461    /// Bytes before the immediate.
462    fn prefix_len(&self) -> usize {
463        let f = self.form;
464        let modrm = if matches!(f.modrm, ModRmSpec::Absent) {
465            0
466        } else {
467            1 + usize::from(self.sib.is_some()) + self.disp.len()
468        };
469        usize::from(f.p66) + usize::from(self.needs_rex()) + usize::from(f.map) + 1 + modrm
470    }
471
472    fn write(&self, imm: i64) -> Encoded {
473        let f = self.form;
474        let mut out = Encoded {
475            bytes: [0; MAX_INST_LEN],
476            flags: f.flags,
477            form: f.id,
478            len: 0,
479            disp_at: 0,
480            imm_at: 0,
481        };
482        let mut n = 0usize;
483        let mut put = |byte: u8, n: &mut usize| {
484            if let Some(slot) = out.bytes.get_mut(*n) {
485                *slot = byte;
486            }
487            *n += 1;
488        };
489        if f.p66 {
490            put(0x66, &mut n);
491        }
492        if self.needs_rex() {
493            put(0x40 | self.rex, &mut n);
494        }
495        if f.map == 1 {
496            put(0x0F, &mut n);
497        }
498        put(self.opcode, &mut n);
499        let mut disp_at = 0;
500        if !matches!(f.modrm, ModRmSpec::Absent) {
501            put(
502                (self.md << 6) | (self.reg_field << 3) | self.rm_field,
503                &mut n,
504            );
505            if let Some(sib) = self.sib {
506                put(sib, &mut n);
507            }
508            if self.rip {
509                disp_at = n;
510            }
511            match self.disp {
512                Disp::None => {}
513                Disp::Byte(d) => put(d as u8, &mut n),
514                Disp::Dword(d) => {
515                    for b in d.to_le_bytes() {
516                        put(b, &mut n);
517                    }
518                }
519            }
520        }
521        let imm_at = if f.imm_bytes > 0 { n } else { 0 };
522        for b in imm.to_le_bytes().iter().take(usize::from(f.imm_bytes)) {
523            put(*b, &mut n);
524        }
525        // Instruction lengths here never exceed 14 bytes, so these fit.
526        out.len = n.min(MAX_INST_LEN) as u8;
527        out.disp_at = disp_at as u8;
528        out.imm_at = imm_at as u8;
529        out
530    }
531}
532
533/// Lays `inst` out against `form`, or says how close it came.
534fn try_form(form: &Form, inst: &Inst, cond: Option<Cond>) -> Result<Encoded, Miss> {
535    let ops = inst.operands();
536    if ops.len() != usize::from(form.count) {
537        return Err(Miss::Kind);
538    }
539    let mut layout = Layout::new(form);
540    if matches!(form.field, OpcodeField::Cond) {
541        layout.opcode |= cond.map_or(0, Cond::code);
542    }
543    let mut imm = 0i64;
544    let mut rel: Option<(i64, u8)> = None;
545    let mut imm_miss = None;
546    for (i, &op) in ops.iter().enumerate() {
547        let (Some(&kind), Some(&slot)) = (form.kinds.get(i), form.slots.get(i)) else {
548            return Err(Miss::Kind);
549        };
550        if !kind_fits(kind, op) {
551            return Err(Miss::Kind);
552        }
553        match (slot, op) {
554            (Slot::Implied, _) => {}
555            (Slot::Reg, Operand::Reg(r)) => layout.reg(r),
556            (Slot::Opcode, Operand::Reg(r)) => {
557                if form.attrs & ATTR_NOT_R0 != 0 && r.num() == 0 {
558                    return Err(Miss::Kind);
559                }
560                layout.opcode_reg(r);
561            }
562            (Slot::Rm, Operand::Reg(r)) => layout.rm_reg(r),
563            (Slot::Rm, Operand::Mem(m)) => layout.rm_mem(m),
564            (Slot::Imm, Operand::Imm(v)) => match kind {
565                OperandKind::Imm { bits, signed } if imm_fits(v, bits, signed) => imm = v,
566                _ => imm_miss = Some(v),
567            },
568            (Slot::Imm, Operand::Rel(d)) => match kind {
569                OperandKind::Rel { bits } => rel = Some((d, bits)),
570                _ => return Err(Miss::Kind),
571            },
572            _ => return Err(Miss::Kind),
573        }
574    }
575    if layout.needs_rex() && layout.high_byte {
576        return Err(Miss::HighByte);
577    }
578    if let Some(v) = imm_miss {
579        return Err(Miss::Imm(v));
580    }
581    if let Some((target, bits)) = rel {
582        let len = (layout.prefix_len() + usize::from(form.imm_bytes)) as i64;
583        match target.checked_sub(len) {
584            Some(r) if imm_fits(r, bits, true) => imm = r,
585            _ => return Err(Miss::Target(target)),
586        }
587    }
588    let mut encoded = layout.write(imm);
589    if form.reads_cond {
590        if let Some(c) = cond {
591            encoded.flags = encoded.flags.with_read(c.reads());
592        }
593    }
594    Ok(encoded)
595}
596
597#[cfg(test)]
598mod tests {
599    use alloc::vec;
600
601    use super::*;
602    use crate::x86_64::{Mnemonic, Scale};
603
604    fn enc(m: Mnemonic, ops: &[Operand]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
605        let inst = Inst::from_slice(m, ops).ok_or(EncodeError::NoForm { mnemonic: m })?;
606        encode(&inst).map(|e| e.to_vec())
607    }
608
609    #[test]
610    fn test_imm_fits_boundaries() {
611        assert!(imm_fits(127, 8, true) && imm_fits(-128, 8, true));
612        assert!(!imm_fits(128, 8, true) && !imm_fits(-129, 8, true));
613        assert!(imm_fits(255, 8, false) && !imm_fits(256, 8, false) && !imm_fits(-1, 8, false));
614        assert!(imm_fits(i64::MIN, 64, true) && imm_fits(i64::MAX, 64, true));
615        assert!(imm_fits(i32::MIN.into(), 32, true) && !imm_fits(1 << 31, 32, true));
616        assert!(imm_fits(65535, 16, false) && !imm_fits(65536, 16, false));
617    }
618
619    #[test]
620    fn test_memory_shapes() {
621        let mov = |m: Mem| enc(Mnemonic::Mov, &[Reg::EAX.into(), m.into()]);
622        let d = Size::Dword;
623        // [rax] -> mod=00 rm=000
624        assert_eq!(mov(Mem::new(d, Reg::RAX)), Ok(vec![0x8B, 0x00]));
625        // [rbp] -> mod=01 rm=101 disp8=0
626        assert_eq!(mov(Mem::new(d, Reg::RBP)), Ok(vec![0x8B, 0x45, 0x00]));
627        // [r13] -> REX.B, mod=01 rm=101 disp8=0
628        assert_eq!(mov(Mem::new(d, Reg::R13)), Ok(vec![0x41, 0x8B, 0x45, 0x00]));
629        // [rsp] -> SIB 24
630        assert_eq!(mov(Mem::new(d, Reg::RSP)), Ok(vec![0x8B, 0x04, 0x24]));
631        // [r12] -> REX.B, SIB 24
632        assert_eq!(mov(Mem::new(d, Reg::R12)), Ok(vec![0x41, 0x8B, 0x04, 0x24]));
633        // [rip+0x10]
634        assert_eq!(mov(Mem::rip(d, 16)), Ok(vec![0x8B, 0x05, 0x10, 0, 0, 0]));
635        // [0x10] absolute -> SIB 25 disp32
636        assert_eq!(
637            mov(Mem::absolute(d, 16)),
638            Ok(vec![0x8B, 0x04, 0x25, 0x10, 0, 0, 0])
639        );
640        // [rcx*4+0x10] -> SIB 8D disp32
641        let idx = Mem::absolute(d, 16).with_index(Reg::RCX, Scale::X4);
642        assert_eq!(mov(idx), Ok(vec![0x8B, 0x04, 0x8D, 0x10, 0, 0, 0]));
643        // [rax+r12*8-0x80] -> REX.X, mod=01, SIB E0, disp8 80
644        let m = Mem::new(d, Reg::RAX)
645            .with_index(Reg::R12, Scale::X8)
646            .with_disp(-128);
647        assert_eq!(mov(m), Ok(vec![0x42, 0x8B, 0x44, 0xE0, 0x80]));
648        // disp 128 needs disp32
649        assert_eq!(
650            mov(Mem::new(d, Reg::RAX).with_disp(128)),
651            Ok(vec![0x8B, 0x80, 0x80, 0, 0, 0])
652        );
653    }
654
655    #[test]
656    fn test_memory_errors() {
657        let mov = |m: Mem| enc(Mnemonic::Mov, &[Reg::EAX.into(), m.into()]);
658        let d = Size::Dword;
659        assert_eq!(
660            mov(Mem::new(d, Reg::EAX)),
661            Err(EncodeError::BaseNotQword { base: Reg::EAX })
662        );
663        let bad_index = Mem::new(d, Reg::RAX).with_index(Reg::ECX, Scale::X1);
664        assert_eq!(
665            mov(bad_index),
666            Err(EncodeError::IndexNotQword { index: Reg::ECX })
667        );
668        let rsp = Mem::new(d, Reg::RAX).with_index(Reg::RSP, Scale::X1);
669        assert_eq!(mov(rsp), Err(EncodeError::RspIndex));
670        let rip = Mem::rip(d, 0).with_index(Reg::RAX, Scale::X2);
671        assert_eq!(mov(rip), Err(EncodeError::RipWithIndex));
672    }
673
674    #[test]
675    fn test_high_byte_rules() {
676        assert_eq!(
677            enc(Mnemonic::Mov, &[Reg::AH.into(), Reg::BL.into()]),
678            Ok(vec![0x88, 0xDC])
679        );
680        assert_eq!(
681            enc(Mnemonic::Mov, &[Reg::SIL.into(), Reg::AL.into()]),
682            Ok(vec![0x40, 0x88, 0xC6])
683        );
684        assert_eq!(
685            enc(Mnemonic::Mov, &[Reg::AH.into(), Reg::SIL.into()]),
686            Err(EncodeError::HighByteWithRex)
687        );
688        assert_eq!(
689            enc(Mnemonic::Movzx, &[Reg::RAX.into(), Reg::AH.into()]),
690            Err(EncodeError::HighByteWithRex)
691        );
692        assert_eq!(
693            enc(Mnemonic::Movzx, &[Reg::EAX.into(), Reg::AH.into()]),
694            Ok(vec![0x0F, 0xB6, 0xC4])
695        );
696    }
697
698    #[test]
699    fn test_branch_selection() {
700        let jmp = |d| enc(Mnemonic::Jmp, &[Operand::Rel(d)]);
701        assert_eq!(jmp(2), Ok(vec![0xEB, 0x00]));
702        assert_eq!(jmp(129), Ok(vec![0xEB, 0x7F]));
703        assert_eq!(jmp(130), Ok(vec![0xE9, 0x7D, 0, 0, 0]));
704        assert_eq!(jmp(-126), Ok(vec![0xEB, 0x80]));
705        assert_eq!(jmp(-127), Ok(vec![0xE9, 0x7C, 0xFF, 0xFF, 0xFF]));
706        let far = i64::from(i32::MAX) + 6;
707        assert_eq!(jmp(far), Err(EncodeError::TargetOutOfRange { offset: far }));
708        assert_eq!(
709            jmp(i64::MIN),
710            Err(EncodeError::TargetOutOfRange { offset: i64::MIN })
711        );
712    }
713
714    #[test]
715    fn test_not_r0_avoids_nop() {
716        assert_eq!(
717            enc(Mnemonic::Xchg, &[Reg::EBX.into(), Reg::EAX.into()]),
718            Ok(vec![0x93])
719        );
720        assert_eq!(
721            enc(Mnemonic::Xchg, &[Reg::EAX.into(), Reg::EAX.into()]),
722            Ok(vec![0x87, 0xC0])
723        );
724        assert_eq!(
725            enc(Mnemonic::Xchg, &[Reg::RAX.into(), Reg::RAX.into()]),
726            Ok(vec![0x48, 0x87, 0xC0])
727        );
728        assert_eq!(
729            enc(Mnemonic::Xchg, &[Reg::R8.into(), Reg::RAX.into()]),
730            Ok(vec![0x49, 0x90])
731        );
732    }
733
734    #[test]
735    fn test_closest_miss_wins() {
736        assert_eq!(
737            enc(Mnemonic::Shl, &[Reg::EAX.into(), Operand::Imm(256)]),
738            Err(EncodeError::ImmediateOutOfRange { value: 256 })
739        );
740        assert_eq!(
741            enc(Mnemonic::Ret, &[Reg::RAX.into()]),
742            Err(EncodeError::NoForm {
743                mnemonic: Mnemonic::Ret
744            })
745        );
746    }
747
748    #[test]
749    fn test_encode_form_rejects_other_mnemonic() {
750        let add = table::by_op(crate::x86_64::inst::Op::Add)[0];
751        let form = table::form(add).copied();
752        let inst = Inst::new(Mnemonic::Sub, [Reg::AL.into(), Operand::Imm(1)]);
753        assert_eq!(
754            form.map(|f| encode_form(&f, &inst)),
755            Some(Err(EncodeError::NoForm {
756                mnemonic: Mnemonic::Sub
757            }))
758        );
759    }
760}