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