@@ -94,6 +94,7 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
9494 header,
9595 debug_string_source : & mut module. debug_string_source ,
9696 annotations : & mut module. annotations ,
97+ types_global_values : & mut module. types_global_values ,
9798
9899 legal_globals,
99100
@@ -492,6 +493,7 @@ struct Inliner<'a, 'b> {
492493 header : & ' b mut ModuleHeader ,
493494 debug_string_source : & ' b mut Vec < Instruction > ,
494495 annotations : & ' b mut Vec < Instruction > ,
496+ types_global_values : & ' b mut Vec < Instruction > ,
495497
496498 legal_globals : FxHashMap < Word , LegalGlobal > ,
497499 functions_that_may_abort : FxHashSet < Word > ,
@@ -521,6 +523,29 @@ impl Inliner<'_, '_> {
521523 }
522524 }
523525
526+ fn ptr_ty ( & mut self , pointee : Word ) -> Word {
527+ // TODO: This is horribly slow, fix this
528+ let existing = self . types_global_values . iter ( ) . find ( |inst| {
529+ inst. class . opcode == Op :: TypePointer
530+ && inst. operands [ 0 ] . unwrap_storage_class ( ) == StorageClass :: Function
531+ && inst. operands [ 1 ] . unwrap_id_ref ( ) == pointee
532+ } ) ;
533+ if let Some ( existing) = existing {
534+ return existing. result_id . unwrap ( ) ;
535+ }
536+ let inst_id = self . id ( ) ;
537+ self . types_global_values . push ( Instruction :: new (
538+ Op :: TypePointer ,
539+ None ,
540+ Some ( inst_id) ,
541+ vec ! [
542+ Operand :: StorageClass ( StorageClass :: Function ) ,
543+ Operand :: IdRef ( pointee) ,
544+ ] ,
545+ ) ) ;
546+ inst_id
547+ }
548+
524549 fn inline_fn (
525550 & mut self ,
526551 function : & mut Function ,
@@ -597,19 +622,15 @@ impl Inliner<'_, '_> {
597622 . insert ( caller. def_id ( ) . unwrap ( ) ) ;
598623 }
599624
600- let mut maybe_call_result_phi = {
625+ let call_result_type = {
601626 let ty = call_inst. result_type . unwrap ( ) ;
602627 if ty == self . op_type_void_id {
603628 None
604629 } else {
605- Some ( Instruction :: new (
606- Op :: Phi ,
607- Some ( ty) ,
608- Some ( call_inst. result_id . unwrap ( ) ) ,
609- vec ! [ ] ,
610- ) )
630+ Some ( ty)
611631 }
612632 } ;
633+ let call_result_id = call_inst. result_id . unwrap ( ) ;
613634
614635 // Get the debug "source location" instruction that applies to the call.
615636 let custom_ext_inst_set_import = self . custom_ext_inst_set_import ;
@@ -646,12 +667,17 @@ impl Inliner<'_, '_> {
646667 } ) ;
647668 let mut rewrite_rules = callee_parameters. zip ( call_arguments) . collect ( ) ;
648669
670+ let return_variable = if call_result_type. is_some ( ) {
671+ Some ( self . id ( ) )
672+ } else {
673+ None
674+ } ;
649675 let return_jump = self . id ( ) ;
650676 // Rewrite OpReturns of the callee.
651677 let mut inlined_callee_blocks = self . get_inlined_blocks (
652678 callee,
653679 call_debug_src_loc_inst,
654- maybe_call_result_phi . as_mut ( ) ,
680+ return_variable ,
655681 return_jump,
656682 ) ;
657683 // Clone the IDs of the callee, because otherwise they'd be defined multiple times if the
@@ -660,55 +686,6 @@ impl Inliner<'_, '_> {
660686 apply_rewrite_rules ( & rewrite_rules, & mut inlined_callee_blocks) ;
661687 self . apply_rewrite_for_decorations ( & rewrite_rules) ;
662688
663- if let Some ( call_result_phi) = & mut maybe_call_result_phi {
664- // HACK(eddyb) new IDs should be generated earlier, to avoid pushing
665- // callee IDs to `call_result_phi.operands` only to rewrite them here.
666- for op in & mut call_result_phi. operands {
667- if let Some ( id) = op. id_ref_any_mut ( )
668- && let Some ( & rewrite) = rewrite_rules. get ( id)
669- {
670- * id = rewrite;
671- }
672- }
673-
674- // HACK(eddyb) this special-casing of the single-return case is
675- // really necessary for passes like `mem2reg` which are not capable
676- // of skipping through the extraneous `OpPhi`s on their own.
677- if let [ returned_value, _return_block] = & call_result_phi. operands [ ..] {
678- let call_result_id = call_result_phi. result_id . unwrap ( ) ;
679- let returned_value_id = returned_value. unwrap_id_ref ( ) ;
680-
681- maybe_call_result_phi = None ;
682-
683- // HACK(eddyb) this is a conservative approximation of all the
684- // instructions that could potentially reference the call result.
685- let reaching_insts = {
686- let ( pre_call_blocks, call_and_post_call_blocks) =
687- caller. blocks . split_at_mut ( block_idx) ;
688- ( pre_call_blocks. iter_mut ( ) . flat_map ( |block| {
689- block
690- . instructions
691- . iter_mut ( )
692- . take_while ( |inst| inst. class . opcode == Op :: Phi )
693- } ) )
694- . chain (
695- call_and_post_call_blocks
696- . iter_mut ( )
697- . flat_map ( |block| & mut block. instructions ) ,
698- )
699- } ;
700- for reaching_inst in reaching_insts {
701- for op in & mut reaching_inst. operands {
702- if let Some ( id) = op. id_ref_any_mut ( )
703- && * id == call_result_id
704- {
705- * id = returned_value_id;
706- }
707- }
708- }
709- }
710- }
711-
712689 // Split the block containing the `OpFunctionCall` into pre-call vs post-call.
713690 let pre_call_block_idx = block_idx;
714691 #[ expect( unused) ]
@@ -724,6 +701,18 @@ impl Inliner<'_, '_> {
724701 . unwrap ( ) ;
725702 assert ! ( call. class. opcode == Op :: FunctionCall ) ;
726703
704+ if let Some ( call_result_type) = call_result_type {
705+ // Generate the storage space for the return value: Do this *after* the split above,
706+ // because if block_idx=0, inserting a variable here shifts call_index.
707+ let ret_var_inst = Instruction :: new (
708+ Op :: Variable ,
709+ Some ( self . ptr_ty ( call_result_type) ) ,
710+ Some ( return_variable. unwrap ( ) ) ,
711+ vec ! [ Operand :: StorageClass ( StorageClass :: Function ) ] ,
712+ ) ;
713+ self . insert_opvariables ( & mut caller. blocks [ 0 ] , [ ret_var_inst] ) ;
714+ }
715+
727716 // Insert non-entry inlined callee blocks just after the pre-call block.
728717 let non_entry_inlined_callee_blocks = inlined_callee_blocks. drain ( 1 ..) ;
729718 let num_non_entry_inlined_callee_blocks = non_entry_inlined_callee_blocks. len ( ) ;
@@ -732,9 +721,18 @@ impl Inliner<'_, '_> {
732721 non_entry_inlined_callee_blocks,
733722 ) ;
734723
735- if let Some ( call_result_phi) = maybe_call_result_phi {
736- // Add the `OpPhi` for the call result value, after the inlined function.
737- post_call_block_insts. insert ( 0 , call_result_phi) ;
724+ if let Some ( call_result_type) = call_result_type {
725+ // Add the load of the result value after the inlined function. Note there's guaranteed no
726+ // OpPhi instructions since we just split this block.
727+ post_call_block_insts. insert (
728+ 0 ,
729+ Instruction :: new (
730+ Op :: Load ,
731+ Some ( call_result_type) ,
732+ Some ( call_result_id) ,
733+ vec ! [ Operand :: IdRef ( return_variable. unwrap( ) ) ] ,
734+ ) ,
735+ ) ;
738736 }
739737
740738 // Insert the post-call block, after all the inlined callee blocks.
@@ -901,7 +899,7 @@ impl Inliner<'_, '_> {
901899 & mut self ,
902900 callee : & Function ,
903901 call_debug_src_loc_inst : Option < & Instruction > ,
904- mut maybe_call_result_phi : Option < & mut Instruction > ,
902+ return_variable : Option < Word > ,
905903 return_jump : Word ,
906904 ) -> Vec < Block > {
907905 let Self {
@@ -999,13 +997,17 @@ impl Inliner<'_, '_> {
999997 if let Op :: Return | Op :: ReturnValue = terminator. class . opcode {
1000998 if Op :: ReturnValue == terminator. class . opcode {
1001999 let return_value = terminator. operands [ 0 ] . id_ref_any ( ) . unwrap ( ) ;
1002- let call_result_phi = maybe_call_result_phi. as_deref_mut ( ) . unwrap ( ) ;
1003- call_result_phi. operands . extend ( [
1004- Operand :: IdRef ( return_value) ,
1005- Operand :: IdRef ( block. label_id ( ) . unwrap ( ) ) ,
1006- ] ) ;
1000+ block. instructions . push ( Instruction :: new (
1001+ Op :: Store ,
1002+ None ,
1003+ None ,
1004+ vec ! [
1005+ Operand :: IdRef ( return_variable. unwrap( ) ) ,
1006+ Operand :: IdRef ( return_value) ,
1007+ ] ,
1008+ ) ) ;
10071009 } else {
1008- assert ! ( maybe_call_result_phi . is_none( ) ) ;
1010+ assert ! ( return_variable . is_none( ) ) ;
10091011 }
10101012 terminator =
10111013 Instruction :: new ( Op :: Branch , None , None , vec ! [ Operand :: IdRef ( return_jump) ] ) ;
0 commit comments