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 set_inlined_value(
319 &mut self,
320 callee: &ir::Function,
321 callee_val: ir::Value,
322 inlined_val: ir::Value,
323 ) {
324 trace!(" --> callee {callee_val:?} = inlined {inlined_val:?}");
325 debug_assert!(self.values[callee_val].is_none());
326 let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
327 debug_assert!(self.values[resolved_callee_val].is_none());
328 self.values[resolved_callee_val] = Some(inlined_val).into();
329 }
330
331 fn get_inlined_value(&self, callee: &ir::Function, callee_val: ir::Value) -> Option<ir::Value> {
332 let resolved_callee_val = callee.dfg.resolve_aliases(callee_val);
333 self.values[resolved_callee_val].expand()
334 }
335}
336
337fn inline_one(
341 allocs: &mut InliningAllocs,
342 func: &mut ir::Function,
343 callee_func_ref: ir::FuncRef,
344 call_block: ir::Block,
345 call_inst: ir::Inst,
346 call_opcode: ir::Opcode,
347 callee: &ir::Function,
348 call_exception_table: Option<ir::ExceptionTable>,
349) -> CodegenResult<ir::Block> {
350 trace!(
351 "Inlining call {call_inst:?}: {}\n\
352 with callee = {callee:?}",
353 func.dfg.display_inst(call_inst)
354 );
355
356 let expected_callee_sig = func.dfg.ext_funcs[callee_func_ref].signature;
358 let expected_callee_sig = &func.dfg.signatures[expected_callee_sig];
359 assert_eq!(expected_callee_sig, &callee.signature);
360
361 allocs.reset(callee);
362
363 let entity_map = create_entities(allocs, func, callee)?;
366
367 let return_block = split_off_return_block(func, call_inst, call_opcode, callee);
370 let call_stack_map = replace_call_with_jump(allocs, func, call_inst, callee, &entity_map);
371
372 let mut last_inlined_block = inline_block_layout(func, call_block, callee, &entity_map);
376
377 let call_debug_tags = func.debug_tags.get(call_inst).to_vec();
382 func.debug_tags.set(call_inst, []);
383
384 for callee_block in Dfs::new().pre_order_iter(callee) {
390 let inlined_block = entity_map.inlined_block(callee_block);
391 trace!(
392 "Processing instructions in callee block {callee_block:?} (inlined block {inlined_block:?}"
393 );
394
395 let mut next_callee_inst = callee.layout.first_inst(callee_block);
396 while let Some(callee_inst) = next_callee_inst {
397 trace!(
398 "Processing callee instruction {callee_inst:?}: {}",
399 callee.dfg.display_inst(callee_inst)
400 );
401
402 let mut inst_remapper = InliningInstRemapper {
405 allocs: &allocs,
406 func,
407 callee,
408 entity_map: &entity_map,
409 error: None,
410 };
411 let inlined_inst_data = callee.dfg.insts[callee_inst].map(&mut inst_remapper);
412 if let Some(err) = inst_remapper.error.take() {
413 return Err(err);
414 }
415 let inlined_inst = func.dfg.make_inst(inlined_inst_data);
416 func.layout.append_inst(inlined_inst, inlined_block);
417
418 let debug_tags = callee.debug_tags.get(callee_inst);
421 if !debug_tags.is_empty() {
428 let tags = call_debug_tags
429 .iter()
430 .cloned()
431 .chain(debug_tags.iter().map(|tag| match *tag {
432 DebugTag::User(value) => DebugTag::User(value),
433 DebugTag::StackSlot(slot) => {
434 DebugTag::StackSlot(entity_map.inlined_stack_slot(slot))
435 }
436 }))
437 .collect::<SmallVec<[_; 4]>>();
438 func.debug_tags.set(inlined_inst, tags);
439 }
440
441 let opcode = callee.dfg.insts[callee_inst].opcode();
442 if opcode.is_return() {
443 if let Some(return_block) = return_block {
448 fixup_inst_that_returns(
449 allocs,
450 func,
451 callee,
452 &entity_map,
453 call_opcode,
454 inlined_inst,
455 callee_inst,
456 return_block,
457 call_stack_map.as_ref().map(|es| &**es),
458 );
459 } else {
460 debug_assert_eq!(call_opcode, ir::Opcode::ReturnCall);
465 }
466 } else {
467 let ctrl_typevar = callee.dfg.ctrl_typevar(callee_inst);
469 func.dfg.make_inst_results(inlined_inst, ctrl_typevar);
470
471 let callee_results = callee.dfg.inst_results(callee_inst);
473 let inlined_results = func.dfg.inst_results(inlined_inst);
474 debug_assert_eq!(callee_results.len(), inlined_results.len());
475 for (callee_val, inlined_val) in callee_results.iter().zip(inlined_results) {
476 allocs.set_inlined_value(callee, *callee_val, *inlined_val);
477 }
478
479 if opcode.is_call() {
480 append_stack_map_entries(
481 func,
482 callee,
483 &entity_map,
484 call_stack_map.as_deref(),
485 inlined_inst,
486 callee_inst,
487 );
488
489 debug_assert_eq!(
502 call_opcode == ir::Opcode::TryCall,
503 call_exception_table.is_some()
504 );
505 if call_opcode == ir::Opcode::TryCall {
506 allocs
507 .calls_needing_exception_table_fixup
508 .push(inlined_inst);
509 }
510 }
511 }
512
513 trace!(
514 " --> inserted inlined instruction {inlined_inst:?}: {}",
515 func.dfg.display_inst(inlined_inst)
516 );
517
518 next_callee_inst = callee.layout.next_inst(callee_inst);
519 }
520 }
521
522 for block in entity_map.iter_inlined_blocks(func) {
529 if func.layout.is_block_inserted(block) && func.layout.first_inst(block).is_none() {
530 log::trace!("removing unreachable inlined block from layout: {block}");
531
532 if block == last_inlined_block {
536 last_inlined_block = func.layout.prev_block(last_inlined_block).expect(
537 "there will always at least be the block that contained the call we are \
538 inlining",
539 );
540 }
541
542 func.layout.remove_block(block);
543 }
544 }
545
546 debug_assert!(
557 allocs.calls_needing_exception_table_fixup.is_empty() || call_exception_table.is_some()
558 );
559 debug_assert_eq!(
560 call_opcode == ir::Opcode::TryCall,
561 call_exception_table.is_some()
562 );
563 if let Some(call_exception_table) = call_exception_table {
564 fixup_inlined_call_exception_tables(allocs, func, call_exception_table);
565 }
566
567 debug_assert!(
568 func.layout.is_block_inserted(last_inlined_block),
569 "last_inlined_block={last_inlined_block} should be inserted in the layout"
570 );
571 Ok(last_inlined_block)
572}
573
574fn append_stack_map_entries(
577 func: &mut ir::Function,
578 callee: &ir::Function,
579 entity_map: &EntityMap,
580 call_stack_map: Option<&[ir::UserStackMapEntry]>,
581 inlined_inst: ir::Inst,
582 callee_inst: ir::Inst,
583) {
584 func.dfg.append_user_stack_map_entries(
588 inlined_inst,
589 call_stack_map
590 .iter()
591 .flat_map(|entries| entries.iter().cloned()),
592 );
593
594 func.dfg.append_user_stack_map_entries(
598 inlined_inst,
599 callee
600 .dfg
601 .user_stack_map_entries(callee_inst)
602 .iter()
603 .flat_map(|entries| entries.iter())
604 .map(|entry| ir::UserStackMapEntry {
605 ty: entry.ty,
606 slot: entity_map.inlined_stack_slot(entry.slot),
607 offset: entry.offset,
608 }),
609 );
610}
611
612fn fixup_inlined_call_exception_tables(
616 allocs: &mut InliningAllocs,
617 func: &mut ir::Function,
618 call_exception_table: ir::ExceptionTable,
619) {
620 let split_block_for_new_try_call = |func: &mut ir::Function, inst: ir::Inst| -> ir::Block {
623 debug_assert!(func.dfg.insts[inst].opcode().is_call());
624 debug_assert!(!func.dfg.insts[inst].opcode().is_terminator());
625
626 let next_inst = func
628 .layout
629 .next_inst(inst)
630 .expect("inst is not a terminator, should have a successor");
631 let new_block = func.dfg.blocks.add();
632 func.layout.split_block(new_block, next_inst);
633
634 let old_results = SmallValueVec::from_iter(func.dfg.inst_results(inst).iter().copied());
639 func.dfg.detach_inst_results(inst);
640 for old_result in old_results {
641 let ty = func.dfg.value_type(old_result);
642 let new_block_param = func.dfg.append_block_param(new_block, ty);
643 func.dfg.change_to_alias(old_result, new_block_param);
644 }
645
646 new_block
647 };
648
649 let clone_exception_table_for_this_call = |func: &mut ir::Function,
652 signature: ir::SigRef,
653 new_block: ir::Block|
654 -> ir::ExceptionTable {
655 let mut exception = func.stencil.dfg.exception_tables[call_exception_table]
656 .deep_clone(&mut func.stencil.dfg.value_lists);
657
658 *exception.signature_mut() = signature;
659
660 let returns_len = func.dfg.signatures[signature].returns.len();
661 let returns_len = u32::try_from(returns_len).unwrap();
662
663 *exception.normal_return_mut() = ir::BlockCall::new(
664 new_block,
665 (0..returns_len).map(|i| ir::BlockArg::TryCallRet(i)),
666 &mut func.dfg.value_lists,
667 );
668
669 func.dfg.exception_tables.push(exception)
670 };
671
672 for inst in allocs.calls_needing_exception_table_fixup.drain(..) {
673 debug_assert!(func.dfg.insts[inst].opcode().is_call());
674 debug_assert!(!func.dfg.insts[inst].opcode().is_return());
675 match func.dfg.insts[inst] {
676 ir::InstructionData::Call {
689 opcode: ir::Opcode::Call,
690 args,
691 func_ref,
692 } => {
693 let new_block = split_block_for_new_try_call(func, inst);
694 let signature = func.dfg.ext_funcs[func_ref].signature;
695 let exception = clone_exception_table_for_this_call(func, signature, new_block);
696 func.dfg.insts[inst] = ir::InstructionData::TryCall {
697 opcode: ir::Opcode::TryCall,
698 args,
699 func_ref,
700 exception,
701 };
702 }
703
704 ir::InstructionData::CallIndirect {
717 opcode: ir::Opcode::CallIndirect,
718 args,
719 sig_ref,
720 } => {
721 let new_block = split_block_for_new_try_call(func, inst);
722 let exception = clone_exception_table_for_this_call(func, sig_ref, new_block);
723 func.dfg.insts[inst] = ir::InstructionData::TryCallIndirect {
724 opcode: ir::Opcode::TryCallIndirect,
725 args,
726 exception,
727 };
728 }
729
730 ir::InstructionData::TryCall {
733 opcode: ir::Opcode::TryCall,
734 exception,
735 ..
736 }
737 | ir::InstructionData::TryCallIndirect {
738 opcode: ir::Opcode::TryCallIndirect,
739 exception,
740 ..
741 } => {
742 let sig = func.dfg.exception_tables[exception].signature();
749 let normal_return = *func.dfg.exception_tables[exception].normal_return();
750 let exception_data = ExceptionTableData::new(
751 sig,
752 normal_return,
753 func.dfg.exception_tables[exception]
754 .items()
755 .chain(func.dfg.exception_tables[call_exception_table].items()),
756 )
757 .deep_clone(&mut func.dfg.value_lists);
758
759 func.dfg.exception_tables[exception] = exception_data;
760 }
761
762 otherwise => unreachable!("unknown non-return call instruction: {otherwise:?}"),
763 }
764 }
765}
766
767fn fixup_inst_that_returns(
772 allocs: &mut InliningAllocs,
773 func: &mut ir::Function,
774 callee: &ir::Function,
775 entity_map: &EntityMap,
776 call_opcode: ir::Opcode,
777 inlined_inst: ir::Inst,
778 callee_inst: ir::Inst,
779 return_block: ir::Block,
780 call_stack_map: Option<&[ir::UserStackMapEntry]>,
781) {
782 debug_assert!(func.dfg.insts[inlined_inst].opcode().is_return());
783 match func.dfg.insts[inlined_inst] {
784 ir::InstructionData::MultiAry {
790 opcode: ir::Opcode::Return,
791 args,
792 } => {
793 let rets = SmallBlockArgVec::from_iter(
794 args.as_slice(&func.dfg.value_lists)
795 .iter()
796 .copied()
797 .map(|v| v.into()),
798 );
799 func.replace(inlined_inst).jump(return_block, &rets);
800 }
801
802 ir::InstructionData::Call {
809 opcode: ir::Opcode::ReturnCall,
810 args,
811 func_ref,
812 } => {
813 func.dfg.insts[inlined_inst] = ir::InstructionData::Call {
814 opcode: ir::Opcode::Call,
815 args,
816 func_ref,
817 };
818 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
819
820 append_stack_map_entries(
821 func,
822 callee,
823 &entity_map,
824 call_stack_map,
825 inlined_inst,
826 callee_inst,
827 );
828
829 let rets = SmallBlockArgVec::from_iter(
830 func.dfg
831 .inst_results(inlined_inst)
832 .iter()
833 .copied()
834 .map(|v| v.into()),
835 );
836 let mut cursor = FuncCursor::new(func);
837 cursor.goto_after_inst(inlined_inst);
838 cursor.ins().jump(return_block, &rets);
839
840 if call_opcode == ir::Opcode::TryCall {
841 allocs
842 .calls_needing_exception_table_fixup
843 .push(inlined_inst);
844 }
845 }
846
847 ir::InstructionData::CallIndirect {
854 opcode: ir::Opcode::ReturnCallIndirect,
855 args,
856 sig_ref,
857 } => {
858 func.dfg.insts[inlined_inst] = ir::InstructionData::CallIndirect {
859 opcode: ir::Opcode::CallIndirect,
860 args,
861 sig_ref,
862 };
863 func.dfg.make_inst_results(inlined_inst, ir::types::INVALID);
864
865 append_stack_map_entries(
866 func,
867 callee,
868 &entity_map,
869 call_stack_map,
870 inlined_inst,
871 callee_inst,
872 );
873
874 let rets = SmallBlockArgVec::from_iter(
875 func.dfg
876 .inst_results(inlined_inst)
877 .iter()
878 .copied()
879 .map(|v| v.into()),
880 );
881 let mut cursor = FuncCursor::new(func);
882 cursor.goto_after_inst(inlined_inst);
883 cursor.ins().jump(return_block, &rets);
884
885 if call_opcode == ir::Opcode::TryCall {
886 allocs
887 .calls_needing_exception_table_fixup
888 .push(inlined_inst);
889 }
890 }
891
892 inst_data => unreachable!(
893 "should have handled all `is_return() == true` instructions above; \
894 got {inst_data:?}"
895 ),
896 }
897}
898
899struct InliningInstRemapper<'a> {
902 allocs: &'a InliningAllocs,
903 func: &'a mut ir::Function,
904 callee: &'a ir::Function,
905 entity_map: &'a EntityMap,
906 error: Option<crate::result::CodegenError>,
907}
908
909impl<'a> ir::instructions::InstructionMapper for InliningInstRemapper<'a> {
910 fn map_value(&mut self, value: ir::Value) -> ir::Value {
911 self.allocs.get_inlined_value(self.callee, value).expect(
912 "defs come before uses; we should have already inlined all values \
913 used by an instruction",
914 )
915 }
916
917 fn map_value_list(&mut self, value_list: ir::ValueList) -> ir::ValueList {
918 let mut inlined_list = ir::ValueList::new();
919 for callee_val in value_list.as_slice(&self.callee.dfg.value_lists) {
920 let inlined_val = self.map_value(*callee_val);
921 inlined_list.push(inlined_val, &mut self.func.dfg.value_lists);
922 }
923 inlined_list
924 }
925
926 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue {
927 self.entity_map.inlined_global_value(global_value)
928 }
929
930 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable {
931 let inlined_default =
932 self.map_block_call(self.callee.dfg.jump_tables[jump_table].default_block());
933 let inlined_table = self.callee.dfg.jump_tables[jump_table]
934 .as_slice()
935 .iter()
936 .map(|callee_block_call| self.map_block_call(*callee_block_call))
937 .collect::<SmallBlockCallVec>();
938 self.func
939 .dfg
940 .jump_tables
941 .push(ir::JumpTableData::new(inlined_default, &inlined_table))
942 }
943
944 fn map_exception_table(&mut self, exception_table: ir::ExceptionTable) -> ir::ExceptionTable {
945 let exception_table = &self.callee.dfg.exception_tables[exception_table];
946 let inlined_sig_ref = self.map_sig_ref(exception_table.signature());
947 let inlined_normal_return = self.map_block_call(*exception_table.normal_return());
948 let inlined_table = exception_table
949 .items()
950 .map(|item| match item {
951 ExceptionTableItem::Tag(tag, block_call) => {
952 ExceptionTableItem::Tag(tag, self.map_block_call(block_call))
953 }
954 ExceptionTableItem::Default(block_call) => {
955 ExceptionTableItem::Default(self.map_block_call(block_call))
956 }
957 ExceptionTableItem::Context(value) => {
958 ExceptionTableItem::Context(self.map_value(value))
959 }
960 })
961 .collect::<SmallVec<[_; 8]>>();
962 self.func
963 .dfg
964 .exception_tables
965 .push(ir::ExceptionTableData::new(
966 inlined_sig_ref,
967 inlined_normal_return,
968 inlined_table,
969 ))
970 }
971
972 fn map_block_call(&mut self, block_call: ir::BlockCall) -> ir::BlockCall {
973 let callee_block = block_call.block(&self.callee.dfg.value_lists);
974 let inlined_block = self.entity_map.inlined_block(callee_block);
975 let args = block_call
976 .args(&self.callee.dfg.value_lists)
977 .map(|arg| match arg {
978 ir::BlockArg::Value(value) => self.map_value(value).into(),
979 ir::BlockArg::TryCallRet(_) | ir::BlockArg::TryCallExn(_) => arg,
980 })
981 .collect::<SmallBlockArgVec>();
982 ir::BlockCall::new(inlined_block, args, &mut self.func.dfg.value_lists)
983 }
984
985 fn map_block(&mut self, block: ir::Block) -> ir::Block {
986 self.entity_map.inlined_block(block)
987 }
988
989 fn map_func_ref(&mut self, func_ref: ir::FuncRef) -> ir::FuncRef {
990 self.entity_map.inlined_func_ref(func_ref)
991 }
992
993 fn map_sig_ref(&mut self, sig_ref: ir::SigRef) -> ir::SigRef {
994 self.entity_map.inlined_sig_ref(sig_ref)
995 }
996
997 fn map_stack_slot(&mut self, stack_slot: ir::StackSlot) -> ir::StackSlot {
998 self.entity_map.inlined_stack_slot(stack_slot)
999 }
1000
1001 fn map_dynamic_stack_slot(
1002 &mut self,
1003 dynamic_stack_slot: ir::DynamicStackSlot,
1004 ) -> ir::DynamicStackSlot {
1005 self.entity_map
1006 .inlined_dynamic_stack_slot(dynamic_stack_slot)
1007 }
1008
1009 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant {
1010 self.allocs
1011 .constants
1012 .get(constant)
1013 .and_then(|o| o.expand())
1014 .expect("should have inlined all callee constants")
1015 }
1016
1017 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate {
1018 self.entity_map.inlined_immediate(immediate)
1019 }
1020 fn map_mem_flags(&mut self, flags: ir::MemFlags) -> ir::MemFlags {
1021 let mut flags_data = self.callee.dfg.mem_flags[flags];
1022 if let Some(callee_region) = flags_data.alias_region() {
1024 let region_data = self.callee.dfg.alias_regions[callee_region].clone();
1025 let caller_region = self.func.dfg.alias_regions.insert(region_data);
1026 flags_data.set_alias_region(Some(caller_region));
1027 }
1028 match self.func.dfg.mem_flags.insert(flags_data) {
1029 Ok(flags) => flags,
1030 Err(_) => {
1031 self.error = Some(crate::result::CodegenError::ImplLimitExceeded);
1032 self.func
1033 .dfg
1034 .mem_flags
1035 .insert(ir::MemFlagsData::trusted())
1036 .unwrap()
1037 }
1038 }
1039 }
1040}
1041
1042fn inline_block_layout(
1046 func: &mut ir::Function,
1047 call_block: ir::Block,
1048 callee: &ir::Function,
1049 entity_map: &EntityMap,
1050) -> ir::Block {
1051 debug_assert!(func.layout.is_block_inserted(call_block));
1052
1053 let mut prev_inlined_block = call_block;
1056 let mut next_callee_block = callee.layout.entry_block();
1057 while let Some(callee_block) = next_callee_block {
1058 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1059
1060 let inlined_block = entity_map.inlined_block(callee_block);
1061 func.layout
1062 .insert_block_after(inlined_block, prev_inlined_block);
1063
1064 prev_inlined_block = inlined_block;
1065 next_callee_block = callee.layout.next_block(callee_block);
1066 }
1067
1068 debug_assert!(func.layout.is_block_inserted(prev_inlined_block));
1069 prev_inlined_block
1070}
1071
1072fn split_off_return_block(
1078 func: &mut ir::Function,
1079 call_inst: ir::Inst,
1080 opcode: ir::Opcode,
1081 callee: &ir::Function,
1082) -> Option<ir::Block> {
1083 let return_block = func.layout.next_inst(call_inst).map(|next_inst| {
1086 let return_block = func.dfg.blocks.add();
1087 func.layout.split_block(return_block, next_inst);
1088
1089 let old_results =
1092 SmallValueVec::from_iter(func.dfg.inst_results(call_inst).iter().copied());
1093 debug_assert_eq!(old_results.len(), callee.signature.returns.len());
1094 func.dfg.detach_inst_results(call_inst);
1095 for (abi, old_val) in callee.signature.returns.iter().zip(old_results) {
1096 debug_assert_eq!(abi.value_type, func.dfg.value_type(old_val));
1097 let ret_param = func.dfg.append_block_param(return_block, abi.value_type);
1098 func.dfg.change_to_alias(old_val, ret_param);
1099 }
1100
1101 return_block
1102 });
1103
1104 debug_assert_eq!(
1135 return_block.is_none(),
1136 opcode == ir::Opcode::ReturnCall || opcode == ir::Opcode::TryCall,
1137 );
1138 return_block.or_else(|| match func.dfg.insts[call_inst] {
1139 ir::InstructionData::TryCall {
1140 opcode: ir::Opcode::TryCall,
1141 args: _,
1142 func_ref: _,
1143 exception,
1144 } => {
1145 let normal_return = func.dfg.exception_tables[exception].normal_return();
1146 let normal_return_block = normal_return.block(&func.dfg.value_lists);
1147
1148 {
1150 let normal_return_args = normal_return.args(&func.dfg.value_lists);
1151 if normal_return_args.len() == callee.signature.returns.len()
1152 && normal_return_args.enumerate().all(|(i, arg)| {
1153 let i = u32::try_from(i).unwrap();
1154 arg == ir::BlockArg::TryCallRet(i)
1155 })
1156 {
1157 return Some(normal_return_block);
1158 }
1159 }
1160
1161 let return_block = func.dfg.blocks.add();
1165 func.layout.insert_block(return_block, normal_return_block);
1166
1167 let return_block_params = callee
1168 .signature
1169 .returns
1170 .iter()
1171 .map(|abi| func.dfg.append_block_param(return_block, abi.value_type))
1172 .collect::<SmallValueVec>();
1173
1174 let normal_return_args = func.dfg.exception_tables[exception]
1175 .normal_return()
1176 .args(&func.dfg.value_lists)
1177 .collect::<SmallBlockArgVec>();
1178 let jump_args = normal_return_args
1179 .into_iter()
1180 .map(|arg| match arg {
1181 ir::BlockArg::Value(value) => ir::BlockArg::Value(value),
1182 ir::BlockArg::TryCallRet(i) => {
1183 let i = usize::try_from(i).unwrap();
1184 ir::BlockArg::Value(return_block_params[i])
1185 }
1186 ir::BlockArg::TryCallExn(_) => {
1187 unreachable!("normal-return edges cannot use exceptional results")
1188 }
1189 })
1190 .collect::<SmallBlockArgVec>();
1191
1192 let mut cursor = FuncCursor::new(func);
1193 cursor.goto_first_insertion_point(return_block);
1194 cursor.ins().jump(normal_return_block, &jump_args);
1195
1196 Some(return_block)
1197 }
1198 _ => None,
1199 })
1200}
1201
1202fn replace_call_with_jump(
1210 allocs: &mut InliningAllocs,
1211 func: &mut ir::Function,
1212 call_inst: ir::Inst,
1213 callee: &ir::Function,
1214 entity_map: &EntityMap,
1215) -> Option<ir::UserStackMapEntryVec> {
1216 trace!("Replacing `call` with `jump`");
1217 trace!(
1218 " --> call instruction: {call_inst:?}: {}",
1219 func.dfg.display_inst(call_inst)
1220 );
1221
1222 let callee_entry_block = callee
1223 .layout
1224 .entry_block()
1225 .expect("callee function should have an entry block");
1226 let callee_param_values = callee.dfg.block_params(callee_entry_block);
1227 let caller_arg_values = SmallValueVec::from_iter(func.dfg.inst_args(call_inst).iter().copied());
1228 debug_assert_eq!(callee_param_values.len(), caller_arg_values.len());
1229 debug_assert_eq!(callee_param_values.len(), callee.signature.params.len());
1230 for (abi, (callee_param_value, caller_arg_value)) in callee
1231 .signature
1232 .params
1233 .iter()
1234 .zip(callee_param_values.into_iter().zip(caller_arg_values))
1235 {
1236 debug_assert_eq!(abi.value_type, callee.dfg.value_type(*callee_param_value));
1237 debug_assert_eq!(abi.value_type, func.dfg.value_type(caller_arg_value));
1238 allocs.set_inlined_value(callee, *callee_param_value, caller_arg_value);
1239 }
1240
1241 let inlined_entry_block = entity_map.inlined_block(callee_entry_block);
1248 func.replace(call_inst).jump(inlined_entry_block, &[]);
1249 trace!(
1250 " --> replaced with jump instruction: {call_inst:?}: {}",
1251 func.dfg.display_inst(call_inst)
1252 );
1253
1254 let stack_map_entries = func.dfg.take_user_stack_map_entries(call_inst);
1255 stack_map_entries
1256}
1257
1258#[derive(Default)]
1261struct EntityMap {
1262 block_offset: Option<u32>,
1275 global_value_offset: Option<u32>,
1276 sig_ref_offset: Option<u32>,
1277 func_ref_offset: Option<u32>,
1278 stack_slot_offset: Option<u32>,
1279 dynamic_type_offset: Option<u32>,
1280 dynamic_stack_slot_offset: Option<u32>,
1281 immediate_offset: Option<u32>,
1282}
1283
1284impl EntityMap {
1285 fn inlined_block(&self, callee_block: ir::Block) -> ir::Block {
1286 let offset = self
1287 .block_offset
1288 .expect("must create inlined `ir::Block`s before calling `EntityMap::inlined_block`");
1289 ir::Block::from_u32(offset + callee_block.as_u32())
1290 }
1291
1292 fn iter_inlined_blocks(&self, func: &ir::Function) -> impl Iterator<Item = ir::Block> + use<> {
1293 let start = self.block_offset.expect(
1294 "must create inlined `ir::Block`s before calling `EntityMap::iter_inlined_blocks`",
1295 );
1296
1297 let end = func.dfg.blocks.len();
1298 let end = u32::try_from(end).unwrap();
1299
1300 (start..end).map(|i| ir::Block::from_u32(i))
1301 }
1302
1303 fn inlined_global_value(&self, callee_global_value: ir::GlobalValue) -> ir::GlobalValue {
1304 let offset = self
1305 .global_value_offset
1306 .expect("must create inlined `ir::GlobalValue`s before calling `EntityMap::inlined_global_value`");
1307 ir::GlobalValue::from_u32(offset + callee_global_value.as_u32())
1308 }
1309
1310 fn inlined_sig_ref(&self, callee_sig_ref: ir::SigRef) -> ir::SigRef {
1311 let offset = self.sig_ref_offset.expect(
1312 "must create inlined `ir::SigRef`s before calling `EntityMap::inlined_sig_ref`",
1313 );
1314 ir::SigRef::from_u32(offset + callee_sig_ref.as_u32())
1315 }
1316
1317 fn inlined_func_ref(&self, callee_func_ref: ir::FuncRef) -> ir::FuncRef {
1318 let offset = self.func_ref_offset.expect(
1319 "must create inlined `ir::FuncRef`s before calling `EntityMap::inlined_func_ref`",
1320 );
1321 ir::FuncRef::from_u32(offset + callee_func_ref.as_u32())
1322 }
1323
1324 fn inlined_stack_slot(&self, callee_stack_slot: ir::StackSlot) -> ir::StackSlot {
1325 let offset = self.stack_slot_offset.expect(
1326 "must create inlined `ir::StackSlot`s before calling `EntityMap::inlined_stack_slot`",
1327 );
1328 ir::StackSlot::from_u32(offset + callee_stack_slot.as_u32())
1329 }
1330
1331 fn inlined_dynamic_type(&self, callee_dynamic_type: ir::DynamicType) -> ir::DynamicType {
1332 let offset = self.dynamic_type_offset.expect(
1333 "must create inlined `ir::DynamicType`s before calling `EntityMap::inlined_dynamic_type`",
1334 );
1335 ir::DynamicType::from_u32(offset + callee_dynamic_type.as_u32())
1336 }
1337
1338 fn inlined_dynamic_stack_slot(
1339 &self,
1340 callee_dynamic_stack_slot: ir::DynamicStackSlot,
1341 ) -> ir::DynamicStackSlot {
1342 let offset = self.dynamic_stack_slot_offset.expect(
1343 "must create inlined `ir::DynamicStackSlot`s before calling `EntityMap::inlined_dynamic_stack_slot`",
1344 );
1345 ir::DynamicStackSlot::from_u32(offset + callee_dynamic_stack_slot.as_u32())
1346 }
1347
1348 fn inlined_immediate(&self, callee_immediate: ir::Immediate) -> ir::Immediate {
1349 let offset = self.immediate_offset.expect(
1350 "must create inlined `ir::Immediate`s before calling `EntityMap::inlined_immediate`",
1351 );
1352 ir::Immediate::from_u32(offset + callee_immediate.as_u32())
1353 }
1354}
1355
1356fn create_entities(
1360 allocs: &mut InliningAllocs,
1361 func: &mut ir::Function,
1362 callee: &ir::Function,
1363) -> CodegenResult<EntityMap> {
1364 let mut entity_map = EntityMap::default();
1365
1366 entity_map.block_offset = Some(create_blocks(allocs, func, callee));
1367 entity_map.global_value_offset = Some(create_global_values(func, callee)?);
1368 entity_map.sig_ref_offset = Some(create_sig_refs(func, callee));
1369 create_user_external_name_refs(allocs, func, callee);
1370 entity_map.func_ref_offset = Some(create_func_refs(allocs, func, callee, &entity_map));
1371 entity_map.stack_slot_offset = Some(create_stack_slots(func, callee));
1372 entity_map.dynamic_type_offset = Some(create_dynamic_types(func, callee, &entity_map));
1373 entity_map.dynamic_stack_slot_offset =
1374 Some(create_dynamic_stack_slots(func, callee, &entity_map));
1375 entity_map.immediate_offset = Some(create_immediates(func, callee));
1376
1377 create_constants(allocs, func, callee);
1381
1382 Ok(entity_map)
1383}
1384
1385fn create_blocks(
1387 allocs: &mut InliningAllocs,
1388 func: &mut ir::Function,
1389 callee: &ir::Function,
1390) -> u32 {
1391 let offset = func.dfg.blocks.len();
1392 let offset = u32::try_from(offset).unwrap();
1393
1394 func.dfg.blocks.reserve(callee.dfg.blocks.len());
1395 for callee_block in callee.dfg.blocks.iter() {
1396 let caller_block = func.dfg.blocks.add();
1397 trace!("Callee {callee_block:?} = inlined {caller_block:?}");
1398
1399 if callee.layout.is_cold(callee_block) {
1400 func.layout.set_cold(caller_block);
1401 }
1402
1403 if callee.layout.entry_block() != Some(callee_block) {
1407 for callee_param in callee.dfg.blocks[callee_block].params(&callee.dfg.value_lists) {
1408 let ty = callee.dfg.value_type(*callee_param);
1409 let caller_param = func.dfg.append_block_param(caller_block, ty);
1410
1411 allocs.set_inlined_value(callee, *callee_param, caller_param);
1412 }
1413 }
1414 }
1415
1416 offset
1417}
1418
1419fn create_global_values(func: &mut ir::Function, callee: &ir::Function) -> CodegenResult<u32> {
1421 let gv_offset = func.global_values.len();
1422 let gv_offset = u32::try_from(gv_offset).unwrap();
1423
1424 func.global_values.reserve(callee.global_values.len());
1425 for gv in callee.global_values.values() {
1426 let remapped_flags = match gv {
1429 ir::GlobalValueData::Load { flags, .. } => {
1430 let mut flags_data = callee.dfg.mem_flags[*flags];
1431 if let Some(callee_region) = flags_data.alias_region() {
1433 let region_data = callee.dfg.alias_regions[callee_region].clone();
1434 let caller_region = func.dfg.alias_regions.insert(region_data);
1435 flags_data.set_alias_region(Some(caller_region));
1436 }
1437 Some(
1438 func.dfg
1439 .mem_flags
1440 .insert(flags_data)
1441 .map_err(|_| crate::result::CodegenError::ImplLimitExceeded)?,
1442 )
1443 }
1444 _ => None,
1445 };
1446 func.global_values.push(match gv {
1447 ir::GlobalValueData::Load {
1450 base,
1451 offset,
1452 global_type,
1453 flags: _,
1454 } => ir::GlobalValueData::Load {
1455 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1456 offset: *offset,
1457 global_type: *global_type,
1458 flags: remapped_flags.unwrap(),
1459 },
1460 ir::GlobalValueData::IAddImm {
1461 base,
1462 offset,
1463 global_type,
1464 } => ir::GlobalValueData::IAddImm {
1465 base: ir::GlobalValue::from_u32(base.as_u32() + gv_offset),
1466 offset: *offset,
1467 global_type: *global_type,
1468 },
1469
1470 ir::GlobalValueData::VMContext
1473 | ir::GlobalValueData::Symbol { .. }
1474 | ir::GlobalValueData::DynScaleTargetConst { .. } => gv.clone(),
1475 });
1476 }
1477
1478 Ok(gv_offset)
1479}
1480
1481fn create_sig_refs(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1483 let offset = func.dfg.signatures.len();
1484 let offset = u32::try_from(offset).unwrap();
1485
1486 func.dfg.signatures.reserve(callee.dfg.signatures.len());
1487 for sig in callee.dfg.signatures.values() {
1488 func.dfg.signatures.push(sig.clone());
1489 }
1490
1491 offset
1492}
1493
1494fn create_user_external_name_refs(
1495 allocs: &mut InliningAllocs,
1496 func: &mut ir::Function,
1497 callee: &ir::Function,
1498) {
1499 for (callee_named_func_ref, name) in callee.params.user_named_funcs().iter() {
1500 let caller_named_func_ref = func.declare_imported_user_function(name.clone());
1501 allocs.user_external_name_refs[callee_named_func_ref] = Some(caller_named_func_ref).into();
1502 }
1503}
1504
1505fn create_func_refs(
1507 allocs: &InliningAllocs,
1508 func: &mut ir::Function,
1509 callee: &ir::Function,
1510 entity_map: &EntityMap,
1511) -> u32 {
1512 let offset = func.dfg.ext_funcs.len();
1513 let offset = u32::try_from(offset).unwrap();
1514
1515 func.dfg.ext_funcs.reserve(callee.dfg.ext_funcs.len());
1516 for ir::ExtFuncData {
1517 name,
1518 signature,
1519 colocated,
1520 patchable,
1521 } in callee.dfg.ext_funcs.values()
1522 {
1523 func.dfg.ext_funcs.push(ir::ExtFuncData {
1524 name: match name {
1525 ir::ExternalName::User(name_ref) => {
1526 ir::ExternalName::User(allocs.user_external_name_refs[*name_ref].expect(
1527 "should have translated all `ir::UserExternalNameRef`s before translating \
1528 `ir::FuncRef`s",
1529 ))
1530 }
1531 ir::ExternalName::TestCase(_)
1532 | ir::ExternalName::LibCall(_)
1533 | ir::ExternalName::KnownSymbol(_) => name.clone(),
1534 },
1535 signature: entity_map.inlined_sig_ref(*signature),
1536 colocated: *colocated,
1537 patchable: *patchable,
1538 });
1539 }
1540
1541 offset
1542}
1543
1544fn create_stack_slots(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1546 let offset = func.sized_stack_slots.len();
1547 let offset = u32::try_from(offset).unwrap();
1548
1549 func.sized_stack_slots
1550 .reserve(callee.sized_stack_slots.len());
1551 for slot in callee.sized_stack_slots.values() {
1552 func.sized_stack_slots.push(slot.clone());
1553 }
1554
1555 offset
1556}
1557
1558fn create_dynamic_types(
1560 func: &mut ir::Function,
1561 callee: &ir::Function,
1562 entity_map: &EntityMap,
1563) -> u32 {
1564 let offset = func.dynamic_stack_slots.len();
1565 let offset = u32::try_from(offset).unwrap();
1566
1567 func.dfg
1568 .dynamic_types
1569 .reserve(callee.dfg.dynamic_types.len());
1570 for ir::DynamicTypeData {
1571 base_vector_ty,
1572 dynamic_scale,
1573 } in callee.dfg.dynamic_types.values()
1574 {
1575 func.dfg.dynamic_types.push(ir::DynamicTypeData {
1576 base_vector_ty: *base_vector_ty,
1577 dynamic_scale: entity_map.inlined_global_value(*dynamic_scale),
1578 });
1579 }
1580
1581 offset
1582}
1583
1584fn create_dynamic_stack_slots(
1586 func: &mut ir::Function,
1587 callee: &ir::Function,
1588 entity_map: &EntityMap,
1589) -> u32 {
1590 let offset = func.dynamic_stack_slots.len();
1591 let offset = u32::try_from(offset).unwrap();
1592
1593 func.dynamic_stack_slots
1594 .reserve(callee.dynamic_stack_slots.len());
1595 for ir::DynamicStackSlotData { kind, dyn_ty } in callee.dynamic_stack_slots.values() {
1596 func.dynamic_stack_slots.push(ir::DynamicStackSlotData {
1597 kind: *kind,
1598 dyn_ty: entity_map.inlined_dynamic_type(*dyn_ty),
1599 });
1600 }
1601
1602 offset
1603}
1604
1605fn create_immediates(func: &mut ir::Function, callee: &ir::Function) -> u32 {
1607 let offset = func.dfg.immediates.len();
1608 let offset = u32::try_from(offset).unwrap();
1609
1610 func.dfg.immediates.reserve(callee.dfg.immediates.len());
1611 for imm in callee.dfg.immediates.values() {
1612 func.dfg.immediates.push(imm.clone());
1613 }
1614
1615 offset
1616}
1617
1618fn create_constants(allocs: &mut InliningAllocs, func: &mut ir::Function, callee: &ir::Function) {
1620 for (callee_constant, data) in callee.dfg.constants.iter() {
1621 let inlined_constant = func.dfg.constants.insert(data.clone());
1622 allocs.constants[*callee_constant] = Some(inlined_constant).into();
1623 }
1624}