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