Skip to main content

isa_lang/x86_64/
asm.rs

1//! The assembler: a code buffer with labels, fixups, and branch relaxation.
2//!
3//! Instructions whose bytes do not depend on a label are encoded at once
4//! into one flat buffer. Label-relative instructions are recorded beside it:
5//! `jmp`/`jcc` to a label as relaxable branch sites (`rel8` or `rel32`, chosen
6//! at the end), and `call` or RIP-relative references to a label as 32-bit
7//! fields patched once the layout is known.
8//!
9//! Relaxation starts with every branch short and lengthens only branches
10//! that cannot reach, until none is out of range. Lengthening only ever
11//! moves code apart, so a branch that has to be long stays long, and the
12//! result is the least fixed point: the same set of long branches whatever
13//! order they were found in, and none longer than necessary. Each
14//! lengthening re-checks only the short branches within 256 bytes of it
15//! (a short branch spans at most 129 bytes), with positions kept in a
16//! Fenwick tree, so the whole pass is O(n log n) in the number of branches.
17
18use alloc::vec;
19use alloc::vec::Vec;
20use core::fmt;
21
22use super::encode::{encode, encode_form};
23use super::error::EncodeError;
24use super::inst::{Inst, Mnemonic, Op};
25use super::nop::recommended_nop;
26use super::operand::{Cond, Operand};
27use super::table;
28
29/// A position in an [`Assembler`]'s code, bound once with
30/// [`Assembler::bind`]. Labels are only meaningful to the assembler that
31/// made them.
32///
33/// # Examples
34///
35/// ```
36/// use isa_lang::x86_64::Assembler;
37///
38/// let mut asm = Assembler::new();
39/// let a = asm.label();
40/// let b = asm.label();
41/// assert_ne!(a, b);
42/// ```
43#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
44pub struct Label(u32);
45
46impl Label {
47    /// The label's number within its assembler, in creation order.
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use isa_lang::x86_64::Assembler;
53    ///
54    /// let mut asm = Assembler::new();
55    /// assert_eq!(asm.label().index(), 0);
56    /// assert_eq!(asm.label().index(), 1);
57    /// ```
58    #[inline]
59    #[must_use]
60    pub const fn index(self) -> usize {
61        self.0 as usize
62    }
63}
64
65impl fmt::Debug for Label {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "Label({})", self.0)
68    }
69}
70
71/// Why the assembler refused an instruction or could not finish.
72///
73/// # Examples
74///
75/// ```
76/// use isa_lang::x86_64::{AsmError, Assembler};
77///
78/// let mut asm = Assembler::new();
79/// let nowhere = asm.label();
80/// asm.jmp(nowhere)?;
81/// assert_eq!(asm.finish().unwrap_err(), AsmError::Unbound(nowhere));
82/// # Ok::<(), AsmError>(())
83/// ```
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
85#[non_exhaustive]
86pub enum AsmError {
87    /// The instruction could not be encoded.
88    Encode(EncodeError),
89    /// The label was not made by this assembler.
90    UnknownLabel(Label),
91    /// The label is already bound.
92    Rebound(Label),
93    /// A branch or reference uses a label that was never bound.
94    Unbound(Label),
95    /// [`Assembler::emit_rip`] was given an instruction without a
96    /// RIP-relative memory operand.
97    NoRipOperand,
98    /// The code would exceed [`Assembler::MAX_CODE`] bytes, beyond which a
99    /// 32-bit displacement cannot reach every label.
100    TooLarge,
101}
102
103impl From<EncodeError> for AsmError {
104    fn from(e: EncodeError) -> AsmError {
105        AsmError::Encode(e)
106    }
107}
108
109impl fmt::Display for AsmError {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            AsmError::Encode(e) => fmt::Display::fmt(e, f),
113            AsmError::UnknownLabel(l) => write!(f, "label {} was not made by this assembler", l.0),
114            AsmError::Rebound(l) => write!(f, "label {} is already bound", l.0),
115            AsmError::Unbound(l) => write!(f, "label {} is used but never bound", l.0),
116            AsmError::NoRipOperand => {
117                f.write_str("the instruction has no rip-relative memory operand to resolve")
118            }
119            AsmError::TooLarge => write!(f, "code exceeds {} bytes", Assembler::MAX_CODE),
120        }
121    }
122}
123
124impl core::error::Error for AsmError {
125    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
126        match self {
127            AsmError::Encode(e) => Some(e),
128            _ => None,
129        }
130    }
131}
132
133/// Where a label is: an offset in the fixed-size code plus the number of
134/// relaxable branches recorded before it.
135#[derive(Clone, Copy)]
136struct Anchor {
137    offset: u32,
138    branches: u32,
139    bound: bool,
140}
141
142/// A `jmp`/`jcc` to a label, relaxed at `finish`.
143#[derive(Clone, Copy)]
144struct Branch {
145    at: u32,
146    label: u32,
147    cond: Option<Cond>,
148}
149
150/// A 32-bit field to resolve against a label once the layout is final.
151#[derive(Clone, Copy)]
152struct Patch {
153    field: u32,
154    end: u32,
155    branches: u32,
156    label: u32,
157    addend: i32,
158}
159
160/// Short branches are two bytes (`EB cb`, `7x cb`).
161const SHORT: u64 = 2;
162/// Lengthening adds 3 bytes to `jmp` (`E9 cd`) and 4 to `jcc` (`0F 8x cd`).
163const fn growth(cond: Option<Cond>) -> u64 {
164    if cond.is_some() { 4 } else { 3 }
165}
166/// Short branches whose start is farther than this from a lengthened branch
167/// cannot span it.
168const WINDOW: i64 = 256;
169
170const JMP_REL8: u16 = table::find_form(Op::Jmp, 1, 0);
171const JMP_REL32: u16 = table::find_form(Op::Jmp, 4, 0);
172const JCC_REL8: u16 = table::find_form(Op::Jcc, 1, 0);
173const JCC_REL32: u16 = table::find_form(Op::Jcc, 4, 1);
174
175/// An x86-64 assembler: emit instructions, branch to labels, bind them, and
176/// `finish` into machine code.
177///
178/// `jmp` and `jcc` to a label are relaxed: each is 2 bytes (`rel8`) when the
179/// final layout lets it reach, and 5 or 6 bytes (`rel32`) otherwise, with
180/// the fewest long branches possible. `call` and RIP-relative references to
181/// labels always use 32-bit displacements.
182///
183/// # Examples
184///
185/// A loop summing `1..=n`, with `n` in `edi`:
186///
187/// ```
188/// use isa_lang::x86_64::{decode_all, Assembler, Cond, Inst, Mnemonic, Operand, Reg};
189///
190/// let mut asm = Assembler::new();
191/// let top = asm.label();
192/// let done = asm.label();
193/// asm.emit(&Inst::new(Mnemonic::Xor, [Reg::EAX.into(), Reg::EAX.into()]))?;
194/// asm.emit(&Inst::new(Mnemonic::Test, [Reg::EDI.into(), Reg::EDI.into()]))?;
195/// asm.jcc(Cond::E, done)?;
196/// asm.bind(top)?;
197/// asm.emit(&Inst::new(Mnemonic::Add, [Reg::EAX.into(), Reg::EDI.into()]))?;
198/// asm.emit(&Inst::new(Mnemonic::Dec, [Reg::EDI.into()]))?;
199/// asm.jcc(Cond::Ne, top)?;
200/// asm.bind(done)?;
201/// asm.emit(&Inst::new(Mnemonic::Ret, []))?;
202/// let code = asm.finish()?;
203///
204/// assert_eq!(code.bytes().len(), 13); // both branches are short
205/// assert_eq!(code.label_offset(top), Some(6));
206/// let text: Vec<String> = decode_all(code.bytes())
207///     .map(|r| r.map(|(at, d)| d.inst().display_at(at as u64).to_string()))
208///     .collect::<Result<_, _>>()
209///     .unwrap();
210/// assert_eq!(text[2], "je 0xc");
211/// assert_eq!(text[5], "jne 0x6");
212/// # Ok::<(), isa_lang::x86_64::AsmError>(())
213/// ```
214#[derive(Clone, Default)]
215pub struct Assembler {
216    code: Vec<u8>,
217    branches: Vec<Branch>,
218    patches: Vec<Patch>,
219    labels: Vec<Anchor>,
220}
221
222impl fmt::Debug for Assembler {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224        f.debug_struct("Assembler")
225            .field("fixed_bytes", &self.code.len())
226            .field("branches", &self.branches.len())
227            .field("patches", &self.patches.len())
228            .field("labels", &self.labels.len())
229            .finish()
230    }
231}
232
233impl Assembler {
234    /// The most code one assembler produces: `i32::MAX` bytes, so every
235    /// 32-bit displacement between two points of it fits.
236    pub const MAX_CODE: usize = i32::MAX as usize;
237
238    /// An empty assembler.
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// use isa_lang::x86_64::Assembler;
244    ///
245    /// assert!(Assembler::new().finish()?.bytes().is_empty());
246    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
247    /// ```
248    #[must_use]
249    pub fn new() -> Assembler {
250        Assembler::default()
251    }
252
253    /// An empty assembler with room for `bytes` bytes of code.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use isa_lang::x86_64::Assembler;
259    ///
260    /// let asm = Assembler::with_capacity(4096);
261    /// # let _ = asm;
262    /// ```
263    #[must_use]
264    pub fn with_capacity(bytes: usize) -> Assembler {
265        Assembler {
266            code: Vec::with_capacity(bytes),
267            ..Assembler::default()
268        }
269    }
270
271    /// A new, unbound label.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// use isa_lang::x86_64::Assembler;
277    ///
278    /// let mut asm = Assembler::new();
279    /// let l = asm.label();
280    /// asm.bind(l)?;
281    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
282    /// ```
283    pub fn label(&mut self) -> Label {
284        // Past 2^32 labels the id saturates to one that never binds.
285        let id = u32::try_from(self.labels.len()).unwrap_or(u32::MAX);
286        if id != u32::MAX {
287            self.labels.push(Anchor {
288                offset: 0,
289                branches: 0,
290                bound: false,
291            });
292        }
293        Label(id)
294    }
295
296    /// Binds `label` to the current position.
297    ///
298    /// # Errors
299    ///
300    /// [`AsmError::Rebound`] if it is already bound, [`AsmError::UnknownLabel`]
301    /// if another assembler made it.
302    ///
303    /// # Examples
304    ///
305    /// ```
306    /// use isa_lang::x86_64::{AsmError, Assembler};
307    ///
308    /// let mut asm = Assembler::new();
309    /// let l = asm.label();
310    /// asm.bind(l)?;
311    /// assert_eq!(asm.bind(l), Err(AsmError::Rebound(l)));
312    /// # Ok::<(), AsmError>(())
313    /// ```
314    pub fn bind(&mut self, label: Label) -> Result<(), AsmError> {
315        let offset = self.code.len() as u32;
316        let branches = self.branches.len() as u32;
317        let anchor = self
318            .labels
319            .get_mut(label.index())
320            .ok_or(AsmError::UnknownLabel(label))?;
321        if anchor.bound {
322            return Err(AsmError::Rebound(label));
323        }
324        *anchor = Anchor {
325            offset,
326            branches,
327            bound: true,
328        };
329        Ok(())
330    }
331
332    /// Encodes `inst` at the current position.
333    ///
334    /// `Rel` operands are encoded as given, relative to this instruction's
335    /// final address; to reach other code in this assembler, use labels
336    /// ([`jmp`](Assembler::jmp), [`jcc`](Assembler::jcc), [`call`](Assembler::call),
337    /// [`emit_rip`](Assembler::emit_rip)).
338    ///
339    /// # Errors
340    ///
341    /// [`AsmError::Encode`] if the instruction cannot be encoded,
342    /// [`AsmError::TooLarge`] past [`MAX_CODE`](Assembler::MAX_CODE).
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// use isa_lang::x86_64::{Assembler, Inst, Mnemonic};
348    ///
349    /// let mut asm = Assembler::new();
350    /// asm.emit(&Inst::new(Mnemonic::Ud2, []))?;
351    /// assert_eq!(asm.finish()?.bytes(), [0x0F, 0x0B]);
352    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
353    /// ```
354    pub fn emit(&mut self, inst: &Inst) -> Result<(), AsmError> {
355        let encoded = encode(inst)?;
356        self.append(&encoded)
357    }
358
359    /// Emits raw bytes: data, or code from elsewhere.
360    ///
361    /// # Errors
362    ///
363    /// [`AsmError::TooLarge`] past [`MAX_CODE`](Assembler::MAX_CODE).
364    ///
365    /// # Examples
366    ///
367    /// ```
368    /// use isa_lang::x86_64::Assembler;
369    ///
370    /// let mut asm = Assembler::new();
371    /// asm.bytes(&[0xDE, 0xAD])?;
372    /// assert_eq!(asm.finish()?.bytes(), [0xDE, 0xAD]);
373    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
374    /// ```
375    pub fn bytes(&mut self, data: &[u8]) -> Result<(), AsmError> {
376        self.append(data)
377    }
378
379    /// Emits `len` bytes of no-ops, using the architecture manual's
380    /// recommended multi-byte `NOP` sequences (at most 9 bytes each).
381    ///
382    /// # Errors
383    ///
384    /// [`AsmError::TooLarge`] past [`MAX_CODE`](Assembler::MAX_CODE).
385    ///
386    /// # Examples
387    ///
388    /// ```
389    /// use isa_lang::x86_64::{decode_all, Assembler};
390    ///
391    /// let mut asm = Assembler::new();
392    /// asm.nops(12)?;
393    /// let code = asm.finish()?;
394    /// assert_eq!(code.bytes().len(), 12);
395    /// assert_eq!(decode_all(code.bytes()).count(), 2); // 9 + 3
396    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
397    /// ```
398    pub fn nops(&mut self, len: usize) -> Result<(), AsmError> {
399        self.reserve(len)?;
400        let mut left = len;
401        while left > 0 {
402            let step = left.min(9);
403            self.code
404                .extend_from_slice(recommended_nop(step).unwrap_or(&[]));
405            left -= step;
406        }
407        Ok(())
408    }
409
410    /// Emits `jmp label`, as `rel8` or `rel32` as the final layout allows.
411    ///
412    /// # Errors
413    ///
414    /// [`AsmError::UnknownLabel`] for another assembler's label,
415    /// [`AsmError::TooLarge`] past [`MAX_CODE`](Assembler::MAX_CODE).
416    ///
417    /// # Examples
418    ///
419    /// ```
420    /// use isa_lang::x86_64::Assembler;
421    ///
422    /// let mut asm = Assembler::new();
423    /// let here = asm.label();
424    /// asm.bind(here)?;
425    /// asm.jmp(here)?;
426    /// assert_eq!(asm.finish()?.bytes(), [0xEB, 0xFE]);
427    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
428    /// ```
429    pub fn jmp(&mut self, label: Label) -> Result<(), AsmError> {
430        self.branch(label, None)
431    }
432
433    /// Emits `jcc label`, as `rel8` or `rel32` as the final layout allows.
434    ///
435    /// # Errors
436    ///
437    /// As for [`jmp`](Assembler::jmp).
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// use isa_lang::x86_64::{Assembler, Cond};
443    ///
444    /// let mut asm = Assembler::new();
445    /// let skip = asm.label();
446    /// asm.jcc(Cond::Ae, skip)?;
447    /// asm.nops(200)?;
448    /// asm.bind(skip)?;
449    /// assert_eq!(asm.finish()?.bytes()[..6], [0x0F, 0x83, 0xC8, 0x00, 0x00, 0x00]);
450    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
451    /// ```
452    pub fn jcc(&mut self, cond: Cond, label: Label) -> Result<(), AsmError> {
453        self.branch(label, Some(cond))
454    }
455
456    /// Emits `call label` (always `rel32`).
457    ///
458    /// # Errors
459    ///
460    /// As for [`jmp`](Assembler::jmp).
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// use isa_lang::x86_64::{Assembler, Inst, Mnemonic};
466    ///
467    /// let mut asm = Assembler::new();
468    /// let f = asm.label();
469    /// asm.call(f)?;
470    /// asm.emit(&Inst::new(Mnemonic::Ret, []))?;
471    /// asm.bind(f)?;
472    /// asm.emit(&Inst::new(Mnemonic::Ret, []))?;
473    /// assert_eq!(asm.finish()?.bytes(), [0xE8, 0x01, 0x00, 0x00, 0x00, 0xC3, 0xC3]);
474    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
475    /// ```
476    pub fn call(&mut self, label: Label) -> Result<(), AsmError> {
477        self.check_label(label)?;
478        let encoded = encode(&Inst::new(Mnemonic::Call, [Operand::Rel(5)]))?;
479        let field = encoded.imm_at().unwrap_or(1);
480        self.patched(&encoded, field, label, 0)
481    }
482
483    /// Emits `inst`, whose RIP-relative memory operand is resolved to point
484    /// at `label`. The operand's displacement is added to the label's
485    /// address, so `Mem::rip(size, 8)` addresses `label + 8`.
486    ///
487    /// # Errors
488    ///
489    /// [`AsmError::NoRipOperand`] if `inst` has no RIP-relative operand; the
490    /// errors of [`emit`](Assembler::emit) and [`jmp`](Assembler::jmp).
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// use isa_lang::x86_64::{Assembler, Inst, Mem, Mnemonic, Reg, Size};
496    ///
497    /// let mut asm = Assembler::new();
498    /// let data = asm.label();
499    /// asm.emit_rip(&Inst::new(Mnemonic::Mov, [Reg::RAX.into(), Mem::rip(Size::Qword, 0).into()]), data)?;
500    /// asm.emit(&Inst::new(Mnemonic::Ret, []))?;
501    /// asm.bind(data)?;
502    /// asm.bytes(&42u64.to_le_bytes())?;
503    /// let code = asm.finish()?;
504    /// // mov rax, [rip+1] skips the one-byte ret.
505    /// assert_eq!(code.bytes()[..7], [0x48, 0x8B, 0x05, 0x01, 0x00, 0x00, 0x00]);
506    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
507    /// ```
508    pub fn emit_rip(&mut self, inst: &Inst, label: Label) -> Result<(), AsmError> {
509        self.check_label(label)?;
510        let addend = inst
511            .operands()
512            .iter()
513            .find_map(|op| match op {
514                Operand::Mem(m) if m.is_rip_relative() => Some(m.disp()),
515                _ => None,
516            })
517            .ok_or(AsmError::NoRipOperand)?;
518        let encoded = encode(inst)?;
519        let field = encoded.rip_disp_at().ok_or(AsmError::NoRipOperand)?;
520        self.patched(&encoded, field, label, addend)
521    }
522
523    /// Resolves every label and returns the machine code.
524    ///
525    /// # Errors
526    ///
527    /// [`AsmError::Unbound`] if a branch or reference names a label that was
528    /// never bound.
529    ///
530    /// # Examples
531    ///
532    /// ```
533    /// use isa_lang::x86_64::{Assembler, Inst, Mnemonic};
534    ///
535    /// let mut asm = Assembler::new();
536    /// let end = asm.label();
537    /// asm.jmp(end)?;
538    /// asm.emit(&Inst::new(Mnemonic::Int3, []))?;
539    /// asm.bind(end)?;
540    /// let code = asm.finish()?;
541    /// assert_eq!(code.bytes(), [0xEB, 0x01, 0xCC]);
542    /// assert_eq!(code.label_offset(end), Some(3));
543    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
544    /// ```
545    pub fn finish(self) -> Result<Code, AsmError> {
546        let layout = Layout::relax(&self)?;
547        let mut out = Vec::with_capacity(layout.total);
548        let mut prev = 0usize;
549        for (i, b) in self.branches.iter().enumerate() {
550            let at = b.at as usize;
551            out.extend_from_slice(self.code.get(prev..at).unwrap_or(&[]));
552            let start = layout.branch_start(i, b.at);
553            let target = layout.label_pos(self.anchor(b.label)?);
554            let mnemonic = match b.cond {
555                Some(c) => Mnemonic::Jcc(c),
556                None => Mnemonic::Jmp,
557            };
558            let form = match (b.cond.is_some(), layout.long[i]) {
559                (false, false) => JMP_REL8,
560                (false, true) => JMP_REL32,
561                (true, false) => JCC_REL8,
562                (true, true) => JCC_REL32,
563            };
564            let inst = Inst::new(mnemonic, [Operand::Rel(target - start)]);
565            let form = table::form(form).ok_or(AsmError::TooLarge)?;
566            out.extend_from_slice(&encode_form(form, &inst)?);
567            prev = at;
568        }
569        out.extend_from_slice(self.code.get(prev..).unwrap_or(&[]));
570        for p in &self.patches {
571            let shift = layout.shift(p.branches);
572            let field = (i64::from(p.field) + shift) as usize;
573            let end = i64::from(p.end) + shift;
574            let disp = layout.label_pos(self.anchor(p.label)?) + i64::from(p.addend) - end;
575            let disp = i32::try_from(disp).map_err(|_| AsmError::TooLarge)?;
576            if let Some(slot) = out.get_mut(field..field + 4) {
577                slot.copy_from_slice(&disp.to_le_bytes());
578            }
579        }
580        let labels = self
581            .labels
582            .iter()
583            .map(|&a| {
584                if a.bound {
585                    layout.label_pos(a) as u32
586                } else {
587                    u32::MAX
588                }
589            })
590            .collect();
591        Ok(Code { bytes: out, labels })
592    }
593
594    /// The anchor of a bound label.
595    fn anchor(&self, label: u32) -> Result<Anchor, AsmError> {
596        match self.labels.get(label as usize) {
597            Some(&a) if a.bound => Ok(a),
598            _ => Err(AsmError::Unbound(Label(label))),
599        }
600    }
601
602    fn check_label(&self, label: Label) -> Result<(), AsmError> {
603        if label.index() < self.labels.len() {
604            Ok(())
605        } else {
606            Err(AsmError::UnknownLabel(label))
607        }
608    }
609
610    /// Checks that `extra` more bytes, plus the worst-case growth of every
611    /// branch, stay within `MAX_CODE`.
612    fn reserve(&self, extra: usize) -> Result<(), AsmError> {
613        let worst = self
614            .code
615            .len()
616            .checked_add(extra)
617            .and_then(|n| n.checked_add(self.branches.len().checked_mul(6)?));
618        match worst {
619            Some(n) if n <= Assembler::MAX_CODE => Ok(()),
620            _ => Err(AsmError::TooLarge),
621        }
622    }
623
624    fn append(&mut self, bytes: &[u8]) -> Result<(), AsmError> {
625        self.reserve(bytes.len())?;
626        self.code.extend_from_slice(bytes);
627        Ok(())
628    }
629
630    fn branch(&mut self, label: Label, cond: Option<Cond>) -> Result<(), AsmError> {
631        self.check_label(label)?;
632        self.reserve(6)?;
633        self.branches.push(Branch {
634            at: self.code.len() as u32,
635            label: label.0,
636            cond,
637        });
638        Ok(())
639    }
640
641    fn patched(
642        &mut self,
643        bytes: &[u8],
644        field: usize,
645        label: Label,
646        addend: i32,
647    ) -> Result<(), AsmError> {
648        let start = self.code.len();
649        self.append(bytes)?;
650        self.patches.push(Patch {
651            field: (start + field) as u32,
652            end: (start + bytes.len()) as u32,
653            branches: self.branches.len() as u32,
654            label: label.0,
655            addend,
656        });
657        Ok(())
658    }
659}
660
661/// The relaxed layout: which branches are long, and positions through a
662/// Fenwick tree of how much each long branch grew.
663struct Layout {
664    long: Vec<bool>,
665    growth: Fenwick,
666    total: usize,
667}
668
669impl Layout {
670    fn relax(asm: &Assembler) -> Result<Layout, AsmError> {
671        // Every referenced label must be bound before positions mean anything.
672        let unbound = |label: u32| match asm.labels.get(label as usize) {
673            Some(a) if a.bound => None,
674            _ => Some(AsmError::Unbound(Label(label))),
675        };
676        for b in &asm.branches {
677            if let Some(e) = unbound(b.label) {
678                return Err(e);
679            }
680        }
681        for p in &asm.patches {
682            if let Some(e) = unbound(p.label) {
683                return Err(e);
684            }
685        }
686        let n = asm.branches.len();
687        let mut layout = Layout {
688            long: vec![false; n],
689            growth: Fenwick::new(n),
690            total: 0,
691        };
692        let mut work = Worklist {
693            queued: vec![false; n],
694            stack: Vec::new(),
695        };
696        for (i, b) in asm.branches.iter().enumerate() {
697            if !layout.reaches(asm, i, b) {
698                work.push(i);
699            }
700        }
701        while let Some(i) = work.stack.pop() {
702            let Some(&b) = asm.branches.get(i) else {
703                continue;
704            };
705            layout.long[i] = true;
706            layout.growth.add(i, growth(b.cond));
707            let here = layout.branch_start(i, b.at);
708            // Only short branches near `i` can span it; check them again.
709            // Branch starts strictly increase, so each scan stops at the
710            // first branch outside the window.
711            let before = asm
712                .branches
713                .get(..i)
714                .unwrap_or(&[])
715                .iter()
716                .enumerate()
717                .rev();
718            for (j, bj) in before {
719                if layout.branch_start(j, bj.at) < here - WINDOW {
720                    break;
721                }
722                if !work.is_queued(j) && !layout.reaches(asm, j, bj) {
723                    work.push(j);
724                }
725            }
726            let after = asm.branches.iter().enumerate().skip(i + 1);
727            for (j, bj) in after {
728                if layout.branch_start(j, bj.at) > here + WINDOW {
729                    break;
730                }
731                if !work.is_queued(j) && !layout.reaches(asm, j, bj) {
732                    work.push(j);
733                }
734            }
735        }
736        let grown = layout.growth.prefix(n);
737        layout.total = asm.code.len() + (SHORT as usize) * n + grown as usize;
738        Ok(layout)
739    }
740
741    /// Bytes inserted before a point that follows `branches` branch sites.
742    fn shift(&self, branches: u32) -> i64 {
743        (SHORT * u64::from(branches) + self.growth.prefix(branches as usize)) as i64
744    }
745
746    fn label_pos(&self, a: Anchor) -> i64 {
747        i64::from(a.offset) + self.shift(a.branches)
748    }
749
750    fn branch_start(&self, i: usize, at: u32) -> i64 {
751        i64::from(at) + self.shift(i as u32)
752    }
753
754    /// Whether short branch `i` reaches its label in the current layout.
755    fn reaches(&self, asm: &Assembler, i: usize, b: &Branch) -> bool {
756        let Some(&anchor) = asm.labels.get(b.label as usize) else {
757            return false;
758        };
759        let end = self.branch_start(i, b.at) + SHORT as i64;
760        let disp = self.label_pos(anchor) - end;
761        (-128..=127).contains(&disp)
762    }
763}
764
765/// Branches found out of range and not yet lengthened. Each is queued at
766/// most once: a branch that cannot reach never reaches again.
767struct Worklist {
768    queued: Vec<bool>,
769    stack: Vec<usize>,
770}
771
772impl Worklist {
773    fn is_queued(&self, i: usize) -> bool {
774        self.queued.get(i).copied().unwrap_or(true)
775    }
776
777    fn push(&mut self, i: usize) {
778        if let Some(q) = self.queued.get_mut(i) {
779            *q = true;
780            self.stack.push(i);
781        }
782    }
783}
784
785/// Prefix sums over branch growth, updated one branch at a time.
786struct Fenwick {
787    tree: Vec<u64>,
788}
789
790impl Fenwick {
791    fn new(n: usize) -> Fenwick {
792        Fenwick { tree: vec![0; n] }
793    }
794
795    fn add(&mut self, index: usize, value: u64) {
796        let mut i = index + 1;
797        while let Some(slot) = self.tree.get_mut(i - 1) {
798            *slot += value;
799            i += i & i.wrapping_neg();
800        }
801    }
802
803    /// The sum over indices `0..end`.
804    fn prefix(&self, end: usize) -> u64 {
805        let mut i = end.min(self.tree.len());
806        let mut sum = 0;
807        while i > 0 {
808            sum += self.tree.get(i - 1).copied().unwrap_or(0);
809            i &= i - 1;
810        }
811        sum
812    }
813}
814
815/// Finished machine code and the final offsets of its labels.
816///
817/// # Examples
818///
819/// ```
820/// use isa_lang::x86_64::{Assembler, Inst, Mnemonic};
821///
822/// let mut asm = Assembler::new();
823/// let entry = asm.label();
824/// asm.bind(entry)?;
825/// asm.emit(&Inst::new(Mnemonic::Ret, []))?;
826/// let code = asm.finish()?;
827/// assert_eq!(code.label_offset(entry), Some(0));
828/// assert_eq!(code.into_bytes(), vec![0xC3]);
829/// # Ok::<(), isa_lang::x86_64::AsmError>(())
830/// ```
831#[derive(Clone, PartialEq, Eq)]
832pub struct Code {
833    bytes: Vec<u8>,
834    labels: Vec<u32>,
835}
836
837impl Code {
838    /// The machine code.
839    ///
840    /// # Examples
841    ///
842    /// ```
843    /// use isa_lang::x86_64::{Assembler, Inst, Mnemonic};
844    ///
845    /// let mut asm = Assembler::new();
846    /// asm.emit(&Inst::new(Mnemonic::Cqo, []))?;
847    /// assert_eq!(asm.finish()?.bytes(), [0x48, 0x99]);
848    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
849    /// ```
850    #[inline]
851    #[must_use]
852    pub fn bytes(&self) -> &[u8] {
853        &self.bytes
854    }
855
856    /// The machine code, by value.
857    ///
858    /// # Examples
859    ///
860    /// ```
861    /// use isa_lang::x86_64::Assembler;
862    ///
863    /// assert!(Assembler::new().finish()?.into_bytes().is_empty());
864    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
865    /// ```
866    #[must_use]
867    pub fn into_bytes(self) -> Vec<u8> {
868        self.bytes
869    }
870
871    /// Where `label` ended up, or `None` if it was never bound (or belongs
872    /// to another assembler).
873    ///
874    /// # Examples
875    ///
876    /// ```
877    /// use isa_lang::x86_64::Assembler;
878    ///
879    /// let mut asm = Assembler::new();
880    /// let unused = asm.label();
881    /// assert_eq!(asm.finish()?.label_offset(unused), None);
882    /// # Ok::<(), isa_lang::x86_64::AsmError>(())
883    /// ```
884    #[must_use]
885    pub fn label_offset(&self, label: Label) -> Option<usize> {
886        match self.labels.get(label.index()) {
887            Some(&off) if off != u32::MAX => Some(off as usize),
888            _ => None,
889        }
890    }
891}
892
893impl fmt::Debug for Code {
894    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
895        f.debug_struct("Code")
896            .field("len", &self.bytes.len())
897            .finish()
898    }
899}
900
901#[cfg(test)]
902mod tests {
903    use super::*;
904    use crate::x86_64::Reg;
905
906    #[test]
907    fn test_fenwick_prefix_sums() {
908        let mut f = Fenwick::new(10);
909        for i in 0..10 {
910            f.add(i, i as u64 + 1);
911        }
912        for end in 0..=10 {
913            let expect: u64 = (1..=end as u64).sum();
914            assert_eq!(f.prefix(end), expect);
915        }
916        assert_eq!(f.prefix(100), 55);
917        let empty = Fenwick::new(0);
918        assert_eq!(empty.prefix(5), 0);
919    }
920
921    #[test]
922    fn test_branch_forms_are_the_table_rows() {
923        let syntax = |id| table::form(id).map(|f| f.syntax());
924        assert_eq!(syntax(JMP_REL8), Some("JMP rel8"));
925        assert_eq!(syntax(JMP_REL32), Some("JMP rel32"));
926        assert_eq!(syntax(JCC_REL8), Some("Jcc rel8"));
927        assert_eq!(syntax(JCC_REL32), Some("Jcc rel32"));
928    }
929
930    #[test]
931    fn test_unknown_label_from_another_assembler() {
932        let mut other = Assembler::new();
933        let _ = other.label();
934        let foreign = other.label();
935        let mut asm = Assembler::new();
936        assert_eq!(asm.jmp(foreign), Err(AsmError::UnknownLabel(foreign)));
937        assert_eq!(asm.bind(foreign), Err(AsmError::UnknownLabel(foreign)));
938        assert_eq!(asm.call(foreign), Err(AsmError::UnknownLabel(foreign)));
939    }
940
941    #[test]
942    fn test_emit_rip_requires_rip_operand() {
943        let mut asm = Assembler::new();
944        let l = asm.label();
945        let inst = Inst::new(Mnemonic::Mov, [Reg::RAX.into(), Reg::RBX.into()]);
946        assert_eq!(asm.emit_rip(&inst, l), Err(AsmError::NoRipOperand));
947    }
948
949    #[test]
950    fn test_boundary_127_and_128() {
951        // A forward jmp over exactly 127 bytes is short; 128 is long.
952        for (gap, expect_len) in [(127usize, 2usize), (128, 5)] {
953            let mut asm = Assembler::new();
954            let l = asm.label();
955            assert!(asm.jmp(l).is_ok());
956            assert!(asm.bytes(&vec![0x90; gap]).is_ok());
957            assert!(asm.bind(l).is_ok());
958            let code = asm.finish();
959            assert_eq!(code.map(|c| c.bytes().len() - gap), Ok(expect_len));
960        }
961    }
962}