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