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}