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