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}; 23a3d6e407SChris Fallin use crate::ir::{self, DebugTag, 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, 40dcedcbf5SNick Fitzgerald 41968952abSNick Fitzgerald /// Inline the call, using this function as the body of the callee. 42968952abSNick Fitzgerald /// 43968952abSNick Fitzgerald /// It is the `Inline` implementor's responsibility to ensure that this 44968952abSNick Fitzgerald /// function is the correct callee. Providing the wrong function may result 45968952abSNick Fitzgerald /// in panics during compilation or incorrect runtime behavior. 46dcedcbf5SNick Fitzgerald Inline { 47dcedcbf5SNick Fitzgerald /// The callee function's body. 48dcedcbf5SNick Fitzgerald callee: Cow<'a, ir::Function>, 49dcedcbf5SNick Fitzgerald /// Whether to visit any function calls within the callee body after 50dcedcbf5SNick Fitzgerald /// inlining and consider them for further inlining. 51dcedcbf5SNick Fitzgerald visit_callee: bool, 52dcedcbf5SNick Fitzgerald }, 53968952abSNick Fitzgerald } 54968952abSNick Fitzgerald 55968952abSNick Fitzgerald /// A trait for directing Cranelift whether to inline a particular call or not. 56968952abSNick Fitzgerald /// 57968952abSNick Fitzgerald /// Used in combination with the [`Context::inline`][crate::Context::inline] 58968952abSNick Fitzgerald /// method. 59968952abSNick Fitzgerald pub trait Inline { 60968952abSNick Fitzgerald /// A hook invoked for each direct call instruction in a function, whose 61968952abSNick Fitzgerald /// result determines whether Cranelift should inline a given call. 62968952abSNick Fitzgerald /// 63968952abSNick Fitzgerald /// The Cranelift user is responsible for defining their own hueristics and 64968952abSNick Fitzgerald /// deciding whether inlining the call is beneficial. 65968952abSNick Fitzgerald /// 66968952abSNick Fitzgerald /// When returning a function and directing Cranelift to inline its body 67968952abSNick Fitzgerald /// into the call site, the `Inline` implementer must ensure the following: 68968952abSNick Fitzgerald /// 69968952abSNick Fitzgerald /// * The returned function's signature exactly matches the `callee` 70968952abSNick Fitzgerald /// `FuncRef`'s signature. 71968952abSNick Fitzgerald /// 72968952abSNick Fitzgerald /// * The returned function must be legalized. 73968952abSNick Fitzgerald /// 74968952abSNick Fitzgerald /// * The returned function must be valid (i.e. it must pass the CLIF 75968952abSNick Fitzgerald /// verifier). 76968952abSNick Fitzgerald /// 77968952abSNick Fitzgerald /// * The returned function is a correct and valid implementation of the 78968952abSNick Fitzgerald /// `callee` according to your language's semantics. 79968952abSNick Fitzgerald /// 80968952abSNick Fitzgerald /// Failure to uphold these invariants may result in panics during 81968952abSNick Fitzgerald /// compilation or incorrect runtime behavior in the generated code. 82968952abSNick Fitzgerald fn inline( 833ecb338eSNick Fitzgerald &mut self, 84968952abSNick Fitzgerald caller: &ir::Function, 85968952abSNick Fitzgerald call_inst: ir::Inst, 86968952abSNick Fitzgerald call_opcode: ir::Opcode, 87968952abSNick Fitzgerald callee: ir::FuncRef, 88968952abSNick Fitzgerald call_args: &[ir::Value], 89968952abSNick Fitzgerald ) -> InlineCommand<'_>; 90968952abSNick Fitzgerald } 91968952abSNick Fitzgerald 923ecb338eSNick Fitzgerald impl<'a, T> Inline for &'a mut T 93968952abSNick Fitzgerald where 94968952abSNick Fitzgerald T: Inline, 95968952abSNick Fitzgerald { 96968952abSNick Fitzgerald fn inline( 973ecb338eSNick Fitzgerald &mut self, 98968952abSNick Fitzgerald caller: &ir::Function, 99968952abSNick Fitzgerald inst: ir::Inst, 100968952abSNick Fitzgerald opcode: ir::Opcode, 101968952abSNick Fitzgerald callee: ir::FuncRef, 102968952abSNick Fitzgerald args: &[ir::Value], 103968952abSNick Fitzgerald ) -> InlineCommand<'_> { 104968952abSNick Fitzgerald (*self).inline(caller, inst, opcode, callee, args) 105968952abSNick Fitzgerald } 106968952abSNick Fitzgerald } 107968952abSNick Fitzgerald 108968952abSNick Fitzgerald /// Walk the given function, invoke the `Inline` implementation for each call 109968952abSNick Fitzgerald /// instruction, and inline the callee when directed to do so. 110968952abSNick Fitzgerald /// 111968952abSNick Fitzgerald /// Returns whether any call was inlined. 1123ecb338eSNick Fitzgerald pub(crate) fn do_inlining( 1133ecb338eSNick Fitzgerald func: &mut ir::Function, 1143ecb338eSNick Fitzgerald mut inliner: impl Inline, 1153ecb338eSNick Fitzgerald ) -> CodegenResult<bool> { 1163ecb338eSNick Fitzgerald trace!("function {} before inlining: {}", func.name, func); 1173ecb338eSNick Fitzgerald 118968952abSNick Fitzgerald let mut inlined_any = false; 119968952abSNick Fitzgerald let mut allocs = InliningAllocs::default(); 120968952abSNick Fitzgerald 121968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 122dcedcbf5SNick Fitzgerald 'block_loop: while let Some(block) = cursor.next_block() { 123dcedcbf5SNick Fitzgerald // Always keep track of our previous cursor position. Assuming that the 124dcedcbf5SNick Fitzgerald // current position is a function call that we will inline, then the 125dcedcbf5SNick Fitzgerald // previous position is just before the inlined callee function. After 126dcedcbf5SNick Fitzgerald // inlining a call, the Cranelift user can decide whether to consider 127dcedcbf5SNick Fitzgerald // any function calls in the inlined callee for further inlining or 128dcedcbf5SNick Fitzgerald // not. When they do, then we back up to this previous cursor position 129dcedcbf5SNick Fitzgerald // so that our traversal will then continue over the inlined body. 130968952abSNick Fitzgerald let mut prev_pos; 131968952abSNick Fitzgerald 132968952abSNick Fitzgerald while let Some(inst) = { 133968952abSNick Fitzgerald prev_pos = cursor.position(); 134968952abSNick Fitzgerald cursor.next_inst() 135968952abSNick Fitzgerald } { 136dcedcbf5SNick Fitzgerald // Make sure that `block` is always `inst`'s block, even with all of 137dcedcbf5SNick Fitzgerald // our cursor-position-updating and block-splitting-during-inlining 138dcedcbf5SNick Fitzgerald // shenanigans below. 139dcedcbf5SNick Fitzgerald debug_assert_eq!(Some(block), cursor.func.layout.inst_block(inst)); 140dcedcbf5SNick Fitzgerald 141968952abSNick Fitzgerald match cursor.func.dfg.insts[inst] { 142*87ed3b60SChris Fallin ir::InstructionData::Call { func_ref, .. } 143*87ed3b60SChris Fallin if cursor.func.dfg.ext_funcs[func_ref].patchable => 144*87ed3b60SChris Fallin { 145*87ed3b60SChris Fallin // Can't inline patchable calls; they need to 146*87ed3b60SChris Fallin // remain patchable and inlining the whole body is 147*87ed3b60SChris Fallin // decidedly *not* patchable! 148*87ed3b60SChris Fallin } 149*87ed3b60SChris Fallin 150968952abSNick Fitzgerald ir::InstructionData::Call { 151968952abSNick Fitzgerald opcode: opcode @ ir::Opcode::Call | opcode @ ir::Opcode::ReturnCall, 152968952abSNick Fitzgerald args: _, 153968952abSNick Fitzgerald func_ref, 154968952abSNick Fitzgerald } => { 1554cbea5e8SNick Fitzgerald trace!( 1564cbea5e8SNick Fitzgerald "considering call site for inlining: {inst}: {}", 1574cbea5e8SNick Fitzgerald cursor.func.dfg.display_inst(inst), 1584cbea5e8SNick Fitzgerald ); 159dcedcbf5SNick Fitzgerald let args = cursor.func.dfg.inst_args(inst); 160968952abSNick Fitzgerald match inliner.inline(&cursor.func, inst, opcode, func_ref, args) { 1614cbea5e8SNick Fitzgerald InlineCommand::KeepCall => { 1624cbea5e8SNick Fitzgerald trace!(" --> keeping call"); 1634cbea5e8SNick Fitzgerald } 164dcedcbf5SNick Fitzgerald InlineCommand::Inline { 165dcedcbf5SNick Fitzgerald callee, 166dcedcbf5SNick Fitzgerald visit_callee, 167dcedcbf5SNick Fitzgerald } => { 168dcedcbf5SNick Fitzgerald let last_inlined_block = inline_one( 169968952abSNick Fitzgerald &mut allocs, 170968952abSNick Fitzgerald cursor.func, 171968952abSNick Fitzgerald func_ref, 172968952abSNick Fitzgerald block, 173968952abSNick Fitzgerald inst, 174968952abSNick Fitzgerald opcode, 175968952abSNick Fitzgerald &callee, 176968952abSNick Fitzgerald None, 177968952abSNick Fitzgerald ); 178968952abSNick Fitzgerald inlined_any = true; 179dcedcbf5SNick Fitzgerald if visit_callee { 180968952abSNick Fitzgerald cursor.set_position(prev_pos); 181dcedcbf5SNick Fitzgerald } else { 182dcedcbf5SNick Fitzgerald // Arrange it so that the `next_block()` loop 183dcedcbf5SNick Fitzgerald // will continue to the next block that is not 184dcedcbf5SNick Fitzgerald // associated with the just-inlined callee. 185dcedcbf5SNick Fitzgerald cursor.goto_bottom(last_inlined_block); 186dcedcbf5SNick Fitzgerald continue 'block_loop; 187dcedcbf5SNick Fitzgerald } 188968952abSNick Fitzgerald } 189968952abSNick Fitzgerald } 190968952abSNick Fitzgerald } 191968952abSNick Fitzgerald ir::InstructionData::TryCall { 192968952abSNick Fitzgerald opcode: opcode @ ir::Opcode::TryCall, 193968952abSNick Fitzgerald args: _, 194968952abSNick Fitzgerald func_ref, 195968952abSNick Fitzgerald exception, 196968952abSNick Fitzgerald } => { 1974cbea5e8SNick Fitzgerald trace!( 1984cbea5e8SNick Fitzgerald "considering call site for inlining: {inst}: {}", 1994cbea5e8SNick Fitzgerald cursor.func.dfg.display_inst(inst), 2004cbea5e8SNick Fitzgerald ); 201dcedcbf5SNick Fitzgerald let args = cursor.func.dfg.inst_args(inst); 202968952abSNick Fitzgerald match inliner.inline(&cursor.func, inst, opcode, func_ref, args) { 2034cbea5e8SNick Fitzgerald InlineCommand::KeepCall => { 2044cbea5e8SNick Fitzgerald trace!(" --> keeping call"); 2054cbea5e8SNick Fitzgerald } 206dcedcbf5SNick Fitzgerald InlineCommand::Inline { 207dcedcbf5SNick Fitzgerald callee, 208dcedcbf5SNick Fitzgerald visit_callee, 209dcedcbf5SNick Fitzgerald } => { 210dcedcbf5SNick Fitzgerald let last_inlined_block = inline_one( 211968952abSNick Fitzgerald &mut allocs, 212968952abSNick Fitzgerald cursor.func, 213968952abSNick Fitzgerald func_ref, 214968952abSNick Fitzgerald block, 215968952abSNick Fitzgerald inst, 216968952abSNick Fitzgerald opcode, 217968952abSNick Fitzgerald &callee, 218968952abSNick Fitzgerald Some(exception), 219968952abSNick Fitzgerald ); 220968952abSNick Fitzgerald inlined_any = true; 221dcedcbf5SNick Fitzgerald if visit_callee { 222968952abSNick Fitzgerald cursor.set_position(prev_pos); 223dcedcbf5SNick Fitzgerald } else { 224dcedcbf5SNick Fitzgerald // Arrange it so that the `next_block()` loop 225dcedcbf5SNick Fitzgerald // will continue to the next block that is not 226dcedcbf5SNick Fitzgerald // associated with the just-inlined callee. 227dcedcbf5SNick Fitzgerald cursor.goto_bottom(last_inlined_block); 228dcedcbf5SNick Fitzgerald continue 'block_loop; 229968952abSNick Fitzgerald } 230968952abSNick Fitzgerald } 231968952abSNick Fitzgerald } 232dcedcbf5SNick Fitzgerald } 233dcedcbf5SNick Fitzgerald ir::InstructionData::CallIndirect { .. } 234dcedcbf5SNick Fitzgerald | ir::InstructionData::TryCallIndirect { .. } => { 235dcedcbf5SNick Fitzgerald // Can't inline indirect calls; need to have some earlier 236dcedcbf5SNick Fitzgerald // pass rewrite them into direct calls first, when possible. 237dcedcbf5SNick Fitzgerald } 238dcedcbf5SNick Fitzgerald _ => { 239dcedcbf5SNick Fitzgerald debug_assert!( 240dcedcbf5SNick Fitzgerald !cursor.func.dfg.insts[inst].opcode().is_call(), 241dcedcbf5SNick Fitzgerald "should have matched all call instructions, but found: {inst}: {}", 242dcedcbf5SNick Fitzgerald cursor.func.dfg.display_inst(inst), 243dcedcbf5SNick Fitzgerald ); 244dcedcbf5SNick Fitzgerald } 245968952abSNick Fitzgerald } 246968952abSNick Fitzgerald } 247968952abSNick Fitzgerald } 248968952abSNick Fitzgerald 2493ecb338eSNick Fitzgerald if inlined_any { 2503ecb338eSNick Fitzgerald trace!("function {} after inlining: {}", func.name, func); 2513ecb338eSNick Fitzgerald } else { 2523ecb338eSNick Fitzgerald trace!("function {} did not have any callees inlined", func.name); 2533ecb338eSNick Fitzgerald } 2543ecb338eSNick Fitzgerald 255968952abSNick Fitzgerald Ok(inlined_any) 256968952abSNick Fitzgerald } 257968952abSNick Fitzgerald 258968952abSNick Fitzgerald #[derive(Default)] 259968952abSNick Fitzgerald struct InliningAllocs { 260968952abSNick Fitzgerald /// Map from callee value to inlined caller value. 261968952abSNick Fitzgerald values: SecondaryMap<ir::Value, PackedOption<ir::Value>>, 262968952abSNick Fitzgerald 263968952abSNick Fitzgerald /// Map from callee constant to inlined caller constant. 2644cbea5e8SNick Fitzgerald /// 2654cbea5e8SNick Fitzgerald /// Not in `EntityMap` because these are hash-consed inside the 2664cbea5e8SNick Fitzgerald /// `ir::Function`. 267968952abSNick Fitzgerald constants: SecondaryMap<ir::Constant, PackedOption<ir::Constant>>, 268968952abSNick Fitzgerald 2694cbea5e8SNick Fitzgerald /// Map from callee to inlined caller external name refs. 2704cbea5e8SNick Fitzgerald /// 2714cbea5e8SNick Fitzgerald /// Not in `EntityMap` because these are hash-consed inside the 2724cbea5e8SNick Fitzgerald /// `ir::Function`. 2734cbea5e8SNick Fitzgerald user_external_name_refs: 2744cbea5e8SNick Fitzgerald SecondaryMap<ir::UserExternalNameRef, PackedOption<ir::UserExternalNameRef>>, 2754cbea5e8SNick Fitzgerald 276968952abSNick Fitzgerald /// The set of _caller_ inlined call instructions that need exception table 277968952abSNick Fitzgerald /// fixups at the end of inlining. 278968952abSNick Fitzgerald /// 279968952abSNick Fitzgerald /// This includes all kinds of non-returning calls, not just the literal 280968952abSNick Fitzgerald /// `call` instruction: `call_indirect`, `try_call`, `try_call_indirect`, 281968952abSNick Fitzgerald /// etc... However, it does not include `return_call` and 282968952abSNick Fitzgerald /// `return_call_indirect` instructions because the caller cannot catch 283968952abSNick Fitzgerald /// exceptions that those calls throw because the caller is no longer on the 284968952abSNick Fitzgerald /// stack as soon as they are executed. 285968952abSNick Fitzgerald /// 286968952abSNick Fitzgerald /// Note: this is a simple `Vec`, and not an `EntitySet`, because it is very 287968952abSNick Fitzgerald /// sparse: most of the caller's instructions are not inlined call 288968952abSNick Fitzgerald /// instructions. Additionally, we require deterministic iteration order and 289968952abSNick Fitzgerald /// do not require set-membership testing, so a hash set is not a good 290968952abSNick Fitzgerald /// choice either. 291968952abSNick Fitzgerald calls_needing_exception_table_fixup: Vec<ir::Inst>, 292968952abSNick Fitzgerald } 293968952abSNick Fitzgerald 294968952abSNick Fitzgerald impl InliningAllocs { 295968952abSNick Fitzgerald fn reset(&mut self, callee: &ir::Function) { 296968952abSNick Fitzgerald let InliningAllocs { 297968952abSNick Fitzgerald values, 298968952abSNick Fitzgerald constants, 2994cbea5e8SNick Fitzgerald user_external_name_refs, 300968952abSNick Fitzgerald calls_needing_exception_table_fixup, 301968952abSNick Fitzgerald } = self; 302968952abSNick Fitzgerald 303968952abSNick Fitzgerald values.clear(); 304968952abSNick Fitzgerald values.resize(callee.dfg.len_values()); 305968952abSNick Fitzgerald 306968952abSNick Fitzgerald constants.clear(); 307968952abSNick Fitzgerald constants.resize(callee.dfg.constants.len()); 308968952abSNick Fitzgerald 3094cbea5e8SNick Fitzgerald user_external_name_refs.clear(); 3104cbea5e8SNick Fitzgerald user_external_name_refs.resize(callee.params.user_named_funcs().len()); 3114cbea5e8SNick Fitzgerald 312968952abSNick Fitzgerald // Note: We do not reserve capacity for 313968952abSNick Fitzgerald // `calls_needing_exception_table_fixup` because it is a sparse set and 314968952abSNick Fitzgerald // we don't know how large it needs to be ahead of time. 315968952abSNick Fitzgerald calls_needing_exception_table_fixup.clear(); 316968952abSNick Fitzgerald } 317968952abSNick Fitzgerald 318968952abSNick Fitzgerald fn set_inlined_value( 319968952abSNick Fitzgerald &mut self, 320968952abSNick Fitzgerald callee: &ir::Function, 321968952abSNick Fitzgerald callee_val: ir::Value, 322968952abSNick Fitzgerald inlined_val: ir::Value, 323968952abSNick Fitzgerald ) { 324968952abSNick Fitzgerald trace!(" --> callee {callee_val:?} = inlined {inlined_val:?}"); 325968952abSNick Fitzgerald debug_assert!(self.values[callee_val].is_none()); 326968952abSNick Fitzgerald let resolved_callee_val = callee.dfg.resolve_aliases(callee_val); 327968952abSNick Fitzgerald debug_assert!(self.values[resolved_callee_val].is_none()); 328968952abSNick Fitzgerald self.values[resolved_callee_val] = Some(inlined_val).into(); 329968952abSNick Fitzgerald } 330968952abSNick Fitzgerald 331968952abSNick Fitzgerald fn get_inlined_value(&self, callee: &ir::Function, callee_val: ir::Value) -> Option<ir::Value> { 332968952abSNick Fitzgerald let resolved_callee_val = callee.dfg.resolve_aliases(callee_val); 333968952abSNick Fitzgerald self.values[resolved_callee_val].expand() 334968952abSNick Fitzgerald } 335968952abSNick Fitzgerald } 336968952abSNick Fitzgerald 337968952abSNick Fitzgerald /// Inline one particular function call. 338dcedcbf5SNick Fitzgerald /// 339dcedcbf5SNick Fitzgerald /// Returns the last inlined block in the layout. 340968952abSNick Fitzgerald fn inline_one( 341968952abSNick Fitzgerald allocs: &mut InliningAllocs, 342968952abSNick Fitzgerald func: &mut ir::Function, 343968952abSNick Fitzgerald callee_func_ref: ir::FuncRef, 344968952abSNick Fitzgerald call_block: ir::Block, 345968952abSNick Fitzgerald call_inst: ir::Inst, 346968952abSNick Fitzgerald call_opcode: ir::Opcode, 347968952abSNick Fitzgerald callee: &ir::Function, 348968952abSNick Fitzgerald call_exception_table: Option<ir::ExceptionTable>, 349dcedcbf5SNick Fitzgerald ) -> ir::Block { 350968952abSNick Fitzgerald trace!( 351968952abSNick Fitzgerald "Inlining call {call_inst:?}: {}\n\ 352968952abSNick Fitzgerald with callee = {callee:?}", 353968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 354968952abSNick Fitzgerald ); 355968952abSNick Fitzgerald 356968952abSNick Fitzgerald // Type check callee signature. 357968952abSNick Fitzgerald let expected_callee_sig = func.dfg.ext_funcs[callee_func_ref].signature; 358968952abSNick Fitzgerald let expected_callee_sig = &func.dfg.signatures[expected_callee_sig]; 359968952abSNick Fitzgerald assert_eq!(expected_callee_sig, &callee.signature); 360968952abSNick Fitzgerald 361968952abSNick Fitzgerald allocs.reset(callee); 362968952abSNick Fitzgerald 363968952abSNick Fitzgerald // First, append various callee entity arenas to the end of the caller's 364968952abSNick Fitzgerald // entity arenas. 365968952abSNick Fitzgerald let entity_map = create_entities(allocs, func, callee); 366968952abSNick Fitzgerald 367968952abSNick Fitzgerald // Inlined prologue: split the call instruction's block at the point of the 368968952abSNick Fitzgerald // call and replace the call with a jump. 369968952abSNick Fitzgerald let return_block = split_off_return_block(func, call_inst, call_opcode, callee); 370968952abSNick Fitzgerald let call_stack_map = replace_call_with_jump(allocs, func, call_inst, callee, &entity_map); 371968952abSNick Fitzgerald 372968952abSNick Fitzgerald // Prepare for translating the actual instructions by inserting the inlined 373968952abSNick Fitzgerald // blocks into the caller's layout in the same order that they appear in the 374968952abSNick Fitzgerald // callee. 375e767c56bSNick Fitzgerald let mut last_inlined_block = inline_block_layout(func, call_block, callee, &entity_map); 376968952abSNick Fitzgerald 377a3d6e407SChris Fallin // Get a copy of debug tags on the call instruction; these are 378a3d6e407SChris Fallin // prepended to debug tags on inlined instructions. Remove them 379a3d6e407SChris Fallin // from the call itself as it will be rewritten to a jump (which 380a3d6e407SChris Fallin // cannot have tags). 381a3d6e407SChris Fallin let call_debug_tags = func.debug_tags.get(call_inst).to_vec(); 382a3d6e407SChris Fallin func.debug_tags.set(call_inst, []); 383a3d6e407SChris Fallin 384968952abSNick Fitzgerald // Translate each instruction from the callee into the caller, 385968952abSNick Fitzgerald // appending them to their associated block in the caller. 386968952abSNick Fitzgerald // 387968952abSNick Fitzgerald // Note that we iterate over the callee with a pre-order traversal so that 388968952abSNick Fitzgerald // we see value defs before uses. 389968952abSNick Fitzgerald for callee_block in Dfs::new().pre_order_iter(callee) { 390968952abSNick Fitzgerald let inlined_block = entity_map.inlined_block(callee_block); 391968952abSNick Fitzgerald trace!( 392968952abSNick Fitzgerald "Processing instructions in callee block {callee_block:?} (inlined block {inlined_block:?}" 393968952abSNick Fitzgerald ); 394968952abSNick Fitzgerald 395968952abSNick Fitzgerald let mut next_callee_inst = callee.layout.first_inst(callee_block); 396968952abSNick Fitzgerald while let Some(callee_inst) = next_callee_inst { 397968952abSNick Fitzgerald trace!( 398968952abSNick Fitzgerald "Processing callee instruction {callee_inst:?}: {}", 399968952abSNick Fitzgerald callee.dfg.display_inst(callee_inst) 400968952abSNick Fitzgerald ); 401968952abSNick Fitzgerald 402968952abSNick Fitzgerald assert_ne!( 403968952abSNick Fitzgerald callee.dfg.insts[callee_inst].opcode(), 404968952abSNick Fitzgerald ir::Opcode::GlobalValue, 405968952abSNick Fitzgerald "callee must already be legalized, we shouldn't see any `global_value` \ 406968952abSNick Fitzgerald instructions when inlining; found {callee_inst:?}: {}", 407968952abSNick Fitzgerald callee.dfg.display_inst(callee_inst) 408968952abSNick Fitzgerald ); 409968952abSNick Fitzgerald 410968952abSNick Fitzgerald // Remap the callee instruction's entities and insert it into the 411968952abSNick Fitzgerald // caller's DFG. 412968952abSNick Fitzgerald let inlined_inst_data = callee.dfg.insts[callee_inst].map(InliningInstRemapper { 413968952abSNick Fitzgerald allocs: &allocs, 414968952abSNick Fitzgerald func, 415968952abSNick Fitzgerald callee, 416968952abSNick Fitzgerald entity_map: &entity_map, 417968952abSNick Fitzgerald }); 418968952abSNick Fitzgerald let inlined_inst = func.dfg.make_inst(inlined_inst_data); 419968952abSNick Fitzgerald func.layout.append_inst(inlined_inst, inlined_block); 420968952abSNick Fitzgerald 421a3d6e407SChris Fallin // Copy over debug tags, translating referenced entities 422a3d6e407SChris Fallin // as appropriate. 423a3d6e407SChris Fallin let debug_tags = callee.debug_tags.get(callee_inst); 424a3d6e407SChris Fallin // If there are tags on the inlined instruction, we always 425a3d6e407SChris Fallin // add tags, and we prepend any tags from the call 426a3d6e407SChris Fallin // instruction; but we don't add tags if only the callsite 427a3d6e407SChris Fallin // had them (this would otherwise mean that every single 428a3d6e407SChris Fallin // instruction in an inlined function body would get 429a3d6e407SChris Fallin // tags). 430a3d6e407SChris Fallin if !debug_tags.is_empty() { 431a3d6e407SChris Fallin let tags = call_debug_tags 432a3d6e407SChris Fallin .iter() 433a3d6e407SChris Fallin .cloned() 434a3d6e407SChris Fallin .chain(debug_tags.iter().map(|tag| match *tag { 435a3d6e407SChris Fallin DebugTag::User(value) => DebugTag::User(value), 436a3d6e407SChris Fallin DebugTag::StackSlot(slot) => { 437a3d6e407SChris Fallin DebugTag::StackSlot(entity_map.inlined_stack_slot(slot)) 438a3d6e407SChris Fallin } 439a3d6e407SChris Fallin })) 440a3d6e407SChris Fallin .collect::<SmallVec<[_; 4]>>(); 441a3d6e407SChris Fallin func.debug_tags.set(inlined_inst, tags); 442a3d6e407SChris Fallin } 443a3d6e407SChris Fallin 444968952abSNick Fitzgerald let opcode = callee.dfg.insts[callee_inst].opcode(); 445968952abSNick Fitzgerald if opcode.is_return() { 446968952abSNick Fitzgerald // Instructions that return do not define any values, so we 447968952abSNick Fitzgerald // don't need to worry about that, but we do need to fix them up 448968952abSNick Fitzgerald // so that they return by jumping to our control-flow join 449968952abSNick Fitzgerald // block, rather than returning from the caller. 450968952abSNick Fitzgerald if let Some(return_block) = return_block { 451968952abSNick Fitzgerald fixup_inst_that_returns( 452968952abSNick Fitzgerald allocs, 453968952abSNick Fitzgerald func, 454968952abSNick Fitzgerald callee, 455968952abSNick Fitzgerald &entity_map, 456968952abSNick Fitzgerald call_opcode, 457968952abSNick Fitzgerald inlined_inst, 458968952abSNick Fitzgerald callee_inst, 459968952abSNick Fitzgerald return_block, 460968952abSNick Fitzgerald call_stack_map.as_ref().map(|es| &**es), 461968952abSNick Fitzgerald ); 462968952abSNick Fitzgerald } else { 463968952abSNick Fitzgerald // If we are inlining a callee that was invoked via 464968952abSNick Fitzgerald // `return_call`, we leave inlined return instructions 465968952abSNick Fitzgerald // as-is: there is no logical caller frame on the stack to 466968952abSNick Fitzgerald // continue to. 467968952abSNick Fitzgerald debug_assert_eq!(call_opcode, ir::Opcode::ReturnCall); 468968952abSNick Fitzgerald } 469968952abSNick Fitzgerald } else { 470968952abSNick Fitzgerald // Make the instruction's result values. 471968952abSNick Fitzgerald let ctrl_typevar = callee.dfg.ctrl_typevar(callee_inst); 472968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ctrl_typevar); 473968952abSNick Fitzgerald 474968952abSNick Fitzgerald // Update the value map for this instruction's defs. 475968952abSNick Fitzgerald let callee_results = callee.dfg.inst_results(callee_inst); 476968952abSNick Fitzgerald let inlined_results = func.dfg.inst_results(inlined_inst); 477968952abSNick Fitzgerald debug_assert_eq!(callee_results.len(), inlined_results.len()); 478968952abSNick Fitzgerald for (callee_val, inlined_val) in callee_results.iter().zip(inlined_results) { 479968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_val, *inlined_val); 480968952abSNick Fitzgerald } 481968952abSNick Fitzgerald 482968952abSNick Fitzgerald if opcode.is_call() { 483968952abSNick Fitzgerald append_stack_map_entries( 484968952abSNick Fitzgerald func, 485968952abSNick Fitzgerald callee, 486968952abSNick Fitzgerald &entity_map, 487968952abSNick Fitzgerald call_stack_map.as_deref(), 488968952abSNick Fitzgerald inlined_inst, 489968952abSNick Fitzgerald callee_inst, 490968952abSNick Fitzgerald ); 491968952abSNick Fitzgerald 492968952abSNick Fitzgerald // When we are inlining a `try_call` call site, we need to merge 493968952abSNick Fitzgerald // the call site's exception table into the inlined calls' 494968952abSNick Fitzgerald // exception tables. This can involve rewriting regular `call`s 495968952abSNick Fitzgerald // into `try_call`s, which requires mutating the CFG because 496968952abSNick Fitzgerald // `try_call` is a block terminator. However, we can't mutate 497968952abSNick Fitzgerald // the CFG in the middle of this traversal because we rely on 498968952abSNick Fitzgerald // the existence of a one-to-one mapping between the callee 499968952abSNick Fitzgerald // layout and the inlined layout. Instead, we record the set of 500968952abSNick Fitzgerald // inlined call instructions that will need fixing up, and 501968952abSNick Fitzgerald // perform that possibly-CFG-mutating exception table merging in 502968952abSNick Fitzgerald // a follow up pass, when we no longer rely on that one-to-one 503968952abSNick Fitzgerald // layout mapping. 504968952abSNick Fitzgerald debug_assert_eq!( 505968952abSNick Fitzgerald call_opcode == ir::Opcode::TryCall, 506968952abSNick Fitzgerald call_exception_table.is_some() 507968952abSNick Fitzgerald ); 508*87ed3b60SChris Fallin if call_opcode == ir::Opcode::TryCall { 509968952abSNick Fitzgerald allocs 510968952abSNick Fitzgerald .calls_needing_exception_table_fixup 511968952abSNick Fitzgerald .push(inlined_inst); 512968952abSNick Fitzgerald } 513968952abSNick Fitzgerald } 514968952abSNick Fitzgerald } 515968952abSNick Fitzgerald 516968952abSNick Fitzgerald trace!( 517968952abSNick Fitzgerald " --> inserted inlined instruction {inlined_inst:?}: {}", 518968952abSNick Fitzgerald func.dfg.display_inst(inlined_inst) 519968952abSNick Fitzgerald ); 520968952abSNick Fitzgerald 521968952abSNick Fitzgerald next_callee_inst = callee.layout.next_inst(callee_inst); 522968952abSNick Fitzgerald } 523968952abSNick Fitzgerald } 524968952abSNick Fitzgerald 525e3a607eaSNick Fitzgerald // We copied *all* callee blocks into the caller's layout, but only copied 526e3a607eaSNick Fitzgerald // the callee instructions in *reachable* callee blocks into the caller's 527e3a607eaSNick Fitzgerald // associated blocks. Therefore, any *unreachable* blocks are empty in the 528e3a607eaSNick Fitzgerald // caller, which is invalid CLIF because all blocks must end in a 529e3a607eaSNick Fitzgerald // terminator, so do a quick pass over the inlined blocks and remove any 530e3a607eaSNick Fitzgerald // empty blocks from the caller's layout. 531e3a607eaSNick Fitzgerald for block in entity_map.iter_inlined_blocks(func) { 532cfc05638SNick Fitzgerald if func.layout.is_block_inserted(block) && func.layout.first_inst(block).is_none() { 533e767c56bSNick Fitzgerald log::trace!("removing unreachable inlined block from layout: {block}"); 534e767c56bSNick Fitzgerald 535e767c56bSNick Fitzgerald // If the block being removed is our last-inlined block, then back 536e767c56bSNick Fitzgerald // it up to the previous block in the layout, which will be the new 537e767c56bSNick Fitzgerald // last-inlined block after this one's removal. 538e767c56bSNick Fitzgerald if block == last_inlined_block { 539e767c56bSNick Fitzgerald last_inlined_block = func.layout.prev_block(last_inlined_block).expect( 540e767c56bSNick Fitzgerald "there will always at least be the block that contained the call we are \ 541e767c56bSNick Fitzgerald inlining", 542e767c56bSNick Fitzgerald ); 543e767c56bSNick Fitzgerald } 544e767c56bSNick Fitzgerald 545e3a607eaSNick Fitzgerald func.layout.remove_block(block); 546e3a607eaSNick Fitzgerald } 547e3a607eaSNick Fitzgerald } 548e3a607eaSNick Fitzgerald 549968952abSNick Fitzgerald // Final step: fixup the exception tables of any inlined calls when we are 550968952abSNick Fitzgerald // inlining a `try_call` site. 551968952abSNick Fitzgerald // 552968952abSNick Fitzgerald // Subtly, this requires rewriting non-catching `call[_indirect]` 553968952abSNick Fitzgerald // instructions into `try_call[_indirect]` instructions so that exceptions 554968952abSNick Fitzgerald // that unwound through the original callee frame and were caught by the 555968952abSNick Fitzgerald // caller's `try_call` do not unwind past this inlined frame. And turning a 556968952abSNick Fitzgerald // `call` into a `try_call` mutates the CFG, breaking our one-to-one mapping 557968952abSNick Fitzgerald // between callee blocks and inlined blocks, so we delay these fixups to 558968952abSNick Fitzgerald // this final step, when we no longer rely on that mapping. 559968952abSNick Fitzgerald debug_assert!( 560968952abSNick Fitzgerald allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some() 561968952abSNick Fitzgerald ); 562968952abSNick Fitzgerald debug_assert_eq!( 563968952abSNick Fitzgerald call_opcode == ir::Opcode::TryCall, 564968952abSNick Fitzgerald call_exception_table.is_some() 565968952abSNick Fitzgerald ); 566968952abSNick Fitzgerald if let Some(call_exception_table) = call_exception_table { 567968952abSNick Fitzgerald fixup_inlined_call_exception_tables(allocs, func, call_exception_table); 568968952abSNick Fitzgerald } 569dcedcbf5SNick Fitzgerald 570e767c56bSNick Fitzgerald debug_assert!( 571e767c56bSNick Fitzgerald func.layout.is_block_inserted(last_inlined_block), 572e767c56bSNick Fitzgerald "last_inlined_block={last_inlined_block} should be inserted in the layout" 573e767c56bSNick Fitzgerald ); 574dcedcbf5SNick Fitzgerald last_inlined_block 575968952abSNick Fitzgerald } 576968952abSNick Fitzgerald 577968952abSNick Fitzgerald /// Append stack map entries from the caller and callee to the given inlined 578968952abSNick Fitzgerald /// instruction. 579968952abSNick Fitzgerald fn append_stack_map_entries( 580968952abSNick Fitzgerald func: &mut ir::Function, 581968952abSNick Fitzgerald callee: &ir::Function, 582968952abSNick Fitzgerald entity_map: &EntityMap, 583968952abSNick Fitzgerald call_stack_map: Option<&[ir::UserStackMapEntry]>, 584968952abSNick Fitzgerald inlined_inst: ir::Inst, 585968952abSNick Fitzgerald callee_inst: ir::Inst, 586968952abSNick Fitzgerald ) { 587968952abSNick Fitzgerald // Add the caller's stack map to this call. These entries 588968952abSNick Fitzgerald // already refer to caller entities and do not need further 589968952abSNick Fitzgerald // translation. 590968952abSNick Fitzgerald func.dfg.append_user_stack_map_entries( 591968952abSNick Fitzgerald inlined_inst, 592968952abSNick Fitzgerald call_stack_map 593968952abSNick Fitzgerald .iter() 594968952abSNick Fitzgerald .flat_map(|entries| entries.iter().cloned()), 595968952abSNick Fitzgerald ); 596968952abSNick Fitzgerald 597968952abSNick Fitzgerald // Append the callee's stack map to this call. These entries 598968952abSNick Fitzgerald // refer to callee entities and therefore do require 599968952abSNick Fitzgerald // translation into the caller's index space. 600968952abSNick Fitzgerald func.dfg.append_user_stack_map_entries( 601968952abSNick Fitzgerald inlined_inst, 602968952abSNick Fitzgerald callee 603968952abSNick Fitzgerald .dfg 604968952abSNick Fitzgerald .user_stack_map_entries(callee_inst) 605968952abSNick Fitzgerald .iter() 606968952abSNick Fitzgerald .flat_map(|entries| entries.iter()) 607968952abSNick Fitzgerald .map(|entry| ir::UserStackMapEntry { 608968952abSNick Fitzgerald ty: entry.ty, 609968952abSNick Fitzgerald slot: entity_map.inlined_stack_slot(entry.slot), 610968952abSNick Fitzgerald offset: entry.offset, 611968952abSNick Fitzgerald }), 612968952abSNick Fitzgerald ); 613968952abSNick Fitzgerald } 614968952abSNick Fitzgerald 615968952abSNick Fitzgerald /// Create or update the exception tables for any inlined call instructions: 616968952abSNick Fitzgerald /// when inlining at a `try_call` site, we must forward our exceptional edges 617968952abSNick Fitzgerald /// into each inlined call instruction. 618968952abSNick Fitzgerald fn fixup_inlined_call_exception_tables( 619968952abSNick Fitzgerald allocs: &mut InliningAllocs, 620968952abSNick Fitzgerald func: &mut ir::Function, 621968952abSNick Fitzgerald call_exception_table: ir::ExceptionTable, 622968952abSNick Fitzgerald ) { 623968952abSNick Fitzgerald // Split a block at a `call[_indirect]` instruction, detach the 624968952abSNick Fitzgerald // instruction's results, and alias them to the new block's parameters. 625968952abSNick Fitzgerald let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block { 626968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inst].opcode().is_call()); 627968952abSNick Fitzgerald debug_assert!(!func.dfg.insts[inst].opcode().is_terminator()); 628968952abSNick Fitzgerald 629968952abSNick Fitzgerald // Split the block. 630968952abSNick Fitzgerald let next_inst = func 631968952abSNick Fitzgerald .layout 632968952abSNick Fitzgerald .next_inst(inst) 633968952abSNick Fitzgerald .expect("inst is not a terminator, should have a successor"); 634968952abSNick Fitzgerald let new_block = func.dfg.blocks.add(); 635968952abSNick Fitzgerald func.layout.split_block(new_block, next_inst); 636968952abSNick Fitzgerald 637968952abSNick Fitzgerald // `try_call[_indirect]` instructions do not define values themselves; 638968952abSNick Fitzgerald // the normal-return block has parameters for the results. So remove 639968952abSNick Fitzgerald // this instruction's results, create an associated block parameter for 640968952abSNick Fitzgerald // each of them, and alias them to the new block parameter. 641968952abSNick Fitzgerald let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied()); 642968952abSNick Fitzgerald func.dfg.detach_inst_results(inst); 643968952abSNick Fitzgerald for old_result in old_results { 644968952abSNick Fitzgerald let ty = func.dfg.value_type(old_result); 645968952abSNick Fitzgerald let new_block_param = func.dfg.append_block_param(new_block, ty); 646968952abSNick Fitzgerald func.dfg.change_to_alias(old_result, new_block_param); 647968952abSNick Fitzgerald } 648968952abSNick Fitzgerald 649968952abSNick Fitzgerald new_block 650968952abSNick Fitzgerald }; 651968952abSNick Fitzgerald 652968952abSNick Fitzgerald // Clone the caller's exception table, updating it for use in the current 653968952abSNick Fitzgerald // `call[_indirect]` instruction as it becomes a `try_call[_indirect]`. 654968952abSNick Fitzgerald let clone_exception_table_for_this_call = |func: &mut ir::Function, 655968952abSNick Fitzgerald signature: ir::SigRef, 656968952abSNick Fitzgerald new_block: ir::Block| 657968952abSNick Fitzgerald -> ir::ExceptionTable { 658968952abSNick Fitzgerald let mut exception = func.stencil.dfg.exception_tables[call_exception_table] 659968952abSNick Fitzgerald .deep_clone(&mut func.stencil.dfg.value_lists); 660968952abSNick Fitzgerald 661968952abSNick Fitzgerald *exception.signature_mut() = signature; 662968952abSNick Fitzgerald 663968952abSNick Fitzgerald let returns_len = func.dfg.signatures[signature].returns.len(); 664968952abSNick Fitzgerald let returns_len = u32::try_from(returns_len).unwrap(); 665968952abSNick Fitzgerald 666968952abSNick Fitzgerald *exception.normal_return_mut() = ir::BlockCall::new( 667968952abSNick Fitzgerald new_block, 668968952abSNick Fitzgerald (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)), 669968952abSNick Fitzgerald &mut func.dfg.value_lists, 670968952abSNick Fitzgerald ); 671968952abSNick Fitzgerald 672968952abSNick Fitzgerald func.dfg.exception_tables.push(exception) 673968952abSNick Fitzgerald }; 674968952abSNick Fitzgerald 675968952abSNick Fitzgerald for inst in allocs.calls_needing_exception_table_fixup.drain(..) { 676968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inst].opcode().is_call()); 677968952abSNick Fitzgerald debug_assert!(!func.dfg.insts[inst].opcode().is_return()); 678968952abSNick Fitzgerald match func.dfg.insts[inst] { 679968952abSNick Fitzgerald // current_block: 680968952abSNick Fitzgerald // preds... 681968952abSNick Fitzgerald // rets... = call f(args...) 682968952abSNick Fitzgerald // succs... 683968952abSNick Fitzgerald // 684968952abSNick Fitzgerald // becomes 685968952abSNick Fitzgerald // 686968952abSNick Fitzgerald // current_block: 687968952abSNick Fitzgerald // preds... 688968952abSNick Fitzgerald // try_call f(args...), new_block(rets...), [call_exception_table...] 689968952abSNick Fitzgerald // new_block(rets...): 690968952abSNick Fitzgerald // succs... 691968952abSNick Fitzgerald ir::InstructionData::Call { 692968952abSNick Fitzgerald opcode: ir::Opcode::Call, 693968952abSNick Fitzgerald args, 694968952abSNick Fitzgerald func_ref, 695968952abSNick Fitzgerald } => { 696968952abSNick Fitzgerald let new_block = split_block_for_new_try_call(func, inst); 697968952abSNick Fitzgerald let signature = func.dfg.ext_funcs[func_ref].signature; 698968952abSNick Fitzgerald let exception = clone_exception_table_for_this_call(func, signature, new_block); 699968952abSNick Fitzgerald func.dfg.insts[inst] = ir::InstructionData::TryCall { 700968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 701968952abSNick Fitzgerald args, 702968952abSNick Fitzgerald func_ref, 703968952abSNick Fitzgerald exception, 704968952abSNick Fitzgerald }; 705968952abSNick Fitzgerald } 706968952abSNick Fitzgerald 707968952abSNick Fitzgerald // current_block: 708968952abSNick Fitzgerald // preds... 709968952abSNick Fitzgerald // rets... = call_indirect sig, val(args...) 710968952abSNick Fitzgerald // succs... 711968952abSNick Fitzgerald // 712968952abSNick Fitzgerald // becomes 713968952abSNick Fitzgerald // 714968952abSNick Fitzgerald // current_block: 715968952abSNick Fitzgerald // preds... 716968952abSNick Fitzgerald // try_call_indirect sig, val(args...), new_block(rets...), [call_exception_table...] 717968952abSNick Fitzgerald // new_block(rets...): 718968952abSNick Fitzgerald // succs... 719968952abSNick Fitzgerald ir::InstructionData::CallIndirect { 720968952abSNick Fitzgerald opcode: ir::Opcode::CallIndirect, 721968952abSNick Fitzgerald args, 722968952abSNick Fitzgerald sig_ref, 723968952abSNick Fitzgerald } => { 724968952abSNick Fitzgerald let new_block = split_block_for_new_try_call(func, inst); 725968952abSNick Fitzgerald let exception = clone_exception_table_for_this_call(func, sig_ref, new_block); 726968952abSNick Fitzgerald func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect { 727968952abSNick Fitzgerald opcode: ir::Opcode::TryCallIndirect, 728968952abSNick Fitzgerald args, 729968952abSNick Fitzgerald exception, 730968952abSNick Fitzgerald }; 731968952abSNick Fitzgerald } 732968952abSNick Fitzgerald 733968952abSNick Fitzgerald // For `try_call[_indirect]` instructions, we just need to merge the 734968952abSNick Fitzgerald // exception tables. 735968952abSNick Fitzgerald ir::InstructionData::TryCall { 736968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 737968952abSNick Fitzgerald exception, 738968952abSNick Fitzgerald .. 739968952abSNick Fitzgerald } 740968952abSNick Fitzgerald | ir::InstructionData::TryCallIndirect { 741968952abSNick Fitzgerald opcode: ir::Opcode::TryCallIndirect, 742968952abSNick Fitzgerald exception, 743968952abSNick Fitzgerald .. 744968952abSNick Fitzgerald } => { 7454590076fSChris Fallin // Construct a new exception table that consists of 7464590076fSChris Fallin // the inlined instruction's exception table match 7474590076fSChris Fallin // sequence, with the inlining site's exception table 7484590076fSChris Fallin // appended. This will ensure that the first-match 7494590076fSChris Fallin // semantics emulates the original behavior of 7504590076fSChris Fallin // matching in the inner frame first. 7514590076fSChris Fallin let sig = func.dfg.exception_tables[exception].signature(); 7524590076fSChris Fallin let normal_return = *func.dfg.exception_tables[exception].normal_return(); 7534590076fSChris Fallin let exception_data = ExceptionTableData::new( 7544590076fSChris Fallin sig, 7554590076fSChris Fallin normal_return, 756968952abSNick Fitzgerald func.dfg.exception_tables[exception] 7574590076fSChris Fallin .items() 7584590076fSChris Fallin .chain(func.dfg.exception_tables[call_exception_table].items()), 7594590076fSChris Fallin ) 7604590076fSChris Fallin .deep_clone(&mut func.dfg.value_lists); 761968952abSNick Fitzgerald 7624590076fSChris Fallin func.dfg.exception_tables[exception] = exception_data; 763968952abSNick Fitzgerald } 764968952abSNick Fitzgerald 765968952abSNick Fitzgerald otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"), 766968952abSNick Fitzgerald } 767968952abSNick Fitzgerald } 768968952abSNick Fitzgerald } 769968952abSNick Fitzgerald 770968952abSNick Fitzgerald /// After having created an inlined version of a callee instruction that returns 771968952abSNick Fitzgerald /// in the caller, we need to fix it up so that it doesn't actually return 772968952abSNick Fitzgerald /// (since we are already in the caller's frame) and instead just jumps to the 773968952abSNick Fitzgerald /// control-flow join point. 774968952abSNick Fitzgerald fn fixup_inst_that_returns( 775968952abSNick Fitzgerald allocs: &mut InliningAllocs, 776968952abSNick Fitzgerald func: &mut ir::Function, 777968952abSNick Fitzgerald callee: &ir::Function, 778968952abSNick Fitzgerald entity_map: &EntityMap, 779968952abSNick Fitzgerald call_opcode: ir::Opcode, 780968952abSNick Fitzgerald inlined_inst: ir::Inst, 781968952abSNick Fitzgerald callee_inst: ir::Inst, 782968952abSNick Fitzgerald return_block: ir::Block, 783968952abSNick Fitzgerald call_stack_map: Option<&[ir::UserStackMapEntry]>, 784968952abSNick Fitzgerald ) { 785968952abSNick Fitzgerald debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return()); 786968952abSNick Fitzgerald match func.dfg.insts[inlined_inst] { 787968952abSNick Fitzgerald // return rets... 788968952abSNick Fitzgerald // 789968952abSNick Fitzgerald // becomes 790968952abSNick Fitzgerald // 791968952abSNick Fitzgerald // jump return_block(rets...) 792968952abSNick Fitzgerald ir::InstructionData::MultiAry { 793968952abSNick Fitzgerald opcode: ir::Opcode::Return, 794968952abSNick Fitzgerald args, 795968952abSNick Fitzgerald } => { 796968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 797968952abSNick Fitzgerald args.as_slice(&func.dfg.value_lists) 798968952abSNick Fitzgerald .iter() 799968952abSNick Fitzgerald .copied() 800968952abSNick Fitzgerald .map(|v| v.into()), 801968952abSNick Fitzgerald ); 802968952abSNick Fitzgerald func.dfg.replace(inlined_inst).jump(return_block, &rets); 803968952abSNick Fitzgerald } 804968952abSNick Fitzgerald 805968952abSNick Fitzgerald // return_call f(args...) 806968952abSNick Fitzgerald // 807968952abSNick Fitzgerald // becomes 808968952abSNick Fitzgerald // 809968952abSNick Fitzgerald // rets... = call f(args...) 810968952abSNick Fitzgerald // jump return_block(rets...) 811968952abSNick Fitzgerald ir::InstructionData::Call { 812968952abSNick Fitzgerald opcode: ir::Opcode::ReturnCall, 813968952abSNick Fitzgerald args, 814968952abSNick Fitzgerald func_ref, 815968952abSNick Fitzgerald } => { 816968952abSNick Fitzgerald func.dfg.insts[inlined_inst] = ir::InstructionData::Call { 817968952abSNick Fitzgerald opcode: ir::Opcode::Call, 818968952abSNick Fitzgerald args, 819968952abSNick Fitzgerald func_ref, 820968952abSNick Fitzgerald }; 821968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ir::types::INVALID); 822968952abSNick Fitzgerald 823968952abSNick Fitzgerald append_stack_map_entries( 824968952abSNick Fitzgerald func, 825968952abSNick Fitzgerald callee, 826968952abSNick Fitzgerald &entity_map, 827968952abSNick Fitzgerald call_stack_map, 828968952abSNick Fitzgerald inlined_inst, 829968952abSNick Fitzgerald callee_inst, 830968952abSNick Fitzgerald ); 831968952abSNick Fitzgerald 832968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 833968952abSNick Fitzgerald func.dfg 834968952abSNick Fitzgerald .inst_results(inlined_inst) 835968952abSNick Fitzgerald .iter() 836968952abSNick Fitzgerald .copied() 837968952abSNick Fitzgerald .map(|v| v.into()), 838968952abSNick Fitzgerald ); 839968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 840968952abSNick Fitzgerald cursor.goto_after_inst(inlined_inst); 841968952abSNick Fitzgerald cursor.ins().jump(return_block, &rets); 842968952abSNick Fitzgerald 843968952abSNick Fitzgerald if call_opcode == ir::Opcode::TryCall { 844968952abSNick Fitzgerald allocs 845968952abSNick Fitzgerald .calls_needing_exception_table_fixup 846968952abSNick Fitzgerald .push(inlined_inst); 847968952abSNick Fitzgerald } 848968952abSNick Fitzgerald } 849968952abSNick Fitzgerald 850968952abSNick Fitzgerald // return_call_indirect val(args...) 851968952abSNick Fitzgerald // 852968952abSNick Fitzgerald // becomes 853968952abSNick Fitzgerald // 854968952abSNick Fitzgerald // rets... = call_indirect val(args...) 855968952abSNick Fitzgerald // jump return_block(rets...) 856968952abSNick Fitzgerald ir::InstructionData::CallIndirect { 857968952abSNick Fitzgerald opcode: ir::Opcode::ReturnCallIndirect, 858968952abSNick Fitzgerald args, 859968952abSNick Fitzgerald sig_ref, 860968952abSNick Fitzgerald } => { 861968952abSNick Fitzgerald func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect { 862968952abSNick Fitzgerald opcode: ir::Opcode::CallIndirect, 863968952abSNick Fitzgerald args, 864968952abSNick Fitzgerald sig_ref, 865968952abSNick Fitzgerald }; 866968952abSNick Fitzgerald func.dfg.make_inst_results(inlined_inst, ir::types::INVALID); 867968952abSNick Fitzgerald 868968952abSNick Fitzgerald append_stack_map_entries( 869968952abSNick Fitzgerald func, 870968952abSNick Fitzgerald callee, 871968952abSNick Fitzgerald &entity_map, 872968952abSNick Fitzgerald call_stack_map, 873968952abSNick Fitzgerald inlined_inst, 874968952abSNick Fitzgerald callee_inst, 875968952abSNick Fitzgerald ); 876968952abSNick Fitzgerald 877968952abSNick Fitzgerald let rets = SmallBlockArgVec::from_iter( 878968952abSNick Fitzgerald func.dfg 879968952abSNick Fitzgerald .inst_results(inlined_inst) 880968952abSNick Fitzgerald .iter() 881968952abSNick Fitzgerald .copied() 882968952abSNick Fitzgerald .map(|v| v.into()), 883968952abSNick Fitzgerald ); 884968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 885968952abSNick Fitzgerald cursor.goto_after_inst(inlined_inst); 886968952abSNick Fitzgerald cursor.ins().jump(return_block, &rets); 887968952abSNick Fitzgerald 888968952abSNick Fitzgerald if call_opcode == ir::Opcode::TryCall { 889968952abSNick Fitzgerald allocs 890968952abSNick Fitzgerald .calls_needing_exception_table_fixup 891968952abSNick Fitzgerald .push(inlined_inst); 892968952abSNick Fitzgerald } 893968952abSNick Fitzgerald } 894968952abSNick Fitzgerald 895968952abSNick Fitzgerald inst_data => unreachable!( 896968952abSNick Fitzgerald "should have handled all `is_return() == true` instructions above; \ 897968952abSNick Fitzgerald got {inst_data:?}" 898968952abSNick Fitzgerald ), 899968952abSNick Fitzgerald } 900968952abSNick Fitzgerald } 901968952abSNick Fitzgerald 902968952abSNick Fitzgerald /// An `InstructionMapper` implementation that remaps a callee instruction's 903968952abSNick Fitzgerald /// entity references to their new indices in the caller function. 904968952abSNick Fitzgerald struct InliningInstRemapper<'a> { 905968952abSNick Fitzgerald allocs: &'a InliningAllocs, 906968952abSNick Fitzgerald func: &'a mut ir::Function, 907968952abSNick Fitzgerald callee: &'a ir::Function, 908968952abSNick Fitzgerald entity_map: &'a EntityMap, 909968952abSNick Fitzgerald } 910968952abSNick Fitzgerald 911968952abSNick Fitzgerald impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> { 912968952abSNick Fitzgerald fn map_value(&mut self, value: ir::Value) -> ir::Value { 913968952abSNick Fitzgerald self.allocs.get_inlined_value(self.callee, value).expect( 914968952abSNick Fitzgerald "defs come before uses; we should have already inlined all values \ 915968952abSNick Fitzgerald used by an instruction", 916968952abSNick Fitzgerald ) 917968952abSNick Fitzgerald } 918968952abSNick Fitzgerald 919968952abSNick Fitzgerald fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList { 920968952abSNick Fitzgerald let mut inlined_list = ir::ValueList::new(); 921968952abSNick Fitzgerald for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) { 922968952abSNick Fitzgerald let inlined_val = self.map_value(*callee_val); 923968952abSNick Fitzgerald inlined_list.push(inlined_val, &mut self.func.dfg.value_lists); 924968952abSNick Fitzgerald } 925968952abSNick Fitzgerald inlined_list 926968952abSNick Fitzgerald } 927968952abSNick Fitzgerald 928968952abSNick Fitzgerald fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue { 929968952abSNick Fitzgerald self.entity_map.inlined_global_value(global_value) 930968952abSNick Fitzgerald } 931968952abSNick Fitzgerald 932968952abSNick Fitzgerald fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable { 933968952abSNick Fitzgerald let inlined_default = 934968952abSNick Fitzgerald self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block()); 935968952abSNick Fitzgerald let inlined_table = self.callee.dfg.jump_tables[jump_table] 936968952abSNick Fitzgerald .as_slice() 937968952abSNick Fitzgerald .iter() 938968952abSNick Fitzgerald .map(|callee_block_call| self.map_block_call(*callee_block_call)) 939968952abSNick Fitzgerald .collect::<SmallBlockCallVec>(); 940968952abSNick Fitzgerald self.func 941968952abSNick Fitzgerald .dfg 942968952abSNick Fitzgerald .jump_tables 943968952abSNick Fitzgerald .push(ir::JumpTableData::new(inlined_default, &inlined_table)) 944968952abSNick Fitzgerald } 945968952abSNick Fitzgerald 946968952abSNick Fitzgerald fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable { 947968952abSNick Fitzgerald let exception_table = &self.callee.dfg.exception_tables[exception_table]; 948968952abSNick Fitzgerald let inlined_sig_ref = self.map_sig_ref(exception_table.signature()); 949968952abSNick Fitzgerald let inlined_normal_return = self.map_block_call(*exception_table.normal_return()); 950968952abSNick Fitzgerald let inlined_table = exception_table 9514590076fSChris Fallin .items() 9524590076fSChris Fallin .map(|item| match item { 9534590076fSChris Fallin ExceptionTableItem::Tag(tag, block_call) => { 9544590076fSChris Fallin ExceptionTableItem::Tag(tag, self.map_block_call(block_call)) 9554590076fSChris Fallin } 9564590076fSChris Fallin ExceptionTableItem::Default(block_call) => { 9574590076fSChris Fallin ExceptionTableItem::Default(self.map_block_call(block_call)) 9584590076fSChris Fallin } 9594590076fSChris Fallin ExceptionTableItem::Context(value) => { 9604590076fSChris Fallin ExceptionTableItem::Context(self.map_value(value)) 9614590076fSChris Fallin } 9624590076fSChris Fallin }) 963968952abSNick Fitzgerald .collect::<SmallVec<[_; 8]>>(); 964968952abSNick Fitzgerald self.func 965968952abSNick Fitzgerald .dfg 966968952abSNick Fitzgerald .exception_tables 967968952abSNick Fitzgerald .push(ir::ExceptionTableData::new( 968968952abSNick Fitzgerald inlined_sig_ref, 969968952abSNick Fitzgerald inlined_normal_return, 970968952abSNick Fitzgerald inlined_table, 971968952abSNick Fitzgerald )) 972968952abSNick Fitzgerald } 973968952abSNick Fitzgerald 974968952abSNick Fitzgerald fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall { 975968952abSNick Fitzgerald let callee_block = block_call.block(&self.callee.dfg.value_lists); 976968952abSNick Fitzgerald let inlined_block = self.entity_map.inlined_block(callee_block); 977968952abSNick Fitzgerald let args = block_call 978968952abSNick Fitzgerald .args(&self.callee.dfg.value_lists) 979968952abSNick Fitzgerald .map(|arg| match arg { 980968952abSNick Fitzgerald ir::BlockArg::Value(value) => self.map_value(value).into(), 981968952abSNick Fitzgerald ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg, 982968952abSNick Fitzgerald }) 983968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 984968952abSNick Fitzgerald ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists) 985968952abSNick Fitzgerald } 986968952abSNick Fitzgerald 9874c01ee2fSChris Fallin fn map_block(&mut self, block: ir::Block) -> ir::Block { 9884c01ee2fSChris Fallin self.entity_map.inlined_block(block) 9894c01ee2fSChris Fallin } 9904c01ee2fSChris Fallin 991968952abSNick Fitzgerald fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef { 992968952abSNick Fitzgerald self.entity_map.inlined_func_ref(func_ref) 993968952abSNick Fitzgerald } 994968952abSNick Fitzgerald 995968952abSNick Fitzgerald fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef { 996968952abSNick Fitzgerald self.entity_map.inlined_sig_ref(sig_ref) 997968952abSNick Fitzgerald } 998968952abSNick Fitzgerald 999968952abSNick Fitzgerald fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot { 1000968952abSNick Fitzgerald self.entity_map.inlined_stack_slot(stack_slot) 1001968952abSNick Fitzgerald } 1002968952abSNick Fitzgerald 1003968952abSNick Fitzgerald fn map_dynamic_stack_slot( 1004968952abSNick Fitzgerald &mut self, 1005968952abSNick Fitzgerald dynamic_stack_slot: ir::DynamicStackSlot, 1006968952abSNick Fitzgerald ) -> ir::DynamicStackSlot { 1007968952abSNick Fitzgerald self.entity_map 1008968952abSNick Fitzgerald .inlined_dynamic_stack_slot(dynamic_stack_slot) 1009968952abSNick Fitzgerald } 1010968952abSNick Fitzgerald 1011968952abSNick Fitzgerald fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant { 1012968952abSNick Fitzgerald self.allocs 1013968952abSNick Fitzgerald .constants 1014968952abSNick Fitzgerald .get(constant) 1015968952abSNick Fitzgerald .and_then(|o| o.expand()) 1016968952abSNick Fitzgerald .expect("should have inlined all callee constants") 1017968952abSNick Fitzgerald } 1018968952abSNick Fitzgerald 1019968952abSNick Fitzgerald fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate { 1020968952abSNick Fitzgerald self.entity_map.inlined_immediate(immediate) 1021968952abSNick Fitzgerald } 1022968952abSNick Fitzgerald } 1023968952abSNick Fitzgerald 1024968952abSNick Fitzgerald /// Inline the callee's layout into the caller's layout. 1025dcedcbf5SNick Fitzgerald /// 1026dcedcbf5SNick Fitzgerald /// Returns the last inlined block in the layout. 1027968952abSNick Fitzgerald fn inline_block_layout( 1028968952abSNick Fitzgerald func: &mut ir::Function, 1029968952abSNick Fitzgerald call_block: ir::Block, 1030968952abSNick Fitzgerald callee: &ir::Function, 1031968952abSNick Fitzgerald entity_map: &EntityMap, 1032dcedcbf5SNick Fitzgerald ) -> ir::Block { 1033e767c56bSNick Fitzgerald debug_assert!(func.layout.is_block_inserted(call_block)); 1034e767c56bSNick Fitzgerald 1035968952abSNick Fitzgerald // Iterate over callee blocks in layout order, inserting their associated 1036968952abSNick Fitzgerald // inlined block into the caller's layout. 1037968952abSNick Fitzgerald let mut prev_inlined_block = call_block; 1038968952abSNick Fitzgerald let mut next_callee_block = callee.layout.entry_block(); 1039968952abSNick Fitzgerald while let Some(callee_block) = next_callee_block { 1040e767c56bSNick Fitzgerald debug_assert!(func.layout.is_block_inserted(prev_inlined_block)); 1041e767c56bSNick Fitzgerald 1042968952abSNick Fitzgerald let inlined_block = entity_map.inlined_block(callee_block); 1043968952abSNick Fitzgerald func.layout 1044968952abSNick Fitzgerald .insert_block_after(inlined_block, prev_inlined_block); 1045968952abSNick Fitzgerald 1046968952abSNick Fitzgerald prev_inlined_block = inlined_block; 1047968952abSNick Fitzgerald next_callee_block = callee.layout.next_block(callee_block); 1048968952abSNick Fitzgerald } 1049e767c56bSNick Fitzgerald 1050e767c56bSNick Fitzgerald debug_assert!(func.layout.is_block_inserted(prev_inlined_block)); 1051dcedcbf5SNick Fitzgerald prev_inlined_block 1052968952abSNick Fitzgerald } 1053968952abSNick Fitzgerald 1054968952abSNick Fitzgerald /// Split the call instruction's block just after the call instruction to create 1055968952abSNick Fitzgerald /// the point where control-flow joins after the inlined callee "returns". 1056968952abSNick Fitzgerald /// 1057968952abSNick Fitzgerald /// Note that tail calls do not return to the caller and therefore do not have a 1058968952abSNick Fitzgerald /// control-flow join point. 1059968952abSNick Fitzgerald fn split_off_return_block( 1060968952abSNick Fitzgerald func: &mut ir::Function, 1061968952abSNick Fitzgerald call_inst: ir::Inst, 1062968952abSNick Fitzgerald opcode: ir::Opcode, 1063968952abSNick Fitzgerald callee: &ir::Function, 1064968952abSNick Fitzgerald ) -> Option<ir::Block> { 1065968952abSNick Fitzgerald // When the `call_inst` is not a block terminator, we need to split the 1066968952abSNick Fitzgerald // block. 1067968952abSNick Fitzgerald let return_block = func.layout.next_inst(call_inst).map(|next_inst| { 1068968952abSNick Fitzgerald let return_block = func.dfg.blocks.add(); 1069968952abSNick Fitzgerald func.layout.split_block(return_block, next_inst); 1070968952abSNick Fitzgerald 1071968952abSNick Fitzgerald // Add block parameters for each return value and alias the call 1072968952abSNick Fitzgerald // instruction's results to them. 1073968952abSNick Fitzgerald let old_results = 1074968952abSNick Fitzgerald SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied()); 1075968952abSNick Fitzgerald debug_assert_eq!(old_results.len(), callee.signature.returns.len()); 1076968952abSNick Fitzgerald func.dfg.detach_inst_results(call_inst); 1077968952abSNick Fitzgerald for (abi, old_val) in callee.signature.returns.iter().zip(old_results) { 1078968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val)); 1079968952abSNick Fitzgerald let ret_param = func.dfg.append_block_param(return_block, abi.value_type); 1080968952abSNick Fitzgerald func.dfg.change_to_alias(old_val, ret_param); 1081968952abSNick Fitzgerald } 1082968952abSNick Fitzgerald 1083968952abSNick Fitzgerald return_block 1084968952abSNick Fitzgerald }); 1085968952abSNick Fitzgerald 1086968952abSNick Fitzgerald // When the `call_inst` is a block terminator, then it is either a 1087968952abSNick Fitzgerald // `return_call` or a `try_call`: 1088968952abSNick Fitzgerald // 1089968952abSNick Fitzgerald // * For `return_call`s, we don't have a control-flow join point, because 1090968952abSNick Fitzgerald // the caller permanently transfers control to the callee. 1091968952abSNick Fitzgerald // 1092968952abSNick Fitzgerald // * For `try_call`s, we probably already have a block for the control-flow 1093968952abSNick Fitzgerald // join point, but it isn't guaranteed: the `try_call` might ignore the 1094968952abSNick Fitzgerald // call's returns and not forward them to the normal-return block or it 1095968952abSNick Fitzgerald // might also pass additional arguments. We can only reuse the existing 1096968952abSNick Fitzgerald // normal-return block when the `try_call` forwards exactly our callee's 1097968952abSNick Fitzgerald // returns to that block (and therefore that block's parameter types also 1098968952abSNick Fitzgerald // exactly match the callee's return types). Otherwise, we must create a new 1099968952abSNick Fitzgerald // return block that forwards to the existing normal-return 1100968952abSNick Fitzgerald // block. (Elsewhere, at the end of inlining, we will also update any inlined 1101968952abSNick Fitzgerald // calls to forward any raised exceptions to the caller's exception table, 1102968952abSNick Fitzgerald // as necessary.) 1103968952abSNick Fitzgerald // 1104968952abSNick Fitzgerald // Finally, note that reusing the normal-return's target block is just an 1105968952abSNick Fitzgerald // optimization to emit a simpler CFG when we can, and is not 1106968952abSNick Fitzgerald // fundamentally required for correctness. We could always insert a 1107968952abSNick Fitzgerald // temporary block as our control-flow join point that then forwards to 1108968952abSNick Fitzgerald // the normal-return's target block. However, at the time of writing, 1109968952abSNick Fitzgerald // Cranelift doesn't currently do any jump-threading or branch 1110968952abSNick Fitzgerald // simplification in the mid-end, and removing unnecessary blocks in this 1111968952abSNick Fitzgerald // way can help some subsequent mid-end optimizations. If, in the future, 1112968952abSNick Fitzgerald // we gain support for jump-threading optimizations in the mid-end, we can 1113968952abSNick Fitzgerald // come back and simplify the below code a bit to always generate the 1114968952abSNick Fitzgerald // temporary block, and then rely on the subsequent optimizations to clean 1115968952abSNick Fitzgerald // everything up. 1116968952abSNick Fitzgerald debug_assert_eq!( 1117968952abSNick Fitzgerald return_block.is_none(), 1118968952abSNick Fitzgerald opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall, 1119968952abSNick Fitzgerald ); 1120968952abSNick Fitzgerald return_block.or_else(|| match func.dfg.insts[call_inst] { 1121968952abSNick Fitzgerald ir::InstructionData::TryCall { 1122968952abSNick Fitzgerald opcode: ir::Opcode::TryCall, 1123968952abSNick Fitzgerald args: _, 1124968952abSNick Fitzgerald func_ref: _, 1125968952abSNick Fitzgerald exception, 1126968952abSNick Fitzgerald } => { 1127968952abSNick Fitzgerald let normal_return = func.dfg.exception_tables[exception].normal_return(); 1128968952abSNick Fitzgerald let normal_return_block = normal_return.block(&func.dfg.value_lists); 1129968952abSNick Fitzgerald 1130968952abSNick Fitzgerald // Check to see if we can reuse the existing normal-return block. 1131968952abSNick Fitzgerald { 1132968952abSNick Fitzgerald let normal_return_args = normal_return.args(&func.dfg.value_lists); 1133968952abSNick Fitzgerald if normal_return_args.len() == callee.signature.returns.len() 1134968952abSNick Fitzgerald && normal_return_args.enumerate().all(|(i, arg)| { 1135968952abSNick Fitzgerald let i = u32::try_from(i).unwrap(); 1136968952abSNick Fitzgerald arg == ir::BlockArg::TryCallRet(i) 1137968952abSNick Fitzgerald }) 1138968952abSNick Fitzgerald { 1139968952abSNick Fitzgerald return Some(normal_return_block); 1140968952abSNick Fitzgerald } 1141968952abSNick Fitzgerald } 1142968952abSNick Fitzgerald 1143968952abSNick Fitzgerald // Okay, we cannot reuse the normal-return block. Create a new block 1144968952abSNick Fitzgerald // that has the expected block parameter types and have it jump to 1145968952abSNick Fitzgerald // the normal-return block. 1146968952abSNick Fitzgerald let return_block = func.dfg.blocks.add(); 1147968952abSNick Fitzgerald func.layout.insert_block(return_block, normal_return_block); 1148968952abSNick Fitzgerald 1149968952abSNick Fitzgerald let return_block_params = callee 1150968952abSNick Fitzgerald .signature 1151968952abSNick Fitzgerald .returns 1152968952abSNick Fitzgerald .iter() 1153968952abSNick Fitzgerald .map(|abi| func.dfg.append_block_param(return_block, abi.value_type)) 1154968952abSNick Fitzgerald .collect::<SmallValueVec>(); 1155968952abSNick Fitzgerald 1156968952abSNick Fitzgerald let normal_return_args = func.dfg.exception_tables[exception] 1157968952abSNick Fitzgerald .normal_return() 1158968952abSNick Fitzgerald .args(&func.dfg.value_lists) 1159968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 1160968952abSNick Fitzgerald let jump_args = normal_return_args 1161968952abSNick Fitzgerald .into_iter() 1162968952abSNick Fitzgerald .map(|arg| match arg { 1163968952abSNick Fitzgerald ir::BlockArg::Value(value) => ir::BlockArg::Value(value), 1164968952abSNick Fitzgerald ir::BlockArg::TryCallRet(i) => { 1165968952abSNick Fitzgerald let i = usize::try_from(i).unwrap(); 1166968952abSNick Fitzgerald ir::BlockArg::Value(return_block_params[i]) 1167968952abSNick Fitzgerald } 1168968952abSNick Fitzgerald ir::BlockArg::TryCallExn(_) => { 1169968952abSNick Fitzgerald unreachable!("normal-return edges cannot use exceptional results") 1170968952abSNick Fitzgerald } 1171968952abSNick Fitzgerald }) 1172968952abSNick Fitzgerald .collect::<SmallBlockArgVec>(); 1173968952abSNick Fitzgerald 1174968952abSNick Fitzgerald let mut cursor = FuncCursor::new(func); 1175968952abSNick Fitzgerald cursor.goto_first_insertion_point(return_block); 1176968952abSNick Fitzgerald cursor.ins().jump(normal_return_block, &jump_args); 1177968952abSNick Fitzgerald 1178968952abSNick Fitzgerald Some(return_block) 1179968952abSNick Fitzgerald } 1180968952abSNick Fitzgerald _ => None, 1181968952abSNick Fitzgerald }) 1182968952abSNick Fitzgerald } 1183968952abSNick Fitzgerald 1184968952abSNick Fitzgerald /// Replace the caller's call instruction with a jump to the caller's inlined 1185968952abSNick Fitzgerald /// copy of the callee's entry block. 1186968952abSNick Fitzgerald /// 1187968952abSNick Fitzgerald /// Also associates the callee's parameters with the caller's arguments in our 1188968952abSNick Fitzgerald /// value map. 1189968952abSNick Fitzgerald /// 1190968952abSNick Fitzgerald /// Returns the caller's stack map entries, if any. 1191968952abSNick Fitzgerald fn replace_call_with_jump( 1192968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1193968952abSNick Fitzgerald func: &mut ir::Function, 1194968952abSNick Fitzgerald call_inst: ir::Inst, 1195968952abSNick Fitzgerald callee: &ir::Function, 1196968952abSNick Fitzgerald entity_map: &EntityMap, 1197968952abSNick Fitzgerald ) -> Option<ir::UserStackMapEntryVec> { 1198968952abSNick Fitzgerald trace!("Replacing `call` with `jump`"); 1199968952abSNick Fitzgerald trace!( 1200968952abSNick Fitzgerald " --> call instruction: {call_inst:?}: {}", 1201968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 1202968952abSNick Fitzgerald ); 1203968952abSNick Fitzgerald 1204968952abSNick Fitzgerald let callee_entry_block = callee 1205968952abSNick Fitzgerald .layout 1206968952abSNick Fitzgerald .entry_block() 1207968952abSNick Fitzgerald .expect("callee function should have an entry block"); 1208968952abSNick Fitzgerald let callee_param_values = callee.dfg.block_params(callee_entry_block); 1209968952abSNick Fitzgerald let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied()); 1210968952abSNick Fitzgerald debug_assert_eq!(callee_param_values.len(), caller_arg_values.len()); 1211968952abSNick Fitzgerald debug_assert_eq!(callee_param_values.len(), callee.signature.params.len()); 1212968952abSNick Fitzgerald for (abi, (callee_param_value, caller_arg_value)) in callee 1213968952abSNick Fitzgerald .signature 1214968952abSNick Fitzgerald .params 1215968952abSNick Fitzgerald .iter() 1216968952abSNick Fitzgerald .zip(callee_param_values.into_iter().zip(caller_arg_values)) 1217968952abSNick Fitzgerald { 1218968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value)); 1219968952abSNick Fitzgerald debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value)); 1220968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value); 1221968952abSNick Fitzgerald } 1222968952abSNick Fitzgerald 1223968952abSNick Fitzgerald // Replace the caller's call instruction with a jump to the caller's inlined 1224968952abSNick Fitzgerald // copy of the callee's entry block. 1225968952abSNick Fitzgerald // 1226968952abSNick Fitzgerald // Note that the call block dominates the inlined entry block (and also all 1227968952abSNick Fitzgerald // other inlined blocks) so we can reference the arguments directly, and do 1228968952abSNick Fitzgerald // not need to add block parameters to the inlined entry block. 1229968952abSNick Fitzgerald let inlined_entry_block = entity_map.inlined_block(callee_entry_block); 1230968952abSNick Fitzgerald func.dfg.replace(call_inst).jump(inlined_entry_block, &[]); 1231968952abSNick Fitzgerald trace!( 1232968952abSNick Fitzgerald " --> replaced with jump instruction: {call_inst:?}: {}", 1233968952abSNick Fitzgerald func.dfg.display_inst(call_inst) 1234968952abSNick Fitzgerald ); 1235968952abSNick Fitzgerald 1236968952abSNick Fitzgerald let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst); 1237968952abSNick Fitzgerald stack_map_entries 1238968952abSNick Fitzgerald } 1239968952abSNick Fitzgerald 1240968952abSNick Fitzgerald /// Keeps track of mapping callee entities to their associated inlined caller 1241968952abSNick Fitzgerald /// entities. 1242968952abSNick Fitzgerald #[derive(Default)] 1243968952abSNick Fitzgerald struct EntityMap { 1244968952abSNick Fitzgerald // Rather than doing an implicit, demand-based, DCE'ing translation of 1245968952abSNick Fitzgerald // entities, which would require maps from each callee entity to its 1246968952abSNick Fitzgerald // associated caller entity, we copy all entities into the caller, remember 1247968952abSNick Fitzgerald // each entity's initial offset, and then mapping from the callee to the 1248968952abSNick Fitzgerald // inlined caller entity is just adding that initial offset to the callee's 1249968952abSNick Fitzgerald // index. This should be both faster and simpler than the alternative. Most 1250968952abSNick Fitzgerald // of these sets are relatively small, and they rarely have too much dead 1251968952abSNick Fitzgerald // code in practice, so this is a good trade off. 1252968952abSNick Fitzgerald // 1253968952abSNick Fitzgerald // Note that there are a few kinds of entities that are excluded from the 1254968952abSNick Fitzgerald // `EntityMap`, and for which we do actually take the demand-based approach: 1255968952abSNick Fitzgerald // values and value lists being the notable ones. 1256968952abSNick Fitzgerald block_offset: Option<u32>, 1257968952abSNick Fitzgerald global_value_offset: Option<u32>, 1258968952abSNick Fitzgerald sig_ref_offset: Option<u32>, 1259968952abSNick Fitzgerald func_ref_offset: Option<u32>, 1260968952abSNick Fitzgerald stack_slot_offset: Option<u32>, 1261968952abSNick Fitzgerald dynamic_type_offset: Option<u32>, 1262968952abSNick Fitzgerald dynamic_stack_slot_offset: Option<u32>, 1263968952abSNick Fitzgerald immediate_offset: Option<u32>, 1264968952abSNick Fitzgerald } 1265968952abSNick Fitzgerald 1266968952abSNick Fitzgerald impl EntityMap { 1267968952abSNick Fitzgerald fn inlined_block(&self, callee_block: ir::Block) -> ir::Block { 1268968952abSNick Fitzgerald let offset = self 1269968952abSNick Fitzgerald .block_offset 1270968952abSNick Fitzgerald .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`"); 1271968952abSNick Fitzgerald ir::Block::from_u32(offset + callee_block.as_u32()) 1272968952abSNick Fitzgerald } 1273968952abSNick Fitzgerald 1274e3a607eaSNick Fitzgerald fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> { 1275e3a607eaSNick Fitzgerald let start = self.block_offset.expect( 1276e3a607eaSNick Fitzgerald "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`", 1277e3a607eaSNick Fitzgerald ); 1278e3a607eaSNick Fitzgerald 1279e3a607eaSNick Fitzgerald let end = func.dfg.blocks.len(); 1280e3a607eaSNick Fitzgerald let end = u32::try_from(end).unwrap(); 1281e3a607eaSNick Fitzgerald 1282e3a607eaSNick Fitzgerald (start..end).map(|i| ir::Block::from_u32(i)) 1283e3a607eaSNick Fitzgerald } 1284e3a607eaSNick Fitzgerald 1285968952abSNick Fitzgerald fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue { 1286968952abSNick Fitzgerald let offset = self 1287968952abSNick Fitzgerald .global_value_offset 1288968952abSNick Fitzgerald .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`"); 1289968952abSNick Fitzgerald ir::GlobalValue::from_u32(offset + callee_global_value.as_u32()) 1290968952abSNick Fitzgerald } 1291968952abSNick Fitzgerald 1292968952abSNick Fitzgerald fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef { 1293968952abSNick Fitzgerald let offset = self.sig_ref_offset.expect( 1294968952abSNick Fitzgerald "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`", 1295968952abSNick Fitzgerald ); 1296968952abSNick Fitzgerald ir::SigRef::from_u32(offset + callee_sig_ref.as_u32()) 1297968952abSNick Fitzgerald } 1298968952abSNick Fitzgerald 1299968952abSNick Fitzgerald fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef { 1300968952abSNick Fitzgerald let offset = self.func_ref_offset.expect( 1301968952abSNick Fitzgerald "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`", 1302968952abSNick Fitzgerald ); 1303968952abSNick Fitzgerald ir::FuncRef::from_u32(offset + callee_func_ref.as_u32()) 1304968952abSNick Fitzgerald } 1305968952abSNick Fitzgerald 1306968952abSNick Fitzgerald fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot { 1307968952abSNick Fitzgerald let offset = self.stack_slot_offset.expect( 1308968952abSNick Fitzgerald "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`", 1309968952abSNick Fitzgerald ); 1310968952abSNick Fitzgerald ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32()) 1311968952abSNick Fitzgerald } 1312968952abSNick Fitzgerald 1313968952abSNick Fitzgerald fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType { 1314968952abSNick Fitzgerald let offset = self.dynamic_type_offset.expect( 1315968952abSNick Fitzgerald "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`", 1316968952abSNick Fitzgerald ); 1317968952abSNick Fitzgerald ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32()) 1318968952abSNick Fitzgerald } 1319968952abSNick Fitzgerald 1320968952abSNick Fitzgerald fn inlined_dynamic_stack_slot( 1321968952abSNick Fitzgerald &self, 1322968952abSNick Fitzgerald callee_dynamic_stack_slot: ir::DynamicStackSlot, 1323968952abSNick Fitzgerald ) -> ir::DynamicStackSlot { 1324968952abSNick Fitzgerald let offset = self.dynamic_stack_slot_offset.expect( 1325968952abSNick Fitzgerald "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`", 1326968952abSNick Fitzgerald ); 1327968952abSNick Fitzgerald ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32()) 1328968952abSNick Fitzgerald } 1329968952abSNick Fitzgerald 1330968952abSNick Fitzgerald fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate { 1331968952abSNick Fitzgerald let offset = self.immediate_offset.expect( 1332968952abSNick Fitzgerald "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`", 1333968952abSNick Fitzgerald ); 1334968952abSNick Fitzgerald ir::Immediate::from_u32(offset + callee_immediate.as_u32()) 1335968952abSNick Fitzgerald } 1336968952abSNick Fitzgerald } 1337968952abSNick Fitzgerald 1338968952abSNick Fitzgerald /// Translate all of the callee's various entities into the caller, producing an 1339968952abSNick Fitzgerald /// `EntityMap` that can be used to translate callee entity references into 1340968952abSNick Fitzgerald /// inlined caller entity references. 1341968952abSNick Fitzgerald fn create_entities( 1342968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1343968952abSNick Fitzgerald func: &mut ir::Function, 1344968952abSNick Fitzgerald callee: &ir::Function, 1345968952abSNick Fitzgerald ) -> EntityMap { 1346968952abSNick Fitzgerald let mut entity_map = EntityMap::default(); 1347968952abSNick Fitzgerald 1348968952abSNick Fitzgerald entity_map.block_offset = Some(create_blocks(allocs, func, callee)); 1349968952abSNick Fitzgerald entity_map.global_value_offset = Some(create_global_values(func, callee)); 1350968952abSNick Fitzgerald entity_map.sig_ref_offset = Some(create_sig_refs(func, callee)); 13514cbea5e8SNick Fitzgerald create_user_external_name_refs(allocs, func, callee); 13524cbea5e8SNick Fitzgerald entity_map.func_ref_offset = Some(create_func_refs(allocs, func, callee, &entity_map)); 1353968952abSNick Fitzgerald entity_map.stack_slot_offset = Some(create_stack_slots(func, callee)); 1354968952abSNick Fitzgerald entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map)); 1355968952abSNick Fitzgerald entity_map.dynamic_stack_slot_offset = 1356968952abSNick Fitzgerald Some(create_dynamic_stack_slots(func, callee, &entity_map)); 1357968952abSNick Fitzgerald entity_map.immediate_offset = Some(create_immediates(func, callee)); 1358968952abSNick Fitzgerald 1359968952abSNick Fitzgerald // `ir::ConstantData` is deduplicated, so we cannot use our offset scheme 1360968952abSNick Fitzgerald // for `ir::Constant`s. Nonetheless, we still insert them into the caller 1361968952abSNick Fitzgerald // now, at the same time as the rest of our entities. 1362968952abSNick Fitzgerald create_constants(allocs, func, callee); 1363968952abSNick Fitzgerald 1364968952abSNick Fitzgerald entity_map 1365968952abSNick Fitzgerald } 1366968952abSNick Fitzgerald 1367968952abSNick Fitzgerald /// Create inlined blocks in the caller for every block in the callee. 1368968952abSNick Fitzgerald fn create_blocks( 1369968952abSNick Fitzgerald allocs: &mut InliningAllocs, 1370968952abSNick Fitzgerald func: &mut ir::Function, 1371968952abSNick Fitzgerald callee: &ir::Function, 1372968952abSNick Fitzgerald ) -> u32 { 1373968952abSNick Fitzgerald let offset = func.dfg.blocks.len(); 1374968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1375968952abSNick Fitzgerald 1376968952abSNick Fitzgerald func.dfg.blocks.reserve(callee.dfg.blocks.len()); 1377968952abSNick Fitzgerald for callee_block in callee.dfg.blocks.iter() { 1378968952abSNick Fitzgerald let caller_block = func.dfg.blocks.add(); 1379968952abSNick Fitzgerald trace!("Callee {callee_block:?} = inlined {caller_block:?}"); 1380968952abSNick Fitzgerald 1381968952abSNick Fitzgerald if callee.layout.is_cold(callee_block) { 1382968952abSNick Fitzgerald func.layout.set_cold(caller_block); 1383968952abSNick Fitzgerald } 1384968952abSNick Fitzgerald 1385968952abSNick Fitzgerald // Note: the entry block does not need parameters because the only 1386968952abSNick Fitzgerald // predecessor is the call block and we associate the callee's 1387968952abSNick Fitzgerald // parameters with the caller's arguments directly. 1388968952abSNick Fitzgerald if callee.layout.entry_block() != Some(callee_block) { 1389968952abSNick Fitzgerald for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) { 1390968952abSNick Fitzgerald let ty = callee.dfg.value_type(*callee_param); 1391968952abSNick Fitzgerald let caller_param = func.dfg.append_block_param(caller_block, ty); 1392968952abSNick Fitzgerald 1393968952abSNick Fitzgerald allocs.set_inlined_value(callee, *callee_param, caller_param); 1394968952abSNick Fitzgerald } 1395968952abSNick Fitzgerald } 1396968952abSNick Fitzgerald } 1397968952abSNick Fitzgerald 1398968952abSNick Fitzgerald offset 1399968952abSNick Fitzgerald } 1400968952abSNick Fitzgerald 1401968952abSNick Fitzgerald /// Copy and translate global values from the callee into the caller. 1402968952abSNick Fitzgerald fn create_global_values(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1403968952abSNick Fitzgerald let gv_offset = func.global_values.len(); 1404968952abSNick Fitzgerald let gv_offset = u32::try_from(gv_offset).unwrap(); 1405968952abSNick Fitzgerald 1406968952abSNick Fitzgerald func.global_values.reserve(callee.global_values.len()); 1407968952abSNick Fitzgerald for gv in callee.global_values.values() { 1408968952abSNick Fitzgerald func.global_values.push(match gv { 1409968952abSNick Fitzgerald // These kinds of global values reference other global values, so we 1410968952abSNick Fitzgerald // need to fixup that reference. 1411968952abSNick Fitzgerald ir::GlobalValueData::Load { 1412968952abSNick Fitzgerald base, 1413968952abSNick Fitzgerald offset, 1414968952abSNick Fitzgerald global_type, 1415968952abSNick Fitzgerald flags, 1416968952abSNick Fitzgerald } => ir::GlobalValueData::Load { 1417968952abSNick Fitzgerald base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset), 1418968952abSNick Fitzgerald offset: *offset, 1419968952abSNick Fitzgerald global_type: *global_type, 1420968952abSNick Fitzgerald flags: *flags, 1421968952abSNick Fitzgerald }, 1422968952abSNick Fitzgerald ir::GlobalValueData::IAddImm { 1423968952abSNick Fitzgerald base, 1424968952abSNick Fitzgerald offset, 1425968952abSNick Fitzgerald global_type, 1426968952abSNick Fitzgerald } => ir::GlobalValueData::IAddImm { 1427968952abSNick Fitzgerald base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset), 1428968952abSNick Fitzgerald offset: *offset, 1429968952abSNick Fitzgerald global_type: *global_type, 1430968952abSNick Fitzgerald }, 1431968952abSNick Fitzgerald 1432968952abSNick Fitzgerald // These kinds of global values do not reference other global 1433968952abSNick Fitzgerald // values, so we can just clone them. 1434968952abSNick Fitzgerald ir::GlobalValueData::VMContext 1435968952abSNick Fitzgerald | ir::GlobalValueData::Symbol { .. } 1436968952abSNick Fitzgerald | ir::GlobalValueData::DynScaleTargetConst { .. } => gv.clone(), 1437968952abSNick Fitzgerald }); 1438968952abSNick Fitzgerald } 1439968952abSNick Fitzgerald 1440968952abSNick Fitzgerald gv_offset 1441968952abSNick Fitzgerald } 1442968952abSNick Fitzgerald 1443968952abSNick Fitzgerald /// Copy `ir::SigRef`s from the callee into the caller. 1444968952abSNick Fitzgerald fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1445968952abSNick Fitzgerald let offset = func.dfg.signatures.len(); 1446968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1447968952abSNick Fitzgerald 1448968952abSNick Fitzgerald func.dfg.signatures.reserve(callee.dfg.signatures.len()); 1449968952abSNick Fitzgerald for sig in callee.dfg.signatures.values() { 1450968952abSNick Fitzgerald func.dfg.signatures.push(sig.clone()); 1451968952abSNick Fitzgerald } 1452968952abSNick Fitzgerald 1453968952abSNick Fitzgerald offset 1454968952abSNick Fitzgerald } 1455968952abSNick Fitzgerald 14564cbea5e8SNick Fitzgerald fn create_user_external_name_refs( 14574cbea5e8SNick Fitzgerald allocs: &mut InliningAllocs, 14584cbea5e8SNick Fitzgerald func: &mut ir::Function, 14594cbea5e8SNick Fitzgerald callee: &ir::Function, 14604cbea5e8SNick Fitzgerald ) { 14614cbea5e8SNick Fitzgerald for (callee_named_func_ref, name) in callee.params.user_named_funcs().iter() { 14624cbea5e8SNick Fitzgerald let caller_named_func_ref = func.declare_imported_user_function(name.clone()); 14634cbea5e8SNick Fitzgerald allocs.user_external_name_refs[callee_named_func_ref] = Some(caller_named_func_ref).into(); 14644cbea5e8SNick Fitzgerald } 14654cbea5e8SNick Fitzgerald } 14664cbea5e8SNick Fitzgerald 1467968952abSNick Fitzgerald /// Translate `ir::FuncRef`s from the callee into the caller. 14684cbea5e8SNick Fitzgerald fn create_func_refs( 14694cbea5e8SNick Fitzgerald allocs: &InliningAllocs, 14704cbea5e8SNick Fitzgerald func: &mut ir::Function, 14714cbea5e8SNick Fitzgerald callee: &ir::Function, 14724cbea5e8SNick Fitzgerald entity_map: &EntityMap, 14734cbea5e8SNick Fitzgerald ) -> u32 { 1474968952abSNick Fitzgerald let offset = func.dfg.ext_funcs.len(); 1475968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1476968952abSNick Fitzgerald 1477968952abSNick Fitzgerald func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len()); 1478968952abSNick Fitzgerald for ir::ExtFuncData { 1479968952abSNick Fitzgerald name, 1480968952abSNick Fitzgerald signature, 1481968952abSNick Fitzgerald colocated, 1482*87ed3b60SChris Fallin patchable, 1483968952abSNick Fitzgerald } in callee.dfg.ext_funcs.values() 1484968952abSNick Fitzgerald { 1485968952abSNick Fitzgerald func.dfg.ext_funcs.push(ir::ExtFuncData { 14864cbea5e8SNick Fitzgerald name: match name { 14874cbea5e8SNick Fitzgerald ir::ExternalName::User(name_ref) => { 14884cbea5e8SNick Fitzgerald ir::ExternalName::User(allocs.user_external_name_refs[*name_ref].expect( 14894cbea5e8SNick Fitzgerald "should have translated all `ir::UserExternalNameRef`s before translating \ 14904cbea5e8SNick Fitzgerald `ir::FuncRef`s", 14914cbea5e8SNick Fitzgerald )) 14924cbea5e8SNick Fitzgerald } 14934cbea5e8SNick Fitzgerald ir::ExternalName::TestCase(_) 14944cbea5e8SNick Fitzgerald | ir::ExternalName::LibCall(_) 14954cbea5e8SNick Fitzgerald | ir::ExternalName::KnownSymbol(_) => name.clone(), 14964cbea5e8SNick Fitzgerald }, 1497968952abSNick Fitzgerald signature: entity_map.inlined_sig_ref(*signature), 1498968952abSNick Fitzgerald colocated: *colocated, 1499*87ed3b60SChris Fallin patchable: *patchable, 1500968952abSNick Fitzgerald }); 1501968952abSNick Fitzgerald } 1502968952abSNick Fitzgerald 1503968952abSNick Fitzgerald offset 1504968952abSNick Fitzgerald } 1505968952abSNick Fitzgerald 1506968952abSNick Fitzgerald /// Copy stack slots from the callee into the caller. 1507968952abSNick Fitzgerald fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1508968952abSNick Fitzgerald let offset = func.sized_stack_slots.len(); 1509968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1510968952abSNick Fitzgerald 1511968952abSNick Fitzgerald func.sized_stack_slots 1512968952abSNick Fitzgerald .reserve(callee.sized_stack_slots.len()); 1513968952abSNick Fitzgerald for slot in callee.sized_stack_slots.values() { 1514968952abSNick Fitzgerald func.sized_stack_slots.push(slot.clone()); 1515968952abSNick Fitzgerald } 1516968952abSNick Fitzgerald 1517968952abSNick Fitzgerald offset 1518968952abSNick Fitzgerald } 1519968952abSNick Fitzgerald 1520968952abSNick Fitzgerald /// Copy dynamic types from the callee into the caller. 1521968952abSNick Fitzgerald fn create_dynamic_types( 1522968952abSNick Fitzgerald func: &mut ir::Function, 1523968952abSNick Fitzgerald callee: &ir::Function, 1524968952abSNick Fitzgerald entity_map: &EntityMap, 1525968952abSNick Fitzgerald ) -> u32 { 1526968952abSNick Fitzgerald let offset = func.dynamic_stack_slots.len(); 1527968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1528968952abSNick Fitzgerald 1529968952abSNick Fitzgerald func.dfg 1530968952abSNick Fitzgerald .dynamic_types 1531968952abSNick Fitzgerald .reserve(callee.dfg.dynamic_types.len()); 1532968952abSNick Fitzgerald for ir::DynamicTypeData { 1533968952abSNick Fitzgerald base_vector_ty, 1534968952abSNick Fitzgerald dynamic_scale, 1535968952abSNick Fitzgerald } in callee.dfg.dynamic_types.values() 1536968952abSNick Fitzgerald { 1537968952abSNick Fitzgerald func.dfg.dynamic_types.push(ir::DynamicTypeData { 1538968952abSNick Fitzgerald base_vector_ty: *base_vector_ty, 1539968952abSNick Fitzgerald dynamic_scale: entity_map.inlined_global_value(*dynamic_scale), 1540968952abSNick Fitzgerald }); 1541968952abSNick Fitzgerald } 1542968952abSNick Fitzgerald 1543968952abSNick Fitzgerald offset 1544968952abSNick Fitzgerald } 1545968952abSNick Fitzgerald 1546968952abSNick Fitzgerald /// Copy dynamic stack slots from the callee into the caller. 1547968952abSNick Fitzgerald fn create_dynamic_stack_slots( 1548968952abSNick Fitzgerald func: &mut ir::Function, 1549968952abSNick Fitzgerald callee: &ir::Function, 1550968952abSNick Fitzgerald entity_map: &EntityMap, 1551968952abSNick Fitzgerald ) -> u32 { 1552968952abSNick Fitzgerald let offset = func.dynamic_stack_slots.len(); 1553968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1554968952abSNick Fitzgerald 1555968952abSNick Fitzgerald func.dynamic_stack_slots 1556968952abSNick Fitzgerald .reserve(callee.dynamic_stack_slots.len()); 1557968952abSNick Fitzgerald for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() { 1558968952abSNick Fitzgerald func.dynamic_stack_slots.push(ir::DynamicStackSlotData { 1559968952abSNick Fitzgerald kind: *kind, 1560968952abSNick Fitzgerald dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty), 1561968952abSNick Fitzgerald }); 1562968952abSNick Fitzgerald } 1563968952abSNick Fitzgerald 1564968952abSNick Fitzgerald offset 1565968952abSNick Fitzgerald } 1566968952abSNick Fitzgerald 1567968952abSNick Fitzgerald /// Copy immediates from the callee into the caller. 1568968952abSNick Fitzgerald fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 { 1569968952abSNick Fitzgerald let offset = func.dfg.immediates.len(); 1570968952abSNick Fitzgerald let offset = u32::try_from(offset).unwrap(); 1571968952abSNick Fitzgerald 1572968952abSNick Fitzgerald func.dfg.immediates.reserve(callee.dfg.immediates.len()); 1573968952abSNick Fitzgerald for imm in callee.dfg.immediates.values() { 1574968952abSNick Fitzgerald func.dfg.immediates.push(imm.clone()); 1575968952abSNick Fitzgerald } 1576968952abSNick Fitzgerald 1577968952abSNick Fitzgerald offset 1578968952abSNick Fitzgerald } 1579968952abSNick Fitzgerald 1580968952abSNick Fitzgerald /// Copy constants from the callee into the caller. 1581968952abSNick Fitzgerald fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) { 1582968952abSNick Fitzgerald for (callee_constant, data) in callee.dfg.constants.iter() { 1583968952abSNick Fitzgerald let inlined_constant = func.dfg.constants.insert(data.clone()); 1584968952abSNick Fitzgerald allocs.constants[*callee_constant] = Some(inlined_constant).into(); 1585968952abSNick Fitzgerald } 1586968952abSNick Fitzgerald } 1587