1use crate::cursor::{Cursor as _, FuncCursor};
23use crate::ir::{self, DebugTag, ExceptionTableData, ExceptionTableItem, InstBuilder as _};
24use crate::result::CodegenResult;
25use crate::trace;
26use crate::traversals::Dfs;
27use alloc::borrow::Cow;
28use alloc::vec::Vec;
29use cranelift_entity::{SecondaryMap, packed_option::PackedOption};
30use smallvec::SmallVec;
31
32type SmallValueVec = SmallVec<[ir::Value; 8]>;
33type SmallBlockArgVec = SmallVec<[ir::BlockArg; 8]>;
34type SmallBlockCallVec = SmallVec<[ir::BlockCall; 8]>;
35
36pub enum InlineCommand<'a> {
38 KeepCall,
40
41 Inline {
47 callee: Cow<'a, ir::Function>,
49 visit_callee: bool,
52 },
53}
54
55pub trait Inline {
60 fn inline(
83 &mut self,
84 caller: &ir::Function,
85 call_inst: ir::Inst,
86 call_opcode: ir::Opcode,
87 callee: ir::FuncRef,
88 call_args: &[ir::Value],
89 ) -> InlineCommand<'_>;
90}
91
92impl<'a, T> Inline for &'a mut T
93where
94 T: Inline,
95{
96 fn inline(
97 &mut self,
98 caller: &ir::Function,
99 inst: ir::Inst,
100 opcode: ir::Opcode,
101 callee: ir::FuncRef,
102 args: &[ir::Value],
103 ) -> InlineCommand<'_> {
104 (*self).inline(caller, inst, opcode, callee, args)
105 }
106}
107
108pub(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 let mut prev_pos;
131
132 while let Some(inst) = {
133 prev_pos = cursor.position();
134 cursor.next_inst()
135 } {
136 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 }
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,
170 cursor.func,
171 func_ref,
172 block,
173 inst,
174 opcode,
175 &callee,
176 None,
177 )?;
178 inlined_any = true;
179 if visit_callee {
180 cursor.set_position(prev_pos);
181 } else {
182 cursor.goto_bottom(last_inlined_block);
186 continue 'block_loop;
187 }
188 }
189 }
190 }
191 ir::InstructionData::TryCall {
192 opcode: opcode @ ir::Opcode::TryCall,
193 args: _,
194 func_ref,
195 exception,
196 } => {
197 trace!(
198 "considering call site for inlining: {inst}: {}",
199 cursor.func.dfg.display_inst(inst),
200 );
201 let args = cursor.func.dfg.inst_args(inst);
202 match inliner.inline(&cursor.func, inst, opcode, func_ref, args) {
203 InlineCommand::KeepCall => {
204 trace!(" --> keeping call");
205 }
206 InlineCommand::Inline {
207 callee,
208 visit_callee,
209 } => {
210 let last_inlined_block = inline_one(
211 &mut allocs,
212 cursor.func,
213 func_ref,
214 block,
215 inst,
216 opcode,
217 &callee,
218 Some(exception),
219 )?;
220 inlined_any = true;
221 if visit_callee {
222 cursor.set_position(prev_pos);
223 } else {
224 cursor.goto_bottom(last_inlined_block);
228 continue 'block_loop;
229 }
230 }
231 }
232 }
233 ir::InstructionData::CallIndirect { .. }
234 | ir::InstructionData::TryCallIndirect { .. } => {
235 }
238 _ => {
239 debug_assert!(
240 !cursor.func.dfg.insts[inst].opcode().is_call(),
241 "should have matched all call instructions, but found: {inst}: {}",
242 cursor.func.dfg.display_inst(inst),
243 );
244 }
245 }
246 }
247 }
248
249 if inlined_any {
250 trace!("function {} after inlining: {}", func.name, func);
251 } else {
252 trace!("function {} did not have any callees inlined", func.name);
253 }
254
255 Ok(inlined_any)
256}
257
258#[derive(Default)]
259struct InliningAllocs {
260 values: SecondaryMap<ir::Value, PackedOption<ir::Value>>,
262
263 constants: SecondaryMap<ir::Constant, PackedOption<ir::Constant>>,
268
269 user_external_name_refs:
274 SecondaryMap<ir::UserExternalNameRef, PackedOption<ir::UserExternalNameRef>>,
275
276 calls_needing_exception_table_fixup: Vec<ir::Inst>,
292}
293
294impl InliningAllocs {
295 fn reset(&mut self, callee: &ir::Function) {
296 let InliningAllocs {
297 values,
298 constants,
299 user_external_name_refs,
300 calls_needing_exception_table_fixup,
301 } = self;
302
303 values.clear();
304 values.resize(callee.dfg.len_values());
305
306 constants.clear();
307 constants.resize(callee.dfg.constants.len());
308
309 user_external_name_refs.clear();
310 user_external_name_refs.resize(callee.params.user_named_funcs().len());
311
312 calls_needing_exception_table_fixup.clear();
316 }
317
318 fn inlined_external_name(&self, name: &ir::ExternalName) -> ir::ExternalName {
322 match name {
323 ir::ExternalName::User(name_ref) => {
324 ir::ExternalName::User(self.user_external_name_refs[*name_ref].expect(
325 "should have translated all `ir::UserExternalNameRef`s before translating \
326 entities that reference them",
327 ))
328 }
329 ir::ExternalName::TestCase(_)
330 | ir::ExternalName::LibCall(_)
331 | ir::ExternalName::KnownSymbol(_) => name.clone(),
332 }
333 }
334
335 fn set_inlined_value(
336 &mut self,
337 callee: &ir::Function,
338 callee_val: ir::Value,
339 inlined_val: ir::Value,
340 ) {
341 trace!(" --> callee {callee_val:?} = inlined {inlined_val:?}");
342 debug_assert!(self.values[callee_val].is_none());
343 let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
344 debug_assert!(self.values[resolved_callee_val].is_none());
345 self.values[resolved_callee_val] = Some(inlined_val).into();
346 }
347
348 fn get_inlined_value(&self, callee: &ir::Function, callee_val: ir::Value) -> Option<ir::Value> {
349 let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
350 self.values[resolved_callee_val].expand()
351 }
352}
353
354fn inline_one(
358 allocs: &mut InliningAllocs,
359 func: &mut ir::Function,
360 callee_func_ref: ir::FuncRef,
361 call_block: ir::Block,
362 call_inst: ir::Inst,
363 call_opcode: ir::Opcode,
364 callee: &ir::Function,
365 call_exception_table: Option<ir::ExceptionTable>,
366) -> CodegenResult<ir::Block> {
367 trace!(
368 "Inlining call {call_inst:?}: {}\n\
369 with callee = {callee:?}",
370 func.dfg.display_inst(call_inst)
371 );
372
373 let expected_callee_sig = func.dfg.ext_funcs[callee_func_ref].signature;
375 let expected_callee_sig = &func.dfg.signatures[expected_callee_sig];
376 assert_eq!(expected_callee_sig, &callee.signature);
377
378 allocs.reset(callee);
379
380 let entity_map = create_entities(allocs, func, callee)?;
383
384 let return_block = split_off_return_block(func, call_inst, call_opcode, callee);
387 let call_stack_map = replace_call_with_jump(allocs, func, call_inst, callee, &entity_map);
388
389 let mut last_inlined_block = inline_block_layout(func, call_block, callee, &entity_map);
393
394 let call_debug_tags = func.debug_tags.get(call_inst).to_vec();
399 func.debug_tags.set(call_inst, []);
400
401 for callee_block in Dfs::new().pre_order_iter(callee) {
407 let inlined_block = entity_map.inlined_block(callee_block);
408 trace!(
409 "Processing instructions in callee block {callee_block:?} (inlined block {inlined_block:?}"
410 );
411
412 let mut next_callee_inst = callee.layout.first_inst(callee_block);
413 while let Some(callee_inst) = next_callee_inst {
414 trace!(
415 "Processing callee instruction {callee_inst:?}: {}",
416 callee.dfg.display_inst(callee_inst)
417 );
418
419 let mut inst_remapper = InliningInstRemapper {
422 allocs: &allocs,
423 func,
424 callee,
425 entity_map: &entity_map,
426 error: None,
427 };
428 let inlined_inst_data = callee.dfg.insts[callee_inst].map(&mut inst_remapper);
429 if let Some(err) = inst_remapper.error.take() {
430 return Err(err);
431 }
432 let inlined_inst = func.dfg.make_inst(inlined_inst_data);
433 func.layout.append_inst(inlined_inst, inlined_block);
434
435 let debug_tags = callee.debug_tags.get(callee_inst);
438 if !debug_tags.is_empty() {
445 let tags = call_debug_tags
446 .iter()
447 .cloned()
448 .chain(debug_tags.iter().map(|tag| match *tag {
449 DebugTag::User(value) => DebugTag::User(value),
450 DebugTag::StackSlot(slot) => {
451 DebugTag::StackSlot(entity_map.inlined_stack_slot(slot))
452 }
453 }))
454 .collect::<SmallVec<[_; 4]>>();
455 func.debug_tags.set(inlined_inst, tags);
456 }
457
458 let opcode = callee.dfg.insts[callee_inst].opcode();
459 if opcode.is_return() {
460 if let Some(return_block) = return_block {
465 fixup_inst_that_returns(
466 allocs,
467 func,
468 callee,
469 &entity_map,
470 call_opcode,
471 inlined_inst,
472 callee_inst,
473 return_block,
474 call_stack_map.as_ref().map(|es| &**es),
475 );
476 } else {
477 debug_assert_eq!(call_opcode, ir::Opcode::ReturnCall);
482 }
483 } else {
484 let ctrl_typevar = callee.dfg.ctrl_typevar(callee_inst);
486 func.dfg.make_inst_results(inlined_inst, ctrl_typevar);
487
488 let callee_results = callee.dfg.inst_results(callee_inst);
490 let inlined_results = func.dfg.inst_results(inlined_inst);
491 debug_assert_eq!(callee_results.len(), inlined_results.len());
492 for (callee_val, inlined_val) in callee_results.iter().zip(inlined_results) {
493 allocs.set_inlined_value(callee, *callee_val, *inlined_val);
494 }
495
496 if opcode.is_call() {
497 append_stack_map_entries(
498 func,
499 callee,
500 &entity_map,
501 call_stack_map.as_deref(),
502 inlined_inst,
503 callee_inst,
504 );
505
506 debug_assert_eq!(
519 call_opcode == ir::Opcode::TryCall,
520 call_exception_table.is_some()
521 );
522 if call_opcode == ir::Opcode::TryCall {
523 allocs
524 .calls_needing_exception_table_fixup
525 .push(inlined_inst);
526 }
527 }
528 }
529
530 trace!(
531 " --> inserted inlined instruction {inlined_inst:?}: {}",
532 func.dfg.display_inst(inlined_inst)
533 );
534
535 next_callee_inst = callee.layout.next_inst(callee_inst);
536 }
537 }
538
539 for block in entity_map.iter_inlined_blocks(func) {
546 if func.layout.is_block_inserted(block) && func.layout.first_inst(block).is_none() {
547 log::trace!("removing unreachable inlined block from layout: {block}");
548
549 if block == last_inlined_block {
553 last_inlined_block = func.layout.prev_block(last_inlined_block).expect(
554 "there will always at least be the block that contained the call we are \
555 inlining",
556 );
557 }
558
559 func.layout.remove_block(block);
560 }
561 }
562
563 debug_assert!(
574 allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some()
575 );
576 debug_assert_eq!(
577 call_opcode == ir::Opcode::TryCall,
578 call_exception_table.is_some()
579 );
580 if let Some(call_exception_table) = call_exception_table {
581 fixup_inlined_call_exception_tables(allocs, func, call_exception_table);
582 }
583
584 debug_assert!(
585 func.layout.is_block_inserted(last_inlined_block),
586 "last_inlined_block={last_inlined_block} should be inserted in the layout"
587 );
588 Ok(last_inlined_block)
589}
590
591fn append_stack_map_entries(
594 func: &mut ir::Function,
595 callee: &ir::Function,
596 entity_map: &EntityMap,
597 call_stack_map: Option<&[ir::UserStackMapEntry]>,
598 inlined_inst: ir::Inst,
599 callee_inst: ir::Inst,
600) {
601 func.dfg.append_user_stack_map_entries(
605 inlined_inst,
606 call_stack_map
607 .iter()
608 .flat_map(|entries| entries.iter().cloned()),
609 );
610
611 func.dfg.append_user_stack_map_entries(
615 inlined_inst,
616 callee
617 .dfg
618 .user_stack_map_entries(callee_inst)
619 .iter()
620 .flat_map(|entries| entries.iter())
621 .map(|entry| ir::UserStackMapEntry {
622 ty: entry.ty,
623 slot: entity_map.inlined_stack_slot(entry.slot),
624 offset: entry.offset,
625 }),
626 );
627}
628
629fn fixup_inlined_call_exception_tables(
633 allocs: &mut InliningAllocs,
634 func: &mut ir::Function,
635 call_exception_table: ir::ExceptionTable,
636) {
637 let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block {
640 debug_assert!(func.dfg.insts[inst].opcode().is_call());
641 debug_assert!(!func.dfg.insts[inst].opcode().is_terminator());
642
643 let next_inst = func
645 .layout
646 .next_inst(inst)
647 .expect("inst is not a terminator, should have a successor");
648 let new_block = func.dfg.blocks.add();
649 func.layout.split_block(new_block, next_inst);
650
651 let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied());
656 func.dfg.detach_inst_results(inst);
657 for old_result in old_results {
658 let ty = func.dfg.value_type(old_result);
659 let new_block_param = func.dfg.append_block_param(new_block, ty);
660 func.dfg.change_to_alias(old_result, new_block_param);
661 }
662
663 new_block
664 };
665
666 let clone_exception_table_for_this_call = |func: &mut ir::Function,
669 signature: ir::SigRef,
670 new_block: ir::Block|
671 -> ir::ExceptionTable {
672 let mut exception = func.stencil.dfg.exception_tables[call_exception_table]
673 .deep_clone(&mut func.stencil.dfg.value_lists);
674
675 *exception.signature_mut() = signature;
676
677 let returns_len = func.dfg.signatures[signature].returns.len();
678 let returns_len = u32::try_from(returns_len).unwrap();
679
680 *exception.normal_return_mut() = ir::BlockCall::new(
681 new_block,
682 (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)),
683 &mut func.dfg.value_lists,
684 );
685
686 func.dfg.exception_tables.push(exception)
687 };
688
689 for inst in allocs.calls_needing_exception_table_fixup.drain(..) {
690 debug_assert!(func.dfg.insts[inst].opcode().is_call());
691 debug_assert!(!func.dfg.insts[inst].opcode().is_return());
692 match func.dfg.insts[inst] {
693 ir::InstructionData::Call {
706 opcode: ir::Opcode::Call,
707 args,
708 func_ref,
709 } => {
710 let new_block = split_block_for_new_try_call(func, inst);
711 let signature = func.dfg.ext_funcs[func_ref].signature;
712 let exception = clone_exception_table_for_this_call(func, signature, new_block);
713 func.dfg.insts[inst] = ir::InstructionData::TryCall {
714 opcode: ir::Opcode::TryCall,
715 args,
716 func_ref,
717 exception,
718 };
719 }
720
721 ir::InstructionData::CallIndirect {
734 opcode: ir::Opcode::CallIndirect,
735 args,
736 sig_ref,
737 } => {
738 let new_block = split_block_for_new_try_call(func, inst);
739 let exception = clone_exception_table_for_this_call(func, sig_ref, new_block);
740 func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect {
741 opcode: ir::Opcode::TryCallIndirect,
742 args,
743 exception,
744 };
745 }
746
747 ir::InstructionData::TryCall {
750 opcode: ir::Opcode::TryCall,
751 exception,
752 ..
753 }
754 | ir::InstructionData::TryCallIndirect {
755 opcode: ir::Opcode::TryCallIndirect,
756 exception,
757 ..
758 } => {
759 let sig = func.dfg.exception_tables[exception].signature();
766 let normal_return = *func.dfg.exception_tables[exception].normal_return();
767 let exception_data = ExceptionTableData::new(
768 sig,
769 normal_return,
770 func.dfg.exception_tables[exception]
771 .items()
772 .chain(func.dfg.exception_tables[call_exception_table].items()),
773 )
774 .deep_clone(&mut func.dfg.value_lists);
775
776 func.dfg.exception_tables[exception] = exception_data;
777 }
778
779 otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"),
780 }
781 }
782}
783
784fn fixup_inst_that_returns(
789 allocs: &mut InliningAllocs,
790 func: &mut ir::Function,
791 callee: &ir::Function,
792 entity_map: &EntityMap,
793 call_opcode: ir::Opcode,
794 inlined_inst: ir::Inst,
795 callee_inst: ir::Inst,
796 return_block: ir::Block,
797 call_stack_map: Option<&[ir::UserStackMapEntry]>,
798) {
799 debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return());
800 match func.dfg.insts[inlined_inst] {
801 ir::InstructionData::MultiAry {
807 opcode: ir::Opcode::Return,
808 args,
809 } => {
810 let rets = SmallBlockArgVec::from_iter(
811 args.as_slice(&func.dfg.value_lists)
812 .iter()
813 .copied()
814 .map(|v| v.into()),
815 );
816 func.replace(inlined_inst).jump(return_block, &rets);
817 }
818
819 ir::InstructionData::Call {
826 opcode: ir::Opcode::ReturnCall,
827 args,
828 func_ref,
829 } => {
830 func.dfg.insts[inlined_inst] = ir::InstructionData::Call {
831 opcode: ir::Opcode::Call,
832 args,
833 func_ref,
834 };
835 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
836
837 append_stack_map_entries(
838 func,
839 callee,
840 &entity_map,
841 call_stack_map,
842 inlined_inst,
843 callee_inst,
844 );
845
846 let rets = SmallBlockArgVec::from_iter(
847 func.dfg
848 .inst_results(inlined_inst)
849 .iter()
850 .copied()
851 .map(|v| v.into()),
852 );
853 let mut cursor = FuncCursor::new(func);
854 cursor.goto_after_inst(inlined_inst);
855 cursor.ins().jump(return_block, &rets);
856
857 if call_opcode == ir::Opcode::TryCall {
858 allocs
859 .calls_needing_exception_table_fixup
860 .push(inlined_inst);
861 }
862 }
863
864 ir::InstructionData::CallIndirect {
871 opcode: ir::Opcode::ReturnCallIndirect,
872 args,
873 sig_ref,
874 } => {
875 func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect {
876 opcode: ir::Opcode::CallIndirect,
877 args,
878 sig_ref,
879 };
880 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
881
882 append_stack_map_entries(
883 func,
884 callee,
885 &entity_map,
886 call_stack_map,
887 inlined_inst,
888 callee_inst,
889 );
890
891 let rets = SmallBlockArgVec::from_iter(
892 func.dfg
893 .inst_results(inlined_inst)
894 .iter()
895 .copied()
896 .map(|v| v.into()),
897 );
898 let mut cursor = FuncCursor::new(func);
899 cursor.goto_after_inst(inlined_inst);
900 cursor.ins().jump(return_block, &rets);
901
902 if call_opcode == ir::Opcode::TryCall {
903 allocs
904 .calls_needing_exception_table_fixup
905 .push(inlined_inst);
906 }
907 }
908
909 inst_data => unreachable!(
910 "should have handled all `is_return() == true` instructions above; \
911 got {inst_data:?}"
912 ),
913 }
914}
915
916struct InliningInstRemapper<'a> {
919 allocs: &'a InliningAllocs,
920 func: &'a mut ir::Function,
921 callee: &'a ir::Function,
922 entity_map: &'a EntityMap,
923 error: Option<crate::result::CodegenError>,
924}
925
926impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> {
927 fn map_value(&mut self, value: ir::Value) -> ir::Value {
928 self.allocs.get_inlined_value(self.callee, value).expect(
929 "defs come before uses; we should have already inlined all values \
930 used by an instruction",
931 )
932 }
933
934 fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList {
935 let mut inlined_list = ir::ValueList::new();
936 for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) {
937 let inlined_val = self.map_value(*callee_val);
938 inlined_list.push(inlined_val, &mut self.func.dfg.value_lists);
939 }
940 inlined_list
941 }
942
943 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
944 self.entity_map.inlined_global_value(global_value)
945 }
946
947 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
948 let inlined_default =
949 self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block());
950 let inlined_table = self.callee.dfg.jump_tables[jump_table]
951 .as_slice()
952 .iter()
953 .map(|callee_block_call| self.map_block_call(*callee_block_call))
954 .collect::<SmallBlockCallVec>();
955 self.func
956 .dfg
957 .jump_tables
958 .push(ir::JumpTableData::new(inlined_default, &inlined_table))
959 }
960
961 fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable {
962 let exception_table = &self.callee.dfg.exception_tables[exception_table];
963 let inlined_sig_ref = self.map_sig_ref(exception_table.signature());
964 let inlined_normal_return = self.map_block_call(*exception_table.normal_return());
965 let inlined_table = exception_table
966 .items()
967 .map(|item| match item {
968 ExceptionTableItem::Tag(tag, block_call) => {
969 ExceptionTableItem::Tag(tag, self.map_block_call(block_call))
970 }
971 ExceptionTableItem::Default(block_call) => {
972 ExceptionTableItem::Default(self.map_block_call(block_call))
973 }
974 ExceptionTableItem::Context(value) => {
975 ExceptionTableItem::Context(self.map_value(value))
976 }
977 })
978 .collect::<SmallVec<[_; 8]>>();
979 self.func
980 .dfg
981 .exception_tables
982 .push(ir::ExceptionTableData::new(
983 inlined_sig_ref,
984 inlined_normal_return,
985 inlined_table,
986 ))
987 }
988
989 fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall {
990 let callee_block = block_call.block(&self.callee.dfg.value_lists);
991 let inlined_block = self.entity_map.inlined_block(callee_block);
992 let args = block_call
993 .args(&self.callee.dfg.value_lists)
994 .map(|arg| match arg {
995 ir::BlockArg::Value(value) => self.map_value(value).into(),
996 ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg,
997 })
998 .collect::<SmallBlockArgVec>();
999 ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists)
1000 }
1001
1002 fn map_block(&mut self, block: ir::Block) -> ir::Block {
1003 self.entity_map.inlined_block(block)
1004 }
1005
1006 fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef {
1007 self.entity_map.inlined_func_ref(func_ref)
1008 }
1009
1010 fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef {
1011 self.entity_map.inlined_sig_ref(sig_ref)
1012 }
1013
1014 fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot {
1015 self.entity_map.inlined_stack_slot(stack_slot)
1016 }
1017
1018 fn map_dynamic_stack_slot(
1019 &mut self,
1020 dynamic_stack_slot: ir::DynamicStackSlot,
1021 ) -> ir::DynamicStackSlot {
1022 self.entity_map
1023 .inlined_dynamic_stack_slot(dynamic_stack_slot)
1024 }
1025
1026 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1027 self.allocs
1028 .constants
1029 .get(constant)
1030 .and_then(|o| o.expand())
1031 .expect("should have inlined all callee constants")
1032 }
1033
1034 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1035 self.entity_map.inlined_immediate(immediate)
1036 }
1037 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1038 let mut flags_data = self.callee.dfg.mem_flags[flags];
1039 if let Some(callee_region) = flags_data.alias_region() {
1041 let region_data = self.callee.dfg.alias_regions[callee_region].clone();
1042 let caller_region = self.func.dfg.alias_regions.insert(region_data);
1043 flags_data.set_alias_region(Some(caller_region));
1044 }
1045 match self.func.dfg.mem_flags.insert(flags_data) {
1046 Ok(flags) => flags,
1047 Err(_) => {
1048 self.error = Some(crate::result::CodegenError::ImplLimitExceeded);
1049 self.func
1050 .dfg
1051 .mem_flags
1052 .insert(ir::MemFlagsData::trusted())
1053 .unwrap()
1054 }
1055 }
1056 }
1057}
1058
1059fn inline_block_layout(
1063 func: &mut ir::Function,
1064 call_block: ir::Block,
1065 callee: &ir::Function,
1066 entity_map: &EntityMap,
1067) -> ir::Block {
1068 debug_assert!(func.layout.is_block_inserted(call_block));
1069
1070 let mut prev_inlined_block = call_block;
1073 let mut next_callee_block = callee.layout.entry_block();
1074 while let Some(callee_block) = next_callee_block {
1075 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1076
1077 let inlined_block = entity_map.inlined_block(callee_block);
1078 func.layout
1079 .insert_block_after(inlined_block, prev_inlined_block);
1080
1081 prev_inlined_block = inlined_block;
1082 next_callee_block = callee.layout.next_block(callee_block);
1083 }
1084
1085 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1086 prev_inlined_block
1087}
1088
1089fn split_off_return_block(
1095 func: &mut ir::Function,
1096 call_inst: ir::Inst,
1097 opcode: ir::Opcode,
1098 callee: &ir::Function,
1099) -> Option<ir::Block> {
1100 let return_block = func.layout.next_inst(call_inst).map(|next_inst| {
1103 let return_block = func.dfg.blocks.add();
1104 func.layout.split_block(return_block, next_inst);
1105
1106 let old_results =
1109 SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied());
1110 debug_assert_eq!(old_results.len(), callee.signature.returns.len());
1111 func.dfg.detach_inst_results(call_inst);
1112 for (abi, old_val) in callee.signature.returns.iter().zip(old_results) {
1113 debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val));
1114 let ret_param = func.dfg.append_block_param(return_block, abi.value_type);
1115 func.dfg.change_to_alias(old_val, ret_param);
1116 }
1117
1118 return_block
1119 });
1120
1121 debug_assert_eq!(
1152 return_block.is_none(),
1153 opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall,
1154 );
1155 return_block.or_else(|| match func.dfg.insts[call_inst] {
1156 ir::InstructionData::TryCall {
1157 opcode: ir::Opcode::TryCall,
1158 args: _,
1159 func_ref: _,
1160 exception,
1161 } => {
1162 let normal_return = func.dfg.exception_tables[exception].normal_return();
1163 let normal_return_block = normal_return.block(&func.dfg.value_lists);
1164
1165 {
1167 let normal_return_args = normal_return.args(&func.dfg.value_lists);
1168 if normal_return_args.len() == callee.signature.returns.len()
1169 && normal_return_args.enumerate().all(|(i, arg)| {
1170 let i = u32::try_from(i).unwrap();
1171 arg == ir::BlockArg::TryCallRet(i)
1172 })
1173 {
1174 return Some(normal_return_block);
1175 }
1176 }
1177
1178 let return_block = func.dfg.blocks.add();
1182 func.layout.insert_block(return_block, normal_return_block);
1183
1184 let return_block_params = callee
1185 .signature
1186 .returns
1187 .iter()
1188 .map(|abi| func.dfg.append_block_param(return_block, abi.value_type))
1189 .collect::<SmallValueVec>();
1190
1191 let normal_return_args = func.dfg.exception_tables[exception]
1192 .normal_return()
1193 .args(&func.dfg.value_lists)
1194 .collect::<SmallBlockArgVec>();
1195 let jump_args = normal_return_args
1196 .into_iter()
1197 .map(|arg| match arg {
1198 ir::BlockArg::Value(value) => ir::BlockArg::Value(value),
1199 ir::BlockArg::TryCallRet(i) => {
1200 let i = usize::try_from(i).unwrap();
1201 ir::BlockArg::Value(return_block_params[i])
1202 }
1203 ir::BlockArg::TryCallExn(_) => {
1204 unreachable!("normal-return edges cannot use exceptional results")
1205 }
1206 })
1207 .collect::<SmallBlockArgVec>();
1208
1209 let mut cursor = FuncCursor::new(func);
1210 cursor.goto_first_insertion_point(return_block);
1211 cursor.ins().jump(normal_return_block, &jump_args);
1212
1213 Some(return_block)
1214 }
1215 _ => None,
1216 })
1217}
1218
1219fn replace_call_with_jump(
1227 allocs: &mut InliningAllocs,
1228 func: &mut ir::Function,
1229 call_inst: ir::Inst,
1230 callee: &ir::Function,
1231 entity_map: &EntityMap,
1232) -> Option<ir::UserStackMapEntryVec> {
1233 trace!("Replacing `call` with `jump`");
1234 trace!(
1235 " --> call instruction: {call_inst:?}: {}",
1236 func.dfg.display_inst(call_inst)
1237 );
1238
1239 let callee_entry_block = callee
1240 .layout
1241 .entry_block()
1242 .expect("callee function should have an entry block");
1243 let callee_param_values = callee.dfg.block_params(callee_entry_block);
1244 let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied());
1245 debug_assert_eq!(callee_param_values.len(), caller_arg_values.len());
1246 debug_assert_eq!(callee_param_values.len(), callee.signature.params.len());
1247 for (abi, (callee_param_value, caller_arg_value)) in callee
1248 .signature
1249 .params
1250 .iter()
1251 .zip(callee_param_values.into_iter().zip(caller_arg_values))
1252 {
1253 debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value));
1254 debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value));
1255 allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value);
1256 }
1257
1258 let inlined_entry_block = entity_map.inlined_block(callee_entry_block);
1265 func.replace(call_inst).jump(inlined_entry_block, &[]);
1266 trace!(
1267 " --> replaced with jump instruction: {call_inst:?}: {}",
1268 func.dfg.display_inst(call_inst)
1269 );
1270
1271 let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst);
1272 stack_map_entries
1273}
1274
1275#[derive(Default)]
1278struct EntityMap {
1279 block_offset: Option<u32>,
1292 global_value_offset: Option<u32>,
1293 sig_ref_offset: Option<u32>,
1294 func_ref_offset: Option<u32>,
1295 stack_slot_offset: Option<u32>,
1296 dynamic_type_offset: Option<u32>,
1297 dynamic_stack_slot_offset: Option<u32>,
1298 immediate_offset: Option<u32>,
1299}
1300
1301impl EntityMap {
1302 fn inlined_block(&self, callee_block: ir::Block) -> ir::Block {
1303 let offset = self
1304 .block_offset
1305 .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`");
1306 ir::Block::from_u32(offset + callee_block.as_u32())
1307 }
1308
1309 fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> {
1310 let start = self.block_offset.expect(
1311 "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`",
1312 );
1313
1314 let end = func.dfg.blocks.len();
1315 let end = u32::try_from(end).unwrap();
1316
1317 (start..end).map(|i| ir::Block::from_u32(i))
1318 }
1319
1320 fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue {
1321 let offset = self
1322 .global_value_offset
1323 .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`");
1324 ir::GlobalValue::from_u32(offset + callee_global_value.as_u32())
1325 }
1326
1327 fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef {
1328 let offset = self.sig_ref_offset.expect(
1329 "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`",
1330 );
1331 ir::SigRef::from_u32(offset + callee_sig_ref.as_u32())
1332 }
1333
1334 fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef {
1335 let offset = self.func_ref_offset.expect(
1336 "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`",
1337 );
1338 ir::FuncRef::from_u32(offset + callee_func_ref.as_u32())
1339 }
1340
1341 fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot {
1342 let offset = self.stack_slot_offset.expect(
1343 "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`",
1344 );
1345 ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32())
1346 }
1347
1348 fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType {
1349 let offset = self.dynamic_type_offset.expect(
1350 "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`",
1351 );
1352 ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32())
1353 }
1354
1355 fn inlined_dynamic_stack_slot(
1356 &self,
1357 callee_dynamic_stack_slot: ir::DynamicStackSlot,
1358 ) -> ir::DynamicStackSlot {
1359 let offset = self.dynamic_stack_slot_offset.expect(
1360 "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`",
1361 );
1362 ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32())
1363 }
1364
1365 fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate {
1366 let offset = self.immediate_offset.expect(
1367 "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`",
1368 );
1369 ir::Immediate::from_u32(offset + callee_immediate.as_u32())
1370 }
1371}
1372
1373fn create_entities(
1377 allocs: &mut InliningAllocs,
1378 func: &mut ir::Function,
1379 callee: &ir::Function,
1380) -> CodegenResult<EntityMap> {
1381 let mut entity_map = EntityMap::default();
1382
1383 entity_map.block_offset = Some(create_blocks(allocs, func, callee));
1384 create_user_external_name_refs(allocs, func, callee);
1385 entity_map.global_value_offset = Some(create_global_values(allocs, func, callee)?);
1386 entity_map.sig_ref_offset = Some(create_sig_refs(func, callee));
1387 entity_map.func_ref_offset = Some(create_func_refs(allocs, func, callee, &entity_map));
1388 entity_map.stack_slot_offset = Some(create_stack_slots(func, callee));
1389 entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map));
1390 entity_map.dynamic_stack_slot_offset =
1391 Some(create_dynamic_stack_slots(func, callee, &entity_map));
1392 entity_map.immediate_offset = Some(create_immediates(func, callee));
1393
1394 create_constants(allocs, func, callee);
1398
1399 Ok(entity_map)
1400}
1401
1402fn create_blocks(
1404 allocs: &mut InliningAllocs,
1405 func: &mut ir::Function,
1406 callee: &ir::Function,
1407) -> u32 {
1408 let offset = func.dfg.blocks.len();
1409 let offset = u32::try_from(offset).unwrap();
1410
1411 func.dfg.blocks.reserve(callee.dfg.blocks.len());
1412 for callee_block in callee.dfg.blocks.iter() {
1413 let caller_block = func.dfg.blocks.add();
1414 trace!("Callee {callee_block:?} = inlined {caller_block:?}");
1415
1416 if callee.layout.is_cold(callee_block) {
1417 func.layout.set_cold(caller_block);
1418 }
1419
1420 if callee.layout.entry_block() != Some(callee_block) {
1424 for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) {
1425 let ty = callee.dfg.value_type(*callee_param);
1426 let caller_param = func.dfg.append_block_param(caller_block, ty);
1427
1428 allocs.set_inlined_value(callee, *callee_param, caller_param);
1429 }
1430 }
1431 }
1432
1433 offset
1434}
1435
1436fn create_global_values(
1438 allocs: &InliningAllocs,
1439 func: &mut ir::Function,
1440 callee: &ir::Function,
1441) -> CodegenResult<u32> {
1442 let gv_offset = func.global_values.len();
1443 let gv_offset = u32::try_from(gv_offset).unwrap();
1444
1445 func.global_values.reserve(callee.global_values.len());
1446 for gv in callee.global_values.values() {
1447 let remapped_flags = match gv {
1450 ir::GlobalValueData::Load { flags, .. } => {
1451 let mut flags_data = callee.dfg.mem_flags[*flags];
1452 if let Some(callee_region) = flags_data.alias_region() {
1454 let region_data = callee.dfg.alias_regions[callee_region].clone();
1455 let caller_region = func.dfg.alias_regions.insert(region_data);
1456 flags_data.set_alias_region(Some(caller_region));
1457 }
1458 Some(
1459 func.dfg
1460 .mem_flags
1461 .insert(flags_data)
1462 .map_err(|_| crate::result::CodegenError::ImplLimitExceeded)?,
1463 )
1464 }
1465 _ => None,
1466 };
1467 func.global_values.push(match gv {
1468 ir::GlobalValueData::Load {
1471 base,
1472 offset,
1473 global_type,
1474 flags: _,
1475 } => ir::GlobalValueData::Load {
1476 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1477 offset: *offset,
1478 global_type: *global_type,
1479 flags: remapped_flags.unwrap(),
1480 },
1481 ir::GlobalValueData::IAddImm {
1482 base,
1483 offset,
1484 global_type,
1485 } => ir::GlobalValueData::IAddImm {
1486 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1487 offset: *offset,
1488 global_type: *global_type,
1489 },
1490
1491 ir::GlobalValueData::Symbol {
1494 name,
1495 offset,
1496 colocated,
1497 tls,
1498 } => ir::GlobalValueData::Symbol {
1499 name: allocs.inlined_external_name(name),
1500 offset: *offset,
1501 colocated: *colocated,
1502 tls: *tls,
1503 },
1504
1505 ir::GlobalValueData::VMContext | ir::GlobalValueData::DynScaleTargetConst { .. } => {
1508 gv.clone()
1509 }
1510 });
1511 }
1512
1513 Ok(gv_offset)
1514}
1515
1516fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1518 let offset = func.dfg.signatures.len();
1519 let offset = u32::try_from(offset).unwrap();
1520
1521 func.dfg.signatures.reserve(callee.dfg.signatures.len());
1522 for sig in callee.dfg.signatures.values() {
1523 func.dfg.signatures.push(sig.clone());
1524 }
1525
1526 offset
1527}
1528
1529fn create_user_external_name_refs(
1530 allocs: &mut InliningAllocs,
1531 func: &mut ir::Function,
1532 callee: &ir::Function,
1533) {
1534 for (callee_named_func_ref, name) in callee.params.user_named_funcs().iter() {
1535 let caller_named_func_ref = func.declare_imported_user_function(name.clone());
1536 allocs.user_external_name_refs[callee_named_func_ref] = Some(caller_named_func_ref).into();
1537 }
1538}
1539
1540fn create_func_refs(
1542 allocs: &InliningAllocs,
1543 func: &mut ir::Function,
1544 callee: &ir::Function,
1545 entity_map: &EntityMap,
1546) -> u32 {
1547 let offset = func.dfg.ext_funcs.len();
1548 let offset = u32::try_from(offset).unwrap();
1549
1550 func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len());
1551 for ir::ExtFuncData {
1552 name,
1553 signature,
1554 colocated,
1555 patchable,
1556 } in callee.dfg.ext_funcs.values()
1557 {
1558 func.dfg.ext_funcs.push(ir::ExtFuncData {
1559 name: allocs.inlined_external_name(name),
1560 signature: entity_map.inlined_sig_ref(*signature),
1561 colocated: *colocated,
1562 patchable: *patchable,
1563 });
1564 }
1565
1566 offset
1567}
1568
1569fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1571 let offset = func.sized_stack_slots.len();
1572 let offset = u32::try_from(offset).unwrap();
1573
1574 func.sized_stack_slots
1575 .reserve(callee.sized_stack_slots.len());
1576 for slot in callee.sized_stack_slots.values() {
1577 func.sized_stack_slots.push(slot.clone());
1578 }
1579
1580 offset
1581}
1582
1583fn create_dynamic_types(
1585 func: &mut ir::Function,
1586 callee: &ir::Function,
1587 entity_map: &EntityMap,
1588) -> u32 {
1589 let offset = func.dfg.dynamic_types.len();
1590 let offset = u32::try_from(offset).unwrap();
1591
1592 func.dfg
1593 .dynamic_types
1594 .reserve(callee.dfg.dynamic_types.len());
1595 for ir::DynamicTypeData {
1596 base_vector_ty,
1597 dynamic_scale,
1598 } in callee.dfg.dynamic_types.values()
1599 {
1600 func.dfg.dynamic_types.push(ir::DynamicTypeData {
1601 base_vector_ty: *base_vector_ty,
1602 dynamic_scale: entity_map.inlined_global_value(*dynamic_scale),
1603 });
1604 }
1605
1606 offset
1607}
1608
1609fn create_dynamic_stack_slots(
1611 func: &mut ir::Function,
1612 callee: &ir::Function,
1613 entity_map: &EntityMap,
1614) -> u32 {
1615 let offset = func.dynamic_stack_slots.len();
1616 let offset = u32::try_from(offset).unwrap();
1617
1618 func.dynamic_stack_slots
1619 .reserve(callee.dynamic_stack_slots.len());
1620 for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() {
1621 func.dynamic_stack_slots.push(ir::DynamicStackSlotData {
1622 kind: *kind,
1623 dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty),
1624 });
1625 }
1626
1627 offset
1628}
1629
1630fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1632 let offset = func.dfg.immediates.len();
1633 let offset = u32::try_from(offset).unwrap();
1634
1635 func.dfg.immediates.reserve(callee.dfg.immediates.len());
1636 for imm in callee.dfg.immediates.values() {
1637 func.dfg.immediates.push(imm.clone());
1638 }
1639
1640 offset
1641}
1642
1643fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) {
1645 for (callee_constant, data) in callee.dfg.constants.iter() {
1646 let inlined_constant = func.dfg.constants.insert(data.clone());
1647 allocs.constants[*callee_constant] = Some(inlined_constant).into();
1648 }
1649}