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