@@ -15,7 +15,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1515use rustc_errors:: ErrorGuaranteed ;
1616use rustc_session:: Session ;
1717use smallvec:: SmallVec ;
18- use std:: mem:: { self , take } ;
18+ use std:: mem;
1919
2020type FunctionMap = FxHashMap < Word , Function > ;
2121
@@ -653,12 +653,13 @@ impl Inliner<'_, '_> {
653653 if let Some ( call_result_type) = call_result_type {
654654 // Generate the storage space for the return value: Do this *after* the split above,
655655 // 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 (
657657 Op :: Variable ,
658658 Some ( self . ptr_ty ( call_result_type) ) ,
659659 Some ( return_variable. unwrap ( ) ) ,
660660 vec ! [ Operand :: StorageClass ( StorageClass :: Function ) ] ,
661- ) ] ) ;
661+ ) ;
662+ self . insert_opvariables ( & mut caller. blocks [ 0 ] , [ ret_var_inst] ) ;
662663 }
663664
664665 // Insert non-entry inlined callee blocks just after the pre-call block.
@@ -704,52 +705,115 @@ impl Inliner<'_, '_> {
704705 // Fuse the inlined callee entry block into the pre-call block.
705706 // This is okay because it's illegal to branch to the first BB in a function.
706707 {
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+
707733 // Return the subsequence of `insts` made from `OpVariable`s, and any
708734 // debuginfo instructions (which may apply to them), while removing
709735 // *only* `OpVariable`s from `insts` (and keeping debuginfo in both).
710736 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+ }
727768 }
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 ,
740771 }
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 ] > > ( )
743808 } ;
744809
745810 let [ mut inlined_callee_entry_block] : [ _ ; 1 ] =
746811 inlined_callee_blocks. try_into ( ) . unwrap ( ) ;
747812
748813 // 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) ;
753817
754818 caller. blocks [ pre_call_block_idx]
755819 . instructions
@@ -977,15 +1041,52 @@ impl Inliner<'_, '_> {
9771041 caller_restore_debuginfo_after_call,
9781042 )
9791043 }
980- }
9811044
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+ }
9891090}
9901091
9911092fn fuse_trivial_branches ( function : & mut Function ) {
@@ -1020,7 +1121,7 @@ fn fuse_trivial_branches(function: &mut Function) {
10201121 } ;
10211122 let pred_insts = & function. blocks [ pred] . instructions ;
10221123 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 ) ;
10241125 let pred_insts = & mut function. blocks [ pred] . instructions ;
10251126 pred_insts. pop ( ) ; // pop the branch
10261127 pred_insts. append ( & mut dest_insts) ;
0 commit comments