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