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}