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