@@ -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,28 +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- // Track range ending instructions that have not been reused for later deletion .
858+ let originalScopeEnds = [ Instruction ] ( self . endInstructions)
859+ // Track scope- ending instructions that have not yet been reused as range-ending instructions .
842860 var unreusedEnds = InstructionSet ( context)
843- for end in endsToErase {
861+ for end in originalScopeEnds {
844862 assert ( range. inclusiveRangeContains ( end) )
845863 unreusedEnds. insert ( end)
846864 }
847865 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+
848873 for end in range. ends {
849874 let location = end. location. asAutoGenerated
850875 switch end {
@@ -857,45 +882,49 @@ extension ExtendableScope {
857882 // function argument.
858883 let builder = Builder ( before: end, location: location, context)
859884 // Insert newEnd so that this scope will be nested in any outer scopes.
860- range. insert ( createEndInstruction ( builder, context) )
885+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
861886 continue
862887 default :
863888 break
864889 }
890+ // If this range ending instruction was also scope-ending, then mark it as reused by removing it from the set.
865891 if unreusedEnds. contains ( end) {
866892 unreusedEnds. erase ( end)
867893 assert ( !unreusedEnds. contains ( end) )
868894 continue
869895 }
870896 Builder . insert ( after: end, location: location, context) {
871- range. insert ( createEndInstruction ( $0, context) )
897+ range. insert ( contentsOf : createEndInstructions ( $0, context) )
872898 }
873899 }
874900 for exitInst in range. exits {
875901 let location = exitInst. location. asAutoGenerated
876902 let builder = Builder ( before: exitInst, location: location, context)
877- range. insert ( createEndInstruction ( builder, context) )
903+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
878904 }
879- return endsToErase. filter { unreusedEnds. contains ( $0) }
905+ endsToErase. append ( contentsOf: originalScopeEnds. filter { unreusedEnds. contains ( $0) } )
906+ return endsToErase
880907 }
881908
882909 /// Create a scope-ending instruction at 'builder's insertion point.
883- func createEndInstruction ( _ builder: Builder , _ context: some Context ) -> Instruction {
910+ func createEndInstructions ( _ builder: Builder , _ context: some Context ) -> SingleInlineArray < Instruction > {
884911 switch self . scope {
885912 case let . access( beginAccess) :
886- return builder. createEndAccess ( beginAccess: beginAccess)
913+ return SingleInlineArray ( element : builder. createEndAccess ( beginAccess: beginAccess) )
887914 case let . borrowed( beginBorrow) :
888- return builder. createEndBorrow ( of: beginBorrow. value)
915+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow. value) )
889916 case let . yield( yieldedValue) :
890917 let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
891918 // createEnd() returns non-nil because beginApply.endReaches() was checked by canExtend()
892- return beginApply. createEnd ( builder, context) !
919+ return SingleInlineArray ( element : beginApply. createEnd ( builder, context) !)
893920 case let . initialized( initializer) :
894921 switch initializer {
895922 case let . store( initializingStore: store, initialAddress: _) :
896923 if let sb = store as? StoreBorrowInst {
897- // FIXME: we may need to rewrite the dealloc_stack.
898- 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
899928 }
900929 break
901930 case . argument, . yield:
0 commit comments