@@ -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
@@ -357,42 +365,70 @@ extension ScopeExtension {
357365private struct ExtendableScope {
358366 enum Introducer {
359367 case scoped( ScopedInstruction )
368+ case stack( Instruction )
360369 case owned( Value )
361370 }
362371
372+ // scope.allocStackInstruction is always valid for Introducer.allocStack and is valid for Introducer.scoped when
373+ // ScopedInstruction is a store_borrow.
363374 let scope : LifetimeDependence . Scope
364375 let introducer : Introducer
365376
366377 var firstInstruction : Instruction {
367378 switch introducer {
368379 case let . scoped( scopedInst) :
369380 return scopedInst. instruction
381+ case let . stack( initializingStore) :
382+ return initializingStore
370383 case let . owned( value) :
371384 if let definingInst = value. definingInstructionOrTerminator {
372385 return definingInst
373386 }
374387 return value. parentBlock. instructions. first!
375388 }
376389 }
390+
377391 var endInstructions : LazyMapSequence < LazyFilterSequence < UseList > , Instruction > {
378392 switch introducer {
379393 case let . scoped( scopedInst) :
380394 return scopedInst. endOperands. users
395+ case . stack:
396+ // For alloc_stack without a store-borrow scope, include the deallocs in its scope to ensure that we never shorten
397+ // the original allocation. It's possible that some other use depends on the address.
398+ //
399+ // Same as 'AllocStackInst.deallocations' but as an Instruction list...
400+ return scope. allocStackInstruction!. uses. lazy. filter
401+ { $0. instruction is DeallocStackInst } . lazy. map { $0. instruction }
402+
381403 case let . owned( value) :
382404 return value. uses. endingLifetime. users
383405 }
384406 }
385407
386- // Allow scope extension as long as `beginInst` is scoped instruction and does not define a variable scope.
408+ var deallocs : LazyMapSequence < LazyFilterSequence < UseList > , DeallocStackInst > ? {
409+ guard let allocStack = scope. allocStackInstruction else {
410+ return nil
411+ }
412+ return allocStack. uses. users ( ofType: DeallocStackInst . self)
413+ }
414+
415+ // Allow scope extension as long as `beginInst` does not define a variable scope and is either a scoped instruction or
416+ // a store to a singly-initialized temporary.
387417 init ? ( _ scope: LifetimeDependence . Scope , beginInst: Instruction ? ) {
388418 self . scope = scope
389419 guard let beginInst = beginInst, VariableScopeInstruction ( beginInst) == nil else {
390420 return nil
391421 }
392- guard let scopedInst = beginInst as? ScopedInstruction else {
393- return nil
422+ // Check for "scoped" store_borrow extension before checking allocStackInstruction.
423+ if let scopedInst = beginInst as? ScopedInstruction {
424+ self . introducer = . scoped( scopedInst)
425+ return
394426 }
395- self . introducer = . scoped( scopedInst)
427+ if scope. allocStackInstruction != nil {
428+ self . introducer = . stack( beginInst)
429+ return
430+ }
431+ return nil
396432 }
397433
398434 // Allow extension of owned temporaries that
@@ -459,11 +495,14 @@ extension ScopeExtension {
459495 switch initializer {
460496 case let . store( initializingStore: store, initialAddress: _) :
461497 if let sb = store as? StoreBorrowInst {
462- // Follow the source for nested scopes .
498+ // Follow the stored value since the owner of the borrowed value needs to cover this allocation .
463499 gatherExtensions ( valueOrAddress: sb. source)
464- scopes. push ( ExtendableScope ( scope, beginInst: sb) !)
500+ }
501+ if scope. allocStackInstruction != nil {
502+ scopes. push ( ExtendableScope ( scope, beginInst: store) !)
465503 return
466504 }
505+ break
467506 case . argument, . yield:
468507 // TODO: extend indirectly yielded scopes.
469508 break
@@ -742,9 +781,9 @@ extension ScopeExtension {
742781 // Extend the scopes that actually required extension.
743782 //
744783 // Consumes 'useRange'
745- private func extend( scopesToExtend: SingleInlineArray < ExtendableScope > ,
746- over useRange: inout InstructionRange ,
747- _ context: some MutatingContext ) {
784+ private mutating func extend( scopesToExtend: SingleInlineArray < ExtendableScope > ,
785+ over useRange: inout InstructionRange ,
786+ _ context: some MutatingContext ) {
748787 var deadInsts = [ Instruction] ( )
749788 for extScope in scopesToExtend {
750789 // Extend 'useRange' to to cover this scope's end instructions. 'useRange' cannot be extended until the
@@ -772,6 +811,9 @@ extension ScopeExtension {
772811
773812 // Delete original end instructions.
774813 for deadInst in deadInsts {
814+ if deadInst is DeallocStackInst {
815+ mustFixStackNesting = true
816+ }
775817 context. erase ( instruction: deadInst)
776818 }
777819 }
@@ -788,13 +830,10 @@ extension ExtendableScope {
788830 switch initializer {
789831 case . argument, . yield:
790832 // A yield is already considered nested within the coroutine.
791- break
792- case let . store( initializingStore, _) :
793- if let sb = initializingStore as? StoreBorrowInst {
794- return canExtend ( storeBorrow: sb, over: & range)
795- }
833+ return true
834+ case . store:
835+ return self . scope. allocStackInstruction != nil
796836 }
797- return true
798837 default :
799838 // non-yield scopes can always be ended at any point.
800839 return true
@@ -823,27 +862,28 @@ extension ExtendableScope {
823862 return true
824863 }
825864
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-
836865 /// Extend this scope over the 'range' boundary. Return the old scope ending instructions to be deleted.
837866 func extend( over range: inout InstructionRange , _ context: some MutatingContext ) -> [ Instruction ] {
838867 // Collect the original end instructions and extend the range to to cover them. The resulting access scope
839868 // 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 {
869+ let originalScopeEnds = [ Instruction] ( self . endInstructions)
870+ // Track scope-ending instructions that have not yet been reused as range-ending instructions.
871+ var unreusedEnds = InstructionSet ( context)
872+ for end in originalScopeEnds {
843873 assert ( range. inclusiveRangeContains ( end) )
844- unusedEnds. insert ( end)
874+ unreusedEnds. insert ( end)
875+ }
876+ defer { unreusedEnds. deinitialize ( ) }
877+
878+ // Never reuse dealloc_stack to avoid running data flow.
879+ var endsToErase = [ Instruction] ( )
880+ if let deallocs = self . deallocs {
881+ endsToErase. append ( contentsOf: deallocs. map { $0 } )
882+ for dealloc in deallocs {
883+ unreusedEnds. erase ( dealloc)
884+ }
845885 }
846- defer { unusedEnds . deinitialize ( ) }
886+
847887 for end in range. ends {
848888 let location = end. location. autoGenerated
849889 switch end {
@@ -856,60 +896,66 @@ extension ExtendableScope {
856896 // function argument.
857897 let builder = Builder ( before: end, location: location, context)
858898 // Insert newEnd so that this scope will be nested in any outer scopes.
859- range. insert ( createEndInstruction ( builder, context) )
899+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
860900 continue
861901 default :
862902 break
863903 }
864- if unusedEnds. contains ( end) {
865- unusedEnds. erase ( end)
866- assert ( !unusedEnds. contains ( end) )
904+ // If this range ending instruction was also scope-ending, then mark it as reused by removing it from the set.
905+ if unreusedEnds. contains ( end) {
906+ unreusedEnds. erase ( end)
907+ assert ( !unreusedEnds. contains ( end) )
867908 continue
868909 }
869910 Builder . insert ( after: end, location: location, context) {
870- range. insert ( createEndInstruction ( $0, context) )
911+ range. insert ( contentsOf : createEndInstructions ( $0, context) )
871912 }
872913 }
873914 for exitInst in range. exits {
874915 let location = exitInst. location. autoGenerated
875916 let builder = Builder ( before: exitInst, location: location, context)
876- range. insert ( createEndInstruction ( builder, context) )
917+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
877918 }
878- return endsToErase. filter { unusedEnds. contains ( $0) }
919+ endsToErase. append ( contentsOf: originalScopeEnds. filter { unreusedEnds. contains ( $0) } )
920+ return endsToErase
879921 }
880922
881923 /// Create a scope-ending instruction at 'builder's insertion point.
882- func createEndInstruction ( _ builder: Builder , _ context: some Context ) -> Instruction {
924+ func createEndInstructions ( _ builder: Builder , _ context: some Context ) -> SingleInlineArray < Instruction > {
883925 switch self . scope {
884926 case let . access( beginAccess) :
885- return builder. createEndAccess ( beginAccess: beginAccess)
927+ return SingleInlineArray ( element : builder. createEndAccess ( beginAccess: beginAccess) )
886928 case let . borrowed( beginBorrow) :
887- return builder. createEndBorrow ( of: beginBorrow. value)
929+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow. value) )
888930 case let . yield( yieldedValue) :
889931 let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
890932 // createEnd() returns non-nil because beginApply.endReaches() was checked by canExtend()
891- return beginApply. createEnd ( builder, context) !
933+ return SingleInlineArray ( element : beginApply. createEnd ( builder, context) !)
892934 case let . initialized( initializer) :
893935 switch initializer {
894936 case let . store( initializingStore: store, initialAddress: _) :
937+ var endInsts = SingleInlineArray < Instruction > ( )
895938 if let sb = store as? StoreBorrowInst {
896- // FIXME: we may need to rewrite the dealloc_stack.
897- return builder. createEndBorrow ( of: sb)
939+ endInsts. append ( builder. createEndBorrow ( of: sb) )
940+ }
941+ if let allocStack = self . scope. allocStackInstruction {
942+ endInsts. append ( builder. createDeallocStack ( allocStack) )
943+ return endInsts
898944 }
899945 break
900946 case . argument, . yield:
901947 // TODO: extend indirectly yielded scopes.
902948 break
903949 }
904950 case let . owned( value) :
905- return builder. createDestroyValue ( operand: value)
951+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: value) )
906952 case let . local( varInst) :
907953 switch varInst {
908954 case let . beginBorrow( beginBorrow) :
909955 // FIXME: we may need to rewrite the dealloc_stack.
910- return builder. createEndBorrow ( of: beginBorrow)
956+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow) )
911957 case let . moveValue( moveValue) :
912- return builder. createDestroyValue ( operand: moveValue)
958+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: moveValue) )
913959 }
914960 default :
915961 break
0 commit comments