@@ -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
@@ -658,15 +658,13 @@ impl Inliner<'_, '_> {
658658 if let Some ( call_result_type) = call_result_type {
659659 // Generate the storage space for the return value: Do this *after* the split above,
660660 // 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 ) ] ,
669666 ) ;
667+ self . insert_opvariables ( & mut caller. blocks [ 0 ] , [ ret_var_inst] ) ;
670668 }
671669
672670 // Insert non-entry inlined callee blocks just after the pre-call block.
@@ -712,52 +710,115 @@ impl Inliner<'_, '_> {
712710 // Fuse the inlined callee entry block into the pre-call block.
713711 // This is okay because it's illegal to branch to the first BB in a function.
714712 {
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+
715738 // Return the subsequence of `insts` made from `OpVariable`s, and any
716739 // debuginfo instructions (which may apply to them), while removing
717740 // *only* `OpVariable`s from `insts` (and keeping debuginfo in both).
718741 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+ }
735773 }
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 ,
748776 }
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 ] > > ( )
751813 } ;
752814
753815 let [ mut inlined_callee_entry_block] : [ _ ; 1 ] =
754816 inlined_callee_blocks. try_into ( ) . unwrap ( ) ;
755817
756818 // 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) ;
761822
762823 caller. blocks [ pre_call_block_idx]
763824 . instructions
@@ -990,15 +1051,52 @@ impl Inliner<'_, '_> {
9901051 caller_restore_debuginfo_after_call,
9911052 )
9921053 }
993- }
9941054
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+ }
10021100}
10031101
10041102fn fuse_trivial_branches ( function : & mut Function ) {
@@ -1033,7 +1131,7 @@ fn fuse_trivial_branches(function: &mut Function) {
10331131 } ;
10341132 let pred_insts = & function. blocks [ pred] . instructions ;
10351133 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 ) ;
10371135 let pred_insts = & mut function. blocks [ pred] . instructions ;
10381136 pred_insts. pop ( ) ; // pop the branch
10391137 pred_insts. append ( & mut dest_insts) ;
0 commit comments