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