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