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