@@ -94,6 +94,7 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
94
94
header,
95
95
debug_string_source : & mut module. debug_string_source ,
96
96
annotations : & mut module. annotations ,
97
+ types_global_values : & mut module. types_global_values ,
97
98
98
99
legal_globals,
99
100
@@ -492,6 +493,7 @@ struct Inliner<'a, 'b> {
492
493
header : & ' b mut ModuleHeader ,
493
494
debug_string_source : & ' b mut Vec < Instruction > ,
494
495
annotations : & ' b mut Vec < Instruction > ,
496
+ types_global_values : & ' b mut Vec < Instruction > ,
495
497
496
498
legal_globals : FxHashMap < Word , LegalGlobal > ,
497
499
functions_that_may_abort : FxHashSet < Word > ,
@@ -521,6 +523,29 @@ impl Inliner<'_, '_> {
521
523
}
522
524
}
523
525
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
+
524
549
fn inline_fn (
525
550
& mut self ,
526
551
function : & mut Function ,
@@ -597,19 +622,15 @@ impl Inliner<'_, '_> {
597
622
. insert ( caller. def_id ( ) . unwrap ( ) ) ;
598
623
}
599
624
600
- let mut maybe_call_result_phi = {
625
+ let call_result_type = {
601
626
let ty = call_inst. result_type . unwrap ( ) ;
602
627
if ty == self . op_type_void_id {
603
628
None
604
629
} else {
605
- Some ( Instruction :: new (
606
- Op :: Phi ,
607
- Some ( ty) ,
608
- Some ( call_inst. result_id . unwrap ( ) ) ,
609
- vec ! [ ] ,
610
- ) )
630
+ Some ( ty)
611
631
}
612
632
} ;
633
+ let call_result_id = call_inst. result_id . unwrap ( ) ;
613
634
614
635
// Get the debug "source location" instruction that applies to the call.
615
636
let custom_ext_inst_set_import = self . custom_ext_inst_set_import ;
@@ -646,12 +667,17 @@ impl Inliner<'_, '_> {
646
667
} ) ;
647
668
let mut rewrite_rules = callee_parameters. zip ( call_arguments) . collect ( ) ;
648
669
670
+ let return_variable = if call_result_type. is_some ( ) {
671
+ Some ( self . id ( ) )
672
+ } else {
673
+ None
674
+ } ;
649
675
let return_jump = self . id ( ) ;
650
676
// Rewrite OpReturns of the callee.
651
677
let mut inlined_callee_blocks = self . get_inlined_blocks (
652
678
callee,
653
679
call_debug_src_loc_inst,
654
- maybe_call_result_phi . as_mut ( ) ,
680
+ return_variable ,
655
681
return_jump,
656
682
) ;
657
683
// Clone the IDs of the callee, because otherwise they'd be defined multiple times if the
@@ -660,55 +686,6 @@ impl Inliner<'_, '_> {
660
686
apply_rewrite_rules ( & rewrite_rules, & mut inlined_callee_blocks) ;
661
687
self . apply_rewrite_for_decorations ( & rewrite_rules) ;
662
688
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
-
712
689
// Split the block containing the `OpFunctionCall` into pre-call vs post-call.
713
690
let pre_call_block_idx = block_idx;
714
691
#[ expect( unused) ]
@@ -724,6 +701,18 @@ impl Inliner<'_, '_> {
724
701
. unwrap ( ) ;
725
702
assert ! ( call. class. opcode == Op :: FunctionCall ) ;
726
703
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
+
727
716
// Insert non-entry inlined callee blocks just after the pre-call block.
728
717
let non_entry_inlined_callee_blocks = inlined_callee_blocks. drain ( 1 ..) ;
729
718
let num_non_entry_inlined_callee_blocks = non_entry_inlined_callee_blocks. len ( ) ;
@@ -732,9 +721,18 @@ impl Inliner<'_, '_> {
732
721
non_entry_inlined_callee_blocks,
733
722
) ;
734
723
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
+ ) ;
738
736
}
739
737
740
738
// Insert the post-call block, after all the inlined callee blocks.
@@ -901,7 +899,7 @@ impl Inliner<'_, '_> {
901
899
& mut self ,
902
900
callee : & Function ,
903
901
call_debug_src_loc_inst : Option < & Instruction > ,
904
- mut maybe_call_result_phi : Option < & mut Instruction > ,
902
+ return_variable : Option < Word > ,
905
903
return_jump : Word ,
906
904
) -> Vec < Block > {
907
905
let Self {
@@ -999,13 +997,17 @@ impl Inliner<'_, '_> {
999
997
if let Op :: Return | Op :: ReturnValue = terminator. class . opcode {
1000
998
if Op :: ReturnValue == terminator. class . opcode {
1001
999
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
+ ) ) ;
1007
1009
} else {
1008
- assert ! ( maybe_call_result_phi . is_none( ) ) ;
1010
+ assert ! ( return_variable . is_none( ) ) ;
1009
1011
}
1010
1012
terminator =
1011
1013
Instruction :: new ( Op :: Branch , None , None , vec ! [ Operand :: IdRef ( return_jump) ] ) ;
0 commit comments