1 //! Function inlining infrastructure.
2 //!
3 //! This module provides "inlining as a library" to Cranelift users; it does
4 //! _not_ provide a complete, off-the-shelf inlining solution. Cranelift's
5 //! compilation context is per-function and does not encompass the full call
6 //! graph. It does not know which functions are hot and which are cold, which
7 //! have been marked the equivalent of `#[inline(never)]`, etc... Only the
8 //! Cranelift user can understand these aspects of the full compilation
9 //! pipeline, and these things can be very different between (say) Wasmtime and
10 //! `cg_clif`. Therefore, this module does not attempt to define hueristics for
11 //! when inlining a particular call is likely beneficial. This module only
12 //! provides hooks for the Cranelift user to define whether a given call should
13 //! be inlined or not, and the mechanics to inline a callee into a particular
14 //! call site when directed to do so by the Cranelift user.
15 //!
16 //! The top-level inlining entry point during Cranelift compilation is
17 //! [`Context::inline`][crate::Context::inline]. It takes an [`Inline`] trait
18 //! implementation, which is authored by the Cranelift user and directs
19 //! Cranelift whether to inline a particular call, and, when inlining, gives
20 //! Cranelift the body of the callee that is to be inlined.
21 
22 use crate::cursor::{Cursor as _, FuncCursor};
23 use crate::ir::{self, InstBuilder as _};
24 use crate::result::CodegenResult;
25 use crate::trace;
26 use crate::traversals::Dfs;
27 use alloc::borrow::Cow;
28 use alloc::vec::Vec;
29 use cranelift_entity::{SecondaryMap, packed_option::PackedOption};
30 use smallvec::SmallVec;
31 
32 type SmallValueVec = SmallVec<[ir::Value; 8]>;
33 type SmallBlockArgVec = SmallVec<[ir::BlockArg; 8]>;
34 type SmallBlockCallVec = SmallVec<[ir::BlockCall; 8]>;
35 
36 /// A command directing Cranelift whether or not to inline a particular call.
37 pub enum InlineCommand<'a> {
38     /// Keep the call as-is, out-of-line, and do not inline the callee.
39     KeepCall,
40     /// Inline the call, using this function as the body of the callee.
41     ///
42     /// It is the `Inline` implementor's responsibility to ensure that this
43     /// function is the correct callee. Providing the wrong function may result
44     /// in panics during compilation or incorrect runtime behavior.
45     Inline(Cow<'a, ir::Function>),
46 }
47 
48 /// A trait for directing Cranelift whether to inline a particular call or not.
49 ///
50 /// Used in combination with the [`Context::inline`][crate::Context::inline]
51 /// method.
52 pub trait Inline {
53     /// A hook invoked for each direct call instruction in a function, whose
54     /// result determines whether Cranelift should inline a given call.
55     ///
56     /// The Cranelift user is responsible for defining their own hueristics and
57     /// deciding whether inlining the call is beneficial.
58     ///
59     /// When returning a function and directing Cranelift to inline its body
60     /// into the call site, the `Inline` implementer must ensure the following:
61     ///
62     /// * The returned function's signature exactly matches the `callee`
63     ///   `FuncRef`'s signature.
64     ///
65     /// * The returned function must be legalized.
66     ///
67     /// * The returned function must be valid (i.e. it must pass the CLIF
68     ///   verifier).
69     ///
70     /// * The returned function is a correct and valid implementation of the
71     ///   `callee` according to your language's semantics.
72     ///
73     /// Failure to uphold these invariants may result in panics during
74     /// compilation or incorrect runtime behavior in the generated code.
75     fn inline(
76         &self,
77         caller: &ir::Function,
78         call_inst: ir::Inst,
79         call_opcode: ir::Opcode,
80         callee: ir::FuncRef,
81         call_args: &[ir::Value],
82     ) -> InlineCommand<'_>;
83 }
84 
85 impl<'a, T> Inline for &'a T
86 where
87     T: Inline,
88 {
89     fn inline(
90         &self,
91         caller: &ir::Function,
92         inst: ir::Inst,
93         opcode: ir::Opcode,
94         callee: ir::FuncRef,
95         args: &[ir::Value],
96     ) -> InlineCommand<'_> {
97         (*self).inline(caller, inst, opcode, callee, args)
98     }
99 }
100 
101 /// Walk the given function, invoke the `Inline` implementation for each call
102 /// instruction, and inline the callee when directed to do so.
103 ///
104 /// Returns whether any call was inlined.
105 pub(crate) fn do_inlining(func: &mut ir::Function, inliner: impl Inline) -> CodegenResult<bool> {
106     let mut inlined_any = false;
107     let mut allocs = InliningAllocs::default();
108 
109     let mut cursor = FuncCursor::new(func);
110     while let Some(block) = cursor.next_block() {
111         // Always keep track of our previous cursor position. After we inline a
112         // call, replacing the current position with an arbitrary sub-CFG, we
113         // back up to this previous position. This makes sure our cursor is
114         // always at a position that is inserted in the layout and also enables
115         // multi-level inlining, if desired by the user, where we consider any
116         // newly-inlined call instructions for further inlining.
117         let mut prev_pos;
118 
119         while let Some(inst) = {
120             prev_pos = cursor.position();
121             cursor.next_inst()
122         } {
123             match cursor.func.dfg.insts[inst] {
124                 ir::InstructionData::Call {
125                     opcode: opcode @ ir::Opcode::Call | opcode @ ir::Opcode::ReturnCall,
126                     args: _,
127                     func_ref,
128                 } => {
129                     let args = cursor.func.dfg.inst_args(inst);
130                     match inliner.inline(&cursor.func, inst, opcode, func_ref, args) {
131                         InlineCommand::KeepCall => continue,
132                         InlineCommand::Inline(callee) => {
133                             inline_one(
134                                 &mut allocs,
135                                 cursor.func,
136                                 func_ref,
137                                 block,
138                                 inst,
139                                 opcode,
140                                 &callee,
141                                 None,
142                             );
143                             inlined_any = true;
144                             cursor.set_position(prev_pos);
145                         }
146                     }
147                 }
148                 ir::InstructionData::TryCall {
149                     opcode: opcode @ ir::Opcode::TryCall,
150                     args: _,
151                     func_ref,
152                     exception,
153                 } => {
154                     let args = cursor.func.dfg.inst_args(inst);
155                     match inliner.inline(&cursor.func, inst, opcode, func_ref, args) {
156                         InlineCommand::KeepCall => continue,
157                         InlineCommand::Inline(callee) => {
158                             inline_one(
159                                 &mut allocs,
160                                 cursor.func,
161                                 func_ref,
162                                 block,
163                                 inst,
164                                 opcode,
165                                 &callee,
166                                 Some(exception),
167                             );
168                             inlined_any = true;
169                             cursor.set_position(prev_pos);
170                         }
171                     }
172                 }
173                 _ => continue,
174             }
175         }
176     }
177 
178     Ok(inlined_any)
179 }
180 
181 #[derive(Default)]
182 struct InliningAllocs {
183     /// Map from callee value to inlined caller value.
184     values: SecondaryMap<ir::Value, PackedOption<ir::Value>>,
185 
186     /// Map from callee constant to inlined caller constant.
187     constants: SecondaryMap<ir::Constant, PackedOption<ir::Constant>>,
188 
189     /// The set of _caller_ inlined call instructions that need exception table
190     /// fixups at the end of inlining.
191     ///
192     /// This includes all kinds of non-returning calls, not just the literal
193     /// `call` instruction: `call_indirect`, `try_call`, `try_call_indirect`,
194     /// etc... However, it does not include `return_call` and
195     /// `return_call_indirect` instructions because the caller cannot catch
196     /// exceptions that those calls throw because the caller is no longer on the
197     /// stack as soon as they are executed.
198     ///
199     /// Note: this is a simple `Vec`, and not an `EntitySet`, because it is very
200     /// sparse: most of the caller's instructions are not inlined call
201     /// instructions. Additionally, we require deterministic iteration order and
202     /// do not require set-membership testing, so a hash set is not a good
203     /// choice either.
204     calls_needing_exception_table_fixup: Vec<ir::Inst>,
205 
206     /// The set of existing tags already caught by an inlined `try_call`
207     /// instruction's exception table, for filtering out duplicate tags when
208     /// merging exception tables.
209     ///
210     /// Note: this is a `HashSet`, and not an `EntitySet`, because tags indices
211     /// are completely arbitrary and there is no guarantee that they start from
212     /// zero or are contiguous.
213     existing_exception_tags: crate::HashSet<Option<ir::ExceptionTag>>,
214 }
215 
216 impl InliningAllocs {
217     fn reset(&mut self, callee: &ir::Function) {
218         let InliningAllocs {
219             values,
220             constants,
221             calls_needing_exception_table_fixup,
222             existing_exception_tags,
223         } = self;
224 
225         values.clear();
226         values.resize(callee.dfg.len_values());
227 
228         constants.clear();
229         constants.resize(callee.dfg.constants.len());
230 
231         // Note: We do not reserve capacity for
232         // `calls_needing_exception_table_fixup` because it is a sparse set and
233         // we don't know how large it needs to be ahead of time.
234         calls_needing_exception_table_fixup.clear();
235 
236         // Note: We do not reserve capacity for `existing_exception_tags`
237         // because it is a sparse set and we don't know how large it needs to be
238         // ahead of time.
239         existing_exception_tags.clear();
240     }
241 
242     fn set_inlined_value(
243         &mut self,
244         callee: &ir::Function,
245         callee_val: ir::Value,
246         inlined_val: ir::Value,
247     ) {
248         trace!("  --> callee {callee_val:?} = inlined {inlined_val:?}");
249         debug_assert!(self.values[callee_val].is_none());
250         let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
251         debug_assert!(self.values[resolved_callee_val].is_none());
252         self.values[resolved_callee_val] = Some(inlined_val).into();
253     }
254 
255     fn get_inlined_value(&self, callee: &ir::Function, callee_val: ir::Value) -> Option<ir::Value> {
256         let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
257         self.values[resolved_callee_val].expand()
258     }
259 }
260 
261 /// Inline one particular function call.
262 fn inline_one(
263     allocs: &mut InliningAllocs,
264     func: &mut ir::Function,
265     callee_func_ref: ir::FuncRef,
266     call_block: ir::Block,
267     call_inst: ir::Inst,
268     call_opcode: ir::Opcode,
269     callee: &ir::Function,
270     call_exception_table: Option<ir::ExceptionTable>,
271 ) {
272     trace!(
273         "Inlining call {call_inst:?}: {}\n\
274          with callee = {callee:?}",
275         func.dfg.display_inst(call_inst)
276     );
277 
278     // Type check callee signature.
279     let expected_callee_sig = func.dfg.ext_funcs[callee_func_ref].signature;
280     let expected_callee_sig = &func.dfg.signatures[expected_callee_sig];
281     assert_eq!(expected_callee_sig, &callee.signature);
282 
283     allocs.reset(callee);
284 
285     // First, append various callee entity arenas to the end of the caller's
286     // entity arenas.
287     let entity_map = create_entities(allocs, func, callee);
288 
289     // Inlined prologue: split the call instruction's block at the point of the
290     // call and replace the call with a jump.
291     let return_block = split_off_return_block(func, call_inst, call_opcode, callee);
292     let call_stack_map = replace_call_with_jump(allocs, func, call_inst, callee, &entity_map);
293 
294     // Prepare for translating the actual instructions by inserting the inlined
295     // blocks into the caller's layout in the same order that they appear in the
296     // callee.
297     inline_block_layout(func, call_block, callee, &entity_map);
298 
299     // Translate each instruction from the callee into the caller,
300     // appending them to their associated block in the caller.
301     //
302     // Note that we iterate over the callee with a pre-order traversal so that
303     // we see value defs before uses.
304     for callee_block in Dfs::new().pre_order_iter(callee) {
305         let inlined_block = entity_map.inlined_block(callee_block);
306         trace!(
307             "Processing instructions in callee block {callee_block:?} (inlined block {inlined_block:?}"
308         );
309 
310         let mut next_callee_inst = callee.layout.first_inst(callee_block);
311         while let Some(callee_inst) = next_callee_inst {
312             trace!(
313                 "Processing callee instruction {callee_inst:?}: {}",
314                 callee.dfg.display_inst(callee_inst)
315             );
316 
317             assert_ne!(
318                 callee.dfg.insts[callee_inst].opcode(),
319                 ir::Opcode::GlobalValue,
320                 "callee must already be legalized, we shouldn't see any `global_value` \
321                  instructions when inlining; found {callee_inst:?}: {}",
322                 callee.dfg.display_inst(callee_inst)
323             );
324 
325             // Remap the callee instruction's entities and insert it into the
326             // caller's DFG.
327             let inlined_inst_data = callee.dfg.insts[callee_inst].map(InliningInstRemapper {
328                 allocs: &allocs,
329                 func,
330                 callee,
331                 entity_map: &entity_map,
332             });
333             let inlined_inst = func.dfg.make_inst(inlined_inst_data);
334             func.layout.append_inst(inlined_inst, inlined_block);
335 
336             let opcode = callee.dfg.insts[callee_inst].opcode();
337             if opcode.is_return() {
338                 // Instructions that return do not define any values, so we
339                 // don't need to worry about that, but we do need to fix them up
340                 // so that they return by jumping to our control-flow join
341                 // block, rather than returning from the caller.
342                 if let Some(return_block) = return_block {
343                     fixup_inst_that_returns(
344                         allocs,
345                         func,
346                         callee,
347                         &entity_map,
348                         call_opcode,
349                         inlined_inst,
350                         callee_inst,
351                         return_block,
352                         call_stack_map.as_ref().map(|es| &**es),
353                     );
354                 } else {
355                     // If we are inlining a callee that was invoked via
356                     // `return_call`, we leave inlined return instructions
357                     // as-is: there is no logical caller frame on the stack to
358                     // continue to.
359                     debug_assert_eq!(call_opcode, ir::Opcode::ReturnCall);
360                 }
361             } else {
362                 // Make the instruction's result values.
363                 let ctrl_typevar = callee.dfg.ctrl_typevar(callee_inst);
364                 func.dfg.make_inst_results(inlined_inst, ctrl_typevar);
365 
366                 // Update the value map for this instruction's defs.
367                 let callee_results = callee.dfg.inst_results(callee_inst);
368                 let inlined_results = func.dfg.inst_results(inlined_inst);
369                 debug_assert_eq!(callee_results.len(), inlined_results.len());
370                 for (callee_val, inlined_val) in callee_results.iter().zip(inlined_results) {
371                     allocs.set_inlined_value(callee, *callee_val, *inlined_val);
372                 }
373 
374                 if opcode.is_call() {
375                     append_stack_map_entries(
376                         func,
377                         callee,
378                         &entity_map,
379                         call_stack_map.as_deref(),
380                         inlined_inst,
381                         callee_inst,
382                     );
383 
384                     // When we are inlining a `try_call` call site, we need to merge
385                     // the call site's exception table into the inlined calls'
386                     // exception tables. This can involve rewriting regular `call`s
387                     // into `try_call`s, which requires mutating the CFG because
388                     // `try_call` is a block terminator. However, we can't mutate
389                     // the CFG in the middle of this traversal because we rely on
390                     // the existence of a one-to-one mapping between the callee
391                     // layout and the inlined layout. Instead, we record the set of
392                     // inlined call instructions that will need fixing up, and
393                     // perform that possibly-CFG-mutating exception table merging in
394                     // a follow up pass, when we no longer rely on that one-to-one
395                     // layout mapping.
396                     debug_assert_eq!(
397                         call_opcode == ir::Opcode::TryCall,
398                         call_exception_table.is_some()
399                     );
400                     if call_opcode == ir::Opcode::TryCall {
401                         allocs
402                             .calls_needing_exception_table_fixup
403                             .push(inlined_inst);
404                     }
405                 }
406             }
407 
408             trace!(
409                 "  --> inserted inlined instruction {inlined_inst:?}: {}",
410                 func.dfg.display_inst(inlined_inst)
411             );
412 
413             next_callee_inst = callee.layout.next_inst(callee_inst);
414         }
415     }
416 
417     // We copied *all* callee blocks into the caller's layout, but only copied
418     // the callee instructions in *reachable* callee blocks into the caller's
419     // associated blocks. Therefore, any *unreachable* blocks are empty in the
420     // caller, which is invalid CLIF because all blocks must end in a
421     // terminator, so do a quick pass over the inlined blocks and remove any
422     // empty blocks from the caller's layout.
423     for block in entity_map.iter_inlined_blocks(func) {
424         if func.layout.first_inst(block).is_none() {
425             func.layout.remove_block(block);
426         }
427     }
428 
429     // Final step: fixup the exception tables of any inlined calls when we are
430     // inlining a `try_call` site.
431     //
432     // Subtly, this requires rewriting non-catching `call[_indirect]`
433     // instructions into `try_call[_indirect]` instructions so that exceptions
434     // that unwound through the original callee frame and were caught by the
435     // caller's `try_call` do not unwind past this inlined frame. And turning a
436     // `call` into a `try_call` mutates the CFG, breaking our one-to-one mapping
437     // between callee blocks and inlined blocks, so we delay these fixups to
438     // this final step, when we no longer rely on that mapping.
439     debug_assert!(
440         allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some()
441     );
442     debug_assert_eq!(
443         call_opcode == ir::Opcode::TryCall,
444         call_exception_table.is_some()
445     );
446     if let Some(call_exception_table) = call_exception_table {
447         fixup_inlined_call_exception_tables(allocs, func, call_exception_table);
448     }
449 }
450 
451 /// Append stack map entries from the caller and callee to the given inlined
452 /// instruction.
453 fn append_stack_map_entries(
454     func: &mut ir::Function,
455     callee: &ir::Function,
456     entity_map: &EntityMap,
457     call_stack_map: Option<&[ir::UserStackMapEntry]>,
458     inlined_inst: ir::Inst,
459     callee_inst: ir::Inst,
460 ) {
461     // Add the caller's stack map to this call. These entries
462     // already refer to caller entities and do not need further
463     // translation.
464     func.dfg.append_user_stack_map_entries(
465         inlined_inst,
466         call_stack_map
467             .iter()
468             .flat_map(|entries| entries.iter().cloned()),
469     );
470 
471     // Append the callee's stack map to this call. These entries
472     // refer to callee entities and therefore do require
473     // translation into the caller's index space.
474     func.dfg.append_user_stack_map_entries(
475         inlined_inst,
476         callee
477             .dfg
478             .user_stack_map_entries(callee_inst)
479             .iter()
480             .flat_map(|entries| entries.iter())
481             .map(|entry| ir::UserStackMapEntry {
482                 ty: entry.ty,
483                 slot: entity_map.inlined_stack_slot(entry.slot),
484                 offset: entry.offset,
485             }),
486     );
487 }
488 
489 /// Create or update the exception tables for any inlined call instructions:
490 /// when inlining at a `try_call` site, we must forward our exceptional edges
491 /// into each inlined call instruction.
492 fn fixup_inlined_call_exception_tables(
493     allocs: &mut InliningAllocs,
494     func: &mut ir::Function,
495     call_exception_table: ir::ExceptionTable,
496 ) {
497     // Split a block at a `call[_indirect]` instruction, detach the
498     // instruction's results, and alias them to the new block's parameters.
499     let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block {
500         debug_assert!(func.dfg.insts[inst].opcode().is_call());
501         debug_assert!(!func.dfg.insts[inst].opcode().is_terminator());
502 
503         // Split the block.
504         let next_inst = func
505             .layout
506             .next_inst(inst)
507             .expect("inst is not a terminator, should have a successor");
508         let new_block = func.dfg.blocks.add();
509         func.layout.split_block(new_block, next_inst);
510 
511         // `try_call[_indirect]` instructions do not define values themselves;
512         // the normal-return block has parameters for the results. So remove
513         // this instruction's results, create an associated block parameter for
514         // each of them, and alias them to the new block parameter.
515         let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied());
516         func.dfg.detach_inst_results(inst);
517         for old_result in old_results {
518             let ty = func.dfg.value_type(old_result);
519             let new_block_param = func.dfg.append_block_param(new_block, ty);
520             func.dfg.change_to_alias(old_result, new_block_param);
521         }
522 
523         new_block
524     };
525 
526     // Clone the caller's exception table, updating it for use in the current
527     // `call[_indirect]` instruction as it becomes a `try_call[_indirect]`.
528     let clone_exception_table_for_this_call = |func: &mut ir::Function,
529                                                signature: ir::SigRef,
530                                                new_block: ir::Block|
531      -> ir::ExceptionTable {
532         let mut exception = func.stencil.dfg.exception_tables[call_exception_table]
533             .deep_clone(&mut func.stencil.dfg.value_lists);
534 
535         *exception.signature_mut() = signature;
536 
537         let returns_len = func.dfg.signatures[signature].returns.len();
538         let returns_len = u32::try_from(returns_len).unwrap();
539 
540         *exception.normal_return_mut() = ir::BlockCall::new(
541             new_block,
542             (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)),
543             &mut func.dfg.value_lists,
544         );
545 
546         func.dfg.exception_tables.push(exception)
547     };
548 
549     for inst in allocs.calls_needing_exception_table_fixup.drain(..) {
550         debug_assert!(func.dfg.insts[inst].opcode().is_call());
551         debug_assert!(!func.dfg.insts[inst].opcode().is_return());
552         match func.dfg.insts[inst] {
553             //     current_block:
554             //         preds...
555             //         rets... = call f(args...)
556             //         succs...
557             //
558             // becomes
559             //
560             //     current_block:
561             //         preds...
562             //         try_call f(args...), new_block(rets...), [call_exception_table...]
563             //     new_block(rets...):
564             //         succs...
565             ir::InstructionData::Call {
566                 opcode: ir::Opcode::Call,
567                 args,
568                 func_ref,
569             } => {
570                 let new_block = split_block_for_new_try_call(func, inst);
571                 let signature = func.dfg.ext_funcs[func_ref].signature;
572                 let exception = clone_exception_table_for_this_call(func, signature, new_block);
573                 func.dfg.insts[inst] = ir::InstructionData::TryCall {
574                     opcode: ir::Opcode::TryCall,
575                     args,
576                     func_ref,
577                     exception,
578                 };
579             }
580 
581             //     current_block:
582             //         preds...
583             //         rets... = call_indirect sig, val(args...)
584             //         succs...
585             //
586             // becomes
587             //
588             //     current_block:
589             //         preds...
590             //         try_call_indirect sig, val(args...), new_block(rets...), [call_exception_table...]
591             //     new_block(rets...):
592             //         succs...
593             ir::InstructionData::CallIndirect {
594                 opcode: ir::Opcode::CallIndirect,
595                 args,
596                 sig_ref,
597             } => {
598                 let new_block = split_block_for_new_try_call(func, inst);
599                 let exception = clone_exception_table_for_this_call(func, sig_ref, new_block);
600                 func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect {
601                     opcode: ir::Opcode::TryCallIndirect,
602                     args,
603                     exception,
604                 };
605             }
606 
607             // For `try_call[_indirect]` instructions, we just need to merge the
608             // exception tables.
609             ir::InstructionData::TryCall {
610                 opcode: ir::Opcode::TryCall,
611                 exception,
612                 ..
613             }
614             | ir::InstructionData::TryCallIndirect {
615                 opcode: ir::Opcode::TryCallIndirect,
616                 exception,
617                 ..
618             } => {
619                 // Gather the set of tags that this instruction's exception
620                 // table already has entries for.
621                 allocs.existing_exception_tags.clear();
622                 allocs.existing_exception_tags.extend(
623                     func.dfg.exception_tables[exception]
624                         .catches()
625                         .map(|(c, _)| c),
626                 );
627 
628                 // Add only the catch edges from our original `try_call`'s
629                 // exception table that are not already handled by this
630                 // instruction.
631                 for i in 0..func.dfg.exception_tables[call_exception_table].len_catches() {
632                     let exception_tables = &mut func.stencil.dfg.exception_tables;
633                     let value_lists = &mut func.stencil.dfg.value_lists;
634 
635                     let (tag, block_call) =
636                         exception_tables[call_exception_table].get_catch(i).unwrap();
637                     if allocs.existing_exception_tags.contains(&tag) {
638                         continue;
639                     }
640 
641                     let block_call = block_call.deep_clone(value_lists);
642                     exception_tables[exception].push_catch(tag, block_call);
643                 }
644             }
645 
646             otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"),
647         }
648     }
649 }
650 
651 /// After having created an inlined version of a callee instruction that returns
652 /// in the caller, we need to fix it up so that it doesn't actually return
653 /// (since we are already in the caller's frame) and instead just jumps to the
654 /// control-flow join point.
655 fn fixup_inst_that_returns(
656     allocs: &mut InliningAllocs,
657     func: &mut ir::Function,
658     callee: &ir::Function,
659     entity_map: &EntityMap,
660     call_opcode: ir::Opcode,
661     inlined_inst: ir::Inst,
662     callee_inst: ir::Inst,
663     return_block: ir::Block,
664     call_stack_map: Option<&[ir::UserStackMapEntry]>,
665 ) {
666     debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return());
667     match func.dfg.insts[inlined_inst] {
668         //     return rets...
669         //
670         // becomes
671         //
672         //     jump return_block(rets...)
673         ir::InstructionData::MultiAry {
674             opcode: ir::Opcode::Return,
675             args,
676         } => {
677             let rets = SmallBlockArgVec::from_iter(
678                 args.as_slice(&func.dfg.value_lists)
679                     .iter()
680                     .copied()
681                     .map(|v| v.into()),
682             );
683             func.dfg.replace(inlined_inst).jump(return_block, &rets);
684         }
685 
686         //     return_call f(args...)
687         //
688         // becomes
689         //
690         //     rets... = call f(args...)
691         //     jump return_block(rets...)
692         ir::InstructionData::Call {
693             opcode: ir::Opcode::ReturnCall,
694             args,
695             func_ref,
696         } => {
697             func.dfg.insts[inlined_inst] = ir::InstructionData::Call {
698                 opcode: ir::Opcode::Call,
699                 args,
700                 func_ref,
701             };
702             func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
703 
704             append_stack_map_entries(
705                 func,
706                 callee,
707                 &entity_map,
708                 call_stack_map,
709                 inlined_inst,
710                 callee_inst,
711             );
712 
713             let rets = SmallBlockArgVec::from_iter(
714                 func.dfg
715                     .inst_results(inlined_inst)
716                     .iter()
717                     .copied()
718                     .map(|v| v.into()),
719             );
720             let mut cursor = FuncCursor::new(func);
721             cursor.goto_after_inst(inlined_inst);
722             cursor.ins().jump(return_block, &rets);
723 
724             if call_opcode == ir::Opcode::TryCall {
725                 allocs
726                     .calls_needing_exception_table_fixup
727                     .push(inlined_inst);
728             }
729         }
730 
731         //     return_call_indirect val(args...)
732         //
733         // becomes
734         //
735         //     rets... = call_indirect val(args...)
736         //     jump return_block(rets...)
737         ir::InstructionData::CallIndirect {
738             opcode: ir::Opcode::ReturnCallIndirect,
739             args,
740             sig_ref,
741         } => {
742             func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect {
743                 opcode: ir::Opcode::CallIndirect,
744                 args,
745                 sig_ref,
746             };
747             func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
748 
749             append_stack_map_entries(
750                 func,
751                 callee,
752                 &entity_map,
753                 call_stack_map,
754                 inlined_inst,
755                 callee_inst,
756             );
757 
758             let rets = SmallBlockArgVec::from_iter(
759                 func.dfg
760                     .inst_results(inlined_inst)
761                     .iter()
762                     .copied()
763                     .map(|v| v.into()),
764             );
765             let mut cursor = FuncCursor::new(func);
766             cursor.goto_after_inst(inlined_inst);
767             cursor.ins().jump(return_block, &rets);
768 
769             if call_opcode == ir::Opcode::TryCall {
770                 allocs
771                     .calls_needing_exception_table_fixup
772                     .push(inlined_inst);
773             }
774         }
775 
776         inst_data => unreachable!(
777             "should have handled all `is_return() == true` instructions above; \
778              got {inst_data:?}"
779         ),
780     }
781 }
782 
783 /// An `InstructionMapper` implementation that remaps a callee instruction's
784 /// entity references to their new indices in the caller function.
785 struct InliningInstRemapper<'a> {
786     allocs: &'a InliningAllocs,
787     func: &'a mut ir::Function,
788     callee: &'a ir::Function,
789     entity_map: &'a EntityMap,
790 }
791 
792 impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> {
793     fn map_value(&mut self, value: ir::Value) -> ir::Value {
794         self.allocs.get_inlined_value(self.callee, value).expect(
795             "defs come before uses; we should have already inlined all values \
796              used by an instruction",
797         )
798     }
799 
800     fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList {
801         let mut inlined_list = ir::ValueList::new();
802         for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) {
803             let inlined_val = self.map_value(*callee_val);
804             inlined_list.push(inlined_val, &mut self.func.dfg.value_lists);
805         }
806         inlined_list
807     }
808 
809     fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
810         self.entity_map.inlined_global_value(global_value)
811     }
812 
813     fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
814         let inlined_default =
815             self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block());
816         let inlined_table = self.callee.dfg.jump_tables[jump_table]
817             .as_slice()
818             .iter()
819             .map(|callee_block_call| self.map_block_call(*callee_block_call))
820             .collect::<SmallBlockCallVec>();
821         self.func
822             .dfg
823             .jump_tables
824             .push(ir::JumpTableData::new(inlined_default, &inlined_table))
825     }
826 
827     fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable {
828         let exception_table = &self.callee.dfg.exception_tables[exception_table];
829         let inlined_sig_ref = self.map_sig_ref(exception_table.signature());
830         let inlined_normal_return = self.map_block_call(*exception_table.normal_return());
831         let inlined_table = exception_table
832             .catches()
833             .map(|(tag, callee_block_call)| (tag, self.map_block_call(*callee_block_call)))
834             .collect::<SmallVec<[_; 8]>>();
835         self.func
836             .dfg
837             .exception_tables
838             .push(ir::ExceptionTableData::new(
839                 inlined_sig_ref,
840                 inlined_normal_return,
841                 inlined_table,
842             ))
843     }
844 
845     fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall {
846         let callee_block = block_call.block(&self.callee.dfg.value_lists);
847         let inlined_block = self.entity_map.inlined_block(callee_block);
848         let args = block_call
849             .args(&self.callee.dfg.value_lists)
850             .map(|arg| match arg {
851                 ir::BlockArg::Value(value) => self.map_value(value).into(),
852                 ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg,
853             })
854             .collect::<SmallBlockArgVec>();
855         ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists)
856     }
857 
858     fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef {
859         self.entity_map.inlined_func_ref(func_ref)
860     }
861 
862     fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef {
863         self.entity_map.inlined_sig_ref(sig_ref)
864     }
865 
866     fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot {
867         self.entity_map.inlined_stack_slot(stack_slot)
868     }
869 
870     fn map_dynamic_stack_slot(
871         &mut self,
872         dynamic_stack_slot: ir::DynamicStackSlot,
873     ) -> ir::DynamicStackSlot {
874         self.entity_map
875             .inlined_dynamic_stack_slot(dynamic_stack_slot)
876     }
877 
878     fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
879         self.allocs
880             .constants
881             .get(constant)
882             .and_then(|o| o.expand())
883             .expect("should have inlined all callee constants")
884     }
885 
886     fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
887         self.entity_map.inlined_immediate(immediate)
888     }
889 }
890 
891 /// Inline the callee's layout into the caller's layout.
892 fn inline_block_layout(
893     func: &mut ir::Function,
894     call_block: ir::Block,
895     callee: &ir::Function,
896     entity_map: &EntityMap,
897 ) {
898     // Iterate over callee blocks in layout order, inserting their associated
899     // inlined block into the caller's layout.
900     let mut prev_inlined_block = call_block;
901     let mut next_callee_block = callee.layout.entry_block();
902     while let Some(callee_block) = next_callee_block {
903         let inlined_block = entity_map.inlined_block(callee_block);
904         func.layout
905             .insert_block_after(inlined_block, prev_inlined_block);
906 
907         prev_inlined_block = inlined_block;
908         next_callee_block = callee.layout.next_block(callee_block);
909     }
910 }
911 
912 /// Split the call instruction's block just after the call instruction to create
913 /// the point where control-flow joins after the inlined callee "returns".
914 ///
915 /// Note that tail calls do not return to the caller and therefore do not have a
916 /// control-flow join point.
917 fn split_off_return_block(
918     func: &mut ir::Function,
919     call_inst: ir::Inst,
920     opcode: ir::Opcode,
921     callee: &ir::Function,
922 ) -> Option<ir::Block> {
923     // When the `call_inst` is not a block terminator, we need to split the
924     // block.
925     let return_block = func.layout.next_inst(call_inst).map(|next_inst| {
926         let return_block = func.dfg.blocks.add();
927         func.layout.split_block(return_block, next_inst);
928 
929         // Add block parameters for each return value and alias the call
930         // instruction's results to them.
931         let old_results =
932             SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied());
933         debug_assert_eq!(old_results.len(), callee.signature.returns.len());
934         func.dfg.detach_inst_results(call_inst);
935         for (abi, old_val) in callee.signature.returns.iter().zip(old_results) {
936             debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val));
937             let ret_param = func.dfg.append_block_param(return_block, abi.value_type);
938             func.dfg.change_to_alias(old_val, ret_param);
939         }
940 
941         return_block
942     });
943 
944     // When the `call_inst` is a block terminator, then it is either a
945     // `return_call` or a `try_call`:
946     //
947     // * For `return_call`s, we don't have a control-flow join point, because
948     //   the caller permanently transfers control to the callee.
949     //
950     // * For `try_call`s, we probably already have a block for the control-flow
951     //   join point, but it isn't guaranteed: the `try_call` might ignore the
952     //   call's returns and not forward them to the normal-return block or it
953     //   might also pass additional arguments. We can only reuse the existing
954     //   normal-return block when the `try_call` forwards exactly our callee's
955     //   returns to that block (and therefore that block's parameter types also
956     //   exactly match the callee's return types). Otherwise, we must create a new
957     //   return block that forwards to the existing normal-return
958     //   block. (Elsewhere, at the end of inlining, we will also update any inlined
959     //   calls to forward any raised exceptions to the caller's exception table,
960     //   as necessary.)
961     //
962     //   Finally, note that reusing the normal-return's target block is just an
963     //   optimization to emit a simpler CFG when we can, and is not
964     //   fundamentally required for correctness. We could always insert a
965     //   temporary block as our control-flow join point that then forwards to
966     //   the normal-return's target block. However, at the time of writing,
967     //   Cranelift doesn't currently do any jump-threading or branch
968     //   simplification in the mid-end, and removing unnecessary blocks in this
969     //   way can help some subsequent mid-end optimizations. If, in the future,
970     //   we gain support for jump-threading optimizations in the mid-end, we can
971     //   come back and simplify the below code a bit to always generate the
972     //   temporary block, and then rely on the subsequent optimizations to clean
973     //   everything up.
974     debug_assert_eq!(
975         return_block.is_none(),
976         opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall,
977     );
978     return_block.or_else(|| match func.dfg.insts[call_inst] {
979         ir::InstructionData::TryCall {
980             opcode: ir::Opcode::TryCall,
981             args: _,
982             func_ref: _,
983             exception,
984         } => {
985             let normal_return = func.dfg.exception_tables[exception].normal_return();
986             let normal_return_block = normal_return.block(&func.dfg.value_lists);
987 
988             // Check to see if we can reuse the existing normal-return block.
989             {
990                 let normal_return_args = normal_return.args(&func.dfg.value_lists);
991                 if normal_return_args.len() == callee.signature.returns.len()
992                     && normal_return_args.enumerate().all(|(i, arg)| {
993                         let i = u32::try_from(i).unwrap();
994                         arg == ir::BlockArg::TryCallRet(i)
995                     })
996                 {
997                     return Some(normal_return_block);
998                 }
999             }
1000 
1001             // Okay, we cannot reuse the normal-return block. Create a new block
1002             // that has the expected block parameter types and have it jump to
1003             // the normal-return block.
1004             let return_block = func.dfg.blocks.add();
1005             func.layout.insert_block(return_block, normal_return_block);
1006 
1007             let return_block_params = callee
1008                 .signature
1009                 .returns
1010                 .iter()
1011                 .map(|abi| func.dfg.append_block_param(return_block, abi.value_type))
1012                 .collect::<SmallValueVec>();
1013 
1014             let normal_return_args = func.dfg.exception_tables[exception]
1015                 .normal_return()
1016                 .args(&func.dfg.value_lists)
1017                 .collect::<SmallBlockArgVec>();
1018             let jump_args = normal_return_args
1019                 .into_iter()
1020                 .map(|arg| match arg {
1021                     ir::BlockArg::Value(value) => ir::BlockArg::Value(value),
1022                     ir::BlockArg::TryCallRet(i) => {
1023                         let i = usize::try_from(i).unwrap();
1024                         ir::BlockArg::Value(return_block_params[i])
1025                     }
1026                     ir::BlockArg::TryCallExn(_) => {
1027                         unreachable!("normal-return edges cannot use exceptional results")
1028                     }
1029                 })
1030                 .collect::<SmallBlockArgVec>();
1031 
1032             let mut cursor = FuncCursor::new(func);
1033             cursor.goto_first_insertion_point(return_block);
1034             cursor.ins().jump(normal_return_block, &jump_args);
1035 
1036             Some(return_block)
1037         }
1038         _ => None,
1039     })
1040 }
1041 
1042 /// Replace the caller's call instruction with a jump to the caller's inlined
1043 /// copy of the callee's entry block.
1044 ///
1045 /// Also associates the callee's parameters with the caller's arguments in our
1046 /// value map.
1047 ///
1048 /// Returns the caller's stack map entries, if any.
1049 fn replace_call_with_jump(
1050     allocs: &mut InliningAllocs,
1051     func: &mut ir::Function,
1052     call_inst: ir::Inst,
1053     callee: &ir::Function,
1054     entity_map: &EntityMap,
1055 ) -> Option<ir::UserStackMapEntryVec> {
1056     trace!("Replacing `call` with `jump`");
1057     trace!(
1058         "  --> call instruction: {call_inst:?}: {}",
1059         func.dfg.display_inst(call_inst)
1060     );
1061 
1062     let callee_entry_block = callee
1063         .layout
1064         .entry_block()
1065         .expect("callee function should have an entry block");
1066     let callee_param_values = callee.dfg.block_params(callee_entry_block);
1067     let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied());
1068     debug_assert_eq!(callee_param_values.len(), caller_arg_values.len());
1069     debug_assert_eq!(callee_param_values.len(), callee.signature.params.len());
1070     for (abi, (callee_param_value, caller_arg_value)) in callee
1071         .signature
1072         .params
1073         .iter()
1074         .zip(callee_param_values.into_iter().zip(caller_arg_values))
1075     {
1076         debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value));
1077         debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value));
1078         allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value);
1079     }
1080 
1081     // Replace the caller's call instruction with a jump to the caller's inlined
1082     // copy of the callee's entry block.
1083     //
1084     // Note that the call block dominates the inlined entry block (and also all
1085     // other inlined blocks) so we can reference the arguments directly, and do
1086     // not need to add block parameters to the inlined entry block.
1087     let inlined_entry_block = entity_map.inlined_block(callee_entry_block);
1088     func.dfg.replace(call_inst).jump(inlined_entry_block, &[]);
1089     trace!(
1090         "  --> replaced with jump instruction: {call_inst:?}: {}",
1091         func.dfg.display_inst(call_inst)
1092     );
1093 
1094     let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst);
1095     stack_map_entries
1096 }
1097 
1098 /// Keeps track of mapping callee entities to their associated inlined caller
1099 /// entities.
1100 #[derive(Default)]
1101 struct EntityMap {
1102     // Rather than doing an implicit, demand-based, DCE'ing translation of
1103     // entities, which would require maps from each callee entity to its
1104     // associated caller entity, we copy all entities into the caller, remember
1105     // each entity's initial offset, and then mapping from the callee to the
1106     // inlined caller entity is just adding that initial offset to the callee's
1107     // index. This should be both faster and simpler than the alternative. Most
1108     // of these sets are relatively small, and they rarely have too much dead
1109     // code in practice, so this is a good trade off.
1110     //
1111     // Note that there are a few kinds of entities that are excluded from the
1112     // `EntityMap`, and for which we do actually take the demand-based approach:
1113     // values and value lists being the notable ones.
1114     block_offset: Option<u32>,
1115     global_value_offset: Option<u32>,
1116     sig_ref_offset: Option<u32>,
1117     func_ref_offset: Option<u32>,
1118     stack_slot_offset: Option<u32>,
1119     dynamic_type_offset: Option<u32>,
1120     dynamic_stack_slot_offset: Option<u32>,
1121     immediate_offset: Option<u32>,
1122 }
1123 
1124 impl EntityMap {
1125     fn inlined_block(&self, callee_block: ir::Block) -> ir::Block {
1126         let offset = self
1127             .block_offset
1128             .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`");
1129         ir::Block::from_u32(offset + callee_block.as_u32())
1130     }
1131 
1132     fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> {
1133         let start = self.block_offset.expect(
1134             "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`",
1135         );
1136 
1137         let end = func.dfg.blocks.len();
1138         let end = u32::try_from(end).unwrap();
1139 
1140         (start..end).map(|i| ir::Block::from_u32(i))
1141     }
1142 
1143     fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue {
1144         let offset = self
1145             .global_value_offset
1146             .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`");
1147         ir::GlobalValue::from_u32(offset + callee_global_value.as_u32())
1148     }
1149 
1150     fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef {
1151         let offset = self.sig_ref_offset.expect(
1152             "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`",
1153         );
1154         ir::SigRef::from_u32(offset + callee_sig_ref.as_u32())
1155     }
1156 
1157     fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef {
1158         let offset = self.func_ref_offset.expect(
1159             "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`",
1160         );
1161         ir::FuncRef::from_u32(offset + callee_func_ref.as_u32())
1162     }
1163 
1164     fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot {
1165         let offset = self.stack_slot_offset.expect(
1166             "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`",
1167         );
1168         ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32())
1169     }
1170 
1171     fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType {
1172         let offset = self.dynamic_type_offset.expect(
1173             "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`",
1174         );
1175         ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32())
1176     }
1177 
1178     fn inlined_dynamic_stack_slot(
1179         &self,
1180         callee_dynamic_stack_slot: ir::DynamicStackSlot,
1181     ) -> ir::DynamicStackSlot {
1182         let offset = self.dynamic_stack_slot_offset.expect(
1183             "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`",
1184         );
1185         ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32())
1186     }
1187 
1188     fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate {
1189         let offset = self.immediate_offset.expect(
1190             "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`",
1191         );
1192         ir::Immediate::from_u32(offset + callee_immediate.as_u32())
1193     }
1194 }
1195 
1196 /// Translate all of the callee's various entities into the caller, producing an
1197 /// `EntityMap` that can be used to translate callee entity references into
1198 /// inlined caller entity references.
1199 fn create_entities(
1200     allocs: &mut InliningAllocs,
1201     func: &mut ir::Function,
1202     callee: &ir::Function,
1203 ) -> EntityMap {
1204     let mut entity_map = EntityMap::default();
1205 
1206     entity_map.block_offset = Some(create_blocks(allocs, func, callee));
1207     entity_map.global_value_offset = Some(create_global_values(func, callee));
1208     entity_map.sig_ref_offset = Some(create_sig_refs(func, callee));
1209     entity_map.func_ref_offset = Some(create_func_refs(func, callee, &entity_map));
1210     entity_map.stack_slot_offset = Some(create_stack_slots(func, callee));
1211     entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map));
1212     entity_map.dynamic_stack_slot_offset =
1213         Some(create_dynamic_stack_slots(func, callee, &entity_map));
1214     entity_map.immediate_offset = Some(create_immediates(func, callee));
1215 
1216     // `ir::ConstantData` is deduplicated, so we cannot use our offset scheme
1217     // for `ir::Constant`s. Nonetheless, we still insert them into the caller
1218     // now, at the same time as the rest of our entities.
1219     create_constants(allocs, func, callee);
1220 
1221     entity_map
1222 }
1223 
1224 /// Create inlined blocks in the caller for every block in the callee.
1225 fn create_blocks(
1226     allocs: &mut InliningAllocs,
1227     func: &mut ir::Function,
1228     callee: &ir::Function,
1229 ) -> u32 {
1230     let offset = func.dfg.blocks.len();
1231     let offset = u32::try_from(offset).unwrap();
1232 
1233     func.dfg.blocks.reserve(callee.dfg.blocks.len());
1234     for callee_block in callee.dfg.blocks.iter() {
1235         let caller_block = func.dfg.blocks.add();
1236         trace!("Callee {callee_block:?} = inlined {caller_block:?}");
1237 
1238         if callee.layout.is_cold(callee_block) {
1239             func.layout.set_cold(caller_block);
1240         }
1241 
1242         // Note: the entry block does not need parameters because the only
1243         // predecessor is the call block and we associate the callee's
1244         // parameters with the caller's arguments directly.
1245         if callee.layout.entry_block() != Some(callee_block) {
1246             for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) {
1247                 let ty = callee.dfg.value_type(*callee_param);
1248                 let caller_param = func.dfg.append_block_param(caller_block, ty);
1249 
1250                 allocs.set_inlined_value(callee, *callee_param, caller_param);
1251             }
1252         }
1253     }
1254 
1255     offset
1256 }
1257 
1258 /// Copy and translate global values from the callee into the caller.
1259 fn create_global_values(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1260     let gv_offset = func.global_values.len();
1261     let gv_offset = u32::try_from(gv_offset).unwrap();
1262 
1263     func.global_values.reserve(callee.global_values.len());
1264     for gv in callee.global_values.values() {
1265         func.global_values.push(match gv {
1266             // These kinds of global values reference other global values, so we
1267             // need to fixup that reference.
1268             ir::GlobalValueData::Load {
1269                 base,
1270                 offset,
1271                 global_type,
1272                 flags,
1273             } => ir::GlobalValueData::Load {
1274                 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1275                 offset: *offset,
1276                 global_type: *global_type,
1277                 flags: *flags,
1278             },
1279             ir::GlobalValueData::IAddImm {
1280                 base,
1281                 offset,
1282                 global_type,
1283             } => ir::GlobalValueData::IAddImm {
1284                 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1285                 offset: *offset,
1286                 global_type: *global_type,
1287             },
1288 
1289             // These kinds of global values do not reference other global
1290             // values, so we can just clone them.
1291             ir::GlobalValueData::VMContext
1292             | ir::GlobalValueData::Symbol { .. }
1293             | ir::GlobalValueData::DynScaleTargetConst { .. } => gv.clone(),
1294         });
1295     }
1296 
1297     gv_offset
1298 }
1299 
1300 /// Copy `ir::SigRef`s from the callee into the caller.
1301 fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1302     let offset = func.dfg.signatures.len();
1303     let offset = u32::try_from(offset).unwrap();
1304 
1305     func.dfg.signatures.reserve(callee.dfg.signatures.len());
1306     for sig in callee.dfg.signatures.values() {
1307         func.dfg.signatures.push(sig.clone());
1308     }
1309 
1310     offset
1311 }
1312 
1313 /// Translate `ir::FuncRef`s from the callee into the caller.
1314 fn create_func_refs(func: &mut ir::Function, callee: &ir::Function, entity_map: &EntityMap) -> u32 {
1315     let offset = func.dfg.ext_funcs.len();
1316     let offset = u32::try_from(offset).unwrap();
1317 
1318     func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len());
1319     for ir::ExtFuncData {
1320         name,
1321         signature,
1322         colocated,
1323     } in callee.dfg.ext_funcs.values()
1324     {
1325         func.dfg.ext_funcs.push(ir::ExtFuncData {
1326             name: name.clone(),
1327             signature: entity_map.inlined_sig_ref(*signature),
1328             colocated: *colocated,
1329         });
1330     }
1331 
1332     offset
1333 }
1334 
1335 /// Copy stack slots from the callee into the caller.
1336 fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1337     let offset = func.sized_stack_slots.len();
1338     let offset = u32::try_from(offset).unwrap();
1339 
1340     func.sized_stack_slots
1341         .reserve(callee.sized_stack_slots.len());
1342     for slot in callee.sized_stack_slots.values() {
1343         func.sized_stack_slots.push(slot.clone());
1344     }
1345 
1346     offset
1347 }
1348 
1349 /// Copy dynamic types from the callee into the caller.
1350 fn create_dynamic_types(
1351     func: &mut ir::Function,
1352     callee: &ir::Function,
1353     entity_map: &EntityMap,
1354 ) -> u32 {
1355     let offset = func.dynamic_stack_slots.len();
1356     let offset = u32::try_from(offset).unwrap();
1357 
1358     func.dfg
1359         .dynamic_types
1360         .reserve(callee.dfg.dynamic_types.len());
1361     for ir::DynamicTypeData {
1362         base_vector_ty,
1363         dynamic_scale,
1364     } in callee.dfg.dynamic_types.values()
1365     {
1366         func.dfg.dynamic_types.push(ir::DynamicTypeData {
1367             base_vector_ty: *base_vector_ty,
1368             dynamic_scale: entity_map.inlined_global_value(*dynamic_scale),
1369         });
1370     }
1371 
1372     offset
1373 }
1374 
1375 /// Copy dynamic stack slots from the callee into the caller.
1376 fn create_dynamic_stack_slots(
1377     func: &mut ir::Function,
1378     callee: &ir::Function,
1379     entity_map: &EntityMap,
1380 ) -> u32 {
1381     let offset = func.dynamic_stack_slots.len();
1382     let offset = u32::try_from(offset).unwrap();
1383 
1384     func.dynamic_stack_slots
1385         .reserve(callee.dynamic_stack_slots.len());
1386     for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() {
1387         func.dynamic_stack_slots.push(ir::DynamicStackSlotData {
1388             kind: *kind,
1389             dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty),
1390         });
1391     }
1392 
1393     offset
1394 }
1395 
1396 /// Copy immediates from the callee into the caller.
1397 fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1398     let offset = func.dfg.immediates.len();
1399     let offset = u32::try_from(offset).unwrap();
1400 
1401     func.dfg.immediates.reserve(callee.dfg.immediates.len());
1402     for imm in callee.dfg.immediates.values() {
1403         func.dfg.immediates.push(imm.clone());
1404     }
1405 
1406     offset
1407 }
1408 
1409 /// Copy constants from the callee into the caller.
1410 fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) {
1411     for (callee_constant, data) in callee.dfg.constants.iter() {
1412         let inlined_constant = func.dfg.constants.insert(data.clone());
1413         allocs.constants[*callee_constant] = Some(inlined_constant).into();
1414     }
1415 }
1416