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). The emitter
42//! has some awareness of this: it either asks for an island between blocks, so
43//! it is not accidentally executed, or else it emits a branch around the island
44//! when all other options fail (see `Inst::EmitIsland` meta-instruction).
45//!
46//! - A "veneer" is an instruction (or sequence of instructions) in an "island"
47//! that implements a longer-range reference to a label. The idea is that, for
48//! example, a branch with a limited range can branch to a "veneer" instead,
49//! which is simply a branch in a form that can use a longer-range reference. On
50//! AArch64, for example, conditionals have a +/- 1 MB range, but a conditional
51//! can branch to an unconditional branch which has a +/- 128 MB range. Hence, a
52//! conditional branch's label reference can be fixed up with a "veneer" to
53//! achieve a longer range.
54//!
55//! - To implement all of this, we require the backend to provide a `LabelUse`
56//! type that implements a trait. This is nominally an enum that records one of
57//! several kinds of references to an offset in code -- basically, a relocation
58//! type -- and will usually correspond to different instruction formats. The
59//! `LabelUse` implementation specifies the maximum range, how to patch in the
60//! actual label location when known, and how to generate a veneer to extend the
61//! range.
62//!
63//! That satisfies label references, but we still may have suboptimal branch
64//! patterns. To clean up the branches, we do a simple "peephole"-style
65//! optimization on the fly. To do so, the emitter (e.g., `Inst::emit()`)
66//! informs the buffer of branches in the code and, in the case of conditionals,
67//! the code that would have been emitted to invert this branch's condition. We
68//! track the "latest branches": these are branches that are contiguous up to
69//! the current offset. (If any code is emitted after a branch, that branch or
70//! run of contiguous branches is no longer "latest".) The latest branches are
71//! those that we can edit by simply truncating the buffer and doing something
72//! else instead.
73//!
74//! To optimize branches, we implement several simple rules, and try to apply
75//! them to the "latest branches" when possible:
76//!
77//! - A branch with a label target, when that label is bound to the ending
78//! offset of the branch (the fallthrough location), can be removed altogether,
79//! because the branch would have no effect).
80//!
81//! - An unconditional branch that starts at a label location, and branches to
82//! another label, results in a "label alias": all references to the label bound
83//! *to* this branch instruction are instead resolved to the *target* of the
84//! branch instruction. This effectively removes empty blocks that just
85//! unconditionally branch to the next block. We call this "branch threading".
86//!
87//! - A conditional followed by an unconditional, when the conditional branches
88//! to the unconditional's fallthrough, results in (i) the truncation of the
89//! unconditional, (ii) the inversion of the condition's condition, and (iii)
90//! replacement of the conditional's target (using the original target of the
91//! unconditional). This is a fancy way of saying "we can flip a two-target
92//! conditional branch's taken/not-taken targets if it works better with our
93//! fallthrough". To make this work, the emitter actually gives the buffer
94//! *both* forms of every conditional branch: the true form is emitted into the
95//! buffer, and the "inverted" machine-code bytes are provided as part of the
96//! branch-fixup metadata.
97//!
98//! - An unconditional B preceded by another unconditional P, when B's label(s) have
99//! been redirected to target(B), can be removed entirely. This is an extension
100//! of the branch-threading optimization, and is valid because if we know there
101//! will be no fallthrough into this branch instruction (the prior instruction
102//! is an unconditional jump), and if we know we have successfully redirected
103//! all labels, then this branch instruction is unreachable. Note that this
104//! works because the redirection happens before the label is ever resolved
105//! (fixups happen at island emission time, at which point latest-branches are
106//! cleared, or at the end of emission), so we are sure to catch and redirect
107//! all possible paths to this instruction.
108//!
109//! # Branch-optimization Correctness
110//!
111//! The branch-optimization mechanism depends on a few data structures with
112//! invariants, which are always held outside the scope of top-level public
113//! methods:
114//!
115//! - The latest-branches list. Each entry describes a span of the buffer
116//! (start/end offsets), the label target, the corresponding fixup-list entry
117//! index, and the bytes (must be the same length) for the inverted form, if
118//! conditional. The list of labels that are bound to the start-offset of this
119//! branch is *complete* (if any label has a resolved offset equal to `start`
120//! and is not an alias, it must appear in this list) and *precise* (no label
121//! in this list can be bound to another offset). No label in this list should
122//! be an alias. No two branch ranges can overlap, and branches are in
123//! ascending-offset order.
124//!
125//! - The labels-at-tail list. This contains all MachLabels that have been bound
126//! to (whose resolved offsets are equal to) the tail offset of the buffer.
127//! No label in this list should be an alias.
128//!
129//! - The label_offsets array, containing the bound offset of a label or
130//! UNKNOWN. No label can be bound at an offset greater than the current
131//! buffer tail.
132//!
133//! - The label_aliases array, containing another label to which a label is
134//! bound or UNKNOWN. A label's resolved offset is the resolved offset
135//! of the label it is aliased to, if this is set.
136//!
137//! We argue below, at each method, how the invariants in these data structures
138//! are maintained (grep for "Post-invariant").
139//!
140//! Given these invariants, we argue why each optimization preserves execution
141//! semantics below (grep for "Preserves execution semantics").
142//!
143//! # Avoiding Quadratic Behavior
144//!
145//! There are two cases where we've had to take some care to avoid
146//! quadratic worst-case behavior:
147//!
148//! - The "labels at this branch" list can grow unboundedly if the
149//! code generator binds many labels at one location. If the count
150//! gets too high (defined by the `LABEL_LIST_THRESHOLD` constant), we
151//! simply abort an optimization early in a way that is always correct
152//! but is conservative.
153//!
154//! - The fixup list can interact with island emission to create
155//! "quadratic island behvior". In a little more detail, one can hit
156//! this behavior by having some pending fixups (forward label
157//! references) with long-range label-use kinds, and some others
158//! with shorter-range references that nonetheless still are pending
159//! long enough to trigger island generation. In such a case, we
160//! process the fixup list, generate veneers to extend some forward
161//! references' ranges, but leave the other (longer-range) ones
162//! alone. The way this was implemented put them back on a list and
163//! resulted in quadratic behavior.
164//!
165//! To avoid this fixups are split into two lists: one "pending" list and one
166//! final list. The pending list is kept around for handling fixups related to
167//! branches so it can be edited/truncated. When an island is reached, which
168//! starts processing fixups, all pending fixups are flushed into the final
169//! list. The final list is a `BinaryHeap` which enables fixup processing to
170//! only process those which are required during island emission, deferring
171//! all longer-range fixups to later.
172
173use crate::binemit::{Addend, CodeOffset, Reloc};
174use crate::ir::function::FunctionParameters;
175use crate::ir::{ExternalName, RelSourceLoc, SourceLoc, TrapCode};
176use crate::isa::unwind::UnwindInst;
177use crate::machinst::{
178 BlockIndex, MachInstLabelUse, TextSectionBuilder, VCodeConstant, VCodeConstants, VCodeInst,
179};
180use crate::trace;
181use crate::{ir, MachInstEmitState};
182use crate::{timing, VCodeConstantData};
183use cranelift_control::ControlPlane;
184use cranelift_entity::{entity_impl, PrimaryMap};
185use smallvec::SmallVec;
186use std::cmp::Ordering;
187use std::collections::BinaryHeap;
188use std::mem;
189use std::string::String;
190use std::vec::Vec;
191
192#[cfg(feature = "enable-serde")]
193use serde::{Deserialize, Serialize};
194
195#[cfg(feature = "enable-serde")]
196pub trait CompilePhase {
197 type MachSrcLocType: for<'a> Deserialize<'a> + Serialize + core::fmt::Debug + PartialEq + Clone;
198 type SourceLocType: for<'a> Deserialize<'a> + Serialize + core::fmt::Debug + PartialEq + Clone;
199}
200
201#[cfg(not(feature = "enable-serde"))]
202pub trait CompilePhase {
203 type MachSrcLocType: core::fmt::Debug + PartialEq + Clone;
204 type SourceLocType: core::fmt::Debug + PartialEq + Clone;
205}
206
207/// Status of a compiled artifact that needs patching before being used.
208#[derive(Clone, Debug, PartialEq)]
209#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
210pub struct Stencil;
211
212/// Status of a compiled artifact ready to use.
213#[derive(Clone, Debug, PartialEq)]
214pub struct Final;
215
216impl CompilePhase for Stencil {
217 type MachSrcLocType = MachSrcLoc<Stencil>;
218 type SourceLocType = RelSourceLoc;
219}
220
221impl CompilePhase for Final {
222 type MachSrcLocType = MachSrcLoc<Final>;
223 type SourceLocType = SourceLoc;
224}
225
226#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227enum ForceVeneers {
228 Yes,
229 No,
230}
231
232/// A buffer of output to be produced, fixed up, and then emitted to a CodeSink
233/// in bulk.
234///
235/// This struct uses `SmallVec`s to support small-ish function bodies without
236/// any heap allocation. As such, it will be several kilobytes large. This is
237/// likely fine as long as it is stack-allocated for function emission then
238/// thrown away; but beware if many buffer objects are retained persistently.
239pub struct MachBuffer<I: VCodeInst> {
240 /// The buffer contents, as raw bytes.
241 data: SmallVec<[u8; 1024]>,
242 /// Any relocations referring to this code. Note that only *external*
243 /// relocations are tracked here; references to labels within the buffer are
244 /// resolved before emission.
245 relocs: SmallVec<[MachReloc; 16]>,
246 /// Any trap records referring to this code.
247 traps: SmallVec<[MachTrap; 16]>,
248 /// Any call site records referring to this code.
249 call_sites: SmallVec<[MachCallSite; 16]>,
250 /// Any source location mappings referring to this code.
251 srclocs: SmallVec<[MachSrcLoc<Stencil>; 64]>,
252 /// Any user stack maps for this code.
253 ///
254 /// Each entry is an `(offset, span, stack_map)` triple. Entries are sorted
255 /// by code offset, and each stack map covers `span` bytes on the stack.
256 user_stack_maps: SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]>,
257 /// Any unwind info at a given location.
258 unwind_info: SmallVec<[(CodeOffset, UnwindInst); 8]>,
259 /// The current source location in progress (after `start_srcloc()` and
260 /// before `end_srcloc()`). This is a (start_offset, src_loc) tuple.
261 cur_srcloc: Option<(CodeOffset, RelSourceLoc)>,
262 /// Known label offsets; `UNKNOWN_LABEL_OFFSET` if unknown.
263 label_offsets: SmallVec<[CodeOffset; 16]>,
264 /// Label aliases: when one label points to an unconditional jump, and that
265 /// jump points to another label, we can redirect references to the first
266 /// label immediately to the second.
267 ///
268 /// Invariant: we don't have label-alias cycles. We ensure this by,
269 /// before setting label A to alias label B, resolving B's alias
270 /// target (iteratively until a non-aliased label); if B is already
271 /// aliased to A, then we cannot alias A back to B.
272 label_aliases: SmallVec<[MachLabel; 16]>,
273 /// Constants that must be emitted at some point.
274 pending_constants: SmallVec<[VCodeConstant; 16]>,
275 /// Byte size of all constants in `pending_constants`.
276 pending_constants_size: CodeOffset,
277 /// Traps that must be emitted at some point.
278 pending_traps: SmallVec<[MachLabelTrap; 16]>,
279 /// Fixups that haven't yet been flushed into `fixup_records` below and may
280 /// be related to branches that are chomped. These all get added to
281 /// `fixup_records` during island emission.
282 pending_fixup_records: SmallVec<[MachLabelFixup<I>; 16]>,
283 /// The nearest upcoming deadline for entries in `pending_fixup_records`.
284 pending_fixup_deadline: CodeOffset,
285 /// Fixups that must be performed after all code is emitted.
286 fixup_records: BinaryHeap<MachLabelFixup<I>>,
287 /// Latest branches, to facilitate in-place editing for better fallthrough
288 /// behavior and empty-block removal.
289 latest_branches: SmallVec<[MachBranch; 4]>,
290 /// All labels at the current offset (emission tail). This is lazily
291 /// cleared: it is actually accurate as long as the current offset is
292 /// `labels_at_tail_off`, but if `cur_offset()` has grown larger, it should
293 /// be considered as empty.
294 ///
295 /// For correctness, this *must* be complete (i.e., the vector must contain
296 /// all labels whose offsets are resolved to the current tail), because we
297 /// rely on it to update labels when we truncate branches.
298 labels_at_tail: SmallVec<[MachLabel; 4]>,
299 /// The last offset at which `labels_at_tail` is valid. It is conceptually
300 /// always describing the tail of the buffer, but we do not clear
301 /// `labels_at_tail` eagerly when the tail grows, rather we lazily clear it
302 /// when the offset has grown past this (`labels_at_tail_off`) point.
303 /// Always <= `cur_offset()`.
304 labels_at_tail_off: CodeOffset,
305 /// Metadata about all constants that this function has access to.
306 ///
307 /// This records the size/alignment of all constants (not the actual data)
308 /// along with the last available label generated for the constant. This map
309 /// is consulted when constants are referred to and the label assigned to a
310 /// constant may change over time as well.
311 constants: PrimaryMap<VCodeConstant, MachBufferConstant>,
312 /// All recorded usages of constants as pairs of the constant and where the
313 /// constant needs to be placed within `self.data`. Note that the same
314 /// constant may appear in this array multiple times if it was emitted
315 /// multiple times.
316 used_constants: SmallVec<[(VCodeConstant, CodeOffset); 4]>,
317 /// Indicates when a patchable region is currently open, to guard that it's
318 /// not possible to nest patchable regions.
319 open_patchable: bool,
320}
321
322impl MachBufferFinalized<Stencil> {
323 /// Get a finalized machine buffer by applying the function's base source location.
324 pub fn apply_base_srcloc(self, base_srcloc: SourceLoc) -> MachBufferFinalized<Final> {
325 MachBufferFinalized {
326 data: self.data,
327 relocs: self.relocs,
328 traps: self.traps,
329 call_sites: self.call_sites,
330 srclocs: self
331 .srclocs
332 .into_iter()
333 .map(|srcloc| srcloc.apply_base_srcloc(base_srcloc))
334 .collect(),
335 user_stack_maps: self.user_stack_maps,
336 unwind_info: self.unwind_info,
337 alignment: self.alignment,
338 }
339 }
340}
341
342/// A `MachBuffer` once emission is completed: holds generated code and records,
343/// without fixups. This allows the type to be independent of the backend.
344#[derive(PartialEq, Debug, Clone)]
345#[cfg_attr(
346 feature = "enable-serde",
347 derive(serde_derive::Serialize, serde_derive::Deserialize)
348)]
349pub struct MachBufferFinalized<T: CompilePhase> {
350 /// The buffer contents, as raw bytes.
351 pub(crate) data: SmallVec<[u8; 1024]>,
352 /// Any relocations referring to this code. Note that only *external*
353 /// relocations are tracked here; references to labels within the buffer are
354 /// resolved before emission.
355 pub(crate) relocs: SmallVec<[FinalizedMachReloc; 16]>,
356 /// Any trap records referring to this code.
357 pub(crate) traps: SmallVec<[MachTrap; 16]>,
358 /// Any call site records referring to this code.
359 pub(crate) call_sites: SmallVec<[MachCallSite; 16]>,
360 /// Any source location mappings referring to this code.
361 pub(crate) srclocs: SmallVec<[T::MachSrcLocType; 64]>,
362 /// Any user stack maps for this code.
363 ///
364 /// Each entry is an `(offset, span, stack_map)` triple. Entries are sorted
365 /// by code offset, and each stack map covers `span` bytes on the stack.
366 pub(crate) user_stack_maps: SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]>,
367 /// Any unwind info at a given location.
368 pub unwind_info: SmallVec<[(CodeOffset, UnwindInst); 8]>,
369 /// The required alignment of this buffer.
370 pub alignment: u32,
371}
372
373const UNKNOWN_LABEL_OFFSET: CodeOffset = 0xffff_ffff;
374const UNKNOWN_LABEL: MachLabel = MachLabel(0xffff_ffff);
375
376/// Threshold on max length of `labels_at_this_branch` list to avoid
377/// unbounded quadratic behavior (see comment below at use-site).
378const LABEL_LIST_THRESHOLD: usize = 100;
379
380/// A label refers to some offset in a `MachBuffer`. It may not be resolved at
381/// the point at which it is used by emitted code; the buffer records "fixups"
382/// for references to the label, and will come back and patch the code
383/// appropriately when the label's location is eventually known.
384#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub struct MachLabel(u32);
386entity_impl!(MachLabel);
387
388impl MachLabel {
389 /// Get a label for a block. (The first N MachLabels are always reserved for
390 /// the N blocks in the vcode.)
391 pub fn from_block(bindex: BlockIndex) -> MachLabel {
392 MachLabel(bindex.index() as u32)
393 }
394
395 /// Get the numeric label index.
396 pub fn get(self) -> u32 {
397 self.0
398 }
399
400 /// Creates a string representing this label, for convenience.
401 pub fn to_string(&self) -> String {
402 format!("label{}", self.0)
403 }
404}
405
406impl Default for MachLabel {
407 fn default() -> Self {
408 UNKNOWN_LABEL
409 }
410}
411
412/// Represents the beginning of an editable region in the [`MachBuffer`], while code emission is
413/// still occurring. An [`OpenPatchRegion`] is closed by [`MachBuffer::end_patchable`], consuming
414/// the [`OpenPatchRegion`] token in the process.
415pub struct OpenPatchRegion(usize);
416
417/// A region in the [`MachBuffer`] code buffer that can be edited prior to finalization. An example
418/// of where you might want to use this is for patching instructions that mention constants that
419/// won't be known until later: [`MachBuffer::start_patchable`] can be used to begin the patchable
420/// region, instructions can be emitted with placeholder constants, and the [`PatchRegion`] token
421/// can be produced by [`MachBuffer::end_patchable`]. Once the values of those constants are known,
422/// the [`PatchRegion::patch`] function can be used to get a mutable buffer to the instruction
423/// bytes, and the constants uses can be updated directly.
424pub struct PatchRegion {
425 range: std::ops::Range<usize>,
426}
427
428impl PatchRegion {
429 /// Consume the patch region to yield a mutable slice of the [`MachBuffer`] data buffer.
430 pub fn patch<I: VCodeInst>(self, buffer: &mut MachBuffer<I>) -> &mut [u8] {
431 &mut buffer.data[self.range]
432 }
433}
434
435impl<I: VCodeInst> MachBuffer<I> {
436 /// Create a new section, known to start at `start_offset` and with a size limited to
437 /// `length_limit`.
438 pub fn new() -> MachBuffer<I> {
439 MachBuffer {
440 data: SmallVec::new(),
441 relocs: SmallVec::new(),
442 traps: SmallVec::new(),
443 call_sites: SmallVec::new(),
444 srclocs: SmallVec::new(),
445 user_stack_maps: SmallVec::new(),
446 unwind_info: SmallVec::new(),
447 cur_srcloc: None,
448 label_offsets: SmallVec::new(),
449 label_aliases: SmallVec::new(),
450 pending_constants: SmallVec::new(),
451 pending_constants_size: 0,
452 pending_traps: SmallVec::new(),
453 pending_fixup_records: SmallVec::new(),
454 pending_fixup_deadline: u32::MAX,
455 fixup_records: Default::default(),
456 latest_branches: SmallVec::new(),
457 labels_at_tail: SmallVec::new(),
458 labels_at_tail_off: 0,
459 constants: Default::default(),
460 used_constants: Default::default(),
461 open_patchable: false,
462 }
463 }
464
465 /// Current offset from start of buffer.
466 pub fn cur_offset(&self) -> CodeOffset {
467 self.data.len() as CodeOffset
468 }
469
470 /// Add a byte.
471 pub fn put1(&mut self, value: u8) {
472 self.data.push(value);
473
474 // Post-invariant: conceptual-labels_at_tail contains a complete and
475 // precise list of labels bound at `cur_offset()`. We have advanced
476 // `cur_offset()`, hence if it had been equal to `labels_at_tail_off`
477 // before, it is not anymore (and it cannot become equal, because
478 // `labels_at_tail_off` is always <= `cur_offset()`). Thus the list is
479 // conceptually empty (even though it is only lazily cleared). No labels
480 // can be bound at this new offset (by invariant on `label_offsets`).
481 // Hence the invariant holds.
482 }
483
484 /// Add 2 bytes.
485 pub fn put2(&mut self, value: u16) {
486 let bytes = value.to_le_bytes();
487 self.data.extend_from_slice(&bytes[..]);
488
489 // Post-invariant: as for `put1()`.
490 }
491
492 /// Add 4 bytes.
493 pub fn put4(&mut self, value: u32) {
494 let bytes = value.to_le_bytes();
495 self.data.extend_from_slice(&bytes[..]);
496
497 // Post-invariant: as for `put1()`.
498 }
499
500 /// Add 8 bytes.
501 pub fn put8(&mut self, value: u64) {
502 let bytes = value.to_le_bytes();
503 self.data.extend_from_slice(&bytes[..]);
504
505 // Post-invariant: as for `put1()`.
506 }
507
508 /// Add a slice of bytes.
509 pub fn put_data(&mut self, data: &[u8]) {
510 self.data.extend_from_slice(data);
511
512 // Post-invariant: as for `put1()`.
513 }
514
515 /// Reserve appended space and return a mutable slice referring to it.
516 pub fn get_appended_space(&mut self, len: usize) -> &mut [u8] {
517 let off = self.data.len();
518 let new_len = self.data.len() + len;
519 self.data.resize(new_len, 0);
520 &mut self.data[off..]
521
522 // Post-invariant: as for `put1()`.
523 }
524
525 /// Align up to the given alignment.
526 pub fn align_to(&mut self, align_to: CodeOffset) {
527 trace!("MachBuffer: align to {}", align_to);
528 assert!(
529 align_to.is_power_of_two(),
530 "{align_to} is not a power of two"
531 );
532 while self.cur_offset() & (align_to - 1) != 0 {
533 self.put1(0);
534 }
535
536 // Post-invariant: as for `put1()`.
537 }
538
539 /// Begin a region of patchable code. There is one requirement for the
540 /// code that is emitted: It must not introduce any instructions that
541 /// could be chomped (branches are an example of this). In other words,
542 /// you must not call [`MachBuffer::add_cond_branch`] or
543 /// [`MachBuffer::add_uncond_branch`] between calls to this method and
544 /// [`MachBuffer::end_patchable`].
545 pub fn start_patchable(&mut self) -> OpenPatchRegion {
546 assert!(!self.open_patchable, "Patchable regions may not be nested");
547 self.open_patchable = true;
548 OpenPatchRegion(usize::try_from(self.cur_offset()).unwrap())
549 }
550
551 /// End a region of patchable code, yielding a [`PatchRegion`] value that
552 /// can be consumed later to produce a one-off mutable slice to the
553 /// associated region of the data buffer.
554 pub fn end_patchable(&mut self, open: OpenPatchRegion) -> PatchRegion {
555 // No need to assert the state of `open_patchable` here, as we take
556 // ownership of the only `OpenPatchable` value.
557 self.open_patchable = false;
558 let end = usize::try_from(self.cur_offset()).unwrap();
559 PatchRegion { range: open.0..end }
560 }
561
562 /// Allocate a `Label` to refer to some offset. May not be bound to a fixed
563 /// offset yet.
564 pub fn get_label(&mut self) -> MachLabel {
565 let l = self.label_offsets.len() as u32;
566 self.label_offsets.push(UNKNOWN_LABEL_OFFSET);
567 self.label_aliases.push(UNKNOWN_LABEL);
568 trace!("MachBuffer: new label -> {:?}", MachLabel(l));
569 MachLabel(l)
570
571 // Post-invariant: the only mutation is to add a new label; it has no
572 // bound offset yet, so it trivially satisfies all invariants.
573 }
574
575 /// Reserve the first N MachLabels for blocks.
576 pub fn reserve_labels_for_blocks(&mut self, blocks: usize) {
577 trace!("MachBuffer: first {} labels are for blocks", blocks);
578 debug_assert!(self.label_offsets.is_empty());
579 self.label_offsets.resize(blocks, UNKNOWN_LABEL_OFFSET);
580 self.label_aliases.resize(blocks, UNKNOWN_LABEL);
581
582 // Post-invariant: as for `get_label()`.
583 }
584
585 /// Registers metadata in this `MachBuffer` about the `constants` provided.
586 ///
587 /// This will record the size/alignment of all constants which will prepare
588 /// them for emission later on.
589 pub fn register_constants(&mut self, constants: &VCodeConstants) {
590 for (c, val) in constants.iter() {
591 self.register_constant(&c, val);
592 }
593 }
594
595 /// Similar to [`MachBuffer::register_constants`] but registers a
596 /// single constant metadata. This function is useful in
597 /// situations where not all constants are known at the time of
598 /// emission.
599 pub fn register_constant(&mut self, constant: &VCodeConstant, data: &VCodeConstantData) {
600 let c2 = self.constants.push(MachBufferConstant {
601 upcoming_label: None,
602 align: data.alignment(),
603 size: data.as_slice().len(),
604 });
605 assert_eq!(*constant, c2);
606 }
607
608 /// Completes constant emission by iterating over `self.used_constants` and
609 /// filling in the "holes" with the constant values provided by `constants`.
610 ///
611 /// Returns the alignment required for this entire buffer. Alignment starts
612 /// at the ISA's minimum function alignment and can be increased due to
613 /// constant requirements.
614 fn finish_constants(&mut self, constants: &VCodeConstants) -> u32 {
615 let mut alignment = I::function_alignment().minimum;
616 for (constant, offset) in mem::take(&mut self.used_constants) {
617 let constant = constants.get(constant);
618 let data = constant.as_slice();
619 self.data[offset as usize..][..data.len()].copy_from_slice(data);
620 alignment = constant.alignment().max(alignment);
621 }
622 alignment
623 }
624
625 /// Returns a label that can be used to refer to the `constant` provided.
626 ///
627 /// This will automatically defer a new constant to be emitted for
628 /// `constant` if it has not been previously emitted. Note that this
629 /// function may return a different label for the same constant at
630 /// different points in time. The label is valid to use only from the
631 /// current location; the MachBuffer takes care to emit the same constant
632 /// multiple times if needed so the constant is always in range.
633 pub fn get_label_for_constant(&mut self, constant: VCodeConstant) -> MachLabel {
634 let MachBufferConstant {
635 align,
636 size,
637 upcoming_label,
638 } = self.constants[constant];
639 if let Some(label) = upcoming_label {
640 return label;
641 }
642
643 let label = self.get_label();
644 trace!(
645 "defer constant: eventually emit {size} bytes aligned \
646 to {align} at label {label:?}",
647 );
648 self.pending_constants.push(constant);
649 self.pending_constants_size += size as u32;
650 self.constants[constant].upcoming_label = Some(label);
651 label
652 }
653
654 /// Bind a label to the current offset. A label can only be bound once.
655 pub fn bind_label(&mut self, label: MachLabel, ctrl_plane: &mut ControlPlane) {
656 trace!(
657 "MachBuffer: bind label {:?} at offset {}",
658 label,
659 self.cur_offset()
660 );
661 debug_assert_eq!(self.label_offsets[label.0 as usize], UNKNOWN_LABEL_OFFSET);
662 debug_assert_eq!(self.label_aliases[label.0 as usize], UNKNOWN_LABEL);
663 let offset = self.cur_offset();
664 self.label_offsets[label.0 as usize] = offset;
665 self.lazily_clear_labels_at_tail();
666 self.labels_at_tail.push(label);
667
668 // Invariants hold: bound offset of label is <= cur_offset (in fact it
669 // is equal). If the `labels_at_tail` list was complete and precise
670 // before, it is still, because we have bound this label to the current
671 // offset and added it to the list (which contains all labels at the
672 // current offset).
673
674 self.optimize_branches(ctrl_plane);
675
676 // Post-invariant: by `optimize_branches()` (see argument there).
677 }
678
679 /// Lazily clear `labels_at_tail` if the tail offset has moved beyond the
680 /// offset that it applies to.
681 fn lazily_clear_labels_at_tail(&mut self) {
682 let offset = self.cur_offset();
683 if offset > self.labels_at_tail_off {
684 self.labels_at_tail_off = offset;
685 self.labels_at_tail.clear();
686 }
687
688 // Post-invariant: either labels_at_tail_off was at cur_offset, and
689 // state is untouched, or was less than cur_offset, in which case the
690 // labels_at_tail list was conceptually empty, and is now actually
691 // empty.
692 }
693
694 /// Resolve a label to an offset, if known. May return `UNKNOWN_LABEL_OFFSET`.
695 pub(crate) fn resolve_label_offset(&self, mut label: MachLabel) -> CodeOffset {
696 let mut iters = 0;
697 while self.label_aliases[label.0 as usize] != UNKNOWN_LABEL {
698 label = self.label_aliases[label.0 as usize];
699 // To protect against an infinite loop (despite our assurances to
700 // ourselves that the invariants make this impossible), assert out
701 // after 1M iterations. The number of basic blocks is limited
702 // in most contexts anyway so this should be impossible to hit with
703 // a legitimate input.
704 iters += 1;
705 assert!(iters < 1_000_000, "Unexpected cycle in label aliases");
706 }
707 self.label_offsets[label.0 as usize]
708
709 // Post-invariant: no mutations.
710 }
711
712 /// Emit a reference to the given label with the given reference type (i.e.,
713 /// branch-instruction format) at the current offset. This is like a
714 /// relocation, but handled internally.
715 ///
716 /// This can be called before the branch is actually emitted; fixups will
717 /// not happen until an island is emitted or the buffer is finished.
718 pub fn use_label_at_offset(&mut self, offset: CodeOffset, label: MachLabel, kind: I::LabelUse) {
719 trace!(
720 "MachBuffer: use_label_at_offset: offset {} label {:?} kind {:?}",
721 offset,
722 label,
723 kind
724 );
725
726 // Add the fixup, and update the worst-case island size based on a
727 // veneer for this label use.
728 let fixup = MachLabelFixup {
729 label,
730 offset,
731 kind,
732 };
733 self.pending_fixup_deadline = self.pending_fixup_deadline.min(fixup.deadline());
734 self.pending_fixup_records.push(fixup);
735
736 // Post-invariant: no mutations to branches/labels data structures.
737 }
738
739 /// Inform the buffer of an unconditional branch at the given offset,
740 /// targeting the given label. May be used to optimize branches.
741 /// The last added label-use must correspond to this branch.
742 /// This must be called when the current offset is equal to `start`; i.e.,
743 /// before actually emitting the branch. This implies that for a branch that
744 /// uses a label and is eligible for optimizations by the MachBuffer, the
745 /// proper sequence is:
746 ///
747 /// - Call `use_label_at_offset()` to emit the fixup record.
748 /// - Call `add_uncond_branch()` to make note of the branch.
749 /// - Emit the bytes for the branch's machine code.
750 ///
751 /// Additional requirement: no labels may be bound between `start` and `end`
752 /// (exclusive on both ends).
753 pub fn add_uncond_branch(&mut self, start: CodeOffset, end: CodeOffset, target: MachLabel) {
754 debug_assert!(
755 !self.open_patchable,
756 "Branch instruction inserted within a patchable region"
757 );
758 assert!(self.cur_offset() == start);
759 debug_assert!(end > start);
760 assert!(!self.pending_fixup_records.is_empty());
761 let fixup = self.pending_fixup_records.len() - 1;
762 self.lazily_clear_labels_at_tail();
763 self.latest_branches.push(MachBranch {
764 start,
765 end,
766 target,
767 fixup,
768 inverted: None,
769 labels_at_this_branch: self.labels_at_tail.clone(),
770 });
771
772 // Post-invariant: we asserted branch start is current tail; the list of
773 // labels at branch is cloned from list of labels at current tail.
774 }
775
776 /// Inform the buffer of a conditional branch at the given offset,
777 /// targeting the given label. May be used to optimize branches.
778 /// The last added label-use must correspond to this branch.
779 ///
780 /// Additional requirement: no labels may be bound between `start` and `end`
781 /// (exclusive on both ends).
782 pub fn add_cond_branch(
783 &mut self,
784 start: CodeOffset,
785 end: CodeOffset,
786 target: MachLabel,
787 inverted: &[u8],
788 ) {
789 debug_assert!(
790 !self.open_patchable,
791 "Branch instruction inserted within a patchable region"
792 );
793 assert!(self.cur_offset() == start);
794 debug_assert!(end > start);
795 assert!(!self.pending_fixup_records.is_empty());
796 debug_assert!(
797 inverted.len() == (end - start) as usize,
798 "branch length = {}, but inverted length = {}",
799 end - start,
800 inverted.len()
801 );
802 let fixup = self.pending_fixup_records.len() - 1;
803 let inverted = Some(SmallVec::from(inverted));
804 self.lazily_clear_labels_at_tail();
805 self.latest_branches.push(MachBranch {
806 start,
807 end,
808 target,
809 fixup,
810 inverted,
811 labels_at_this_branch: self.labels_at_tail.clone(),
812 });
813
814 // Post-invariant: we asserted branch start is current tail; labels at
815 // branch list is cloned from list of labels at current tail.
816 }
817
818 fn truncate_last_branch(&mut self) {
819 debug_assert!(
820 !self.open_patchable,
821 "Branch instruction truncated within a patchable region"
822 );
823
824 self.lazily_clear_labels_at_tail();
825 // Invariants hold at this point.
826
827 let b = self.latest_branches.pop().unwrap();
828 assert!(b.end == self.cur_offset());
829
830 // State:
831 // [PRE CODE]
832 // Offset b.start, b.labels_at_this_branch:
833 // [BRANCH CODE]
834 // cur_off, self.labels_at_tail -->
835 // (end of buffer)
836 self.data.truncate(b.start as usize);
837 self.pending_fixup_records.truncate(b.fixup);
838 while let Some(last_srcloc) = self.srclocs.last_mut() {
839 if last_srcloc.end <= b.start {
840 break;
841 }
842 if last_srcloc.start < b.start {
843 last_srcloc.end = b.start;
844 break;
845 }
846 self.srclocs.pop();
847 }
848 // State:
849 // [PRE CODE]
850 // cur_off, Offset b.start, b.labels_at_this_branch:
851 // (end of buffer)
852 //
853 // self.labels_at_tail --> (past end of buffer)
854 let cur_off = self.cur_offset();
855 self.labels_at_tail_off = cur_off;
856 // State:
857 // [PRE CODE]
858 // cur_off, Offset b.start, b.labels_at_this_branch,
859 // self.labels_at_tail:
860 // (end of buffer)
861 //
862 // resolve_label_offset(l) for l in labels_at_tail:
863 // (past end of buffer)
864
865 trace!(
866 "truncate_last_branch: truncated {:?}; off now {}",
867 b,
868 cur_off
869 );
870
871 // Fix up resolved label offsets for labels at tail.
872 for &l in &self.labels_at_tail {
873 self.label_offsets[l.0 as usize] = cur_off;
874 }
875 // Old labels_at_this_branch are now at cur_off.
876 self.labels_at_tail
877 .extend(b.labels_at_this_branch.into_iter());
878
879 // Post-invariant: this operation is defined to truncate the buffer,
880 // which moves cur_off backward, and to move labels at the end of the
881 // buffer back to the start-of-branch offset.
882 //
883 // latest_branches satisfies all invariants:
884 // - it has no branches past the end of the buffer (branches are in
885 // order, we removed the last one, and we truncated the buffer to just
886 // before the start of that branch)
887 // - no labels were moved to lower offsets than the (new) cur_off, so
888 // the labels_at_this_branch list for any other branch need not change.
889 //
890 // labels_at_tail satisfies all invariants:
891 // - all labels that were at the tail after the truncated branch are
892 // moved backward to just before the branch, which becomes the new tail;
893 // thus every element in the list should remain (ensured by `.extend()`
894 // above).
895 // - all labels that refer to the new tail, which is the start-offset of
896 // the truncated branch, must be present. The `labels_at_this_branch`
897 // list in the truncated branch's record is a complete and precise list
898 // of exactly these labels; we append these to labels_at_tail.
899 // - labels_at_tail_off is at cur_off after truncation occurs, so the
900 // list is valid (not to be lazily cleared).
901 //
902 // The stated operation was performed:
903 // - For each label at the end of the buffer prior to this method, it
904 // now resolves to the new (truncated) end of the buffer: it must have
905 // been in `labels_at_tail` (this list is precise and complete, and
906 // the tail was at the end of the truncated branch on entry), and we
907 // iterate over this list and set `label_offsets` to the new tail.
908 // None of these labels could have been an alias (by invariant), so
909 // `label_offsets` is authoritative for each.
910 // - No other labels will be past the end of the buffer, because of the
911 // requirement that no labels be bound to the middle of branch ranges
912 // (see comments to `add_{cond,uncond}_branch()`).
913 // - The buffer is truncated to just before the last branch, and the
914 // fixup record referring to that last branch is removed.
915 }
916
917 /// Performs various optimizations on branches pointing at the current label.
918 pub fn optimize_branches(&mut self, ctrl_plane: &mut ControlPlane) {
919 if ctrl_plane.get_decision() {
920 return;
921 }
922
923 self.lazily_clear_labels_at_tail();
924 // Invariants valid at this point.
925
926 trace!(
927 "enter optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
928 self.latest_branches,
929 self.labels_at_tail,
930 self.pending_fixup_records
931 );
932
933 // We continue to munch on branches at the tail of the buffer until no
934 // more rules apply. Note that the loop only continues if a branch is
935 // actually truncated (or if labels are redirected away from a branch),
936 // so this always makes progress.
937 while let Some(b) = self.latest_branches.last() {
938 let cur_off = self.cur_offset();
939 trace!("optimize_branches: last branch {:?} at off {}", b, cur_off);
940 // If there has been any code emission since the end of the last branch or
941 // label definition, then there's nothing we can edit (because we
942 // don't move code once placed, only back up and overwrite), so
943 // clear the records and finish.
944 if b.end < cur_off {
945 break;
946 }
947
948 // If the "labels at this branch" list on this branch is
949 // longer than a threshold, don't do any simplification,
950 // and let the branch remain to separate those labels from
951 // the current tail. This avoids quadratic behavior (see
952 // #3468): otherwise, if a long string of "goto next;
953 // next:" patterns are emitted, all of the labels will
954 // coalesce into a long list of aliases for the current
955 // buffer tail. We must track all aliases of the current
956 // tail for correctness, but we are also allowed to skip
957 // optimization (removal) of any branch, so we take the
958 // escape hatch here and let it stand. In effect this
959 // "spreads" the many thousands of labels in the
960 // pathological case among an actual (harmless but
961 // suboptimal) instruction once per N labels.
962 if b.labels_at_this_branch.len() > LABEL_LIST_THRESHOLD {
963 break;
964 }
965
966 // Invariant: we are looking at a branch that ends at the tail of
967 // the buffer.
968
969 // For any branch, conditional or unconditional:
970 // - If the target is a label at the current offset, then remove
971 // the conditional branch, and reset all labels that targeted
972 // the current offset (end of branch) to the truncated
973 // end-of-code.
974 //
975 // Preserves execution semantics: a branch to its own fallthrough
976 // address is equivalent to a no-op; in both cases, nextPC is the
977 // fallthrough.
978 if self.resolve_label_offset(b.target) == cur_off {
979 trace!("branch with target == cur off; truncating");
980 self.truncate_last_branch();
981 continue;
982 }
983
984 // If latest is an unconditional branch:
985 //
986 // - If the branch's target is not its own start address, then for
987 // each label at the start of branch, make the label an alias of the
988 // branch target, and remove the label from the "labels at this
989 // branch" list.
990 //
991 // - Preserves execution semantics: an unconditional branch's
992 // only effect is to set PC to a new PC; this change simply
993 // collapses one step in the step-semantics.
994 //
995 // - Post-invariant: the labels that were bound to the start of
996 // this branch become aliases, so they must not be present in any
997 // labels-at-this-branch list or the labels-at-tail list. The
998 // labels are removed form the latest-branch record's
999 // labels-at-this-branch list, and are never placed in the
1000 // labels-at-tail list. Furthermore, it is correct that they are
1001 // not in either list, because they are now aliases, and labels
1002 // that are aliases remain aliases forever.
1003 //
1004 // - If there is a prior unconditional branch that ends just before
1005 // this one begins, and this branch has no labels bound to its
1006 // start, then we can truncate this branch, because it is entirely
1007 // unreachable (we have redirected all labels that make it
1008 // reachable otherwise). Do so and continue around the loop.
1009 //
1010 // - Preserves execution semantics: the branch is unreachable,
1011 // because execution can only flow into an instruction from the
1012 // prior instruction's fallthrough or from a branch bound to that
1013 // instruction's start offset. Unconditional branches have no
1014 // fallthrough, so if the prior instruction is an unconditional
1015 // branch, no fallthrough entry can happen. The
1016 // labels-at-this-branch list is complete (by invariant), so if it
1017 // is empty, then the instruction is entirely unreachable. Thus,
1018 // it can be removed.
1019 //
1020 // - Post-invariant: ensured by truncate_last_branch().
1021 //
1022 // - If there is a prior conditional branch whose target label
1023 // resolves to the current offset (branches around the
1024 // unconditional branch), then remove the unconditional branch,
1025 // and make the target of the unconditional the target of the
1026 // conditional instead.
1027 //
1028 // - Preserves execution semantics: previously we had:
1029 //
1030 // L1:
1031 // cond_br L2
1032 // br L3
1033 // L2:
1034 // (end of buffer)
1035 //
1036 // by removing the last branch, we have:
1037 //
1038 // L1:
1039 // cond_br L2
1040 // L2:
1041 // (end of buffer)
1042 //
1043 // we then fix up the records for the conditional branch to
1044 // have:
1045 //
1046 // L1:
1047 // cond_br.inverted L3
1048 // L2:
1049 //
1050 // In the original code, control flow reaches L2 when the
1051 // conditional branch's predicate is true, and L3 otherwise. In
1052 // the optimized code, the same is true.
1053 //
1054 // - Post-invariant: all edits to latest_branches and
1055 // labels_at_tail are performed by `truncate_last_branch()`,
1056 // which maintains the invariants at each step.
1057
1058 if b.is_uncond() {
1059 // Set any label equal to current branch's start as an alias of
1060 // the branch's target, if the target is not the branch itself
1061 // (i.e., an infinite loop).
1062 //
1063 // We cannot perform this aliasing if the target of this branch
1064 // ultimately aliases back here; if so, we need to keep this
1065 // branch, so break out of this loop entirely (and clear the
1066 // latest-branches list below).
1067 //
1068 // Note that this check is what prevents cycles from forming in
1069 // `self.label_aliases`. To see why, consider an arbitrary start
1070 // state:
1071 //
1072 // label_aliases[L1] = L2, label_aliases[L2] = L3, ..., up to
1073 // Ln, which is not aliased.
1074 //
1075 // We would create a cycle if we assigned label_aliases[Ln]
1076 // = L1. Note that the below assignment is the only write
1077 // to label_aliases.
1078 //
1079 // By our other invariants, we have that Ln (`l` below)
1080 // resolves to the offset `b.start`, because it is in the
1081 // set `b.labels_at_this_branch`.
1082 //
1083 // If L1 were already aliased, through some arbitrarily deep
1084 // chain, to Ln, then it must also resolve to this offset
1085 // `b.start`.
1086 //
1087 // By checking the resolution of `L1` against this offset,
1088 // and aborting this branch-simplification if they are
1089 // equal, we prevent the below assignment from ever creating
1090 // a cycle.
1091 if self.resolve_label_offset(b.target) != b.start {
1092 let redirected = b.labels_at_this_branch.len();
1093 for &l in &b.labels_at_this_branch {
1094 trace!(
1095 " -> label at start of branch {:?} redirected to target {:?}",
1096 l,
1097 b.target
1098 );
1099 self.label_aliases[l.0 as usize] = b.target;
1100 // NOTE: we continue to ensure the invariant that labels
1101 // pointing to tail of buffer are in `labels_at_tail`
1102 // because we already ensured above that the last branch
1103 // cannot have a target of `cur_off`; so we never have
1104 // to put the label into `labels_at_tail` when moving it
1105 // here.
1106 }
1107 // Maintain invariant: all branches have been redirected
1108 // and are no longer pointing at the start of this branch.
1109 let mut_b = self.latest_branches.last_mut().unwrap();
1110 mut_b.labels_at_this_branch.clear();
1111
1112 if redirected > 0 {
1113 trace!(" -> after label redirects, restarting loop");
1114 continue;
1115 }
1116 } else {
1117 break;
1118 }
1119
1120 let b = self.latest_branches.last().unwrap();
1121
1122 // Examine any immediately preceding branch.
1123 if self.latest_branches.len() > 1 {
1124 let prev_b = &self.latest_branches[self.latest_branches.len() - 2];
1125 trace!(" -> more than one branch; prev_b = {:?}", prev_b);
1126 // This uncond is immediately after another uncond; we
1127 // should have already redirected labels to this uncond away
1128 // (but check to be sure); so we can truncate this uncond.
1129 if prev_b.is_uncond()
1130 && prev_b.end == b.start
1131 && b.labels_at_this_branch.is_empty()
1132 {
1133 trace!(" -> uncond follows another uncond; truncating");
1134 self.truncate_last_branch();
1135 continue;
1136 }
1137
1138 // This uncond is immediately after a conditional, and the
1139 // conditional's target is the end of this uncond, and we've
1140 // already redirected labels to this uncond away; so we can
1141 // truncate this uncond, flip the sense of the conditional, and
1142 // set the conditional's target (in `latest_branches` and in
1143 // `fixup_records`) to the uncond's target.
1144 if prev_b.is_cond()
1145 && prev_b.end == b.start
1146 && self.resolve_label_offset(prev_b.target) == cur_off
1147 {
1148 trace!(" -> uncond follows a conditional, and conditional's target resolves to current offset");
1149 // Save the target of the uncond (this becomes the
1150 // target of the cond), and truncate the uncond.
1151 let target = b.target;
1152 let data = prev_b.inverted.clone().unwrap();
1153 self.truncate_last_branch();
1154
1155 // Mutate the code and cond branch.
1156 let off_before_edit = self.cur_offset();
1157 let prev_b = self.latest_branches.last_mut().unwrap();
1158 let not_inverted = SmallVec::from(
1159 &self.data[(prev_b.start as usize)..(prev_b.end as usize)],
1160 );
1161
1162 // Low-level edit: replaces bytes of branch with
1163 // inverted form. cur_off remains the same afterward, so
1164 // we do not need to modify label data structures.
1165 self.data.truncate(prev_b.start as usize);
1166 self.data.extend_from_slice(&data[..]);
1167
1168 // Save the original code as the inversion of the
1169 // inverted branch, in case we later edit this branch
1170 // again.
1171 prev_b.inverted = Some(not_inverted);
1172 self.pending_fixup_records[prev_b.fixup].label = target;
1173 trace!(" -> reassigning target of condbr to {:?}", target);
1174 prev_b.target = target;
1175 debug_assert_eq!(off_before_edit, self.cur_offset());
1176 continue;
1177 }
1178 }
1179 }
1180
1181 // If we couldn't do anything with the last branch, then break.
1182 break;
1183 }
1184
1185 self.purge_latest_branches();
1186
1187 trace!(
1188 "leave optimize_branches:\n b = {:?}\n l = {:?}\n f = {:?}",
1189 self.latest_branches,
1190 self.labels_at_tail,
1191 self.pending_fixup_records
1192 );
1193 }
1194
1195 fn purge_latest_branches(&mut self) {
1196 // All of our branch simplification rules work only if a branch ends at
1197 // the tail of the buffer, with no following code; and branches are in
1198 // order in latest_branches; so if the last entry ends prior to
1199 // cur_offset, then clear all entries.
1200 let cur_off = self.cur_offset();
1201 if let Some(l) = self.latest_branches.last() {
1202 if l.end < cur_off {
1203 trace!("purge_latest_branches: removing branch {:?}", l);
1204 self.latest_branches.clear();
1205 }
1206 }
1207
1208 // Post-invariant: no invariant requires any branch to appear in
1209 // `latest_branches`; it is always optional. The list-clear above thus
1210 // preserves all semantics.
1211 }
1212
1213 /// Emit a trap at some point in the future with the specified code and
1214 /// stack map.
1215 ///
1216 /// This function returns a [`MachLabel`] which will be the future address
1217 /// of the trap. Jumps should refer to this label, likely by using the
1218 /// [`MachBuffer::use_label_at_offset`] method, to get a relocation
1219 /// patched in once the address of the trap is known.
1220 ///
1221 /// This will batch all traps into the end of the function.
1222 pub fn defer_trap(&mut self, code: TrapCode) -> MachLabel {
1223 let label = self.get_label();
1224 self.pending_traps.push(MachLabelTrap {
1225 label,
1226 code,
1227 loc: self.cur_srcloc.map(|(_start, loc)| loc),
1228 });
1229 label
1230 }
1231
1232 /// Is an island needed within the next N bytes?
1233 pub fn island_needed(&self, distance: CodeOffset) -> bool {
1234 let deadline = match self.fixup_records.peek() {
1235 Some(fixup) => fixup.deadline().min(self.pending_fixup_deadline),
1236 None => self.pending_fixup_deadline,
1237 };
1238 deadline < u32::MAX && self.worst_case_end_of_island(distance) > deadline
1239 }
1240
1241 /// Returns the maximal offset that islands can reach if `distance` more
1242 /// bytes are appended.
1243 ///
1244 /// This is used to determine if veneers need insertions since jumps that
1245 /// can't reach past this point must get a veneer of some form.
1246 fn worst_case_end_of_island(&self, distance: CodeOffset) -> CodeOffset {
1247 // Assume that all fixups will require veneers and that the veneers are
1248 // the worst-case size for each platform. This is an over-generalization
1249 // to avoid iterating over the `fixup_records` list or maintaining
1250 // information about it as we go along.
1251 let island_worst_case_size = ((self.fixup_records.len() + self.pending_fixup_records.len())
1252 as u32)
1253 * (I::LabelUse::worst_case_veneer_size())
1254 + self.pending_constants_size
1255 + (self.pending_traps.len() * I::TRAP_OPCODE.len()) as u32;
1256 self.cur_offset()
1257 .saturating_add(distance)
1258 .saturating_add(island_worst_case_size)
1259 }
1260
1261 /// Emit all pending constants and required pending veneers.
1262 ///
1263 /// Should only be called if `island_needed()` returns true, i.e., if we
1264 /// actually reach a deadline. It's not necessarily a problem to do so
1265 /// otherwise but it may result in unnecessary work during emission.
1266 pub fn emit_island(&mut self, distance: CodeOffset, ctrl_plane: &mut ControlPlane) {
1267 self.emit_island_maybe_forced(ForceVeneers::No, distance, ctrl_plane);
1268 }
1269
1270 /// Same as `emit_island`, but an internal API with a `force_veneers`
1271 /// argument to force all veneers to always get emitted for debugging.
1272 fn emit_island_maybe_forced(
1273 &mut self,
1274 force_veneers: ForceVeneers,
1275 distance: CodeOffset,
1276 ctrl_plane: &mut ControlPlane,
1277 ) {
1278 // We're going to purge fixups, so no latest-branch editing can happen
1279 // anymore.
1280 self.latest_branches.clear();
1281
1282 // End the current location tracking since anything emitted during this
1283 // function shouldn't be attributed to whatever the current source
1284 // location is.
1285 //
1286 // Note that the current source location, if it's set right now, will be
1287 // restored at the end of this island emission.
1288 let cur_loc = self.cur_srcloc.map(|(_, loc)| loc);
1289 if cur_loc.is_some() {
1290 self.end_srcloc();
1291 }
1292
1293 let forced_threshold = self.worst_case_end_of_island(distance);
1294
1295 // First flush out all traps/constants so we have more labels in case
1296 // fixups are applied against these labels.
1297 //
1298 // Note that traps are placed first since this typically happens at the
1299 // end of the function and for disassemblers we try to keep all the code
1300 // contiguously together.
1301 for MachLabelTrap { label, code, loc } in mem::take(&mut self.pending_traps) {
1302 // If this trap has source information associated with it then
1303 // emit this information for the trap instruction going out now too.
1304 if let Some(loc) = loc {
1305 self.start_srcloc(loc);
1306 }
1307 self.align_to(I::LabelUse::ALIGN);
1308 self.bind_label(label, ctrl_plane);
1309 self.add_trap(code);
1310 self.put_data(I::TRAP_OPCODE);
1311 if loc.is_some() {
1312 self.end_srcloc();
1313 }
1314 }
1315
1316 for constant in mem::take(&mut self.pending_constants) {
1317 let MachBufferConstant { align, size, .. } = self.constants[constant];
1318 let label = self.constants[constant].upcoming_label.take().unwrap();
1319 self.align_to(align);
1320 self.bind_label(label, ctrl_plane);
1321 self.used_constants.push((constant, self.cur_offset()));
1322 self.get_appended_space(size);
1323 }
1324
1325 // Either handle all pending fixups because they're ready or move them
1326 // onto the `BinaryHeap` tracking all pending fixups if they aren't
1327 // ready.
1328 assert!(self.latest_branches.is_empty());
1329 for fixup in mem::take(&mut self.pending_fixup_records) {
1330 if self.should_apply_fixup(&fixup, forced_threshold) {
1331 self.handle_fixup(fixup, force_veneers, forced_threshold);
1332 } else {
1333 self.fixup_records.push(fixup);
1334 }
1335 }
1336 self.pending_fixup_deadline = u32::MAX;
1337 while let Some(fixup) = self.fixup_records.peek() {
1338 trace!("emit_island: fixup {:?}", fixup);
1339
1340 // If this fixup shouldn't be applied, that means its label isn't
1341 // defined yet and there'll be remaining space to apply a veneer if
1342 // necessary in the future after this island. In that situation
1343 // because `fixup_records` is sorted by deadline this loop can
1344 // exit.
1345 if !self.should_apply_fixup(fixup, forced_threshold) {
1346 break;
1347 }
1348
1349 let fixup = self.fixup_records.pop().unwrap();
1350 self.handle_fixup(fixup, force_veneers, forced_threshold);
1351 }
1352
1353 if let Some(loc) = cur_loc {
1354 self.start_srcloc(loc);
1355 }
1356 }
1357
1358 fn should_apply_fixup(&self, fixup: &MachLabelFixup<I>, forced_threshold: CodeOffset) -> bool {
1359 let label_offset = self.resolve_label_offset(fixup.label);
1360 label_offset != UNKNOWN_LABEL_OFFSET || fixup.deadline() < forced_threshold
1361 }
1362
1363 fn handle_fixup(
1364 &mut self,
1365 fixup: MachLabelFixup<I>,
1366 force_veneers: ForceVeneers,
1367 forced_threshold: CodeOffset,
1368 ) {
1369 let MachLabelFixup {
1370 label,
1371 offset,
1372 kind,
1373 } = fixup;
1374 let start = offset as usize;
1375 let end = (offset + kind.patch_size()) as usize;
1376 let label_offset = self.resolve_label_offset(label);
1377
1378 if label_offset != UNKNOWN_LABEL_OFFSET {
1379 // If the offset of the label for this fixup is known then
1380 // we're going to do something here-and-now. We're either going
1381 // to patch the original offset because it's an in-bounds jump,
1382 // or we're going to generate a veneer, patch the fixup to jump
1383 // to the veneer, and then keep going.
1384 //
1385 // If the label comes after the original fixup, then we should
1386 // be guaranteed that the jump is in-bounds. Otherwise there's
1387 // a bug somewhere because this method wasn't called soon
1388 // enough. All forward-jumps are tracked and should get veneers
1389 // before their deadline comes and they're unable to jump
1390 // further.
1391 //
1392 // Otherwise if the label is before the fixup, then that's a
1393 // backwards jump. If it's past the maximum negative range
1394 // then we'll emit a veneer that to jump forward to which can
1395 // then jump backwards.
1396 let veneer_required = if label_offset >= offset {
1397 assert!((label_offset - offset) <= kind.max_pos_range());
1398 false
1399 } else {
1400 (offset - label_offset) > kind.max_neg_range()
1401 };
1402 trace!(
1403 " -> label_offset = {}, known, required = {} (pos {} neg {})",
1404 label_offset,
1405 veneer_required,
1406 kind.max_pos_range(),
1407 kind.max_neg_range()
1408 );
1409
1410 if (force_veneers == ForceVeneers::Yes && kind.supports_veneer()) || veneer_required {
1411 self.emit_veneer(label, offset, kind);
1412 } else {
1413 let slice = &mut self.data[start..end];
1414 trace!("patching in-range! slice = {slice:?}; offset = {offset:#x}; label_offset = {label_offset:#x}");
1415 kind.patch(slice, offset, label_offset);
1416 }
1417 } else {
1418 // If the offset of this label is not known at this time then
1419 // that means that a veneer is required because after this
1420 // island the target can't be in range of the original target.
1421 assert!(forced_threshold - offset > kind.max_pos_range());
1422 self.emit_veneer(label, offset, kind);
1423 }
1424 }
1425
1426 /// Emits a "veneer" the `kind` code at `offset` to jump to `label`.
1427 ///
1428 /// This will generate extra machine code, using `kind`, to get a
1429 /// larger-jump-kind than `kind` allows. The code at `offset` is then
1430 /// patched to jump to our new code, and then the new code is enqueued for
1431 /// a fixup to get processed at some later time.
1432 fn emit_veneer(&mut self, label: MachLabel, offset: CodeOffset, kind: I::LabelUse) {
1433 // If this `kind` doesn't support a veneer then that's a bug in the
1434 // backend because we need to implement support for such a veneer.
1435 assert!(
1436 kind.supports_veneer(),
1437 "jump beyond the range of {kind:?} but a veneer isn't supported",
1438 );
1439
1440 // Allocate space for a veneer in the island.
1441 self.align_to(I::LabelUse::ALIGN);
1442 let veneer_offset = self.cur_offset();
1443 trace!("making a veneer at {}", veneer_offset);
1444 let start = offset as usize;
1445 let end = (offset + kind.patch_size()) as usize;
1446 let slice = &mut self.data[start..end];
1447 // Patch the original label use to refer to the veneer.
1448 trace!(
1449 "patching original at offset {} to veneer offset {}",
1450 offset,
1451 veneer_offset
1452 );
1453 kind.patch(slice, offset, veneer_offset);
1454 // Generate the veneer.
1455 let veneer_slice = self.get_appended_space(kind.veneer_size() as usize);
1456 let (veneer_fixup_off, veneer_label_use) =
1457 kind.generate_veneer(veneer_slice, veneer_offset);
1458 trace!(
1459 "generated veneer; fixup offset {}, label_use {:?}",
1460 veneer_fixup_off,
1461 veneer_label_use
1462 );
1463 // Register a new use of `label` with our new veneer fixup and
1464 // offset. This'll recalculate deadlines accordingly and
1465 // enqueue this fixup to get processed at some later
1466 // time.
1467 self.use_label_at_offset(veneer_fixup_off, label, veneer_label_use);
1468 }
1469
1470 fn finish_emission_maybe_forcing_veneers(
1471 &mut self,
1472 force_veneers: ForceVeneers,
1473 ctrl_plane: &mut ControlPlane,
1474 ) {
1475 while !self.pending_constants.is_empty()
1476 || !self.pending_traps.is_empty()
1477 || !self.fixup_records.is_empty()
1478 || !self.pending_fixup_records.is_empty()
1479 {
1480 // `emit_island()` will emit any pending veneers and constants, and
1481 // as a side-effect, will also take care of any fixups with resolved
1482 // labels eagerly.
1483 self.emit_island_maybe_forced(force_veneers, u32::MAX, ctrl_plane);
1484 }
1485
1486 // Ensure that all labels have been fixed up after the last island is emitted. This is a
1487 // full (release-mode) assert because an unresolved label means the emitted code is
1488 // incorrect.
1489 assert!(self.fixup_records.is_empty());
1490 assert!(self.pending_fixup_records.is_empty());
1491 }
1492
1493 /// Finish any deferred emissions and/or fixups.
1494 pub fn finish(
1495 mut self,
1496 constants: &VCodeConstants,
1497 ctrl_plane: &mut ControlPlane,
1498 ) -> MachBufferFinalized<Stencil> {
1499 let _tt = timing::vcode_emit_finish();
1500
1501 self.finish_emission_maybe_forcing_veneers(ForceVeneers::No, ctrl_plane);
1502
1503 let alignment = self.finish_constants(constants);
1504
1505 // Resolve all labels to their offsets.
1506 let finalized_relocs = self
1507 .relocs
1508 .iter()
1509 .map(|reloc| FinalizedMachReloc {
1510 offset: reloc.offset,
1511 kind: reloc.kind,
1512 addend: reloc.addend,
1513 target: match &reloc.target {
1514 RelocTarget::ExternalName(name) => {
1515 FinalizedRelocTarget::ExternalName(name.clone())
1516 }
1517 RelocTarget::Label(label) => {
1518 FinalizedRelocTarget::Func(self.resolve_label_offset(*label))
1519 }
1520 },
1521 })
1522 .collect();
1523
1524 let mut srclocs = self.srclocs;
1525 srclocs.sort_by_key(|entry| entry.start);
1526
1527 MachBufferFinalized {
1528 data: self.data,
1529 relocs: finalized_relocs,
1530 traps: self.traps,
1531 call_sites: self.call_sites,
1532 srclocs,
1533 user_stack_maps: self.user_stack_maps,
1534 unwind_info: self.unwind_info,
1535 alignment,
1536 }
1537 }
1538
1539 /// Add an external relocation at the given offset from current offset.
1540 pub fn add_reloc_at_offset<T: Into<RelocTarget> + Clone>(
1541 &mut self,
1542 offset: CodeOffset,
1543 kind: Reloc,
1544 target: &T,
1545 addend: Addend,
1546 ) {
1547 let target: RelocTarget = target.clone().into();
1548 // FIXME(#3277): This should use `I::LabelUse::from_reloc` to optionally
1549 // generate a label-use statement to track whether an island is possibly
1550 // needed to escape this function to actually get to the external name.
1551 // This is most likely to come up on AArch64 where calls between
1552 // functions use a 26-bit signed offset which gives +/- 64MB. This means
1553 // that if a function is 128MB in size and there's a call in the middle
1554 // it's impossible to reach the actual target. Also, while it's
1555 // technically possible to jump to the start of a function and then jump
1556 // further, island insertion below always inserts islands after
1557 // previously appended code so for Cranelift's own implementation this
1558 // is also a problem for 64MB functions on AArch64 which start with a
1559 // call instruction, those won't be able to escape.
1560 //
1561 // Ideally what needs to happen here is that a `LabelUse` is
1562 // transparently generated (or call-sites of this function are audited
1563 // to generate a `LabelUse` instead) and tracked internally. The actual
1564 // relocation would then change over time if and when a veneer is
1565 // inserted, where the relocation here would be patched by this
1566 // `MachBuffer` to jump to the veneer. The problem, though, is that all
1567 // this still needs to end up, in the case of a singular function,
1568 // generating a final relocation pointing either to this particular
1569 // relocation or to the veneer inserted. Additionally
1570 // `MachBuffer` needs the concept of a label which will never be
1571 // resolved, so `emit_island` doesn't trip over not actually ever
1572 // knowning what some labels are. Currently the loop in
1573 // `finish_emission_maybe_forcing_veneers` would otherwise infinitely
1574 // loop.
1575 //
1576 // For now this means that because relocs aren't tracked at all that
1577 // AArch64 functions have a rough size limits of 64MB. For now that's
1578 // somewhat reasonable and the failure mode is a panic in `MachBuffer`
1579 // when a relocation can't otherwise be resolved later, so it shouldn't
1580 // actually result in any memory unsafety or anything like that.
1581 self.relocs.push(MachReloc {
1582 offset: self.data.len() as CodeOffset + offset,
1583 kind,
1584 target,
1585 addend,
1586 });
1587 }
1588
1589 /// Add an external relocation at the current offset.
1590 pub fn add_reloc<T: Into<RelocTarget> + Clone>(
1591 &mut self,
1592 kind: Reloc,
1593 target: &T,
1594 addend: Addend,
1595 ) {
1596 self.add_reloc_at_offset(0, kind, target, addend);
1597 }
1598
1599 /// Add a trap record at the current offset.
1600 pub fn add_trap(&mut self, code: TrapCode) {
1601 self.traps.push(MachTrap {
1602 offset: self.data.len() as CodeOffset,
1603 code,
1604 });
1605 }
1606
1607 /// Add a call-site record at the current offset.
1608 pub fn add_call_site(&mut self) {
1609 self.call_sites.push(MachCallSite {
1610 ret_addr: self.data.len() as CodeOffset,
1611 });
1612 }
1613
1614 /// Add an unwind record at the current offset.
1615 pub fn add_unwind(&mut self, unwind: UnwindInst) {
1616 self.unwind_info.push((self.cur_offset(), unwind));
1617 }
1618
1619 /// Set the `SourceLoc` for code from this offset until the offset at the
1620 /// next call to `end_srcloc()`.
1621 /// Returns the current [CodeOffset] and [RelSourceLoc].
1622 pub fn start_srcloc(&mut self, loc: RelSourceLoc) -> (CodeOffset, RelSourceLoc) {
1623 let cur = (self.cur_offset(), loc);
1624 self.cur_srcloc = Some(cur);
1625 cur
1626 }
1627
1628 /// Mark the end of the `SourceLoc` segment started at the last
1629 /// `start_srcloc()` call.
1630 pub fn end_srcloc(&mut self) {
1631 let (start, loc) = self
1632 .cur_srcloc
1633 .take()
1634 .expect("end_srcloc() called without start_srcloc()");
1635 let end = self.cur_offset();
1636 // Skip zero-length extends.
1637 debug_assert!(end >= start);
1638 if end > start {
1639 self.srclocs.push(MachSrcLoc { start, end, loc });
1640 }
1641 }
1642
1643 /// Push a user stack map onto this buffer.
1644 ///
1645 /// The stack map is associated with the given `return_addr` code
1646 /// offset. This must be the PC for the instruction just *after* this stack
1647 /// map's associated instruction. For example in the sequence `call $foo;
1648 /// add r8, rax`, the `return_addr` must be the offset of the start of the
1649 /// `add` instruction.
1650 ///
1651 /// Stack maps must be pushed in sorted `return_addr` order.
1652 pub fn push_user_stack_map(
1653 &mut self,
1654 emit_state: &I::State,
1655 return_addr: CodeOffset,
1656 mut stack_map: ir::UserStackMap,
1657 ) {
1658 let span = emit_state.frame_layout().active_size();
1659 trace!("Adding user stack map @ {return_addr:#x} spanning {span} bytes: {stack_map:?}");
1660
1661 debug_assert!(
1662 self.user_stack_maps
1663 .last()
1664 .map_or(true, |(prev_addr, _, _)| *prev_addr < return_addr),
1665 "pushed stack maps out of order: {} is not less than {}",
1666 self.user_stack_maps.last().unwrap().0,
1667 return_addr,
1668 );
1669
1670 stack_map.finalize(emit_state.frame_layout().sp_to_sized_stack_slots());
1671 self.user_stack_maps.push((return_addr, span, stack_map));
1672 }
1673}
1674
1675impl<I: VCodeInst> Extend<u8> for MachBuffer<I> {
1676 fn extend<T: IntoIterator<Item = u8>>(&mut self, iter: T) {
1677 for b in iter {
1678 self.put1(b);
1679 }
1680 }
1681}
1682
1683impl<T: CompilePhase> MachBufferFinalized<T> {
1684 /// Get a list of source location mapping tuples in sorted-by-start-offset order.
1685 pub fn get_srclocs_sorted(&self) -> &[T::MachSrcLocType] {
1686 &self.srclocs[..]
1687 }
1688
1689 /// Get the total required size for the code.
1690 pub fn total_size(&self) -> CodeOffset {
1691 self.data.len() as CodeOffset
1692 }
1693
1694 /// Return the code in this mach buffer as a hex string for testing purposes.
1695 pub fn stringify_code_bytes(&self) -> String {
1696 // This is pretty lame, but whatever ..
1697 use std::fmt::Write;
1698 let mut s = String::with_capacity(self.data.len() * 2);
1699 for b in &self.data {
1700 write!(&mut s, "{b:02X}").unwrap();
1701 }
1702 s
1703 }
1704
1705 /// Get the code bytes.
1706 pub fn data(&self) -> &[u8] {
1707 // N.B.: we emit every section into the .text section as far as
1708 // the `CodeSink` is concerned; we do not bother to segregate
1709 // the contents into the actual program text, the jumptable and the
1710 // rodata (constant pool). This allows us to generate code assuming
1711 // that these will not be relocated relative to each other, and avoids
1712 // having to designate each section as belonging in one of the three
1713 // fixed categories defined by `CodeSink`. If this becomes a problem
1714 // later (e.g. because of memory permissions or similar), we can
1715 // add this designation and segregate the output; take care, however,
1716 // to add the appropriate relocations in this case.
1717
1718 &self.data[..]
1719 }
1720
1721 /// Get the list of external relocations for this code.
1722 pub fn relocs(&self) -> &[FinalizedMachReloc] {
1723 &self.relocs[..]
1724 }
1725
1726 /// Get the list of trap records for this code.
1727 pub fn traps(&self) -> &[MachTrap] {
1728 &self.traps[..]
1729 }
1730
1731 /// Get the user stack map metadata for this code.
1732 pub fn user_stack_maps(&self) -> &[(CodeOffset, u32, ir::UserStackMap)] {
1733 &self.user_stack_maps
1734 }
1735
1736 /// Take this buffer's user strack map metadata.
1737 pub fn take_user_stack_maps(&mut self) -> SmallVec<[(CodeOffset, u32, ir::UserStackMap); 8]> {
1738 mem::take(&mut self.user_stack_maps)
1739 }
1740
1741 /// Get the list of call sites for this code.
1742 pub fn call_sites(&self) -> &[MachCallSite] {
1743 &self.call_sites[..]
1744 }
1745}
1746
1747/// Metadata about a constant.
1748struct MachBufferConstant {
1749 /// A label which has not yet been bound which can be used for this
1750 /// constant.
1751 ///
1752 /// This is lazily created when a label is requested for a constant and is
1753 /// cleared when a constant is emitted.
1754 upcoming_label: Option<MachLabel>,
1755 /// Required alignment.
1756 align: CodeOffset,
1757 /// The byte size of this constant.
1758 size: usize,
1759}
1760
1761/// A trap that is deferred to the next time an island is emitted for either
1762/// traps, constants, or fixups.
1763struct MachLabelTrap {
1764 /// This label will refer to the trap's offset.
1765 label: MachLabel,
1766 /// The code associated with this trap.
1767 code: TrapCode,
1768 /// An optional source location to assign for this trap.
1769 loc: Option<RelSourceLoc>,
1770}
1771
1772/// A fixup to perform on the buffer once code is emitted. Fixups always refer
1773/// to labels and patch the code based on label offsets. Hence, they are like
1774/// relocations, but internal to one buffer.
1775#[derive(Debug)]
1776struct MachLabelFixup<I: VCodeInst> {
1777 /// The label whose offset controls this fixup.
1778 label: MachLabel,
1779 /// The offset to fix up / patch to refer to this label.
1780 offset: CodeOffset,
1781 /// The kind of fixup. This is architecture-specific; each architecture may have,
1782 /// e.g., several types of branch instructions, each with differently-sized
1783 /// offset fields and different places within the instruction to place the
1784 /// bits.
1785 kind: I::LabelUse,
1786}
1787
1788impl<I: VCodeInst> MachLabelFixup<I> {
1789 fn deadline(&self) -> CodeOffset {
1790 self.offset.saturating_add(self.kind.max_pos_range())
1791 }
1792}
1793
1794impl<I: VCodeInst> PartialEq for MachLabelFixup<I> {
1795 fn eq(&self, other: &Self) -> bool {
1796 self.deadline() == other.deadline()
1797 }
1798}
1799
1800impl<I: VCodeInst> Eq for MachLabelFixup<I> {}
1801
1802impl<I: VCodeInst> PartialOrd for MachLabelFixup<I> {
1803 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1804 Some(self.cmp(other))
1805 }
1806}
1807
1808impl<I: VCodeInst> Ord for MachLabelFixup<I> {
1809 fn cmp(&self, other: &Self) -> Ordering {
1810 other.deadline().cmp(&self.deadline())
1811 }
1812}
1813
1814/// A relocation resulting from a compilation.
1815#[derive(Clone, Debug, PartialEq)]
1816#[cfg_attr(
1817 feature = "enable-serde",
1818 derive(serde_derive::Serialize, serde_derive::Deserialize)
1819)]
1820pub struct MachRelocBase<T> {
1821 /// The offset at which the relocation applies, *relative to the
1822 /// containing section*.
1823 pub offset: CodeOffset,
1824 /// The kind of relocation.
1825 pub kind: Reloc,
1826 /// The external symbol / name to which this relocation refers.
1827 pub target: T,
1828 /// The addend to add to the symbol value.
1829 pub addend: i64,
1830}
1831
1832type MachReloc = MachRelocBase<RelocTarget>;
1833
1834/// A relocation resulting from a compilation.
1835pub type FinalizedMachReloc = MachRelocBase<FinalizedRelocTarget>;
1836
1837/// A Relocation target
1838#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1839pub enum RelocTarget {
1840 /// Points to an [ExternalName] outside the current function.
1841 ExternalName(ExternalName),
1842 /// Points to a [MachLabel] inside this function.
1843 /// This is different from [MachLabelFixup] in that both the relocation and the
1844 /// label will be emitted and are only resolved at link time.
1845 ///
1846 /// There is no reason to prefer this over [MachLabelFixup] unless the ABI requires it.
1847 Label(MachLabel),
1848}
1849
1850impl From<ExternalName> for RelocTarget {
1851 fn from(name: ExternalName) -> Self {
1852 Self::ExternalName(name)
1853 }
1854}
1855
1856impl From<MachLabel> for RelocTarget {
1857 fn from(label: MachLabel) -> Self {
1858 Self::Label(label)
1859 }
1860}
1861
1862/// A Relocation target
1863#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1864#[cfg_attr(
1865 feature = "enable-serde",
1866 derive(serde_derive::Serialize, serde_derive::Deserialize)
1867)]
1868pub enum FinalizedRelocTarget {
1869 /// Points to an [ExternalName] outside the current function.
1870 ExternalName(ExternalName),
1871 /// Points to a [CodeOffset] from the start of the current function.
1872 Func(CodeOffset),
1873}
1874
1875impl FinalizedRelocTarget {
1876 /// Returns a display for the current [FinalizedRelocTarget], with extra context to prettify the
1877 /// output.
1878 pub fn display<'a>(&'a self, params: Option<&'a FunctionParameters>) -> String {
1879 match self {
1880 FinalizedRelocTarget::ExternalName(name) => format!("{}", name.display(params)),
1881 FinalizedRelocTarget::Func(offset) => format!("func+{offset}"),
1882 }
1883 }
1884}
1885
1886/// A trap record resulting from a compilation.
1887#[derive(Clone, Debug, PartialEq)]
1888#[cfg_attr(
1889 feature = "enable-serde",
1890 derive(serde_derive::Serialize, serde_derive::Deserialize)
1891)]
1892pub struct MachTrap {
1893 /// The offset at which the trap instruction occurs, *relative to the
1894 /// containing section*.
1895 pub offset: CodeOffset,
1896 /// The trap code.
1897 pub code: TrapCode,
1898}
1899
1900/// A call site record resulting from a compilation.
1901#[derive(Clone, Debug, PartialEq)]
1902#[cfg_attr(
1903 feature = "enable-serde",
1904 derive(serde_derive::Serialize, serde_derive::Deserialize)
1905)]
1906pub struct MachCallSite {
1907 /// The offset of the call's return address, *relative to the containing section*.
1908 pub ret_addr: CodeOffset,
1909}
1910
1911/// A source-location mapping resulting from a compilation.
1912#[derive(PartialEq, Debug, Clone)]
1913#[cfg_attr(
1914 feature = "enable-serde",
1915 derive(serde_derive::Serialize, serde_derive::Deserialize)
1916)]
1917pub struct MachSrcLoc<T: CompilePhase> {
1918 /// The start of the region of code corresponding to a source location.
1919 /// This is relative to the start of the function, not to the start of the
1920 /// section.
1921 pub start: CodeOffset,
1922 /// The end of the region of code corresponding to a source location.
1923 /// This is relative to the start of the section, not to the start of the
1924 /// section.
1925 pub end: CodeOffset,
1926 /// The source location.
1927 pub loc: T::SourceLocType,
1928}
1929
1930impl MachSrcLoc<Stencil> {
1931 fn apply_base_srcloc(self, base_srcloc: SourceLoc) -> MachSrcLoc<Final> {
1932 MachSrcLoc {
1933 start: self.start,
1934 end: self.end,
1935 loc: self.loc.expand(base_srcloc),
1936 }
1937 }
1938}
1939
1940/// Record of branch instruction in the buffer, to facilitate editing.
1941#[derive(Clone, Debug)]
1942struct MachBranch {
1943 start: CodeOffset,
1944 end: CodeOffset,
1945 target: MachLabel,
1946 fixup: usize,
1947 inverted: Option<SmallVec<[u8; 8]>>,
1948 /// All labels pointing to the start of this branch. For correctness, this
1949 /// *must* be complete (i.e., must contain all labels whose resolved offsets
1950 /// are at the start of this branch): we rely on being able to redirect all
1951 /// labels that could jump to this branch before removing it, if it is
1952 /// otherwise unreachable.
1953 labels_at_this_branch: SmallVec<[MachLabel; 4]>,
1954}
1955
1956impl MachBranch {
1957 fn is_cond(&self) -> bool {
1958 self.inverted.is_some()
1959 }
1960 fn is_uncond(&self) -> bool {
1961 self.inverted.is_none()
1962 }
1963}
1964
1965/// Implementation of the `TextSectionBuilder` trait backed by `MachBuffer`.
1966///
1967/// Note that `MachBuffer` was primarily written for intra-function references
1968/// of jumps between basic blocks, but it's also quite usable for entire text
1969/// sections and resolving references between functions themselves. This
1970/// builder interprets "blocks" as labeled functions for the purposes of
1971/// resolving labels internally in the buffer.
1972pub struct MachTextSectionBuilder<I: VCodeInst> {
1973 buf: MachBuffer<I>,
1974 next_func: usize,
1975 force_veneers: ForceVeneers,
1976}
1977
1978impl<I: VCodeInst> MachTextSectionBuilder<I> {
1979 /// Creates a new text section builder which will have `num_funcs` functions
1980 /// pushed into it.
1981 pub fn new(num_funcs: usize) -> MachTextSectionBuilder<I> {
1982 let mut buf = MachBuffer::new();
1983 buf.reserve_labels_for_blocks(num_funcs);
1984 MachTextSectionBuilder {
1985 buf,
1986 next_func: 0,
1987 force_veneers: ForceVeneers::No,
1988 }
1989 }
1990}
1991
1992impl<I: VCodeInst> TextSectionBuilder for MachTextSectionBuilder<I> {
1993 fn append(
1994 &mut self,
1995 labeled: bool,
1996 func: &[u8],
1997 align: u32,
1998 ctrl_plane: &mut ControlPlane,
1999 ) -> u64 {
2000 // Conditionally emit an island if it's necessary to resolve jumps
2001 // between functions which are too far away.
2002 let size = func.len() as u32;
2003 if self.force_veneers == ForceVeneers::Yes || self.buf.island_needed(size) {
2004 self.buf
2005 .emit_island_maybe_forced(self.force_veneers, size, ctrl_plane);
2006 }
2007
2008 self.buf.align_to(align);
2009 let pos = self.buf.cur_offset();
2010 if labeled {
2011 self.buf.bind_label(
2012 MachLabel::from_block(BlockIndex::new(self.next_func)),
2013 ctrl_plane,
2014 );
2015 self.next_func += 1;
2016 }
2017 self.buf.put_data(func);
2018 u64::from(pos)
2019 }
2020
2021 fn resolve_reloc(&mut self, offset: u64, reloc: Reloc, addend: Addend, target: usize) -> bool {
2022 crate::trace!(
2023 "Resolving relocation @ {offset:#x} + {addend:#x} to target {target} of kind {reloc:?}"
2024 );
2025 let label = MachLabel::from_block(BlockIndex::new(target));
2026 let offset = u32::try_from(offset).unwrap();
2027 match I::LabelUse::from_reloc(reloc, addend) {
2028 Some(label_use) => {
2029 self.buf.use_label_at_offset(offset, label, label_use);
2030 true
2031 }
2032 None => false,
2033 }
2034 }
2035
2036 fn force_veneers(&mut self) {
2037 self.force_veneers = ForceVeneers::Yes;
2038 }
2039
2040 fn finish(&mut self, ctrl_plane: &mut ControlPlane) -> Vec<u8> {
2041 // Double-check all functions were pushed.
2042 assert_eq!(self.next_func, self.buf.label_offsets.len());
2043
2044 // Finish up any veneers, if necessary.
2045 self.buf
2046 .finish_emission_maybe_forcing_veneers(self.force_veneers, ctrl_plane);
2047
2048 // We don't need the data any more, so return it to the caller.
2049 mem::take(&mut self.buf.data).into_vec()
2050 }
2051}
2052
2053// We use an actual instruction definition to do tests, so we depend on the `arm64` feature here.
2054#[cfg(all(test, feature = "arm64"))]
2055mod test {
2056 use cranelift_entity::EntityRef as _;
2057
2058 use super::*;
2059 use crate::ir::UserExternalNameRef;
2060 use crate::isa::aarch64::inst::xreg;
2061 use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst};
2062 use crate::machinst::{MachInstEmit, MachInstEmitState};
2063 use crate::settings;
2064
2065 fn label(n: u32) -> MachLabel {
2066 MachLabel::from_block(BlockIndex::new(n as usize))
2067 }
2068 fn target(n: u32) -> BranchTarget {
2069 BranchTarget::Label(label(n))
2070 }
2071
2072 #[test]
2073 fn test_elide_jump_to_next() {
2074 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2075 let mut buf = MachBuffer::new();
2076 let mut state = <Inst as MachInstEmit>::State::default();
2077 let constants = Default::default();
2078
2079 buf.reserve_labels_for_blocks(2);
2080 buf.bind_label(label(0), state.ctrl_plane_mut());
2081 let inst = Inst::Jump { dest: target(1) };
2082 inst.emit(&mut buf, &info, &mut state);
2083 buf.bind_label(label(1), state.ctrl_plane_mut());
2084 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2085 assert_eq!(0, buf.total_size());
2086 }
2087
2088 #[test]
2089 fn test_elide_trivial_jump_blocks() {
2090 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2091 let mut buf = MachBuffer::new();
2092 let mut state = <Inst as MachInstEmit>::State::default();
2093 let constants = Default::default();
2094
2095 buf.reserve_labels_for_blocks(4);
2096
2097 buf.bind_label(label(0), state.ctrl_plane_mut());
2098 let inst = Inst::CondBr {
2099 kind: CondBrKind::NotZero(xreg(0)),
2100 taken: target(1),
2101 not_taken: target(2),
2102 };
2103 inst.emit(&mut buf, &info, &mut state);
2104
2105 buf.bind_label(label(1), state.ctrl_plane_mut());
2106 let inst = Inst::Jump { dest: target(3) };
2107 inst.emit(&mut buf, &info, &mut state);
2108
2109 buf.bind_label(label(2), state.ctrl_plane_mut());
2110 let inst = Inst::Jump { dest: target(3) };
2111 inst.emit(&mut buf, &info, &mut state);
2112
2113 buf.bind_label(label(3), state.ctrl_plane_mut());
2114
2115 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2116 assert_eq!(0, buf.total_size());
2117 }
2118
2119 #[test]
2120 fn test_flip_cond() {
2121 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2122 let mut buf = MachBuffer::new();
2123 let mut state = <Inst as MachInstEmit>::State::default();
2124 let constants = Default::default();
2125
2126 buf.reserve_labels_for_blocks(4);
2127
2128 buf.bind_label(label(0), state.ctrl_plane_mut());
2129 let inst = Inst::CondBr {
2130 kind: CondBrKind::Zero(xreg(0)),
2131 taken: target(1),
2132 not_taken: target(2),
2133 };
2134 inst.emit(&mut buf, &info, &mut state);
2135
2136 buf.bind_label(label(1), state.ctrl_plane_mut());
2137 let inst = Inst::Nop4;
2138 inst.emit(&mut buf, &info, &mut state);
2139
2140 buf.bind_label(label(2), state.ctrl_plane_mut());
2141 let inst = Inst::Udf {
2142 trap_code: TrapCode::Interrupt,
2143 };
2144 inst.emit(&mut buf, &info, &mut state);
2145
2146 buf.bind_label(label(3), state.ctrl_plane_mut());
2147
2148 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2149
2150 let mut buf2 = MachBuffer::new();
2151 let mut state = Default::default();
2152 let inst = Inst::TrapIf {
2153 kind: CondBrKind::NotZero(xreg(0)),
2154 trap_code: TrapCode::Interrupt,
2155 };
2156 inst.emit(&mut buf2, &info, &mut state);
2157 let inst = Inst::Nop4;
2158 inst.emit(&mut buf2, &info, &mut state);
2159
2160 let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2161
2162 assert_eq!(buf.data, buf2.data);
2163 }
2164
2165 #[test]
2166 fn test_island() {
2167 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2168 let mut buf = MachBuffer::new();
2169 let mut state = <Inst as MachInstEmit>::State::default();
2170 let constants = Default::default();
2171
2172 buf.reserve_labels_for_blocks(4);
2173
2174 buf.bind_label(label(0), state.ctrl_plane_mut());
2175 let inst = Inst::CondBr {
2176 kind: CondBrKind::NotZero(xreg(0)),
2177 taken: target(2),
2178 not_taken: target(3),
2179 };
2180 inst.emit(&mut buf, &info, &mut state);
2181
2182 buf.bind_label(label(1), state.ctrl_plane_mut());
2183 while buf.cur_offset() < 2000000 {
2184 if buf.island_needed(0) {
2185 buf.emit_island(0, state.ctrl_plane_mut());
2186 }
2187 let inst = Inst::Nop4;
2188 inst.emit(&mut buf, &info, &mut state);
2189 }
2190
2191 buf.bind_label(label(2), state.ctrl_plane_mut());
2192 let inst = Inst::Nop4;
2193 inst.emit(&mut buf, &info, &mut state);
2194
2195 buf.bind_label(label(3), state.ctrl_plane_mut());
2196 let inst = Inst::Nop4;
2197 inst.emit(&mut buf, &info, &mut state);
2198
2199 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2200
2201 assert_eq!(2000000 + 8, buf.total_size());
2202
2203 let mut buf2 = MachBuffer::new();
2204 let mut state = Default::default();
2205 let inst = Inst::CondBr {
2206 kind: CondBrKind::NotZero(xreg(0)),
2207
2208 // This conditionally taken branch has a 19-bit constant, shifted
2209 // to the left by two, giving us a 21-bit range in total. Half of
2210 // this range positive so the we should be around 1 << 20 bytes
2211 // away for our jump target.
2212 //
2213 // There are two pending fixups by the time we reach this point,
2214 // one for this 19-bit jump and one for the unconditional 26-bit
2215 // jump below. A 19-bit veneer is 4 bytes large and the 26-bit
2216 // veneer is 20 bytes large, which means that pessimistically
2217 // assuming we'll need two veneers. Currently each veneer is
2218 // pessimistically assumed to be the maximal size which means we
2219 // need 40 bytes of extra space, meaning that the actual island
2220 // should come 40-bytes before the deadline.
2221 taken: BranchTarget::ResolvedOffset((1 << 20) - 20 - 20),
2222
2223 // This branch is in-range so no veneers should be needed, it should
2224 // go directly to the target.
2225 not_taken: BranchTarget::ResolvedOffset(2000000 + 4 - 4),
2226 };
2227 inst.emit(&mut buf2, &info, &mut state);
2228
2229 let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2230
2231 assert_eq!(&buf.data[0..8], &buf2.data[..]);
2232 }
2233
2234 #[test]
2235 fn test_island_backward() {
2236 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2237 let mut buf = MachBuffer::new();
2238 let mut state = <Inst as MachInstEmit>::State::default();
2239 let constants = Default::default();
2240
2241 buf.reserve_labels_for_blocks(4);
2242
2243 buf.bind_label(label(0), state.ctrl_plane_mut());
2244 let inst = Inst::Nop4;
2245 inst.emit(&mut buf, &info, &mut state);
2246
2247 buf.bind_label(label(1), state.ctrl_plane_mut());
2248 let inst = Inst::Nop4;
2249 inst.emit(&mut buf, &info, &mut state);
2250
2251 buf.bind_label(label(2), state.ctrl_plane_mut());
2252 while buf.cur_offset() < 2000000 {
2253 let inst = Inst::Nop4;
2254 inst.emit(&mut buf, &info, &mut state);
2255 }
2256
2257 buf.bind_label(label(3), state.ctrl_plane_mut());
2258 let inst = Inst::CondBr {
2259 kind: CondBrKind::NotZero(xreg(0)),
2260 taken: target(0),
2261 not_taken: target(1),
2262 };
2263 inst.emit(&mut buf, &info, &mut state);
2264
2265 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2266
2267 assert_eq!(2000000 + 12, buf.total_size());
2268
2269 let mut buf2 = MachBuffer::new();
2270 let mut state = Default::default();
2271 let inst = Inst::CondBr {
2272 kind: CondBrKind::NotZero(xreg(0)),
2273 taken: BranchTarget::ResolvedOffset(8),
2274 not_taken: BranchTarget::ResolvedOffset(4 - (2000000 + 4)),
2275 };
2276 inst.emit(&mut buf2, &info, &mut state);
2277 let inst = Inst::Jump {
2278 dest: BranchTarget::ResolvedOffset(-(2000000 + 8)),
2279 };
2280 inst.emit(&mut buf2, &info, &mut state);
2281
2282 let buf2 = buf2.finish(&constants, state.ctrl_plane_mut());
2283
2284 assert_eq!(&buf.data[2000000..], &buf2.data[..]);
2285 }
2286
2287 #[test]
2288 fn test_multiple_redirect() {
2289 // label0:
2290 // cbz x0, label1
2291 // b label2
2292 // label1:
2293 // b label3
2294 // label2:
2295 // nop
2296 // nop
2297 // b label0
2298 // label3:
2299 // b label4
2300 // label4:
2301 // b label5
2302 // label5:
2303 // b label7
2304 // label6:
2305 // nop
2306 // label7:
2307 // ret
2308 //
2309 // -- should become:
2310 //
2311 // label0:
2312 // cbz x0, label7
2313 // label2:
2314 // nop
2315 // nop
2316 // b label0
2317 // label6:
2318 // nop
2319 // label7:
2320 // ret
2321
2322 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2323 let mut buf = MachBuffer::new();
2324 let mut state = <Inst as MachInstEmit>::State::default();
2325 let constants = Default::default();
2326
2327 buf.reserve_labels_for_blocks(8);
2328
2329 buf.bind_label(label(0), state.ctrl_plane_mut());
2330 let inst = Inst::CondBr {
2331 kind: CondBrKind::Zero(xreg(0)),
2332 taken: target(1),
2333 not_taken: target(2),
2334 };
2335 inst.emit(&mut buf, &info, &mut state);
2336
2337 buf.bind_label(label(1), state.ctrl_plane_mut());
2338 let inst = Inst::Jump { dest: target(3) };
2339 inst.emit(&mut buf, &info, &mut state);
2340
2341 buf.bind_label(label(2), state.ctrl_plane_mut());
2342 let inst = Inst::Nop4;
2343 inst.emit(&mut buf, &info, &mut state);
2344 inst.emit(&mut buf, &info, &mut state);
2345 let inst = Inst::Jump { dest: target(0) };
2346 inst.emit(&mut buf, &info, &mut state);
2347
2348 buf.bind_label(label(3), state.ctrl_plane_mut());
2349 let inst = Inst::Jump { dest: target(4) };
2350 inst.emit(&mut buf, &info, &mut state);
2351
2352 buf.bind_label(label(4), state.ctrl_plane_mut());
2353 let inst = Inst::Jump { dest: target(5) };
2354 inst.emit(&mut buf, &info, &mut state);
2355
2356 buf.bind_label(label(5), state.ctrl_plane_mut());
2357 let inst = Inst::Jump { dest: target(7) };
2358 inst.emit(&mut buf, &info, &mut state);
2359
2360 buf.bind_label(label(6), state.ctrl_plane_mut());
2361 let inst = Inst::Nop4;
2362 inst.emit(&mut buf, &info, &mut state);
2363
2364 buf.bind_label(label(7), state.ctrl_plane_mut());
2365 let inst = Inst::Ret {};
2366 inst.emit(&mut buf, &info, &mut state);
2367
2368 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2369
2370 let golden_data = vec![
2371 0xa0, 0x00, 0x00, 0xb4, // cbz x0, 0x14
2372 0x1f, 0x20, 0x03, 0xd5, // nop
2373 0x1f, 0x20, 0x03, 0xd5, // nop
2374 0xfd, 0xff, 0xff, 0x17, // b 0
2375 0x1f, 0x20, 0x03, 0xd5, // nop
2376 0xc0, 0x03, 0x5f, 0xd6, // ret
2377 ];
2378
2379 assert_eq!(&golden_data[..], &buf.data[..]);
2380 }
2381
2382 #[test]
2383 fn test_handle_branch_cycle() {
2384 // label0:
2385 // b label1
2386 // label1:
2387 // b label2
2388 // label2:
2389 // b label3
2390 // label3:
2391 // b label4
2392 // label4:
2393 // b label1 // note: not label0 (to make it interesting).
2394 //
2395 // -- should become:
2396 //
2397 // label0, label1, ..., label4:
2398 // b label0
2399 let info = EmitInfo::new(settings::Flags::new(settings::builder()));
2400 let mut buf = MachBuffer::new();
2401 let mut state = <Inst as MachInstEmit>::State::default();
2402 let constants = Default::default();
2403
2404 buf.reserve_labels_for_blocks(5);
2405
2406 buf.bind_label(label(0), state.ctrl_plane_mut());
2407 let inst = Inst::Jump { dest: target(1) };
2408 inst.emit(&mut buf, &info, &mut state);
2409
2410 buf.bind_label(label(1), state.ctrl_plane_mut());
2411 let inst = Inst::Jump { dest: target(2) };
2412 inst.emit(&mut buf, &info, &mut state);
2413
2414 buf.bind_label(label(2), state.ctrl_plane_mut());
2415 let inst = Inst::Jump { dest: target(3) };
2416 inst.emit(&mut buf, &info, &mut state);
2417
2418 buf.bind_label(label(3), state.ctrl_plane_mut());
2419 let inst = Inst::Jump { dest: target(4) };
2420 inst.emit(&mut buf, &info, &mut state);
2421
2422 buf.bind_label(label(4), state.ctrl_plane_mut());
2423 let inst = Inst::Jump { dest: target(1) };
2424 inst.emit(&mut buf, &info, &mut state);
2425
2426 let buf = buf.finish(&constants, state.ctrl_plane_mut());
2427
2428 let golden_data = vec![
2429 0x00, 0x00, 0x00, 0x14, // b 0
2430 ];
2431
2432 assert_eq!(&golden_data[..], &buf.data[..]);
2433 }
2434
2435 #[test]
2436 fn metadata_records() {
2437 let mut buf = MachBuffer::<Inst>::new();
2438 let ctrl_plane = &mut Default::default();
2439 let constants = Default::default();
2440
2441 buf.reserve_labels_for_blocks(1);
2442
2443 buf.bind_label(label(0), ctrl_plane);
2444 buf.put1(1);
2445 buf.add_trap(TrapCode::HeapOutOfBounds);
2446 buf.put1(2);
2447 buf.add_trap(TrapCode::IntegerOverflow);
2448 buf.add_trap(TrapCode::IntegerDivisionByZero);
2449 buf.add_call_site();
2450 buf.add_reloc(
2451 Reloc::Abs4,
2452 &ExternalName::User(UserExternalNameRef::new(0)),
2453 0,
2454 );
2455 buf.put1(3);
2456 buf.add_reloc(
2457 Reloc::Abs8,
2458 &ExternalName::User(UserExternalNameRef::new(1)),
2459 1,
2460 );
2461 buf.put1(4);
2462
2463 let buf = buf.finish(&constants, ctrl_plane);
2464
2465 assert_eq!(buf.data(), &[1, 2, 3, 4]);
2466 assert_eq!(
2467 buf.traps()
2468 .iter()
2469 .map(|trap| (trap.offset, trap.code))
2470 .collect::<Vec<_>>(),
2471 vec![
2472 (1, TrapCode::HeapOutOfBounds),
2473 (2, TrapCode::IntegerOverflow),
2474 (2, TrapCode::IntegerDivisionByZero)
2475 ]
2476 );
2477 assert_eq!(
2478 buf.call_sites()
2479 .iter()
2480 .map(|call_site| call_site.ret_addr)
2481 .collect::<Vec<_>>(),
2482 vec![2]
2483 );
2484 assert_eq!(
2485 buf.relocs()
2486 .iter()
2487 .map(|reloc| (reloc.offset, reloc.kind))
2488 .collect::<Vec<_>>(),
2489 vec![(2, Reloc::Abs4), (3, Reloc::Abs8)]
2490 );
2491 }
2492}