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