Skip to main content

cranelift_codegen/machinst/
mod.rs

1//! This module exposes the machine-specific backend definition pieces.
2//!
3//! The MachInst infrastructure is the compiler backend, from CLIF
4//! (ir::Function) to machine code. The purpose of this infrastructure is, at a
5//! high level, to do instruction selection/lowering (to machine instructions),
6//! register allocation, and then perform all the fixups to branches, constant
7//! data references, etc., needed to actually generate machine code.
8//!
9//! The container for machine instructions, at various stages of construction,
10//! is the `VCode` struct. We refer to a sequence of machine instructions organized
11//! into basic blocks as "vcode". This is short for "virtual-register code".
12//!
13//! The compilation pipeline, from an `ir::Function` (already optimized as much as
14//! you like by machine-independent optimization passes) onward, is as follows.
15//!
16//! ```plain
17//!
18//!     ir::Function                (SSA IR, machine-independent opcodes)
19//!         |
20//!         |  [lower]
21//!         |
22//!     VCode<arch_backend::Inst>   (machine instructions:
23//!         |                        - mostly virtual registers.
24//!         |                        - cond branches in two-target form.
25//!         |                        - branch targets are block indices.
26//!         |                        - in-memory constants held by insns,
27//!         |                          with unknown offsets.
28//!         |                        - critical edges (actually all edges)
29//!         |                          are split.)
30//!         |
31//!         | [regalloc --> `regalloc2::Output`; VCode is unchanged]
32//!         |
33//!         | [binary emission via MachBuffer]
34//!         |
35//!     Vec<u8>                     (machine code:
36//!         |                        - two-dest branches resolved via
37//!         |                          streaming branch resolution/simplification.
38//!         |                        - regalloc `Allocation` results used directly
39//!         |                          by instruction emission code.
40//!         |                        - prologue and epilogue(s) built and emitted
41//!         |                          directly during emission.
42//!         |                        - SP-relative offsets resolved by tracking
43//!         |                          EmitState.)
44//!
45//! ```
46
47use crate::binemit::{Addend, CodeInfo, CodeOffset, Reloc};
48use crate::ir::{
49    self, DynamicStackSlot, Endianness, RelSourceLoc, StackSlot, TrapCode, Type,
50    function::FunctionParameters,
51};
52use crate::isa::FunctionAlignment;
53use crate::result::CodegenResult;
54use crate::settings;
55use crate::settings::Flags;
56use crate::value_label::ValueLabelsRanges;
57use alloc::string::String;
58use alloc::vec::Vec;
59use core::fmt;
60use core::fmt::Debug;
61use core::num::NonZeroU8;
62use cranelift_control::ControlPlane;
63use cranelift_entity::PrimaryMap;
64use regalloc2::VReg;
65use smallvec::{SmallVec, smallvec};
66
67#[cfg(feature = "enable-serde")]
68use serde_derive::{Deserialize, Serialize};
69
70/// Guaranteed to use "natural alignment" for the given type.
71const BIT_ALIGNED: u16 = 1 << 0;
72
73/// A load that reads data in memory that does not change for the
74/// duration of the function's execution.
75const BIT_READONLY: u16 = 1 << 1;
76
77/// Load multi-byte values from memory in a little-endian format.
78const BIT_LITTLE_ENDIAN: u16 = 1 << 2;
79
80/// Load multi-byte values from memory in a big-endian format.
81const BIT_BIG_ENDIAN: u16 = 1 << 3;
82
83/// Trap code, if any, for this memory operation.
84const MASK_TRAP_CODE: u16 = ((1 << TRAP_CODE_BITS) - 1) << TRAP_CODE_OFFSET;
85const TRAP_CODE_BITS: u16 = 8;
86const TRAP_CODE_OFFSET: u16 = 7;
87
88/// Whether this memory operation may be freely moved by the optimizer.
89const BIT_CAN_MOVE: u16 = 1 << 15;
90
91/// Backend memory-operation flags.
92///
93/// These are the bit-packed flags that backends operate on directly.
94///
95/// Unlike [`ir::MemFlagsData`], this does not carry alias-region metadata.
96#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
97#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
98pub struct MachMemFlags {
99    // Bit layout:
100    //
101    // - Bit 0: aligned
102    // - Bit 1: readonly
103    // - Bit 2: little-endian
104    // - Bit 3: big-endian
105    // - Bits 4..6: unused
106    // - Bits 7..14: trap code
107    // - Bit 15: can_move
108    bits: u16,
109}
110
111impl MachMemFlags {
112    /// Create a new empty set of flags.
113    pub const fn new() -> Self {
114        Self { bits: 0 }.with_trap_code(Some(TrapCode::HEAP_OUT_OF_BOUNDS))
115    }
116
117    /// Create a set of flags representing an access from a "trusted" address.
118    pub const fn trusted() -> Self {
119        Self::new().with_notrap().with_aligned()
120    }
121
122    const fn read_bit(self, bit: u16) -> bool {
123        self.bits & bit != 0
124    }
125
126    const fn with_bit(mut self, bit: u16) -> Self {
127        self.bits |= bit;
128        self
129    }
130
131    /// Return endianness of the memory access.
132    pub const fn endianness(self, native_endianness: Endianness) -> Endianness {
133        if self.read_bit(BIT_LITTLE_ENDIAN) {
134            Endianness::Little
135        } else if self.read_bit(BIT_BIG_ENDIAN) {
136            Endianness::Big
137        } else {
138            native_endianness
139        }
140    }
141
142    /// Return endianness of the memory access, if explicitly specified.
143    pub const fn explicit_endianness(self) -> Option<Endianness> {
144        if self.read_bit(BIT_LITTLE_ENDIAN) {
145            Some(Endianness::Little)
146        } else if self.read_bit(BIT_BIG_ENDIAN) {
147            Some(Endianness::Big)
148        } else {
149            None
150        }
151    }
152
153    /// Set endianness of the memory access, returning new flags.
154    pub const fn with_endianness(self, endianness: Endianness) -> Self {
155        let res = match endianness {
156            Endianness::Little => self.with_bit(BIT_LITTLE_ENDIAN),
157            Endianness::Big => self.with_bit(BIT_BIG_ENDIAN),
158        };
159        assert!(!(res.read_bit(BIT_LITTLE_ENDIAN) && res.read_bit(BIT_BIG_ENDIAN)));
160        res
161    }
162
163    /// Test if this memory access cannot trap.
164    pub const fn notrap(self) -> bool {
165        self.trap_code().is_none()
166    }
167
168    /// Set these flags to indicate this access does not trap.
169    pub const fn with_notrap(self) -> Self {
170        self.with_trap_code(None)
171    }
172
173    /// Test if the `can_move` flag is set.
174    pub const fn can_move(self) -> bool {
175        self.read_bit(BIT_CAN_MOVE)
176    }
177
178    /// Set the `can_move` flag, returning new flags.
179    pub const fn with_can_move(self) -> Self {
180        self.with_bit(BIT_CAN_MOVE)
181    }
182
183    /// Test if the `aligned` flag is set.
184    pub const fn aligned(self) -> bool {
185        self.read_bit(BIT_ALIGNED)
186    }
187
188    /// Set the `aligned` flag, returning new flags.
189    pub const fn with_aligned(self) -> Self {
190        self.with_bit(BIT_ALIGNED)
191    }
192
193    /// Test if the `readonly` flag is set.
194    pub const fn readonly(self) -> bool {
195        self.read_bit(BIT_READONLY)
196    }
197
198    /// Set the `readonly` flag, returning new flags.
199    pub const fn with_readonly(self) -> Self {
200        self.with_bit(BIT_READONLY)
201    }
202
203    /// Get the trap code to report if this memory access traps.
204    pub const fn trap_code(self) -> Option<TrapCode> {
205        let byte = ((self.bits & MASK_TRAP_CODE) >> TRAP_CODE_OFFSET) as u8;
206        match NonZeroU8::new(byte) {
207            Some(code) => Some(TrapCode::from_raw(code)),
208            None => None,
209        }
210    }
211
212    /// Configures these flags with the specified trap code `code`.
213    pub const fn with_trap_code(mut self, code: Option<TrapCode>) -> Self {
214        let bits = match code {
215            Some(code) => code.as_raw().get() as u16,
216            None => 0,
217        };
218        self.bits &= !MASK_TRAP_CODE;
219        self.bits |= bits << TRAP_CODE_OFFSET;
220        self
221    }
222}
223
224impl fmt::Display for MachMemFlags {
225    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        match self.trap_code() {
227            None => write!(f, " notrap")?,
228            Some(TrapCode::HEAP_OUT_OF_BOUNDS) => {}
229            Some(t) => write!(f, " {t}")?,
230        }
231        if self.aligned() {
232            write!(f, " aligned")?;
233        }
234        if self.readonly() {
235            write!(f, " readonly")?;
236        }
237        if self.can_move() {
238            write!(f, " can_move")?;
239        }
240        if self.read_bit(BIT_BIG_ENDIAN) {
241            write!(f, " big")?;
242        }
243        if self.read_bit(BIT_LITTLE_ENDIAN) {
244            write!(f, " little")?;
245        }
246        Ok(())
247    }
248}
249
250#[macro_use]
251pub mod isle;
252
253pub mod lower;
254pub use lower::*;
255pub mod vcode;
256pub use vcode::*;
257pub mod compile;
258pub use compile::*;
259pub mod blockorder;
260pub use blockorder::*;
261pub mod abi;
262pub use abi::*;
263pub mod buffer;
264pub use buffer::*;
265pub mod helpers;
266pub use helpers::*;
267pub mod valueregs;
268pub use reg::*;
269pub use valueregs::*;
270pub mod reg;
271
272/// A machine instruction.
273pub trait MachInst: Clone + Debug {
274    /// The ABI machine spec for this `MachInst`.
275    type ABIMachineSpec: ABIMachineSpec<I = Self>;
276
277    /// Return the registers referenced by this machine instruction along with
278    /// the modes of reference (use, def, modify).
279    fn get_operands(&mut self, collector: &mut impl OperandVisitor);
280
281    /// If this is a simple move, return the (source, destination) tuple of registers.
282    fn is_move(&self) -> Option<(Writable<Reg>, Reg)>;
283
284    /// Is this a terminator (branch or ret)? If so, return its type
285    /// (ret/uncond/cond) and target if applicable.
286    fn is_term(&self) -> MachTerminator;
287
288    /// Is this an unconditional trap?
289    fn is_trap(&self) -> bool;
290
291    /// Is this an "args" pseudoinst?
292    fn is_args(&self) -> bool;
293
294    /// Classify the type of call instruction this is.
295    ///
296    /// This enables more granular function type analysis and optimization.
297    /// Returns `CallType::None` for non-call instructions, `CallType::Regular`
298    /// for normal calls that return to the caller, and `CallType::TailCall`
299    /// for tail calls that don't return to the caller.
300    fn call_type(&self) -> CallType;
301
302    /// Should this instruction's clobber-list be included in the
303    /// clobber-set?
304    fn is_included_in_clobbers(&self) -> bool;
305
306    /// Does this instruction access memory?
307    fn is_mem_access(&self) -> bool;
308
309    /// Generate a move.
310    fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;
311
312    /// Generate a dummy instruction that will keep a value alive but
313    /// has no other purpose.
314    fn gen_dummy_use(reg: Reg) -> Self;
315
316    /// Determine register class(es) to store the given Cranelift type, and the
317    /// Cranelift type actually stored in the underlying register(s).  May return
318    /// an error if the type isn't supported by this backend.
319    ///
320    /// If the type requires multiple registers, then the list of registers is
321    /// returned in little-endian order.
322    ///
323    /// Note that the type actually stored in the register(s) may differ in the
324    /// case that a value is split across registers: for example, on a 32-bit
325    /// target, an I64 may be stored in two registers, each of which holds an
326    /// I32. The actually-stored types are used only to inform the backend when
327    /// generating spills and reloads for individual registers.
328    fn rc_for_type(ty: Type) -> CodegenResult<(&'static [RegClass], &'static [Type])>;
329
330    /// Get an appropriate type that can fully hold a value in a given
331    /// register class. This may not be the only type that maps to
332    /// that class, but when used with `gen_move()` or the ABI trait's
333    /// load/spill constructors, it should produce instruction(s) that
334    /// move the entire register contents.
335    fn canonical_type_for_rc(rc: RegClass) -> Type;
336
337    /// Generate a jump to another target. Used during lowering of
338    /// control flow.
339    fn gen_jump(target: MachLabel) -> Self;
340
341    /// Generate a store of an immediate 64-bit integer to a register. Used by
342    /// the control plane to generate random instructions.
343    fn gen_imm_u64(_value: u64, _dst: Writable<Reg>) -> Option<Self> {
344        None
345    }
346
347    /// Generate a store of an immediate 64-bit integer to a register. Used by
348    /// the control plane to generate random instructions. The tmp register may
349    /// be used by architectures which don't support writing immediate values to
350    /// floating point registers directly.
351    fn gen_imm_f64(_value: f64, _tmp: Writable<Reg>, _dst: Writable<Reg>) -> SmallVec<[Self; 2]> {
352        SmallVec::new()
353    }
354
355    /// Generate a NOP. The `preferred_size` parameter allows the caller to
356    /// request a NOP of that size, or as close to it as possible. The machine
357    /// backend may return a NOP whose binary encoding is smaller than the
358    /// preferred size, but must not return a NOP that is larger. However,
359    /// the instruction must have a nonzero size if preferred_size is nonzero.
360    fn gen_nop(preferred_size: usize) -> Self;
361
362    /// The various kinds of NOP, with size, sorted in ascending-size
363    /// order.
364    fn gen_nop_units() -> Vec<Vec<u8>>;
365
366    /// Align a basic block offset (from start of function).  By default, no
367    /// alignment occurs.
368    fn align_basic_block(offset: CodeOffset) -> CodeOffset {
369        offset
370    }
371
372    /// What is the worst-case instruction size emitted by this instruction type?
373    fn worst_case_size() -> CodeOffset;
374
375    /// Worst-case growth, in bytes, that emitting a single `MachInst`
376    /// instruction may add to the `MachBuffer`'s pending-island state
377    /// (constants, deferred traps, and worst-case veneers for new
378    /// fixups).
379    ///
380    /// `MachBuffer` treats one instruction emission as the atomic
381    /// "commit unit" and uses `worst_case_size() +
382    /// worst_case_island_growth()` as the per-instruction lookahead
383    /// bound when deciding whether to flush an island. Backends whose
384    /// label-use kinds always have wide enough range that islands are
385    /// never required may leave this at zero.
386    fn worst_case_island_growth() -> CodeOffset;
387
388    /// What is the register class used for reference types (GC-observable pointers)? Can
389    /// be dependent on compilation flags.
390    fn ref_type_regclass(_flags: &Flags) -> RegClass;
391
392    /// Is this a safepoint?
393    fn is_safepoint(&self) -> bool;
394
395    /// Generate an instruction that must appear at the beginning of a basic
396    /// block, if any. Note that the return value must not be subject to
397    /// register allocation.
398    fn gen_block_start(
399        _is_indirect_branch_target: bool,
400        _is_forward_edge_cfi_enabled: bool,
401    ) -> Option<Self> {
402        None
403    }
404
405    /// Returns a description of the alignment required for functions for this
406    /// architecture.
407    fn function_alignment() -> FunctionAlignment;
408
409    /// Is this a low-level, one-way branch, not meant for use in a
410    /// VCode body? These instructions are meant to be used only when
411    /// directly emitted, i.e. when `MachInst` is used as an assembler
412    /// library.
413    fn is_low_level_branch(&self) -> bool {
414        false
415    }
416
417    /// A label-use kind: a type that describes the types of label references that
418    /// can occur in an instruction.
419    type LabelUse: MachInstLabelUse;
420
421    /// Byte representation of a trap opcode which is inserted by `MachBuffer`
422    /// during its `defer_trap` method.
423    const TRAP_OPCODE: &'static [u8];
424}
425
426/// A descriptor of a label reference (use) in an instruction set.
427pub trait MachInstLabelUse: Clone + Copy + Debug + Eq {
428    /// Required alignment for any veneer. Usually the required instruction
429    /// alignment (e.g., 4 for a RISC with 32-bit instructions, or 1 for x86).
430    const ALIGN: CodeOffset;
431
432    /// What is the maximum PC-relative range (positive)? E.g., if `1024`, a
433    /// label-reference fixup at offset `x` is valid if the label resolves to `x
434    /// + 1024`.
435    fn max_pos_range(self) -> CodeOffset;
436    /// What is the maximum PC-relative range (negative)? This is the absolute
437    /// value; i.e., if `1024`, then a label-reference fixup at offset `x` is
438    /// valid if the label resolves to `x - 1024`.
439    fn max_neg_range(self) -> CodeOffset;
440    /// What is the size of code-buffer slice this label-use needs to patch in
441    /// the label's value?
442    fn patch_size(self) -> CodeOffset;
443    /// Perform a code-patch, given the offset into the buffer of this label use
444    /// and the offset into the buffer of the label's definition.
445    /// It is guaranteed that, given `delta = offset - label_offset`, we will
446    /// have `offset >= -self.max_neg_range()` and `offset <=
447    /// self.max_pos_range()`.
448    fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset);
449    /// Can the label-use be patched to a veneer that supports a longer range?
450    /// Usually valid for jumps (a short-range jump can jump to a longer-range
451    /// jump), but not for e.g. constant pool references, because the constant
452    /// load would require different code (one more level of indirection).
453    fn supports_veneer(self) -> bool;
454    /// How many bytes are needed for a veneer?
455    fn veneer_size(self) -> CodeOffset;
456    /// What's the largest possible veneer that may be generated?
457    fn worst_case_veneer_size() -> CodeOffset;
458    /// Generate a veneer. The given code-buffer slice is `self.veneer_size()`
459    /// bytes long at offset `veneer_offset` in the buffer. The original
460    /// label-use will be patched to refer to this veneer's offset.  A new
461    /// (offset, LabelUse) is returned that allows the veneer to use the actual
462    /// label. For veneers to work properly, it is expected that the new veneer
463    /// has a larger range; on most platforms this probably means either a
464    /// "long-range jump" (e.g., on ARM, the 26-bit form), or if already at that
465    /// stage, a jump that supports a full 32-bit range, for example.
466    fn generate_veneer(self, buffer: &mut [u8], veneer_offset: CodeOffset) -> (CodeOffset, Self);
467
468    /// Returns the corresponding label-use for the relocation specified.
469    ///
470    /// This returns `None` if the relocation doesn't have a corresponding
471    /// representation for the target architecture.
472    fn from_reloc(reloc: Reloc, addend: Addend) -> Option<Self>;
473}
474
475/// Classification of call instruction types for granular analysis.
476#[derive(Clone, Copy, Debug, PartialEq, Eq)]
477pub enum CallType {
478    /// Not a call instruction.
479    None,
480    /// Regular call that returns to the caller.
481    Regular,
482    /// Tail call that doesn't return to the caller.
483    TailCall,
484}
485
486/// Function classification based on call patterns.
487///
488/// This enum classifies functions based on their calling behavior to enable
489/// targeted optimizations. Functions are categorized as:
490/// - `None`: No calls at all (can use simplified calling conventions)
491/// - `TailOnly`: Only tail calls (may skip frame setup in some cases)
492/// - `Regular`: Has regular calls (requires full calling convention support)
493#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
494pub enum FunctionCalls {
495    /// Function makes no calls at all.
496    #[default]
497    None,
498    /// Function only makes tail calls (no regular calls).
499    TailOnly,
500    /// Function makes at least one regular call (may also have tail calls).
501    Regular,
502}
503
504impl FunctionCalls {
505    /// Update the function classification based on a new call instruction.
506    ///
507    /// This method implements the merge logic for accumulating call patterns:
508    /// - Any regular call makes the function Regular
509    /// - Tail calls upgrade None to TailOnly
510    /// - Regular always stays Regular
511    pub fn update(&mut self, call_type: CallType) {
512        *self = match (*self, call_type) {
513            // No call instruction - state unchanged
514            (current, CallType::None) => current,
515            // Regular call always results in Regular classification
516            (_, CallType::Regular) => FunctionCalls::Regular,
517            // Tail call: None becomes TailOnly, others unchanged
518            (FunctionCalls::None, CallType::TailCall) => FunctionCalls::TailOnly,
519            (current, CallType::TailCall) => current,
520        };
521    }
522}
523
524/// Describes a block terminator (not call) in the VCode.
525///
526/// Actual targets are not included: the single-source-of-truth for
527/// those is the VCode itself, which holds, for each block, successors
528/// and outgoing branch args per successor.
529#[derive(Clone, Debug, PartialEq, Eq)]
530pub enum MachTerminator {
531    /// Not a terminator.
532    None,
533    /// A return instruction.
534    Ret,
535    /// A tail call.
536    RetCall,
537    /// A branch.
538    Branch,
539}
540
541/// A trait describing the ability to encode a MachInst into binary machine code.
542pub trait MachInstEmit: MachInst {
543    /// Persistent state carried across `emit` invocations.
544    type State: MachInstEmitState<Self>;
545
546    /// Constant information used in `emit` invocations.
547    type Info;
548
549    /// Emit the instruction.
550    fn emit(&self, code: &mut MachBuffer<Self>, info: &Self::Info, state: &mut Self::State);
551
552    /// Pretty-print the instruction.
553    fn pretty_print_inst(&self, state: &mut Self::State) -> String;
554}
555
556/// A trait describing the emission state carried between MachInsts when
557/// emitting a function body.
558pub trait MachInstEmitState<I: VCodeInst>: Default + Clone + Debug {
559    /// Create a new emission state given the ABI object.
560    fn new(abi: &Callee<I::ABIMachineSpec>, ctrl_plane: ControlPlane) -> Self;
561
562    /// Update the emission state before emitting an instruction that is a
563    /// safepoint.
564    fn pre_safepoint(&mut self, user_stack_map: Option<ir::UserStackMap>);
565
566    /// The emission state holds ownership of a control plane, so it doesn't
567    /// have to be passed around explicitly too much. `ctrl_plane_mut` may
568    /// be used if temporary access to the control plane is needed by some
569    /// other function that doesn't have access to the emission state.
570    fn ctrl_plane_mut(&mut self) -> &mut ControlPlane;
571
572    /// Used to continue using a control plane after the emission state is
573    /// not needed anymore.
574    fn take_ctrl_plane(self) -> ControlPlane;
575
576    /// A hook that triggers when first emitting a new block.
577    /// It is guaranteed to be called before any instructions are emitted.
578    fn on_new_block(&mut self) {}
579
580    /// The [`FrameLayout`] for the function currently being compiled.
581    fn frame_layout(&self) -> &FrameLayout;
582}
583
584/// The result of a `MachBackend::compile_function()` call. Contains machine
585/// code (as bytes) and a disassembly, if requested.
586#[derive(PartialEq, Debug, Clone)]
587#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
588pub struct CompiledCodeBase<T: CompilePhase> {
589    /// Machine code.
590    pub buffer: MachBufferFinalized<T>,
591    /// Disassembly, if requested.
592    pub vcode: Option<String>,
593    /// Debug info: value labels to registers/stackslots at code offsets.
594    pub value_labels_ranges: ValueLabelsRanges,
595    /// Basic-block layout info: block start offsets.
596    ///
597    /// This info is generated only if the `machine_code_cfg_info`
598    /// flag is set.
599    pub bb_starts: Vec<CodeOffset>,
600    /// Basic-block layout info: block edges. Each edge is `(from,
601    /// to)`, where `from` and `to` are basic-block start offsets of
602    /// the respective blocks.
603    ///
604    /// This info is generated only if the `machine_code_cfg_info`
605    /// flag is set.
606    pub bb_edges: Vec<(CodeOffset, CodeOffset)>,
607}
608
609impl CompiledCodeStencil {
610    /// Apply function parameters to finalize a stencil into its final form.
611    pub fn apply_params(self, params: &FunctionParameters) -> CompiledCode {
612        CompiledCode {
613            buffer: self.buffer.apply_base_srcloc(params.base_srcloc()),
614            vcode: self.vcode,
615            value_labels_ranges: self.value_labels_ranges,
616            bb_starts: self.bb_starts,
617            bb_edges: self.bb_edges,
618        }
619    }
620}
621
622impl<T: CompilePhase> CompiledCodeBase<T> {
623    /// Get a `CodeInfo` describing section sizes from this compilation result.
624    pub fn code_info(&self) -> CodeInfo {
625        CodeInfo {
626            total_size: self.buffer.total_size(),
627        }
628    }
629
630    /// Returns a reference to the machine code generated for this function compilation.
631    pub fn code_buffer(&self) -> &[u8] {
632        self.buffer.data()
633    }
634
635    /// Get the disassembly of the buffer, using the given capstone context.
636    #[cfg(feature = "disas")]
637    pub fn disassemble(
638        &self,
639        params: Option<&crate::ir::function::FunctionParameters>,
640        cs: &capstone::Capstone,
641    ) -> Result<String, anyhow::Error> {
642        use core::fmt::Write;
643
644        let mut buf = String::new();
645
646        let relocs = self.buffer.relocs();
647        let traps = self.buffer.traps();
648        let mut patchables = self.buffer.patchable_call_sites().peekable();
649
650        // Normalize the block starts to include an initial block of offset 0.
651        let mut block_starts = Vec::new();
652        if self.bb_starts.first().copied() != Some(0) {
653            block_starts.push(0);
654        }
655        block_starts.extend_from_slice(&self.bb_starts);
656        block_starts.push(self.buffer.data().len() as u32);
657
658        // Iterate over block regions, to ensure that we always produce block labels
659        for (n, (&start, &end)) in block_starts
660            .iter()
661            .zip(block_starts.iter().skip(1))
662            .enumerate()
663        {
664            writeln!(buf, "block{n}: ; offset 0x{start:x}")?;
665
666            let buffer = &self.buffer.data()[start as usize..end as usize];
667            let insns = cs.disasm_all(buffer, start as u64).map_err(map_caperr)?;
668            for i in insns.iter() {
669                write!(buf, "  ")?;
670
671                let op_str = i.op_str().unwrap_or("");
672                if let Some(s) = i.mnemonic() {
673                    write!(buf, "{s}")?;
674                    if !op_str.is_empty() {
675                        write!(buf, " ")?;
676                    }
677                }
678
679                write!(buf, "{op_str}")?;
680
681                let end = i.address() + i.bytes().len() as u64;
682                let contains = |off| i.address() <= off && off < end;
683
684                for reloc in relocs.iter().filter(|reloc| contains(reloc.offset as u64)) {
685                    write!(
686                        buf,
687                        " ; reloc_external {} {} {}",
688                        reloc.kind,
689                        reloc.target.display(params),
690                        reloc.addend,
691                    )?;
692                }
693
694                if let Some(trap) = traps.iter().find(|trap| contains(trap.offset as u64)) {
695                    write!(buf, " ; trap: {}", trap.code)?;
696                }
697
698                if let Some(patchable) = patchables.peek()
699                    && patchable.ret_addr == end as u32
700                {
701                    write!(
702                        buf,
703                        " ; patchable call: NOP out last {} bytes",
704                        patchable.len
705                    )?;
706                    patchables.next();
707                }
708
709                writeln!(buf)?;
710            }
711        }
712
713        return Ok(buf);
714
715        fn map_caperr(err: capstone::Error) -> anyhow::Error {
716            anyhow::format_err!("{err}")
717        }
718    }
719}
720
721/// Result of compiling a `FunctionStencil`, before applying `FunctionParameters` onto it.
722///
723/// Only used internally, in a transient manner, for the incremental compilation cache.
724pub type CompiledCodeStencil = CompiledCodeBase<Stencil>;
725
726/// `CompiledCode` in its final form (i.e. after `FunctionParameters` have been applied), ready for
727/// consumption.
728pub type CompiledCode = CompiledCodeBase<Final>;
729
730impl CompiledCode {
731    /// If available, return information about the code layout in the
732    /// final machine code: the offsets (in bytes) of each basic-block
733    /// start, and all basic-block edges.
734    pub fn get_code_bb_layout(&self) -> (Vec<usize>, Vec<(usize, usize)>) {
735        (
736            self.bb_starts.iter().map(|&off| off as usize).collect(),
737            self.bb_edges
738                .iter()
739                .map(|&(from, to)| (from as usize, to as usize))
740                .collect(),
741        )
742    }
743
744    /// Creates unwind information for the function.
745    ///
746    /// Returns `None` if the function has no unwind information.
747    #[cfg(feature = "unwind")]
748    pub fn create_unwind_info(
749        &self,
750        isa: &dyn crate::isa::TargetIsa,
751    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
752        use crate::isa::unwind::UnwindInfoKind;
753        let unwind_info_kind = match isa.triple().operating_system {
754            target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows,
755            _ => UnwindInfoKind::SystemV,
756        };
757        self.create_unwind_info_of_kind(isa, unwind_info_kind)
758    }
759
760    /// Creates unwind information for the function using the supplied
761    /// "kind". Supports cross-OS (but not cross-arch) generation.
762    ///
763    /// Returns `None` if the function has no unwind information.
764    #[cfg(feature = "unwind")]
765    pub fn create_unwind_info_of_kind(
766        &self,
767        isa: &dyn crate::isa::TargetIsa,
768        unwind_info_kind: crate::isa::unwind::UnwindInfoKind,
769    ) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
770        isa.emit_unwind_info(self, unwind_info_kind)
771    }
772}
773
774/// An object that can be used to create the text section of an executable.
775///
776/// This primarily handles resolving relative relocations at
777/// text-section-assembly time rather than at load/link time. This
778/// architecture-specific logic is sort of like a linker, but only for one
779/// object file at a time.
780pub trait TextSectionBuilder {
781    /// Appends `data` to the text section with the `align` specified.
782    ///
783    /// If `labeled` is `true` then this also binds the appended data to the
784    /// `n`th label for how many times this has been called with `labeled:
785    /// true`. The label target can be passed as the `target` argument to
786    /// `resolve_reloc`.
787    ///
788    /// This function returns the offset at which the data was placed in the
789    /// text section.
790    fn append(
791        &mut self,
792        labeled: bool,
793        data: &[u8],
794        align: u32,
795        ctrl_plane: &mut ControlPlane,
796    ) -> u64;
797
798    /// Attempts to resolve a relocation for this function.
799    ///
800    /// The `offset` is the offset of the relocation, within the text section.
801    /// The `reloc` is the kind of relocation.
802    /// The `addend` is the value to add to the relocation.
803    /// The `target` is the labeled function that is the target of this
804    /// relocation.
805    ///
806    /// Labeled functions are created with the `append` function above by
807    /// setting the `labeled` parameter to `true`.
808    ///
809    /// If this builder does not know how to handle `reloc` then this function
810    /// will return `false`. Otherwise this function will return `true` and this
811    /// relocation will be resolved in the final bytes returned by `finish`.
812    fn resolve_reloc(&mut self, offset: u64, reloc: Reloc, addend: Addend, target: usize) -> bool;
813
814    /// A debug-only option which is used to for
815    fn force_veneers(&mut self);
816
817    /// Write the `data` provided at `offset`, for example when resolving a
818    /// relocation.
819    fn write(&mut self, offset: u64, data: &[u8]);
820
821    /// Completes this text section, filling out any final details, and returns
822    /// the bytes of the text section.
823    fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8>;
824}