Walk the given function, invoke the `Inline` implementation for each call instruction, and inline the callee when directed to do so. Returns whether any call was inlined.
(
func: &mut ir::Function,
mut inliner: impl Inline,
)
| 110 | /// |
| 111 | /// Returns whether any call was inlined. |
| 112 | pub(crate) fn do_inlining( |
| 113 | func: &mut ir::Function, |
| 114 | mut inliner: impl Inline, |
| 115 | ) -> CodegenResult<bool> { |
| 116 | trace!("function {} before inlining: {}", func.name, func); |
| 117 | |
| 118 | let mut inlined_any = false; |
| 119 | let mut allocs = InliningAllocs::default(); |
| 120 | |
| 121 | let mut cursor = FuncCursor::new(func); |
| 122 | 'block_loop: while let Some(block) = cursor.next_block() { |
| 123 | // Always keep track of our previous cursor position. Assuming that the |
| 124 | // current position is a function call that we will inline, then the |
| 125 | // previous position is just before the inlined callee function. After |
| 126 | // inlining a call, the Cranelift user can decide whether to consider |
| 127 | // any function calls in the inlined callee for further inlining or |
| 128 | // not. When they do, then we back up to this previous cursor position |
| 129 | // so that our traversal will then continue over the inlined body. |
| 130 | let mut prev_pos; |
| 131 | |
| 132 | while let Some(inst) = { |
| 133 | prev_pos = cursor.position(); |
| 134 | cursor.next_inst() |
| 135 | } { |
| 136 | // Make sure that `block` is always `inst`'s block, even with all of |
| 137 | // our cursor-position-updating and block-splitting-during-inlining |
| 138 | // shenanigans below. |
| 139 | debug_assert_eq!(Some(block), cursor.func.layout.inst_block(inst)); |
| 140 | |
| 141 | match cursor.func.dfg.insts[inst] { |
| 142 | ir::InstructionData::Call { func_ref, .. } |
| 143 | if cursor.func.dfg.ext_funcs[func_ref].patchable => |
| 144 | { |
| 145 | // Can't inline patchable calls; they need to |
| 146 | // remain patchable and inlining the whole body is |
| 147 | // decidedly *not* patchable! |
| 148 | } |
| 149 | |
| 150 | ir::InstructionData::Call { |
| 151 | opcode: opcode @ ir::Opcode::Call | opcode @ ir::Opcode::ReturnCall, |
| 152 | args: _, |
| 153 | func_ref, |
| 154 | } => { |
| 155 | trace!( |
| 156 | "considering call site for inlining: {inst}: {}", |
| 157 | cursor.func.dfg.display_inst(inst), |
| 158 | ); |
| 159 | let args = cursor.func.dfg.inst_args(inst); |
| 160 | match inliner.inline(&cursor.func, inst, opcode, func_ref, args) { |
| 161 | InlineCommand::KeepCall => { |
| 162 | trace!(" --> keeping call"); |
| 163 | } |
| 164 | InlineCommand::Inline { |
| 165 | callee, |
| 166 | visit_callee, |
| 167 | } => { |
| 168 | let last_inlined_block = inline_one( |
| 169 | &mut allocs, |
no test coverage detected