1968952abSNick Fitzgerald //! Function inlining infrastructure. 2968952abSNick Fitzgerald //! 3968952abSNick Fitzgerald //! This module provides "inlining as a library" to Cranelift users; it does 4968952abSNick Fitzgerald //! _not_ provide a complete, off-the-shelf inlining solution. Cranelift's 5968952abSNick Fitzgerald //! compilation context is per-function and does not encompass the full call 6968952abSNick Fitzgerald //! graph. It does not know which functions are hot and which are cold, which 7968952abSNick Fitzgerald //! have been marked the equivalent of `#[inline(never)]`, etc... Only the 8968952abSNick Fitzgerald //! Cranelift user can understand these aspects of the full compilation 9968952abSNick Fitzgerald //! pipeline, and these things can be very different between (say) Wasmtime and 10968952abSNick Fitzgerald //! `cg_clif`. Therefore, this module does not attempt to define hueristics for 11968952abSNick Fitzgerald //! when inlining a particular call is likely beneficial. This module only 12968952abSNick Fitzgerald //! provides hooks for the Cranelift user to define whether a given call should 13968952abSNick Fitzgerald //! be inlined or not, and the mechanics to inline a callee into a particular 14968952abSNick Fitzgerald //! call site when directed to do so by the Cranelift user. 15968952abSNick Fitzgerald //! 16968952abSNick Fitzgerald //! The top-level inlining entry point during Cranelift compilation is 17968952abSNick Fitzgerald //! [`Context::inline`][crate::Context::inline]. It takes an [`Inline`] trait 18968952abSNick Fitzgerald //! implementation, which is authored by the Cranelift user and directs 19968952abSNick Fitzgerald //! Cranelift whether to inline a particular call, and, when inlining, gives 20968952abSNick Fitzgerald //! Cranelift the body of the callee that is to be inlined. 21968952abSNick Fitzgerald 22968952abSNick Fitzgerald use crate::cursor::{Cursor as _, FuncCursor}; 234590076fSChris Fallin use crate::ir::{self, ExceptionTableData, ExceptionTableItem, InstBuilder as _}; 24968952abSNick Fitzgerald use crate::result::CodegenResult; 25968952abSNick Fitzgerald use crate::trace; 26968952abSNick Fitzgerald use crate::traversals::Dfs; 27968952abSNick Fitzgerald use alloc::borrow::Cow; 28968952abSNick Fitzgerald use alloc::vec::Vec; 29968952abSNick Fitzgerald use cranelift_entity::{SecondaryMap, packed_option::PackedOption}; 30968952abSNick Fitzgerald use smallvec::SmallVec; 31968952abSNick Fitzgerald 32968952abSNick Fitzgerald type SmallValueVec = SmallVec<[ir::Value; 8]>; 33968952abSNick Fitzgerald type SmallBlockArgVec = SmallVec<[ir::BlockArg; 8]>; 34968952abSNick Fitzgerald type SmallBlockCallVec = SmallVec<[ir::BlockCall; 8]>; 35968952abSNick Fitzgerald 36968952abSNick Fitzgerald /// A command directing Cranelift whether or not to inline a particular call. 37968952abSNick Fitzgerald pub enum InlineCommand<'a> { 38968952abSNick Fitzgerald /// Keep the call as-is, out-of-line, and do not inline the callee. 39968952abSNick Fitzgerald KeepCall, 40968952abSNick Fitzgerald /// Inline the call, using this function as the body of the callee. 41968952abSNick Fitzgerald /// 42968952abSNick Fitzgerald /// It is the `Inline` implementor's responsibility to ensure that this 43968952abSNick Fitzgerald /// function is the correct callee. Providing the wrong function may result 44968952abSNick Fitzgerald /// in panics during compilation or incorrect runtime behavior. 45968952abSNick Fitzgerald Inline(Cow<'a, ir::Function>), 46968952abSNick Fitzgerald } 47968952abSNick Fitzgerald 48968952abSNick Fitzgerald /// A trait for directing Cranelift whether to inline a particular call or not. 49968952abSNick Fitzgerald /// 50968952abSNick Fitzgerald /// Used in combination with the [`Context::inline`][crate::Context::inline] 51968952abSNick Fitzgerald /// method. 52968952abSNick Fitzgerald pub trait Inline { 53968952abSNick Fitzgerald /// A hook invoked for each direct call instruction in a function, whose 54968952abSNick Fitzgerald /// result determines whether Cranelift should inline a given call. 55968952abSNick Fitzgerald /// 56968952abSNick Fitzgerald /// The Cranelift user is responsible for defining their own hueristics and 57968952abSNick Fitzgerald /// deciding whether inlining the call is beneficial. 58968952abSNick Fitzgerald /// 59968952abSNick Fitzgerald /// When returning a function and directing Cranelift to inline its body 60968952abSNick Fitzgerald /// into the call site, the `Inline` implementer must ensure the following: 61968952abSNick Fitzgerald /// 62968952abSNick Fitzgerald /// * The returned function's signature exactly matches the `callee` 63968952abSNick Fitzgerald /// `FuncRef`'s signature. 64968952abSNick Fitzgerald /// 65968952abSNick Fitzgerald /// * The returned function must be legalized. 66968952abSNick Fitzgerald /// 67968952abSNick Fitzgerald /// * The returned function must be valid (i.e. it must pass the CLIF 68968952abSNick Fitzgerald /// verifier). 69968952abSNick Fitzgerald /// 70968952abSNick Fitzgerald /// * The returned function is a correct and valid implementation of the 71968952abSNick Fitzgerald /// `callee` according to your language's semantics. 72968952abSNick Fitzgerald /// 73968952abSNick Fitzgerald /// Failure to uphold these invariants may result in panics during 74968952abSNick Fitzgerald /// compilation or incorrect runtime behavior in the generated code. 75968952abSNick Fitzgerald fn inline( 76*3ecb338eSNick Fitzgerald &mut self, 77968952abSNick Fitzgerald caller: &ir::Function, 78968952abSNick Fitzgerald call_inst: ir::Inst, 79968952abSNick Fitzgerald call_opcode: ir::Opcode, 80968952abSNick Fitzgerald callee: ir::FuncRef, 81968952abSNick Fitzgerald call_args: &[ir::Value], 82968952abSNick Fitzgerald ) -> InlineCommand<'_>; 83968952abSNick Fitzgerald } 84968952abSNick Fitzgerald 85*3ecb338eSNick Fitzgerald impl<'a, T> Inline for &'a mut T 86968952abSNick Fitzgerald where 87968952abSNick Fitzgerald T: Inline, 88968952abSNick Fitzgerald { 89968952abSNick Fitzgerald fn inline( 90*3ecb338eSNick Fitzgerald &mut self, 91968952abSNick Fitzgerald caller: &ir::Function, 92968952abSNick Fitzgerald inst: ir::Inst, 93968952abSNick Fitzgerald opcode: ir::Opcode, 94968952abSNick Fitzgerald callee: ir::FuncRef, 95968952abSNick Fitzgerald args: &[ir::Value], 96968952abSNick Fitzgerald ) -> InlineCommand<'_> { 97968952abSNick Fitzgerald (*self).inline(caller, inst, opcode, callee, args) 98968952abSNick Fitzgerald } 99968952abSNick Fitzgerald } 100968952abSNick Fitzgerald 101968952abSNick Fitzgerald /// Walk the given function, invoke the `Inline` implementation for each call 102968952abSNick Fitzgerald /// instruction, and inline the callee when directed to do so. 103968952abSNick Fitzgerald /// 104968952abSNick Fitzgerald /// Returns whether any call was inlined. 105*3ecb338eSNick Fitzgerald pub(crate) fn do_inlining( 106*3ecb338eSNick Fitzgerald func: &mut ir::Function, 107*3ecb338eSNick Fitzgerald mut inliner: impl Inline, 108*3ecb338eSNick Fitzgerald ) -> CodegenResult<bool> { 109*3ecb338eSNick Fitzgerald trace!("function {} before inlining: {}", func.name, func); 110*3ecb338eSNick Fitzgerald 111968952abSNick Fitzgerald let mut inlined_any = false; 112968952abSNick Fitzgerald let mut allocs = InliningAllocs::default(); 113968952abSNick Fitzgerald 114968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 115968952abSNick Fitzgerald while let Some(block) = cursor.next_block() { 116968952abSNick Fitzgerald // Always keep track of our previous cursor position. After we inline a 117968952abSNick Fitzgerald // call, replacing the current position with an arbitrary sub-CFG, we 118968952abSNick Fitzgerald // back up to this previous position. This makes sure our cursor is 119968952abSNick Fitzgerald // always at a position that is inserted in the layout and also enables 120968952abSNick Fitzgerald // multi-level inlining, if desired by the user, where we consider any 121968952abSNick Fitzgerald // newly-inlined call instructions for further inlining. 122968952abSNick Fitzgerald let mut prev_pos; 123968952abSNick Fitzgerald 124968952abSNick Fitzgerald while let Some(inst) = { 125968952abSNick Fitzgerald prev_pos = cursor.position(); 126968952abSNick Fitzgerald cursor.next_inst() 127968952abSNick Fitzgerald } { 128968952abSNick Fitzgerald match cursor.func.dfg.insts[inst] { 129968952abSNick Fitzgerald ir::InstructionData::Call { 130968952abSNick Fitzgerald opcode: opcode @ ir::Opcode::Call | opcode @ ir::Opcode::ReturnCall, 131968952abSNick Fitzgerald args: _, 132968952abSNick Fitzgerald func_ref, 133968952abSNick Fitzgerald } => { 134968952abSNick Fitzgerald let args = cursor.func.dfg.inst_args(inst); 135968952abSNick Fitzgerald match inliner.inline(&cursor.func, inst, opcode, func_ref, args) { 136968952abSNick Fitzgerald InlineCommand::KeepCall => continue, 137968952abSNick Fitzgerald InlineCommand::Inline(callee) => { 138968952abSNick Fitzgerald inline_one( 139968952abSNick Fitzgerald &mut allocs, 140968952abSNick Fitzgerald cursor.func, 141968952abSNick Fitzgerald func_ref, 142968952abSNick Fitzgerald block, 143968952abSNick Fitzgerald inst, 144968952abSNick Fitzgerald opcode, 145968952abSNick Fitzgerald &callee, 146968952abSNick Fitzgerald None, 147968952abSNick Fitzgerald ); 148968952abSNick Fitzgerald inlined_any = true; 149968952abSNick Fitzgerald cursor.set_position(prev_pos); 150968952abSNick Fitzgerald } 151968952abSNick Fitzgerald } 152968952abSNick Fitzgerald } 153968952abSNick Fitzgerald ir::InstructionData::TryCall { 154968952abSNick Fitzgerald opcode: opcode @ ir::Opcode::TryCall, 155968952abSNick Fitzgerald args: _, 156968952abSNick Fitzgerald func_ref, 157968952abSNick Fitzgerald exception, 158968952abSNick Fitzgerald } => { 159968952abSNick Fitzgerald let args = cursor.func.dfg.inst_args(inst); 160968952abSNick Fitzgerald match inliner.inline(&cursor.func, inst, opcode, func_ref, args) { 161968952abSNick Fitzgerald InlineCommand::KeepCall => continue, 162968952abSNick Fitzgerald InlineCommand::Inline(callee) => { 163968952abSNick Fitzgerald inline_one( 164968952abSNick Fitzgerald &mut allocs, 165968952abSNick Fitzgerald cursor.func, 166968952abSNick Fitzgerald func_ref, 167968952abSNick Fitzgerald block, 168968952abSNick Fitzgerald inst, 169968952abSNick Fitzgerald opcode, 170968952abSNick Fitzgerald &callee, 171968952abSNick Fitzgerald Some(exception), 172968952abSNick Fitzgerald ); 173968952abSNick Fitzgerald inlined_any = true; 174968952abSNick Fitzgerald cursor.set_position(prev_pos); 175968952abSNick Fitzgerald } 176968952abSNick Fitzgerald } 177968952abSNick Fitzgerald } 178968952abSNick Fitzgerald _ => continue, 179968952abSNick Fitzgerald } 180968952abSNick Fitzgerald } 181968952abSNick Fitzgerald } 182968952abSNick Fitzgerald 183*3ecb338eSNick Fitzgerald if inlined_any { 184*3ecb338eSNick Fitzgerald trace!("function {} after inlining: {}", func.name, func); 185*3ecb338eSNick Fitzgerald } else { 186*3ecb338eSNick Fitzgerald trace!("function {} did not have any callees inlined", func.name); 187*3ecb338eSNick Fitzgerald } 188*3ecb338eSNick Fitzgerald 189968952abSNick Fitzgerald Ok(inlined_any) 190968952abSNick Fitzgerald } 191968952abSNick Fitzgerald 192968952abSNick Fitzgerald #[derive(Default)] 193968952abSNick Fitzgerald struct InliningAllocs { 194968952abSNick Fitzgerald /// Map from callee value to inlined caller value. 195968952abSNick Fitzgerald values: SecondaryMap<ir::Value, PackedOption<ir::Value>>, 196968952abSNick Fitzgerald 197968952abSNick Fitzgerald /// Map from callee constant to inlined caller constant. 198968952abSNick Fitzgerald constants: SecondaryMap<ir::Constant, PackedOption<ir::Constant>>, 199968952abSNick Fitzgerald 200968952abSNick Fitzgerald /// The set of _caller_ inlined call instructions that need exception table 201968952abSNick Fitzgerald /// fixups at the end of inlining. 202968952abSNick Fitzgerald /// 203968952abSNick Fitzgerald /// This includes all kinds of non-returning calls, not just the literal 204968952abSNick Fitzgerald /// `call` instruction: `call_indirect`, `try_call`, `try_call_indirect`, 205968952abSNick Fitzgerald /// etc... However, it does not include `return_call` and 206968952abSNick Fitzgerald /// `return_call_indirect` instructions because the caller cannot catch 207968952abSNick Fitzgerald /// exceptions that those calls throw because the caller is no longer on the 208968952abSNick Fitzgerald /// stack as soon as they are executed. 209968952abSNick Fitzgerald /// 210968952abSNick Fitzgerald /// Note: this is a simple `Vec`, and not an `EntitySet`, because it is very 211968952abSNick Fitzgerald /// sparse: most of the caller's instructions are not inlined call 212968952abSNick Fitzgerald /// instructions. Additionally, we require deterministic iteration order and 213968952abSNick Fitzgerald /// do not require set-membership testing, so a hash set is not a good 214968952abSNick Fitzgerald /// choice either. 215968952abSNick Fitzgerald calls_needing_exception_table_fixup: Vec<ir::Inst>, 216968952abSNick Fitzgerald } 217968952abSNick Fitzgerald 218968952abSNick Fitzgerald impl InliningAllocs { 219968952abSNick Fitzgerald fn reset(&mut self, callee: &ir::Function) { 220968952abSNick Fitzgerald let InliningAllocs { 221968952abSNick Fitzgerald values, 222968952abSNick Fitzgerald constants, 223968952abSNick Fitzgerald calls_needing_exception_table_fixup, 224968952abSNick Fitzgerald } = self; 225968952abSNick Fitzgerald 226968952abSNick Fitzgerald values.clear(); 227968952abSNick Fitzgerald values.resize(callee.dfg.len_values()); 228968952abSNick Fitzgerald 229968952abSNick Fitzgerald constants.clear(); 230968952abSNick Fitzgerald constants.resize(callee.dfg.constants.len()); 231968952abSNick Fitzgerald 232968952abSNick Fitzgerald // Note: We do not reserve capacity for 233968952abSNick Fitzgerald // `calls_needing_exception_table_fixup` because it is a sparse set and 234968952abSNick Fitzgerald // we don't know how large it needs to be ahead of time. 235968952abSNick Fitzgerald calls_needing_exception_table_fixup.clear(); 236968952abSNick Fitzgerald } 237968952abSNick Fitzgerald 238968952abSNick Fitzgerald fn set_inlined_value( 239968952abSNick Fitzgerald &mut self, 240968952abSNick Fitzgerald callee: &ir::Function, 241968952abSNick Fitzgerald callee_val: ir::Value, 242968952abSNick Fitzgerald inlined_val: ir::Value, 243968952abSNick Fitzgerald ) { 244968952abSNick Fitzgerald trace!(" --> callee {callee_val:?} = inlined {inlined_val:?}"); 245968952abSNick Fitzgerald debug_assert!(self.values[callee_val].is_none()); 246968952abSNick Fitzgerald let resolved_callee_val = callee.dfg.resolve_aliases(callee_val); 247968952abSNick Fitzgerald debug_assert!(self.values[resolved_callee_val].is_none()); 248968952abSNick Fitzgerald self.values[resolved_callee_val] = Some(inlined_val).into(); 249968952abSNick Fitzgerald } 250968952abSNick Fitzgerald 251968952abSNick Fitzgerald fn get_inlined_value(&self, callee: &ir::Function, callee_val: ir::Value) -> Option<ir::Value> { 252968952abSNick Fitzgerald let resolved_callee_val = callee.dfg.resolve_aliases(callee_val); 253968952abSNick Fitzgerald self.values[resolved_callee_val].expand() 254968952abSNick Fitzgerald } 255968952abSNick Fitzgerald } 256968952abSNick Fitzgerald 257968952abSNick Fitzgerald /// Inline one particular function call. 258968952abSNick Fitzgerald fn inline_one( 259968952abSNick Fitzgerald allocs: &mut InliningAllocs, 260968952abSNick Fitzgerald func: &mut ir::Function, 261968952abSNick Fitzgerald callee_func_ref: ir::FuncRef, 262968952abSNick Fitzgerald call_block: ir::Block, 263968952abSNick Fitzgerald call_inst: ir::Inst, 264968952abSNick Fitzgerald call_opcode: ir::Opcode, 265968952abSNick Fitzgerald callee: &ir::Function, 266968952abSNick Fitzgerald call_exception_table: Option<ir::ExceptionTable>, 267968952abSNick Fitzgerald ) { 268968952abSNick Fitzgerald trace!( 269968952abSNick Fitzgerald "Inlining call {call_inst:?}: {}\n\ 270968952abSNick Fitzgerald with callee = {callee:?}", 271968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 272968952abSNick Fitzgerald ); 273968952abSNick Fitzgerald 274968952abSNick Fitzgerald // Type check callee signature. 275968952abSNick Fitzgerald let expected_callee_sig = func.dfg.ext_funcs[callee_func_ref].signature; 276968952abSNick Fitzgerald let expected_callee_sig = &func.dfg.signatures[expected_callee_sig]; 277968952abSNick Fitzgerald assert_eq!(expected_callee_sig, &callee.signature); 278968952abSNick Fitzgerald 279968952abSNick Fitzgerald allocs.reset(callee); 280968952abSNick Fitzgerald 281968952abSNick Fitzgerald // First, append various callee entity arenas to the end of the caller's 282968952abSNick Fitzgerald // entity arenas. 283968952abSNick Fitzgerald let entity_map = create_entities(allocs, func, callee); 284968952abSNick Fitzgerald 285968952abSNick Fitzgerald // Inlined prologue: split the call instruction's block at the point of the 286968952abSNick Fitzgerald // call and replace the call with a jump. 287968952abSNick Fitzgerald let return_block = split_off_return_block(func, call_inst, call_opcode, callee); 288968952abSNick Fitzgerald let call_stack_map = replace_call_with_jump(allocs, func, call_inst, callee, &entity_map); 289968952abSNick Fitzgerald 290968952abSNick Fitzgerald // Prepare for translating the actual instructions by inserting the inlined 291968952abSNick Fitzgerald // blocks into the caller's layout in the same order that they appear in the 292968952abSNick Fitzgerald // callee. 293968952abSNick Fitzgerald inline_block_layout(func, call_block, callee, &entity_map); 294968952abSNick Fitzgerald 295968952abSNick Fitzgerald // Translate each instruction from the callee into the caller, 296968952abSNick Fitzgerald // appending them to their associated block in the caller. 297968952abSNick Fitzgerald // 298968952abSNick Fitzgerald // Note that we iterate over the callee with a pre-order traversal so that 299968952abSNick Fitzgerald // we see value defs before uses. 300968952abSNick Fitzgerald for callee_block in Dfs::new().pre_order_iter(callee) { 301968952abSNick Fitzgerald let inlined_block = entity_map.inlined_block(callee_block); 302968952abSNick Fitzgerald trace!( 303968952abSNick Fitzgerald "Processing instructions in callee block {callee_block:?} (inlined block {inlined_block:?}" 304968952abSNick Fitzgerald ); 305968952abSNick Fitzgerald 306968952abSNick Fitzgerald let mut next_callee_inst = callee.layout.first_inst(callee_block); 307968952abSNick Fitzgerald while let Some(callee_inst) = next_callee_inst { 308968952abSNick Fitzgerald trace!( 309968952abSNick Fitzgerald "Processing callee instruction {callee_inst:?}: {}", 310968952abSNick Fitzgerald callee.dfg.display_inst(callee_inst) 311968952abSNick Fitzgerald ); 312968952abSNick Fitzgerald 313968952abSNick Fitzgerald assert_ne!( 314968952abSNick Fitzgerald callee.dfg.insts[callee_inst].opcode(), 315968952abSNick Fitzgerald ir::Opcode::GlobalValue, 316968952abSNick Fitzgerald "callee must already be legalized, we shouldn't see any `global_value` \ 317968952abSNick Fitzgerald instructions when inlining; found {callee_inst:?}: {}", 318968952abSNick Fitzgerald callee.dfg.display_inst(callee_inst) 319968952abSNick Fitzgerald ); 320968952abSNick Fitzgerald 321968952abSNick Fitzgerald // Remap the callee instruction's entities and insert it into the 322968952abSNick Fitzgerald // caller's DFG. 323968952abSNick Fitzgerald let inlined_inst_data = callee.dfg.insts[callee_inst].map(InliningInstRemapper { 324968952abSNick Fitzgerald allocs: &allocs, 325968952abSNick Fitzgerald func, 326968952abSNick Fitzgerald callee, 327968952abSNick Fitzgerald entity_map: &entity_map, 328968952abSNick Fitzgerald }); 329968952abSNick Fitzgerald let inlined_inst = func.dfg.make_inst(inlined_inst_data); 330968952abSNick Fitzgerald func.layout.append_inst(inlined_inst, inlined_block); 331968952abSNick Fitzgerald 332968952abSNick Fitzgerald let opcode = callee.dfg.insts[callee_inst].opcode(); 333968952abSNick Fitzgerald if opcode.is_return() { 334968952abSNick Fitzgerald // Instructions that return do not define any values, so we 335968952abSNick Fitzgerald // don't need to worry about that, but we do need to fix them up 336968952abSNick Fitzgerald // so that they return by jumping to our control-flow join 337968952abSNick Fitzgerald // block, rather than returning from the caller. 338968952abSNick Fitzgerald if let Some(return_block) = return_block { 339968952abSNick Fitzgerald fixup_inst_that_returns( 340968952abSNick Fitzgerald allocs, 341968952abSNick Fitzgerald func, 342968952abSNick Fitzgerald callee, 343968952abSNick Fitzgerald &entity_map, 344968952abSNick Fitzgerald call_opcode, 345968952abSNick Fitzgerald inlined_inst, 346968952abSNick Fitzgerald callee_inst, 347968952abSNick Fitzgerald return_block, 348968952abSNick Fitzgerald call_stack_map.as_ref().map(|es| &**es), 349968952abSNick Fitzgerald ); 350968952abSNick Fitzgerald } else { 351968952abSNick Fitzgerald // If we are inlining a callee that was invoked via 352968952abSNick Fitzgerald // `return_call`, we leave inlined return instructions 353968952abSNick Fitzgerald // as-is: there is no logical caller frame on the stack to 354968952abSNick Fitzgerald // continue to. 355968952abSNick Fitzgerald debug_assert_eq!(call_opcode, ir::Opcode::ReturnCall); 356968952abSNick Fitzgerald } 357968952abSNick Fitzgerald } else { 358968952abSNick Fitzgerald // Make the instruction's result values. 359968952abSNick Fitzgerald let ctrl_typevar = callee.dfg.ctrl_typevar(callee_inst); 360968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ctrl_typevar); 361968952abSNick Fitzgerald 362968952abSNick Fitzgerald // Update the value map for this instruction's defs. 363968952abSNick Fitzgerald let callee_results = callee.dfg.inst_results(callee_inst); 364968952abSNick Fitzgerald let inlined_results = func.dfg.inst_results(inlined_inst); 365968952abSNick Fitzgerald debug_assert_eq!(callee_results.len(), inlined_results.len()); 366968952abSNick Fitzgerald for (callee_val, inlined_val) in callee_results.iter().zip(inlined_results) { 367968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_val, *inlined_val); 368968952abSNick Fitzgerald } 369968952abSNick Fitzgerald 370968952abSNick Fitzgerald if opcode.is_call() { 371968952abSNick Fitzgerald append_stack_map_entries( 372968952abSNick Fitzgerald func, 373968952abSNick Fitzgerald callee, 374968952abSNick Fitzgerald &entity_map, 375968952abSNick Fitzgerald call_stack_map.as_deref(), 376968952abSNick Fitzgerald inlined_inst, 377968952abSNick Fitzgerald callee_inst, 378968952abSNick Fitzgerald ); 379968952abSNick Fitzgerald 380968952abSNick Fitzgerald // When we are inlining a `try_call` call site, we need to merge 381968952abSNick Fitzgerald // the call site's exception table into the inlined calls' 382968952abSNick Fitzgerald // exception tables. This can involve rewriting regular `call`s 383968952abSNick Fitzgerald // into `try_call`s, which requires mutating the CFG because 384968952abSNick Fitzgerald // `try_call` is a block terminator. However, we can't mutate 385968952abSNick Fitzgerald // the CFG in the middle of this traversal because we rely on 386968952abSNick Fitzgerald // the existence of a one-to-one mapping between the callee 387968952abSNick Fitzgerald // layout and the inlined layout. Instead, we record the set of 388968952abSNick Fitzgerald // inlined call instructions that will need fixing up, and 389968952abSNick Fitzgerald // perform that possibly-CFG-mutating exception table merging in 390968952abSNick Fitzgerald // a follow up pass, when we no longer rely on that one-to-one 391968952abSNick Fitzgerald // layout mapping. 392968952abSNick Fitzgerald debug_assert_eq!( 393968952abSNick Fitzgerald call_opcode == ir::Opcode::TryCall, 394968952abSNick Fitzgerald call_exception_table.is_some() 395968952abSNick Fitzgerald ); 396968952abSNick Fitzgerald if call_opcode == ir::Opcode::TryCall { 397968952abSNick Fitzgerald allocs 398968952abSNick Fitzgerald .calls_needing_exception_table_fixup 399968952abSNick Fitzgerald .push(inlined_inst); 400968952abSNick Fitzgerald } 401968952abSNick Fitzgerald } 402968952abSNick Fitzgerald } 403968952abSNick Fitzgerald 404968952abSNick Fitzgerald trace!( 405968952abSNick Fitzgerald " --> inserted inlined instruction {inlined_inst:?}: {}", 406968952abSNick Fitzgerald func.dfg.display_inst(inlined_inst) 407968952abSNick Fitzgerald ); 408968952abSNick Fitzgerald 409968952abSNick Fitzgerald next_callee_inst = callee.layout.next_inst(callee_inst); 410968952abSNick Fitzgerald } 411968952abSNick Fitzgerald } 412968952abSNick Fitzgerald 413e3a607eaSNick Fitzgerald // We copied *all* callee blocks into the caller's layout, but only copied 414e3a607eaSNick Fitzgerald // the callee instructions in *reachable* callee blocks into the caller's 415e3a607eaSNick Fitzgerald // associated blocks. Therefore, any *unreachable* blocks are empty in the 416e3a607eaSNick Fitzgerald // caller, which is invalid CLIF because all blocks must end in a 417e3a607eaSNick Fitzgerald // terminator, so do a quick pass over the inlined blocks and remove any 418e3a607eaSNick Fitzgerald // empty blocks from the caller's layout. 419e3a607eaSNick Fitzgerald for block in entity_map.iter_inlined_blocks(func) { 420e3a607eaSNick Fitzgerald if func.layout.first_inst(block).is_none() { 421e3a607eaSNick Fitzgerald func.layout.remove_block(block); 422e3a607eaSNick Fitzgerald } 423e3a607eaSNick Fitzgerald } 424e3a607eaSNick Fitzgerald 425968952abSNick Fitzgerald // Final step: fixup the exception tables of any inlined calls when we are 426968952abSNick Fitzgerald // inlining a `try_call` site. 427968952abSNick Fitzgerald // 428968952abSNick Fitzgerald // Subtly, this requires rewriting non-catching `call[_indirect]` 429968952abSNick Fitzgerald // instructions into `try_call[_indirect]` instructions so that exceptions 430968952abSNick Fitzgerald // that unwound through the original callee frame and were caught by the 431968952abSNick Fitzgerald // caller's `try_call` do not unwind past this inlined frame. And turning a 432968952abSNick Fitzgerald // `call` into a `try_call` mutates the CFG, breaking our one-to-one mapping 433968952abSNick Fitzgerald // between callee blocks and inlined blocks, so we delay these fixups to 434968952abSNick Fitzgerald // this final step, when we no longer rely on that mapping. 435968952abSNick Fitzgerald debug_assert!( 436968952abSNick Fitzgerald allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some() 437968952abSNick Fitzgerald ); 438968952abSNick Fitzgerald debug_assert_eq!( 439968952abSNick Fitzgerald call_opcode == ir::Opcode::TryCall, 440968952abSNick Fitzgerald call_exception_table.is_some() 441968952abSNick Fitzgerald ); 442968952abSNick Fitzgerald if let Some(call_exception_table) = call_exception_table { 443968952abSNick Fitzgerald fixup_inlined_call_exception_tables(allocs, func, call_exception_table); 444968952abSNick Fitzgerald } 445968952abSNick Fitzgerald } 446968952abSNick Fitzgerald 447968952abSNick Fitzgerald /// Append stack map entries from the caller and callee to the given inlined 448968952abSNick Fitzgerald /// instruction. 449968952abSNick Fitzgerald fn append_stack_map_entries( 450968952abSNick Fitzgerald func: &mut ir::Function, 451968952abSNick Fitzgerald callee: &ir::Function, 452968952abSNick Fitzgerald entity_map: &EntityMap, 453968952abSNick Fitzgerald call_stack_map: Option<&[ir::UserStackMapEntry]>, 454968952abSNick Fitzgerald inlined_inst: ir::Inst, 455968952abSNick Fitzgerald callee_inst: ir::Inst, 456968952abSNick Fitzgerald ) { 457968952abSNick Fitzgerald // Add the caller's stack map to this call. These entries 458968952abSNick Fitzgerald // already refer to caller entities and do not need further 459968952abSNick Fitzgerald // translation. 460968952abSNick Fitzgerald func.dfg.append_user_stack_map_entries( 461968952abSNick Fitzgerald inlined_inst, 462968952abSNick Fitzgerald call_stack_map 463968952abSNick Fitzgerald .iter() 464968952abSNick Fitzgerald .flat_map(|entries| entries.iter().cloned()), 465968952abSNick Fitzgerald ); 466968952abSNick Fitzgerald 467968952abSNick Fitzgerald // Append the callee's stack map to this call. These entries 468968952abSNick Fitzgerald // refer to callee entities and therefore do require 469968952abSNick Fitzgerald // translation into the caller's index space. 470968952abSNick Fitzgerald func.dfg.append_user_stack_map_entries( 471968952abSNick Fitzgerald inlined_inst, 472968952abSNick Fitzgerald callee 473968952abSNick Fitzgerald .dfg 474968952abSNick Fitzgerald .user_stack_map_entries(callee_inst) 475968952abSNick Fitzgerald .iter() 476968952abSNick Fitzgerald .flat_map(|entries| entries.iter()) 477968952abSNick Fitzgerald .map(|entry| ir::UserStackMapEntry { 478968952abSNick Fitzgerald ty: entry.ty, 479968952abSNick Fitzgerald slot: entity_map.inlined_stack_slot(entry.slot), 480968952abSNick Fitzgerald offset: entry.offset, 481968952abSNick Fitzgerald }), 482968952abSNick Fitzgerald ); 483968952abSNick Fitzgerald } 484968952abSNick Fitzgerald 485968952abSNick Fitzgerald /// Create or update the exception tables for any inlined call instructions: 486968952abSNick Fitzgerald /// when inlining at a `try_call` site, we must forward our exceptional edges 487968952abSNick Fitzgerald /// into each inlined call instruction. 488968952abSNick Fitzgerald fn fixup_inlined_call_exception_tables( 489968952abSNick Fitzgerald allocs: &mut InliningAllocs, 490968952abSNick Fitzgerald func: &mut ir::Function, 491968952abSNick Fitzgerald call_exception_table: ir::ExceptionTable, 492968952abSNick Fitzgerald ) { 493968952abSNick Fitzgerald // Split a block at a `call[_indirect]` instruction, detach the 494968952abSNick Fitzgerald // instruction's results, and alias them to the new block's parameters. 495968952abSNick Fitzgerald let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block { 496968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inst].opcode().is_call()); 497968952abSNick Fitzgerald debug_assert!(!func.dfg.insts[inst].opcode().is_terminator()); 498968952abSNick Fitzgerald 499968952abSNick Fitzgerald // Split the block. 500968952abSNick Fitzgerald let next_inst = func 501968952abSNick Fitzgerald .layout 502968952abSNick Fitzgerald .next_inst(inst) 503968952abSNick Fitzgerald .expect("inst is not a terminator, should have a successor"); 504968952abSNick Fitzgerald let new_block = func.dfg.blocks.add(); 505968952abSNick Fitzgerald func.layout.split_block(new_block, next_inst); 506968952abSNick Fitzgerald 507968952abSNick Fitzgerald // `try_call[_indirect]` instructions do not define values themselves; 508968952abSNick Fitzgerald // the normal-return block has parameters for the results. So remove 509968952abSNick Fitzgerald // this instruction's results, create an associated block parameter for 510968952abSNick Fitzgerald // each of them, and alias them to the new block parameter. 511968952abSNick Fitzgerald let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied()); 512968952abSNick Fitzgerald func.dfg.detach_inst_results(inst); 513968952abSNick Fitzgerald for old_result in old_results { 514968952abSNick Fitzgerald let ty = func.dfg.value_type(old_result); 515968952abSNick Fitzgerald let new_block_param = func.dfg.append_block_param(new_block, ty); 516968952abSNick Fitzgerald func.dfg.change_to_alias(old_result, new_block_param); 517968952abSNick Fitzgerald } 518968952abSNick Fitzgerald 519968952abSNick Fitzgerald new_block 520968952abSNick Fitzgerald }; 521968952abSNick Fitzgerald 522968952abSNick Fitzgerald // Clone the caller's exception table, updating it for use in the current 523968952abSNick Fitzgerald // `call[_indirect]` instruction as it becomes a `try_call[_indirect]`. 524968952abSNick Fitzgerald let clone_exception_table_for_this_call = |func: &mut ir::Function, 525968952abSNick Fitzgerald signature: ir::SigRef, 526968952abSNick Fitzgerald new_block: ir::Block| 527968952abSNick Fitzgerald -> ir::ExceptionTable { 528968952abSNick Fitzgerald let mut exception = func.stencil.dfg.exception_tables[call_exception_table] 529968952abSNick Fitzgerald .deep_clone(&mut func.stencil.dfg.value_lists); 530968952abSNick Fitzgerald 531968952abSNick Fitzgerald *exception.signature_mut() = signature; 532968952abSNick Fitzgerald 533968952abSNick Fitzgerald let returns_len = func.dfg.signatures[signature].returns.len(); 534968952abSNick Fitzgerald let returns_len = u32::try_from(returns_len).unwrap(); 535968952abSNick Fitzgerald 536968952abSNick Fitzgerald *exception.normal_return_mut() = ir::BlockCall::new( 537968952abSNick Fitzgerald new_block, 538968952abSNick Fitzgerald (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)), 539968952abSNick Fitzgerald &mut func.dfg.value_lists, 540968952abSNick Fitzgerald ); 541968952abSNick Fitzgerald 542968952abSNick Fitzgerald func.dfg.exception_tables.push(exception) 543968952abSNick Fitzgerald }; 544968952abSNick Fitzgerald 545968952abSNick Fitzgerald for inst in allocs.calls_needing_exception_table_fixup.drain(..) { 546968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inst].opcode().is_call()); 547968952abSNick Fitzgerald debug_assert!(!func.dfg.insts[inst].opcode().is_return()); 548968952abSNick Fitzgerald match func.dfg.insts[inst] { 549968952abSNick Fitzgerald // current_block: 550968952abSNick Fitzgerald // preds... 551968952abSNick Fitzgerald // rets... = call f(args...) 552968952abSNick Fitzgerald // succs... 553968952abSNick Fitzgerald // 554968952abSNick Fitzgerald // becomes 555968952abSNick Fitzgerald // 556968952abSNick Fitzgerald // current_block: 557968952abSNick Fitzgerald // preds... 558968952abSNick Fitzgerald // try_call f(args...), new_block(rets...), [call_exception_table...] 559968952abSNick Fitzgerald // new_block(rets...): 560968952abSNick Fitzgerald // succs... 561968952abSNick Fitzgerald ir::InstructionData::Call { 562968952abSNick Fitzgerald opcode: ir::Opcode::Call, 563968952abSNick Fitzgerald args, 564968952abSNick Fitzgerald func_ref, 565968952abSNick Fitzgerald } => { 566968952abSNick Fitzgerald let new_block = split_block_for_new_try_call(func, inst); 567968952abSNick Fitzgerald let signature = func.dfg.ext_funcs[func_ref].signature; 568968952abSNick Fitzgerald let exception = clone_exception_table_for_this_call(func, signature, new_block); 569968952abSNick Fitzgerald func.dfg.insts[inst] = ir::InstructionData::TryCall { 570968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 571968952abSNick Fitzgerald args, 572968952abSNick Fitzgerald func_ref, 573968952abSNick Fitzgerald exception, 574968952abSNick Fitzgerald }; 575968952abSNick Fitzgerald } 576968952abSNick Fitzgerald 577968952abSNick Fitzgerald // current_block: 578968952abSNick Fitzgerald // preds... 579968952abSNick Fitzgerald // rets... = call_indirect sig, val(args...) 580968952abSNick Fitzgerald // succs... 581968952abSNick Fitzgerald // 582968952abSNick Fitzgerald // becomes 583968952abSNick Fitzgerald // 584968952abSNick Fitzgerald // current_block: 585968952abSNick Fitzgerald // preds... 586968952abSNick Fitzgerald // try_call_indirect sig, val(args...), new_block(rets...), [call_exception_table...] 587968952abSNick Fitzgerald // new_block(rets...): 588968952abSNick Fitzgerald // succs... 589968952abSNick Fitzgerald ir::InstructionData::CallIndirect { 590968952abSNick Fitzgerald opcode: ir::Opcode::CallIndirect, 591968952abSNick Fitzgerald args, 592968952abSNick Fitzgerald sig_ref, 593968952abSNick Fitzgerald } => { 594968952abSNick Fitzgerald let new_block = split_block_for_new_try_call(func, inst); 595968952abSNick Fitzgerald let exception = clone_exception_table_for_this_call(func, sig_ref, new_block); 596968952abSNick Fitzgerald func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect { 597968952abSNick Fitzgerald opcode: ir::Opcode::TryCallIndirect, 598968952abSNick Fitzgerald args, 599968952abSNick Fitzgerald exception, 600968952abSNick Fitzgerald }; 601968952abSNick Fitzgerald } 602968952abSNick Fitzgerald 603968952abSNick Fitzgerald // For `try_call[_indirect]` instructions, we just need to merge the 604968952abSNick Fitzgerald // exception tables. 605968952abSNick Fitzgerald ir::InstructionData::TryCall { 606968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 607968952abSNick Fitzgerald exception, 608968952abSNick Fitzgerald .. 609968952abSNick Fitzgerald } 610968952abSNick Fitzgerald | ir::InstructionData::TryCallIndirect { 611968952abSNick Fitzgerald opcode: ir::Opcode::TryCallIndirect, 612968952abSNick Fitzgerald exception, 613968952abSNick Fitzgerald .. 614968952abSNick Fitzgerald } => { 6154590076fSChris Fallin // Construct a new exception table that consists of 6164590076fSChris Fallin // the inlined instruction's exception table match 6174590076fSChris Fallin // sequence, with the inlining site's exception table 6184590076fSChris Fallin // appended. This will ensure that the first-match 6194590076fSChris Fallin // semantics emulates the original behavior of 6204590076fSChris Fallin // matching in the inner frame first. 6214590076fSChris Fallin let sig = func.dfg.exception_tables[exception].signature(); 6224590076fSChris Fallin let normal_return = *func.dfg.exception_tables[exception].normal_return(); 6234590076fSChris Fallin let exception_data = ExceptionTableData::new( 6244590076fSChris Fallin sig, 6254590076fSChris Fallin normal_return, 626968952abSNick Fitzgerald func.dfg.exception_tables[exception] 6274590076fSChris Fallin .items() 6284590076fSChris Fallin .chain(func.dfg.exception_tables[call_exception_table].items()), 6294590076fSChris Fallin ) 6304590076fSChris Fallin .deep_clone(&mut func.dfg.value_lists); 631968952abSNick Fitzgerald 6324590076fSChris Fallin func.dfg.exception_tables[exception] = exception_data; 633968952abSNick Fitzgerald } 634968952abSNick Fitzgerald 635968952abSNick Fitzgerald otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"), 636968952abSNick Fitzgerald } 637968952abSNick Fitzgerald } 638968952abSNick Fitzgerald } 639968952abSNick Fitzgerald 640968952abSNick Fitzgerald /// After having created an inlined version of a callee instruction that returns 641968952abSNick Fitzgerald /// in the caller, we need to fix it up so that it doesn't actually return 642968952abSNick Fitzgerald /// (since we are already in the caller's frame) and instead just jumps to the 643968952abSNick Fitzgerald /// control-flow join point. 644968952abSNick Fitzgerald fn fixup_inst_that_returns( 645968952abSNick Fitzgerald allocs: &mut InliningAllocs, 646968952abSNick Fitzgerald func: &mut ir::Function, 647968952abSNick Fitzgerald callee: &ir::Function, 648968952abSNick Fitzgerald entity_map: &EntityMap, 649968952abSNick Fitzgerald call_opcode: ir::Opcode, 650968952abSNick Fitzgerald inlined_inst: ir::Inst, 651968952abSNick Fitzgerald callee_inst: ir::Inst, 652968952abSNick Fitzgerald return_block: ir::Block, 653968952abSNick Fitzgerald call_stack_map: Option<&[ir::UserStackMapEntry]>, 654968952abSNick Fitzgerald ) { 655968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return()); 656968952abSNick Fitzgerald match func.dfg.insts[inlined_inst] { 657968952abSNick Fitzgerald // return rets... 658968952abSNick Fitzgerald // 659968952abSNick Fitzgerald // becomes 660968952abSNick Fitzgerald // 661968952abSNick Fitzgerald // jump return_block(rets...) 662968952abSNick Fitzgerald ir::InstructionData::MultiAry { 663968952abSNick Fitzgerald opcode: ir::Opcode::Return, 664968952abSNick Fitzgerald args, 665968952abSNick Fitzgerald } => { 666968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 667968952abSNick Fitzgerald args.as_slice(&func.dfg.value_lists) 668968952abSNick Fitzgerald .iter() 669968952abSNick Fitzgerald .copied() 670968952abSNick Fitzgerald .map(|v| v.into()), 671968952abSNick Fitzgerald ); 672968952abSNick Fitzgerald func.dfg.replace(inlined_inst).jump(return_block, &rets); 673968952abSNick Fitzgerald } 674968952abSNick Fitzgerald 675968952abSNick Fitzgerald // return_call f(args...) 676968952abSNick Fitzgerald // 677968952abSNick Fitzgerald // becomes 678968952abSNick Fitzgerald // 679968952abSNick Fitzgerald // rets... = call f(args...) 680968952abSNick Fitzgerald // jump return_block(rets...) 681968952abSNick Fitzgerald ir::InstructionData::Call { 682968952abSNick Fitzgerald opcode: ir::Opcode::ReturnCall, 683968952abSNick Fitzgerald args, 684968952abSNick Fitzgerald func_ref, 685968952abSNick Fitzgerald } => { 686968952abSNick Fitzgerald func.dfg.insts[inlined_inst] = ir::InstructionData::Call { 687968952abSNick Fitzgerald opcode: ir::Opcode::Call, 688968952abSNick Fitzgerald args, 689968952abSNick Fitzgerald func_ref, 690968952abSNick Fitzgerald }; 691968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ir::types::INVALID); 692968952abSNick Fitzgerald 693968952abSNick Fitzgerald append_stack_map_entries( 694968952abSNick Fitzgerald func, 695968952abSNick Fitzgerald callee, 696968952abSNick Fitzgerald &entity_map, 697968952abSNick Fitzgerald call_stack_map, 698968952abSNick Fitzgerald inlined_inst, 699968952abSNick Fitzgerald callee_inst, 700968952abSNick Fitzgerald ); 701968952abSNick Fitzgerald 702968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 703968952abSNick Fitzgerald func.dfg 704968952abSNick Fitzgerald .inst_results(inlined_inst) 705968952abSNick Fitzgerald .iter() 706968952abSNick Fitzgerald .copied() 707968952abSNick Fitzgerald .map(|v| v.into()), 708968952abSNick Fitzgerald ); 709968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 710968952abSNick Fitzgerald cursor.goto_after_inst(inlined_inst); 711968952abSNick Fitzgerald cursor.ins().jump(return_block, &rets); 712968952abSNick Fitzgerald 713968952abSNick Fitzgerald if call_opcode == ir::Opcode::TryCall { 714968952abSNick Fitzgerald allocs 715968952abSNick Fitzgerald .calls_needing_exception_table_fixup 716968952abSNick Fitzgerald .push(inlined_inst); 717968952abSNick Fitzgerald } 718968952abSNick Fitzgerald } 719968952abSNick Fitzgerald 720968952abSNick Fitzgerald // return_call_indirect val(args...) 721968952abSNick Fitzgerald // 722968952abSNick Fitzgerald // becomes 723968952abSNick Fitzgerald // 724968952abSNick Fitzgerald // rets... = call_indirect val(args...) 725968952abSNick Fitzgerald // jump return_block(rets...) 726968952abSNick Fitzgerald ir::InstructionData::CallIndirect { 727968952abSNick Fitzgerald opcode: ir::Opcode::ReturnCallIndirect, 728968952abSNick Fitzgerald args, 729968952abSNick Fitzgerald sig_ref, 730968952abSNick Fitzgerald } => { 731968952abSNick Fitzgerald func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect { 732968952abSNick Fitzgerald opcode: ir::Opcode::CallIndirect, 733968952abSNick Fitzgerald args, 734968952abSNick Fitzgerald sig_ref, 735968952abSNick Fitzgerald }; 736968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ir::types::INVALID); 737968952abSNick Fitzgerald 738968952abSNick Fitzgerald append_stack_map_entries( 739968952abSNick Fitzgerald func, 740968952abSNick Fitzgerald callee, 741968952abSNick Fitzgerald &entity_map, 742968952abSNick Fitzgerald call_stack_map, 743968952abSNick Fitzgerald inlined_inst, 744968952abSNick Fitzgerald callee_inst, 745968952abSNick Fitzgerald ); 746968952abSNick Fitzgerald 747968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 748968952abSNick Fitzgerald func.dfg 749968952abSNick Fitzgerald .inst_results(inlined_inst) 750968952abSNick Fitzgerald .iter() 751968952abSNick Fitzgerald .copied() 752968952abSNick Fitzgerald .map(|v| v.into()), 753968952abSNick Fitzgerald ); 754968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 755968952abSNick Fitzgerald cursor.goto_after_inst(inlined_inst); 756968952abSNick Fitzgerald cursor.ins().jump(return_block, &rets); 757968952abSNick Fitzgerald 758968952abSNick Fitzgerald if call_opcode == ir::Opcode::TryCall { 759968952abSNick Fitzgerald allocs 760968952abSNick Fitzgerald .calls_needing_exception_table_fixup 761968952abSNick Fitzgerald .push(inlined_inst); 762968952abSNick Fitzgerald } 763968952abSNick Fitzgerald } 764968952abSNick Fitzgerald 765968952abSNick Fitzgerald inst_data => unreachable!( 766968952abSNick Fitzgerald "should have handled all `is_return() == true` instructions above; \ 767968952abSNick Fitzgerald got {inst_data:?}" 768968952abSNick Fitzgerald ), 769968952abSNick Fitzgerald } 770968952abSNick Fitzgerald } 771968952abSNick Fitzgerald 772968952abSNick Fitzgerald /// An `InstructionMapper` implementation that remaps a callee instruction's 773968952abSNick Fitzgerald /// entity references to their new indices in the caller function. 774968952abSNick Fitzgerald struct InliningInstRemapper<'a> { 775968952abSNick Fitzgerald allocs: &'a InliningAllocs, 776968952abSNick Fitzgerald func: &'a mut ir::Function, 777968952abSNick Fitzgerald callee: &'a ir::Function, 778968952abSNick Fitzgerald entity_map: &'a EntityMap, 779968952abSNick Fitzgerald } 780968952abSNick Fitzgerald 781968952abSNick Fitzgerald impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> { 782968952abSNick Fitzgerald fn map_value(&mut self, value: ir::Value) -> ir::Value { 783968952abSNick Fitzgerald self.allocs.get_inlined_value(self.callee, value).expect( 784968952abSNick Fitzgerald "defs come before uses; we should have already inlined all values \ 785968952abSNick Fitzgerald used by an instruction", 786968952abSNick Fitzgerald ) 787968952abSNick Fitzgerald } 788968952abSNick Fitzgerald 789968952abSNick Fitzgerald fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList { 790968952abSNick Fitzgerald let mut inlined_list = ir::ValueList::new(); 791968952abSNick Fitzgerald for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) { 792968952abSNick Fitzgerald let inlined_val = self.map_value(*callee_val); 793968952abSNick Fitzgerald inlined_list.push(inlined_val, &mut self.func.dfg.value_lists); 794968952abSNick Fitzgerald } 795968952abSNick Fitzgerald inlined_list 796968952abSNick Fitzgerald } 797968952abSNick Fitzgerald 798968952abSNick Fitzgerald fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue { 799968952abSNick Fitzgerald self.entity_map.inlined_global_value(global_value) 800968952abSNick Fitzgerald } 801968952abSNick Fitzgerald 802968952abSNick Fitzgerald fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable { 803968952abSNick Fitzgerald let inlined_default = 804968952abSNick Fitzgerald self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block()); 805968952abSNick Fitzgerald let inlined_table = self.callee.dfg.jump_tables[jump_table] 806968952abSNick Fitzgerald .as_slice() 807968952abSNick Fitzgerald .iter() 808968952abSNick Fitzgerald .map(|callee_block_call| self.map_block_call(*callee_block_call)) 809968952abSNick Fitzgerald .collect::<SmallBlockCallVec>(); 810968952abSNick Fitzgerald self.func 811968952abSNick Fitzgerald .dfg 812968952abSNick Fitzgerald .jump_tables 813968952abSNick Fitzgerald .push(ir::JumpTableData::new(inlined_default, &inlined_table)) 814968952abSNick Fitzgerald } 815968952abSNick Fitzgerald 816968952abSNick Fitzgerald fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable { 817968952abSNick Fitzgerald let exception_table = &self.callee.dfg.exception_tables[exception_table]; 818968952abSNick Fitzgerald let inlined_sig_ref = self.map_sig_ref(exception_table.signature()); 819968952abSNick Fitzgerald let inlined_normal_return = self.map_block_call(*exception_table.normal_return()); 820968952abSNick Fitzgerald let inlined_table = exception_table 8214590076fSChris Fallin .items() 8224590076fSChris Fallin .map(|item| match item { 8234590076fSChris Fallin ExceptionTableItem::Tag(tag, block_call) => { 8244590076fSChris Fallin ExceptionTableItem::Tag(tag, self.map_block_call(block_call)) 8254590076fSChris Fallin } 8264590076fSChris Fallin ExceptionTableItem::Default(block_call) => { 8274590076fSChris Fallin ExceptionTableItem::Default(self.map_block_call(block_call)) 8284590076fSChris Fallin } 8294590076fSChris Fallin ExceptionTableItem::Context(value) => { 8304590076fSChris Fallin ExceptionTableItem::Context(self.map_value(value)) 8314590076fSChris Fallin } 8324590076fSChris Fallin }) 833968952abSNick Fitzgerald .collect::<SmallVec<[_; 8]>>(); 834968952abSNick Fitzgerald self.func 835968952abSNick Fitzgerald .dfg 836968952abSNick Fitzgerald .exception_tables 837968952abSNick Fitzgerald .push(ir::ExceptionTableData::new( 838968952abSNick Fitzgerald inlined_sig_ref, 839968952abSNick Fitzgerald inlined_normal_return, 840968952abSNick Fitzgerald inlined_table, 841968952abSNick Fitzgerald )) 842968952abSNick Fitzgerald } 843968952abSNick Fitzgerald 844968952abSNick Fitzgerald fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall { 845968952abSNick Fitzgerald let callee_block = block_call.block(&self.callee.dfg.value_lists); 846968952abSNick Fitzgerald let inlined_block = self.entity_map.inlined_block(callee_block); 847968952abSNick Fitzgerald let args = block_call 848968952abSNick Fitzgerald .args(&self.callee.dfg.value_lists) 849968952abSNick Fitzgerald .map(|arg| match arg { 850968952abSNick Fitzgerald ir::BlockArg::Value(value) => self.map_value(value).into(), 851968952abSNick Fitzgerald ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg, 852968952abSNick Fitzgerald }) 853968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 854968952abSNick Fitzgerald ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists) 855968952abSNick Fitzgerald } 856968952abSNick Fitzgerald 857968952abSNick Fitzgerald fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef { 858968952abSNick Fitzgerald self.entity_map.inlined_func_ref(func_ref) 859968952abSNick Fitzgerald } 860968952abSNick Fitzgerald 861968952abSNick Fitzgerald fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef { 862968952abSNick Fitzgerald self.entity_map.inlined_sig_ref(sig_ref) 863968952abSNick Fitzgerald } 864968952abSNick Fitzgerald 865968952abSNick Fitzgerald fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot { 866968952abSNick Fitzgerald self.entity_map.inlined_stack_slot(stack_slot) 867968952abSNick Fitzgerald } 868968952abSNick Fitzgerald 869968952abSNick Fitzgerald fn map_dynamic_stack_slot( 870968952abSNick Fitzgerald &mut self, 871968952abSNick Fitzgerald dynamic_stack_slot: ir::DynamicStackSlot, 872968952abSNick Fitzgerald ) -> ir::DynamicStackSlot { 873968952abSNick Fitzgerald self.entity_map 874968952abSNick Fitzgerald .inlined_dynamic_stack_slot(dynamic_stack_slot) 875968952abSNick Fitzgerald } 876968952abSNick Fitzgerald 877968952abSNick Fitzgerald fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant { 878968952abSNick Fitzgerald self.allocs 879968952abSNick Fitzgerald .constants 880968952abSNick Fitzgerald .get(constant) 881968952abSNick Fitzgerald .and_then(|o| o.expand()) 882968952abSNick Fitzgerald .expect("should have inlined all callee constants") 883968952abSNick Fitzgerald } 884968952abSNick Fitzgerald 885968952abSNick Fitzgerald fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate { 886968952abSNick Fitzgerald self.entity_map.inlined_immediate(immediate) 887968952abSNick Fitzgerald } 888968952abSNick Fitzgerald } 889968952abSNick Fitzgerald 890968952abSNick Fitzgerald /// Inline the callee's layout into the caller's layout. 891968952abSNick Fitzgerald fn inline_block_layout( 892968952abSNick Fitzgerald func: &mut ir::Function, 893968952abSNick Fitzgerald call_block: ir::Block, 894968952abSNick Fitzgerald callee: &ir::Function, 895968952abSNick Fitzgerald entity_map: &EntityMap, 896968952abSNick Fitzgerald ) { 897968952abSNick Fitzgerald // Iterate over callee blocks in layout order, inserting their associated 898968952abSNick Fitzgerald // inlined block into the caller's layout. 899968952abSNick Fitzgerald let mut prev_inlined_block = call_block; 900968952abSNick Fitzgerald let mut next_callee_block = callee.layout.entry_block(); 901968952abSNick Fitzgerald while let Some(callee_block) = next_callee_block { 902968952abSNick Fitzgerald let inlined_block = entity_map.inlined_block(callee_block); 903968952abSNick Fitzgerald func.layout 904968952abSNick Fitzgerald .insert_block_after(inlined_block, prev_inlined_block); 905968952abSNick Fitzgerald 906968952abSNick Fitzgerald prev_inlined_block = inlined_block; 907968952abSNick Fitzgerald next_callee_block = callee.layout.next_block(callee_block); 908968952abSNick Fitzgerald } 909968952abSNick Fitzgerald } 910968952abSNick Fitzgerald 911968952abSNick Fitzgerald /// Split the call instruction's block just after the call instruction to create 912968952abSNick Fitzgerald /// the point where control-flow joins after the inlined callee "returns". 913968952abSNick Fitzgerald /// 914968952abSNick Fitzgerald /// Note that tail calls do not return to the caller and therefore do not have a 915968952abSNick Fitzgerald /// control-flow join point. 916968952abSNick Fitzgerald fn split_off_return_block( 917968952abSNick Fitzgerald func: &mut ir::Function, 918968952abSNick Fitzgerald call_inst: ir::Inst, 919968952abSNick Fitzgerald opcode: ir::Opcode, 920968952abSNick Fitzgerald callee: &ir::Function, 921968952abSNick Fitzgerald ) -> Option<ir::Block> { 922968952abSNick Fitzgerald // When the `call_inst` is not a block terminator, we need to split the 923968952abSNick Fitzgerald // block. 924968952abSNick Fitzgerald let return_block = func.layout.next_inst(call_inst).map(|next_inst| { 925968952abSNick Fitzgerald let return_block = func.dfg.blocks.add(); 926968952abSNick Fitzgerald func.layout.split_block(return_block, next_inst); 927968952abSNick Fitzgerald 928968952abSNick Fitzgerald // Add block parameters for each return value and alias the call 929968952abSNick Fitzgerald // instruction's results to them. 930968952abSNick Fitzgerald let old_results = 931968952abSNick Fitzgerald SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied()); 932968952abSNick Fitzgerald debug_assert_eq!(old_results.len(), callee.signature.returns.len()); 933968952abSNick Fitzgerald func.dfg.detach_inst_results(call_inst); 934968952abSNick Fitzgerald for (abi, old_val) in callee.signature.returns.iter().zip(old_results) { 935968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val)); 936968952abSNick Fitzgerald let ret_param = func.dfg.append_block_param(return_block, abi.value_type); 937968952abSNick Fitzgerald func.dfg.change_to_alias(old_val, ret_param); 938968952abSNick Fitzgerald } 939968952abSNick Fitzgerald 940968952abSNick Fitzgerald return_block 941968952abSNick Fitzgerald }); 942968952abSNick Fitzgerald 943968952abSNick Fitzgerald // When the `call_inst` is a block terminator, then it is either a 944968952abSNick Fitzgerald // `return_call` or a `try_call`: 945968952abSNick Fitzgerald // 946968952abSNick Fitzgerald // * For `return_call`s, we don't have a control-flow join point, because 947968952abSNick Fitzgerald // the caller permanently transfers control to the callee. 948968952abSNick Fitzgerald // 949968952abSNick Fitzgerald // * For `try_call`s, we probably already have a block for the control-flow 950968952abSNick Fitzgerald // join point, but it isn't guaranteed: the `try_call` might ignore the 951968952abSNick Fitzgerald // call's returns and not forward them to the normal-return block or it 952968952abSNick Fitzgerald // might also pass additional arguments. We can only reuse the existing 953968952abSNick Fitzgerald // normal-return block when the `try_call` forwards exactly our callee's 954968952abSNick Fitzgerald // returns to that block (and therefore that block's parameter types also 955968952abSNick Fitzgerald // exactly match the callee's return types). Otherwise, we must create a new 956968952abSNick Fitzgerald // return block that forwards to the existing normal-return 957968952abSNick Fitzgerald // block. (Elsewhere, at the end of inlining, we will also update any inlined 958968952abSNick Fitzgerald // calls to forward any raised exceptions to the caller's exception table, 959968952abSNick Fitzgerald // as necessary.) 960968952abSNick Fitzgerald // 961968952abSNick Fitzgerald // Finally, note that reusing the normal-return's target block is just an 962968952abSNick Fitzgerald // optimization to emit a simpler CFG when we can, and is not 963968952abSNick Fitzgerald // fundamentally required for correctness. We could always insert a 964968952abSNick Fitzgerald // temporary block as our control-flow join point that then forwards to 965968952abSNick Fitzgerald // the normal-return's target block. However, at the time of writing, 966968952abSNick Fitzgerald // Cranelift doesn't currently do any jump-threading or branch 967968952abSNick Fitzgerald // simplification in the mid-end, and removing unnecessary blocks in this 968968952abSNick Fitzgerald // way can help some subsequent mid-end optimizations. If, in the future, 969968952abSNick Fitzgerald // we gain support for jump-threading optimizations in the mid-end, we can 970968952abSNick Fitzgerald // come back and simplify the below code a bit to always generate the 971968952abSNick Fitzgerald // temporary block, and then rely on the subsequent optimizations to clean 972968952abSNick Fitzgerald // everything up. 973968952abSNick Fitzgerald debug_assert_eq!( 974968952abSNick Fitzgerald return_block.is_none(), 975968952abSNick Fitzgerald opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall, 976968952abSNick Fitzgerald ); 977968952abSNick Fitzgerald return_block.or_else(|| match func.dfg.insts[call_inst] { 978968952abSNick Fitzgerald ir::InstructionData::TryCall { 979968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 980968952abSNick Fitzgerald args: _, 981968952abSNick Fitzgerald func_ref: _, 982968952abSNick Fitzgerald exception, 983968952abSNick Fitzgerald } => { 984968952abSNick Fitzgerald let normal_return = func.dfg.exception_tables[exception].normal_return(); 985968952abSNick Fitzgerald let normal_return_block = normal_return.block(&func.dfg.value_lists); 986968952abSNick Fitzgerald 987968952abSNick Fitzgerald // Check to see if we can reuse the existing normal-return block. 988968952abSNick Fitzgerald { 989968952abSNick Fitzgerald let normal_return_args = normal_return.args(&func.dfg.value_lists); 990968952abSNick Fitzgerald if normal_return_args.len() == callee.signature.returns.len() 991968952abSNick Fitzgerald && normal_return_args.enumerate().all(|(i, arg)| { 992968952abSNick Fitzgerald let i = u32::try_from(i).unwrap(); 993968952abSNick Fitzgerald arg == ir::BlockArg::TryCallRet(i) 994968952abSNick Fitzgerald }) 995968952abSNick Fitzgerald { 996968952abSNick Fitzgerald return Some(normal_return_block); 997968952abSNick Fitzgerald } 998968952abSNick Fitzgerald } 999968952abSNick Fitzgerald 1000968952abSNick Fitzgerald // Okay, we cannot reuse the normal-return block. Create a new block 1001968952abSNick Fitzgerald // that has the expected block parameter types and have it jump to 1002968952abSNick Fitzgerald // the normal-return block. 1003968952abSNick Fitzgerald let return_block = func.dfg.blocks.add(); 1004968952abSNick Fitzgerald func.layout.insert_block(return_block, normal_return_block); 1005968952abSNick Fitzgerald 1006968952abSNick Fitzgerald let return_block_params = callee 1007968952abSNick Fitzgerald .signature 1008968952abSNick Fitzgerald .returns 1009968952abSNick Fitzgerald .iter() 1010968952abSNick Fitzgerald .map(|abi| func.dfg.append_block_param(return_block, abi.value_type)) 1011968952abSNick Fitzgerald .collect::<SmallValueVec>(); 1012968952abSNick Fitzgerald 1013968952abSNick Fitzgerald let normal_return_args = func.dfg.exception_tables[exception] 1014968952abSNick Fitzgerald .normal_return() 1015968952abSNick Fitzgerald .args(&func.dfg.value_lists) 1016968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 1017968952abSNick Fitzgerald let jump_args = normal_return_args 1018968952abSNick Fitzgerald .into_iter() 1019968952abSNick Fitzgerald .map(|arg| match arg { 1020968952abSNick Fitzgerald ir::BlockArg::Value(value) => ir::BlockArg::Value(value), 1021968952abSNick Fitzgerald ir::BlockArg::TryCallRet(i) => { 1022968952abSNick Fitzgerald let i = usize::try_from(i).unwrap(); 1023968952abSNick Fitzgerald ir::BlockArg::Value(return_block_params[i]) 1024968952abSNick Fitzgerald } 1025968952abSNick Fitzgerald ir::BlockArg::TryCallExn(_) => { 1026968952abSNick Fitzgerald unreachable!("normal-return edges cannot use exceptional results") 1027968952abSNick Fitzgerald } 1028968952abSNick Fitzgerald }) 1029968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 1030968952abSNick Fitzgerald 1031968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 1032968952abSNick Fitzgerald cursor.goto_first_insertion_point(return_block); 1033968952abSNick Fitzgerald cursor.ins().jump(normal_return_block, &jump_args); 1034968952abSNick Fitzgerald 1035968952abSNick Fitzgerald Some(return_block) 1036968952abSNick Fitzgerald } 1037968952abSNick Fitzgerald _ => None, 1038968952abSNick Fitzgerald }) 1039968952abSNick Fitzgerald } 1040968952abSNick Fitzgerald 1041968952abSNick Fitzgerald /// Replace the caller's call instruction with a jump to the caller's inlined 1042968952abSNick Fitzgerald /// copy of the callee's entry block. 1043968952abSNick Fitzgerald /// 1044968952abSNick Fitzgerald /// Also associates the callee's parameters with the caller's arguments in our 1045968952abSNick Fitzgerald /// value map. 1046968952abSNick Fitzgerald /// 1047968952abSNick Fitzgerald /// Returns the caller's stack map entries, if any. 1048968952abSNick Fitzgerald fn replace_call_with_jump( 1049968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1050968952abSNick Fitzgerald func: &mut ir::Function, 1051968952abSNick Fitzgerald call_inst: ir::Inst, 1052968952abSNick Fitzgerald callee: &ir::Function, 1053968952abSNick Fitzgerald entity_map: &EntityMap, 1054968952abSNick Fitzgerald ) -> Option<ir::UserStackMapEntryVec> { 1055968952abSNick Fitzgerald trace!("Replacing `call` with `jump`"); 1056968952abSNick Fitzgerald trace!( 1057968952abSNick Fitzgerald " --> call instruction: {call_inst:?}: {}", 1058968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 1059968952abSNick Fitzgerald ); 1060968952abSNick Fitzgerald 1061968952abSNick Fitzgerald let callee_entry_block = callee 1062968952abSNick Fitzgerald .layout 1063968952abSNick Fitzgerald .entry_block() 1064968952abSNick Fitzgerald .expect("callee function should have an entry block"); 1065968952abSNick Fitzgerald let callee_param_values = callee.dfg.block_params(callee_entry_block); 1066968952abSNick Fitzgerald let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied()); 1067968952abSNick Fitzgerald debug_assert_eq!(callee_param_values.len(), caller_arg_values.len()); 1068968952abSNick Fitzgerald debug_assert_eq!(callee_param_values.len(), callee.signature.params.len()); 1069968952abSNick Fitzgerald for (abi, (callee_param_value, caller_arg_value)) in callee 1070968952abSNick Fitzgerald .signature 1071968952abSNick Fitzgerald .params 1072968952abSNick Fitzgerald .iter() 1073968952abSNick Fitzgerald .zip(callee_param_values.into_iter().zip(caller_arg_values)) 1074968952abSNick Fitzgerald { 1075968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value)); 1076968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value)); 1077968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value); 1078968952abSNick Fitzgerald } 1079968952abSNick Fitzgerald 1080968952abSNick Fitzgerald // Replace the caller's call instruction with a jump to the caller's inlined 1081968952abSNick Fitzgerald // copy of the callee's entry block. 1082968952abSNick Fitzgerald // 1083968952abSNick Fitzgerald // Note that the call block dominates the inlined entry block (and also all 1084968952abSNick Fitzgerald // other inlined blocks) so we can reference the arguments directly, and do 1085968952abSNick Fitzgerald // not need to add block parameters to the inlined entry block. 1086968952abSNick Fitzgerald let inlined_entry_block = entity_map.inlined_block(callee_entry_block); 1087968952abSNick Fitzgerald func.dfg.replace(call_inst).jump(inlined_entry_block, &[]); 1088968952abSNick Fitzgerald trace!( 1089968952abSNick Fitzgerald " --> replaced with jump instruction: {call_inst:?}: {}", 1090968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 1091968952abSNick Fitzgerald ); 1092968952abSNick Fitzgerald 1093968952abSNick Fitzgerald let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst); 1094968952abSNick Fitzgerald stack_map_entries 1095968952abSNick Fitzgerald } 1096968952abSNick Fitzgerald 1097968952abSNick Fitzgerald /// Keeps track of mapping callee entities to their associated inlined caller 1098968952abSNick Fitzgerald /// entities. 1099968952abSNick Fitzgerald #[derive(Default)] 1100968952abSNick Fitzgerald struct EntityMap { 1101968952abSNick Fitzgerald // Rather than doing an implicit, demand-based, DCE'ing translation of 1102968952abSNick Fitzgerald // entities, which would require maps from each callee entity to its 1103968952abSNick Fitzgerald // associated caller entity, we copy all entities into the caller, remember 1104968952abSNick Fitzgerald // each entity's initial offset, and then mapping from the callee to the 1105968952abSNick Fitzgerald // inlined caller entity is just adding that initial offset to the callee's 1106968952abSNick Fitzgerald // index. This should be both faster and simpler than the alternative. Most 1107968952abSNick Fitzgerald // of these sets are relatively small, and they rarely have too much dead 1108968952abSNick Fitzgerald // code in practice, so this is a good trade off. 1109968952abSNick Fitzgerald // 1110968952abSNick Fitzgerald // Note that there are a few kinds of entities that are excluded from the 1111968952abSNick Fitzgerald // `EntityMap`, and for which we do actually take the demand-based approach: 1112968952abSNick Fitzgerald // values and value lists being the notable ones. 1113968952abSNick Fitzgerald block_offset: Option<u32>, 1114968952abSNick Fitzgerald global_value_offset: Option<u32>, 1115968952abSNick Fitzgerald sig_ref_offset: Option<u32>, 1116968952abSNick Fitzgerald func_ref_offset: Option<u32>, 1117968952abSNick Fitzgerald stack_slot_offset: Option<u32>, 1118968952abSNick Fitzgerald dynamic_type_offset: Option<u32>, 1119968952abSNick Fitzgerald dynamic_stack_slot_offset: Option<u32>, 1120968952abSNick Fitzgerald immediate_offset: Option<u32>, 1121968952abSNick Fitzgerald } 1122968952abSNick Fitzgerald 1123968952abSNick Fitzgerald impl EntityMap { 1124968952abSNick Fitzgerald fn inlined_block(&self, callee_block: ir::Block) -> ir::Block { 1125968952abSNick Fitzgerald let offset = self 1126968952abSNick Fitzgerald .block_offset 1127968952abSNick Fitzgerald .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`"); 1128968952abSNick Fitzgerald ir::Block::from_u32(offset + callee_block.as_u32()) 1129968952abSNick Fitzgerald } 1130968952abSNick Fitzgerald 1131e3a607eaSNick Fitzgerald fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> { 1132e3a607eaSNick Fitzgerald let start = self.block_offset.expect( 1133e3a607eaSNick Fitzgerald "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`", 1134e3a607eaSNick Fitzgerald ); 1135e3a607eaSNick Fitzgerald 1136e3a607eaSNick Fitzgerald let end = func.dfg.blocks.len(); 1137e3a607eaSNick Fitzgerald let end = u32::try_from(end).unwrap(); 1138e3a607eaSNick Fitzgerald 1139e3a607eaSNick Fitzgerald (start..end).map(|i| ir::Block::from_u32(i)) 1140e3a607eaSNick Fitzgerald } 1141e3a607eaSNick Fitzgerald 1142968952abSNick Fitzgerald fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue { 1143968952abSNick Fitzgerald let offset = self 1144968952abSNick Fitzgerald .global_value_offset 1145968952abSNick Fitzgerald .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`"); 1146968952abSNick Fitzgerald ir::GlobalValue::from_u32(offset + callee_global_value.as_u32()) 1147968952abSNick Fitzgerald } 1148968952abSNick Fitzgerald 1149968952abSNick Fitzgerald fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef { 1150968952abSNick Fitzgerald let offset = self.sig_ref_offset.expect( 1151968952abSNick Fitzgerald "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`", 1152968952abSNick Fitzgerald ); 1153968952abSNick Fitzgerald ir::SigRef::from_u32(offset + callee_sig_ref.as_u32()) 1154968952abSNick Fitzgerald } 1155968952abSNick Fitzgerald 1156968952abSNick Fitzgerald fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef { 1157968952abSNick Fitzgerald let offset = self.func_ref_offset.expect( 1158968952abSNick Fitzgerald "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`", 1159968952abSNick Fitzgerald ); 1160968952abSNick Fitzgerald ir::FuncRef::from_u32(offset + callee_func_ref.as_u32()) 1161968952abSNick Fitzgerald } 1162968952abSNick Fitzgerald 1163968952abSNick Fitzgerald fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot { 1164968952abSNick Fitzgerald let offset = self.stack_slot_offset.expect( 1165968952abSNick Fitzgerald "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`", 1166968952abSNick Fitzgerald ); 1167968952abSNick Fitzgerald ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32()) 1168968952abSNick Fitzgerald } 1169968952abSNick Fitzgerald 1170968952abSNick Fitzgerald fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType { 1171968952abSNick Fitzgerald let offset = self.dynamic_type_offset.expect( 1172968952abSNick Fitzgerald "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`", 1173968952abSNick Fitzgerald ); 1174968952abSNick Fitzgerald ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32()) 1175968952abSNick Fitzgerald } 1176968952abSNick Fitzgerald 1177968952abSNick Fitzgerald fn inlined_dynamic_stack_slot( 1178968952abSNick Fitzgerald &self, 1179968952abSNick Fitzgerald callee_dynamic_stack_slot: ir::DynamicStackSlot, 1180968952abSNick Fitzgerald ) -> ir::DynamicStackSlot { 1181968952abSNick Fitzgerald let offset = self.dynamic_stack_slot_offset.expect( 1182968952abSNick Fitzgerald "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`", 1183968952abSNick Fitzgerald ); 1184968952abSNick Fitzgerald ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32()) 1185968952abSNick Fitzgerald } 1186968952abSNick Fitzgerald 1187968952abSNick Fitzgerald fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate { 1188968952abSNick Fitzgerald let offset = self.immediate_offset.expect( 1189968952abSNick Fitzgerald "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`", 1190968952abSNick Fitzgerald ); 1191968952abSNick Fitzgerald ir::Immediate::from_u32(offset + callee_immediate.as_u32()) 1192968952abSNick Fitzgerald } 1193968952abSNick Fitzgerald } 1194968952abSNick Fitzgerald 1195968952abSNick Fitzgerald /// Translate all of the callee's various entities into the caller, producing an 1196968952abSNick Fitzgerald /// `EntityMap` that can be used to translate callee entity references into 1197968952abSNick Fitzgerald /// inlined caller entity references. 1198968952abSNick Fitzgerald fn create_entities( 1199968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1200968952abSNick Fitzgerald func: &mut ir::Function, 1201968952abSNick Fitzgerald callee: &ir::Function, 1202968952abSNick Fitzgerald ) -> EntityMap { 1203968952abSNick Fitzgerald let mut entity_map = EntityMap::default(); 1204968952abSNick Fitzgerald 1205968952abSNick Fitzgerald entity_map.block_offset = Some(create_blocks(allocs, func, callee)); 1206968952abSNick Fitzgerald entity_map.global_value_offset = Some(create_global_values(func, callee)); 1207968952abSNick Fitzgerald entity_map.sig_ref_offset = Some(create_sig_refs(func, callee)); 1208968952abSNick Fitzgerald entity_map.func_ref_offset = Some(create_func_refs(func, callee, &entity_map)); 1209968952abSNick Fitzgerald entity_map.stack_slot_offset = Some(create_stack_slots(func, callee)); 1210968952abSNick Fitzgerald entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map)); 1211968952abSNick Fitzgerald entity_map.dynamic_stack_slot_offset = 1212968952abSNick Fitzgerald Some(create_dynamic_stack_slots(func, callee, &entity_map)); 1213968952abSNick Fitzgerald entity_map.immediate_offset = Some(create_immediates(func, callee)); 1214968952abSNick Fitzgerald 1215968952abSNick Fitzgerald // `ir::ConstantData` is deduplicated, so we cannot use our offset scheme 1216968952abSNick Fitzgerald // for `ir::Constant`s. Nonetheless, we still insert them into the caller 1217968952abSNick Fitzgerald // now, at the same time as the rest of our entities. 1218968952abSNick Fitzgerald create_constants(allocs, func, callee); 1219968952abSNick Fitzgerald 1220968952abSNick Fitzgerald entity_map 1221968952abSNick Fitzgerald } 1222968952abSNick Fitzgerald 1223968952abSNick Fitzgerald /// Create inlined blocks in the caller for every block in the callee. 1224968952abSNick Fitzgerald fn create_blocks( 1225968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1226968952abSNick Fitzgerald func: &mut ir::Function, 1227968952abSNick Fitzgerald callee: &ir::Function, 1228968952abSNick Fitzgerald ) -> u32 { 1229968952abSNick Fitzgerald let offset = func.dfg.blocks.len(); 1230968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1231968952abSNick Fitzgerald 1232968952abSNick Fitzgerald func.dfg.blocks.reserve(callee.dfg.blocks.len()); 1233968952abSNick Fitzgerald for callee_block in callee.dfg.blocks.iter() { 1234968952abSNick Fitzgerald let caller_block = func.dfg.blocks.add(); 1235968952abSNick Fitzgerald trace!("Callee {callee_block:?} = inlined {caller_block:?}"); 1236968952abSNick Fitzgerald 1237968952abSNick Fitzgerald if callee.layout.is_cold(callee_block) { 1238968952abSNick Fitzgerald func.layout.set_cold(caller_block); 1239968952abSNick Fitzgerald } 1240968952abSNick Fitzgerald 1241968952abSNick Fitzgerald // Note: the entry block does not need parameters because the only 1242968952abSNick Fitzgerald // predecessor is the call block and we associate the callee's 1243968952abSNick Fitzgerald // parameters with the caller's arguments directly. 1244968952abSNick Fitzgerald if callee.layout.entry_block() != Some(callee_block) { 1245968952abSNick Fitzgerald for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) { 1246968952abSNick Fitzgerald let ty = callee.dfg.value_type(*callee_param); 1247968952abSNick Fitzgerald let caller_param = func.dfg.append_block_param(caller_block, ty); 1248968952abSNick Fitzgerald 1249968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_param, caller_param); 1250968952abSNick Fitzgerald } 1251968952abSNick Fitzgerald } 1252968952abSNick Fitzgerald } 1253968952abSNick Fitzgerald 1254968952abSNick Fitzgerald offset 1255968952abSNick Fitzgerald } 1256968952abSNick Fitzgerald 1257968952abSNick Fitzgerald /// Copy and translate global values from the callee into the caller. 1258968952abSNick Fitzgerald fn create_global_values(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1259968952abSNick Fitzgerald let gv_offset = func.global_values.len(); 1260968952abSNick Fitzgerald let gv_offset = u32::try_from(gv_offset).unwrap(); 1261968952abSNick Fitzgerald 1262968952abSNick Fitzgerald func.global_values.reserve(callee.global_values.len()); 1263968952abSNick Fitzgerald for gv in callee.global_values.values() { 1264968952abSNick Fitzgerald func.global_values.push(match gv { 1265968952abSNick Fitzgerald // These kinds of global values reference other global values, so we 1266968952abSNick Fitzgerald // need to fixup that reference. 1267968952abSNick Fitzgerald ir::GlobalValueData::Load { 1268968952abSNick Fitzgerald base, 1269968952abSNick Fitzgerald offset, 1270968952abSNick Fitzgerald global_type, 1271968952abSNick Fitzgerald flags, 1272968952abSNick Fitzgerald } => ir::GlobalValueData::Load { 1273968952abSNick Fitzgerald base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset), 1274968952abSNick Fitzgerald offset: *offset, 1275968952abSNick Fitzgerald global_type: *global_type, 1276968952abSNick Fitzgerald flags: *flags, 1277968952abSNick Fitzgerald }, 1278968952abSNick Fitzgerald ir::GlobalValueData::IAddImm { 1279968952abSNick Fitzgerald base, 1280968952abSNick Fitzgerald offset, 1281968952abSNick Fitzgerald global_type, 1282968952abSNick Fitzgerald } => ir::GlobalValueData::IAddImm { 1283968952abSNick Fitzgerald base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset), 1284968952abSNick Fitzgerald offset: *offset, 1285968952abSNick Fitzgerald global_type: *global_type, 1286968952abSNick Fitzgerald }, 1287968952abSNick Fitzgerald 1288968952abSNick Fitzgerald // These kinds of global values do not reference other global 1289968952abSNick Fitzgerald // values, so we can just clone them. 1290968952abSNick Fitzgerald ir::GlobalValueData::VMContext 1291968952abSNick Fitzgerald | ir::GlobalValueData::Symbol { .. } 1292968952abSNick Fitzgerald | ir::GlobalValueData::DynScaleTargetConst { .. } => gv.clone(), 1293968952abSNick Fitzgerald }); 1294968952abSNick Fitzgerald } 1295968952abSNick Fitzgerald 1296968952abSNick Fitzgerald gv_offset 1297968952abSNick Fitzgerald } 1298968952abSNick Fitzgerald 1299968952abSNick Fitzgerald /// Copy `ir::SigRef`s from the callee into the caller. 1300968952abSNick Fitzgerald fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1301968952abSNick Fitzgerald let offset = func.dfg.signatures.len(); 1302968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1303968952abSNick Fitzgerald 1304968952abSNick Fitzgerald func.dfg.signatures.reserve(callee.dfg.signatures.len()); 1305968952abSNick Fitzgerald for sig in callee.dfg.signatures.values() { 1306968952abSNick Fitzgerald func.dfg.signatures.push(sig.clone()); 1307968952abSNick Fitzgerald } 1308968952abSNick Fitzgerald 1309968952abSNick Fitzgerald offset 1310968952abSNick Fitzgerald } 1311968952abSNick Fitzgerald 1312968952abSNick Fitzgerald /// Translate `ir::FuncRef`s from the callee into the caller. 1313968952abSNick Fitzgerald fn create_func_refs(func: &mut ir::Function, callee: &ir::Function, entity_map: &EntityMap) -> u32 { 1314968952abSNick Fitzgerald let offset = func.dfg.ext_funcs.len(); 1315968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1316968952abSNick Fitzgerald 1317968952abSNick Fitzgerald func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len()); 1318968952abSNick Fitzgerald for ir::ExtFuncData { 1319968952abSNick Fitzgerald name, 1320968952abSNick Fitzgerald signature, 1321968952abSNick Fitzgerald colocated, 1322968952abSNick Fitzgerald } in callee.dfg.ext_funcs.values() 1323968952abSNick Fitzgerald { 1324968952abSNick Fitzgerald func.dfg.ext_funcs.push(ir::ExtFuncData { 1325968952abSNick Fitzgerald name: name.clone(), 1326968952abSNick Fitzgerald signature: entity_map.inlined_sig_ref(*signature), 1327968952abSNick Fitzgerald colocated: *colocated, 1328968952abSNick Fitzgerald }); 1329968952abSNick Fitzgerald } 1330968952abSNick Fitzgerald 1331968952abSNick Fitzgerald offset 1332968952abSNick Fitzgerald } 1333968952abSNick Fitzgerald 1334968952abSNick Fitzgerald /// Copy stack slots from the callee into the caller. 1335968952abSNick Fitzgerald fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1336968952abSNick Fitzgerald let offset = func.sized_stack_slots.len(); 1337968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1338968952abSNick Fitzgerald 1339968952abSNick Fitzgerald func.sized_stack_slots 1340968952abSNick Fitzgerald .reserve(callee.sized_stack_slots.len()); 1341968952abSNick Fitzgerald for slot in callee.sized_stack_slots.values() { 1342968952abSNick Fitzgerald func.sized_stack_slots.push(slot.clone()); 1343968952abSNick Fitzgerald } 1344968952abSNick Fitzgerald 1345968952abSNick Fitzgerald offset 1346968952abSNick Fitzgerald } 1347968952abSNick Fitzgerald 1348968952abSNick Fitzgerald /// Copy dynamic types from the callee into the caller. 1349968952abSNick Fitzgerald fn create_dynamic_types( 1350968952abSNick Fitzgerald func: &mut ir::Function, 1351968952abSNick Fitzgerald callee: &ir::Function, 1352968952abSNick Fitzgerald entity_map: &EntityMap, 1353968952abSNick Fitzgerald ) -> u32 { 1354968952abSNick Fitzgerald let offset = func.dynamic_stack_slots.len(); 1355968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1356968952abSNick Fitzgerald 1357968952abSNick Fitzgerald func.dfg 1358968952abSNick Fitzgerald .dynamic_types 1359968952abSNick Fitzgerald .reserve(callee.dfg.dynamic_types.len()); 1360968952abSNick Fitzgerald for ir::DynamicTypeData { 1361968952abSNick Fitzgerald base_vector_ty, 1362968952abSNick Fitzgerald dynamic_scale, 1363968952abSNick Fitzgerald } in callee.dfg.dynamic_types.values() 1364968952abSNick Fitzgerald { 1365968952abSNick Fitzgerald func.dfg.dynamic_types.push(ir::DynamicTypeData { 1366968952abSNick Fitzgerald base_vector_ty: *base_vector_ty, 1367968952abSNick Fitzgerald dynamic_scale: entity_map.inlined_global_value(*dynamic_scale), 1368968952abSNick Fitzgerald }); 1369968952abSNick Fitzgerald } 1370968952abSNick Fitzgerald 1371968952abSNick Fitzgerald offset 1372968952abSNick Fitzgerald } 1373968952abSNick Fitzgerald 1374968952abSNick Fitzgerald /// Copy dynamic stack slots from the callee into the caller. 1375968952abSNick Fitzgerald fn create_dynamic_stack_slots( 1376968952abSNick Fitzgerald func: &mut ir::Function, 1377968952abSNick Fitzgerald callee: &ir::Function, 1378968952abSNick Fitzgerald entity_map: &EntityMap, 1379968952abSNick Fitzgerald ) -> u32 { 1380968952abSNick Fitzgerald let offset = func.dynamic_stack_slots.len(); 1381968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1382968952abSNick Fitzgerald 1383968952abSNick Fitzgerald func.dynamic_stack_slots 1384968952abSNick Fitzgerald .reserve(callee.dynamic_stack_slots.len()); 1385968952abSNick Fitzgerald for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() { 1386968952abSNick Fitzgerald func.dynamic_stack_slots.push(ir::DynamicStackSlotData { 1387968952abSNick Fitzgerald kind: *kind, 1388968952abSNick Fitzgerald dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty), 1389968952abSNick Fitzgerald }); 1390968952abSNick Fitzgerald } 1391968952abSNick Fitzgerald 1392968952abSNick Fitzgerald offset 1393968952abSNick Fitzgerald } 1394968952abSNick Fitzgerald 1395968952abSNick Fitzgerald /// Copy immediates from the callee into the caller. 1396968952abSNick Fitzgerald fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1397968952abSNick Fitzgerald let offset = func.dfg.immediates.len(); 1398968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1399968952abSNick Fitzgerald 1400968952abSNick Fitzgerald func.dfg.immediates.reserve(callee.dfg.immediates.len()); 1401968952abSNick Fitzgerald for imm in callee.dfg.immediates.values() { 1402968952abSNick Fitzgerald func.dfg.immediates.push(imm.clone()); 1403968952abSNick Fitzgerald } 1404968952abSNick Fitzgerald 1405968952abSNick Fitzgerald offset 1406968952abSNick Fitzgerald } 1407968952abSNick Fitzgerald 1408968952abSNick Fitzgerald /// Copy constants from the callee into the caller. 1409968952abSNick Fitzgerald fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) { 1410968952abSNick Fitzgerald for (callee_constant, data) in callee.dfg.constants.iter() { 1411968952abSNick Fitzgerald let inlined_constant = func.dfg.constants.insert(data.clone()); 1412968952abSNick Fitzgerald allocs.constants[*callee_constant] = Some(inlined_constant).into(); 1413968952abSNick Fitzgerald } 1414968952abSNick Fitzgerald } 1415