@@ -106,6 +106,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
106106
107107 let localReachabilityCache = LocalVariableReachabilityCache ( )
108108
109+ var mustFixStackNesting = false
109110 for instruction in function. instructions {
110111 guard let markDep = instruction as? MarkDependenceInstruction else {
111112 continue
@@ -122,6 +123,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
122123 guard scopeExtension. extendScopes ( dependence: newLifetimeDep) else {
123124 continue
124125 }
126+ mustFixStackNesting = mustFixStackNesting || scopeExtension. mustFixStackNesting
125127 let args = scopeExtension. findArgumentDependencies ( )
126128
127129 // If the scope cannot be extended to the caller, this must be the outermost dependency level.
@@ -133,6 +135,9 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
133135 // Redirect the dependence base to the function arguments. This may create additional mark_dependence instructions.
134136 markDep. redirectFunctionReturn ( to: args, context)
135137 }
138+ if mustFixStackNesting {
139+ context. fixStackNesting ( in: function)
140+ }
136141}
137142
138143private extension Type {
@@ -285,6 +290,9 @@ private struct ScopeExtension {
285290 // Initialized after walking dependent uses. True if the scope can be extended into the caller.
286291 var dependsOnCaller : Bool ?
287292
293+ // Does scope extension potentially invalidate stack nesting?
294+ var mustFixStackNesting = false
295+
288296 // Scopes listed in RPO over an upward walk. The outermost scope is first.
289297 var scopes = SingleInlineArray < ExtendableScope > ( )
290298
@@ -383,6 +391,23 @@ private struct ExtendableScope {
383391 }
384392 }
385393
394+ var deallocs : LazyMapSequence < LazyFilterSequence < UseList > , DeallocStackInst > ? {
395+ switch self . scope {
396+ case let . initialized( initializer) :
397+ switch initializer {
398+ case let . store( initializingStore: store, initialAddress: _) :
399+ if let sb = store as? StoreBorrowInst {
400+ return sb. allocStack. uses. filterUsers ( ofType: DeallocStackInst . self)
401+ }
402+ default :
403+ break
404+ }
405+ default :
406+ break
407+ }
408+ return nil
409+ }
410+
386411 // Allow scope extension as long as `beginInst` is scoped instruction and does not define a variable scope.
387412 init ? ( _ scope: LifetimeDependence . Scope , beginInst: Instruction ? ) {
388413 self . scope = scope
@@ -742,9 +767,9 @@ extension ScopeExtension {
742767 // Extend the scopes that actually required extension.
743768 //
744769 // Consumes 'useRange'
745- private func extend( scopesToExtend: SingleInlineArray < ExtendableScope > ,
746- over useRange: inout InstructionRange ,
747- _ context: some MutatingContext ) {
770+ private mutating func extend( scopesToExtend: SingleInlineArray < ExtendableScope > ,
771+ over useRange: inout InstructionRange ,
772+ _ context: some MutatingContext ) {
748773 var deadInsts = [ Instruction] ( )
749774 for extScope in scopesToExtend {
750775 // Extend 'useRange' to to cover this scope's end instructions. 'useRange' cannot be extended until the
@@ -772,6 +797,9 @@ extension ScopeExtension {
772797
773798 // Delete original end instructions.
774799 for deadInst in deadInsts {
800+ if deadInst is DeallocStackInst {
801+ mustFixStackNesting = true
802+ }
775803 context. erase ( instruction: deadInst)
776804 }
777805 }
@@ -790,8 +818,8 @@ extension ExtendableScope {
790818 // A yield is already considered nested within the coroutine.
791819 break
792820 case let . store( initializingStore, _) :
793- if let sb = initializingStore as? StoreBorrowInst {
794- return canExtend ( storeBorrow : sb , over : & range )
821+ if initializingStore is StoreBorrowInst {
822+ return true
795823 }
796824 }
797825 return true
@@ -823,27 +851,25 @@ extension ExtendableScope {
823851 return true
824852 }
825853
826- /// A store borrow is considered to be nested within the scope of its stored values. It is, however, also
827- /// restricted to the range of its allocation.
828- ///
829- /// TODO: consider rewriting the dealloc_stack instructions if we ever find that SILGen emits them sooner that
830- /// we need for lifetime dependencies.
831- func canExtend( storeBorrow: StoreBorrowInst , over range: inout InstructionRange ) -> Bool {
832- // store_borrow can be extended if all deallocations occur after the use range.
833- return storeBorrow. allocStack. deallocations. allSatisfy ( { !range. contains ( $0) } )
834- }
835-
836854 /// Extend this scope over the 'range' boundary. Return the old scope ending instructions to be deleted.
837855 func extend( over range: inout InstructionRange , _ context: some MutatingContext ) -> [ Instruction ] {
838856 // Collect the original end instructions and extend the range to to cover them. The resulting access scope
839857 // must cover the original scope because it may protect other memory operations.
840- let endsToErase = self . endInstructions
841- var unusedEnds = InstructionSet ( context)
842- for end in endsToErase {
858+ let originalScopeEnds = [ Instruction] ( self . endInstructions)
859+ // Track scope-ending instructions that have not yet been reused as range-ending instructions.
860+ var unreusedEnds = InstructionSet ( context)
861+ for end in originalScopeEnds {
843862 assert ( range. inclusiveRangeContains ( end) )
844- unusedEnds . insert ( end)
863+ unreusedEnds . insert ( end)
845864 }
846- defer { unusedEnds. deinitialize ( ) }
865+ defer { unreusedEnds. deinitialize ( ) }
866+
867+ // Never reuse dealloc_stack to avoid running data flow.
868+ var endsToErase = [ Instruction] ( )
869+ if let deallocs = self . deallocs {
870+ endsToErase. append ( contentsOf: deallocs. map { $0 } )
871+ }
872+
847873 for end in range. ends {
848874 let location = end. location. asAutoGenerated
849875 switch end {
@@ -856,60 +882,64 @@ extension ExtendableScope {
856882 // function argument.
857883 let builder = Builder ( before: end, location: location, context)
858884 // Insert newEnd so that this scope will be nested in any outer scopes.
859- range. insert ( createEndInstruction ( builder, context) )
885+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
860886 continue
861887 default :
862888 break
863889 }
864- if unusedEnds. contains ( end) {
865- unusedEnds. erase ( end)
866- assert ( !unusedEnds. contains ( end) )
890+ // If this range ending instruction was also scope-ending, then mark it as reused by removing it from the set.
891+ if unreusedEnds. contains ( end) {
892+ unreusedEnds. erase ( end)
893+ assert ( !unreusedEnds. contains ( end) )
867894 continue
868895 }
869896 Builder . insert ( after: end, location: location, context) {
870- range. insert ( createEndInstruction ( $0, context) )
897+ range. insert ( contentsOf : createEndInstructions ( $0, context) )
871898 }
872899 }
873900 for exitInst in range. exits {
874901 let location = exitInst. location. asAutoGenerated
875902 let builder = Builder ( before: exitInst, location: location, context)
876- range. insert ( createEndInstruction ( builder, context) )
903+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
877904 }
878- return endsToErase. filter { unusedEnds. contains ( $0) }
905+ endsToErase. append ( contentsOf: originalScopeEnds. filter { unreusedEnds. contains ( $0) } )
906+ return endsToErase
879907 }
880908
881909 /// Create a scope-ending instruction at 'builder's insertion point.
882- func createEndInstruction ( _ builder: Builder , _ context: some Context ) -> Instruction {
910+ func createEndInstructions ( _ builder: Builder , _ context: some Context ) -> SingleInlineArray < Instruction > {
883911 switch self . scope {
884912 case let . access( beginAccess) :
885- return builder. createEndAccess ( beginAccess: beginAccess)
913+ return SingleInlineArray ( element : builder. createEndAccess ( beginAccess: beginAccess) )
886914 case let . borrowed( beginBorrow) :
887- return builder. createEndBorrow ( of: beginBorrow. value)
915+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow. value) )
888916 case let . yield( yieldedValue) :
889917 let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
890918 // createEnd() returns non-nil because beginApply.endReaches() was checked by canExtend()
891- return beginApply. createEnd ( builder, context) !
919+ return SingleInlineArray ( element : beginApply. createEnd ( builder, context) !)
892920 case let . initialized( initializer) :
893921 switch initializer {
894922 case let . store( initializingStore: store, initialAddress: _) :
895923 if let sb = store as? StoreBorrowInst {
896- // FIXME: we may need to rewrite the dealloc_stack.
897- return builder. createEndBorrow ( of: sb)
924+ var endInsts = SingleInlineArray < Instruction > ( )
925+ endInsts. append ( builder. createEndBorrow ( of: sb) )
926+ endInsts. append ( builder. createDeallocStack ( sb. allocStack) )
927+ return endInsts
898928 }
899929 break
900930 case . argument, . yield:
901931 // TODO: extend indirectly yielded scopes.
902932 break
903933 }
904934 case let . owned( value) :
905- return builder. createDestroyValue ( operand: value)
935+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: value) )
906936 case let . local( varInst) :
907937 switch varInst {
908938 case let . beginBorrow( beginBorrow) :
909939 // FIXME: we may need to rewrite the dealloc_stack.
910- return builder. createEndBorrow ( of: beginBorrow)
940+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow) )
911941 case let . moveValue( moveValue) :
912- return builder. createDestroyValue ( operand: moveValue)
942+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: moveValue) )
913943 }
914944 default :
915945 break
0 commit comments