1 //! This implements the VCode container: a CFG of Insts that have been lowered. 2 //! 3 //! VCode is virtual-register code. An instruction in VCode is almost a machine 4 //! instruction; however, its register slots can refer to virtual registers in 5 //! addition to real machine registers. 6 //! 7 //! VCode is structured with traditional basic blocks, and 8 //! each block must be terminated by an unconditional branch (one target), a 9 //! conditional branch (two targets), or a return (no targets). Note that this 10 //! slightly differs from the machine code of most ISAs: in most ISAs, a 11 //! conditional branch has one target (and the not-taken case falls through). 12 //! However, we expect that machine backends will elide branches to the following 13 //! block (i.e., zero-offset jumps), and will be able to codegen a branch-cond / 14 //! branch-uncond pair if *both* targets are not fallthrough. This allows us to 15 //! play with layout prior to final binary emission, as well, if we want. 16 //! 17 //! See the main module comment in `mod.rs` for more details on the VCode-based 18 //! backend pipeline. 19 20 use crate::fx::FxHashMap; 21 use crate::ir::{self, types, Constant, ConstantData, SourceLoc}; 22 use crate::machinst::*; 23 use crate::settings; 24 use crate::timing; 25 use regalloc::Function as RegallocFunction; 26 use regalloc::Set as RegallocSet; 27 use regalloc::{ 28 BlockIx, InstIx, PrettyPrint, Range, RegAllocResult, RegClass, RegUsageCollector, 29 RegUsageMapper, SpillSlot, StackmapRequestInfo, 30 }; 31 32 use alloc::boxed::Box; 33 use alloc::{borrow::Cow, vec::Vec}; 34 use cranelift_entity::{entity_impl, Keys, PrimaryMap}; 35 use std::cell::RefCell; 36 use std::collections::HashMap; 37 use std::fmt; 38 use std::iter; 39 use std::string::String; 40 41 /// Index referring to an instruction in VCode. 42 pub type InsnIndex = u32; 43 /// Index referring to a basic block in VCode. 44 pub type BlockIndex = u32; 45 46 /// VCodeInst wraps all requirements for a MachInst to be in VCode: it must be 47 /// a `MachInst` and it must be able to emit itself at least to a `SizeCodeSink`. 48 pub trait VCodeInst: MachInst + MachInstEmit {} 49 impl<I: MachInst + MachInstEmit> VCodeInst for I {} 50 51 /// A function in "VCode" (virtualized-register code) form, after lowering. 52 /// This is essentially a standard CFG of basic blocks, where each basic block 53 /// consists of lowered instructions produced by the machine-specific backend. 54 pub struct VCode<I: VCodeInst> { 55 /// Function liveins. 56 liveins: RegallocSet<RealReg>, 57 58 /// Function liveouts. 59 liveouts: RegallocSet<RealReg>, 60 61 /// VReg IR-level types. 62 vreg_types: Vec<Type>, 63 64 /// Do we have any ref values among our vregs? 65 have_ref_values: bool, 66 67 /// Lowered machine instructions in order corresponding to the original IR. 68 insts: Vec<I>, 69 70 /// Source locations for each instruction. (`SourceLoc` is a `u32`, so it is 71 /// reasonable to keep one of these per instruction.) 72 srclocs: Vec<SourceLoc>, 73 74 /// Entry block. 75 entry: BlockIndex, 76 77 /// Block instruction indices. 78 block_ranges: Vec<(InsnIndex, InsnIndex)>, 79 80 /// Block successors: index range in the successor-list below. 81 block_succ_range: Vec<(usize, usize)>, 82 83 /// Block successor lists, concatenated into one Vec. The `block_succ_range` 84 /// list of tuples above gives (start, end) ranges within this list that 85 /// correspond to each basic block's successors. 86 block_succs: Vec<BlockIx>, 87 88 /// Block-order information. 89 block_order: BlockLoweringOrder, 90 91 /// ABI object. 92 abi: Box<dyn ABICallee<I = I>>, 93 94 /// Constant information used during code emission. This should be 95 /// immutable across function compilations within the same module. 96 emit_info: I::Info, 97 98 /// Safepoint instruction indices. Filled in post-regalloc. (Prior to 99 /// regalloc, the safepoint instructions are listed in the separate 100 /// `StackmapRequestInfo` held separate from the `VCode`.) 101 safepoint_insns: Vec<InsnIndex>, 102 103 /// For each safepoint entry in `safepoint_insns`, a list of `SpillSlot`s. 104 /// These are used to generate actual stack maps at emission. Filled in 105 /// post-regalloc. 106 safepoint_slots: Vec<Vec<SpillSlot>>, 107 108 /// Do we generate debug info? 109 generate_debug_info: bool, 110 111 /// Instruction end offsets, instruction indices at each label, 112 /// total buffer size, and start of cold code. Only present if 113 /// `generate_debug_info` is set. 114 insts_layout: RefCell<InstsLayoutInfo>, 115 116 /// Constants. 117 constants: VCodeConstants, 118 119 /// Are any debug value-labels present? If not, we can skip the 120 /// post-emission analysis. 121 has_value_labels: bool, 122 } 123 124 #[derive(Debug, Default)] 125 pub(crate) struct InstsLayoutInfo { 126 pub(crate) inst_end_offsets: Vec<CodeOffset>, 127 pub(crate) label_inst_indices: Vec<CodeOffset>, 128 pub(crate) start_of_cold_code: Option<CodeOffset>, 129 } 130 131 /// A builder for a VCode function body. This builder is designed for the 132 /// lowering approach that we take: we traverse basic blocks in forward 133 /// (original IR) order, but within each basic block, we generate code from 134 /// bottom to top; and within each IR instruction that we visit in this reverse 135 /// order, we emit machine instructions in *forward* order again. 136 /// 137 /// Hence, to produce the final instructions in proper order, we perform two 138 /// swaps. First, the machine instructions (`I` instances) are produced in 139 /// forward order for an individual IR instruction. Then these are *reversed* 140 /// and concatenated to `bb_insns` at the end of the IR instruction lowering. 141 /// The `bb_insns` vec will thus contain all machine instructions for a basic 142 /// block, in reverse order. Finally, when we're done with a basic block, we 143 /// reverse the whole block's vec of instructions again, and concatenate onto 144 /// the VCode's insts. 145 pub struct VCodeBuilder<I: VCodeInst> { 146 /// In-progress VCode. 147 vcode: VCode<I>, 148 149 /// In-progress stack map-request info. 150 stack_map_info: StackmapRequestInfo, 151 152 /// Index of the last block-start in the vcode. 153 block_start: InsnIndex, 154 155 /// Start of succs for the current block in the concatenated succs list. 156 succ_start: usize, 157 158 /// Current source location. 159 cur_srcloc: SourceLoc, 160 } 161 162 impl<I: VCodeInst> VCodeBuilder<I> { 163 /// Create a new VCodeBuilder. 164 pub fn new( 165 abi: Box<dyn ABICallee<I = I>>, 166 emit_info: I::Info, 167 block_order: BlockLoweringOrder, 168 constants: VCodeConstants, 169 ) -> VCodeBuilder<I> { 170 let reftype_class = I::ref_type_regclass(abi.flags()); 171 let vcode = VCode::new( 172 abi, 173 emit_info, 174 block_order, 175 constants, 176 /* generate_debug_info = */ true, 177 ); 178 let stack_map_info = StackmapRequestInfo { 179 reftype_class, 180 reftyped_vregs: vec![], 181 safepoint_insns: vec![], 182 }; 183 184 VCodeBuilder { 185 vcode, 186 stack_map_info, 187 block_start: 0, 188 succ_start: 0, 189 cur_srcloc: SourceLoc::default(), 190 } 191 } 192 193 /// Access the ABI object. 194 pub fn abi(&mut self) -> &mut dyn ABICallee<I = I> { 195 &mut *self.vcode.abi 196 } 197 198 /// Access to the BlockLoweringOrder object. 199 pub fn block_order(&self) -> &BlockLoweringOrder { 200 &self.vcode.block_order 201 } 202 203 /// Set the type of a VReg. 204 pub fn set_vreg_type(&mut self, vreg: VirtualReg, ty: Type) { 205 if self.vcode.vreg_types.len() <= vreg.get_index() { 206 self.vcode 207 .vreg_types 208 .resize(vreg.get_index() + 1, ir::types::I8); 209 } 210 self.vcode.vreg_types[vreg.get_index()] = ty; 211 if is_reftype(ty) { 212 self.stack_map_info.reftyped_vregs.push(vreg); 213 self.vcode.have_ref_values = true; 214 } 215 } 216 217 /// Set the current block as the entry block. 218 pub fn set_entry(&mut self, block: BlockIndex) { 219 self.vcode.entry = block; 220 } 221 222 /// End the current basic block. Must be called after emitting vcode insts 223 /// for IR insts and prior to ending the function (building the VCode). 224 pub fn end_bb(&mut self) { 225 let start_idx = self.block_start; 226 let end_idx = self.vcode.insts.len() as InsnIndex; 227 self.block_start = end_idx; 228 // Add the instruction index range to the list of blocks. 229 self.vcode.block_ranges.push((start_idx, end_idx)); 230 // End the successors list. 231 let succ_end = self.vcode.block_succs.len(); 232 self.vcode 233 .block_succ_range 234 .push((self.succ_start, succ_end)); 235 self.succ_start = succ_end; 236 } 237 238 /// Push an instruction for the current BB and current IR inst within the BB. 239 pub fn push(&mut self, insn: I, is_safepoint: bool) { 240 match insn.is_term() { 241 MachTerminator::None | MachTerminator::Ret => {} 242 MachTerminator::Uncond(target) => { 243 self.vcode.block_succs.push(BlockIx::new(target.get())); 244 } 245 MachTerminator::Cond(true_branch, false_branch) => { 246 self.vcode.block_succs.push(BlockIx::new(true_branch.get())); 247 self.vcode 248 .block_succs 249 .push(BlockIx::new(false_branch.get())); 250 } 251 MachTerminator::Indirect(targets) => { 252 for target in targets { 253 self.vcode.block_succs.push(BlockIx::new(target.get())); 254 } 255 } 256 } 257 if insn.defines_value_label().is_some() { 258 self.vcode.has_value_labels = true; 259 } 260 self.vcode.insts.push(insn); 261 self.vcode.srclocs.push(self.cur_srcloc); 262 if is_safepoint { 263 self.stack_map_info 264 .safepoint_insns 265 .push(InstIx::new((self.vcode.insts.len() - 1) as u32)); 266 } 267 } 268 269 /// Set the current source location. 270 pub fn set_srcloc(&mut self, srcloc: SourceLoc) { 271 self.cur_srcloc = srcloc; 272 } 273 274 /// Access the constants. 275 pub fn constants(&mut self) -> &mut VCodeConstants { 276 &mut self.vcode.constants 277 } 278 279 /// Build the final VCode, returning the vcode itself as well as auxiliary 280 /// information, such as the stack map request information. 281 pub fn build(self) -> (VCode<I>, StackmapRequestInfo) { 282 // TODO: come up with an abstraction for "vcode and auxiliary data". The 283 // auxiliary data needs to be separate from the vcode so that it can be 284 // referenced as the vcode is mutated (e.g. by the register allocator). 285 (self.vcode, self.stack_map_info) 286 } 287 } 288 289 fn is_redundant_move<I: VCodeInst>(insn: &I) -> bool { 290 if let Some((to, from)) = insn.is_move() { 291 to.to_reg() == from 292 } else { 293 false 294 } 295 } 296 297 /// Is this type a reference type? 298 fn is_reftype(ty: Type) -> bool { 299 ty == types::R64 || ty == types::R32 300 } 301 302 impl<I: VCodeInst> VCode<I> { 303 /// New empty VCode. 304 fn new( 305 abi: Box<dyn ABICallee<I = I>>, 306 emit_info: I::Info, 307 block_order: BlockLoweringOrder, 308 constants: VCodeConstants, 309 generate_debug_info: bool, 310 ) -> VCode<I> { 311 VCode { 312 liveins: abi.liveins(), 313 liveouts: abi.liveouts(), 314 vreg_types: vec![], 315 have_ref_values: false, 316 insts: vec![], 317 srclocs: vec![], 318 entry: 0, 319 block_ranges: vec![], 320 block_succ_range: vec![], 321 block_succs: vec![], 322 block_order, 323 abi, 324 emit_info, 325 safepoint_insns: vec![], 326 safepoint_slots: vec![], 327 generate_debug_info, 328 insts_layout: RefCell::new(Default::default()), 329 constants, 330 has_value_labels: false, 331 } 332 } 333 334 /// Returns the flags controlling this function's compilation. 335 pub fn flags(&self) -> &settings::Flags { 336 self.abi.flags() 337 } 338 339 /// Get the IR-level type of a VReg. 340 pub fn vreg_type(&self, vreg: VirtualReg) -> Type { 341 self.vreg_types[vreg.get_index()] 342 } 343 344 /// Get the number of blocks. Block indices will be in the range `0 .. 345 /// (self.num_blocks() - 1)`. 346 pub fn num_blocks(&self) -> usize { 347 self.block_ranges.len() 348 } 349 350 /// Stack frame size for the full function's body. 351 pub fn frame_size(&self) -> u32 { 352 self.abi.frame_size() 353 } 354 355 /// Get the successors for a block. 356 pub fn succs(&self, block: BlockIndex) -> &[BlockIx] { 357 let (start, end) = self.block_succ_range[block as usize]; 358 &self.block_succs[start..end] 359 } 360 361 /// Take the results of register allocation, with a sequence of 362 /// instructions including spliced fill/reload/move instructions, and replace 363 /// the VCode with them. 364 pub fn replace_insns_from_regalloc(&mut self, result: RegAllocResult<Self>) { 365 // Record the spillslot count and clobbered registers for the ABI/stack 366 // setup code. 367 self.abi.set_num_spillslots(result.num_spill_slots as usize); 368 self.abi 369 .set_clobbered(result.clobbered_registers.map(|r| Writable::from_reg(*r))); 370 371 let mut final_insns = vec![]; 372 let mut final_block_ranges = vec![(0, 0); self.num_blocks()]; 373 let mut final_srclocs = vec![]; 374 let mut final_safepoint_insns = vec![]; 375 let mut safept_idx = 0; 376 377 assert!(result.target_map.elems().len() == self.num_blocks()); 378 for block in 0..self.num_blocks() { 379 let start = result.target_map.elems()[block].get() as usize; 380 let end = if block == self.num_blocks() - 1 { 381 result.insns.len() 382 } else { 383 result.target_map.elems()[block + 1].get() as usize 384 }; 385 let block = block as BlockIndex; 386 let final_start = final_insns.len() as InsnIndex; 387 388 if block == self.entry { 389 // Start with the prologue. 390 let prologue = self.abi.gen_prologue(); 391 let len = prologue.len(); 392 final_insns.extend(prologue.into_iter()); 393 final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len)); 394 } 395 396 for i in start..end { 397 let insn = &result.insns[i]; 398 399 // Elide redundant moves at this point (we only know what is 400 // redundant once registers are allocated). 401 if is_redundant_move(insn) { 402 continue; 403 } 404 405 // Is there a srcloc associated with this insn? Look it up based on original 406 // instruction index (if new insn corresponds to some original insn, i.e., is not 407 // an inserted load/spill/move). 408 let orig_iix = result.orig_insn_map[InstIx::new(i as u32)]; 409 let srcloc = if orig_iix.is_invalid() { 410 SourceLoc::default() 411 } else { 412 self.srclocs[orig_iix.get() as usize] 413 }; 414 415 // Whenever encountering a return instruction, replace it 416 // with the epilogue. 417 let is_ret = insn.is_term() == MachTerminator::Ret; 418 if is_ret { 419 let epilogue = self.abi.gen_epilogue(); 420 let len = epilogue.len(); 421 final_insns.extend(epilogue.into_iter()); 422 final_srclocs.extend(iter::repeat(srcloc).take(len)); 423 } else { 424 final_insns.push(insn.clone()); 425 final_srclocs.push(srcloc); 426 } 427 428 // Was this instruction a safepoint instruction? Add its final 429 // index to the safepoint insn-index list if so. 430 if safept_idx < result.new_safepoint_insns.len() 431 && (result.new_safepoint_insns[safept_idx].get() as usize) == i 432 { 433 let idx = final_insns.len() - 1; 434 final_safepoint_insns.push(idx as InsnIndex); 435 safept_idx += 1; 436 } 437 } 438 439 let final_end = final_insns.len() as InsnIndex; 440 final_block_ranges[block as usize] = (final_start, final_end); 441 } 442 443 debug_assert!(final_insns.len() == final_srclocs.len()); 444 445 self.insts = final_insns; 446 self.srclocs = final_srclocs; 447 self.block_ranges = final_block_ranges; 448 self.safepoint_insns = final_safepoint_insns; 449 450 // Save safepoint slot-lists. These will be passed to the `EmitState` 451 // for the machine backend during emission so that it can do 452 // target-specific translations of slot numbers to stack offsets. 453 self.safepoint_slots = result.stackmaps; 454 } 455 456 /// Emit the instructions to a `MachBuffer`, containing fixed-up code and external 457 /// reloc/trap/etc. records ready for use. 458 pub fn emit( 459 &self, 460 ) -> ( 461 MachBuffer<I>, 462 Vec<CodeOffset>, 463 Vec<(CodeOffset, CodeOffset)>, 464 ) 465 where 466 I: MachInstEmit, 467 { 468 let _tt = timing::vcode_emit(); 469 let mut buffer = MachBuffer::new(); 470 let mut state = I::State::new(&*self.abi); 471 let cfg_metadata = self.flags().machine_code_cfg_info(); 472 let mut bb_starts: Vec<Option<CodeOffset>> = vec![]; 473 474 // The first M MachLabels are reserved for block indices, the next N MachLabels for 475 // constants. 476 buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex); 477 buffer.reserve_labels_for_constants(&self.constants); 478 479 let mut inst_end_offsets = vec![0; self.insts.len()]; 480 let mut label_inst_indices = vec![0; self.num_blocks()]; 481 482 // Map from instruction index to index in 483 // `safepoint_slots`. We need this because we emit 484 // instructions out-of-order, while the safepoint_insns / 485 // safepoint_slots data structures are sorted in instruction 486 // order. 487 let mut safepoint_indices: FxHashMap<u32, usize> = FxHashMap::default(); 488 for (safepoint_idx, iix) in self.safepoint_insns.iter().enumerate() { 489 // Disregard safepoints that ended up having no live refs. 490 if self.safepoint_slots[safepoint_idx].len() > 0 { 491 safepoint_indices.insert(*iix, safepoint_idx); 492 } 493 } 494 495 // Construct the final order we emit code in: cold blocks at the end. 496 let mut final_order: SmallVec<[BlockIndex; 16]> = smallvec![]; 497 let mut cold_blocks: SmallVec<[BlockIndex; 16]> = smallvec![]; 498 for block in 0..self.num_blocks() { 499 let block = block as BlockIndex; 500 if self.block_order.is_cold(block) { 501 cold_blocks.push(block); 502 } else { 503 final_order.push(block); 504 } 505 } 506 let first_cold_block = cold_blocks.first().cloned(); 507 final_order.extend(cold_blocks.clone()); 508 509 // Emit blocks. 510 let mut cur_srcloc = None; 511 let mut last_offset = None; 512 let mut start_of_cold_code = None; 513 for block in final_order { 514 let new_offset = I::align_basic_block(buffer.cur_offset()); 515 while new_offset > buffer.cur_offset() { 516 // Pad with NOPs up to the aligned block offset. 517 let nop = I::gen_nop((new_offset - buffer.cur_offset()) as usize); 518 nop.emit(&mut buffer, &self.emit_info, &mut Default::default()); 519 } 520 assert_eq!(buffer.cur_offset(), new_offset); 521 522 if Some(block) == first_cold_block { 523 start_of_cold_code = Some(buffer.cur_offset()); 524 } 525 526 let (start, end) = self.block_ranges[block as usize]; 527 buffer.bind_label(MachLabel::from_block(block)); 528 label_inst_indices[block as usize] = start; 529 530 if cfg_metadata { 531 // Track BB starts. If we have backed up due to MachBuffer 532 // branch opts, note that the removed blocks were removed. 533 let cur_offset = buffer.cur_offset(); 534 if last_offset.is_some() && cur_offset <= last_offset.unwrap() { 535 for i in (0..bb_starts.len()).rev() { 536 if bb_starts[i].is_some() && cur_offset > bb_starts[i].unwrap() { 537 break; 538 } 539 bb_starts[i] = None; 540 } 541 } 542 bb_starts.push(Some(cur_offset)); 543 last_offset = Some(cur_offset); 544 } 545 546 for iix in start..end { 547 let srcloc = self.srclocs[iix as usize]; 548 if cur_srcloc != Some(srcloc) { 549 if cur_srcloc.is_some() { 550 buffer.end_srcloc(); 551 } 552 buffer.start_srcloc(srcloc); 553 cur_srcloc = Some(srcloc); 554 } 555 state.pre_sourceloc(cur_srcloc.unwrap_or(SourceLoc::default())); 556 557 if let Some(safepoint_idx) = safepoint_indices.get(&iix) { 558 let stack_map = self 559 .abi 560 .spillslots_to_stack_map(&self.safepoint_slots[*safepoint_idx][..], &state); 561 state.pre_safepoint(stack_map); 562 } 563 564 self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state); 565 566 if self.generate_debug_info { 567 // Buffer truncation may have happened since last inst append; trim inst-end 568 // layout info as appropriate. 569 let l = &mut inst_end_offsets[0..iix as usize]; 570 for end in l.iter_mut().rev() { 571 if *end > buffer.cur_offset() { 572 *end = buffer.cur_offset(); 573 } else { 574 break; 575 } 576 } 577 inst_end_offsets[iix as usize] = buffer.cur_offset(); 578 } 579 } 580 581 if cur_srcloc.is_some() { 582 buffer.end_srcloc(); 583 cur_srcloc = None; 584 } 585 586 // Do we need an island? Get the worst-case size of the next BB and see if, having 587 // emitted that many bytes, we will be beyond the deadline. 588 if block < (self.num_blocks() - 1) as BlockIndex { 589 let next_block = block + 1; 590 let next_block_range = self.block_ranges[next_block as usize]; 591 let next_block_size = next_block_range.1 - next_block_range.0; 592 let worst_case_next_bb = I::worst_case_size() * next_block_size; 593 if buffer.island_needed(worst_case_next_bb) { 594 buffer.emit_island(worst_case_next_bb); 595 } 596 } 597 } 598 599 // Emit the constants used by the function. 600 for (constant, data) in self.constants.iter() { 601 let label = buffer.get_label_for_constant(constant); 602 buffer.defer_constant(label, data.alignment(), data.as_slice(), u32::max_value()); 603 } 604 605 if self.generate_debug_info { 606 for end in inst_end_offsets.iter_mut().rev() { 607 if *end > buffer.cur_offset() { 608 *end = buffer.cur_offset(); 609 } else { 610 break; 611 } 612 } 613 *self.insts_layout.borrow_mut() = InstsLayoutInfo { 614 inst_end_offsets, 615 label_inst_indices, 616 start_of_cold_code, 617 }; 618 } 619 620 // Create `bb_edges` and final (filtered) `bb_starts`. 621 let mut final_bb_starts = vec![]; 622 let mut bb_edges = vec![]; 623 if cfg_metadata { 624 for block in 0..self.num_blocks() { 625 if bb_starts[block].is_none() { 626 // Block was deleted by MachBuffer; skip. 627 continue; 628 } 629 let from = bb_starts[block].unwrap(); 630 631 final_bb_starts.push(from); 632 // Resolve each `succ` label and add edges. 633 let succs = self.block_succs(BlockIx::new(block as u32)); 634 for succ in succs.iter() { 635 let to = buffer.resolve_label_offset(MachLabel::from_block(succ.get())); 636 bb_edges.push((from, to)); 637 } 638 } 639 } 640 641 (buffer, final_bb_starts, bb_edges) 642 } 643 644 /// Generates value-label ranges. 645 pub fn value_labels_ranges(&self) -> ValueLabelsRanges { 646 if !self.has_value_labels { 647 return ValueLabelsRanges::default(); 648 } 649 650 let layout_info = &self.insts_layout.borrow(); 651 debug::compute(&self.insts, &*layout_info) 652 } 653 654 /// Get the offsets of stackslots. 655 pub fn stackslot_offsets(&self) -> &PrimaryMap<StackSlot, u32> { 656 self.abi.stackslot_offsets() 657 } 658 659 /// Get the IR block for a BlockIndex, if one exists. 660 pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> { 661 self.block_order.lowered_order()[block as usize].orig_block() 662 } 663 } 664 665 impl<I: VCodeInst> RegallocFunction for VCode<I> { 666 type Inst = I; 667 668 fn insns(&self) -> &[I] { 669 &self.insts[..] 670 } 671 672 fn insns_mut(&mut self) -> &mut [I] { 673 &mut self.insts[..] 674 } 675 676 fn get_insn(&self, insn: InstIx) -> &I { 677 &self.insts[insn.get() as usize] 678 } 679 680 fn get_insn_mut(&mut self, insn: InstIx) -> &mut I { 681 &mut self.insts[insn.get() as usize] 682 } 683 684 fn blocks(&self) -> Range<BlockIx> { 685 Range::new(BlockIx::new(0), self.block_ranges.len()) 686 } 687 688 fn entry_block(&self) -> BlockIx { 689 BlockIx::new(self.entry) 690 } 691 692 fn block_insns(&self, block: BlockIx) -> Range<InstIx> { 693 let (start, end) = self.block_ranges[block.get() as usize]; 694 Range::new(InstIx::new(start), (end - start) as usize) 695 } 696 697 fn block_succs(&self, block: BlockIx) -> Cow<[BlockIx]> { 698 let (start, end) = self.block_succ_range[block.get() as usize]; 699 Cow::Borrowed(&self.block_succs[start..end]) 700 } 701 702 fn is_ret(&self, insn: InstIx) -> bool { 703 match self.insts[insn.get() as usize].is_term() { 704 MachTerminator::Ret => true, 705 _ => false, 706 } 707 } 708 709 fn is_included_in_clobbers(&self, insn: &I) -> bool { 710 insn.is_included_in_clobbers() 711 } 712 713 fn get_regs(insn: &I, collector: &mut RegUsageCollector) { 714 insn.get_regs(collector) 715 } 716 717 fn map_regs<RUM: RegUsageMapper>(insn: &mut I, mapper: &RUM) { 718 insn.map_regs(mapper); 719 } 720 721 fn is_move(&self, insn: &I) -> Option<(Writable<Reg>, Reg)> { 722 insn.is_move() 723 } 724 725 fn get_num_vregs(&self) -> usize { 726 self.vreg_types.len() 727 } 728 729 fn get_spillslot_size(&self, regclass: RegClass, _: VirtualReg) -> u32 { 730 self.abi.get_spillslot_size(regclass) 731 } 732 733 fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, _: Option<VirtualReg>) -> I { 734 self.abi.gen_spill(to_slot, from_reg) 735 } 736 737 fn gen_reload( 738 &self, 739 to_reg: Writable<RealReg>, 740 from_slot: SpillSlot, 741 _: Option<VirtualReg>, 742 ) -> I { 743 self.abi.gen_reload(to_reg, from_slot) 744 } 745 746 fn gen_move(&self, to_reg: Writable<RealReg>, from_reg: RealReg, vreg: VirtualReg) -> I { 747 let ty = self.vreg_type(vreg); 748 I::gen_move(to_reg.map(|r| r.to_reg()), from_reg.to_reg(), ty) 749 } 750 751 fn gen_zero_len_nop(&self) -> I { 752 I::gen_nop(0) 753 } 754 755 fn maybe_direct_reload(&self, insn: &I, reg: VirtualReg, slot: SpillSlot) -> Option<I> { 756 insn.maybe_direct_reload(reg, slot) 757 } 758 759 fn func_liveins(&self) -> RegallocSet<RealReg> { 760 self.liveins.clone() 761 } 762 763 fn func_liveouts(&self) -> RegallocSet<RealReg> { 764 self.liveouts.clone() 765 } 766 } 767 768 impl<I: VCodeInst> fmt::Debug for VCode<I> { 769 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 770 writeln!(f, "VCode_Debug {{")?; 771 writeln!(f, " Entry block: {}", self.entry)?; 772 773 for block in 0..self.num_blocks() { 774 writeln!(f, "Block {}:", block,)?; 775 for succ in self.succs(block as BlockIndex) { 776 writeln!(f, " (successor: Block {})", succ.get())?; 777 } 778 let (start, end) = self.block_ranges[block]; 779 writeln!(f, " (instruction range: {} .. {})", start, end)?; 780 for inst in start..end { 781 writeln!(f, " Inst {}: {:?}", inst, self.insts[inst as usize])?; 782 } 783 } 784 785 writeln!(f, "}}")?; 786 Ok(()) 787 } 788 } 789 790 /// Pretty-printing with `RealRegUniverse` context. 791 impl<I: VCodeInst> PrettyPrint for VCode<I> { 792 fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String { 793 use std::fmt::Write; 794 795 let mut s = String::new(); 796 write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap(); 797 write!(&mut s, " Entry block: {}\n", self.entry).unwrap(); 798 799 let mut state = Default::default(); 800 let mut safepoint_idx = 0; 801 for i in 0..self.num_blocks() { 802 let block = i as BlockIndex; 803 804 write!(&mut s, "Block {}:\n", block).unwrap(); 805 if let Some(bb) = self.bindex_to_bb(block) { 806 write!(&mut s, " (original IR block: {})\n", bb).unwrap(); 807 } 808 for succ in self.succs(block) { 809 write!(&mut s, " (successor: Block {})\n", succ.get()).unwrap(); 810 } 811 let (start, end) = self.block_ranges[block as usize]; 812 write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap(); 813 for inst in start..end { 814 if safepoint_idx < self.safepoint_insns.len() 815 && self.safepoint_insns[safepoint_idx] == inst 816 { 817 write!( 818 &mut s, 819 " (safepoint: slots {:?} with EmitState {:?})\n", 820 self.safepoint_slots[safepoint_idx], state, 821 ) 822 .unwrap(); 823 safepoint_idx += 1; 824 } 825 write!( 826 &mut s, 827 " Inst {}: {}\n", 828 inst, 829 self.insts[inst as usize].pretty_print(mb_rru, &mut state) 830 ) 831 .unwrap(); 832 } 833 } 834 835 write!(&mut s, "}}}}\n").unwrap(); 836 837 s 838 } 839 } 840 841 /// This structure tracks the large constants used in VCode that will be emitted separately by the 842 /// [MachBuffer]. 843 /// 844 /// First, during the lowering phase, constants are inserted using 845 /// [VCodeConstants.insert]; an intermediate handle, [VCodeConstant], tracks what constants are 846 /// used in this phase. Some deduplication is performed, when possible, as constant 847 /// values are inserted. 848 /// 849 /// Secondly, during the emission phase, the [MachBuffer] assigns [MachLabel]s for each of the 850 /// constants so that instructions can refer to the value's memory location. The [MachBuffer] 851 /// then writes the constant values to the buffer. 852 #[derive(Default)] 853 pub struct VCodeConstants { 854 constants: PrimaryMap<VCodeConstant, VCodeConstantData>, 855 pool_uses: HashMap<Constant, VCodeConstant>, 856 well_known_uses: HashMap<*const [u8], VCodeConstant>, 857 } 858 impl VCodeConstants { 859 /// Initialize the structure with the expected number of constants. 860 pub fn with_capacity(expected_num_constants: usize) -> Self { 861 Self { 862 constants: PrimaryMap::with_capacity(expected_num_constants), 863 pool_uses: HashMap::with_capacity(expected_num_constants), 864 well_known_uses: HashMap::new(), 865 } 866 } 867 868 /// Insert a constant; using this method indicates that a constant value will be used and thus 869 /// will be emitted to the `MachBuffer`. The current implementation can deduplicate constants 870 /// that are [VCodeConstantData::Pool] or [VCodeConstantData::WellKnown] but not 871 /// [VCodeConstantData::Generated]. 872 pub fn insert(&mut self, data: VCodeConstantData) -> VCodeConstant { 873 match data { 874 VCodeConstantData::Generated(_) => self.constants.push(data), 875 VCodeConstantData::Pool(constant, _) => match self.pool_uses.get(&constant) { 876 None => { 877 let vcode_constant = self.constants.push(data); 878 self.pool_uses.insert(constant, vcode_constant); 879 vcode_constant 880 } 881 Some(&vcode_constant) => vcode_constant, 882 }, 883 VCodeConstantData::WellKnown(data_ref) => { 884 match self.well_known_uses.get(&(data_ref as *const [u8])) { 885 None => { 886 let vcode_constant = self.constants.push(data); 887 self.well_known_uses 888 .insert(data_ref as *const [u8], vcode_constant); 889 vcode_constant 890 } 891 Some(&vcode_constant) => vcode_constant, 892 } 893 } 894 } 895 } 896 897 /// Return the number of constants inserted. 898 pub fn len(&self) -> usize { 899 self.constants.len() 900 } 901 902 /// Iterate over the [VCodeConstant] keys inserted in this structure. 903 pub fn keys(&self) -> Keys<VCodeConstant> { 904 self.constants.keys() 905 } 906 907 /// Iterate over the [VCodeConstant] keys and the data (as a byte slice) inserted in this 908 /// structure. 909 pub fn iter(&self) -> impl Iterator<Item = (VCodeConstant, &VCodeConstantData)> { 910 self.constants.iter() 911 } 912 } 913 914 /// A use of a constant by one or more VCode instructions; see [VCodeConstants]. 915 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 916 pub struct VCodeConstant(u32); 917 entity_impl!(VCodeConstant); 918 919 /// Identify the different types of constant that can be inserted into [VCodeConstants]. Tracking 920 /// these separately instead of as raw byte buffers allows us to avoid some duplication. 921 pub enum VCodeConstantData { 922 /// A constant already present in the Cranelift IR 923 /// [ConstantPool](crate::ir::constant::ConstantPool). 924 Pool(Constant, ConstantData), 925 /// A reference to a well-known constant value that is statically encoded within the compiler. 926 WellKnown(&'static [u8]), 927 /// A constant value generated during lowering; the value may depend on the instruction context 928 /// which makes it difficult to de-duplicate--if possible, use other variants. 929 Generated(ConstantData), 930 } 931 impl VCodeConstantData { 932 /// Retrieve the constant data as a byte slice. 933 pub fn as_slice(&self) -> &[u8] { 934 match self { 935 VCodeConstantData::Pool(_, d) | VCodeConstantData::Generated(d) => d.as_slice(), 936 VCodeConstantData::WellKnown(d) => d, 937 } 938 } 939 940 /// Calculate the alignment of the constant data. 941 pub fn alignment(&self) -> u32 { 942 if self.as_slice().len() <= 8 { 943 8 944 } else { 945 16 946 } 947 } 948 } 949 950 #[cfg(test)] 951 mod test { 952 use super::*; 953 use std::mem::size_of; 954 955 #[test] 956 fn size_of_constant_structs() { 957 assert_eq!(size_of::<Constant>(), 4); 958 assert_eq!(size_of::<VCodeConstant>(), 4); 959 assert_eq!(size_of::<ConstantData>(), 24); 960 assert_eq!(size_of::<VCodeConstantData>(), 32); 961 assert_eq!( 962 size_of::<PrimaryMap<VCodeConstant, VCodeConstantData>>(), 963 24 964 ); 965 // TODO The VCodeConstants structure's memory size could be further optimized. 966 // With certain versions of Rust, each `HashMap` in `VCodeConstants` occupied at 967 // least 48 bytes, making an empty `VCodeConstants` cost 120 bytes. 968 } 969 } 970