Skip to main content

cranelift_codegen/machinst/
buffer.rs

1//! In-memory representation of compiled machine code, with labels and fixups to
2//! refer to those labels. Handles constant-pool island insertion and also
3//! veneer insertion for out-of-range jumps.
4//!
5//! This code exists to solve three problems:
6//!
7//! - Branch targets for forward branches are not known until later, when we
8//!   emit code in a single pass through the instruction structs.
9//!
10//! - On many architectures, address references or offsets have limited range.
11//!   For example, on AArch64, conditional branches can only target code +/- 1MB
12//!   from the branch itself.
13//!
14//! - The lowering of control flow from the CFG-with-edges produced by
15//!   [BlockLoweringOrder](super::BlockLoweringOrder), combined with many empty
16//!   edge blocks when the register allocator does not need to insert any
17//!   spills/reloads/moves in edge blocks, results in many suboptimal branch
18//!   patterns. The lowering also pays no attention to block order, and so
19//!   two-target conditional forms (cond-br followed by uncond-br) can often by
20//!   avoided because one of the targets is the fallthrough. There are several
21//!   cases here where we can simplify to use fewer branches.
22//!
23//! This "buffer" implements a single-pass code emission strategy (with a later
24//! "fixup" pass, but only through recorded fixups, not all instructions). The
25//! basic idea is:
26//!
27//! - Emit branches as they are, including two-target (cond/uncond) compound
28//!   forms, but with zero offsets and optimistically assuming the target will be
29//!   in range. Record the "fixup" for later. Targets are denoted instead by
30//!   symbolic "labels" that are then bound to certain offsets in the buffer as
31//!   we emit code. (Nominally, there is a label at the start of every basic
32//!   block.)
33//!
34//! - As we do this, track the offset in the buffer at which the first label
35//!   reference "goes out of range". We call this the "deadline". If we reach the
36//!   deadline and we still have not bound the label to which an unresolved branch
37//!   refers, we have a problem!
38//!
39//! - To solve this problem, we emit "islands" full of "veneers". An island is
40//!   simply a chunk of code inserted in the middle of the code actually produced
41//!   by the emitter (e.g., VCode iterating over instruction structs). Islands
42//!   are emitted at "safe" points (no fall-through into the island contents):
43//!   between basic blocks during emission, or via a jump around the island.
44//!
45//! - A "veneer" is an instruction (or sequence of instructions) in an "island"
46//!   that implements a longer-range reference to a label. The idea is that, for
47//!   example, a branch with a limited range can branch to a "veneer" instead,
48//!   which is simply a branch in a form that can use a longer-range reference. On
49//!   AArch64, for example, conditionals have a +/- 1 MB range, but a conditional
50//!   can branch to an unconditional branch which has a +/- 128 MB range. Hence, a
51//!   conditional branch's label reference can be fixed up with a "veneer" to
52//!   achieve a longer range.
53//!
54//! - To implement all of this, we require the backend to provide a `LabelUse`
55//!   type that implements a trait. This is nominally an enum that records one of
56//!   several kinds of references to an offset in code -- basically, a relocation
57//!   type -- and will usually correspond to different instruction formats. The
58//!   `LabelUse` implementation specifies the maximum range, how to patch in the
59//!   actual label location when known, and how to generate a veneer to extend the
60//!   range.
61//!
62//! That satisfies label references, but we still may have suboptimal branch
63//! patterns. To clean up the branches, we do a simple "peephole"-style
64//! optimization on the fly. To do so, the emitter (e.g., `Inst::emit()`)
65//! informs the buffer of branches in the code and, in the case of conditionals,
66//! the code that would have been emitted to invert this branch's condition. We
67//! track the "latest branches": these are branches that are contiguous up to
68//! the current offset. (If any code is emitted after a branch, that branch or
69//! run of contiguous branches is no longer "latest".) The latest branches are
70//! those that we can edit by simply truncating the buffer and doing something
71//! else instead.
72//!
73//! To optimize branches, we implement several simple rules, and try to apply
74//! them to the "latest branches" when possible:
75//!
76//! - A branch with a label target, when that label is bound to the ending
77//!   offset of the branch (the fallthrough location), can be removed altogether,
78//!   because the branch would have no effect).
79//!
80//! - An unconditional branch that starts at a label location, and branches to
81//!   another label, results in a "label alias": all references to the label bound
82//!   *to* this branch instruction are instead resolved to the *target* of the
83//!   branch instruction. This effectively removes empty blocks that just
84//!   unconditionally branch to the next block. We call this "branch threading".
85//!
86//! - A conditional followed by an unconditional, when the conditional branches
87//!   to the unconditional's fallthrough, results in (i) the truncation of the
88//!   unconditional, (ii) the inversion of the condition's condition, and (iii)
89//!   replacement of the conditional's target (using the original target of the
90//!   unconditional). This is a fancy way of saying "we can flip a two-target
91//!   conditional branch's taken/not-taken targets if it works better with our
92//!   fallthrough". To make this work, the emitter actually gives the buffer
93//!   *both* forms of every conditional branch: the true form is emitted into the
94//!   buffer, and the "inverted" machine-code bytes are provided as part of the
95//!   branch-fixup metadata.
96//!
97//! - An unconditional B preceded by another unconditional P, when B's label(s) have
98//!   been redirected to target(B), can be removed entirely. This is an extension
99//!   of the branch-threading optimization, and is valid because if we know there
100//!   will be no fallthrough into this branch instruction (the prior instruction
101//!   is an unconditional jump), and if we know we have successfully redirected
102//!   all labels, then this branch instruction is unreachable. Note that this
103//!   works because the redirection happens before the label is ever resolved
104//!   (fixups happen at island emission time, at which point latest-branches are
105//!   cleared, or at the end of emission), so we are sure to catch and redirect
106//!   all possible paths to this instruction.
107//!
108//! # Branch-optimization Correctness
109//!
110//! The branch-optimization mechanism depends on a few data structures with
111//! invariants, which are always held outside the scope of top-level public
112//! methods:
113//!
114//! - The latest-branches list. Each entry describes a span of the buffer
115//!   (start/end offsets), the label target, the corresponding fixup-list entry
116//!   index, and the bytes (must be the same length) for the inverted form, if
117//!   conditional. The list of labels that are bound to the start-offset of this
118//!   branch is *complete* (if any label has a resolved offset equal to `start`
119//!   and is not an alias, it must appear in this list) and *precise* (no label
120//!   in this list can be bound to another offset). No label in this list should
121//!   be an alias.  No two branch ranges can overlap, and branches are in
122//!   ascending-offset order.
123//!
124//! - The labels-at-tail list. This contains all MachLabels that have been bound
125//!   to (whose resolved offsets are equal to) the tail offset of the buffer.
126//!   No label in this list should be an alias.
127//!
128//! - The label_offsets array, containing the bound offset of a label or
129//!   UNKNOWN. No label can be bound at an offset greater than the current
130//!   buffer tail.
131//!
132//! - The label_aliases array, containing another label to which a label is
133//!   bound or UNKNOWN. A label's resolved offset is the resolved offset
134//!   of the label it is aliased to, if this is set.
135//!
136//! We argue below, at each method, how the invariants in these data structures
137//! are maintained (grep for "Post-invariant").
138//!
139//! Given these invariants, we argue why each optimization preserves execution
140//! semantics below (grep for "Preserves execution semantics").
141//!
142//! # Deadline-correctness for islands
143//!
144//! Every label-use (and indirectly every pending constant/trap, since
145//! each is referred to by a fixup) imposes a *deadline*: the maximum
146//! offset at which the use's target may be bound while still
147//! remaining in range. Each item that may be emitted into an island
148//! (a veneer, a pending constant, or a pending trap) also contributes
149//! a bounded number of bytes to a worst-case island size. The
150//! buffer's central invariant is:
151//!
152//! > `worst_case_end_of_island(0) <= soonest_deadline`
153//!
154//! Equivalently, "if we emitted an island right now, its end offset
155//! would land before the closest expiring deadline." Given this
156//! invariant, an island is always *feasible*: items can be laid out
157//! in any order and each one lands at an offset no later than the
158//! soonest deadline, which is no later than each individual item's
159//! deadline.
160//!
161//! To maintain the invariant, the buffer's user is expected to treat
162//! *one `MachInst` emission* as the atomic commit unit. After each
163//! instruction, the worst-case end-of-island and the soonest deadline
164//! can shift by no more than `worst_case_size() +
165//! worst_case_island_growth()` and one "smallest label-use range"
166//! worth of new deadline, respectively. The user (in VCode emission)
167//! consults [`MachBuffer::island_needed`] after each instruction and
168//! if one is needed, emits a jump-around branch followed by
169//! [`MachBuffer::emit_island`].
170//!
171//! # Avoiding Quadratic Behavior
172//!
173//! There are two cases where we've had to take some care to avoid
174//! quadratic worst-case behavior:
175//!
176//! - The "labels at this branch" list can grow unboundedly if the
177//!   code generator binds many labels at one location. If the count
178//!   gets too high (defined by the `LABEL_LIST_THRESHOLD` constant), we
179//!   simply abort an optimization early in a way that is always correct
180//!   but is conservative.
181//!
182//! - The fixup list can interact with island emission to create
183//!   "quadratic island behavior". In a little more detail, one can hit
184//!   this behavior by having some pending fixups (forward label
185//!   references) with long-range label-use kinds, and some others
186//!   with shorter-range references that nonetheless still are pending
187//!   long enough to trigger island generation. In such a case, we
188//!   process the fixup list, generate veneers to extend some forward
189//!   references' ranges, but leave the other (longer-range) ones
190//!   alone. The way this was implemented put them back on a list and
191//!   resulted in quadratic behavior.
192//!
193//!   To avoid this fixups are split into two lists: one "pending" list and one
194//!   final list. The pending list is kept around for handling fixups related to
195//!   branches so it can be edited/truncated. When an island is reached, which
196//!   starts processing fixups, all pending fixups are flushed into the final
197//!   list. The final list is a `BinaryHeap` which enables fixup processing to
198//!   only process those which are required during island emission, deferring
199//!   all longer-range fixups to later.
200
201use crate::binemit::{Addend, CodeOffset, Reloc};
202use crate::ir::function::FunctionParameters;
203use crate::ir::{
204    DebugTag, ExceptionTag, ExternalName, MaybeRelSourceLoc, RelSourceLoc, SourceLoc, TrapCode,
205};
206use crate::isa::unwind::UnwindInst;
207use crate::machinst::{
208    BlockIndex, MachInstLabelUse, TextSectionBuilder, VCodeConstant, VCodeConstants, VCodeInst,
209};
210use crate::trace;
211use crate::{MachInstEmitState, ir};
212use crate::{VCodeConstantData, timing};
213use alloc::boxed::Box;
214use alloc::collections::BinaryHeap;
215use alloc::string::String;
216use alloc::vec::Vec;
217use core::cmp::Ordering;
218use core::mem;
219use core::ops::Range;
220use core::ops::{Deref, DerefMut};
221use cranelift_control::ControlPlane;
222use cranelift_entity::{PrimaryMap, SecondaryMap, entity_impl};
223use smallvec::SmallVec;
224
225#[derive(Clone, Copy, Debug, PartialEq, Eq)]
226enum ForceVeneers {
227    Yes,
228    No,
229}
230
231/// A `MachLabel` or `CodeOffset`, bitpacked into a u32.
232///
233/// This type is used to represent a label reference in some
234/// MachBuffer metadata (specifically, relocations and
235/// exception-handler records). These start as labels before the
236/// `MachBuffer` is finalized; once `finish()` is called, they become
237/// code offsets.
238#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
239#[cfg_attr(
240    feature = "enable-serde",
241    derive(serde_derive::Serialize, serde_derive::Deserialize)
242)]
243pub struct LabelOrOffset(u32);
244
245impl LabelOrOffset {
246    const LABEL_BIT: u32 = 0x8000_0000;
247    const MASK: u32 = !Self::LABEL_BIT;
248
249    /// Create a `LabelOrOffset` that refers to a label.
250    pub fn label(label: MachLabel) -> Self {
251        debug_assert!(label.0 & Self::MASK == label.0);
252        LabelOrOffset(label.0 | Self::LABEL_BIT)
253    }
254
255    /// Create a `LabelOrOffset` that refers to a code offset.
256    pub fn offset(offset: CodeOffset) -> Self {
257        debug_assert!(offset & Self::MASK == offset);
258        LabelOrOffset(offset)
259    }
260
261    /// Is this a label?
262    pub fn is_label(&self) -> bool {
263        self.0 & Self::LABEL_BIT != 0
264    }
265
266    /// Is this a code offset?
267    pub fn is_offset(&self) -> bool {
268        self.0 & Self::LABEL_BIT == 0
269    }
270
271    /// Unwrap as a label.
272    ///
273    /// # Panics
274    ///
275    /// Panics if this is not a label.
276    pub fn as_label(&self) -> MachLabel {
277        assert!(self.is_label());
278        MachLabel(self.0 & Self::MASK)
279    }
280
281    /// Unwrap as a code offset.
282    ///
283    /// # Panics
284    ///
285    /// Panics if this is not a code offset.
286    pub fn as_offset(&self) -> CodeOffset {
287        assert!(self.is_offset());
288        self.0 & Self::MASK
289    }
290}
291
292impl From<MachLabel> for LabelOrOffset {
293    fn from(value: MachLabel) -> Self {
294        LabelOrOffset::label(value)
295    }
296}
297
298impl core::fmt::Display for LabelOrOffset {
299    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
300        if self.is_offset() {
301            write!(fmt, "0x{:x}", self.as_offset())
302        } else {
303            write!(fmt, "label{}", self.as_label().0)
304        }
305    }
306}
307
308/// A buffer of output to be produced, fixed up, and then emitted to a CodeSink
309/// in bulk.
310///
311/// This struct uses `SmallVec`s to support small-ish function bodies without
312/// any heap allocation. As such, it will be several kilobytes large. This is
313/// likely fine as long as it is stack-allocated for function emission then
314/// thrown away; but beware if many buffer objects are retained persistently.
315pub struct MachBuffer<I: VCodeInst> {
316    // --- machine-code data and metadata:
317    //
318    /// Data shared between the unfinalized and finalized MachBuffers.
319    inner: Box<MachBufferInner>,
320
321    // --- emission-pass state:
322    //
323    /// The current source location in progress (after `start_srcloc()` and
324    /// before `end_srcloc()`).  This is a (start_offset, src_loc) tuple.
325    cur_srcloc: Option<(CodeOffset, RelSourceLoc)>,
326    /// Known label offsets; `UNKNOWN_LABEL_OFFSET` if unknown.
327    label_offsets: SmallVec<[CodeOffset; 16]>,
328    /// Label aliases: when one label points to an unconditional jump, and that
329    /// jump points to another label, we can redirect references to the first
330    /// label immediately to the second.
331    ///
332    /// Invariant: we don't have label-alias cycles. We ensure this by,
333    /// before setting label A to alias label B, resolving B's alias
334    /// target (iteratively until a non-aliased label); if B is already
335    /// aliased to A, then we cannot alias A back to B.
336    label_aliases: SmallVec<[MachLabel; 16]>,
337    /// Constants that must be emitted at some point.
338    pending_constants: SmallVec<[VCodeConstant; 16]>,
339    /// Byte size of all constants in `pending_constants`.
340    pending_constants_size: CodeOffset,
341    /// Traps that must be emitted at some point.
342    pending_traps: SmallVec<[MachLabelTrap; 16]>,
343    /// Fixups that haven't yet been flushed into `fixup_records` below and may
344    /// be related to branches that are chomped. These all get added to
345    /// `fixup_records` during island emission.
346    pending_fixup_records: SmallVec<[MachLabelFixup<I>; 16]>,
347    /// The nearest upcoming deadline for entries in `pending_fixup_records`.
348    pending_fixup_deadline: CodeOffset,
349    /// Fixups that must be performed after all code is emitted.
350    fixup_records: BinaryHeap<MachLabelFixup<I>>,
351    /// Latest branches, to facilitate in-place editing for better fallthrough
352    /// behavior and empty-block removal.
353    latest_branches: SmallVec<[MachBranch; 4]>,
354    /// All labels at the current offset (emission tail). This is lazily
355    /// cleared: it is actually accurate as long as the current offset is
356    /// `labels_at_tail_off`, but if `cur_offset()` has grown larger, it should
357    /// be considered as empty.
358    ///
359    /// For correctness, this *must* be complete (i.e., the vector must contain
360    /// all labels whose offsets are resolved to the current tail), because we
361    /// rely on it to update labels when we truncate branches.
362    labels_at_tail: SmallVec<[MachLabel; 4]>,
363    /// The last offset at which `labels_at_tail` is valid. It is conceptually
364    /// always describing the tail of the buffer, but we do not clear
365    /// `labels_at_tail` eagerly when the tail grows, rather we lazily clear it
366    /// when the offset has grown past this (`labels_at_tail_off`) point.
367    /// Always <= `cur_offset()`.
368    labels_at_tail_off: CodeOffset,
369    /// Metadata about all constants that this function has access to.
370    ///
371    /// This records the size/alignment of all constants (not the actual data)
372    /// along with the last available label generated for the constant. This map
373    /// is consulted when constants are referred to and the label assigned to a
374    /// constant may change over time as well.
375    constants: PrimaryMap<VCodeConstant, MachBufferConstant>,
376    /// All recorded usages of constants as pairs of the constant and where the
377    /// constant needs to be placed within `self.data`. Note that the same
378    /// constant may appear in this array multiple times if it was emitted
379    /// multiple times.
380    used_constants: SmallVec<[(VCodeConstant, CodeOffset); 4]>,
381    /// Indicates when a patchable region is currently open, to guard that it's
382    /// not possible to nest patchable regions.
383    open_patchable: bool,
384}
385
386/// Bulk data that is common between `MachBuffer` and
387/// `MachBufferFinalized`.
388///
389/// The goal is to indirect the large allocations (`SmallVec`s) so
390/// that we don't move a lot of memory during compilation.
391///
392/// The two named types are essentially a builder/final object pair.
393/// The inner state is the same (except that the builder has further
394/// transient state that is later dropped).
395///
396/// However, many of the fields are either moved over wholesale or
397/// patched then moved over (data). We put these fields in
398/// `MachBufferInner`, hold that shared data in a box so that
399/// finalization can just move a pointer, and then impl `Deref` on the
400/// two `MachBuffer` variants so accesses to these fields are
401/// transparent.
402#[derive(PartialEq, Debug, Clone)]
403#[cfg_attr(
404    feature = "enable-serde",
405    derive(serde_derive::Serialize, serde_derive::Deserialize)
406)]
407pub struct MachBufferInner {
408    /// The buffer contents, as raw bytes.
409    pub(crate) data: SmallVec<[u8; 1024]>,
410    /// Any trap records referring to this code.
411    pub(crate) traps: SmallVec<[MachTrap; 16]>,
412    /// Any relocations referring to this code. Note that only *external*
413    /// relocations are tracked here; references to labels within the buffer are
414    /// resolved before emission.
415    pub(crate) relocs: SmallVec<[MachReloc; 16]>,
416    /// Any exception-handler records referred to at call sites.
417    pub(crate) exception_handlers: SmallVec<[MachExceptionHandler; 16]>,
418    /// Any call site records referring to this code.
419    pub(crate) call_sites: SmallVec<[MachCallSite; 16]>,
420    /// Any patchable call site locations.
421    pub(crate) patchable_call_sites: SmallVec<[MachPatchableCallSite; 16]>,
422    /// Any debug tags referring to this code.
423    pub(crate) debug_tags: Vec<MachDebugTags>,
424    /// Pool of debug tags referenced by `MachDebugTags` entries.
425    pub(crate) debug_tag_pool: Vec<DebugTag>,
426    /// Any user stack maps for this code.
427    ///
428    /// Each entry is an `(offset, span, stack_map)` triple. Entries are sorted
429    /// by code offset, and each stack map covers `span` bytes on the stack.
430    pub(crate) user_stack_maps: SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]>,
431    /// Any unwind info at a given location.
432    pub(crate) unwind_info: SmallVec<[(CodeOffset, UnwindInst); 8]>,
433    /// Stack frame layout metadata. If provided for a MachBuffer
434    /// containing a function body, this allows interpretation of
435    /// runtime state given a view of an active stack frame.
436    pub(crate) frame_layout: Option<MachBufferFrameLayout>,
437    /// Any source location mappings referring to this code.
438    pub(crate) srclocs: SmallVec<[MachSrcLoc; 64]>,
439    /// The required alignment of this buffer.
440    pub min_alignment: u32,
441}
442
443impl<I: VCodeInst> Deref for MachBuffer<I> {
444    type Target = MachBufferInner;
445    fn deref(&self) -> &Self::Target {
446        &*self.inner
447    }
448}
449impl<I: VCodeInst> DerefMut for MachBuffer<I> {
450    fn deref_mut(&mut self) -> &mut Self::Target {
451        &mut *self.inner
452    }
453}
454impl Deref for MachBufferFinalized {
455    type Target = MachBufferInner;
456    fn deref(&self) -> &Self::Target {
457        &*self.inner
458    }
459}
460impl DerefMut for MachBufferFinalized {
461    fn deref_mut(&mut self) -> &mut Self::Target {
462        &mut *self.inner
463    }
464}
465
466impl MachBufferFinalized {
467    /// Get a finalized machine buffer by applying the function's base source location.
468    pub fn apply_base_srcloc(&mut self, base_srcloc: SourceLoc) {
469        for loc in &mut self.inner.srclocs {
470            loc.apply_base_srcloc(base_srcloc);
471        }
472    }
473}
474
475/// A `MachBuffer` once emission is completed: holds generated code and records,
476/// without fixups. This allows the type to be independent of the backend.
477#[derive(PartialEq, Debug, Clone)]
478#[cfg_attr(
479    feature = "enable-serde",
480    derive(serde_derive::Serialize, serde_derive::Deserialize)
481)]
482pub struct MachBufferFinalized {
483    /// The raw data and finalization-invariant metadata attached to it.
484    pub(crate) inner: Box<MachBufferInner>,
485    /// The means by which to NOP out patchable call sites.
486    ///
487    /// This allows a consumer of a `MachBufferFinalized` to disable
488    /// patchable call sites (which are enabled by default) without
489    /// specific knowledge of the target ISA.
490    ///
491    /// Each entry is one form of nop, and these are required to be
492    /// sorted in ascending-size order.
493    pub nop_units: Vec<Vec<u8>>,
494}
495
496const UNKNOWN_LABEL_OFFSET: CodeOffset = 0xffff_ffff;
497const UNKNOWN_LABEL: MachLabel = MachLabel(0xffff_ffff);
498
499/// Threshold on max length of `labels_at_this_branch` list to avoid
500/// unbounded quadratic behavior (see comment below at use-site).
501const LABEL_LIST_THRESHOLD: usize = 100;
502
503/// A label refers to some offset in a `MachBuffer`. It may not be resolved at
504/// the point at which it is used by emitted code; the buffer records "fixups"
505/// for references to the label, and will come back and patch the code
506/// appropriately when the label's location is eventually known.
507#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
508pub struct MachLabel(u32);
509entity_impl!(MachLabel);
510
511impl MachLabel {
512    /// Get a label for a block. (The first N MachLabels are always reserved for
513    /// the N blocks in the vcode.)
514    pub fn from_block(bindex: BlockIndex) -> MachLabel {
515        MachLabel(bindex.index() as u32)
516    }
517
518    /// Creates a string representing this label, for convenience.
519    pub fn to_string(&self) -> String {
520        format!("label{}", self.0)
521    }
522}
523
524impl Default for MachLabel {
525    fn default() -> Self {
526        UNKNOWN_LABEL
527    }
528}
529
530/// Represents the beginning of an editable region in the [`MachBuffer`], while code emission is
531/// still occurring. An [`OpenPatchRegion`] is closed by [`MachBuffer::end_patchable`], consuming
532/// the [`OpenPatchRegion`] token in the process.
533pub struct OpenPatchRegion(usize);
534
535/// A region in the [`MachBuffer`] code buffer that can be edited prior to finalization. An example
536/// of where you might want to use this is for patching instructions that mention constants that
537/// won't be known until later: [`MachBuffer::start_patchable`] can be used to begin the patchable
538/// region, instructions can be emitted with placeholder constants, and the [`PatchRegion`] token
539/// can be produced by [`MachBuffer::end_patchable`]. Once the values of those constants are known,
540/// the [`PatchRegion::patch`] function can be used to get a mutable buffer to the instruction
541/// bytes, and the constants uses can be updated directly.
542pub struct PatchRegion {
543    range: Range<usize>,
544}
545
546impl PatchRegion {
547    /// Consume the patch region to yield a mutable slice of the [`MachBuffer`] data buffer.
548    pub fn patch<I: VCodeInst>(self, buffer: &mut MachBuffer<I>) -> &mut [u8] {
549        &mut buffer.data[self.range]
550    }
551}
552
553impl<I: VCodeInst> MachBuffer<I> {
554    /// Create a new section, known to start at `start_offset` and with a size limited to
555    /// `length_limit`.
556    pub fn new() -> MachBuffer<I> {
557        let inner = Box::new(MachBufferInner {
558            data: SmallVec::new(),
559            traps: SmallVec::new(),
560            relocs: SmallVec::new(),
561            exception_handlers: SmallVec::new(),
562            call_sites: SmallVec::new(),
563            patchable_call_sites: SmallVec::new(),
564            debug_tags: vec![],
565            debug_tag_pool: vec![],
566            user_stack_maps: SmallVec::new(),
567            unwind_info: SmallVec::new(),
568            frame_layout: None,
569            srclocs: SmallVec::new(),
570            min_alignment: I::function_alignment().minimum,
571        });
572        MachBuffer {
573            inner,
574            cur_srcloc: None,
575            label_offsets: SmallVec::new(),
576            label_aliases: SmallVec::new(),
577            pending_constants: SmallVec::new(),
578            pending_constants_size: 0,
579            pending_traps: SmallVec::new(),
580            pending_fixup_records: SmallVec::new(),
581            pending_fixup_deadline: u32::MAX,
582            fixup_records: Default::default(),
583            latest_branches: SmallVec::new(),
584            labels_at_tail: SmallVec::new(),
585            labels_at_tail_off: 0,
586            constants: Default::default(),
587            used_constants: Default::default(),
588            open_patchable: false,
589        }
590    }
591
592    /// Current offset from start of buffer.
593    pub fn cur_offset(&self) -> CodeOffset {
594        self.data.len() as CodeOffset
595    }
596
597    /// Add a byte.
598    pub fn put1(&mut self, value: u8) {
599        self.data.push(value);
600
601        // Post-invariant: conceptual-labels_at_tail contains a complete and
602        // precise list of labels bound at `cur_offset()`. We have advanced
603        // `cur_offset()`, hence if it had been equal to `labels_at_tail_off`
604        // before, it is not anymore (and it cannot become equal, because
605        // `labels_at_tail_off` is always <= `cur_offset()`). Thus the list is
606        // conceptually empty (even though it is only lazily cleared). No labels
607        // can be bound at this new offset (by invariant on `label_offsets`).
608        // Hence the invariant holds.
609    }
610
611    /// Add 2 bytes.
612    pub fn put2(&mut self, value: u16) {
613        let bytes = value.to_le_bytes();
614        self.data.extend_from_slice(&bytes[..]);
615
616        // Post-invariant: as for `put1()`.
617    }
618
619    /// Add 4 bytes.
620    pub fn put4(&mut self, value: u32) {
621        let bytes = value.to_le_bytes();
622        self.data.extend_from_slice(&bytes[..]);
623
624        // Post-invariant: as for `put1()`.
625    }
626
627    /// Add 8 bytes.
628    pub fn put8(&mut self, value: u64) {
629        let bytes = value.to_le_bytes();
630        self.data.extend_from_slice(&bytes[..]);
631
632        // Post-invariant: as for `put1()`.
633    }
634
635    /// Add a slice of bytes.
636    pub fn put_data(&mut self, data: &[u8]) {
637        self.data.extend_from_slice(data);
638
639        // Post-invariant: as for `put1()`.
640    }
641
642    /// Reserve appended space and return a mutable slice referring to it.
643    pub fn get_appended_space(&mut self, len: usize) -> &mut [u8] {
644        let off = self.data.len();
645        let new_len = self.data.len() + len;
646        self.data.resize(new_len, 0);
647        &mut self.data[off..]
648
649        // Post-invariant: as for `put1()`.
650    }
651
652    /// Align up to the given alignment.
653    pub fn align_to(&mut self, align_to: CodeOffset) {
654        trace!("MachBuffer: align to {}", align_to);
655        assert!(
656            align_to.is_power_of_two(),
657            "{align_to} is not a power of two"
658        );
659        while self.cur_offset() & (align_to - 1) != 0 {
660            self.put1(0);
661        }
662
663        // Post-invariant: as for `put1()`.
664    }
665
666    /// Begin a region of patchable code. There is one requirement for the
667    /// code that is emitted: It must not introduce any instructions that
668    /// could be chomped (branches are an example of this). In other words,
669    /// you must not call [`MachBuffer::add_cond_branch`] or
670    /// [`MachBuffer::add_uncond_branch`] between calls to this method and
671    /// [`MachBuffer::end_patchable`].
672    pub fn start_patchable(&mut self) -> OpenPatchRegion {
673        assert!(!self.open_patchable, "Patchable regions may not be nested");
674        self.open_patchable = true;
675        OpenPatchRegion(usize::try_from(self.cur_offset()).unwrap())
676    }
677
678    /// End a region of patchable code, yielding a [`PatchRegion`] value that
679    /// can be consumed later to produce a one-off mutable slice to the
680    /// associated region of the data buffer.
681    pub fn end_patchable(&mut self, open: OpenPatchRegion) -> PatchRegion {
682        // No need to assert the state of `open_patchable` here, as we take
683        // ownership of the only `OpenPatchable` value.
684        self.open_patchable = false;
685        let end = usize::try_from(self.cur_offset()).unwrap();
686        PatchRegion { range: open.0..end }
687    }
688
689    /// Allocate a `Label` to refer to some offset. May not be bound to a fixed
690    /// offset yet.
691    pub fn get_label(&mut self) -> MachLabel {
692        let l = self.label_offsets.len() as u32;
693        self.label_offsets.push(UNKNOWN_LABEL_OFFSET);
694        self.label_aliases.push(UNKNOWN_LABEL);
695        trace!("MachBuffer: new label -> {:?}", MachLabel(l));
696        MachLabel(l)
697
698        // Post-invariant: the only mutation is to add a new label; it has no
699        // bound offset yet, so it trivially satisfies all invariants.
700    }
701
702    /// Reserve the first N MachLabels for blocks.
703    pub fn reserve_labels_for_blocks(&mut self, blocks: usize) {
704        trace!("MachBuffer: first {} labels are for blocks", blocks);
705        debug_assert!(self.label_offsets.is_empty());
706        self.label_offsets.resize(blocks, UNKNOWN_LABEL_OFFSET);
707        self.label_aliases.resize(blocks, UNKNOWN_LABEL);
708
709        // Post-invariant: as for `get_label()`.
710    }
711
712    /// Registers metadata in this `MachBuffer` about the `constants` provided.
713    ///
714    /// This will record the size/alignment of all constants which will prepare
715    /// them for emission later on.
716    pub fn register_constants(&mut self, constants: &VCodeConstants) {
717        for (c, val) in constants.iter() {
718            self.register_constant(&c, val);
719        }
720    }
721
722    /// Similar to [`MachBuffer::register_constants`] but registers a
723    /// single constant metadata. This function is useful in
724    /// situations where not all constants are known at the time of
725    /// emission.
726    pub fn register_constant(&mut self, constant: &VCodeConstant, data: &VCodeConstantData) {
727        let c2 = self.constants.push(MachBufferConstant {
728            upcoming_label: None,
729            align: data.alignment(),
730            size: data.as_slice().len(),
731        });
732        assert_eq!(*constant, c2);
733    }
734
735    /// Completes constant emission by iterating over `self.used_constants` and
736    /// filling in the "holes" with the constant values provided by `constants`.
737    ///
738    /// Returns the alignment required for this entire buffer. Alignment starts
739    /// at the ISA's minimum function alignment and can be increased due to
740    /// constant requirements.
741    fn finish_constants(&mut self, constants: &VCodeConstants) {
742        for (constant, offset) in mem::take(&mut self.used_constants) {
743            let constant = constants.get(constant);
744            let data = constant.as_slice();
745            self.data[offset as usize..][..data.len()].copy_from_slice(data);
746            self.min_alignment = constant.alignment().max(self.min_alignment);
747        }
748    }
749
750    /// Returns a label that can be used to refer to the `constant` provided.
751    ///
752    /// This will automatically defer a new constant to be emitted for
753    /// `constant` if it has not been previously emitted. Note that this
754    /// function may return a different label for the same constant at
755    /// different points in time. The label is valid to use only from the
756    /// current location; the MachBuffer takes care to emit the same constant
757    /// multiple times if needed so the constant is always in range.
758    pub fn get_label_for_constant(&mut self, constant: VCodeConstant) -> MachLabel {
759        let MachBufferConstant {
760            align,
761            size,
762            upcoming_label,
763        } = self.constants[constant];
764        if let Some(label) = upcoming_label {
765            return label;
766        }
767
768        let label = self.get_label();
769        trace!(
770            "defer constant: eventually emit {size} bytes aligned \
771             to {align} at label {label:?}",
772        );
773        self.pending_constants.push(constant);
774        self.pending_constants_size += size as u32;
775        self.constants[constant].upcoming_label = Some(label);
776        label
777    }
778
779    /// Bind a label to the current offset. A label can only be bound once.
780    pub fn bind_label(&mut self, label: MachLabel, ctrl_plane: &mut ControlPlane) {
781        trace!(
782            "MachBuffer: bind label {:?} at offset {}",
783            label,
784            self.cur_offset()
785        );
786        debug_assert_eq!(self.label_offsets[label.0 as usize], UNKNOWN_LABEL_OFFSET);
787        debug_assert_eq!(self.label_aliases[label.0 as usize], UNKNOWN_LABEL);
788        let offset = self.cur_offset();
789        self.label_offsets[label.0 as usize] = offset;
790        self.lazily_clear_labels_at_tail();
791        self.labels_at_tail.push(label);
792
793        // Invariants hold: bound offset of label is <= cur_offset (in fact it
794        // is equal). If the `labels_at_tail` list was complete and precise
795        // before, it is still, because we have bound this label to the current
796        // offset and added it to the list (which contains all labels at the
797        // current offset).
798
799        self.optimize_branches(ctrl_plane);
800
801        // Post-invariant: by `optimize_branches()` (see argument there).
802    }
803
804    /// Lazily clear `labels_at_tail` if the tail offset has moved beyond the
805    /// offset that it applies to.
806    fn lazily_clear_labels_at_tail(&mut self) {
807        let offset = self.cur_offset();
808        if offset > self.labels_at_tail_off {
809            self.labels_at_tail_off = offset;
810            self.labels_at_tail.clear();
811        }
812
813        // Post-invariant: either labels_at_tail_off was at cur_offset, and
814        // state is untouched, or was less than cur_offset, in which case the
815        // labels_at_tail list was conceptually empty, and is now actually
816        // empty.
817    }
818
819    /// Resolve a label to an offset, if known. May return `UNKNOWN_LABEL_OFFSET`.
820    pub(crate) fn resolve_label_offset(&self, mut label: MachLabel) -> CodeOffset {
821        let mut iters = 0;
822        while self.label_aliases[label.0 as usize] != UNKNOWN_LABEL {
823            label = self.label_aliases[label.0 as usize];
824            // To protect against an infinite loop (despite our assurances to
825            // ourselves that the invariants make this impossible), assert out
826            // after 1M iterations. The number of basic blocks is limited
827            // in most contexts anyway so this should be impossible to hit with
828            // a legitimate input.
829            iters += 1;
830            assert!(iters < 1_000_000, "Unexpected cycle in label aliases");
831        }
832        self.label_offsets[label.0 as usize]
833
834        // Post-invariant: no mutations.
835    }
836
837    /// Emit a reference to the given label with the given reference type (i.e.,
838    /// branch-instruction format) at the current offset.  This is like a
839    /// relocation, but handled internally.
840    ///
841    /// This can be called before the branch is actually emitted; fixups will
842    /// not happen until an island is emitted or the buffer is finished.
843    pub fn use_label_at_offset(&mut self, offset: CodeOffset, label: MachLabel, kind: I::LabelUse) {
844        trace!(
845            "MachBuffer: use_label_at_offset: offset {} label {:?} kind {:?}",
846            offset, label, kind
847        );
848
849        // Add the fixup, and update the worst-case island size based on a
850        // veneer for this label use.
851        let fixup = MachLabelFixup {
852            label,
853            offset,
854            kind,
855        };
856        self.pending_fixup_deadline = self
857            .pending_fixup_deadline
858            // Subtract one alignment here to the deadline to account for
859            // extra space taken by aligning an island.
860            .min(fixup.deadline() - I::LabelUse::ALIGN);
861        trace!("pending_fixup_deadline = {}", self.pending_fixup_deadline);
862        self.pending_fixup_records.push(fixup);
863
864        // Post-invariant: no mutations to branches/labels data structures.
865    }
866
867    /// Inform the buffer of an unconditional branch at the given offset,
868    /// targeting the given label. May be used to optimize branches.
869    /// The last added label-use must correspond to this branch.
870    /// This must be called when the current offset is equal to `start`; i.e.,
871    /// before actually emitting the branch. This implies that for a branch that
872    /// uses a label and is eligible for optimizations by the MachBuffer, the
873    /// proper sequence is:
874    ///
875    /// - Call `use_label_at_offset()` to emit the fixup record.
876    /// - Call `add_uncond_branch()` to make note of the branch.
877    /// - Emit the bytes for the branch's machine code.
878    ///
879    /// Additional requirement: no labels may be bound between `start` and `end`
880    /// (exclusive on both ends).
881    pub fn add_uncond_branch(&mut self, start: CodeOffset, end: CodeOffset, target: MachLabel) {
882        debug_assert!(
883            !self.open_patchable,
884            "Branch instruction inserted within a patchable region"
885        );
886        assert!(self.cur_offset() == start);
887        debug_assert!(end > start);
888        assert!(!self.pending_fixup_records.is_empty());
889        let fixup = self.pending_fixup_records.len() - 1;
890        self.lazily_clear_labels_at_tail();
891        self.latest_branches.push(MachBranch {
892            start,
893            end,
894            target,
895            fixup,
896            inverted: None,
897            labels_at_this_branch: self.labels_at_tail.clone(),
898        });
899
900        // Post-invariant: we asserted branch start is current tail; the list of
901        // labels at branch is cloned from list of labels at current tail.
902    }
903
904    /// Inform the buffer of a conditional branch at the given offset,
905    /// targeting the given label. May be used to optimize branches.
906    /// The last added label-use must correspond to this branch.
907    ///
908    /// Additional requirement: no labels may be bound between `start` and `end`
909    /// (exclusive on both ends).
910    pub fn add_cond_branch(
911        &mut self,
912        start: CodeOffset,
913        end: CodeOffset,
914        target: MachLabel,
915        inverted: &[u8],
916    ) {
917        debug_assert!(
918            !self.open_patchable,
919            "Branch instruction inserted within a patchable region"
920        );
921        assert!(self.cur_offset() == start);
922        debug_assert!(end > start);
923        assert!(!self.pending_fixup_records.is_empty());
924        debug_assert!(
925            inverted.len() == (end - start) as usize,
926            "branch length = {}, but inverted length = {}",
927            end - start,
928            inverted.len()
929        );
930        let fixup = self.pending_fixup_records.len() - 1;
931        let inverted = Some(SmallVec::from(inverted));
932        self.lazily_clear_labels_at_tail();
933        self.latest_branches.push(MachBranch {
934            start,
935            end,
936            target,
937            fixup,
938            inverted,
939            labels_at_this_branch: self.labels_at_tail.clone(),
940        });
941
942        // Post-invariant: we asserted branch start is current tail; labels at
943        // branch list is cloned from list of labels at current tail.
944    }
945
946    fn truncate_last_branch(&mut self) {
947        debug_assert!(
948            !self.open_patchable,
949            "Branch instruction truncated within a patchable region"
950        );
951
952        self.lazily_clear_labels_at_tail();
953        // Invariants hold at this point.
954
955        let b = self.latest_branches.pop().unwrap();
956        assert!(b.end == self.cur_offset());
957
958        // State:
959        //    [PRE CODE]
960        //  Offset b.start, b.labels_at_this_branch:
961        //    [BRANCH CODE]
962        //  cur_off, self.labels_at_tail -->
963        //    (end of buffer)
964        self.data.truncate(b.start as usize);
965        self.pending_fixup_records.truncate(b.fixup);
966
967        // Trim srclocs and debug tags now past the end of the buffer.
968        while let Some(last_srcloc) = self.srclocs.last_mut() {
969            if last_srcloc.end <= b.start {
970                break;
971            }
972            if last_srcloc.start < b.start {
973                last_srcloc.end = b.start;
974                break;
975            }
976            self.srclocs.pop();
977        }
978        while let Some(last_debug_tag) = self.debug_tags.last() {
979            if last_debug_tag.offset <= b.start {
980                break;
981            }
982            self.debug_tags.pop();
983        }
984
985        // State:
986        //    [PRE CODE]
987        //  cur_off, Offset b.start, b.labels_at_this_branch:
988        //    (end of buffer)
989        //
990        //  self.labels_at_tail -->  (past end of buffer)
991        let cur_off = self.cur_offset();
992        self.labels_at_tail_off = cur_off;
993        // State:
994        //    [PRE CODE]
995        //  cur_off, Offset b.start, b.labels_at_this_branch,
996        //  self.labels_at_tail:
997        //    (end of buffer)
998        //
999        // resolve_label_offset(l) for l in labels_at_tail:
1000        //    (past end of buffer)
1001
1002        trace!(
1003            "truncate_last_branch: truncated {:?}; off now {}",
1004            b, cur_off
1005        );
1006
1007        // Fix up resolved label offsets for labels at tail.
1008        for &l in &self.labels_at_tail {
1009            self.label_offsets[l.0 as usize] = cur_off;
1010        }
1011        // Old labels_at_this_branch are now at cur_off.
1012        self.labels_at_tail.extend(b.labels_at_this_branch);
1013
1014        // Post-invariant: this operation is defined to truncate the buffer,
1015        // which moves cur_off backward, and to move labels at the end of the
1016        // buffer back to the start-of-branch offset.
1017        //
1018        // latest_branches satisfies all invariants:
1019        // - it has no branches past the end of the buffer (branches are in
1020        //   order, we removed the last one, and we truncated the buffer to just
1021        //   before the start of that branch)
1022        // - no labels were moved to lower offsets than the (new) cur_off, so
1023        //   the labels_at_this_branch list for any other branch need not change.
1024        //
1025        // labels_at_tail satisfies all invariants:
1026        // - all labels that were at the tail after the truncated branch are
1027        //   moved backward to just before the branch, which becomes the new tail;
1028        //   thus every element in the list should remain (ensured by `.extend()`
1029        //   above).
1030        // - all labels that refer to the new tail, which is the start-offset of
1031        //   the truncated branch, must be present. The `labels_at_this_branch`
1032        //   list in the truncated branch's record is a complete and precise list
1033        //   of exactly these labels; we append these to labels_at_tail.
1034        // - labels_at_tail_off is at cur_off after truncation occurs, so the
1035        //   list is valid (not to be lazily cleared).
1036        //
1037        // The stated operation was performed:
1038        // - For each label at the end of the buffer prior to this method, it
1039        //   now resolves to the new (truncated) end of the buffer: it must have
1040        //   been in `labels_at_tail` (this list is precise and complete, and
1041        //   the tail was at the end of the truncated branch on entry), and we
1042        //   iterate over this list and set `label_offsets` to the new tail.
1043        //   None of these labels could have been an alias (by invariant), so
1044        //   `label_offsets` is authoritative for each.
1045        // - No other labels will be past the end of the buffer, because of the
1046        //   requirement that no labels be bound to the middle of branch ranges
1047        //   (see comments to `add_{cond,uncond}_branch()`).
1048        // - The buffer is truncated to just before the last branch, and the
1049        //   fixup record referring to that last branch is removed.
1050    }
1051
1052    /// Performs various optimizations on branches pointing at the current label.
1053    pub fn optimize_branches(&mut self, ctrl_plane: &mut ControlPlane) {
1054        if ctrl_plane.get_decision() {
1055            return;
1056        }
1057
1058        self.lazily_clear_labels_at_tail();
1059        // Invariants valid at this point.
1060
1061        trace!(
1062            "enter optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
1063            self.latest_branches, self.labels_at_tail, self.pending_fixup_records
1064        );
1065
1066        // We continue to munch on branches at the tail of the buffer until no
1067        // more rules apply. Note that the loop only continues if a branch is
1068        // actually truncated (or if labels are redirected away from a branch),
1069        // so this always makes progress.
1070        while let Some(b) = self.latest_branches.last() {
1071            let cur_off = self.cur_offset();
1072            trace!("optimize_branches: last branch {:?} at off {}", b, cur_off);
1073            // If there has been any code emission since the end of the last branch or
1074            // label definition, then there's nothing we can edit (because we
1075            // don't move code once placed, only back up and overwrite), so
1076            // clear the records and finish.
1077            if b.end < cur_off {
1078                break;
1079            }
1080
1081            // If the "labels at this branch" list on this branch is
1082            // longer than a threshold, don't do any simplification,
1083            // and let the branch remain to separate those labels from
1084            // the current tail. This avoids quadratic behavior (see
1085            // #3468): otherwise, if a long string of "goto next;
1086            // next:" patterns are emitted, all of the labels will
1087            // coalesce into a long list of aliases for the current
1088            // buffer tail. We must track all aliases of the current
1089            // tail for correctness, but we are also allowed to skip
1090            // optimization (removal) of any branch, so we take the
1091            // escape hatch here and let it stand. In effect this
1092            // "spreads" the many thousands of labels in the
1093            // pathological case among an actual (harmless but
1094            // suboptimal) instruction once per N labels.
1095            if b.labels_at_this_branch.len() > LABEL_LIST_THRESHOLD {
1096                break;
1097            }
1098
1099            // Invariant: we are looking at a branch that ends at the tail of
1100            // the buffer.
1101
1102            // For any branch, conditional or unconditional:
1103            // - If the target is a label at the current offset, then remove
1104            //   the conditional branch, and reset all labels that targeted
1105            //   the current offset (end of branch) to the truncated
1106            //   end-of-code.
1107            //
1108            // Preserves execution semantics: a branch to its own fallthrough
1109            // address is equivalent to a no-op; in both cases, nextPC is the
1110            // fallthrough.
1111            if self.resolve_label_offset(b.target) == cur_off {
1112                trace!("branch with target == cur off; truncating");
1113                self.truncate_last_branch();
1114                continue;
1115            }
1116
1117            // If latest is an unconditional branch:
1118            //
1119            // - If the branch's target is not its own start address, then for
1120            //   each label at the start of branch, make the label an alias of the
1121            //   branch target, and remove the label from the "labels at this
1122            //   branch" list.
1123            //
1124            //   - Preserves execution semantics: an unconditional branch's
1125            //     only effect is to set PC to a new PC; this change simply
1126            //     collapses one step in the step-semantics.
1127            //
1128            //   - Post-invariant: the labels that were bound to the start of
1129            //     this branch become aliases, so they must not be present in any
1130            //     labels-at-this-branch list or the labels-at-tail list. The
1131            //     labels are removed form the latest-branch record's
1132            //     labels-at-this-branch list, and are never placed in the
1133            //     labels-at-tail list. Furthermore, it is correct that they are
1134            //     not in either list, because they are now aliases, and labels
1135            //     that are aliases remain aliases forever.
1136            //
1137            // - If there is a prior unconditional branch that ends just before
1138            //   this one begins, and this branch has no labels bound to its
1139            //   start, then we can truncate this branch, because it is entirely
1140            //   unreachable (we have redirected all labels that make it
1141            //   reachable otherwise). Do so and continue around the loop.
1142            //
1143            //   - Preserves execution semantics: the branch is unreachable,
1144            //     because execution can only flow into an instruction from the
1145            //     prior instruction's fallthrough or from a branch bound to that
1146            //     instruction's start offset. Unconditional branches have no
1147            //     fallthrough, so if the prior instruction is an unconditional
1148            //     branch, no fallthrough entry can happen. The
1149            //     labels-at-this-branch list is complete (by invariant), so if it
1150            //     is empty, then the instruction is entirely unreachable. Thus,
1151            //     it can be removed.
1152            //
1153            //   - Post-invariant: ensured by truncate_last_branch().
1154            //
1155            // - If there is a prior conditional branch whose target label
1156            //   resolves to the current offset (branches around the
1157            //   unconditional branch), then remove the unconditional branch,
1158            //   and make the target of the unconditional the target of the
1159            //   conditional instead.
1160            //
1161            //   - Preserves execution semantics: previously we had:
1162            //
1163            //         L1:
1164            //            cond_br L2
1165            //            br L3
1166            //         L2:
1167            //            (end of buffer)
1168            //
1169            //     by removing the last branch, we have:
1170            //
1171            //         L1:
1172            //            cond_br L2
1173            //         L2:
1174            //            (end of buffer)
1175            //
1176            //     we then fix up the records for the conditional branch to
1177            //     have:
1178            //
1179            //         L1:
1180            //           cond_br.inverted L3
1181            //         L2:
1182            //
1183            //     In the original code, control flow reaches L2 when the
1184            //     conditional branch's predicate is true, and L3 otherwise. In
1185            //     the optimized code, the same is true.
1186            //
1187            //   - Post-invariant: all edits to latest_branches and
1188            //     labels_at_tail are performed by `truncate_last_branch()`,
1189            //     which maintains the invariants at each step.
1190
1191            if b.is_uncond() {
1192                // Set any label equal to current branch's start as an alias of
1193                // the branch's target, if the target is not the branch itself
1194                // (i.e., an infinite loop).
1195                //
1196                // We cannot perform this aliasing if the target of this branch
1197                // ultimately aliases back here; if so, we need to keep this
1198                // branch, so break out of this loop entirely (and clear the
1199                // latest-branches list below).
1200                //
1201                // Note that this check is what prevents cycles from forming in
1202                // `self.label_aliases`. To see why, consider an arbitrary start
1203                // state:
1204                //
1205                // label_aliases[L1] = L2, label_aliases[L2] = L3, ..., up to
1206                // Ln, which is not aliased.
1207                //
1208                // We would create a cycle if we assigned label_aliases[Ln]
1209                // = L1.  Note that the below assignment is the only write
1210                // to label_aliases.
1211                //
1212                // By our other invariants, we have that Ln (`l` below)
1213                // resolves to the offset `b.start`, because it is in the
1214                // set `b.labels_at_this_branch`.
1215                //
1216                // If L1 were already aliased, through some arbitrarily deep
1217                // chain, to Ln, then it must also resolve to this offset
1218                // `b.start`.
1219                //
1220                // By checking the resolution of `L1` against this offset,
1221                // and aborting this branch-simplification if they are
1222                // equal, we prevent the below assignment from ever creating
1223                // a cycle.
1224                if self.resolve_label_offset(b.target) != b.start {
1225                    let redirected = b.labels_at_this_branch.len();
1226                    for &l in &b.labels_at_this_branch {
1227                        trace!(
1228                            " -> label at start of branch {:?} redirected to target {:?}",
1229                            l, b.target
1230                        );
1231                        self.label_aliases[l.0 as usize] = b.target;
1232                        // NOTE: we continue to ensure the invariant that labels
1233                        // pointing to tail of buffer are in `labels_at_tail`
1234                        // because we already ensured above that the last branch
1235                        // cannot have a target of `cur_off`; so we never have
1236                        // to put the label into `labels_at_tail` when moving it
1237                        // here.
1238                    }
1239                    // Maintain invariant: all branches have been redirected
1240                    // and are no longer pointing at the start of this branch.
1241                    let mut_b = self.latest_branches.last_mut().unwrap();
1242                    mut_b.labels_at_this_branch.clear();
1243
1244                    if redirected > 0 {
1245                        trace!(" -> after label redirects, restarting loop");
1246                        continue;
1247                    }
1248                } else {
1249                    break;
1250                }
1251
1252                let b = self.latest_branches.last().unwrap();
1253
1254                // Examine any immediately preceding branch.
1255                if self.latest_branches.len() > 1 {
1256                    let prev_b = &self.latest_branches[self.latest_branches.len() - 2];
1257                    trace!(" -> more than one branch; prev_b = {:?}", prev_b);
1258                    // This uncond is immediately after another uncond; we
1259                    // should have already redirected labels to this uncond away
1260                    // (but check to be sure); so we can truncate this uncond.
1261                    if prev_b.is_uncond()
1262                        && prev_b.end == b.start
1263                        && b.labels_at_this_branch.is_empty()
1264                    {
1265                        trace!(" -> uncond follows another uncond; truncating");
1266                        self.truncate_last_branch();
1267                        continue;
1268                    }
1269
1270                    // This uncond is immediately after a conditional, and the
1271                    // conditional's target is the end of this uncond, and we've
1272                    // already redirected labels to this uncond away; so we can
1273                    // truncate this uncond, flip the sense of the conditional, and
1274                    // set the conditional's target (in `latest_branches` and in
1275                    // `fixup_records`) to the uncond's target.
1276                    if prev_b.is_cond()
1277                        && prev_b.end == b.start
1278                        && self.resolve_label_offset(prev_b.target) == cur_off
1279                    {
1280                        trace!(
1281                            " -> uncond follows a conditional, and conditional's target resolves to current offset"
1282                        );
1283                        // Save the target of the uncond (this becomes the
1284                        // target of the cond), and truncate the uncond.
1285                        let target = b.target;
1286                        let data = prev_b.inverted.clone().unwrap();
1287                        self.truncate_last_branch();
1288
1289                        // Mutate the code and cond branch.
1290                        let off_before_edit = self.cur_offset();
1291                        let prev_b = self.latest_branches.last_mut().unwrap();
1292                        let not_inverted = SmallVec::from(
1293                            &self.inner.data[(prev_b.start as usize)..(prev_b.end as usize)],
1294                        );
1295
1296                        // Low-level edit: replaces bytes of branch with
1297                        // inverted form. cur_off remains the same afterward, so
1298                        // we do not need to modify label data structures.
1299                        self.inner.data.truncate(prev_b.start as usize);
1300                        self.inner.data.extend_from_slice(&data[..]);
1301
1302                        // Save the original code as the inversion of the
1303                        // inverted branch, in case we later edit this branch
1304                        // again.
1305                        prev_b.inverted = Some(not_inverted);
1306                        self.pending_fixup_records[prev_b.fixup].label = target;
1307                        trace!(" -> reassigning target of condbr to {:?}", target);
1308                        prev_b.target = target;
1309                        debug_assert_eq!(off_before_edit, self.cur_offset());
1310                        continue;
1311                    }
1312                }
1313            }
1314
1315            // If we couldn't do anything with the last branch, then break.
1316            break;
1317        }
1318
1319        self.purge_latest_branches();
1320
1321        trace!(
1322            "leave optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
1323            self.latest_branches, self.labels_at_tail, self.pending_fixup_records
1324        );
1325    }
1326
1327    fn purge_latest_branches(&mut self) {
1328        // All of our branch simplification rules work only if a branch ends at
1329        // the tail of the buffer, with no following code; and branches are in
1330        // order in latest_branches; so if the last entry ends prior to
1331        // cur_offset, then clear all entries.
1332        let cur_off = self.cur_offset();
1333        if let Some(l) = self.latest_branches.last() {
1334            if l.end < cur_off {
1335                trace!("purge_latest_branches: removing branch {:?}", l);
1336                self.latest_branches.clear();
1337            }
1338        }
1339
1340        // Post-invariant: no invariant requires any branch to appear in
1341        // `latest_branches`; it is always optional. The list-clear above thus
1342        // preserves all semantics.
1343    }
1344
1345    /// Emit a trap at some point in the future with the specified code and
1346    /// stack map.
1347    ///
1348    /// This function returns a [`MachLabel`] which will be the future address
1349    /// of the trap. Jumps should refer to this label, likely by using the
1350    /// [`MachBuffer::use_label_at_offset`] method, to get a relocation
1351    /// patched in once the address of the trap is known.
1352    ///
1353    /// This will batch all traps into the end of the function.
1354    pub fn defer_trap(&mut self, code: TrapCode) -> MachLabel {
1355        let label = self.get_label();
1356        self.pending_traps.push(MachLabelTrap {
1357            label,
1358            code,
1359            loc: self.cur_srcloc.map(|(_start, loc)| loc),
1360        });
1361        label
1362    }
1363
1364    /// Is an island needed within the next N bytes?
1365    pub fn island_needed(&self, distance: CodeOffset) -> bool {
1366        let deadline = match self.fixup_records.peek() {
1367            Some(fixup) => fixup.deadline().min(self.pending_fixup_deadline),
1368            None => self.pending_fixup_deadline,
1369        };
1370        trace!(
1371            "checking island_needed: cur_offset = {} deadline = {} worst_case_end_of_island = {}",
1372            self.cur_offset(),
1373            deadline,
1374            self.worst_case_end_of_island(distance)
1375        );
1376        let needed = deadline < u32::MAX && self.worst_case_end_of_island(distance) > deadline;
1377        trace!(" -> needed = {needed}");
1378        needed
1379    }
1380
1381    /// Returns the maximal offset that islands can reach if `distance` more
1382    /// bytes are appended.
1383    ///
1384    /// This is used to determine if veneers need insertions since jumps that
1385    /// can't reach past this point must get a veneer of some form.
1386    fn worst_case_end_of_island(&self, distance: CodeOffset) -> CodeOffset {
1387        // Assume that all fixups will require veneers and that the veneers are
1388        // the worst-case size for each platform. This is an over-generalization
1389        // to avoid iterating over the `fixup_records` list or maintaining
1390        // information about it as we go along.
1391        let max_veneer_count =
1392            u32::try_from(self.fixup_records.len() + self.pending_fixup_records.len()).unwrap();
1393        let island_worst_case_size = max_veneer_count
1394            .saturating_mul(I::LabelUse::worst_case_veneer_size())
1395            + self.pending_constants_size
1396            + (self.pending_traps.len() * I::TRAP_OPCODE.len()) as u32;
1397        self.cur_offset()
1398            .saturating_add(distance)
1399            .saturating_add(island_worst_case_size)
1400    }
1401
1402    /// Emit all pending constants and required pending veneers.
1403    ///
1404    /// Should only be called if `island_needed()` returns true, i.e., if we
1405    /// actually reach a deadline. It's not necessarily a problem to do so
1406    /// otherwise but it may result in unnecessary work during emission.
1407    ///
1408    /// The current code-emission position must be a "safe" location for an
1409    /// island: i.e., no fallthrough into the island contents from the
1410    /// previous instruction is possible. Callers emitting inside a basic
1411    /// block should emit a jump-around branch.
1412    pub fn emit_island(&mut self, distance: CodeOffset, ctrl_plane: &mut ControlPlane) {
1413        self.emit_island_maybe_forced(ForceVeneers::No, distance, ctrl_plane);
1414    }
1415
1416    /// Same as `emit_island`, but an internal API with a `force_veneers`
1417    /// argument to force all veneers to always get emitted for debugging.
1418    fn emit_island_maybe_forced(
1419        &mut self,
1420        force_veneers: ForceVeneers,
1421        distance: CodeOffset,
1422        ctrl_plane: &mut ControlPlane,
1423    ) {
1424        trace!(
1425            "emitting island at {}, distance = {distance}",
1426            self.cur_offset()
1427        );
1428
1429        // We're going to purge fixups, so no latest-branch editing can happen
1430        // anymore.
1431        self.latest_branches.clear();
1432
1433        // End the current location tracking since anything emitted during this
1434        // function shouldn't be attributed to whatever the current source
1435        // location is.
1436        //
1437        // Note that the current source location, if it's set right now, will be
1438        // restored at the end of this island emission.
1439        let cur_loc = self.cur_srcloc.map(|(_, loc)| loc);
1440        if cur_loc.is_some() {
1441            self.end_srcloc();
1442        }
1443
1444        let forced_threshold = self.worst_case_end_of_island(distance);
1445        trace!("forced_threshold = {forced_threshold}");
1446
1447        // Emit traps/constants after the island: with potentially
1448        // unbounded pending constants/traps and potentially small
1449        // deadlines, it would otherwise be possible to emit a
1450        // small-range jump, have a nearby deadline *before* the end
1451        // of pending constants/traps, and not be able to emit a
1452        // veneer in time.
1453        //
1454        // Fixups whose labels aren't yet defined (e.g. references to
1455        // pending constants/traps) are simply deferred here; they'll
1456        // be resolved in the next island or in the final fixup pass
1457        // at the end of emission.
1458
1459        // Either handle all pending fixups because they're ready or move them
1460        // onto the `BinaryHeap` tracking all pending fixups if they aren't
1461        // ready.
1462        assert!(self.latest_branches.is_empty());
1463        trace!(
1464            "About to handle fixups at offset {}: {:?}",
1465            self.cur_offset(),
1466            self.pending_fixup_records
1467        );
1468        for fixup in mem::take(&mut self.pending_fixup_records) {
1469            if self.should_apply_fixup(&fixup, forced_threshold) {
1470                self.handle_fixup(fixup, force_veneers, forced_threshold);
1471            } else {
1472                self.fixup_records.push(fixup);
1473            }
1474        }
1475        self.pending_fixup_deadline = u32::MAX;
1476        while let Some(fixup) = self.fixup_records.peek() {
1477            trace!(
1478                "emit_island: fixup {:?} deadline {}",
1479                fixup,
1480                fixup.deadline()
1481            );
1482
1483            // If this fixup shouldn't be applied, that means its label isn't
1484            // defined yet and there'll be remaining space to apply a veneer if
1485            // necessary in the future after this island. In that situation
1486            // because `fixup_records` is sorted by deadline this loop can
1487            // exit.
1488            if !self.should_apply_fixup(fixup, forced_threshold) {
1489                break;
1490            }
1491
1492            let fixup = self.fixup_records.pop().unwrap();
1493            self.handle_fixup(fixup, force_veneers, forced_threshold);
1494        }
1495
1496        // Now emit pending traps and constants.
1497        //
1498        // Note that traps are placed first since this typically happens at the
1499        // end of the function and for disassemblers we try to keep all the code
1500        // contiguously together.
1501        trace!("emitting pending traps: {:?}", self.pending_traps);
1502        for MachLabelTrap { label, code, loc } in mem::take(&mut self.pending_traps) {
1503            // If this trap has source information associated with it then
1504            // emit this information for the trap instruction going out now too.
1505            if let Some(loc) = loc {
1506                self.start_srcloc(loc);
1507            }
1508            self.align_to(I::LabelUse::ALIGN);
1509            self.bind_label(label, ctrl_plane);
1510            self.add_trap(code);
1511            self.put_data(I::TRAP_OPCODE);
1512            if loc.is_some() {
1513                self.end_srcloc();
1514            }
1515        }
1516
1517        trace!("emitting pending constants: {:?}", self.pending_constants);
1518        for constant in mem::take(&mut self.pending_constants) {
1519            let MachBufferConstant { align, size, .. } = self.constants[constant];
1520            let label = self.constants[constant].upcoming_label.take().unwrap();
1521            self.align_to(align);
1522            self.bind_label(label, ctrl_plane);
1523            self.used_constants.push((constant, self.cur_offset()));
1524            self.get_appended_space(size);
1525        }
1526
1527        if let Some(loc) = cur_loc {
1528            self.start_srcloc(loc);
1529        }
1530    }
1531
1532    fn should_apply_fixup(&self, fixup: &MachLabelFixup<I>, forced_threshold: CodeOffset) -> bool {
1533        let label_offset = self.resolve_label_offset(fixup.label);
1534        trace!(
1535            "should_apply_fixup: fixup {fixup:?} label_offset {label_offset} deadline {} forced_threshold {forced_threshold} supports_veneer {}",
1536            fixup.deadline(),
1537            fixup.kind.supports_veneer()
1538        );
1539        let result = (label_offset != UNKNOWN_LABEL_OFFSET)
1540            || ((fixup.deadline() < forced_threshold) && fixup.kind.supports_veneer());
1541        trace!(
1542            " -> {}, {}, {} -> {result}",
1543            label_offset != UNKNOWN_LABEL_OFFSET,
1544            fixup.deadline() < forced_threshold,
1545            fixup.kind.supports_veneer()
1546        );
1547        result
1548    }
1549
1550    fn handle_fixup(
1551        &mut self,
1552        fixup: MachLabelFixup<I>,
1553        force_veneers: ForceVeneers,
1554        forced_threshold: CodeOffset,
1555    ) {
1556        let MachLabelFixup {
1557            label,
1558            offset,
1559            kind,
1560        } = fixup;
1561        let start = offset as usize;
1562        let end = (offset + kind.patch_size()) as usize;
1563        let label_offset = self.resolve_label_offset(label);
1564
1565        if label_offset != UNKNOWN_LABEL_OFFSET {
1566            // If the offset of the label for this fixup is known then
1567            // we're going to do something here-and-now. We're either going
1568            // to patch the original offset because it's an in-bounds jump,
1569            // or we're going to generate a veneer, patch the fixup to jump
1570            // to the veneer, and then keep going.
1571            //
1572            // If the label comes after the original fixup, then we should
1573            // be guaranteed that the jump is in-bounds. Otherwise there's
1574            // a bug somewhere because this method wasn't called soon
1575            // enough. All forward-jumps are tracked and should get veneers
1576            // before their deadline comes and they're unable to jump
1577            // further.
1578            //
1579            // Otherwise if the label is before the fixup, then that's a
1580            // backwards jump. If it's past the maximum negative range
1581            // then we'll emit a veneer that to jump forward to which can
1582            // then jump backwards.
1583            let veneer_required = if label_offset >= offset {
1584                assert!((label_offset - offset) <= kind.max_pos_range());
1585                false
1586            } else {
1587                (offset - label_offset) > kind.max_neg_range()
1588            };
1589            trace!(
1590                " -> label_offset = {}, known, required = {} (pos {} neg {})",
1591                label_offset,
1592                veneer_required,
1593                kind.max_pos_range(),
1594                kind.max_neg_range()
1595            );
1596
1597            if (force_veneers == ForceVeneers::Yes && kind.supports_veneer()) || veneer_required {
1598                self.emit_veneer(label, offset, kind);
1599            } else {
1600                let slice = &mut self.data[start..end];
1601                trace!(
1602                    "patching in-range! slice = {slice:?}; offset = {offset:#x}; label_offset = {label_offset:#x}"
1603                );
1604                kind.patch(slice, offset, label_offset);
1605            }
1606        } else {
1607            // If the offset of this label is not known at this time then
1608            // that means that a veneer is required because after this
1609            // island the target can't be in range of the original target.
1610            assert!(forced_threshold - offset > kind.max_pos_range());
1611            self.emit_veneer(label, offset, kind);
1612        }
1613    }
1614
1615    /// Emits a "veneer" the `kind` code at `offset` to jump to `label`.
1616    ///
1617    /// This will generate extra machine code, using `kind`, to get a
1618    /// larger-jump-kind than `kind` allows. The code at `offset` is then
1619    /// patched to jump to our new code, and then the new code is enqueued for
1620    /// a fixup to get processed at some later time.
1621    fn emit_veneer(&mut self, label: MachLabel, offset: CodeOffset, kind: I::LabelUse) {
1622        // If this `kind` doesn't support a veneer then that's a bug in the
1623        // backend because we need to implement support for such a veneer.
1624        assert!(
1625            kind.supports_veneer(),
1626            "jump beyond the range of {kind:?} but a veneer isn't supported",
1627        );
1628
1629        // Allocate space for a veneer in the island.
1630        self.align_to(I::LabelUse::ALIGN);
1631        let veneer_offset = self.cur_offset();
1632        trace!("making a veneer at {}", veneer_offset);
1633        let start = offset as usize;
1634        let end = (offset + kind.patch_size()) as usize;
1635        let slice = &mut self.data[start..end];
1636        // Patch the original label use to refer to the veneer.
1637        trace!(
1638            "patching original at offset {} to veneer offset {}",
1639            offset, veneer_offset
1640        );
1641        kind.patch(slice, offset, veneer_offset);
1642        // Generate the veneer.
1643        let veneer_slice = self.get_appended_space(kind.veneer_size() as usize);
1644        let (veneer_fixup_off, veneer_label_use) =
1645            kind.generate_veneer(veneer_slice, veneer_offset);
1646        trace!(
1647            "generated veneer; fixup offset {}, label_use {:?}",
1648            veneer_fixup_off, veneer_label_use
1649        );
1650        // Register a new use of `label` with our new veneer fixup and
1651        // offset. This'll recalculate deadlines accordingly and
1652        // enqueue this fixup to get processed at some later
1653        // time.
1654        self.use_label_at_offset(veneer_fixup_off, label, veneer_label_use);
1655    }
1656
1657    fn finish_emission_maybe_forcing_veneers(
1658        &mut self,
1659        force_veneers: ForceVeneers,
1660        ctrl_plane: &mut ControlPlane,
1661    ) {
1662        while !self.pending_constants.is_empty()
1663            || !self.pending_traps.is_empty()
1664            || !self.fixup_records.is_empty()
1665            || !self.pending_fixup_records.is_empty()
1666        {
1667            // `emit_island()` will emit any pending veneers and constants, and
1668            // as a side-effect, will also take care of any fixups with resolved
1669            // labels eagerly.
1670            self.emit_island_maybe_forced(force_veneers, 0, ctrl_plane);
1671        }
1672
1673        // Ensure that all labels have been fixed up after the last island is emitted. This is a
1674        // full (release-mode) assert because an unresolved label means the emitted code is
1675        // incorrect.
1676        assert!(self.fixup_records.is_empty());
1677        assert!(self.pending_fixup_records.is_empty());
1678    }
1679
1680    /// Finish any deferred emissions and/or fixups.
1681    pub fn finish(
1682        mut self,
1683        constants: &VCodeConstants,
1684        ctrl_plane: &mut ControlPlane,
1685    ) -> MachBufferFinalized {
1686        let _tt = timing::vcode_emit_finish();
1687
1688        self.finish_emission_maybe_forcing_veneers(ForceVeneers::No, ctrl_plane);
1689        self.finish_constants(constants);
1690
1691        // Resolve all labels to their offsets.
1692        let mut relocs = core::mem::take(&mut self.relocs);
1693        let mut exception_handlers = core::mem::take(&mut self.exception_handlers);
1694        let resolve = |label: LabelOrOffset| {
1695            LabelOrOffset::offset(self.resolve_label_offset(label.as_label()))
1696        };
1697        for reloc in &mut relocs {
1698            reloc.target.map(resolve);
1699        }
1700        for handler in &mut exception_handlers {
1701            handler.map(resolve);
1702        }
1703        self.relocs = relocs;
1704        self.exception_handlers = exception_handlers;
1705        self.srclocs.sort_by_key(|entry| entry.start);
1706
1707        MachBufferFinalized {
1708            inner: self.inner,
1709            nop_units: I::gen_nop_units(),
1710        }
1711    }
1712
1713    /// Add an external relocation at the given offset.
1714    pub fn add_reloc_at_offset<T: Into<RelocTarget> + Clone>(
1715        &mut self,
1716        offset: CodeOffset,
1717        kind: Reloc,
1718        target: &T,
1719        addend: Addend,
1720    ) {
1721        let target: RelocTarget = target.clone().into();
1722        // FIXME(#3277): This should use `I::LabelUse::from_reloc` to optionally
1723        // generate a label-use statement to track whether an island is possibly
1724        // needed to escape this function to actually get to the external name.
1725        // This is most likely to come up on AArch64 where calls between
1726        // functions use a 26-bit signed offset which gives +/- 64MB. This means
1727        // that if a function is 128MB in size and there's a call in the middle
1728        // it's impossible to reach the actual target. Also, while it's
1729        // technically possible to jump to the start of a function and then jump
1730        // further, island insertion below always inserts islands after
1731        // previously appended code so for Cranelift's own implementation this
1732        // is also a problem for 64MB functions on AArch64 which start with a
1733        // call instruction, those won't be able to escape.
1734        //
1735        // Ideally what needs to happen here is that a `LabelUse` is
1736        // transparently generated (or call-sites of this function are audited
1737        // to generate a `LabelUse` instead) and tracked internally. The actual
1738        // relocation would then change over time if and when a veneer is
1739        // inserted, where the relocation here would be patched by this
1740        // `MachBuffer` to jump to the veneer. The problem, though, is that all
1741        // this still needs to end up, in the case of a singular function,
1742        // generating a final relocation pointing either to this particular
1743        // relocation or to the veneer inserted. Additionally
1744        // `MachBuffer` needs the concept of a label which will never be
1745        // resolved, so `emit_island` doesn't trip over not actually ever
1746        // knowing what some labels are. Currently the loop in
1747        // `finish_emission_maybe_forcing_veneers` would otherwise infinitely
1748        // loop.
1749        //
1750        // For now this means that because relocs aren't tracked at all that
1751        // AArch64 functions have a rough size limits of 64MB. For now that's
1752        // somewhat reasonable and the failure mode is a panic in `MachBuffer`
1753        // when a relocation can't otherwise be resolved later, so it shouldn't
1754        // actually result in any memory unsafety or anything like that.
1755        self.relocs.push(MachReloc {
1756            offset,
1757            kind,
1758            target,
1759            addend,
1760        });
1761    }
1762
1763    /// Add an external relocation at the current offset.
1764    pub fn add_reloc<T: Into<RelocTarget> + Clone>(
1765        &mut self,
1766        kind: Reloc,
1767        target: &T,
1768        addend: Addend,
1769    ) {
1770        self.add_reloc_at_offset(self.data.len() as CodeOffset, kind, target, addend);
1771    }
1772
1773    /// Add a trap record at the current offset.
1774    pub fn add_trap(&mut self, code: TrapCode) {
1775        self.inner.traps.push(MachTrap {
1776            offset: self.inner.data.len() as CodeOffset,
1777            code,
1778        });
1779    }
1780
1781    /// Add a call-site record at the current offset.
1782    pub fn add_call_site(&mut self) {
1783        self.add_try_call_site(None, core::iter::empty());
1784    }
1785
1786    /// Add a call-site record at the current offset with exception
1787    /// handlers.
1788    pub fn add_try_call_site(
1789        &mut self,
1790        frame_offset: Option<u32>,
1791        exception_handlers: impl Iterator<Item = MachExceptionHandler>,
1792    ) {
1793        let start = u32::try_from(self.exception_handlers.len()).unwrap();
1794        self.exception_handlers.extend(exception_handlers);
1795        let end = u32::try_from(self.exception_handlers.len()).unwrap();
1796        let exception_handler_range = start..end;
1797
1798        self.inner.call_sites.push(MachCallSite {
1799            ret_addr: self.inner.data.len() as CodeOffset,
1800            frame_offset,
1801            exception_handler_range,
1802        });
1803    }
1804
1805    /// Add a patchable call record at the current offset The actual
1806    /// call is expected to have been emitted; the VCodeInst trait
1807    /// specifies how to NOP it out, and we carry that information to
1808    /// the finalized Machbuffer.
1809    pub fn add_patchable_call_site(&mut self, len: u32) {
1810        self.inner.patchable_call_sites.push(MachPatchableCallSite {
1811            ret_addr: self.cur_offset(),
1812            len,
1813        });
1814    }
1815
1816    /// Add an unwind record at the current offset.
1817    pub fn add_unwind(&mut self, unwind: UnwindInst) {
1818        self.inner.unwind_info.push((self.cur_offset(), unwind));
1819    }
1820
1821    /// Set the `SourceLoc` for code from this offset until the offset at the
1822    /// next call to `end_srcloc()`.
1823    /// Returns the current [CodeOffset] and [RelSourceLoc].
1824    pub fn start_srcloc(&mut self, loc: RelSourceLoc) -> (CodeOffset, RelSourceLoc) {
1825        let cur = (self.cur_offset(), loc);
1826        self.cur_srcloc = Some(cur);
1827        cur
1828    }
1829
1830    /// Mark the end of the `SourceLoc` segment started at the last
1831    /// `start_srcloc()` call.
1832    pub fn end_srcloc(&mut self) {
1833        let (start, loc) = self
1834            .cur_srcloc
1835            .take()
1836            .expect("end_srcloc() called without start_srcloc()");
1837        let end = self.cur_offset();
1838        // Skip zero-length extends.
1839        debug_assert!(end >= start);
1840        if end > start {
1841            self.srclocs.push(MachSrcLoc {
1842                start,
1843                end,
1844                loc: MaybeRelSourceLoc::rel(loc),
1845            });
1846        }
1847    }
1848
1849    /// Push a user stack map onto this buffer.
1850    ///
1851    /// The stack map is associated with the given `return_addr` code
1852    /// offset. This must be the PC for the instruction just *after* this stack
1853    /// map's associated instruction. For example in the sequence `call $foo;
1854    /// add r8, rax`, the `return_addr` must be the offset of the start of the
1855    /// `add` instruction.
1856    ///
1857    /// Stack maps must be pushed in sorted `return_addr` order.
1858    pub fn push_user_stack_map(
1859        &mut self,
1860        emit_state: &I::State,
1861        return_addr: CodeOffset,
1862        mut stack_map: ir::UserStackMap,
1863    ) {
1864        let span = emit_state.frame_layout().active_size();
1865        trace!("Adding user stack map @ {return_addr:#x} spanning {span} bytes: {stack_map:?}");
1866
1867        debug_assert!(
1868            self.user_stack_maps
1869                .last()
1870                .map_or(true, |(prev_addr, _, _)| *prev_addr < return_addr),
1871            "pushed stack maps out of order: {} is not less than {}",
1872            self.user_stack_maps.last().unwrap().0,
1873            return_addr,
1874        );
1875
1876        stack_map.finalize(emit_state.frame_layout().sp_to_sized_stack_slots());
1877        self.user_stack_maps.push((return_addr, span, stack_map));
1878    }
1879
1880    /// Push a user stack map whose offsets are already relative to the stack
1881    /// pointer at the safepoint, with an explicitly provided frame size.
1882    pub fn push_user_stack_map_sp_relative(
1883        &mut self,
1884        return_addr: CodeOffset,
1885        frame_size: u32,
1886        stack_map: ir::UserStackMap,
1887    ) {
1888        debug_assert!(
1889            self.user_stack_maps
1890                .last()
1891                .map_or(true, |(prev_addr, _, _)| *prev_addr < return_addr),
1892        );
1893        self.user_stack_maps
1894            .push((return_addr, frame_size, stack_map));
1895    }
1896
1897    /// Push a debug tag associated with the current buffer offset.
1898    pub fn push_debug_tags(&mut self, pos: MachDebugTagPos, tags: &[DebugTag]) {
1899        trace!("debug tags at offset {}: {tags:?}", self.cur_offset());
1900        let start = u32::try_from(self.debug_tag_pool.len()).unwrap();
1901        self.debug_tag_pool.extend(tags.iter().cloned());
1902        let end = u32::try_from(self.debug_tag_pool.len()).unwrap();
1903        self.inner.debug_tags.push(MachDebugTags {
1904            offset: self.cur_offset(),
1905            pos,
1906            range: start..end,
1907        });
1908    }
1909
1910    /// Increase the alignment of the buffer to the given alignment if bigger
1911    /// than the current alignment.
1912    pub fn set_log2_min_function_alignment(&mut self, align_to: u8) {
1913        self.min_alignment = self.min_alignment.max(
1914            1u32.checked_shl(u32::from(align_to))
1915                .expect("log2_min_function_alignment too large"),
1916        );
1917    }
1918
1919    /// Set the frame layout metadata.
1920    pub fn set_frame_layout(&mut self, frame_layout: MachBufferFrameLayout) {
1921        debug_assert!(self.frame_layout.is_none());
1922        self.frame_layout = Some(frame_layout);
1923    }
1924}
1925
1926impl<I: VCodeInst> Extend<u8> for MachBuffer<I> {
1927    fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
1928        for b in iter {
1929            self.put1(b);
1930        }
1931    }
1932}
1933
1934impl MachBufferFinalized {
1935    /// Get a list of source location mapping tuples in sorted-by-start-offset order.
1936    pub fn get_srclocs_sorted(&self) -> &[MachSrcLoc] {
1937        &self.srclocs[..]
1938    }
1939
1940    /// Get all debug tags, sorted by associated offset.
1941    pub fn debug_tags(&self) -> impl Iterator<Item = MachBufferDebugTagList<'_>> {
1942        self.debug_tags.iter().map(|tags| {
1943            let start = usize::try_from(tags.range.start).unwrap();
1944            let end = usize::try_from(tags.range.end).unwrap();
1945            MachBufferDebugTagList {
1946                offset: tags.offset,
1947                pos: tags.pos,
1948                tags: &self.debug_tag_pool[start..end],
1949            }
1950        })
1951    }
1952
1953    /// Get the total required size for the code.
1954    pub fn total_size(&self) -> CodeOffset {
1955        self.data.len() as CodeOffset
1956    }
1957
1958    /// Return the code in this mach buffer as a hex string for testing purposes.
1959    pub fn stringify_code_bytes(&self) -> String {
1960        // This is pretty lame, but whatever ..
1961        use core::fmt::Write;
1962        let mut s = String::with_capacity(self.data.len() * 2);
1963        for b in &self.data {
1964            write!(&mut s, "{b:02X}").unwrap();
1965        }
1966        s
1967    }
1968
1969    /// Get the code bytes.
1970    pub fn data(&self) -> &[u8] {
1971        // N.B.: we emit every section into the .text section as far as
1972        // the `CodeSink` is concerned; we do not bother to segregate
1973        // the contents into the actual program text, the jumptable and the
1974        // rodata (constant pool). This allows us to generate code assuming
1975        // that these will not be relocated relative to each other, and avoids
1976        // having to designate each section as belonging in one of the three
1977        // fixed categories defined by `CodeSink`. If this becomes a problem
1978        // later (e.g. because of memory permissions or similar), we can
1979        // add this designation and segregate the output; take care, however,
1980        // to add the appropriate relocations in this case.
1981
1982        &self.data[..]
1983    }
1984
1985    /// Get a mutable slice of the code bytes, allowing patching
1986    /// post-passes.
1987    pub fn data_mut(&mut self) -> &mut [u8] {
1988        &mut self.data[..]
1989    }
1990
1991    /// Get the list of external relocations for this code.
1992    pub fn relocs(&self) -> &[MachReloc] {
1993        &self.relocs[..]
1994    }
1995
1996    /// Get the list of trap records for this code.
1997    pub fn traps(&self) -> &[MachTrap] {
1998        &self.traps[..]
1999    }
2000
2001    /// Get the user stack map metadata for this code.
2002    pub fn user_stack_maps(&self) -> &[(CodeOffset, u32, ir::UserStackMap)] {
2003        &self.user_stack_maps
2004    }
2005
2006    /// Take this buffer's user stack map metadata.
2007    pub fn take_user_stack_maps(&mut self) -> SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]> {
2008        mem::take(&mut self.user_stack_maps)
2009    }
2010
2011    /// Get the list of call sites for this code, along with
2012    /// associated exception handlers.
2013    ///
2014    /// Each item yielded by the returned iterator is a struct with:
2015    ///
2016    /// - The call site metadata record, with a `ret_addr` field
2017    ///   directly accessible and denoting the offset of the return
2018    ///   address into this buffer's code.
2019    /// - The slice of pairs of exception tags and code offsets
2020    ///   denoting exception-handler entry points associated with this
2021    ///   call site.
2022    pub fn call_sites(&self) -> impl Iterator<Item = MachCallSiteItem<'_>> + '_ {
2023        self.call_sites.iter().map(|call_site| {
2024            let handler_range = call_site.exception_handler_range.clone();
2025            let handler_range = usize::try_from(handler_range.start).unwrap()
2026                ..usize::try_from(handler_range.end).unwrap();
2027            MachCallSiteItem {
2028                ret_addr: call_site.ret_addr,
2029                frame_offset: call_site.frame_offset,
2030                exception_handlers: &self.exception_handlers[handler_range],
2031            }
2032        })
2033    }
2034
2035    /// Get the frame layout, if known.
2036    pub fn frame_layout(&self) -> Option<&MachBufferFrameLayout> {
2037        self.frame_layout.as_ref()
2038    }
2039
2040    /// Get the list of patchable call sites for this code.
2041    ///
2042    /// Each location in the buffer contains the bytes for a call
2043    /// instruction to the specified target. If the call is to be
2044    /// patched out, the bytes in the region should be replaced with
2045    /// those given in the `MachBufferFinalized::nop` array, repeated
2046    /// as many times as necessary. (The length of the patchable
2047    /// region is guaranteed to be an integer multiple of that NOP
2048    /// unit size.)
2049    pub fn patchable_call_sites(&self) -> impl Iterator<Item = &MachPatchableCallSite> + '_ {
2050        self.patchable_call_sites.iter()
2051    }
2052}
2053
2054/// An item in the exception-handler list for a callsite, with label
2055/// references.  Items are interpreted in left-to-right order and the
2056/// first match wins.
2057#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2058#[cfg_attr(
2059    feature = "enable-serde",
2060    derive(serde_derive::Serialize, serde_derive::Deserialize)
2061)]
2062pub enum MachExceptionHandler {
2063    /// A specific tag (in the current dynamic context) should be
2064    /// handled by the code at the given offset.
2065    Tag(ExceptionTag, LabelOrOffset),
2066    /// All exceptions should be handled by the code at the given
2067    /// offset.
2068    Default(LabelOrOffset),
2069    /// The dynamic context for interpreting tags is updated to the
2070    /// value stored in the given machine location (in this frame's
2071    /// context).
2072    Context(ExceptionContextLoc),
2073}
2074
2075impl MachExceptionHandler {
2076    fn map<F: Fn(LabelOrOffset) -> LabelOrOffset>(&mut self, f: F) {
2077        match self {
2078            Self::Tag(_, label) => {
2079                *label = f(*label);
2080            }
2081            Self::Default(label) => {
2082                *label = f(*label);
2083            }
2084            Self::Context(_loc) => {}
2085        }
2086    }
2087}
2088
2089/// A location for a dynamic exception context value.
2090#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2091#[cfg_attr(
2092    feature = "enable-serde",
2093    derive(serde_derive::Serialize, serde_derive::Deserialize)
2094)]
2095pub enum ExceptionContextLoc {
2096    /// An offset from SP at the callsite.
2097    SPOffset(u32),
2098    /// A GPR at the callsite. The physical register number for the
2099    /// GPR register file on the target architecture is used.
2100    GPR(u8),
2101}
2102
2103/// Metadata about a constant.
2104struct MachBufferConstant {
2105    /// A label which has not yet been bound which can be used for this
2106    /// constant.
2107    ///
2108    /// This is lazily created when a label is requested for a constant and is
2109    /// cleared when a constant is emitted.
2110    upcoming_label: Option<MachLabel>,
2111    /// Required alignment.
2112    align: CodeOffset,
2113    /// The byte size of this constant.
2114    size: usize,
2115}
2116
2117/// A trap that is deferred to the next time an island is emitted for either
2118/// traps, constants, or fixups.
2119#[derive(Debug)]
2120struct MachLabelTrap {
2121    /// This label will refer to the trap's offset.
2122    label: MachLabel,
2123    /// The code associated with this trap.
2124    code: TrapCode,
2125    /// An optional source location to assign for this trap.
2126    loc: Option<RelSourceLoc>,
2127}
2128
2129/// A fixup to perform on the buffer once code is emitted. Fixups always refer
2130/// to labels and patch the code based on label offsets. Hence, they are like
2131/// relocations, but internal to one buffer.
2132#[derive(Debug)]
2133struct MachLabelFixup<I: VCodeInst> {
2134    /// The label whose offset controls this fixup.
2135    label: MachLabel,
2136    /// The offset to fix up / patch to refer to this label.
2137    offset: CodeOffset,
2138    /// The kind of fixup. This is architecture-specific; each architecture may have,
2139    /// e.g., several types of branch instructions, each with differently-sized
2140    /// offset fields and different places within the instruction to place the
2141    /// bits.
2142    kind: I::LabelUse,
2143}
2144
2145impl<I: VCodeInst> MachLabelFixup<I> {
2146    fn deadline(&self) -> CodeOffset {
2147        self.offset.saturating_add(self.kind.max_pos_range())
2148    }
2149}
2150
2151impl<I: VCodeInst> PartialEq for MachLabelFixup<I> {
2152    fn eq(&self, other: &Self) -> bool {
2153        self.deadline() == other.deadline()
2154    }
2155}
2156
2157impl<I: VCodeInst> Eq for MachLabelFixup<I> {}
2158
2159impl<I: VCodeInst> PartialOrd for MachLabelFixup<I> {
2160    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2161        Some(self.cmp(other))
2162    }
2163}
2164
2165impl<I: VCodeInst> Ord for MachLabelFixup<I> {
2166    fn cmp(&self, other: &Self) -> Ordering {
2167        other.deadline().cmp(&self.deadline())
2168    }
2169}
2170
2171/// A relocation resulting from a compilation.
2172#[derive(Clone, Debug, PartialEq)]
2173#[cfg_attr(
2174    feature = "enable-serde",
2175    derive(serde_derive::Serialize, serde_derive::Deserialize)
2176)]
2177pub struct MachReloc {
2178    /// The offset at which the relocation applies, *relative to the
2179    /// containing section*.
2180    pub offset: CodeOffset,
2181    /// The kind of relocation.
2182    pub kind: Reloc,
2183    /// The external symbol / name to which this relocation refers.
2184    pub target: RelocTarget,
2185    /// The addend to add to the symbol value.
2186    pub addend: i64,
2187}
2188
2189/// A Relocation target
2190#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2191#[cfg_attr(
2192    feature = "enable-serde",
2193    derive(serde_derive::Serialize, serde_derive::Deserialize)
2194)]
2195pub enum RelocTarget {
2196    /// Points to an [ExternalName] outside the current function.
2197    ExternalName(ExternalName),
2198    /// Points to a [MachLabel] inside this function.  This is different
2199    /// from an internal fixup/label reference in that both the
2200    /// relocation and the label will be emitted and are only resolved
2201    /// at link time.
2202    ///
2203    /// There is no reason to prefer this over internal fixups unless
2204    /// the ABI requires it.
2205    Label(LabelOrOffset),
2206}
2207
2208impl From<ExternalName> for RelocTarget {
2209    fn from(name: ExternalName) -> Self {
2210        Self::ExternalName(name)
2211    }
2212}
2213
2214impl From<MachLabel> for RelocTarget {
2215    fn from(label: MachLabel) -> Self {
2216        Self::Label(LabelOrOffset::label(label))
2217    }
2218}
2219
2220impl RelocTarget {
2221    /// Returns a display for the current [RelocTarget], with extra context to prettify the
2222    /// output.
2223    pub fn display<'a>(&'a self, params: Option<&'a FunctionParameters>) -> String {
2224        match self {
2225            RelocTarget::ExternalName(name) => format!("{}", name.display(params)),
2226            RelocTarget::Label(offset) => format!("func+{offset}"),
2227        }
2228    }
2229
2230    fn map<F: Fn(LabelOrOffset) -> LabelOrOffset>(&mut self, f: F) {
2231        match self {
2232            RelocTarget::ExternalName(_) => {}
2233            RelocTarget::Label(label) => {
2234                *label = f(*label);
2235            }
2236        }
2237    }
2238}
2239
2240/// A trap record resulting from a compilation.
2241#[derive(Clone, Debug, PartialEq)]
2242#[cfg_attr(
2243    feature = "enable-serde",
2244    derive(serde_derive::Serialize, serde_derive::Deserialize)
2245)]
2246pub struct MachTrap {
2247    /// The offset at which the trap instruction occurs, *relative to the
2248    /// containing section*.
2249    pub offset: CodeOffset,
2250    /// The trap code.
2251    pub code: TrapCode,
2252}
2253
2254/// A call site record resulting from a compilation.
2255#[derive(Clone, Debug, PartialEq)]
2256#[cfg_attr(
2257    feature = "enable-serde",
2258    derive(serde_derive::Serialize, serde_derive::Deserialize)
2259)]
2260pub struct MachCallSite {
2261    /// The offset of the call's return address, *relative to the
2262    /// start of the buffer*.
2263    pub ret_addr: CodeOffset,
2264
2265    /// The offset from the FP at this callsite down to the SP when
2266    /// the call occurs, if known. In other words, the size of the
2267    /// stack frame up to the saved FP slot. Useful to recover the
2268    /// start of the stack frame and to look up dynamic contexts
2269    /// stored in [`ExceptionContextLoc::SPOffset`].
2270    ///
2271    /// If `None`, the compiler backend did not specify a frame
2272    /// offset. The runtime in use with the compiled code may require
2273    /// the frame offset if exception handlers are present or dynamic
2274    /// context is used, but that is not Cranelift's concern: the
2275    /// frame offset is optional at this level.
2276    pub frame_offset: Option<u32>,
2277
2278    /// Range in `exception_handlers` corresponding to the exception
2279    /// handlers for this callsite.
2280    exception_handler_range: Range<u32>,
2281}
2282
2283/// A view onto a call site record resulting from a compilation,
2284/// returned during iteration.
2285#[derive(Clone, Debug, PartialEq)]
2286pub struct MachCallSiteItem<'a> {
2287    /// The offset of the call's return address, *relative to the
2288    /// start of the buffer*.
2289    pub ret_addr: CodeOffset,
2290
2291    /// The offset from the FP at this callsite down to the SP when
2292    /// the call occurs, if known.
2293    ///
2294    /// See [`MachCallSite::frame_offset`] for more.
2295    pub frame_offset: Option<u32>,
2296
2297    /// Exception handlers at this site.
2298    pub exception_handlers: &'a [MachExceptionHandler],
2299}
2300
2301/// A patchable call site record resulting from a compilation.
2302#[derive(Clone, Debug, PartialEq)]
2303#[cfg_attr(
2304    feature = "enable-serde",
2305    derive(serde_derive::Serialize, serde_derive::Deserialize)
2306)]
2307pub struct MachPatchableCallSite {
2308    /// The offset of the call's return address (i.e., the address
2309    /// after the end of the patchable region), *relative to the start
2310    /// of the buffer*.
2311    pub ret_addr: CodeOffset,
2312
2313    /// The length of the region to be patched by NOP bytes.
2314    pub len: u32,
2315}
2316
2317/// A source-location mapping resulting from a compilation.
2318#[derive(PartialEq, Debug, Clone)]
2319#[cfg_attr(
2320    feature = "enable-serde",
2321    derive(serde_derive::Serialize, serde_derive::Deserialize)
2322)]
2323pub struct MachSrcLoc {
2324    /// The start of the region of code corresponding to a source location.
2325    /// This is relative to the start of the function, not to the start of the
2326    /// section.
2327    pub start: CodeOffset,
2328    /// The end of the region of code corresponding to a source location.
2329    /// This is relative to the start of the function, not to the start of the
2330    /// section.
2331    pub end: CodeOffset,
2332    /// The source location.
2333    pub loc: MaybeRelSourceLoc,
2334}
2335
2336impl MachSrcLoc {
2337    fn apply_base_srcloc(&mut self, base_srcloc: SourceLoc) {
2338        self.loc = MaybeRelSourceLoc::abs(self.loc.relocate(base_srcloc));
2339    }
2340}
2341
2342/// Record of branch instruction in the buffer, to facilitate editing.
2343#[derive(Clone, Debug)]
2344struct MachBranch {
2345    start: CodeOffset,
2346    end: CodeOffset,
2347    target: MachLabel,
2348    fixup: usize,
2349    inverted: Option<SmallVec<[u8; 8]>>,
2350    /// All labels pointing to the start of this branch. For correctness, this
2351    /// *must* be complete (i.e., must contain all labels whose resolved offsets
2352    /// are at the start of this branch): we rely on being able to redirect all
2353    /// labels that could jump to this branch before removing it, if it is
2354    /// otherwise unreachable.
2355    labels_at_this_branch: SmallVec<[MachLabel; 4]>,
2356}
2357
2358impl MachBranch {
2359    fn is_cond(&self) -> bool {
2360        self.inverted.is_some()
2361    }
2362    fn is_uncond(&self) -> bool {
2363        self.inverted.is_none()
2364    }
2365}
2366
2367/// Stack-frame layout information carried through to machine
2368/// code. This provides sufficient information to interpret an active
2369/// stack frame from a running function, if provided.
2370#[derive(Clone, Debug, PartialEq)]
2371#[cfg_attr(
2372    feature = "enable-serde",
2373    derive(serde_derive::Serialize, serde_derive::Deserialize)
2374)]
2375pub struct MachBufferFrameLayout {
2376    /// Offset from bottom of frame to FP (near top of frame). This
2377    /// allows reading the frame given only FP.
2378    pub frame_to_fp_offset: u32,
2379    /// Offset from bottom of frame for each StackSlot,
2380    pub stackslots: SecondaryMap<ir::StackSlot, MachBufferStackSlot>,
2381}
2382
2383/// Descriptor for a single stack slot in the compiled function.
2384#[derive(Clone, Debug, PartialEq, Default)]
2385#[cfg_attr(
2386    feature = "enable-serde",
2387    derive(serde_derive::Serialize, serde_derive::Deserialize)
2388)]
2389pub struct MachBufferStackSlot {
2390    /// Offset from the bottom of the stack frame.
2391    pub offset: u32,
2392
2393    /// User-provided key to describe this stack slot.
2394    pub key: Option<ir::StackSlotKey>,
2395}
2396
2397/// Debug tags: a sequence of references to a stack slot, or a
2398/// user-defined value, at a particular PC.
2399#[derive(Clone, Debug, PartialEq)]
2400#[cfg_attr(
2401    feature = "enable-serde",
2402    derive(serde_derive::Serialize, serde_derive::Deserialize)
2403)]
2404pub(crate) struct MachDebugTags {
2405    /// Offset at which this tag applies.
2406    pub offset: CodeOffset,
2407
2408    /// Position on the attached instruction. This indicates whether
2409    /// the tags attach to the prior instruction (i.e., as a return
2410    /// point from a call) or the current instruction (i.e., as a PC
2411    /// seen during a trap).
2412    pub pos: MachDebugTagPos,
2413
2414    /// The range in the tag pool.
2415    pub range: Range<u32>,
2416}
2417
2418/// Debug tag position on an instruction.
2419///
2420/// We need to distinguish position on an instruction, and not just
2421/// use offsets, because of the following case:
2422///
2423/// ```plain
2424/// <tag1, tag2> call ...
2425/// <tag3, tag4> trapping_store ...
2426/// ```
2427///
2428/// If the stack is walked and interpreted with debug tags while
2429/// within the call, the PC seen will be the return point, i.e. the
2430/// address after the call. If the stack is walked and interpreted
2431/// with debug tags upon a trap of the following instruction, it will
2432/// be the PC of that instruction -- which is the same PC! Thus to
2433/// disambiguate which tags we want, we attach a "pre/post" flag to
2434/// every group of tags at an offset; and when we look up tags, we
2435/// look them up for an offset and "position" at that offset.
2436///
2437/// Thus there are logically two positions at every offset -- so the
2438/// above will be emitted as
2439///
2440/// ```plain
2441/// 0: call ...
2442///                          4, post: <tag1, tag2>
2443///                          4, pre: <tag3, tag4>
2444/// 4: trapping_store ...
2445/// ```
2446#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2447#[cfg_attr(
2448    feature = "enable-serde",
2449    derive(serde_derive::Serialize, serde_derive::Deserialize)
2450)]
2451pub enum MachDebugTagPos {
2452    /// Tags attached after the instruction that ends at this offset.
2453    ///
2454    /// This is used to attach tags to a call, because the PC we see
2455    /// when walking the stack is the *return point*.
2456    Post,
2457    /// Tags attached before the instruction that starts at this offset.
2458    ///
2459    /// This is used to attach tags to every other kind of
2460    /// instruction, because the PC we see when processing a trap of
2461    /// that instruction is the PC of that instruction, not the
2462    /// following one.
2463    Pre,
2464}
2465
2466/// Iterator item for visiting debug tags.
2467pub struct MachBufferDebugTagList<'a> {
2468    /// Offset at which this tag applies.
2469    pub offset: CodeOffset,
2470
2471    /// Position at this offset ("post", attaching to prior
2472    /// instruction, or "pre", attaching to next instruction).
2473    pub pos: MachDebugTagPos,
2474
2475    /// The underlying tags.
2476    pub tags: &'a [DebugTag],
2477}
2478
2479/// Implementation of the `TextSectionBuilder` trait backed by `MachBuffer`.
2480///
2481/// Note that `MachBuffer` was primarily written for intra-function references
2482/// of jumps between basic blocks, but it's also quite usable for entire text
2483/// sections and resolving references between functions themselves. This
2484/// builder interprets "blocks" as labeled functions for the purposes of
2485/// resolving labels internally in the buffer.
2486pub struct MachTextSectionBuilder<I: VCodeInst> {
2487    buf: MachBuffer<I>,
2488    next_func: usize,
2489    force_veneers: ForceVeneers,
2490}
2491
2492impl<I: VCodeInst> MachTextSectionBuilder<I> {
2493    /// Creates a new text section builder which will have `num_funcs` functions
2494    /// pushed into it.
2495    pub fn new(num_funcs: usize) -> MachTextSectionBuilder<I> {
2496        let mut buf = MachBuffer::new();
2497        buf.reserve_labels_for_blocks(num_funcs);
2498        MachTextSectionBuilder {
2499            buf,
2500            next_func: 0,
2501            force_veneers: ForceVeneers::No,
2502        }
2503    }
2504}
2505
2506impl<I: VCodeInst> TextSectionBuilder for MachTextSectionBuilder<I> {
2507    fn append(
2508        &mut self,
2509        labeled: bool,
2510        func: &[u8],
2511        align: u32,
2512        ctrl_plane: &mut ControlPlane,
2513    ) -> u64 {
2514        // Conditionally emit an island if it's necessary to resolve jumps
2515        // between functions which are too far away.
2516        let size = func.len() as u32;
2517        if self.force_veneers == ForceVeneers::Yes || self.buf.island_needed(size) {
2518            self.buf
2519                .emit_island_maybe_forced(self.force_veneers, size, ctrl_plane);
2520        }
2521
2522        self.buf.align_to(align);
2523        let pos = self.buf.cur_offset();
2524        if labeled {
2525            self.buf.bind_label(
2526                MachLabel::from_block(BlockIndex::new(self.next_func)),
2527                ctrl_plane,
2528            );
2529            self.next_func += 1;
2530        }
2531        self.buf.put_data(func);
2532        u64::from(pos)
2533    }
2534
2535    fn resolve_reloc(&mut self, offset: u64, reloc: Reloc, addend: Addend, target: usize) -> bool {
2536        crate::trace!(
2537            "Resolving relocation @ {offset:#x} + {addend:#x} to target {target} of kind {reloc:?}"
2538        );
2539        let label = MachLabel::from_block(BlockIndex::new(target));
2540        let offset = u32::try_from(offset).unwrap();
2541        match I::LabelUse::from_reloc(reloc, addend) {
2542            Some(label_use) => {
2543                self.buf.use_label_at_offset(offset, label, label_use);
2544                true
2545            }
2546            None => false,
2547        }
2548    }
2549
2550    fn force_veneers(&mut self) {
2551        self.force_veneers = ForceVeneers::Yes;
2552    }
2553
2554    fn write(&mut self, offset: u64, data: &[u8]) {
2555        self.buf.data[offset.try_into().unwrap()..][..data.len()].copy_from_slice(data);
2556    }
2557
2558    fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8> {
2559        // Double-check all functions were pushed.
2560        assert_eq!(self.next_func, self.buf.label_offsets.len());
2561
2562        // Finish up any veneers, if necessary.
2563        self.buf
2564            .finish_emission_maybe_forcing_veneers(self.force_veneers, ctrl_plane);
2565
2566        // We don't need the data any more, so return it to the caller.
2567        mem::take(&mut self.buf.data).into_vec()
2568    }
2569}
2570
2571// We use an actual instruction definition to do tests, so we depend on the `arm64` feature here.
2572#[cfg(all(test, feature = "arm64"))]
2573mod test {
2574    use cranelift_entity::EntityRef as _;
2575
2576    use super::*;
2577    use crate::ir::UserExternalNameRef;
2578    use crate::isa::aarch64;
2579    use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst};
2580    use crate::isa::aarch64::inst::{OperandSize, xreg};
2581    use crate::machinst::{MachInst, MachInstEmit, MachInstEmitState};
2582    use crate::settings;
2583
2584    fn label(n: u32) -> MachLabel {
2585        MachLabel::from_block(BlockIndex::new(n as usize))
2586    }
2587    fn target(n: u32) -> BranchTarget {
2588        BranchTarget::Label(label(n))
2589    }
2590
2591    fn emit_info() -> EmitInfo {
2592        let flags = settings::Flags::new(settings::builder());
2593        let isa_flags = aarch64::settings::Flags::new(&flags, &aarch64::settings::builder());
2594        EmitInfo::new(flags, isa_flags)
2595    }
2596
2597    #[test]
2598    fn test_elide_jump_to_next() {
2599        let info = emit_info();
2600        let mut buf = MachBuffer::new();
2601        let mut state = <Inst as MachInstEmit>::State::default();
2602        let constants = Default::default();
2603
2604        buf.reserve_labels_for_blocks(2);
2605        buf.bind_label(label(0), state.ctrl_plane_mut());
2606        let inst = Inst::Jump { dest: target(1) };
2607        inst.emit(&mut buf, &info, &mut state);
2608        buf.bind_label(label(1), state.ctrl_plane_mut());
2609        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2610        assert_eq!(0, buf.total_size());
2611    }
2612
2613    #[test]
2614    fn test_elide_trivial_jump_blocks() {
2615        let info = emit_info();
2616        let mut buf = MachBuffer::new();
2617        let mut state = <Inst as MachInstEmit>::State::default();
2618        let constants = Default::default();
2619
2620        buf.reserve_labels_for_blocks(4);
2621
2622        buf.bind_label(label(0), state.ctrl_plane_mut());
2623        let inst = Inst::CondBr {
2624            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2625            taken: target(1),
2626            not_taken: target(2),
2627        };
2628        inst.emit(&mut buf, &info, &mut state);
2629
2630        buf.bind_label(label(1), state.ctrl_plane_mut());
2631        let inst = Inst::Jump { dest: target(3) };
2632        inst.emit(&mut buf, &info, &mut state);
2633
2634        buf.bind_label(label(2), state.ctrl_plane_mut());
2635        let inst = Inst::Jump { dest: target(3) };
2636        inst.emit(&mut buf, &info, &mut state);
2637
2638        buf.bind_label(label(3), state.ctrl_plane_mut());
2639
2640        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2641        assert_eq!(0, buf.total_size());
2642    }
2643
2644    #[test]
2645    fn test_flip_cond() {
2646        let info = emit_info();
2647        let mut buf = MachBuffer::new();
2648        let mut state = <Inst as MachInstEmit>::State::default();
2649        let constants = Default::default();
2650
2651        buf.reserve_labels_for_blocks(4);
2652
2653        buf.bind_label(label(0), state.ctrl_plane_mut());
2654        let inst = Inst::CondBr {
2655            kind: CondBrKind::Zero(xreg(0), OperandSize::Size64),
2656            taken: target(1),
2657            not_taken: target(2),
2658        };
2659        inst.emit(&mut buf, &info, &mut state);
2660
2661        buf.bind_label(label(1), state.ctrl_plane_mut());
2662        let inst = Inst::Nop4;
2663        inst.emit(&mut buf, &info, &mut state);
2664
2665        buf.bind_label(label(2), state.ctrl_plane_mut());
2666        let inst = Inst::Udf {
2667            trap_code: TrapCode::STACK_OVERFLOW,
2668        };
2669        inst.emit(&mut buf, &info, &mut state);
2670
2671        buf.bind_label(label(3), state.ctrl_plane_mut());
2672
2673        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2674
2675        let mut buf2 = MachBuffer::new();
2676        let mut state = Default::default();
2677        let inst = Inst::TrapIf {
2678            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2679            trap_code: TrapCode::STACK_OVERFLOW,
2680        };
2681        inst.emit(&mut buf2, &info, &mut state);
2682        let inst = Inst::Nop4;
2683        inst.emit(&mut buf2, &info, &mut state);
2684
2685        let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2686
2687        assert_eq!(buf.data, buf2.data);
2688    }
2689
2690    #[test]
2691    fn test_island() {
2692        let info = emit_info();
2693        let mut buf = MachBuffer::new();
2694        let mut state = <Inst as MachInstEmit>::State::default();
2695        let constants = Default::default();
2696
2697        buf.reserve_labels_for_blocks(4);
2698
2699        buf.bind_label(label(0), state.ctrl_plane_mut());
2700        let inst = Inst::CondBr {
2701            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2702            taken: target(2),
2703            not_taken: target(3),
2704        };
2705        inst.emit(&mut buf, &info, &mut state);
2706
2707        buf.bind_label(label(1), state.ctrl_plane_mut());
2708        while buf.cur_offset() < 2000000 {
2709            if buf.island_needed(0) {
2710                buf.emit_island(0, state.ctrl_plane_mut());
2711            }
2712            let inst = Inst::Nop4;
2713            inst.emit(&mut buf, &info, &mut state);
2714        }
2715
2716        buf.bind_label(label(2), state.ctrl_plane_mut());
2717        let inst = Inst::Nop4;
2718        inst.emit(&mut buf, &info, &mut state);
2719
2720        buf.bind_label(label(3), state.ctrl_plane_mut());
2721        let inst = Inst::Nop4;
2722        inst.emit(&mut buf, &info, &mut state);
2723
2724        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2725
2726        assert_eq!(2000000 + 8, buf.total_size());
2727
2728        let mut buf2 = MachBuffer::new();
2729        let mut state = Default::default();
2730        let inst = Inst::CondBr {
2731            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2732
2733            // This conditionally taken branch has a 19-bit constant, shifted
2734            // to the left by two, giving us a 21-bit range in total. Half of
2735            // this range positive so the we should be around 1 << 20 bytes
2736            // away for our jump target.
2737            //
2738            // There are two pending fixups by the time we reach this point,
2739            // one for this 19-bit jump and one for the unconditional 26-bit
2740            // jump below. A 19-bit veneer is 4 bytes large and the 26-bit
2741            // veneer is 20 bytes large, which means that pessimistically
2742            // assuming we'll need two veneers. Currently each veneer is
2743            // pessimistically assumed to be the maximal size which means we
2744            // need 40 bytes of extra space, meaning that the actual island
2745            // should come 40-bytes before the deadline.
2746            taken: BranchTarget::ResolvedOffset((1 << 20) - 20 - 20),
2747
2748            // This branch is in-range so no veneers should be needed, it should
2749            // go directly to the target.
2750            not_taken: BranchTarget::ResolvedOffset(2000000 + 4 - 4),
2751        };
2752        inst.emit(&mut buf2, &info, &mut state);
2753
2754        let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2755
2756        assert_eq!(&buf.data[0..8], &buf2.data[..]);
2757    }
2758
2759    #[test]
2760    fn test_island_backward() {
2761        let info = emit_info();
2762        let mut buf = MachBuffer::new();
2763        let mut state = <Inst as MachInstEmit>::State::default();
2764        let constants = Default::default();
2765
2766        buf.reserve_labels_for_blocks(4);
2767
2768        buf.bind_label(label(0), state.ctrl_plane_mut());
2769        let inst = Inst::Nop4;
2770        inst.emit(&mut buf, &info, &mut state);
2771
2772        buf.bind_label(label(1), state.ctrl_plane_mut());
2773        let inst = Inst::Nop4;
2774        inst.emit(&mut buf, &info, &mut state);
2775
2776        buf.bind_label(label(2), state.ctrl_plane_mut());
2777        while buf.cur_offset() < 2000000 {
2778            let inst = Inst::Nop4;
2779            inst.emit(&mut buf, &info, &mut state);
2780        }
2781
2782        buf.bind_label(label(3), state.ctrl_plane_mut());
2783        let inst = Inst::CondBr {
2784            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2785            taken: target(0),
2786            not_taken: target(1),
2787        };
2788        inst.emit(&mut buf, &info, &mut state);
2789
2790        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2791
2792        assert_eq!(2000000 + 12, buf.total_size());
2793
2794        let mut buf2 = MachBuffer::new();
2795        let mut state = Default::default();
2796        let inst = Inst::CondBr {
2797            kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
2798            taken: BranchTarget::ResolvedOffset(8),
2799            not_taken: BranchTarget::ResolvedOffset(4 - (2000000 + 4)),
2800        };
2801        inst.emit(&mut buf2, &info, &mut state);
2802        let inst = Inst::Jump {
2803            dest: BranchTarget::ResolvedOffset(-(2000000 + 8)),
2804        };
2805        inst.emit(&mut buf2, &info, &mut state);
2806
2807        let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2808
2809        assert_eq!(&buf.data[2000000..], &buf2.data[..]);
2810    }
2811
2812    #[test]
2813    fn test_multiple_redirect() {
2814        // label0:
2815        //   cbz x0, label1
2816        //   b label2
2817        // label1:
2818        //   b label3
2819        // label2:
2820        //   nop
2821        //   nop
2822        //   b label0
2823        // label3:
2824        //   b label4
2825        // label4:
2826        //   b label5
2827        // label5:
2828        //   b label7
2829        // label6:
2830        //   nop
2831        // label7:
2832        //   ret
2833        //
2834        // -- should become:
2835        //
2836        // label0:
2837        //   cbz x0, label7
2838        // label2:
2839        //   nop
2840        //   nop
2841        //   b label0
2842        // label6:
2843        //   nop
2844        // label7:
2845        //   ret
2846
2847        let info = emit_info();
2848        let mut buf = MachBuffer::new();
2849        let mut state = <Inst as MachInstEmit>::State::default();
2850        let constants = Default::default();
2851
2852        buf.reserve_labels_for_blocks(8);
2853
2854        buf.bind_label(label(0), state.ctrl_plane_mut());
2855        let inst = Inst::CondBr {
2856            kind: CondBrKind::Zero(xreg(0), OperandSize::Size64),
2857            taken: target(1),
2858            not_taken: target(2),
2859        };
2860        inst.emit(&mut buf, &info, &mut state);
2861
2862        buf.bind_label(label(1), state.ctrl_plane_mut());
2863        let inst = Inst::Jump { dest: target(3) };
2864        inst.emit(&mut buf, &info, &mut state);
2865
2866        buf.bind_label(label(2), state.ctrl_plane_mut());
2867        let inst = Inst::Nop4;
2868        inst.emit(&mut buf, &info, &mut state);
2869        inst.emit(&mut buf, &info, &mut state);
2870        let inst = Inst::Jump { dest: target(0) };
2871        inst.emit(&mut buf, &info, &mut state);
2872
2873        buf.bind_label(label(3), state.ctrl_plane_mut());
2874        let inst = Inst::Jump { dest: target(4) };
2875        inst.emit(&mut buf, &info, &mut state);
2876
2877        buf.bind_label(label(4), state.ctrl_plane_mut());
2878        let inst = Inst::Jump { dest: target(5) };
2879        inst.emit(&mut buf, &info, &mut state);
2880
2881        buf.bind_label(label(5), state.ctrl_plane_mut());
2882        let inst = Inst::Jump { dest: target(7) };
2883        inst.emit(&mut buf, &info, &mut state);
2884
2885        buf.bind_label(label(6), state.ctrl_plane_mut());
2886        let inst = Inst::Nop4;
2887        inst.emit(&mut buf, &info, &mut state);
2888
2889        buf.bind_label(label(7), state.ctrl_plane_mut());
2890        let inst = Inst::Ret {};
2891        inst.emit(&mut buf, &info, &mut state);
2892
2893        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2894
2895        let golden_data = vec![
2896            0xa0, 0x00, 0x00, 0xb4, // cbz x0, 0x14
2897            0x1f, 0x20, 0x03, 0xd5, // nop
2898            0x1f, 0x20, 0x03, 0xd5, // nop
2899            0xfd, 0xff, 0xff, 0x17, // b 0
2900            0x1f, 0x20, 0x03, 0xd5, // nop
2901            0xc0, 0x03, 0x5f, 0xd6, // ret
2902        ];
2903
2904        assert_eq!(&golden_data[..], &buf.data[..]);
2905    }
2906
2907    #[test]
2908    fn test_handle_branch_cycle() {
2909        // label0:
2910        //   b label1
2911        // label1:
2912        //   b label2
2913        // label2:
2914        //   b label3
2915        // label3:
2916        //   b label4
2917        // label4:
2918        //   b label1  // note: not label0 (to make it interesting).
2919        //
2920        // -- should become:
2921        //
2922        // label0, label1, ..., label4:
2923        //   b label0
2924        let info = emit_info();
2925        let mut buf = MachBuffer::new();
2926        let mut state = <Inst as MachInstEmit>::State::default();
2927        let constants = Default::default();
2928
2929        buf.reserve_labels_for_blocks(5);
2930
2931        buf.bind_label(label(0), state.ctrl_plane_mut());
2932        let inst = Inst::Jump { dest: target(1) };
2933        inst.emit(&mut buf, &info, &mut state);
2934
2935        buf.bind_label(label(1), state.ctrl_plane_mut());
2936        let inst = Inst::Jump { dest: target(2) };
2937        inst.emit(&mut buf, &info, &mut state);
2938
2939        buf.bind_label(label(2), state.ctrl_plane_mut());
2940        let inst = Inst::Jump { dest: target(3) };
2941        inst.emit(&mut buf, &info, &mut state);
2942
2943        buf.bind_label(label(3), state.ctrl_plane_mut());
2944        let inst = Inst::Jump { dest: target(4) };
2945        inst.emit(&mut buf, &info, &mut state);
2946
2947        buf.bind_label(label(4), state.ctrl_plane_mut());
2948        let inst = Inst::Jump { dest: target(1) };
2949        inst.emit(&mut buf, &info, &mut state);
2950
2951        let buf = buf.finish(&constants, state.ctrl_plane_mut());
2952
2953        let golden_data = vec![
2954            0x00, 0x00, 0x00, 0x14, // b 0
2955        ];
2956
2957        assert_eq!(&golden_data[..], &buf.data[..]);
2958    }
2959
2960    #[test]
2961    fn metadata_records() {
2962        let mut buf = MachBuffer::<Inst>::new();
2963        let ctrl_plane = &mut Default::default();
2964        let constants = Default::default();
2965
2966        buf.reserve_labels_for_blocks(3);
2967
2968        buf.bind_label(label(0), ctrl_plane);
2969        buf.put1(1);
2970        buf.add_trap(TrapCode::HEAP_OUT_OF_BOUNDS);
2971        buf.put1(2);
2972        buf.add_trap(TrapCode::INTEGER_OVERFLOW);
2973        buf.add_trap(TrapCode::INTEGER_DIVISION_BY_ZERO);
2974        buf.add_try_call_site(
2975            Some(0x10),
2976            [
2977                MachExceptionHandler::Tag(ExceptionTag::new(42), label(2).into()),
2978                MachExceptionHandler::Default(label(1).into()),
2979            ]
2980            .into_iter(),
2981        );
2982        buf.add_reloc(
2983            Reloc::Abs4,
2984            &ExternalName::User(UserExternalNameRef::new(0)),
2985            0,
2986        );
2987        buf.put1(3);
2988        buf.add_reloc(
2989            Reloc::Abs8,
2990            &ExternalName::User(UserExternalNameRef::new(1)),
2991            1,
2992        );
2993        buf.put1(4);
2994        buf.bind_label(label(1), ctrl_plane);
2995        buf.put1(0xff);
2996        buf.bind_label(label(2), ctrl_plane);
2997        buf.put1(0xff);
2998
2999        let buf = buf.finish(&constants, ctrl_plane);
3000
3001        assert_eq!(buf.data(), &[1, 2, 3, 4, 0xff, 0xff]);
3002        assert_eq!(
3003            buf.traps()
3004                .iter()
3005                .map(|trap| (trap.offset, trap.code))
3006                .collect::<Vec<_>>(),
3007            vec![
3008                (1, TrapCode::HEAP_OUT_OF_BOUNDS),
3009                (2, TrapCode::INTEGER_OVERFLOW),
3010                (2, TrapCode::INTEGER_DIVISION_BY_ZERO)
3011            ]
3012        );
3013        let call_sites: Vec<_> = buf.call_sites().collect();
3014        assert_eq!(call_sites[0].ret_addr, 2);
3015        assert_eq!(call_sites[0].frame_offset, Some(0x10));
3016        assert_eq!(
3017            call_sites[0].exception_handlers,
3018            &[
3019                MachExceptionHandler::Tag(ExceptionTag::new(42), LabelOrOffset::offset(5)),
3020                MachExceptionHandler::Default(LabelOrOffset::offset(4))
3021            ],
3022        );
3023        assert_eq!(
3024            buf.relocs()
3025                .iter()
3026                .map(|reloc| (reloc.offset, reloc.kind))
3027                .collect::<Vec<_>>(),
3028            vec![(2, Reloc::Abs4), (3, Reloc::Abs8)]
3029        );
3030    }
3031
3032    /// Drive the buffer in the same idiom that VCode emission uses:
3033    /// emit instruction bytes via the given closure, then run the
3034    /// per-instruction island check.
3035    fn emit_with_island_check(
3036        buf: &mut MachBuffer<Inst>,
3037        state: &mut <Inst as MachInstEmit>::State,
3038        f: impl FnOnce(&mut MachBuffer<Inst>, &mut <Inst as MachInstEmit>::State),
3039    ) {
3040        f(buf, state);
3041        let lookahead = Inst::worst_case_size() + Inst::worst_case_island_growth();
3042        if buf.island_needed(lookahead) {
3043            let jump_around = buf.get_label();
3044            Inst::gen_jump(jump_around).emit(buf, &emit_info(), state);
3045            buf.emit_island(0, state.ctrl_plane_mut());
3046            buf.bind_label(jump_around, state.ctrl_plane_mut());
3047        }
3048    }
3049
3050    /// Many constant loads in a single basic block: each emits an
3051    /// `Ldr19` reference (+/- 1 MiB range, no veneer support) and
3052    /// adds a 16-byte constant to the pool. Without the
3053    /// per-instruction island check, the pool would grow past the
3054    /// reach of earlier `ldr`s and the buffer would panic during the
3055    /// final fixup pass (see issue #12968).
3056    #[test]
3057    fn test_many_constants_in_one_block() {
3058        use crate::ir::constant::ConstantData;
3059
3060        let mut buf = MachBuffer::<Inst>::new();
3061        let mut state = <Inst as MachInstEmit>::State::default();
3062        let mut constants = VCodeConstants::default();
3063
3064        let n = 200_000;
3065        let mut handles = Vec::with_capacity(n);
3066        for i in 0..n {
3067            let bytes: Vec<u8> = (0..16).map(|b| ((i + b) & 0xff) as u8).collect();
3068            let data = VCodeConstantData::Generated(ConstantData::from(&bytes[..]));
3069            handles.push(constants.insert(data));
3070        }
3071        buf.register_constants(&constants);
3072
3073        buf.reserve_labels_for_blocks(1);
3074        buf.bind_label(label(0), state.ctrl_plane_mut());
3075
3076        for &handle in &handles {
3077            emit_with_island_check(&mut buf, &mut state, |buf, _state| {
3078                let off = buf.cur_offset();
3079                let const_label = buf.get_label_for_constant(handle);
3080                buf.use_label_at_offset(off, const_label, aarch64::inst::LabelUse::Ldr19);
3081                // Placeholder ldr literal instruction.
3082                buf.put4(0);
3083            });
3084        }
3085
3086        let _ = buf.finish(&constants, state.ctrl_plane_mut());
3087    }
3088
3089    /// Mix conditional branches with short ranges (`Branch19`, +/- 1
3090    /// MiB) and many constant loads. The branches' veneers must
3091    /// remain in range as the constant pool grows.
3092    #[test]
3093    fn test_short_branch_amid_constants() {
3094        use crate::ir::constant::ConstantData;
3095
3096        let mut buf = MachBuffer::<Inst>::new();
3097        let mut state = <Inst as MachInstEmit>::State::default();
3098        let mut constants = VCodeConstants::default();
3099
3100        let n = 100_000;
3101        let mut handles = Vec::with_capacity(n);
3102        for i in 0..n {
3103            let bytes: Vec<u8> = (0..16).map(|b| ((i ^ b) & 0xff) as u8).collect();
3104            handles.push(
3105                constants.insert(VCodeConstantData::Generated(ConstantData::from(&bytes[..]))),
3106            );
3107        }
3108        buf.register_constants(&constants);
3109
3110        buf.reserve_labels_for_blocks(2);
3111        buf.bind_label(label(0), state.ctrl_plane_mut());
3112
3113        emit_with_island_check(&mut buf, &mut state, |buf, state| {
3114            let inst = Inst::CondBr {
3115                kind: CondBrKind::NotZero(xreg(0), OperandSize::Size64),
3116                taken: target(1),
3117                not_taken: target(0),
3118            };
3119            inst.emit(buf, &emit_info(), state);
3120        });
3121
3122        for &handle in &handles {
3123            emit_with_island_check(&mut buf, &mut state, |buf, _state| {
3124                let off = buf.cur_offset();
3125                let const_label = buf.get_label_for_constant(handle);
3126                buf.use_label_at_offset(off, const_label, aarch64::inst::LabelUse::Ldr19);
3127                buf.put4(0);
3128            });
3129        }
3130
3131        buf.bind_label(label(1), state.ctrl_plane_mut());
3132        let _ = buf.finish(&constants, state.ctrl_plane_mut());
3133    }
3134
3135    /// Driving an island in the middle of a block via the
3136    /// jump-around-plus-`emit_island` idiom must emit a branch followed
3137    /// by the island contents, such that fall-through reaches the
3138    /// post-island code.
3139    #[test]
3140    fn test_mid_block_island_via_gen_jump() {
3141        let mut buf = MachBuffer::<Inst>::new();
3142        let mut state = <Inst as MachInstEmit>::State::default();
3143        let constants = VCodeConstants::default();
3144
3145        buf.reserve_labels_for_blocks(1);
3146        buf.bind_label(label(0), state.ctrl_plane_mut());
3147
3148        // Place a trap which will need to be emitted in the next island.
3149        let trap_label = buf.defer_trap(TrapCode::HEAP_OUT_OF_BOUNDS);
3150        let off = buf.cur_offset();
3151        buf.use_label_at_offset(off, trap_label, aarch64::inst::LabelUse::Branch19);
3152        buf.put4(0); // placeholder for cbnz-like reference
3153
3154        let before = buf.cur_offset();
3155        let jump_around = buf.get_label();
3156        Inst::gen_jump(jump_around).emit(&mut buf, &emit_info(), &mut state);
3157        buf.emit_island(0, state.ctrl_plane_mut());
3158        buf.bind_label(jump_around, state.ctrl_plane_mut());
3159        let after = buf.cur_offset();
3160
3161        // The jump-around branch (4 bytes on AArch64) plus at least the
3162        // deferred trap (4 bytes) gives a minimum island growth of 8 bytes.
3163        assert!(
3164            after - before >= 8,
3165            "island grew too little: {}",
3166            after - before
3167        );
3168
3169        let buf = buf.finish(&constants, state.ctrl_plane_mut());
3170        let _ = buf.total_size();
3171    }
3172}