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_safepoint() {
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
507 if opcode.is_call() {
508 debug_assert_eq!(
521 call_opcode == ir::Opcode::TryCall,
522 call_exception_table.is_some()
523 );
524 if call_opcode == ir::Opcode::TryCall {
525 allocs
526 .calls_needing_exception_table_fixup
527 .push(inlined_inst);
528 }
529 }
530 }
531
532 trace!(
533 " --> inserted inlined instruction {inlined_inst:?}: {}",
534 func.dfg.display_inst(inlined_inst)
535 );
536
537 next_callee_inst = callee.layout.next_inst(callee_inst);
538 }
539 }
540
541 for block in entity_map.iter_inlined_blocks(func) {
548 if func.layout.is_block_inserted(block) && func.layout.first_inst(block).is_none() {
549 log::trace!("removing unreachable inlined block from layout: {block}");
550
551 if block == last_inlined_block {
555 last_inlined_block = func.layout.prev_block(last_inlined_block).expect(
556 "there will always at least be the block that contained the call we are \
557 inlining",
558 );
559 }
560
561 func.layout.remove_block(block);
562 }
563 }
564
565 debug_assert!(
576 allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some()
577 );
578 debug_assert_eq!(
579 call_opcode == ir::Opcode::TryCall,
580 call_exception_table.is_some()
581 );
582 if let Some(call_exception_table) = call_exception_table {
583 fixup_inlined_call_exception_tables(allocs, func, call_exception_table);
584 }
585
586 debug_assert!(
587 func.layout.is_block_inserted(last_inlined_block),
588 "last_inlined_block={last_inlined_block} should be inserted in the layout"
589 );
590 Ok(last_inlined_block)
591}
592
593fn append_stack_map_entries(
596 func: &mut ir::Function,
597 callee: &ir::Function,
598 entity_map: &EntityMap,
599 call_stack_map: Option<&[ir::UserStackMapEntry]>,
600 inlined_inst: ir::Inst,
601 callee_inst: ir::Inst,
602) {
603 func.dfg.append_user_stack_map_entries(
607 inlined_inst,
608 call_stack_map
609 .iter()
610 .flat_map(|entries| entries.iter().cloned()),
611 );
612
613 func.dfg.append_user_stack_map_entries(
617 inlined_inst,
618 callee
619 .dfg
620 .user_stack_map_entries(callee_inst)
621 .iter()
622 .flat_map(|entries| entries.iter())
623 .map(|entry| ir::UserStackMapEntry {
624 ty: entry.ty,
625 slot: entity_map.inlined_stack_slot(entry.slot),
626 offset: entry.offset,
627 }),
628 );
629}
630
631fn fixup_inlined_call_exception_tables(
635 allocs: &mut InliningAllocs,
636 func: &mut ir::Function,
637 call_exception_table: ir::ExceptionTable,
638) {
639 let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block {
642 debug_assert!(func.dfg.insts[inst].opcode().is_call());
643 debug_assert!(!func.dfg.insts[inst].opcode().is_terminator());
644
645 let next_inst = func
647 .layout
648 .next_inst(inst)
649 .expect("inst is not a terminator, should have a successor");
650 let new_block = func.dfg.blocks.add();
651 func.layout.split_block(new_block, next_inst);
652
653 let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied());
658 func.dfg.detach_inst_results(inst);
659 for old_result in old_results {
660 let ty = func.dfg.value_type(old_result);
661 let new_block_param = func.dfg.append_block_param(new_block, ty);
662 func.dfg.change_to_alias(old_result, new_block_param);
663 }
664
665 new_block
666 };
667
668 let clone_exception_table_for_this_call = |func: &mut ir::Function,
671 signature: ir::SigRef,
672 new_block: ir::Block|
673 -> ir::ExceptionTable {
674 let mut exception = func.stencil.dfg.exception_tables[call_exception_table]
675 .deep_clone(&mut func.stencil.dfg.value_lists);
676
677 *exception.signature_mut() = signature;
678
679 let returns_len = func.dfg.signatures[signature].returns.len();
680 let returns_len = u32::try_from(returns_len).unwrap();
681
682 *exception.normal_return_mut() = ir::BlockCall::new(
683 new_block,
684 (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)),
685 &mut func.dfg.value_lists,
686 );
687
688 func.dfg.exception_tables.push(exception)
689 };
690
691 for inst in allocs.calls_needing_exception_table_fixup.drain(..) {
692 debug_assert!(func.dfg.insts[inst].opcode().is_call());
693 debug_assert!(!func.dfg.insts[inst].opcode().is_return());
694 match func.dfg.insts[inst] {
695 ir::InstructionData::Call {
708 opcode: ir::Opcode::Call,
709 args,
710 func_ref,
711 } => {
712 let new_block = split_block_for_new_try_call(func, inst);
713 let signature = func.dfg.ext_funcs[func_ref].signature;
714 let exception = clone_exception_table_for_this_call(func, signature, new_block);
715 func.dfg.insts[inst] = ir::InstructionData::TryCall {
716 opcode: ir::Opcode::TryCall,
717 args,
718 func_ref,
719 exception,
720 };
721 }
722
723 ir::InstructionData::CallIndirect {
736 opcode: ir::Opcode::CallIndirect,
737 args,
738 sig_ref,
739 } => {
740 let new_block = split_block_for_new_try_call(func, inst);
741 let exception = clone_exception_table_for_this_call(func, sig_ref, new_block);
742 func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect {
743 opcode: ir::Opcode::TryCallIndirect,
744 args,
745 exception,
746 };
747 }
748
749 ir::InstructionData::TryCall {
752 opcode: ir::Opcode::TryCall,
753 exception,
754 ..
755 }
756 | ir::InstructionData::TryCallIndirect {
757 opcode: ir::Opcode::TryCallIndirect,
758 exception,
759 ..
760 } => {
761 let sig = func.dfg.exception_tables[exception].signature();
768 let normal_return = *func.dfg.exception_tables[exception].normal_return();
769 let exception_data = ExceptionTableData::new(
770 sig,
771 normal_return,
772 func.dfg.exception_tables[exception]
773 .items()
774 .chain(func.dfg.exception_tables[call_exception_table].items()),
775 )
776 .deep_clone(&mut func.dfg.value_lists);
777
778 func.dfg.exception_tables[exception] = exception_data;
779 }
780
781 otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"),
782 }
783 }
784}
785
786fn fixup_inst_that_returns(
791 allocs: &mut InliningAllocs,
792 func: &mut ir::Function,
793 callee: &ir::Function,
794 entity_map: &EntityMap,
795 call_opcode: ir::Opcode,
796 inlined_inst: ir::Inst,
797 callee_inst: ir::Inst,
798 return_block: ir::Block,
799 call_stack_map: Option<&[ir::UserStackMapEntry]>,
800) {
801 debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return());
802 match func.dfg.insts[inlined_inst] {
803 ir::InstructionData::MultiAry {
809 opcode: ir::Opcode::Return,
810 args,
811 } => {
812 let rets = SmallBlockArgVec::from_iter(
813 args.as_slice(&func.dfg.value_lists)
814 .iter()
815 .copied()
816 .map(|v| v.into()),
817 );
818 func.replace(inlined_inst).jump(return_block, &rets);
819 }
820
821 ir::InstructionData::Call {
828 opcode: ir::Opcode::ReturnCall,
829 args,
830 func_ref,
831 } => {
832 func.dfg.insts[inlined_inst] = ir::InstructionData::Call {
833 opcode: ir::Opcode::Call,
834 args,
835 func_ref,
836 };
837 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
838
839 append_stack_map_entries(
840 func,
841 callee,
842 &entity_map,
843 call_stack_map,
844 inlined_inst,
845 callee_inst,
846 );
847
848 let rets = SmallBlockArgVec::from_iter(
849 func.dfg
850 .inst_results(inlined_inst)
851 .iter()
852 .copied()
853 .map(|v| v.into()),
854 );
855 let mut cursor = FuncCursor::new(func);
856 cursor.goto_after_inst(inlined_inst);
857 cursor.ins().jump(return_block, &rets);
858
859 if call_opcode == ir::Opcode::TryCall {
860 allocs
861 .calls_needing_exception_table_fixup
862 .push(inlined_inst);
863 }
864 }
865
866 ir::InstructionData::CallIndirect {
873 opcode: ir::Opcode::ReturnCallIndirect,
874 args,
875 sig_ref,
876 } => {
877 func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect {
878 opcode: ir::Opcode::CallIndirect,
879 args,
880 sig_ref,
881 };
882 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
883
884 append_stack_map_entries(
885 func,
886 callee,
887 &entity_map,
888 call_stack_map,
889 inlined_inst,
890 callee_inst,
891 );
892
893 let rets = SmallBlockArgVec::from_iter(
894 func.dfg
895 .inst_results(inlined_inst)
896 .iter()
897 .copied()
898 .map(|v| v.into()),
899 );
900 let mut cursor = FuncCursor::new(func);
901 cursor.goto_after_inst(inlined_inst);
902 cursor.ins().jump(return_block, &rets);
903
904 if call_opcode == ir::Opcode::TryCall {
905 allocs
906 .calls_needing_exception_table_fixup
907 .push(inlined_inst);
908 }
909 }
910
911 inst_data => unreachable!(
912 "should have handled all `is_return() == true` instructions above; \
913 got {inst_data:?}"
914 ),
915 }
916}
917
918struct InliningInstRemapper<'a> {
921 allocs: &'a InliningAllocs,
922 func: &'a mut ir::Function,
923 callee: &'a ir::Function,
924 entity_map: &'a EntityMap,
925 error: Option<crate::result::CodegenError>,
926}
927
928impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> {
929 fn map_value(&mut self, value: ir::Value) -> ir::Value {
930 self.allocs.get_inlined_value(self.callee, value).expect(
931 "defs come before uses; we should have already inlined all values \
932 used by an instruction",
933 )
934 }
935
936 fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList {
937 let mut inlined_list = ir::ValueList::new();
938 for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) {
939 let inlined_val = self.map_value(*callee_val);
940 inlined_list.push(inlined_val, &mut self.func.dfg.value_lists);
941 }
942 inlined_list
943 }
944
945 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
946 self.entity_map.inlined_global_value(global_value)
947 }
948
949 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
950 let inlined_default =
951 self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block());
952 let inlined_table = self.callee.dfg.jump_tables[jump_table]
953 .as_slice()
954 .iter()
955 .map(|callee_block_call| self.map_block_call(*callee_block_call))
956 .collect::<SmallBlockCallVec>();
957 self.func
958 .dfg
959 .jump_tables
960 .push(ir::JumpTableData::new(inlined_default, &inlined_table))
961 }
962
963 fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable {
964 let exception_table = &self.callee.dfg.exception_tables[exception_table];
965 let inlined_sig_ref = self.map_sig_ref(exception_table.signature());
966 let inlined_normal_return = self.map_block_call(*exception_table.normal_return());
967 let inlined_table = exception_table
968 .items()
969 .map(|item| match item {
970 ExceptionTableItem::Tag(tag, block_call) => {
971 ExceptionTableItem::Tag(tag, self.map_block_call(block_call))
972 }
973 ExceptionTableItem::Default(block_call) => {
974 ExceptionTableItem::Default(self.map_block_call(block_call))
975 }
976 ExceptionTableItem::Context(value) => {
977 ExceptionTableItem::Context(self.map_value(value))
978 }
979 })
980 .collect::<SmallVec<[_; 8]>>();
981 self.func
982 .dfg
983 .exception_tables
984 .push(ir::ExceptionTableData::new(
985 inlined_sig_ref,
986 inlined_normal_return,
987 inlined_table,
988 ))
989 }
990
991 fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall {
992 let callee_block = block_call.block(&self.callee.dfg.value_lists);
993 let inlined_block = self.entity_map.inlined_block(callee_block);
994 let args = block_call
995 .args(&self.callee.dfg.value_lists)
996 .map(|arg| match arg {
997 ir::BlockArg::Value(value) => self.map_value(value).into(),
998 ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg,
999 })
1000 .collect::<SmallBlockArgVec>();
1001 ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists)
1002 }
1003
1004 fn map_block(&mut self, block: ir::Block) -> ir::Block {
1005 self.entity_map.inlined_block(block)
1006 }
1007
1008 fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef {
1009 self.entity_map.inlined_func_ref(func_ref)
1010 }
1011
1012 fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef {
1013 self.entity_map.inlined_sig_ref(sig_ref)
1014 }
1015
1016 fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot {
1017 self.entity_map.inlined_stack_slot(stack_slot)
1018 }
1019
1020 fn map_dynamic_stack_slot(
1021 &mut self,
1022 dynamic_stack_slot: ir::DynamicStackSlot,
1023 ) -> ir::DynamicStackSlot {
1024 self.entity_map
1025 .inlined_dynamic_stack_slot(dynamic_stack_slot)
1026 }
1027
1028 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1029 self.allocs
1030 .constants
1031 .get(constant)
1032 .and_then(|o| o.expand())
1033 .expect("should have inlined all callee constants")
1034 }
1035
1036 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1037 self.entity_map.inlined_immediate(immediate)
1038 }
1039 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1040 let mut flags_data = self.callee.dfg.mem_flags[flags];
1041 if let Some(callee_region) = flags_data.alias_region() {
1043 let region_data = self.callee.dfg.alias_regions[callee_region].clone();
1044 let caller_region = self.func.dfg.alias_regions.insert(region_data);
1045 flags_data.set_alias_region(Some(caller_region));
1046 }
1047 match self.func.dfg.mem_flags.insert(flags_data) {
1048 Ok(flags) => flags,
1049 Err(_) => {
1050 self.error = Some(crate::result::CodegenError::ImplLimitExceeded);
1051 self.func
1052 .dfg
1053 .mem_flags
1054 .insert(ir::MemFlagsData::trusted())
1055 .unwrap()
1056 }
1057 }
1058 }
1059}
1060
1061fn inline_block_layout(
1065 func: &mut ir::Function,
1066 call_block: ir::Block,
1067 callee: &ir::Function,
1068 entity_map: &EntityMap,
1069) -> ir::Block {
1070 debug_assert!(func.layout.is_block_inserted(call_block));
1071
1072 let mut prev_inlined_block = call_block;
1075 let mut next_callee_block = callee.layout.entry_block();
1076 while let Some(callee_block) = next_callee_block {
1077 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1078
1079 let inlined_block = entity_map.inlined_block(callee_block);
1080 func.layout
1081 .insert_block_after(inlined_block, prev_inlined_block);
1082
1083 prev_inlined_block = inlined_block;
1084 next_callee_block = callee.layout.next_block(callee_block);
1085 }
1086
1087 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1088 prev_inlined_block
1089}
1090
1091fn split_off_return_block(
1097 func: &mut ir::Function,
1098 call_inst: ir::Inst,
1099 opcode: ir::Opcode,
1100 callee: &ir::Function,
1101) -> Option<ir::Block> {
1102 let return_block = func.layout.next_inst(call_inst).map(|next_inst| {
1105 let return_block = func.dfg.blocks.add();
1106 func.layout.split_block(return_block, next_inst);
1107
1108 let old_results =
1111 SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied());
1112 debug_assert_eq!(old_results.len(), callee.signature.returns.len());
1113 func.dfg.detach_inst_results(call_inst);
1114 for (abi, old_val) in callee.signature.returns.iter().zip(old_results) {
1115 debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val));
1116 let ret_param = func.dfg.append_block_param(return_block, abi.value_type);
1117 func.dfg.change_to_alias(old_val, ret_param);
1118 }
1119
1120 return_block
1121 });
1122
1123 debug_assert_eq!(
1154 return_block.is_none(),
1155 opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall,
1156 );
1157 return_block.or_else(|| match func.dfg.insts[call_inst] {
1158 ir::InstructionData::TryCall {
1159 opcode: ir::Opcode::TryCall,
1160 args: _,
1161 func_ref: _,
1162 exception,
1163 } => {
1164 let normal_return = func.dfg.exception_tables[exception].normal_return();
1165 let normal_return_block = normal_return.block(&func.dfg.value_lists);
1166
1167 {
1169 let normal_return_args = normal_return.args(&func.dfg.value_lists);
1170 if normal_return_args.len() == callee.signature.returns.len()
1171 && normal_return_args.enumerate().all(|(i, arg)| {
1172 let i = u32::try_from(i).unwrap();
1173 arg == ir::BlockArg::TryCallRet(i)
1174 })
1175 {
1176 return Some(normal_return_block);
1177 }
1178 }
1179
1180 let return_block = func.dfg.blocks.add();
1184 func.layout.insert_block(return_block, normal_return_block);
1185
1186 let return_block_params = callee
1187 .signature
1188 .returns
1189 .iter()
1190 .map(|abi| func.dfg.append_block_param(return_block, abi.value_type))
1191 .collect::<SmallValueVec>();
1192
1193 let normal_return_args = func.dfg.exception_tables[exception]
1194 .normal_return()
1195 .args(&func.dfg.value_lists)
1196 .collect::<SmallBlockArgVec>();
1197 let jump_args = normal_return_args
1198 .into_iter()
1199 .map(|arg| match arg {
1200 ir::BlockArg::Value(value) => ir::BlockArg::Value(value),
1201 ir::BlockArg::TryCallRet(i) => {
1202 let i = usize::try_from(i).unwrap();
1203 ir::BlockArg::Value(return_block_params[i])
1204 }
1205 ir::BlockArg::TryCallExn(_) => {
1206 unreachable!("normal-return edges cannot use exceptional results")
1207 }
1208 })
1209 .collect::<SmallBlockArgVec>();
1210
1211 let mut cursor = FuncCursor::new(func);
1212 cursor.goto_first_insertion_point(return_block);
1213 cursor.ins().jump(normal_return_block, &jump_args);
1214
1215 Some(return_block)
1216 }
1217 _ => None,
1218 })
1219}
1220
1221fn replace_call_with_jump(
1229 allocs: &mut InliningAllocs,
1230 func: &mut ir::Function,
1231 call_inst: ir::Inst,
1232 callee: &ir::Function,
1233 entity_map: &EntityMap,
1234) -> Option<ir::UserStackMapEntryVec> {
1235 trace!("Replacing `call` with `jump`");
1236 trace!(
1237 " --> call instruction: {call_inst:?}: {}",
1238 func.dfg.display_inst(call_inst)
1239 );
1240
1241 let callee_entry_block = callee
1242 .layout
1243 .entry_block()
1244 .expect("callee function should have an entry block");
1245 let callee_param_values = callee.dfg.block_params(callee_entry_block);
1246 let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied());
1247 debug_assert_eq!(callee_param_values.len(), caller_arg_values.len());
1248 debug_assert_eq!(callee_param_values.len(), callee.signature.params.len());
1249 for (abi, (callee_param_value, caller_arg_value)) in callee
1250 .signature
1251 .params
1252 .iter()
1253 .zip(callee_param_values.into_iter().zip(caller_arg_values))
1254 {
1255 debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value));
1256 debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value));
1257 allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value);
1258 }
1259
1260 let inlined_entry_block = entity_map.inlined_block(callee_entry_block);
1267 func.replace(call_inst).jump(inlined_entry_block, &[]);
1268 trace!(
1269 " --> replaced with jump instruction: {call_inst:?}: {}",
1270 func.dfg.display_inst(call_inst)
1271 );
1272
1273 let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst);
1274 stack_map_entries
1275}
1276
1277#[derive(Default)]
1280struct EntityMap {
1281 block_offset: Option<u32>,
1294 global_value_offset: Option<u32>,
1295 sig_ref_offset: Option<u32>,
1296 func_ref_offset: Option<u32>,
1297 stack_slot_offset: Option<u32>,
1298 dynamic_type_offset: Option<u32>,
1299 dynamic_stack_slot_offset: Option<u32>,
1300 immediate_offset: Option<u32>,
1301}
1302
1303impl EntityMap {
1304 fn inlined_block(&self, callee_block: ir::Block) -> ir::Block {
1305 let offset = self
1306 .block_offset
1307 .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`");
1308 ir::Block::from_u32(offset + callee_block.as_u32())
1309 }
1310
1311 fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> {
1312 let start = self.block_offset.expect(
1313 "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`",
1314 );
1315
1316 let end = func.dfg.blocks.len();
1317 let end = u32::try_from(end).unwrap();
1318
1319 (start..end).map(|i| ir::Block::from_u32(i))
1320 }
1321
1322 fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue {
1323 let offset = self
1324 .global_value_offset
1325 .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`");
1326 ir::GlobalValue::from_u32(offset + callee_global_value.as_u32())
1327 }
1328
1329 fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef {
1330 let offset = self.sig_ref_offset.expect(
1331 "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`",
1332 );
1333 ir::SigRef::from_u32(offset + callee_sig_ref.as_u32())
1334 }
1335
1336 fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef {
1337 let offset = self.func_ref_offset.expect(
1338 "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`",
1339 );
1340 ir::FuncRef::from_u32(offset + callee_func_ref.as_u32())
1341 }
1342
1343 fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot {
1344 let offset = self.stack_slot_offset.expect(
1345 "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`",
1346 );
1347 ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32())
1348 }
1349
1350 fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType {
1351 let offset = self.dynamic_type_offset.expect(
1352 "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`",
1353 );
1354 ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32())
1355 }
1356
1357 fn inlined_dynamic_stack_slot(
1358 &self,
1359 callee_dynamic_stack_slot: ir::DynamicStackSlot,
1360 ) -> ir::DynamicStackSlot {
1361 let offset = self.dynamic_stack_slot_offset.expect(
1362 "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`",
1363 );
1364 ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32())
1365 }
1366
1367 fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate {
1368 let offset = self.immediate_offset.expect(
1369 "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`",
1370 );
1371 ir::Immediate::from_u32(offset + callee_immediate.as_u32())
1372 }
1373}
1374
1375fn create_entities(
1379 allocs: &mut InliningAllocs,
1380 func: &mut ir::Function,
1381 callee: &ir::Function,
1382) -> CodegenResult<EntityMap> {
1383 let mut entity_map = EntityMap::default();
1384
1385 entity_map.block_offset = Some(create_blocks(allocs, func, callee));
1386 create_user_external_name_refs(allocs, func, callee);
1387 entity_map.global_value_offset = Some(create_global_values(allocs, func, callee)?);
1388 entity_map.sig_ref_offset = Some(create_sig_refs(func, callee));
1389 entity_map.func_ref_offset = Some(create_func_refs(allocs, func, callee, &entity_map));
1390 entity_map.stack_slot_offset = Some(create_stack_slots(func, callee));
1391 entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map));
1392 entity_map.dynamic_stack_slot_offset =
1393 Some(create_dynamic_stack_slots(func, callee, &entity_map));
1394 entity_map.immediate_offset = Some(create_immediates(func, callee));
1395
1396 create_constants(allocs, func, callee);
1400
1401 Ok(entity_map)
1402}
1403
1404fn create_blocks(
1406 allocs: &mut InliningAllocs,
1407 func: &mut ir::Function,
1408 callee: &ir::Function,
1409) -> u32 {
1410 let offset = func.dfg.blocks.len();
1411 let offset = u32::try_from(offset).unwrap();
1412
1413 func.dfg.blocks.reserve(callee.dfg.blocks.len());
1414 for callee_block in callee.dfg.blocks.iter() {
1415 let caller_block = func.dfg.blocks.add();
1416 trace!("Callee {callee_block:?} = inlined {caller_block:?}");
1417
1418 if callee.layout.is_cold(callee_block) {
1419 func.layout.set_cold(caller_block);
1420 }
1421
1422 if callee.layout.entry_block() != Some(callee_block) {
1426 for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) {
1427 let ty = callee.dfg.value_type(*callee_param);
1428 let caller_param = func.dfg.append_block_param(caller_block, ty);
1429
1430 allocs.set_inlined_value(callee, *callee_param, caller_param);
1431 }
1432 }
1433 }
1434
1435 offset
1436}
1437
1438fn create_global_values(
1440 allocs: &InliningAllocs,
1441 func: &mut ir::Function,
1442 callee: &ir::Function,
1443) -> CodegenResult<u32> {
1444 let gv_offset = func.global_values.len();
1445 let gv_offset = u32::try_from(gv_offset).unwrap();
1446
1447 func.global_values.reserve(callee.global_values.len());
1448 for gv in callee.global_values.values() {
1449 let remapped_flags = match gv {
1452 ir::GlobalValueData::Load { flags, .. } => {
1453 let mut flags_data = callee.dfg.mem_flags[*flags];
1454 if let Some(callee_region) = flags_data.alias_region() {
1456 let region_data = callee.dfg.alias_regions[callee_region].clone();
1457 let caller_region = func.dfg.alias_regions.insert(region_data);
1458 flags_data.set_alias_region(Some(caller_region));
1459 }
1460 Some(
1461 func.dfg
1462 .mem_flags
1463 .insert(flags_data)
1464 .map_err(|_| crate::result::CodegenError::ImplLimitExceeded)?,
1465 )
1466 }
1467 _ => None,
1468 };
1469 func.global_values.push(match gv {
1470 ir::GlobalValueData::Load {
1473 base,
1474 offset,
1475 global_type,
1476 flags: _,
1477 } => ir::GlobalValueData::Load {
1478 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1479 offset: *offset,
1480 global_type: *global_type,
1481 flags: remapped_flags.unwrap(),
1482 },
1483 ir::GlobalValueData::IAddImm {
1484 base,
1485 offset,
1486 global_type,
1487 } => ir::GlobalValueData::IAddImm {
1488 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1489 offset: *offset,
1490 global_type: *global_type,
1491 },
1492
1493 ir::GlobalValueData::Symbol {
1496 name,
1497 offset,
1498 colocated,
1499 tls,
1500 } => ir::GlobalValueData::Symbol {
1501 name: allocs.inlined_external_name(name),
1502 offset: *offset,
1503 colocated: *colocated,
1504 tls: *tls,
1505 },
1506
1507 ir::GlobalValueData::VMContext | ir::GlobalValueData::DynScaleTargetConst { .. } => {
1510 gv.clone()
1511 }
1512 });
1513 }
1514
1515 Ok(gv_offset)
1516}
1517
1518fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1520 let offset = func.dfg.signatures.len();
1521 let offset = u32::try_from(offset).unwrap();
1522
1523 func.dfg.signatures.reserve(callee.dfg.signatures.len());
1524 for sig in callee.dfg.signatures.values() {
1525 func.dfg.signatures.push(sig.clone());
1526 }
1527
1528 offset
1529}
1530
1531fn create_user_external_name_refs(
1532 allocs: &mut InliningAllocs,
1533 func: &mut ir::Function,
1534 callee: &ir::Function,
1535) {
1536 for (callee_named_func_ref, name) in callee.params.user_named_funcs().iter() {
1537 let caller_named_func_ref = func.declare_imported_user_function(name.clone());
1538 allocs.user_external_name_refs[callee_named_func_ref] = Some(caller_named_func_ref).into();
1539 }
1540}
1541
1542fn create_func_refs(
1544 allocs: &InliningAllocs,
1545 func: &mut ir::Function,
1546 callee: &ir::Function,
1547 entity_map: &EntityMap,
1548) -> u32 {
1549 let offset = func.dfg.ext_funcs.len();
1550 let offset = u32::try_from(offset).unwrap();
1551
1552 func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len());
1553 for ir::ExtFuncData {
1554 name,
1555 signature,
1556 colocated,
1557 patchable,
1558 } in callee.dfg.ext_funcs.values()
1559 {
1560 func.dfg.ext_funcs.push(ir::ExtFuncData {
1561 name: allocs.inlined_external_name(name),
1562 signature: entity_map.inlined_sig_ref(*signature),
1563 colocated: *colocated,
1564 patchable: *patchable,
1565 });
1566 }
1567
1568 offset
1569}
1570
1571fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1573 let offset = func.sized_stack_slots.len();
1574 let offset = u32::try_from(offset).unwrap();
1575
1576 func.sized_stack_slots
1577 .reserve(callee.sized_stack_slots.len());
1578 for slot in callee.sized_stack_slots.values() {
1579 func.sized_stack_slots.push(slot.clone());
1580 }
1581
1582 offset
1583}
1584
1585fn create_dynamic_types(
1587 func: &mut ir::Function,
1588 callee: &ir::Function,
1589 entity_map: &EntityMap,
1590) -> u32 {
1591 let offset = func.dfg.dynamic_types.len();
1592 let offset = u32::try_from(offset).unwrap();
1593
1594 func.dfg
1595 .dynamic_types
1596 .reserve(callee.dfg.dynamic_types.len());
1597 for ir::DynamicTypeData {
1598 base_vector_ty,
1599 dynamic_scale,
1600 } in callee.dfg.dynamic_types.values()
1601 {
1602 func.dfg.dynamic_types.push(ir::DynamicTypeData {
1603 base_vector_ty: *base_vector_ty,
1604 dynamic_scale: entity_map.inlined_global_value(*dynamic_scale),
1605 });
1606 }
1607
1608 offset
1609}
1610
1611fn create_dynamic_stack_slots(
1613 func: &mut ir::Function,
1614 callee: &ir::Function,
1615 entity_map: &EntityMap,
1616) -> u32 {
1617 let offset = func.dynamic_stack_slots.len();
1618 let offset = u32::try_from(offset).unwrap();
1619
1620 func.dynamic_stack_slots
1621 .reserve(callee.dynamic_stack_slots.len());
1622 for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() {
1623 func.dynamic_stack_slots.push(ir::DynamicStackSlotData {
1624 kind: *kind,
1625 dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty),
1626 });
1627 }
1628
1629 offset
1630}
1631
1632fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1634 let offset = func.dfg.immediates.len();
1635 let offset = u32::try_from(offset).unwrap();
1636
1637 func.dfg.immediates.reserve(callee.dfg.immediates.len());
1638 for imm in callee.dfg.immediates.values() {
1639 func.dfg.immediates.push(imm.clone());
1640 }
1641
1642 offset
1643}
1644
1645fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) {
1647 for (callee_constant, data) in callee.dfg.constants.iter() {
1648 let inlined_constant = func.dfg.constants.insert(data.clone());
1649 allocs.constants[*callee_constant] = Some(inlined_constant).into();
1650 }
1651}