1d8357426SChris Fallin //! This implements the VCode container: a CFG of Insts that have been lowered. 2d8357426SChris Fallin //! 3d8357426SChris Fallin //! VCode is virtual-register code. An instruction in VCode is almost a machine 4d8357426SChris Fallin //! instruction; however, its register slots can refer to virtual registers in 5d8357426SChris Fallin //! addition to real machine registers. 6d8357426SChris Fallin //! 7d8357426SChris Fallin //! VCode is structured with traditional basic blocks, and 8d8357426SChris Fallin //! each block must be terminated by an unconditional branch (one target), a 9d8357426SChris Fallin //! conditional branch (two targets), or a return (no targets). Note that this 10d8357426SChris Fallin //! slightly differs from the machine code of most ISAs: in most ISAs, a 11d8357426SChris Fallin //! conditional branch has one target (and the not-taken case falls through). 12d8357426SChris Fallin //! However, we expect that machine backends will elide branches to the following 13d8357426SChris Fallin //! block (i.e., zero-offset jumps), and will be able to codegen a branch-cond / 14d8357426SChris Fallin //! branch-uncond pair if *both* targets are not fallthrough. This allows us to 15d8357426SChris Fallin //! play with layout prior to final binary emission, as well, if we want. 16d8357426SChris Fallin //! 17d8357426SChris Fallin //! See the main module comment in `mod.rs` for more details on the VCode-based 18d8357426SChris Fallin //! backend pipeline. 19d8357426SChris Fallin 20b691770fSChris Fallin use crate::entity::SecondaryMap; 211d90751bSBenjamin Bouvier use crate::ir::{self, Block, SourceLoc}; 22d8357426SChris Fallin use crate::machinst::*; 23d8357426SChris Fallin use crate::settings; 24d8357426SChris Fallin 25d8357426SChris Fallin use regalloc::Function as RegallocFunction; 26d8357426SChris Fallin use regalloc::Set as RegallocSet; 27be6f060aSChris Fallin use regalloc::{ 28be6f060aSChris Fallin BlockIx, InstIx, Range, RegAllocResult, RegClass, RegUsageCollector, RegUsageMapper, 29be6f060aSChris Fallin }; 30d8357426SChris Fallin 31d8357426SChris Fallin use alloc::boxed::Box; 32*4f919c64SBenjamin Bouvier use alloc::{borrow::Cow, vec::Vec}; 33d8357426SChris Fallin use log::debug; 34d8357426SChris Fallin use smallvec::SmallVec; 35d8357426SChris Fallin use std::fmt; 36d8357426SChris Fallin use std::iter; 37d8357426SChris Fallin use std::string::String; 38d8357426SChris Fallin 39d8357426SChris Fallin /// Index referring to an instruction in VCode. 40d8357426SChris Fallin pub type InsnIndex = u32; 41d8357426SChris Fallin /// Index referring to a basic block in VCode. 42d8357426SChris Fallin pub type BlockIndex = u32; 43d8357426SChris Fallin 44d8357426SChris Fallin /// VCodeInst wraps all requirements for a MachInst to be in VCode: it must be 45d8357426SChris Fallin /// a `MachInst` and it must be able to emit itself at least to a `SizeCodeSink`. 46d8357426SChris Fallin pub trait VCodeInst: MachInst + MachInstEmit<MachSection> + MachInstEmit<MachSectionSize> {} 47d8357426SChris Fallin impl<I: MachInst + MachInstEmit<MachSection> + MachInstEmit<MachSectionSize>> VCodeInst for I {} 48d8357426SChris Fallin 49d8357426SChris Fallin /// A function in "VCode" (virtualized-register code) form, after lowering. 50d8357426SChris Fallin /// This is essentially a standard CFG of basic blocks, where each basic block 51d8357426SChris Fallin /// consists of lowered instructions produced by the machine-specific backend. 52d8357426SChris Fallin pub struct VCode<I: VCodeInst> { 53d8357426SChris Fallin /// Function liveins. 54d8357426SChris Fallin liveins: RegallocSet<RealReg>, 55d8357426SChris Fallin 56d8357426SChris Fallin /// Function liveouts. 57d8357426SChris Fallin liveouts: RegallocSet<RealReg>, 58d8357426SChris Fallin 59d8357426SChris Fallin /// VReg IR-level types. 60d8357426SChris Fallin vreg_types: Vec<Type>, 61d8357426SChris Fallin 62d8357426SChris Fallin /// Lowered machine instructions in order corresponding to the original IR. 6348cf2c2fSChris Fallin insts: Vec<I>, 64d8357426SChris Fallin 65b691770fSChris Fallin /// Source locations for each instruction. (`SourceLoc` is a `u32`, so it is 66b691770fSChris Fallin /// reasonable to keep one of these per instruction.) 67b691770fSChris Fallin srclocs: Vec<SourceLoc>, 68b691770fSChris Fallin 69d8357426SChris Fallin /// Entry block. 70d8357426SChris Fallin entry: BlockIndex, 71d8357426SChris Fallin 72d8357426SChris Fallin /// Block instruction indices. 7348cf2c2fSChris Fallin block_ranges: Vec<(InsnIndex, InsnIndex)>, 74d8357426SChris Fallin 75d8357426SChris Fallin /// Block successors: index range in the successor-list below. 76d8357426SChris Fallin block_succ_range: Vec<(usize, usize)>, 77d8357426SChris Fallin 78d8357426SChris Fallin /// Block successor lists, concatenated into one Vec. The `block_succ_range` 79d8357426SChris Fallin /// list of tuples above gives (start, end) ranges within this list that 80d8357426SChris Fallin /// correspond to each basic block's successors. 81*4f919c64SBenjamin Bouvier block_succs: Vec<BlockIx>, 82d8357426SChris Fallin 83d8357426SChris Fallin /// Block indices by IR block. 84d8357426SChris Fallin block_by_bb: SecondaryMap<ir::Block, BlockIndex>, 85d8357426SChris Fallin 86d8357426SChris Fallin /// IR block for each VCode Block. The length of this Vec will likely be 87d8357426SChris Fallin /// less than the total number of Blocks, because new Blocks (for edge 88d8357426SChris Fallin /// splits, for example) are appended during lowering. 89d8357426SChris Fallin bb_by_block: Vec<ir::Block>, 90d8357426SChris Fallin 91d8357426SChris Fallin /// Order of block IDs in final generated code. 92d8357426SChris Fallin final_block_order: Vec<BlockIndex>, 93d8357426SChris Fallin 94d8357426SChris Fallin /// Final block offsets. Computed during branch finalization and used 95d8357426SChris Fallin /// during emission. 96d8357426SChris Fallin final_block_offsets: Vec<CodeOffset>, 97d8357426SChris Fallin 98d8357426SChris Fallin /// Size of code, accounting for block layout / alignment. 99d8357426SChris Fallin code_size: CodeOffset, 100d8357426SChris Fallin 101d8357426SChris Fallin /// ABI object. 10248cf2c2fSChris Fallin abi: Box<dyn ABIBody<I = I>>, 1031d90751bSBenjamin Bouvier 1041d90751bSBenjamin Bouvier /// The block targeted by fallthrough_returns, if there's one. 1051d90751bSBenjamin Bouvier pub fallthrough_return_block: Option<BlockIndex>, 106d8357426SChris Fallin } 107d8357426SChris Fallin 108d8357426SChris Fallin /// A builder for a VCode function body. This builder is designed for the 109d8357426SChris Fallin /// lowering approach that we take: we traverse basic blocks in forward 110d8357426SChris Fallin /// (original IR) order, but within each basic block, we generate code from 111d8357426SChris Fallin /// bottom to top; and within each IR instruction that we visit in this reverse 112d8357426SChris Fallin /// order, we emit machine instructions in *forward* order again. 113d8357426SChris Fallin /// 114d8357426SChris Fallin /// Hence, to produce the final instructions in proper order, we perform two 115d8357426SChris Fallin /// swaps. First, the machine instructions (`I` instances) are produced in 116d8357426SChris Fallin /// forward order for an individual IR instruction. Then these are *reversed* 117d8357426SChris Fallin /// and concatenated to `bb_insns` at the end of the IR instruction lowering. 118d8357426SChris Fallin /// The `bb_insns` vec will thus contain all machine instructions for a basic 119d8357426SChris Fallin /// block, in reverse order. Finally, when we're done with a basic block, we 120d8357426SChris Fallin /// reverse the whole block's vec of instructions again, and concatenate onto 121d8357426SChris Fallin /// the VCode's insts. 122d8357426SChris Fallin pub struct VCodeBuilder<I: VCodeInst> { 123d8357426SChris Fallin /// In-progress VCode. 124d8357426SChris Fallin vcode: VCode<I>, 125d8357426SChris Fallin 126d8357426SChris Fallin /// Current basic block instructions, in reverse order (because blocks are 127d8357426SChris Fallin /// built bottom-to-top). 128b691770fSChris Fallin bb_insns: SmallVec<[(I, SourceLoc); 32]>, 129d8357426SChris Fallin 130d8357426SChris Fallin /// Current IR-inst instructions, in forward order. 131b691770fSChris Fallin ir_inst_insns: SmallVec<[(I, SourceLoc); 4]>, 132d8357426SChris Fallin 133d8357426SChris Fallin /// Start of succs for the current block in the concatenated succs list. 134d8357426SChris Fallin succ_start: usize, 135b691770fSChris Fallin 136b691770fSChris Fallin /// Current source location. 137b691770fSChris Fallin cur_srcloc: SourceLoc, 138d8357426SChris Fallin } 139d8357426SChris Fallin 140d8357426SChris Fallin impl<I: VCodeInst> VCodeBuilder<I> { 141d8357426SChris Fallin /// Create a new VCodeBuilder. 14248cf2c2fSChris Fallin pub fn new(abi: Box<dyn ABIBody<I = I>>) -> VCodeBuilder<I> { 143d8357426SChris Fallin let vcode = VCode::new(abi); 144d8357426SChris Fallin VCodeBuilder { 145d8357426SChris Fallin vcode, 146d8357426SChris Fallin bb_insns: SmallVec::new(), 147d8357426SChris Fallin ir_inst_insns: SmallVec::new(), 148d8357426SChris Fallin succ_start: 0, 149b691770fSChris Fallin cur_srcloc: SourceLoc::default(), 150d8357426SChris Fallin } 151d8357426SChris Fallin } 152d8357426SChris Fallin 153d8357426SChris Fallin /// Access the ABI object. 15448cf2c2fSChris Fallin pub fn abi(&mut self) -> &mut dyn ABIBody<I = I> { 155d8357426SChris Fallin &mut *self.vcode.abi 156d8357426SChris Fallin } 157d8357426SChris Fallin 1581d90751bSBenjamin Bouvier /// Set the fallthrough_return target block for this function. There must be at most once per 1591d90751bSBenjamin Bouvier /// function. 1601d90751bSBenjamin Bouvier pub fn set_fallthrough_return_block(&mut self, bb: Block) { 1611d90751bSBenjamin Bouvier debug_assert!( 1621d90751bSBenjamin Bouvier self.vcode.fallthrough_return_block.is_none(), 1631d90751bSBenjamin Bouvier "a function must have at most one fallthrough-return instruction" 1641d90751bSBenjamin Bouvier ); 1651d90751bSBenjamin Bouvier self.vcode.fallthrough_return_block = Some(self.bb_to_bindex(bb)); 1661d90751bSBenjamin Bouvier } 1671d90751bSBenjamin Bouvier 168d8357426SChris Fallin /// Set the type of a VReg. 169d8357426SChris Fallin pub fn set_vreg_type(&mut self, vreg: VirtualReg, ty: Type) { 170d8357426SChris Fallin while self.vcode.vreg_types.len() <= vreg.get_index() { 171d8357426SChris Fallin self.vcode.vreg_types.push(ir::types::I8); // Default type. 172d8357426SChris Fallin } 173d8357426SChris Fallin self.vcode.vreg_types[vreg.get_index()] = ty; 174d8357426SChris Fallin } 175d8357426SChris Fallin 176d8357426SChris Fallin /// Return the underlying bb-to-BlockIndex map. 177d8357426SChris Fallin pub fn blocks_by_bb(&self) -> &SecondaryMap<ir::Block, BlockIndex> { 178d8357426SChris Fallin &self.vcode.block_by_bb 179d8357426SChris Fallin } 180d8357426SChris Fallin 181d8357426SChris Fallin /// Initialize the bb-to-BlockIndex map. Returns the first free 182d8357426SChris Fallin /// BlockIndex. 183d8357426SChris Fallin pub fn init_bb_map(&mut self, blocks: &[ir::Block]) -> BlockIndex { 184d8357426SChris Fallin let mut bindex: BlockIndex = 0; 185d8357426SChris Fallin for bb in blocks.iter() { 186d8357426SChris Fallin self.vcode.block_by_bb[*bb] = bindex; 187d8357426SChris Fallin self.vcode.bb_by_block.push(*bb); 188d8357426SChris Fallin bindex += 1; 189d8357426SChris Fallin } 190d8357426SChris Fallin bindex 191d8357426SChris Fallin } 192d8357426SChris Fallin 193d8357426SChris Fallin /// Get the BlockIndex for an IR block. 194d8357426SChris Fallin pub fn bb_to_bindex(&self, bb: ir::Block) -> BlockIndex { 195d8357426SChris Fallin self.vcode.block_by_bb[bb] 196d8357426SChris Fallin } 197d8357426SChris Fallin 198d8357426SChris Fallin /// Set the current block as the entry block. 199d8357426SChris Fallin pub fn set_entry(&mut self, block: BlockIndex) { 200d8357426SChris Fallin self.vcode.entry = block; 201d8357426SChris Fallin } 202d8357426SChris Fallin 203d8357426SChris Fallin /// End the current IR instruction. Must be called after pushing any 204d8357426SChris Fallin /// instructions and prior to ending the basic block. 205d8357426SChris Fallin pub fn end_ir_inst(&mut self) { 206b691770fSChris Fallin while let Some(pair) = self.ir_inst_insns.pop() { 207b691770fSChris Fallin self.bb_insns.push(pair); 208d8357426SChris Fallin } 209d8357426SChris Fallin } 210d8357426SChris Fallin 211d8357426SChris Fallin /// End the current basic block. Must be called after emitting vcode insts 212d8357426SChris Fallin /// for IR insts and prior to ending the function (building the VCode). 213d8357426SChris Fallin pub fn end_bb(&mut self) -> BlockIndex { 214d8357426SChris Fallin assert!(self.ir_inst_insns.is_empty()); 215d8357426SChris Fallin let block_num = self.vcode.block_ranges.len() as BlockIndex; 216d8357426SChris Fallin // Push the instructions. 217d8357426SChris Fallin let start_idx = self.vcode.insts.len() as InsnIndex; 218b691770fSChris Fallin while let Some((i, loc)) = self.bb_insns.pop() { 219d8357426SChris Fallin self.vcode.insts.push(i); 220b691770fSChris Fallin self.vcode.srclocs.push(loc); 221d8357426SChris Fallin } 222d8357426SChris Fallin let end_idx = self.vcode.insts.len() as InsnIndex; 223d8357426SChris Fallin // Add the instruction index range to the list of blocks. 224d8357426SChris Fallin self.vcode.block_ranges.push((start_idx, end_idx)); 225d8357426SChris Fallin // End the successors list. 226d8357426SChris Fallin let succ_end = self.vcode.block_succs.len(); 227d8357426SChris Fallin self.vcode 228d8357426SChris Fallin .block_succ_range 229d8357426SChris Fallin .push((self.succ_start, succ_end)); 230d8357426SChris Fallin self.succ_start = succ_end; 231d8357426SChris Fallin 232d8357426SChris Fallin block_num 233d8357426SChris Fallin } 234d8357426SChris Fallin 235d8357426SChris Fallin /// Push an instruction for the current BB and current IR inst within the BB. 236d8357426SChris Fallin pub fn push(&mut self, insn: I) { 237d8357426SChris Fallin match insn.is_term() { 238d8357426SChris Fallin MachTerminator::None | MachTerminator::Ret => {} 239d8357426SChris Fallin MachTerminator::Uncond(target) => { 240*4f919c64SBenjamin Bouvier self.vcode.block_succs.push(BlockIx::new(target)); 241d8357426SChris Fallin } 242d8357426SChris Fallin MachTerminator::Cond(true_branch, false_branch) => { 243*4f919c64SBenjamin Bouvier self.vcode.block_succs.push(BlockIx::new(true_branch)); 244*4f919c64SBenjamin Bouvier self.vcode.block_succs.push(BlockIx::new(false_branch)); 245d8357426SChris Fallin } 246d8357426SChris Fallin MachTerminator::Indirect(targets) => { 247d8357426SChris Fallin for target in targets { 248*4f919c64SBenjamin Bouvier self.vcode.block_succs.push(BlockIx::new(*target)); 249d8357426SChris Fallin } 250d8357426SChris Fallin } 251d8357426SChris Fallin } 252b691770fSChris Fallin self.ir_inst_insns.push((insn, self.cur_srcloc)); 253b691770fSChris Fallin } 254b691770fSChris Fallin 255b691770fSChris Fallin /// Set the current source location. 256b691770fSChris Fallin pub fn set_srcloc(&mut self, srcloc: SourceLoc) { 257b691770fSChris Fallin self.cur_srcloc = srcloc; 258d8357426SChris Fallin } 259d8357426SChris Fallin 260d8357426SChris Fallin /// Build the final VCode. 261d8357426SChris Fallin pub fn build(self) -> VCode<I> { 262d8357426SChris Fallin assert!(self.ir_inst_insns.is_empty()); 263d8357426SChris Fallin assert!(self.bb_insns.is_empty()); 264d8357426SChris Fallin self.vcode 265d8357426SChris Fallin } 266d8357426SChris Fallin } 267d8357426SChris Fallin 268d8357426SChris Fallin fn block_ranges(indices: &[InstIx], len: usize) -> Vec<(usize, usize)> { 269d8357426SChris Fallin let v = indices 270d8357426SChris Fallin .iter() 271d8357426SChris Fallin .map(|iix| iix.get() as usize) 272d8357426SChris Fallin .chain(iter::once(len)) 273d8357426SChris Fallin .collect::<Vec<usize>>(); 274d8357426SChris Fallin v.windows(2).map(|p| (p[0], p[1])).collect() 275d8357426SChris Fallin } 276d8357426SChris Fallin 277d8357426SChris Fallin fn is_redundant_move<I: VCodeInst>(insn: &I) -> bool { 278d8357426SChris Fallin if let Some((to, from)) = insn.is_move() { 279d8357426SChris Fallin to.to_reg() == from 280d8357426SChris Fallin } else { 281d8357426SChris Fallin false 282d8357426SChris Fallin } 283d8357426SChris Fallin } 284d8357426SChris Fallin 285d8357426SChris Fallin fn is_trivial_jump_block<I: VCodeInst>(vcode: &VCode<I>, block: BlockIndex) -> Option<BlockIndex> { 286d8357426SChris Fallin let range = vcode.block_insns(BlockIx::new(block)); 287d8357426SChris Fallin 288d8357426SChris Fallin debug!( 289d8357426SChris Fallin "is_trivial_jump_block: block {} has len {}", 290d8357426SChris Fallin block, 291d8357426SChris Fallin range.len() 292d8357426SChris Fallin ); 293d8357426SChris Fallin 294d8357426SChris Fallin if range.len() != 1 { 295d8357426SChris Fallin return None; 296d8357426SChris Fallin } 297d8357426SChris Fallin let insn = range.first(); 298d8357426SChris Fallin 299d8357426SChris Fallin debug!( 300d8357426SChris Fallin " -> only insn is: {:?} with terminator {:?}", 301d8357426SChris Fallin vcode.get_insn(insn), 302d8357426SChris Fallin vcode.get_insn(insn).is_term() 303d8357426SChris Fallin ); 304d8357426SChris Fallin 305d8357426SChris Fallin match vcode.get_insn(insn).is_term() { 306d8357426SChris Fallin MachTerminator::Uncond(target) => Some(target), 307d8357426SChris Fallin _ => None, 308d8357426SChris Fallin } 309d8357426SChris Fallin } 310d8357426SChris Fallin 311d8357426SChris Fallin impl<I: VCodeInst> VCode<I> { 312d8357426SChris Fallin /// New empty VCode. 31348cf2c2fSChris Fallin fn new(abi: Box<dyn ABIBody<I = I>>) -> VCode<I> { 314d8357426SChris Fallin VCode { 315d8357426SChris Fallin liveins: abi.liveins(), 316d8357426SChris Fallin liveouts: abi.liveouts(), 317d8357426SChris Fallin vreg_types: vec![], 318d8357426SChris Fallin insts: vec![], 319b691770fSChris Fallin srclocs: vec![], 320d8357426SChris Fallin entry: 0, 321d8357426SChris Fallin block_ranges: vec![], 322d8357426SChris Fallin block_succ_range: vec![], 323d8357426SChris Fallin block_succs: vec![], 324d8357426SChris Fallin block_by_bb: SecondaryMap::with_default(0), 325d8357426SChris Fallin bb_by_block: vec![], 326d8357426SChris Fallin final_block_order: vec![], 327d8357426SChris Fallin final_block_offsets: vec![], 328d8357426SChris Fallin code_size: 0, 329d8357426SChris Fallin abi, 3301d90751bSBenjamin Bouvier fallthrough_return_block: None, 331d8357426SChris Fallin } 332d8357426SChris Fallin } 333d8357426SChris Fallin 334d1b5df31SBenjamin Bouvier /// Returns the flags controlling this function's compilation. 335d1b5df31SBenjamin Bouvier pub fn flags(&self) -> &settings::Flags { 336d1b5df31SBenjamin Bouvier self.abi.flags() 337d1b5df31SBenjamin Bouvier } 338d1b5df31SBenjamin Bouvier 339d8357426SChris Fallin /// Get the IR-level type of a VReg. 340d8357426SChris Fallin pub fn vreg_type(&self, vreg: VirtualReg) -> Type { 341d8357426SChris Fallin self.vreg_types[vreg.get_index()] 342d8357426SChris Fallin } 343d8357426SChris Fallin 344d8357426SChris Fallin /// Get the entry block. 345d8357426SChris Fallin pub fn entry(&self) -> BlockIndex { 346d8357426SChris Fallin self.entry 347d8357426SChris Fallin } 348d8357426SChris Fallin 349d8357426SChris Fallin /// Get the number of blocks. Block indices will be in the range `0 .. 350d8357426SChris Fallin /// (self.num_blocks() - 1)`. 351d8357426SChris Fallin pub fn num_blocks(&self) -> usize { 352d8357426SChris Fallin self.block_ranges.len() 353d8357426SChris Fallin } 354d8357426SChris Fallin 355d8357426SChris Fallin /// Stack frame size for the full function's body. 356d8357426SChris Fallin pub fn frame_size(&self) -> u32 { 357d8357426SChris Fallin self.abi.frame_size() 358d8357426SChris Fallin } 359d8357426SChris Fallin 360d8357426SChris Fallin /// Get the successors for a block. 361*4f919c64SBenjamin Bouvier pub fn succs(&self, block: BlockIndex) -> &[BlockIx] { 362d8357426SChris Fallin let (start, end) = self.block_succ_range[block as usize]; 363d8357426SChris Fallin &self.block_succs[start..end] 364d8357426SChris Fallin } 365d8357426SChris Fallin 366d8357426SChris Fallin /// Take the results of register allocation, with a sequence of 367d8357426SChris Fallin /// instructions including spliced fill/reload/move instructions, and replace 368d8357426SChris Fallin /// the VCode with them. 369d1b5df31SBenjamin Bouvier pub fn replace_insns_from_regalloc(&mut self, result: RegAllocResult<Self>) { 370d8357426SChris Fallin self.final_block_order = compute_final_block_order(self); 371d8357426SChris Fallin 372d8357426SChris Fallin // Record the spillslot count and clobbered registers for the ABI/stack 373d8357426SChris Fallin // setup code. 374d8357426SChris Fallin self.abi.set_num_spillslots(result.num_spill_slots as usize); 375d8357426SChris Fallin self.abi 376d8357426SChris Fallin .set_clobbered(result.clobbered_registers.map(|r| Writable::from_reg(*r))); 377d8357426SChris Fallin 378d8357426SChris Fallin // We want to move instructions over in final block order, using the new 379d8357426SChris Fallin // block-start map given by the regalloc. 380d8357426SChris Fallin let block_ranges: Vec<(usize, usize)> = 381d8357426SChris Fallin block_ranges(result.target_map.elems(), result.insns.len()); 382d8357426SChris Fallin let mut final_insns = vec![]; 383d8357426SChris Fallin let mut final_block_ranges = vec![(0, 0); self.num_blocks()]; 384b691770fSChris Fallin let mut final_srclocs = vec![]; 385d8357426SChris Fallin 386d8357426SChris Fallin for block in &self.final_block_order { 387d8357426SChris Fallin let (start, end) = block_ranges[*block as usize]; 388d8357426SChris Fallin let final_start = final_insns.len() as InsnIndex; 389d8357426SChris Fallin 390d8357426SChris Fallin if *block == self.entry { 391d8357426SChris Fallin // Start with the prologue. 392b691770fSChris Fallin let prologue = self.abi.gen_prologue(); 393b691770fSChris Fallin let len = prologue.len(); 394b691770fSChris Fallin final_insns.extend(prologue.into_iter()); 395b691770fSChris Fallin final_srclocs.extend(iter::repeat(SourceLoc::default()).take(len)); 396d8357426SChris Fallin } 397d8357426SChris Fallin 398d8357426SChris Fallin for i in start..end { 399d8357426SChris Fallin let insn = &result.insns[i]; 400d8357426SChris Fallin 401d8357426SChris Fallin // Elide redundant moves at this point (we only know what is 402d8357426SChris Fallin // redundant once registers are allocated). 403d8357426SChris Fallin if is_redundant_move(insn) { 404d8357426SChris Fallin continue; 405d8357426SChris Fallin } 406d8357426SChris Fallin 407b691770fSChris Fallin // Is there a srcloc associated with this insn? Look it up based on original 408b691770fSChris Fallin // instruction index (if new insn corresponds to some original insn, i.e., is not 409b691770fSChris Fallin // an inserted load/spill/move). 410b691770fSChris Fallin let orig_iix = result.orig_insn_map[InstIx::new(i as u32)]; 411b691770fSChris Fallin let srcloc = if orig_iix.is_invalid() { 412b691770fSChris Fallin SourceLoc::default() 413b691770fSChris Fallin } else { 414b691770fSChris Fallin self.srclocs[orig_iix.get() as usize] 415b691770fSChris Fallin }; 416b691770fSChris Fallin 417d8357426SChris Fallin // Whenever encountering a return instruction, replace it 418d8357426SChris Fallin // with the epilogue. 419d8357426SChris Fallin let is_ret = insn.is_term() == MachTerminator::Ret; 420d8357426SChris Fallin if is_ret { 421b691770fSChris Fallin let epilogue = self.abi.gen_epilogue(); 422b691770fSChris Fallin let len = epilogue.len(); 423b691770fSChris Fallin final_insns.extend(epilogue.into_iter()); 424b691770fSChris Fallin final_srclocs.extend(iter::repeat(srcloc).take(len)); 425d8357426SChris Fallin } else { 426d8357426SChris Fallin final_insns.push(insn.clone()); 427b691770fSChris Fallin final_srclocs.push(srcloc); 428d8357426SChris Fallin } 429d8357426SChris Fallin } 430d8357426SChris Fallin 431d8357426SChris Fallin let final_end = final_insns.len() as InsnIndex; 432d8357426SChris Fallin final_block_ranges[*block as usize] = (final_start, final_end); 433d8357426SChris Fallin } 434d8357426SChris Fallin 435b691770fSChris Fallin debug_assert!(final_insns.len() == final_srclocs.len()); 436b691770fSChris Fallin 437d8357426SChris Fallin self.insts = final_insns; 438b691770fSChris Fallin self.srclocs = final_srclocs; 439d8357426SChris Fallin self.block_ranges = final_block_ranges; 440d8357426SChris Fallin } 441d8357426SChris Fallin 442d8357426SChris Fallin /// Removes redundant branches, rewriting targets to point directly to the 443d8357426SChris Fallin /// ultimate block at the end of a chain of trivial one-target jumps. 444d8357426SChris Fallin pub fn remove_redundant_branches(&mut self) { 445d8357426SChris Fallin // For each block, compute the actual target block, looking through up to one 446d8357426SChris Fallin // block with single-target jumps (this will remove empty edge blocks inserted 447d8357426SChris Fallin // by phi-lowering). 448d8357426SChris Fallin let block_rewrites: Vec<BlockIndex> = (0..self.num_blocks() as u32) 449d8357426SChris Fallin .map(|bix| is_trivial_jump_block(self, bix).unwrap_or(bix)) 450d8357426SChris Fallin .collect(); 451d8357426SChris Fallin let mut refcounts: Vec<usize> = vec![0; self.num_blocks()]; 452d8357426SChris Fallin 453d8357426SChris Fallin debug!( 454d8357426SChris Fallin "remove_redundant_branches: block_rewrites = {:?}", 455d8357426SChris Fallin block_rewrites 456d8357426SChris Fallin ); 457d8357426SChris Fallin 458d8357426SChris Fallin refcounts[self.entry as usize] = 1; 459d8357426SChris Fallin 460d8357426SChris Fallin for block in 0..self.num_blocks() as u32 { 461d8357426SChris Fallin for insn in self.block_insns(BlockIx::new(block)) { 462d8357426SChris Fallin self.get_insn_mut(insn) 463d8357426SChris Fallin .with_block_rewrites(&block_rewrites[..]); 464d8357426SChris Fallin match self.get_insn(insn).is_term() { 465d8357426SChris Fallin MachTerminator::Uncond(bix) => { 466d8357426SChris Fallin refcounts[bix as usize] += 1; 467d8357426SChris Fallin } 468d8357426SChris Fallin MachTerminator::Cond(bix1, bix2) => { 469d8357426SChris Fallin refcounts[bix1 as usize] += 1; 470d8357426SChris Fallin refcounts[bix2 as usize] += 1; 471d8357426SChris Fallin } 472d8357426SChris Fallin MachTerminator::Indirect(blocks) => { 473d8357426SChris Fallin for block in blocks { 474d8357426SChris Fallin refcounts[*block as usize] += 1; 475d8357426SChris Fallin } 476d8357426SChris Fallin } 477d8357426SChris Fallin _ => {} 478d8357426SChris Fallin } 479d8357426SChris Fallin } 480d8357426SChris Fallin } 481d8357426SChris Fallin 482d8357426SChris Fallin let deleted: Vec<bool> = refcounts.iter().map(|r| *r == 0).collect(); 483d8357426SChris Fallin 484d8357426SChris Fallin let block_order = std::mem::replace(&mut self.final_block_order, vec![]); 485d8357426SChris Fallin self.final_block_order = block_order 486d8357426SChris Fallin .into_iter() 487d8357426SChris Fallin .filter(|b| !deleted[*b as usize]) 488d8357426SChris Fallin .collect(); 489d8357426SChris Fallin 490d8357426SChris Fallin // Rewrite successor information based on the block-rewrite map. 491d8357426SChris Fallin for succ in &mut self.block_succs { 492*4f919c64SBenjamin Bouvier let new_succ = block_rewrites[succ.get() as usize]; 493*4f919c64SBenjamin Bouvier *succ = BlockIx::new(new_succ); 494d8357426SChris Fallin } 495d8357426SChris Fallin } 496d8357426SChris Fallin 497d8357426SChris Fallin /// Mutate branch instructions to (i) lower two-way condbrs to one-way, 498d8357426SChris Fallin /// depending on fallthrough; and (ii) use concrete offsets. 499d8357426SChris Fallin pub fn finalize_branches(&mut self) 500d8357426SChris Fallin where 501d8357426SChris Fallin I: MachInstEmit<MachSectionSize>, 502d8357426SChris Fallin { 503d8357426SChris Fallin // Compute fallthrough block, indexed by block. 504d8357426SChris Fallin let num_final_blocks = self.final_block_order.len(); 505d8357426SChris Fallin let mut block_fallthrough: Vec<Option<BlockIndex>> = vec![None; self.num_blocks()]; 506d8357426SChris Fallin for i in 0..(num_final_blocks - 1) { 507d8357426SChris Fallin let from = self.final_block_order[i]; 508d8357426SChris Fallin let to = self.final_block_order[i + 1]; 509d8357426SChris Fallin block_fallthrough[from as usize] = Some(to); 510d8357426SChris Fallin } 511d8357426SChris Fallin 512d8357426SChris Fallin // Pass over VCode instructions and finalize two-way branches into 513d8357426SChris Fallin // one-way branches with fallthrough. 514d8357426SChris Fallin for block in 0..self.num_blocks() { 515d8357426SChris Fallin let next_block = block_fallthrough[block]; 516d8357426SChris Fallin let (start, end) = self.block_ranges[block]; 517d8357426SChris Fallin 518d8357426SChris Fallin for iix in start..end { 519d8357426SChris Fallin let insn = &mut self.insts[iix as usize]; 520d8357426SChris Fallin insn.with_fallthrough_block(next_block); 521d8357426SChris Fallin } 522d8357426SChris Fallin } 523d8357426SChris Fallin 524a7ca37e4SBenjamin Bouvier let flags = self.abi.flags(); 525a7ca37e4SBenjamin Bouvier 526d8357426SChris Fallin // Compute block offsets. 527d8357426SChris Fallin let mut code_section = MachSectionSize::new(0); 528d8357426SChris Fallin let mut block_offsets = vec![0; self.num_blocks()]; 529a66724aaSChris Fallin let mut state = Default::default(); 53048cf2c2fSChris Fallin for &block in &self.final_block_order { 531d8357426SChris Fallin code_section.offset = I::align_basic_block(code_section.offset); 53248cf2c2fSChris Fallin block_offsets[block as usize] = code_section.offset; 53348cf2c2fSChris Fallin let (start, end) = self.block_ranges[block as usize]; 534d8357426SChris Fallin for iix in start..end { 535a66724aaSChris Fallin self.insts[iix as usize].emit(&mut code_section, flags, &mut state); 536d8357426SChris Fallin } 537d8357426SChris Fallin } 538d8357426SChris Fallin 539d8357426SChris Fallin // We now have the section layout. 540d8357426SChris Fallin self.final_block_offsets = block_offsets; 541d8357426SChris Fallin self.code_size = code_section.size(); 542d8357426SChris Fallin 543d8357426SChris Fallin // Update branches with known block offsets. This looks like the 544d8357426SChris Fallin // traversal above, but (i) does not update block_offsets, rather uses 545d8357426SChris Fallin // it (so forward references are now possible), and (ii) mutates the 546d8357426SChris Fallin // instructions. 547d8357426SChris Fallin let mut code_section = MachSectionSize::new(0); 548a66724aaSChris Fallin let mut state = Default::default(); 54948cf2c2fSChris Fallin for &block in &self.final_block_order { 550d8357426SChris Fallin code_section.offset = I::align_basic_block(code_section.offset); 55148cf2c2fSChris Fallin let (start, end) = self.block_ranges[block as usize]; 552d8357426SChris Fallin for iix in start..end { 553d8357426SChris Fallin self.insts[iix as usize] 554d8357426SChris Fallin .with_block_offsets(code_section.offset, &self.final_block_offsets[..]); 555a66724aaSChris Fallin self.insts[iix as usize].emit(&mut code_section, flags, &mut state); 556d8357426SChris Fallin } 557d8357426SChris Fallin } 558d8357426SChris Fallin } 559d8357426SChris Fallin 560d8357426SChris Fallin /// Emit the instructions to a list of sections. 561d8357426SChris Fallin pub fn emit(&self) -> MachSections 562d8357426SChris Fallin where 563d8357426SChris Fallin I: MachInstEmit<MachSection>, 564d8357426SChris Fallin { 565d8357426SChris Fallin let mut sections = MachSections::new(); 566d8357426SChris Fallin let code_idx = sections.add_section(0, self.code_size); 567d8357426SChris Fallin let code_section = sections.get_section(code_idx); 568a66724aaSChris Fallin let mut state = Default::default(); 569d8357426SChris Fallin 570a7ca37e4SBenjamin Bouvier let flags = self.abi.flags(); 571964c6087SChris Fallin let mut cur_srcloc = None; 57248cf2c2fSChris Fallin for &block in &self.final_block_order { 573d8357426SChris Fallin let new_offset = I::align_basic_block(code_section.cur_offset_from_start()); 574d8357426SChris Fallin while new_offset > code_section.cur_offset_from_start() { 575d8357426SChris Fallin // Pad with NOPs up to the aligned block offset. 576d8357426SChris Fallin let nop = I::gen_nop((new_offset - code_section.cur_offset_from_start()) as usize); 577a66724aaSChris Fallin nop.emit(code_section, flags, &mut Default::default()); 578d8357426SChris Fallin } 579d8357426SChris Fallin assert_eq!(code_section.cur_offset_from_start(), new_offset); 580d8357426SChris Fallin 58148cf2c2fSChris Fallin let (start, end) = self.block_ranges[block as usize]; 582d8357426SChris Fallin for iix in start..end { 583b691770fSChris Fallin let srcloc = self.srclocs[iix as usize]; 584964c6087SChris Fallin if cur_srcloc != Some(srcloc) { 585964c6087SChris Fallin if cur_srcloc.is_some() { 586b691770fSChris Fallin code_section.end_srcloc(); 587b691770fSChris Fallin } 588b691770fSChris Fallin code_section.start_srcloc(srcloc); 589964c6087SChris Fallin cur_srcloc = Some(srcloc); 590b691770fSChris Fallin } 591b691770fSChris Fallin 592a66724aaSChris Fallin self.insts[iix as usize].emit(code_section, flags, &mut state); 593d8357426SChris Fallin } 594b691770fSChris Fallin 595964c6087SChris Fallin if cur_srcloc.is_some() { 596b691770fSChris Fallin code_section.end_srcloc(); 597964c6087SChris Fallin cur_srcloc = None; 598b691770fSChris Fallin } 599d8357426SChris Fallin } 600d8357426SChris Fallin 601d8357426SChris Fallin sections 602d8357426SChris Fallin } 603d8357426SChris Fallin 604d8357426SChris Fallin /// Get the IR block for a BlockIndex, if one exists. 605d8357426SChris Fallin pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> { 606d8357426SChris Fallin if (block as usize) < self.bb_by_block.len() { 607d8357426SChris Fallin Some(self.bb_by_block[block as usize]) 608d8357426SChris Fallin } else { 609d8357426SChris Fallin None 610d8357426SChris Fallin } 611d8357426SChris Fallin } 612d8357426SChris Fallin } 613d8357426SChris Fallin 614d8357426SChris Fallin impl<I: VCodeInst> RegallocFunction for VCode<I> { 615d8357426SChris Fallin type Inst = I; 616d8357426SChris Fallin 617d8357426SChris Fallin fn insns(&self) -> &[I] { 618d8357426SChris Fallin &self.insts[..] 619d8357426SChris Fallin } 620d8357426SChris Fallin 621d8357426SChris Fallin fn insns_mut(&mut self) -> &mut [I] { 622d8357426SChris Fallin &mut self.insts[..] 623d8357426SChris Fallin } 624d8357426SChris Fallin 625d8357426SChris Fallin fn get_insn(&self, insn: InstIx) -> &I { 626d8357426SChris Fallin &self.insts[insn.get() as usize] 627d8357426SChris Fallin } 628d8357426SChris Fallin 629d8357426SChris Fallin fn get_insn_mut(&mut self, insn: InstIx) -> &mut I { 630d8357426SChris Fallin &mut self.insts[insn.get() as usize] 631d8357426SChris Fallin } 632d8357426SChris Fallin 633d8357426SChris Fallin fn blocks(&self) -> Range<BlockIx> { 634d8357426SChris Fallin Range::new(BlockIx::new(0), self.block_ranges.len()) 635d8357426SChris Fallin } 636d8357426SChris Fallin 637d8357426SChris Fallin fn entry_block(&self) -> BlockIx { 638d8357426SChris Fallin BlockIx::new(self.entry) 639d8357426SChris Fallin } 640d8357426SChris Fallin 641d8357426SChris Fallin fn block_insns(&self, block: BlockIx) -> Range<InstIx> { 642d8357426SChris Fallin let (start, end) = self.block_ranges[block.get() as usize]; 643d8357426SChris Fallin Range::new(InstIx::new(start), (end - start) as usize) 644d8357426SChris Fallin } 645d8357426SChris Fallin 646*4f919c64SBenjamin Bouvier fn block_succs(&self, block: BlockIx) -> Cow<[BlockIx]> { 647d8357426SChris Fallin let (start, end) = self.block_succ_range[block.get() as usize]; 648*4f919c64SBenjamin Bouvier Cow::Borrowed(&self.block_succs[start..end]) 649d8357426SChris Fallin } 650d8357426SChris Fallin 651d8357426SChris Fallin fn is_ret(&self, insn: InstIx) -> bool { 652d8357426SChris Fallin match self.insts[insn.get() as usize].is_term() { 653d8357426SChris Fallin MachTerminator::Ret => true, 654d8357426SChris Fallin _ => false, 655d8357426SChris Fallin } 656d8357426SChris Fallin } 657d8357426SChris Fallin 658d8357426SChris Fallin fn get_regs(insn: &I, collector: &mut RegUsageCollector) { 659d8357426SChris Fallin insn.get_regs(collector) 660d8357426SChris Fallin } 661d8357426SChris Fallin 662be6f060aSChris Fallin fn map_regs(insn: &mut I, mapper: &RegUsageMapper) { 663be6f060aSChris Fallin insn.map_regs(mapper); 664d8357426SChris Fallin } 665d8357426SChris Fallin 666d8357426SChris Fallin fn is_move(&self, insn: &I) -> Option<(Writable<Reg>, Reg)> { 667d8357426SChris Fallin insn.is_move() 668d8357426SChris Fallin } 669d8357426SChris Fallin 670be6f060aSChris Fallin fn get_vreg_count_estimate(&self) -> Option<usize> { 671be6f060aSChris Fallin Some(self.vreg_types.len()) 672be6f060aSChris Fallin } 673be6f060aSChris Fallin 674d8357426SChris Fallin fn get_spillslot_size(&self, regclass: RegClass, vreg: VirtualReg) -> u32 { 675d8357426SChris Fallin let ty = self.vreg_type(vreg); 676d8357426SChris Fallin self.abi.get_spillslot_size(regclass, ty) 677d8357426SChris Fallin } 678d8357426SChris Fallin 679d8357426SChris Fallin fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, vreg: VirtualReg) -> I { 680d8357426SChris Fallin let ty = self.vreg_type(vreg); 681d8357426SChris Fallin self.abi.gen_spill(to_slot, from_reg, ty) 682d8357426SChris Fallin } 683d8357426SChris Fallin 684d8357426SChris Fallin fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, vreg: VirtualReg) -> I { 685d8357426SChris Fallin let ty = self.vreg_type(vreg); 686d8357426SChris Fallin self.abi.gen_reload(to_reg, from_slot, ty) 687d8357426SChris Fallin } 688d8357426SChris Fallin 689d8357426SChris Fallin fn gen_move(&self, to_reg: Writable<RealReg>, from_reg: RealReg, vreg: VirtualReg) -> I { 690d8357426SChris Fallin let ty = self.vreg_type(vreg); 691d8357426SChris Fallin I::gen_move(to_reg.map(|r| r.to_reg()), from_reg.to_reg(), ty) 692d8357426SChris Fallin } 693d8357426SChris Fallin 694d8357426SChris Fallin fn gen_zero_len_nop(&self) -> I { 695d8357426SChris Fallin I::gen_zero_len_nop() 696d8357426SChris Fallin } 697d8357426SChris Fallin 698d8357426SChris Fallin fn maybe_direct_reload(&self, insn: &I, reg: VirtualReg, slot: SpillSlot) -> Option<I> { 699d8357426SChris Fallin insn.maybe_direct_reload(reg, slot) 700d8357426SChris Fallin } 701d8357426SChris Fallin 702d8357426SChris Fallin fn func_liveins(&self) -> RegallocSet<RealReg> { 703d8357426SChris Fallin self.liveins.clone() 704d8357426SChris Fallin } 705d8357426SChris Fallin 706d8357426SChris Fallin fn func_liveouts(&self) -> RegallocSet<RealReg> { 707d8357426SChris Fallin self.liveouts.clone() 708d8357426SChris Fallin } 709d8357426SChris Fallin } 710d8357426SChris Fallin 711d8357426SChris Fallin impl<I: VCodeInst> fmt::Debug for VCode<I> { 712d8357426SChris Fallin fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 713d8357426SChris Fallin writeln!(f, "VCode_Debug {{")?; 714d8357426SChris Fallin writeln!(f, " Entry block: {}", self.entry)?; 715d8357426SChris Fallin writeln!(f, " Final block order: {:?}", self.final_block_order)?; 716d8357426SChris Fallin 717d8357426SChris Fallin for block in 0..self.num_blocks() { 718d8357426SChris Fallin writeln!(f, "Block {}:", block,)?; 719d8357426SChris Fallin for succ in self.succs(block as BlockIndex) { 720*4f919c64SBenjamin Bouvier writeln!(f, " (successor: Block {})", succ.get())?; 721d8357426SChris Fallin } 722d8357426SChris Fallin let (start, end) = self.block_ranges[block]; 723d8357426SChris Fallin writeln!(f, " (instruction range: {} .. {})", start, end)?; 724d8357426SChris Fallin for inst in start..end { 725d8357426SChris Fallin writeln!(f, " Inst {}: {:?}", inst, self.insts[inst as usize])?; 726d8357426SChris Fallin } 727d8357426SChris Fallin } 728d8357426SChris Fallin 729d8357426SChris Fallin writeln!(f, "}}")?; 730d8357426SChris Fallin Ok(()) 731d8357426SChris Fallin } 732d8357426SChris Fallin } 733d8357426SChris Fallin 73448cf2c2fSChris Fallin /// Pretty-printing with `RealRegUniverse` context. 735d8357426SChris Fallin impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> { 736d8357426SChris Fallin fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String { 737d8357426SChris Fallin use std::fmt::Write; 738d8357426SChris Fallin 739d8357426SChris Fallin // Calculate an order in which to display the blocks. This is the same 740d8357426SChris Fallin // as final_block_order, but also includes blocks which are in the 741d8357426SChris Fallin // representation but not in final_block_order. 742d8357426SChris Fallin let mut display_order = Vec::<usize>::new(); 74348cf2c2fSChris Fallin // First display blocks in `final_block_order` 744d8357426SChris Fallin for bix in &self.final_block_order { 745d8357426SChris Fallin assert!((*bix as usize) < self.num_blocks()); 746d8357426SChris Fallin display_order.push(*bix as usize); 747d8357426SChris Fallin } 74848cf2c2fSChris Fallin // Now also take care of those not listed in `final_block_order`. 749d8357426SChris Fallin // This is quadratic, but it's also debug-only code. 750d8357426SChris Fallin for bix in 0..self.num_blocks() { 751d8357426SChris Fallin if display_order.contains(&bix) { 752d8357426SChris Fallin continue; 753d8357426SChris Fallin } 754d8357426SChris Fallin display_order.push(bix); 755d8357426SChris Fallin } 756d8357426SChris Fallin 757d8357426SChris Fallin let mut s = String::new(); 75848cf2c2fSChris Fallin write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap(); 75948cf2c2fSChris Fallin write!(&mut s, " Entry block: {}\n", self.entry).unwrap(); 76048cf2c2fSChris Fallin write!( 76148cf2c2fSChris Fallin &mut s, 76248cf2c2fSChris Fallin " Final block order: {:?}\n", 76348cf2c2fSChris Fallin self.final_block_order 76448cf2c2fSChris Fallin ) 76548cf2c2fSChris Fallin .unwrap(); 766d8357426SChris Fallin 767d8357426SChris Fallin for i in 0..self.num_blocks() { 768d8357426SChris Fallin let block = display_order[i]; 769d8357426SChris Fallin 77048cf2c2fSChris Fallin let omitted = if !self.final_block_order.is_empty() && i >= self.final_block_order.len() 77148cf2c2fSChris Fallin { 772d8357426SChris Fallin "** OMITTED **" 773d8357426SChris Fallin } else { 774d8357426SChris Fallin "" 77548cf2c2fSChris Fallin }; 776d8357426SChris Fallin 77748cf2c2fSChris Fallin write!(&mut s, "Block {}: {}\n", block, omitted).unwrap(); 778d8357426SChris Fallin if let Some(bb) = self.bindex_to_bb(block as BlockIndex) { 77948cf2c2fSChris Fallin write!(&mut s, " (original IR block: {})\n", bb).unwrap(); 780d8357426SChris Fallin } 781d8357426SChris Fallin for succ in self.succs(block as BlockIndex) { 782*4f919c64SBenjamin Bouvier write!(&mut s, " (successor: Block {})\n", succ.get()).unwrap(); 783d8357426SChris Fallin } 784d8357426SChris Fallin let (start, end) = self.block_ranges[block]; 78548cf2c2fSChris Fallin write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap(); 786d8357426SChris Fallin for inst in start..end { 78748cf2c2fSChris Fallin write!( 78848cf2c2fSChris Fallin &mut s, 78948cf2c2fSChris Fallin " Inst {}: {}\n", 790d8357426SChris Fallin inst, 791d8357426SChris Fallin self.insts[inst as usize].show_rru(mb_rru) 79248cf2c2fSChris Fallin ) 79348cf2c2fSChris Fallin .unwrap(); 794d8357426SChris Fallin } 795d8357426SChris Fallin } 796d8357426SChris Fallin 79748cf2c2fSChris Fallin write!(&mut s, "}}}}\n").unwrap(); 798d8357426SChris Fallin 799d8357426SChris Fallin s 800d8357426SChris Fallin } 801d8357426SChris Fallin } 802