Skip to main content

backend_lang/x86_64/
emit.rs

1//! Emission: a MIR function on physical registers, through isa-lang's
2//! assembler, to x86-64 machine code.
3//!
4//! The emitter lays out the frame, writes the prologue, gives every block a
5//! label, and encodes each instruction through the row's isa-lang mapping.
6//! Control-flow edges with arguments, `copy`, and `pcopy` are parallel copies
7//! and go through the [`MoveResolver`](crate::moves::MoveResolver): register
8//! cycles become `xchg`, memory-to-memory moves go through a register borrowed
9//! around the move (saved to a hidden frame slot), and a cycle through memory
10//! is broken with a second hidden slot. The hidden slots are added to the
11//! frame only when an edge needs them.
12//!
13//! A conditional branch whose taken edge carries moves gets those moves on
14//! its own path: the emitter branches on the negated condition around them,
15//! which splits the critical edge without adding a block. Moves never touch
16//! the flags, and every return runs the epilogue before `ret`.
17//!
18//! Functions are emitted into one [`Emitter`] and call each other by name;
19//! [`Emitter::finish`] runs isa-lang's branch relaxation and patches calls,
20//! and reports any name that was called but never defined.
21
22use alloc::collections::BTreeMap;
23use alloc::string::{String, ToString};
24use alloc::vec::Vec;
25use core::fmt;
26
27use isa_lang::x86_64::{
28    AsmError, Assembler, Cond as ICond, EncodeError, Inst as IInst, Label, Mem as IMem,
29    Mnemonic as M, Operand as IOp, Reg as IReg, Scale as IScale, Size,
30};
31
32use super::table::{DESCS, ENCODINGS, Enc, Fam, Src};
33use super::{GPR, R10, R11, REGS};
34use crate::mir::{
35    Block, BlockCall, Function, ImplicitKind, Inst, Mem, MirError, Operand, Scale, SlotKind,
36    StackSlot,
37};
38use crate::moves::{Loc, Move, MoveEnv, MoveError, MoveResolver, Source, Step};
39use crate::regs::{PReg, Reg, RegClass};
40use crate::target::frame::{FrameError, FrameInput, FrameLayout, FrameStep};
41use crate::target::{InstKind, OperandRule};
42
43/// Why a function could not be emitted.
44///
45/// # Examples
46///
47/// ```
48/// use backend_lang::mir::{Function, Operand};
49/// use backend_lang::x86_64::{op, EmitError, GPR, SYSV};
50///
51/// // Emission needs physical registers: run register allocation first.
52/// let mut f = Function::new("f", &SYSV);
53/// let b = f.block()?;
54/// let v = f.vreg(GPR)?;
55/// f.push(b, op::MOV64RI, &[v.into(), Operand::Imm(1)])?;
56/// f.push(b, op::RET, &[])?;
57/// let err = backend_lang::emit(&f).unwrap_err();
58/// assert!(matches!(err, EmitError::VirtualRegister { .. }));
59/// # Ok::<(), backend_lang::mir::MirError>(())
60/// ```
61#[derive(Clone, Debug, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum EmitError {
64    /// The function is malformed ([`Function::verify`] fails).
65    Mir(MirError),
66    /// The frame cannot be laid out.
67    Frame(FrameError),
68    /// A parallel copy cannot be resolved.
69    Moves(MoveError),
70    /// The function's target is not this crate's x86-64 description (a
71    /// custom [`Target`](crate::target::Target) must reuse its register and
72    /// instruction tables to be emitted by this emitter).
73    UnsupportedTarget,
74    /// A virtual register remains (in an instruction, or, with `inst` absent,
75    /// in a block parameter). Emission runs after register allocation.
76    VirtualRegister {
77        /// The block.
78        block: Block,
79        /// The instruction, if not a parameter.
80        inst: Option<Inst>,
81    },
82    /// A tied use is not the same register as its definition. The register
83    /// allocator must give them one register (or insert a copy).
84    TiedMismatch {
85        /// The instruction.
86        inst: Inst,
87    },
88    /// The body writes the stack pointer, or the frame pointer while it is
89    /// kept (in an instruction, or, with `inst` absent, as a block
90    /// parameter). The frame owns both.
91    ReservedWrite {
92        /// The block.
93        block: Block,
94        /// The instruction, if not a parameter.
95        inst: Option<Inst>,
96    },
97    /// The operation needs an instruction isa-lang 0.2 does not encode: a
98    /// move of a vector register, or a Win64 prologue saving one. (With
99    /// `inst` absent: a prologue, epilogue, or edge move.)
100    Unencodable {
101        /// The instruction, if one.
102        inst: Option<Inst>,
103    },
104    /// isa-lang refused an instruction (with `inst` absent: a prologue,
105    /// epilogue, or edge move).
106    Encode {
107        /// The instruction, if one.
108        inst: Option<Inst>,
109        /// isa-lang's reason.
110        error: EncodeError,
111    },
112    /// The assembler failed (code beyond 2 GiB, or a label error).
113    Asm(AsmError),
114    /// A function was called (or its address taken) but never emitted.
115    UndefinedSymbol(String),
116    /// Two functions with one name were emitted.
117    DuplicateSymbol(String),
118    /// A frame large enough to need a probe loop takes its counter in `r11`
119    /// or `r10`, and the entry block receives values in both.
120    ProbeScratch,
121    /// An operand constraint the instruction relies on does not hold: a
122    /// table branch's temporary is its index register, or a tail call's
123    /// register target is one the epilogue restores.
124    Constraint {
125        /// The instruction.
126        inst: Inst,
127    },
128    /// A function was declared external and also emitted, or a name was
129    /// declared external twice in conflicting ways.
130    ExternalConflict(String),
131}
132
133impl fmt::Display for EmitError {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match self {
136            EmitError::Mir(e) => fmt::Display::fmt(e, f),
137            EmitError::Frame(e) => fmt::Display::fmt(e, f),
138            EmitError::Moves(e) => fmt::Display::fmt(e, f),
139            EmitError::UnsupportedTarget => {
140                f.write_str("the function's target is not the x86-64 description this emitter encodes")
141            }
142            EmitError::VirtualRegister { block, inst } => match inst {
143                Some(i) => write!(f, "{i} in {block} still names a virtual register; allocate registers first"),
144                None => write!(f, "a parameter of {block} is a virtual register; allocate registers first"),
145            },
146            EmitError::TiedMismatch { inst } => write!(
147                f,
148                "{inst}: a tied use must be the same register as its definition"
149            ),
150            EmitError::ReservedWrite { block, inst } => match inst {
151                Some(i) => write!(f, "{i} in {block} writes the stack or frame pointer"),
152                None => write!(f, "a parameter of {block} is the stack or frame pointer"),
153            },
154            EmitError::Unencodable { inst } => match inst {
155                Some(i) => write!(f, "{i}: isa-lang 0.2 has no encoding for this vector-register operation"),
156                None => f.write_str("a frame or edge move needs a vector-register instruction isa-lang 0.2 does not encode"),
157            },
158            EmitError::Encode { inst, error } => match inst {
159                Some(i) => write!(f, "{i}: {error}"),
160                None => write!(f, "frame or edge move: {error}"),
161            },
162            EmitError::Asm(e) => fmt::Display::fmt(e, f),
163            EmitError::UndefinedSymbol(name) => {
164                write!(f, "`{name}` is called but no function of that name was emitted")
165            }
166            EmitError::DuplicateSymbol(name) => {
167                write!(f, "two functions named `{name}` were emitted")
168            }
169            EmitError::ProbeScratch => f.write_str(
170                "the stack probe loop needs r10 or r11, and the entry block receives values in both",
171            ),
172            EmitError::Constraint { inst } => write!(
173                f,
174                "{inst}: a table branch's temporary is its index, or a tail call's register target is restored by the epilogue"
175            ),
176            EmitError::ExternalConflict(name) => {
177                write!(f, "`{name}` is declared external and also emitted")
178            }
179        }
180    }
181}
182
183impl core::error::Error for EmitError {
184    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
185        match self {
186            EmitError::Mir(e) => Some(e),
187            EmitError::Frame(e) => Some(e),
188            EmitError::Moves(e) => Some(e),
189            EmitError::Encode { error, .. } => Some(error),
190            EmitError::Asm(e) => Some(e),
191            _ => None,
192        }
193    }
194}
195
196impl From<MirError> for EmitError {
197    fn from(e: MirError) -> EmitError {
198        EmitError::Mir(e)
199    }
200}
201
202impl From<FrameError> for EmitError {
203    fn from(e: FrameError) -> EmitError {
204        EmitError::Frame(e)
205    }
206}
207
208impl From<MoveError> for EmitError {
209    fn from(e: MoveError) -> EmitError {
210        EmitError::Moves(e)
211    }
212}
213
214/// What emission decided about one function.
215///
216/// # Examples
217///
218/// ```
219/// use backend_lang::mir::Function;
220/// use backend_lang::x86_64::{op, Emitter, SYSV};
221///
222/// let mut f = Function::new("f", &SYSV);
223/// let b = f.block()?;
224/// f.push(b, op::RET, &[])?;
225/// let mut e = Emitter::new();
226/// let info = e.function(&f)?;
227/// assert_eq!(info.frame().alloc(), 0);
228/// # Ok::<(), Box<dyn std::error::Error>>(())
229/// ```
230#[derive(Clone, Debug, PartialEq, Eq)]
231pub struct FunctionInfo {
232    frame: FrameLayout,
233}
234
235impl FunctionInfo {
236    /// The frame as laid out, including any hidden slots edge moves needed
237    /// (numbered after the function's own).
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use backend_lang::mir::{Function, SlotKind};
243    /// use backend_lang::x86_64::{op, Emitter, SYSV};
244    ///
245    /// let mut f = Function::new("f", &SYSV);
246    /// let s = f.stack_slot(SlotKind::Local { size: 8, align: 8 })?;
247    /// let b = f.block()?;
248    /// f.push(b, op::RET, &[])?;
249    /// let info = Emitter::new().function(&f)?;
250    /// assert!(info.frame().slot_offset(s).is_some());
251    /// # Ok::<(), Box<dyn std::error::Error>>(())
252    /// ```
253    #[inline]
254    #[must_use]
255    pub fn frame(&self) -> &FrameLayout {
256        &self.frame
257    }
258}
259
260/// What a relocation computes. `S` is the symbol's address, `A` the addend,
261/// and `P` the address of the patched bytes.
262///
263/// # Examples
264///
265/// ```
266/// use backend_lang::x86_64::RelocKind;
267///
268/// assert_eq!(RelocKind::Call32.size(), 4);
269/// assert_eq!(RelocKind::Abs64.size(), 8);
270/// ```
271#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
272#[non_exhaustive]
273pub enum RelocKind {
274    /// `S + A - P`, 32 bits: the displacement of a `call` or `jmp` to a
275    /// function (ELF `R_X86_64_PLT32`; object-lang `X86_64Reloc::Plt32`).
276    Call32,
277    /// `S + A - P`, 32 bits: the displacement of a RIP-relative address
278    /// (ELF `R_X86_64_PC32`; object-lang `X86_64Reloc::Pc32`).
279    Pc32,
280    /// `S + A`, 64 bits: an absolute address stored in data (ELF
281    /// `R_X86_64_64`; object-lang `X86_64Reloc::Abs64`).
282    Abs64,
283}
284
285impl RelocKind {
286    /// How many bytes the relocation patches.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use backend_lang::x86_64::RelocKind;
292    ///
293    /// assert_eq!(RelocKind::Pc32.size(), 4);
294    /// ```
295    #[inline]
296    #[must_use]
297    pub const fn size(self) -> usize {
298        match self {
299            RelocKind::Call32 | RelocKind::Pc32 => 4,
300            RelocKind::Abs64 => 8,
301        }
302    }
303}
304
305/// A place in the code that refers to a symbol defined elsewhere (declared
306/// with [`Emitter::declare_external`]): the bytes at `offset` receive the
307/// value `kind` computes from the symbol's address and `addend`. The bytes
308/// hold zero until a linker patches them.
309///
310/// # Examples
311///
312/// ```
313/// use backend_lang::mir::Function;
314/// use backend_lang::x86_64::{op, Emitter, RelocKind, SYSV};
315///
316/// let mut f = Function::new("f", &SYSV);
317/// f.omit_frame_pointer();
318/// let b = f.block()?;
319/// let puts = f.symbol("puts")?;
320/// f.push(b, op::CALL, &[puts.into()])?;
321/// f.push(b, op::RET, &[])?;
322/// let mut e = Emitter::new();
323/// e.declare_external("puts")?;
324/// e.function(&f)?;
325/// let p = e.finish()?;
326/// let r = &p.relocations()[0];
327/// assert_eq!((r.kind, r.symbol.as_str(), r.addend), (RelocKind::Call32, "puts", -4));
328/// // The field follows the `call` opcode and holds zero until linked.
329/// assert_eq!(p.bytes()[r.offset - 1..r.offset + 4], [0xE8, 0, 0, 0, 0]);
330/// # Ok::<(), Box<dyn std::error::Error>>(())
331/// ```
332#[derive(Clone, Debug, PartialEq, Eq, Hash)]
333#[non_exhaustive]
334pub struct Reloc {
335    /// Where the patched bytes start, from the start of the code.
336    pub offset: usize,
337    /// What is computed.
338    pub kind: RelocKind,
339    /// The symbol whose address is used.
340    pub symbol: String,
341    /// The constant added to the address.
342    pub addend: i64,
343}
344
345impl Reloc {
346    /// A relocation at `offset` of `kind` against `symbol` plus `addend`.
347    ///
348    /// # Examples
349    ///
350    /// ```
351    /// use backend_lang::x86_64::{Reloc, RelocKind};
352    ///
353    /// let r = Reloc::new(8, RelocKind::Abs64, "table", 16);
354    /// assert_eq!(r.addend, 16);
355    /// ```
356    #[must_use]
357    pub fn new(offset: usize, kind: RelocKind, symbol: &str, addend: i64) -> Reloc {
358        Reloc {
359            offset,
360            kind,
361            symbol: String::from(symbol),
362            addend,
363        }
364    }
365}
366
367/// One call site with a record ([`Function::set_call_site`]): where it
368/// returns to, where its GC roots are while it runs, and where unwinding
369/// lands.
370///
371/// # Examples
372///
373/// ```
374/// use backend_lang::mir::{Function, SlotKind};
375/// use backend_lang::x86_64::{op, Emitter, SYSV};
376///
377/// let mut f = Function::new("f", &SYSV);
378/// let root = f.stack_slot(SlotKind::Local { size: 8, align: 8 })?;
379/// let b = f.block()?;
380/// let g = f.symbol("f")?;
381/// let call = f.push(b, op::CALL, &[g.into()])?;
382/// f.push(b, op::RET, &[])?;
383/// f.set_call_site(call, &[root], None)?;
384/// let mut e = Emitter::new();
385/// e.function(&f)?;
386/// let p = e.finish()?;
387/// let site = &p.functions()[0].call_sites()[0];
388/// assert_eq!(site.gc_slots().len(), 1);
389/// assert!(site.return_offset() > 0);
390/// # Ok::<(), Box<dyn std::error::Error>>(())
391/// ```
392#[derive(Clone, Debug, PartialEq, Eq)]
393pub struct CallSiteRecord {
394    return_offset: usize,
395    gc_slots: Vec<i32>,
396    landing_pad: Option<usize>,
397}
398
399impl CallSiteRecord {
400    /// The offset of the instruction after the call (the return address),
401    /// from the start of the code. A stack walker identifies the call by it.
402    ///
403    /// # Examples
404    ///
405    /// See [`CallSiteRecord`].
406    #[inline]
407    #[must_use]
408    pub fn return_offset(&self) -> usize {
409        self.return_offset
410    }
411
412    /// Where the GC roots live while the call runs: byte offsets from the
413    /// caller's stack pointer at the call instruction (before the return
414    /// address is pushed), one 8-byte reference each. This is the call's
415    /// **stack map**: a collector reads the references there and writes the
416    /// relocated ones back, and the caller reloads them after the call.
417    ///
418    /// # Examples
419    ///
420    /// See [`CallSiteRecord`].
421    #[inline]
422    #[must_use]
423    pub fn gc_slots(&self) -> &[i32] {
424        &self.gc_slots
425    }
426
427    /// The offset of the landing pad unwinding through this call reaches, if
428    /// any.
429    ///
430    /// # Examples
431    ///
432    /// See [`CallSiteRecord`].
433    #[inline]
434    #[must_use]
435    pub fn landing_pad(&self) -> Option<usize> {
436        self.landing_pad
437    }
438}
439
440/// One emitted function: its place in the code, its frame, and its call-site
441/// records.
442///
443/// # Examples
444///
445/// ```
446/// use backend_lang::mir::Function;
447/// use backend_lang::x86_64::{op, Emitter, SYSV};
448///
449/// let mut f = Function::new("f", &SYSV);
450/// let b = f.block()?;
451/// f.push(b, op::RET, &[])?;
452/// let mut e = Emitter::new();
453/// e.function(&f)?;
454/// let p = e.finish()?;
455/// let ef = &p.functions()[0];
456/// assert_eq!((ef.name(), ef.offset(), ef.size()), ("f", 0, 6));
457/// # Ok::<(), Box<dyn std::error::Error>>(())
458/// ```
459#[derive(Clone, Debug, PartialEq, Eq)]
460pub struct EmittedFunction {
461    name: String,
462    offset: usize,
463    size: usize,
464    frame: FrameLayout,
465    call_sites: Vec<CallSiteRecord>,
466}
467
468impl EmittedFunction {
469    /// The function's name.
470    ///
471    /// # Examples
472    ///
473    /// See [`EmittedFunction`].
474    #[inline]
475    #[must_use]
476    pub fn name(&self) -> &str {
477        &self.name
478    }
479
480    /// The offset of its first byte.
481    ///
482    /// # Examples
483    ///
484    /// See [`EmittedFunction`].
485    #[inline]
486    #[must_use]
487    pub fn offset(&self) -> usize {
488        self.offset
489    }
490
491    /// Its size in bytes, jump tables included.
492    ///
493    /// # Examples
494    ///
495    /// See [`EmittedFunction`].
496    #[inline]
497    #[must_use]
498    pub fn size(&self) -> usize {
499        self.size
500    }
501
502    /// Its frame. With the stack pointer fixed in the body, the canonical
503    /// frame address is the body's stack pointer plus
504    /// [`cfa_offset`](FrameLayout::cfa_offset), which is all a stack walker
505    /// needs to step from a call site to its caller.
506    ///
507    /// # Examples
508    ///
509    /// See [`EmittedFunction`].
510    #[inline]
511    #[must_use]
512    pub fn frame(&self) -> &FrameLayout {
513        &self.frame
514    }
515
516    /// Its call-site records, in code order.
517    ///
518    /// # Examples
519    ///
520    /// See [`CallSiteRecord`].
521    #[inline]
522    #[must_use]
523    pub fn call_sites(&self) -> &[CallSiteRecord] {
524        &self.call_sites
525    }
526}
527
528/// Machine code with the offset of each function, the relocations against
529/// external symbols, and the call-site records.
530///
531/// # Examples
532///
533/// ```
534/// use backend_lang::mir::Function;
535/// use backend_lang::x86_64::{op, Emitter, SYSV};
536///
537/// let mut f = Function::new("f", &SYSV);
538/// let b = f.block()?;
539/// f.push(b, op::RET, &[])?;
540/// let mut e = Emitter::new();
541/// e.function(&f)?;
542/// let program = e.finish()?;
543/// assert_eq!(program.symbol("f"), Some(0));
544/// // push rbp; mov rbp, rsp; pop rbp; ret
545/// assert_eq!(program.bytes(), [0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3]);
546/// # Ok::<(), Box<dyn std::error::Error>>(())
547/// ```
548#[derive(Clone, Debug, PartialEq, Eq)]
549pub struct Program {
550    bytes: Vec<u8>,
551    symbols: Vec<(String, usize)>,
552    functions: Vec<EmittedFunction>,
553    relocs: Vec<Reloc>,
554}
555
556impl Program {
557    /// The machine code.
558    ///
559    /// # Examples
560    ///
561    /// ```
562    /// use backend_lang::x86_64::Emitter;
563    ///
564    /// assert!(Emitter::new().finish()?.bytes().is_empty());
565    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
566    /// ```
567    #[inline]
568    #[must_use]
569    pub fn bytes(&self) -> &[u8] {
570        &self.bytes
571    }
572
573    /// The machine code, by value.
574    ///
575    /// # Examples
576    ///
577    /// ```
578    /// use backend_lang::x86_64::Emitter;
579    ///
580    /// assert!(Emitter::new().finish()?.into_bytes().is_empty());
581    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
582    /// ```
583    #[must_use]
584    pub fn into_bytes(self) -> Vec<u8> {
585        self.bytes
586    }
587
588    /// The offset of the function named `name`.
589    ///
590    /// # Examples
591    ///
592    /// ```
593    /// use backend_lang::x86_64::Emitter;
594    ///
595    /// assert_eq!(Emitter::new().finish()?.symbol("missing"), None);
596    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
597    /// ```
598    #[must_use]
599    pub fn symbol(&self, name: &str) -> Option<usize> {
600        self.symbols
601            .binary_search_by(|(n, _)| n.as_str().cmp(name))
602            .ok()
603            .and_then(|i| self.symbols.get(i))
604            .map(|&(_, off)| off)
605    }
606
607    /// Every function and its offset, in name order.
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// use backend_lang::x86_64::Emitter;
613    ///
614    /// assert_eq!(Emitter::new().finish()?.symbols().count(), 0);
615    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
616    /// ```
617    pub fn symbols(&self) -> impl Iterator<Item = (&str, usize)> {
618        self.symbols.iter().map(|(n, o)| (n.as_str(), *o))
619    }
620
621    /// Every function, in emission order, with its size, frame, and
622    /// call-site records.
623    ///
624    /// # Examples
625    ///
626    /// ```
627    /// use backend_lang::x86_64::Emitter;
628    ///
629    /// assert!(Emitter::new().finish()?.functions().is_empty());
630    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
631    /// ```
632    #[inline]
633    #[must_use]
634    pub fn functions(&self) -> &[EmittedFunction] {
635        &self.functions
636    }
637
638    /// The relocations against external symbols, in code order.
639    ///
640    /// # Examples
641    ///
642    /// See [`Reloc`].
643    #[inline]
644    #[must_use]
645    pub fn relocations(&self) -> &[Reloc] {
646        &self.relocs
647    }
648}
649
650#[derive(Clone, Copy, Debug)]
651struct Sym {
652    label: Label,
653    defined: bool,
654    external: bool,
655}
656
657/// A function emitted but not yet finished: its labels and record ranges.
658#[derive(Clone, Debug)]
659struct PendingFunc {
660    name: String,
661    start: Label,
662    end: Label,
663    frame: FrameLayout,
664    sites: core::ops::Range<usize>,
665}
666
667/// A call site whose return address is a label bound after the call.
668#[derive(Clone, Debug)]
669struct PendingSite {
670    ret: Label,
671    gc: core::ops::Range<usize>,
672    landing: Option<Label>,
673}
674
675/// A reference to an external symbol: the patched field ends `back` bytes
676/// before the label bound right after the instruction (the field is always
677/// the instruction's last four bytes).
678#[derive(Clone, Debug)]
679struct PendingReloc {
680    after: Label,
681    kind: RelocKind,
682    symbol: String,
683}
684
685/// A jump table: eight-byte entries at `at`, each the offset of a target
686/// from the table, filled in once the layout is final.
687#[derive(Clone, Debug)]
688struct PendingTable {
689    at: Label,
690    targets: core::ops::Range<usize>,
691}
692
693/// Emits functions into one buffer of x86-64 machine code.
694///
695/// # Examples
696///
697/// Two functions, one calling the other:
698///
699/// ```
700/// use backend_lang::mir::{Effects, Function, Operand};
701/// use backend_lang::x86_64::{op, Emitter, SYSV, RAX};
702///
703/// let mut answer = Function::new("answer", &SYSV);
704/// let b = answer.block()?;
705/// answer.push(b, op::MOV32RI, &[RAX.into(), Operand::Imm(42)])?;
706/// answer.push_with(b, op::RET, &[], Effects::new().uses(&[RAX]))?;
707///
708/// let mut main = Function::new("main", &SYSV);
709/// let b = main.block()?;
710/// let callee = main.symbol("answer")?;
711/// main.push_with(b, op::CALL, &[callee.into()], Effects::new().defs(&[RAX]).clobbers(SYSV.call_clobbers()))?;
712/// main.push_with(b, op::RET, &[], Effects::new().uses(&[RAX]))?;
713///
714/// let mut e = Emitter::new();
715/// e.function(&main)?;
716/// e.function(&answer)?;
717/// let program = e.finish()?;
718/// assert_eq!(program.symbol("main"), Some(0));
719/// assert!(program.symbol("answer").is_some());
720/// # Ok::<(), Box<dyn std::error::Error>>(())
721/// ```
722#[derive(Debug, Default)]
723pub struct Emitter {
724    asm: Assembler,
725    symbols: BTreeMap<String, Sym>,
726    resolver: MoveResolver,
727    moves: Vec<Move>,
728    steps: Vec<Step>,
729    sites: Vec<(u32, u32)>,
730    labels: Vec<Label>,
731    funcs: Vec<PendingFunc>,
732    call_sites: Vec<PendingSite>,
733    gc_offsets: Vec<i32>,
734    relocs: Vec<PendingReloc>,
735    tables: Vec<PendingTable>,
736    table_targets: Vec<Label>,
737}
738
739/// Per-function state while emitting its body.
740struct Ctx<'a> {
741    f: &'a Function,
742    frame: &'a FrameLayout,
743    site: usize,
744    tables: usize,
745}
746impl Emitter {
747    /// An empty emitter.
748    ///
749    /// # Examples
750    ///
751    /// ```
752    /// use backend_lang::x86_64::Emitter;
753    ///
754    /// assert!(Emitter::new().finish()?.bytes().is_empty());
755    /// # Ok::<(), backend_lang::x86_64::EmitError>(())
756    /// ```
757    #[must_use]
758    pub fn new() -> Emitter {
759        Emitter::default()
760    }
761
762    /// An empty emitter with room for `bytes` bytes of code.
763    ///
764    /// # Examples
765    ///
766    /// ```
767    /// use backend_lang::x86_64::Emitter;
768    ///
769    /// let _e = Emitter::with_capacity(1 << 20);
770    /// ```
771    #[must_use]
772    pub fn with_capacity(bytes: usize) -> Emitter {
773        Emitter {
774            asm: Assembler::with_capacity(bytes),
775            ..Emitter::default()
776        }
777    }
778
779    /// Emits one function: verifies it, checks it uses only physical
780    /// registers, lays out its frame, and encodes its prologue, blocks, and
781    /// epilogues.
782    ///
783    /// # Errors
784    ///
785    /// An [`EmitError`] naming the problem. After an error the emitter may
786    /// hold part of the function; discard it.
787    ///
788    /// # Examples
789    ///
790    /// ```
791    /// use backend_lang::mir::{Function, Operand};
792    /// use backend_lang::x86_64::{op, Emitter, WIN64, RAX};
793    ///
794    /// let mut f = Function::new("seven", &WIN64);
795    /// let b = f.block()?;
796    /// f.push(b, op::MOV32RI, &[RAX.into(), Operand::Imm(7)])?;
797    /// f.push(b, op::RET, &[])?;
798    /// let mut e = Emitter::new();
799    /// e.function(&f)?;
800    /// // push rbp; mov rbp, rsp; mov eax, 7; pop rbp; ret
801    /// assert_eq!(e.finish()?.bytes(), [0x55, 0x48, 0x89, 0xE5, 0xB8, 7, 0, 0, 0, 0x5D, 0xC3]);
802    /// # Ok::<(), Box<dyn std::error::Error>>(())
803    /// ```
804    pub fn function(&mut self, f: &Function) -> Result<FunctionInfo, EmitError> {
805        let target = f.target();
806        if !core::ptr::eq(target.insts(), &DESCS[..]) || !core::ptr::eq(target.regs(), &REGS[..]) {
807            return Err(EmitError::UnsupportedTarget);
808        }
809        f.verify()?;
810        check_physical(f)?;
811
812        // Resolve every parallel copy first: the frame needs to know whether
813        // any of them borrows the hidden slots.
814        let base = f.slots().len();
815        let scratch = StackSlot::from_index(base);
816        let victim = StackSlot::from_index(base + 1);
817        let env = MoveEnv::new()
818            .with_scratch_slot(scratch)
819            .with_victim_slot(victim);
820        let (mut need_scratch, mut need_victim) = (false, false);
821        self.steps.clear();
822        self.sites.clear();
823        for block in f.blocks() {
824            for &inst in f.insts(block) {
825                let Some(opcode) = f.opcode(inst) else {
826                    continue;
827                };
828                let ops = f.operands(inst);
829                let mut site =
830                    |em: &mut Emitter, pairs: &mut dyn Iterator<Item = (Operand, Operand)>| {
831                        em.moves.clear();
832                        for (d, s) in pairs {
833                            em.moves.push(to_move(f, d, s)?);
834                        }
835                        let start = em.steps.len();
836                        let used = em
837                            .resolver
838                            .resolve(target, &em.moves, &env, &mut em.steps)?;
839                        need_scratch |= used.scratch_slot;
840                        need_victim |= used.victim_slot;
841                        em.sites.push((start as u32, em.steps.len() as u32));
842                        Ok::<(), EmitError>(())
843                    };
844                match ENCODINGS.get(opcode.index()) {
845                    Some(Enc::Copy) => {
846                        site(
847                            self,
848                            &mut ops.first().copied().zip(ops.get(1).copied()).into_iter(),
849                        )?;
850                    }
851                    Some(Enc::Pcopy) => {
852                        let (d, s) = ops.split_at(ops.len() / 2);
853                        site(self, &mut d.iter().copied().zip(s.iter().copied()))?;
854                    }
855                    Some(Enc::Jmp | Enc::Jcc) => {
856                        for op in ops {
857                            if let Operand::Block(call) = op {
858                                let params = f.params(call.block()).iter().copied();
859                                site(self, &mut params.zip(f.args(*call).iter().copied()))?;
860                            }
861                        }
862                    }
863                    _ => {}
864                }
865            }
866        }
867
868        let mut slots: Vec<SlotKind> = Vec::with_capacity(base + 2);
869        slots.extend_from_slice(f.slots());
870        for used in [need_scratch, need_victim] {
871            slots.push(if used {
872                SlotKind::Local {
873                    size: 16,
874                    align: 16,
875                }
876            } else {
877                SlotKind::Local { size: 0, align: 1 }
878            });
879        }
880        let frame = f.abi().layout_frame(&FrameInput {
881            slots: &slots,
882            written: f.written_regs(),
883            has_calls: f.has_calls(),
884            outgoing: f.outgoing(),
885            frame_pointer: f.frame_pointer(),
886        })?;
887
888        let entry = self.define(f.name())?;
889        self.asm.bind(entry).map_err(EmitError::Asm)?;
890        let probe_reg = probe_scratch(f)?;
891        for &step in frame.prologue() {
892            self.frame_step(step, probe_reg)?;
893        }
894        self.labels.clear();
895        for _ in 0..f.block_count() {
896            let l = self.asm.label();
897            self.labels.push(l);
898        }
899        let first_site = self.call_sites.len();
900        let first_table = self.tables.len();
901        let mut ctx = Ctx {
902            f,
903            frame: &frame,
904            site: 0,
905            tables: 0,
906        };
907        for (bi, block) in f.blocks().enumerate() {
908            let label = self.labels[bi];
909            self.asm.bind(label).map_err(EmitError::Asm)?;
910            for &inst in f.insts(block) {
911                self.inst(&mut ctx, bi, inst)?;
912            }
913        }
914        // Jump tables follow the code, zero-filled until the layout is final.
915        for t in first_table..self.tables.len() {
916            let (at, n) = match self.tables.get(t) {
917                Some(p) => (p.at, p.targets.len()),
918                None => continue,
919            };
920            self.asm.bind(at).map_err(EmitError::Asm)?;
921            let zeros = [0u8; 64];
922            let mut left = n.saturating_mul(8);
923            while left > 0 {
924                let k = left.min(zeros.len());
925                self.asm
926                    .bytes(&zeros[..k])
927                    .map_err(|e| asm_error(None, e))?;
928                left -= k;
929            }
930        }
931        let end = self.asm.label();
932        self.asm.bind(end).map_err(EmitError::Asm)?;
933        self.funcs.push(PendingFunc {
934            name: f.name().to_string(),
935            start: entry,
936            end,
937            frame: frame.clone(),
938            sites: first_site..self.call_sites.len(),
939        });
940        Ok(FunctionInfo { frame })
941    }
942
943    /// Declares `name` as a symbol defined outside this code (an imported
944    /// function, or data such as a global): every call, tail call, or address
945    /// of it becomes a [`Reloc`] in [`Program::relocations`] instead of an
946    /// error at [`finish`](Emitter::finish).
947    ///
948    /// # Errors
949    ///
950    /// [`EmitError::ExternalConflict`] if a function of that name was already
951    /// emitted.
952    ///
953    /// # Examples
954    ///
955    /// ```
956    /// use backend_lang::mir::Function;
957    /// use backend_lang::x86_64::{op, Emitter, SYSV};
958    ///
959    /// let mut f = Function::new("f", &SYSV);
960    /// let b = f.block()?;
961    /// let g = f.symbol("g")?;
962    /// f.push(b, op::TAILCALL, &[g.into()])?;
963    /// let mut e = Emitter::new();
964    /// e.declare_external("g")?;
965    /// e.function(&f)?;
966    /// assert_eq!(e.finish()?.relocations().len(), 1);
967    /// # Ok::<(), Box<dyn std::error::Error>>(())
968    /// ```
969    pub fn declare_external(&mut self, name: &str) -> Result<(), EmitError> {
970        let _ = self.label_for(name);
971        match self.symbols.get_mut(name) {
972            Some(s) if s.defined => Err(EmitError::ExternalConflict(name.to_string())),
973            Some(s) => {
974                s.external = true;
975                Ok(())
976            }
977            None => Ok(()),
978        }
979    }
980
981    /// Finishes the code: relaxes branches, patches calls and jump tables,
982    /// and returns the bytes with every function's offset, the relocations
983    /// against external symbols, and the call-site records.
984    ///
985    /// # Errors
986    ///
987    /// [`EmitError::UndefinedSymbol`] for the first name (in name order)
988    /// that was called but neither emitted nor declared external;
989    /// [`EmitError::Asm`] if the code exceeds 2 GiB.
990    ///
991    /// # Examples
992    ///
993    /// ```
994    /// use backend_lang::mir::Function;
995    /// use backend_lang::x86_64::{op, EmitError, Emitter, SYSV};
996    ///
997    /// let mut f = Function::new("f", &SYSV);
998    /// let b = f.block()?;
999    /// let g = f.symbol("g")?;
1000    /// f.push(b, op::CALL, &[g.into()])?;
1001    /// f.push(b, op::RET, &[])?;
1002    /// let mut e = Emitter::new();
1003    /// e.function(&f)?;
1004    /// assert_eq!(e.finish().unwrap_err(), EmitError::UndefinedSymbol("g".into()));
1005    /// # Ok::<(), Box<dyn std::error::Error>>(())
1006    /// ```
1007    pub fn finish(self) -> Result<Program, EmitError> {
1008        if let Some((name, _)) = self.symbols.iter().find(|(_, s)| !s.defined && !s.external) {
1009            return Err(EmitError::UndefinedSymbol(name.clone()));
1010        }
1011        let code = self.asm.finish().map_err(EmitError::Asm)?;
1012        let at = |l: Label| {
1013            code.label_offset(l)
1014                .ok_or(EmitError::Asm(AsmError::Unbound(l)))
1015        };
1016        let symbols = self
1017            .symbols
1018            .iter()
1019            .filter(|(_, s)| s.defined)
1020            .filter_map(|(n, s)| code.label_offset(s.label).map(|o| (n.clone(), o)))
1021            .collect();
1022        let mut relocs = Vec::with_capacity(self.relocs.len());
1023        for r in &self.relocs {
1024            // The field is the last four bytes of its instruction, and the
1025            // processor measures from the end of the instruction.
1026            let end = at(r.after)?;
1027            relocs.push(Reloc {
1028                offset: end.saturating_sub(4),
1029                kind: r.kind,
1030                symbol: r.symbol.clone(),
1031                addend: -4,
1032            });
1033        }
1034        let mut patches: Vec<(usize, i64)> = Vec::new();
1035        for t in &self.tables {
1036            let base = at(t.at)?;
1037            for (k, &target) in self
1038                .table_targets
1039                .get(t.targets.clone())
1040                .unwrap_or(&[])
1041                .iter()
1042                .enumerate()
1043            {
1044                let to = at(target)?;
1045                patches.push((base + 8 * k, to as i64 - base as i64));
1046            }
1047        }
1048        let mut functions = Vec::with_capacity(self.funcs.len());
1049        for pf in &self.funcs {
1050            let offset = at(pf.start)?;
1051            let size = at(pf.end)?.saturating_sub(offset);
1052            let mut call_sites = Vec::with_capacity(pf.sites.len());
1053            for ps in self.call_sites.get(pf.sites.clone()).unwrap_or(&[]) {
1054                call_sites.push(CallSiteRecord {
1055                    return_offset: at(ps.ret)?,
1056                    gc_slots: self.gc_offsets.get(ps.gc.clone()).unwrap_or(&[]).to_vec(),
1057                    landing_pad: match ps.landing {
1058                        Some(l) => Some(at(l)?),
1059                        None => None,
1060                    },
1061                });
1062            }
1063            functions.push(EmittedFunction {
1064                name: pf.name.clone(),
1065                offset,
1066                size,
1067                frame: pf.frame.clone(),
1068                call_sites,
1069            });
1070        }
1071        let mut bytes = code.into_bytes();
1072        for (pos, value) in patches {
1073            if let Some(field) = bytes.get_mut(pos..pos + 8) {
1074                field.copy_from_slice(&value.to_le_bytes());
1075            }
1076        }
1077        Ok(Program {
1078            bytes,
1079            symbols,
1080            functions,
1081            relocs,
1082        })
1083    }
1084
1085    fn label_for(&mut self, name: &str) -> Label {
1086        if let Some(s) = self.symbols.get(name) {
1087            return s.label;
1088        }
1089        let label = self.asm.label();
1090        let _ = self.symbols.insert(
1091            name.to_string(),
1092            Sym {
1093                label,
1094                defined: false,
1095                external: false,
1096            },
1097        );
1098        label
1099    }
1100
1101    fn define(&mut self, name: &str) -> Result<Label, EmitError> {
1102        let label = self.label_for(name);
1103        match self.symbols.get_mut(name) {
1104            Some(s) if s.external => Err(EmitError::ExternalConflict(name.to_string())),
1105            Some(s) if s.defined => Err(EmitError::DuplicateSymbol(name.to_string())),
1106            Some(s) => {
1107                s.defined = true;
1108                Ok(label)
1109            }
1110            None => Ok(label),
1111        }
1112    }
1113
1114    fn is_external(&self, name: &str) -> bool {
1115        self.symbols.get(name).is_some_and(|s| s.external)
1116    }
1117
1118    /// Emits already-encoded bytes that end in a four-byte field referring to
1119    /// an external symbol, and records the relocation.
1120    fn external_ref(
1121        &mut self,
1122        inst: Option<Inst>,
1123        bytes: &[u8],
1124        kind: RelocKind,
1125        symbol: &str,
1126    ) -> Result<(), EmitError> {
1127        self.asm.bytes(bytes).map_err(|e| asm_error(inst, e))?;
1128        let after = self.asm.label();
1129        self.asm.bind(after).map_err(EmitError::Asm)?;
1130        self.relocs.push(PendingReloc {
1131            after,
1132            kind,
1133            symbol: symbol.to_string(),
1134        });
1135        Ok(())
1136    }
1137
1138    /// `call name` or `jmp name`: to a label when the name is defined here,
1139    /// through a relocation when it is external.
1140    fn branch_to_symbol(&mut self, inst: Inst, name: &str, jump: bool) -> Result<(), EmitError> {
1141        if self.is_external(name) {
1142            let opcode = if jump { 0xE9 } else { 0xE8 };
1143            return self.external_ref(Some(inst), &[opcode, 0, 0, 0, 0], RelocKind::Call32, name);
1144        }
1145        let label = self.label_for(name);
1146        if jump {
1147            self.asm.jmp(label).map_err(|e| asm_error(Some(inst), e))
1148        } else {
1149            self.asm.call(label).map_err(|e| asm_error(Some(inst), e))
1150        }
1151    }
1152
1153    fn emit(&mut self, inst: Option<Inst>, i: &IInst) -> Result<(), EmitError> {
1154        self.asm.emit(i).map_err(|e| asm_error(inst, e))
1155    }
1156    fn frame_step(&mut self, step: FrameStep, probe_reg: PReg) -> Result<(), EmitError> {
1157        let rsp = IOp::Reg(IReg::RSP);
1158        match step {
1159            FrameStep::Push(r) => {
1160                self.emit(None, &IInst::new(M::Push, [ireg(None, r, Size::Qword)?]))
1161            }
1162            FrameStep::Pop(r) => {
1163                self.emit(None, &IInst::new(M::Pop, [ireg(None, r, Size::Qword)?]))
1164            }
1165            FrameStep::SetFramePointer => {
1166                self.emit(None, &IInst::new(M::Mov, [IOp::Reg(IReg::RBP), rsp]))
1167            }
1168            FrameStep::Alloc { bytes, probe } if probe == 0 || bytes < probe => {
1169                self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(bytes))]))
1170            }
1171            FrameStep::Alloc { bytes, probe } => self.probe(bytes, probe, probe_reg),
1172            FrameStep::Free(bytes) => {
1173                self.emit(None, &IInst::new(M::Add, [rsp, IOp::Imm(i64::from(bytes))]))
1174            }
1175            _ => Err(EmitError::Unencodable { inst: None }),
1176        }
1177    }
1178
1179    /// Allocates `bytes` one `page` at a time, touching each new page, so the
1180    /// guard page below the stack is never skipped. Up to four pages are
1181    /// unrolled; beyond that a counted loop runs in `counter` (a
1182    /// caller-saved register the convention does not pass arguments in).
1183    fn probe(&mut self, bytes: u32, page: u32, counter: PReg) -> Result<(), EmitError> {
1184        let rsp = IOp::Reg(IReg::RSP);
1185        let touch = IInst::new(M::Test, [IMem::new(Size::Qword, IReg::RSP).into(), rsp]);
1186        let step = IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(page))]);
1187        let pages = bytes / page;
1188        let rest = bytes % page;
1189        if pages <= 4 {
1190            for _ in 0..pages {
1191                self.emit(None, &step)?;
1192                self.emit(None, &touch)?;
1193            }
1194        } else {
1195            let c = ireg(None, counter, Size::Dword)?;
1196            self.emit(None, &IInst::new(M::Mov, [c, IOp::Imm(i64::from(pages))]))?;
1197            let top = self.asm.label();
1198            self.asm.bind(top).map_err(EmitError::Asm)?;
1199            self.emit(None, &step)?;
1200            self.emit(None, &touch)?;
1201            self.emit(None, &IInst::new(M::Dec, [c]))?;
1202            self.asm.jcc(ICond::Ne, top).map_err(EmitError::Asm)?;
1203        }
1204        if rest > 0 {
1205            self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(rest))]))?;
1206        }
1207        Ok(())
1208    }
1209
1210    /// Emits the steps of the next parallel-copy site.
1211    fn site(&mut self, ctx: &mut Ctx<'_>) -> Result<bool, EmitError> {
1212        let (start, end) = self.sites.get(ctx.site).copied().unwrap_or((0, 0));
1213        ctx.site += 1;
1214        for i in start as usize..end as usize {
1215            let step = self.steps[i];
1216            self.step(ctx.frame, step)?;
1217        }
1218        Ok(end > start)
1219    }
1220
1221    fn next_site_empty(&self, ctx: &Ctx<'_>, ahead: usize) -> bool {
1222        self.sites
1223            .get(ctx.site + ahead)
1224            .is_none_or(|&(s, e)| s == e)
1225    }
1226
1227    fn step(&mut self, frame: &FrameLayout, step: Step) -> Result<(), EmitError> {
1228        let gpr = |class: RegClass| {
1229            if class == GPR {
1230                Ok(())
1231            } else {
1232                Err(EmitError::Unencodable { inst: None })
1233            }
1234        };
1235        match step {
1236            Step::Swap { a, b, class } => {
1237                gpr(class)?;
1238                let i = IInst::new(
1239                    M::Xchg,
1240                    [ireg(None, a, Size::Qword)?, ireg(None, b, Size::Qword)?],
1241                );
1242                self.emit(None, &i)
1243            }
1244            Step::Move { dst, src, class } => {
1245                gpr(class)?;
1246                let i = match (dst, src) {
1247                    (Loc::Reg(d), Source::Imm(v)) => return self.mov_imm(None, d, v),
1248                    (Loc::Reg(d), Source::Loc(Loc::Reg(s))) => IInst::new(
1249                        M::Mov,
1250                        [ireg(None, d, Size::Qword)?, ireg(None, s, Size::Qword)?],
1251                    ),
1252                    (Loc::Reg(d), Source::Loc(Loc::Slot(s))) => IInst::new(
1253                        M::Mov,
1254                        [
1255                            ireg(None, d, Size::Qword)?,
1256                            slot_mem(frame, s, 0, Size::Qword)?.into(),
1257                        ],
1258                    ),
1259                    (Loc::Slot(d), Source::Loc(Loc::Reg(s))) => IInst::new(
1260                        M::Mov,
1261                        [
1262                            slot_mem(frame, d, 0, Size::Qword)?.into(),
1263                            ireg(None, s, Size::Qword)?,
1264                        ],
1265                    ),
1266                    (Loc::Slot(d), Source::Imm(v)) => IInst::new(
1267                        M::Mov,
1268                        [slot_mem(frame, d, 0, Size::Qword)?.into(), IOp::Imm(v)],
1269                    ),
1270                    // The resolver legalizes memory-to-memory moves away.
1271                    (Loc::Slot(_), Source::Loc(Loc::Slot(_))) => {
1272                        return Err(EmitError::Unencodable { inst: None });
1273                    }
1274                };
1275                self.emit(None, &i)
1276            }
1277        }
1278    }
1279
1280    /// `mov r64, imm`, as `mov r32, imm32` when the value is below 2^32 (the
1281    /// upper half is zeroed either way).
1282    fn mov_imm(&mut self, inst: Option<Inst>, reg: PReg, value: i64) -> Result<(), EmitError> {
1283        let i = if (0..=i64::from(u32::MAX)).contains(&value) {
1284            IInst::new(
1285                M::Mov,
1286                [
1287                    ireg(inst, reg, Size::Dword)?,
1288                    IOp::Imm(i64::from(value as u32 as i32)),
1289                ],
1290            )
1291        } else {
1292            IInst::new(M::Mov, [ireg(inst, reg, Size::Qword)?, IOp::Imm(value)])
1293        };
1294        self.emit(inst, &i)
1295    }
1296
1297    fn inst(&mut self, ctx: &mut Ctx<'_>, bi: usize, inst: Inst) -> Result<(), EmitError> {
1298        let f = ctx.f;
1299        let Some(opcode) = f.opcode(inst) else {
1300            return Ok(());
1301        };
1302        let ops = f.operands(inst);
1303        let enc = ENCODINGS
1304            .get(opcode.index())
1305            .copied()
1306            .ok_or(EmitError::UnsupportedTarget)?;
1307        let next = Block::from_index(bi + 1);
1308        match enc {
1309            Enc::Copy | Enc::Pcopy => {
1310                let _ = self.site(ctx)?;
1311                Ok(())
1312            }
1313            Enc::Jmp => {
1314                let call = block_operand(ops, 0, inst)?;
1315                let _ = self.site(ctx)?;
1316                if call.block() != next {
1317                    self.jmp(call.block())?;
1318                }
1319                Ok(())
1320            }
1321            Enc::Jcc => {
1322                let cond = cond_operand(ops, 0, inst)?;
1323                let then = block_operand(ops, 1, inst)?;
1324                let other = block_operand(ops, 2, inst)?;
1325                let then_moves = !self.next_site_empty(ctx, 0);
1326                let else_moves = !self.next_site_empty(ctx, 1);
1327                if !then_moves {
1328                    self.jcc(cond, then.block())?;
1329                    let _ = self.site(ctx)?;
1330                    let _ = self.site(ctx)?;
1331                } else if !else_moves {
1332                    self.jcc(cond.negate(), other.block())?;
1333                    let _ = self.site(ctx)?;
1334                    if then.block() != next {
1335                        self.jmp(then.block())?;
1336                    }
1337                    let _ = self.site(ctx)?;
1338                    return Ok(());
1339                } else {
1340                    let stub = self.asm.label();
1341                    self.asm.jcc(cond.negate(), stub).map_err(EmitError::Asm)?;
1342                    let _ = self.site(ctx)?;
1343                    self.jmp(then.block())?;
1344                    self.asm.bind(stub).map_err(EmitError::Asm)?;
1345                    let _ = self.site(ctx)?;
1346                }
1347                if other.block() != next {
1348                    self.jmp(other.block())?;
1349                }
1350                Ok(())
1351            }
1352            Enc::Ret => {
1353                for &step in ctx.frame.epilogue() {
1354                    self.frame_step(step, R11)?;
1355                }
1356                self.emit(Some(inst), &IInst::new(M::Ret, []))
1357            }
1358            Enc::TailCall => {
1359                // The target register must outlive the epilogue's pops.
1360                let target = match ops.first() {
1361                    Some(Operand::Reg(r)) => {
1362                        let p = r.as_phys().ok_or(EmitError::VirtualRegister {
1363                            block: Block::from_index(bi),
1364                            inst: Some(inst),
1365                        })?;
1366                        if ctx.frame.pushed().contains(&p)
1367                            || (ctx.frame.frame_pointer() && p == super::RBP)
1368                        {
1369                            return Err(EmitError::Constraint { inst });
1370                        }
1371                        Some(ireg(Some(inst), p, Size::Qword)?)
1372                    }
1373                    _ => None,
1374                };
1375                for &step in ctx.frame.epilogue() {
1376                    self.frame_step(step, R11)?;
1377                }
1378                match (target, ops.first()) {
1379                    (Some(t), _) => self.emit(Some(inst), &IInst::new(M::Jmp, [t])),
1380                    (None, Some(Operand::Symbol(s))) => {
1381                        let name = f
1382                            .symbol_name(*s)
1383                            .ok_or(EmitError::Mir(MirError::UnknownSymbol(*s)))?;
1384                        self.branch_to_symbol(inst, name, true)
1385                    }
1386                    _ => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1387                }
1388            }
1389            Enc::JumpTable => {
1390                let reg = |index: usize| -> Result<IReg, EmitError> {
1391                    match ops.get(index) {
1392                        Some(Operand::Reg(r)) => qword(inst, *r),
1393                        _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1394                    }
1395                };
1396                let (t, i) = (reg(0)?, reg(1)?);
1397                if t == i {
1398                    return Err(EmitError::Constraint { inst });
1399                }
1400                let table = self.asm.label();
1401                let start = self.table_targets.len();
1402                for (index, op) in ops.iter().enumerate().skip(2) {
1403                    let b = match op {
1404                        Operand::Block(call) => call.block(),
1405                        _ => return Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1406                    };
1407                    let l = self
1408                        .labels
1409                        .get(b.index())
1410                        .copied()
1411                        .ok_or(EmitError::Mir(MirError::UnknownBlock(b)))?;
1412                    self.table_targets.push(l);
1413                }
1414                self.tables.push(PendingTable {
1415                    at: table,
1416                    targets: start..self.table_targets.len(),
1417                });
1418                ctx.tables += 1;
1419                // t = &table; t += table[i] (the target's offset from the
1420                // table); jmp t. Position-independent: no absolute address.
1421                let lea = IInst::new(M::Lea, [IOp::Reg(t), IOp::Mem(IMem::rip(Size::Qword, 0))]);
1422                self.asm
1423                    .emit_rip(&lea, table)
1424                    .map_err(|e| asm_error(Some(inst), e))?;
1425                let entry = IMem::new(Size::Qword, t).with_index(i, IScale::X8);
1426                self.emit(
1427                    Some(inst),
1428                    &IInst::new(M::Add, [IOp::Reg(t), IOp::Mem(entry)]),
1429                )?;
1430                self.emit(Some(inst), &IInst::new(M::Jmp, [IOp::Reg(t)]))
1431            }
1432            Enc::Call => {
1433                match ops.first() {
1434                    Some(Operand::Symbol(s)) => {
1435                        let name = f
1436                            .symbol_name(*s)
1437                            .ok_or(EmitError::Mir(MirError::UnknownSymbol(*s)))?;
1438                        self.branch_to_symbol(inst, name, false)?;
1439                    }
1440                    Some(op) => {
1441                        let target = self.operand(ctx, inst, op, Size::Qword)?;
1442                        self.emit(Some(inst), &IInst::new(M::Call, [target]))?;
1443                    }
1444                    None => return Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1445                }
1446                if let Some(site) = f.call_site(inst) {
1447                    // The return address identifies the call to a stack
1448                    // walker: bind it right after the call.
1449                    let ret = self.asm.label();
1450                    self.asm.bind(ret).map_err(EmitError::Asm)?;
1451                    let gc_start = self.gc_offsets.len();
1452                    for &slot in site.gc_slots() {
1453                        let off = ctx
1454                            .frame
1455                            .slot_offset(slot)
1456                            .ok_or(EmitError::Mir(MirError::UnknownSlot(slot)))?;
1457                        self.gc_offsets.push(off);
1458                    }
1459                    let landing = match site.landing_pad() {
1460                        Some(b) => Some(
1461                            self.labels
1462                                .get(b.index())
1463                                .copied()
1464                                .ok_or(EmitError::Mir(MirError::UnknownBlock(b)))?,
1465                        ),
1466                        None => None,
1467                    };
1468                    self.call_sites.push(PendingSite {
1469                        ret,
1470                        gc: gc_start..self.gc_offsets.len(),
1471                        landing,
1472                    });
1473                }
1474                Ok(())
1475            }
1476            Enc::MovImm64 => match (
1477                ops.first().and_then(Operand::reg).and_then(Reg::as_phys),
1478                ops.get(1),
1479            ) {
1480                (Some(d), Some(Operand::Imm(v))) => self.mov_imm(Some(inst), d, *v),
1481                _ => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1482            },
1483            Enc::Raw(bytes) => self.asm.bytes(bytes).map_err(|e| asm_error(Some(inst), e)),
1484            Enc::Isa {
1485                fam,
1486                ops: srcs,
1487                cond,
1488            } => {
1489                let mnemonic = match fam {
1490                    Fam::Plain(m) => m,
1491                    Fam::Setcc => M::Setcc(cond_operand(ops, usize::from(cond), inst)?),
1492                    Fam::Cmovcc => M::Cmovcc(cond_operand(ops, usize::from(cond), inst)?),
1493                };
1494                let mut out = [IOp::Imm(0); 3];
1495                let mut rip = None;
1496                for (k, src) in srcs.iter().enumerate().take(3) {
1497                    out[k] = match *src {
1498                        Src::Fixed(r) => IOp::Reg(r),
1499                        Src::Op(n, size) => match ops.get(usize::from(n)) {
1500                            Some(Operand::Symbol(s)) => {
1501                                rip = Some(*s);
1502                                IOp::Mem(IMem::rip(size, 0))
1503                            }
1504                            Some(op) => self.operand(ctx, inst, op, size)?,
1505                            None => {
1506                                return Err(EmitError::Mir(MirError::OperandShape {
1507                                    inst,
1508                                    index: usize::from(n),
1509                                }));
1510                            }
1511                        },
1512                    };
1513                }
1514                let i = IInst::from_slice(mnemonic, &out[..srcs.len().min(3)])
1515                    .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
1516                match rip {
1517                    Some(s) => {
1518                        let name = f
1519                            .symbol_name(s)
1520                            .ok_or(EmitError::Mir(MirError::UnknownSymbol(s)))?;
1521                        if self.is_external(name) {
1522                            // A RIP-relative address of an external symbol:
1523                            // no row with a symbol operand has an immediate,
1524                            // so the displacement is the last four bytes.
1525                            let encoded = isa_lang::x86_64::encode(&i).map_err(|error| {
1526                                EmitError::Encode {
1527                                    inst: Some(inst),
1528                                    error,
1529                                }
1530                            })?;
1531                            return self.external_ref(
1532                                Some(inst),
1533                                encoded.as_bytes(),
1534                                RelocKind::Pc32,
1535                                name,
1536                            );
1537                        }
1538                        let label = self.label_for(name);
1539                        self.asm
1540                            .emit_rip(&i, label)
1541                            .map_err(|e| asm_error(Some(inst), e))
1542                    }
1543                    None => self.emit(Some(inst), &i),
1544                }
1545            }
1546        }
1547    }
1548
1549    fn jmp(&mut self, block: Block) -> Result<(), EmitError> {
1550        let label = self
1551            .labels
1552            .get(block.index())
1553            .copied()
1554            .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
1555        self.asm.jmp(label).map_err(EmitError::Asm)
1556    }
1557
1558    fn jcc(&mut self, cond: ICond, block: Block) -> Result<(), EmitError> {
1559        let label = self
1560            .labels
1561            .get(block.index())
1562            .copied()
1563            .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
1564        self.asm.jcc(cond, label).map_err(EmitError::Asm)
1565    }
1566
1567    fn operand(
1568        &self,
1569        ctx: &Ctx<'_>,
1570        inst: Inst,
1571        op: &Operand,
1572        size: Size,
1573    ) -> Result<IOp, EmitError> {
1574        Ok(match *op {
1575            Operand::Reg(r) => {
1576                let p = r.as_phys().ok_or(EmitError::VirtualRegister {
1577                    block: ctx.f.inst_block(inst).unwrap_or(Block::new(0)),
1578                    inst: Some(inst),
1579                })?;
1580                ireg(Some(inst), p, size)?
1581            }
1582            Operand::Imm(v) => IOp::Imm(v),
1583            Operand::Mem(m) => IOp::Mem(mem(inst, &m, size)?),
1584            Operand::Slot { slot, offset } => IOp::Mem(slot_mem(ctx.frame, slot, offset, size)?),
1585            _ => return Err(EmitError::Unencodable { inst: Some(inst) }),
1586        })
1587    }
1588}
1589
1590fn asm_error(inst: Option<Inst>, e: AsmError) -> EmitError {
1591    match e {
1592        AsmError::Encode(error) => EmitError::Encode { inst, error },
1593        other => EmitError::Asm(other),
1594    }
1595}
1596
1597/// The isa-lang register for a general-purpose physical register.
1598fn ireg(inst: Option<Inst>, p: PReg, size: Size) -> Result<IOp, EmitError> {
1599    let info = REGS.get(p.index()).ok_or(EmitError::Unencodable { inst })?;
1600    if info.class() != GPR {
1601        return Err(EmitError::Unencodable { inst });
1602    }
1603    IReg::new(size, info.hw())
1604        .map(IOp::Reg)
1605        .ok_or(EmitError::Unencodable { inst })
1606}
1607
1608fn qword(inst: Inst, r: Reg) -> Result<IReg, EmitError> {
1609    let p = r
1610        .as_phys()
1611        .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
1612    match ireg(Some(inst), p, Size::Qword)? {
1613        IOp::Reg(r) => Ok(r),
1614        _ => Err(EmitError::Unencodable { inst: Some(inst) }),
1615    }
1616}
1617
1618fn mem(inst: Inst, m: &Mem, size: Size) -> Result<IMem, EmitError> {
1619    let mut out = match m.base_reg() {
1620        Some(b) => IMem::new(size, qword(inst, b)?),
1621        None => IMem::absolute(size, 0),
1622    };
1623    if let Some((i, s)) = m.index_reg() {
1624        let scale = match s {
1625            Scale::X1 => IScale::X1,
1626            Scale::X2 => IScale::X2,
1627            Scale::X4 => IScale::X4,
1628            Scale::X8 => IScale::X8,
1629        };
1630        out = out.with_index(qword(inst, i)?, scale);
1631    }
1632    Ok(out.with_disp(m.disp()))
1633}
1634
1635fn slot_mem(
1636    frame: &FrameLayout,
1637    slot: StackSlot,
1638    offset: i32,
1639    size: Size,
1640) -> Result<IMem, EmitError> {
1641    let base = frame
1642        .slot_offset(slot)
1643        .ok_or(EmitError::Mir(MirError::UnknownSlot(slot)))?;
1644    let disp = base
1645        .checked_add(offset)
1646        .ok_or(EmitError::Frame(FrameError::TooLarge))?;
1647    Ok(IMem::new(size, IReg::RSP).with_disp(disp))
1648}
1649
1650fn block_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<BlockCall, EmitError> {
1651    match ops.get(index) {
1652        Some(Operand::Block(c)) => Ok(*c),
1653        _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1654    }
1655}
1656
1657fn cond_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<ICond, EmitError> {
1658    match ops.get(index) {
1659        Some(Operand::Cond(c)) => {
1660            ICond::from_code(c.code()).ok_or(EmitError::Mir(MirError::OperandShape { inst, index }))
1661        }
1662        _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1663    }
1664}
1665
1666/// A parallel-copy pair as a resolver move: the destination is a block
1667/// parameter or a copy's destination, the source an argument.
1668fn to_move(f: &Function, dst: Operand, src: Operand) -> Result<Move, EmitError> {
1669    let class_of = |op: &Operand| op.reg().and_then(|r| f.reg_class(r));
1670    let class = class_of(&dst)
1671        .or_else(|| class_of(&src))
1672        .unwrap_or(f.target().addr_class());
1673    let loc = |op: Operand| -> Option<Loc> {
1674        match op {
1675            Operand::Reg(r) => r.as_phys().map(Loc::Reg),
1676            Operand::Slot { slot, offset: 0 } => Some(Loc::Slot(slot)),
1677            _ => None,
1678        }
1679    };
1680    let bad = EmitError::Unencodable { inst: None };
1681    let d = loc(dst).ok_or(bad.clone())?;
1682    let s = match src {
1683        Operand::Imm(v) => Source::Imm(v),
1684        other => Source::Loc(loc(other).ok_or(bad)?),
1685    };
1686    Ok(Move {
1687        dst: d,
1688        src: s,
1689        class,
1690    })
1691}
1692
1693/// The probe loop's counter: `r11`, or `r10` if the entry block receives a
1694/// value in `r11` (neither carries arguments in SysV or Win64).
1695fn probe_scratch(f: &Function) -> Result<PReg, EmitError> {
1696    let params = f.entry().map_or(&[][..], |e| f.params(e));
1697    let taken = |r: PReg| params.iter().any(|p| p.reg() == Some(r.into()));
1698    [R11, R10]
1699        .into_iter()
1700        .find(|&r| !taken(r))
1701        .ok_or(EmitError::ProbeScratch)
1702}
1703
1704/// Checks the function is ready for emission: physical registers only, tied
1705/// operands equal, and the frame's registers never written by the body.
1706fn check_physical(f: &Function) -> Result<(), EmitError> {
1707    let target = f.target();
1708    let reserved = f.reserved();
1709    for block in f.blocks() {
1710        for p in f.params(block) {
1711            if let Some(r) = p.reg() {
1712                match r.as_phys() {
1713                    None => return Err(EmitError::VirtualRegister { block, inst: None }),
1714                    Some(p) if reserved.contains(p) => {
1715                        return Err(EmitError::ReservedWrite { block, inst: None });
1716                    }
1717                    Some(_) => {}
1718                }
1719            }
1720        }
1721        for &inst in f.insts(block) {
1722            let virt = EmitError::VirtualRegister {
1723                block,
1724                inst: Some(inst),
1725            };
1726            let ops = f.operands(inst);
1727            for op in ops {
1728                let any_virtual = match op {
1729                    Operand::Reg(r) => r.is_virtual(),
1730                    Operand::Mem(m) => m.regs().any(Reg::is_virtual),
1731                    Operand::Block(call) => f
1732                        .args(*call)
1733                        .iter()
1734                        .any(|a| a.reg().is_some_and(Reg::is_virtual)),
1735                    _ => false,
1736                };
1737                if any_virtual {
1738                    return Err(virt);
1739                }
1740            }
1741            let Some(desc) = f.opcode(inst).and_then(|o| target.inst(o)) else {
1742                continue;
1743            };
1744            let defs = match desc.kind() {
1745                InstKind::Copy => 1,
1746                InstKind::ParallelCopy => ops.len() / 2,
1747                _ => desc.defs(),
1748            };
1749            let reserved_write = ops
1750                .iter()
1751                .take(defs)
1752                .filter_map(|o| o.reg().and_then(Reg::as_phys))
1753                .chain(
1754                    f.implicit(inst)
1755                        .iter()
1756                        .filter(|i| i.kind == ImplicitKind::Def)
1757                        .map(|i| i.reg),
1758                )
1759                .chain(desc.implicit_defs().iter().copied())
1760                .any(|p| reserved.contains(p))
1761                || !f.clobbers(inst).intersect(reserved).is_empty();
1762            if reserved_write {
1763                return Err(EmitError::ReservedWrite {
1764                    block,
1765                    inst: Some(inst),
1766                });
1767            }
1768            for (i, rule) in desc.operands().iter().enumerate() {
1769                if let OperandRule::Tied(d) = rule {
1770                    if ops.get(i).and_then(Operand::reg)
1771                        != ops.get(usize::from(*d)).and_then(Operand::reg)
1772                    {
1773                        return Err(EmitError::TiedMismatch { inst });
1774                    }
1775                }
1776            }
1777        }
1778    }
1779    Ok(())
1780}