@@ -15,7 +15,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
15
15
use rustc_errors:: ErrorGuaranteed ;
16
16
use rustc_session:: Session ;
17
17
use smallvec:: SmallVec ;
18
- use std:: mem:: { self , take } ;
18
+ use std:: mem;
19
19
20
20
type FunctionMap = FxHashMap < Word , Function > ;
21
21
@@ -658,15 +658,13 @@ impl Inliner<'_, '_> {
658
658
if let Some ( call_result_type) = call_result_type {
659
659
// Generate the storage space for the return value: Do this *after* the split above,
660
660
// because if block_idx=0, inserting a variable here shifts call_index.
661
- insert_opvariables (
662
- & mut caller. blocks [ 0 ] ,
663
- [ Instruction :: new (
664
- Op :: Variable ,
665
- Some ( self . ptr_ty ( call_result_type) ) ,
666
- Some ( return_variable. unwrap ( ) ) ,
667
- vec ! [ Operand :: StorageClass ( StorageClass :: Function ) ] ,
668
- ) ] ,
661
+ let ret_var_inst = Instruction :: new (
662
+ Op :: Variable ,
663
+ Some ( self . ptr_ty ( call_result_type) ) ,
664
+ Some ( return_variable. unwrap ( ) ) ,
665
+ vec ! [ Operand :: StorageClass ( StorageClass :: Function ) ] ,
669
666
) ;
667
+ self . insert_opvariables ( & mut caller. blocks [ 0 ] , [ ret_var_inst] ) ;
670
668
}
671
669
672
670
// Insert non-entry inlined callee blocks just after the pre-call block.
@@ -712,52 +710,115 @@ impl Inliner<'_, '_> {
712
710
// Fuse the inlined callee entry block into the pre-call block.
713
711
// This is okay because it's illegal to branch to the first BB in a function.
714
712
{
713
+ // NOTE(eddyb) `OpExtInst`s have a result ID, even if unused, and
714
+ // it has to be unique, so this allocates new IDs as-needed.
715
+ let instantiate_debuginfo = |this : & mut Self , inst : & Instruction | {
716
+ let mut inst = inst. clone ( ) ;
717
+ if let Some ( id) = & mut inst. result_id {
718
+ * id = this. id ( ) ;
719
+ }
720
+ inst
721
+ } ;
722
+
723
+ let custom_inst_to_inst = |this : & mut Self , inst : CustomInst < _ > | {
724
+ Instruction :: new (
725
+ Op :: ExtInst ,
726
+ Some ( this. op_type_void_id ) ,
727
+ Some ( this. id ( ) ) ,
728
+ [
729
+ Operand :: IdRef ( this. custom_ext_inst_set_import ) ,
730
+ Operand :: LiteralExtInstInteger ( inst. op ( ) as u32 ) ,
731
+ ]
732
+ . into_iter ( )
733
+ . chain ( inst. into_operands ( ) )
734
+ . collect ( ) ,
735
+ )
736
+ } ;
737
+
715
738
// Return the subsequence of `insts` made from `OpVariable`s, and any
716
739
// debuginfo instructions (which may apply to them), while removing
717
740
// *only* `OpVariable`s from `insts` (and keeping debuginfo in both).
718
741
let mut steal_vars = |insts : & mut Vec < Instruction > | {
719
- let mut vars_and_debuginfo = vec ! [ ] ;
720
- insts. retain_mut ( |inst| {
721
- let is_debuginfo = match inst. class . opcode {
722
- Op :: Line | Op :: NoLine => true ,
723
- Op :: ExtInst => {
724
- inst. operands [ 0 ] . unwrap_id_ref ( ) == self . custom_ext_inst_set_import
725
- && CustomOp :: decode_from_ext_inst ( inst) . is_debuginfo ( )
726
- }
727
- _ => false ,
728
- } ;
729
- if is_debuginfo {
730
- // NOTE(eddyb) `OpExtInst`s have a result ID,
731
- // even if unused, and it has to be unique.
732
- let mut inst = inst. clone ( ) ;
733
- if let Some ( id) = & mut inst. result_id {
734
- * id = self . id ( ) ;
742
+ // HACK(eddyb) this duplicates some code from `get_inlined_blocks`,
743
+ // but that will be removed once the inliner is refactored to be
744
+ // inside-out instead of outside-in (already finished in a branch).
745
+ let mut enclosing_inlined_frames = SmallVec :: < [ _ ; 8 ] > :: new ( ) ;
746
+ let mut current_debug_src_loc_inst = None ;
747
+ let mut vars_and_debuginfo_range = 0 ..0 ;
748
+ while vars_and_debuginfo_range. end < insts. len ( ) {
749
+ let inst = & insts[ vars_and_debuginfo_range. end ] ;
750
+ match inst. class . opcode {
751
+ Op :: Line => current_debug_src_loc_inst = Some ( inst) ,
752
+ Op :: NoLine => current_debug_src_loc_inst = None ,
753
+ Op :: ExtInst
754
+ if inst. operands [ 0 ] . unwrap_id_ref ( )
755
+ == self . custom_ext_inst_set_import =>
756
+ {
757
+ match CustomOp :: decode_from_ext_inst ( inst) {
758
+ CustomOp :: SetDebugSrcLoc => current_debug_src_loc_inst = Some ( inst) ,
759
+ CustomOp :: ClearDebugSrcLoc => current_debug_src_loc_inst = None ,
760
+ CustomOp :: PushInlinedCallFrame => {
761
+ enclosing_inlined_frames
762
+ . push ( ( current_debug_src_loc_inst. take ( ) , inst) ) ;
763
+ }
764
+ CustomOp :: PopInlinedCallFrame => {
765
+ if let Some ( ( callsite_debug_src_loc_inst, _) ) =
766
+ enclosing_inlined_frames. pop ( )
767
+ {
768
+ current_debug_src_loc_inst = callsite_debug_src_loc_inst;
769
+ }
770
+ }
771
+ CustomOp :: Abort => break ,
772
+ }
735
773
}
736
- vars_and_debuginfo. push ( inst) ;
737
- true
738
- } else if inst. class . opcode == Op :: Variable {
739
- // HACK(eddyb) we're removing this `Instruction` from
740
- // `inst`, so it doesn't really matter what we use here.
741
- vars_and_debuginfo. push ( mem:: replace (
742
- inst,
743
- Instruction :: new ( Op :: Nop , None , None , vec ! [ ] ) ,
744
- ) ) ;
745
- false
746
- } else {
747
- true
774
+ Op :: Variable => { }
775
+ _ => break ,
748
776
}
749
- } ) ;
750
- vars_and_debuginfo
777
+ vars_and_debuginfo_range. end += 1 ;
778
+ }
779
+
780
+ // `vars_and_debuginfo_range.end` indicates where `OpVariable`s
781
+ // end and other instructions start (module debuginfo), but to
782
+ // split the block in two, both sides of the "cut" need "repair":
783
+ // - the variables are missing "inlined call frames" pops, that
784
+ // may happen later in the block, and have to be synthesized
785
+ // - the non-variables are missing "inlined call frames" pushes,
786
+ // that must be recreated to avoid ending up with dangling pops
787
+ //
788
+ // FIXME(eddyb) this only collects to avoid borrow conflicts,
789
+ // between e.g. `enclosing_inlined_frames` and mutating `insts`,
790
+ // but also between different uses of `self`.
791
+ let all_pops_after_vars: SmallVec < [ _ ; 8 ] > = enclosing_inlined_frames
792
+ . iter ( )
793
+ . map ( |_| custom_inst_to_inst ( self , CustomInst :: PopInlinedCallFrame ) )
794
+ . collect ( ) ;
795
+ let all_repushes_before_non_vars: SmallVec < [ _ ; 8 ] > =
796
+ ( enclosing_inlined_frames. into_iter ( ) . flat_map (
797
+ |( callsite_debug_src_loc_inst, push_inlined_call_frame_inst) | {
798
+ ( callsite_debug_src_loc_inst. into_iter ( ) )
799
+ . chain ( [ push_inlined_call_frame_inst] )
800
+ } ,
801
+ ) )
802
+ . chain ( current_debug_src_loc_inst)
803
+ . map ( |inst| instantiate_debuginfo ( self , inst) )
804
+ . collect ( ) ;
805
+
806
+ let vars_and_debuginfo =
807
+ insts. splice ( vars_and_debuginfo_range, all_repushes_before_non_vars) ;
808
+ let repaired_vars_and_debuginfo = vars_and_debuginfo. chain ( all_pops_after_vars) ;
809
+
810
+ // FIXME(eddyb) collecting shouldn't be necessary but this is
811
+ // nested in a closure, and `splice` borrows the original `Vec`.
812
+ repaired_vars_and_debuginfo. collect :: < SmallVec < [ _ ; 8 ] > > ( )
751
813
} ;
752
814
753
815
let [ mut inlined_callee_entry_block] : [ _ ; 1 ] =
754
816
inlined_callee_blocks. try_into ( ) . unwrap ( ) ;
755
817
756
818
// Move the `OpVariable`s of the callee to the caller.
757
- insert_opvariables (
758
- & mut caller. blocks [ 0 ] ,
759
- steal_vars ( & mut inlined_callee_entry_block. instructions ) ,
760
- ) ;
819
+ let callee_vars_and_debuginfo =
820
+ steal_vars ( & mut inlined_callee_entry_block. instructions ) ;
821
+ self . insert_opvariables ( & mut caller. blocks [ 0 ] , callee_vars_and_debuginfo) ;
761
822
762
823
caller. blocks [ pre_call_block_idx]
763
824
. instructions
@@ -990,15 +1051,52 @@ impl Inliner<'_, '_> {
990
1051
caller_restore_debuginfo_after_call,
991
1052
)
992
1053
}
993
- }
994
1054
995
- fn insert_opvariables ( block : & mut Block , insts : impl IntoIterator < Item = Instruction > ) {
996
- let first_non_variable = block
997
- . instructions
998
- . iter ( )
999
- . position ( |inst| inst. class . opcode != Op :: Variable ) ;
1000
- let i = first_non_variable. unwrap_or ( block. instructions . len ( ) ) ;
1001
- block. instructions . splice ( i..i, insts) ;
1055
+ fn insert_opvariables ( & self , block : & mut Block , insts : impl IntoIterator < Item = Instruction > ) {
1056
+ // HACK(eddyb) this isn't as efficient as it could be in theory, but it's
1057
+ // very important to make sure sure to never insert new instructions in
1058
+ // the middle of debuginfo (as it would be affected by it).
1059
+ let mut inlined_frames_depth = 0usize ;
1060
+ let mut outermost_has_debug_src_loc = false ;
1061
+ let mut last_debugless_var_insertion_point_candidate = None ;
1062
+ for ( i, inst) in block. instructions . iter ( ) . enumerate ( ) {
1063
+ last_debugless_var_insertion_point_candidate =
1064
+ ( inlined_frames_depth == 0 && !outermost_has_debug_src_loc) . then_some ( i) ;
1065
+
1066
+ let changed_has_debug_src_loc = match inst. class . opcode {
1067
+ Op :: Line => true ,
1068
+ Op :: NoLine => false ,
1069
+ Op :: ExtInst
1070
+ if inst. operands [ 0 ] . unwrap_id_ref ( ) == self . custom_ext_inst_set_import =>
1071
+ {
1072
+ match CustomOp :: decode_from_ext_inst ( inst) {
1073
+ CustomOp :: SetDebugSrcLoc => true ,
1074
+ CustomOp :: ClearDebugSrcLoc => false ,
1075
+ CustomOp :: PushInlinedCallFrame => {
1076
+ inlined_frames_depth += 1 ;
1077
+ continue ;
1078
+ }
1079
+ CustomOp :: PopInlinedCallFrame => {
1080
+ inlined_frames_depth = inlined_frames_depth. saturating_sub ( 1 ) ;
1081
+ continue ;
1082
+ }
1083
+ CustomOp :: Abort => break ,
1084
+ }
1085
+ }
1086
+ Op :: Variable => continue ,
1087
+ _ => break ,
1088
+ } ;
1089
+
1090
+ if inlined_frames_depth == 0 {
1091
+ outermost_has_debug_src_loc = changed_has_debug_src_loc;
1092
+ }
1093
+ }
1094
+
1095
+ // HACK(eddyb) fallback to inserting at the start, which should be correct.
1096
+ // FIXME(eddyb) some level of debuginfo repair could prevent needing this.
1097
+ let i = last_debugless_var_insertion_point_candidate. unwrap_or ( 0 ) ;
1098
+ block. instructions . splice ( i..i, insts) ;
1099
+ }
1002
1100
}
1003
1101
1004
1102
fn fuse_trivial_branches ( function : & mut Function ) {
@@ -1033,7 +1131,7 @@ fn fuse_trivial_branches(function: &mut Function) {
1033
1131
} ;
1034
1132
let pred_insts = & function. blocks [ pred] . instructions ;
1035
1133
if pred_insts. last ( ) . unwrap ( ) . class . opcode == Op :: Branch {
1036
- let mut dest_insts = take ( & mut function. blocks [ dest_block] . instructions ) ;
1134
+ let mut dest_insts = mem :: take ( & mut function. blocks [ dest_block] . instructions ) ;
1037
1135
let pred_insts = & mut function. blocks [ pred] . instructions ;
1038
1136
pred_insts. pop ( ) ; // pop the branch
1039
1137
pred_insts. append ( & mut dest_insts) ;
0 commit comments