@@ -106,6 +106,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
106
106
107
107
let localReachabilityCache = LocalVariableReachabilityCache ( )
108
108
109
+ var mustFixStackNesting = false
109
110
for instruction in function. instructions {
110
111
guard let markDep = instruction as? MarkDependenceInstruction else {
111
112
continue
@@ -122,6 +123,7 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
122
123
guard scopeExtension. extendScopes ( dependence: newLifetimeDep) else {
123
124
continue
124
125
}
126
+ mustFixStackNesting = mustFixStackNesting || scopeExtension. mustFixStackNesting
125
127
let args = scopeExtension. findArgumentDependencies ( )
126
128
127
129
// If the scope cannot be extended to the caller, this must be the outermost dependency level.
@@ -133,6 +135,9 @@ let lifetimeDependenceScopeFixupPass = FunctionPass(
133
135
// Redirect the dependence base to the function arguments. This may create additional mark_dependence instructions.
134
136
markDep. redirectFunctionReturn ( to: args, context)
135
137
}
138
+ if mustFixStackNesting {
139
+ context. fixStackNesting ( in: function)
140
+ }
136
141
}
137
142
138
143
private extension Type {
@@ -285,6 +290,9 @@ private struct ScopeExtension {
285
290
// Initialized after walking dependent uses. True if the scope can be extended into the caller.
286
291
var dependsOnCaller : Bool ?
287
292
293
+ // Does scope extension potentially invalidate stack nesting?
294
+ var mustFixStackNesting = false
295
+
288
296
// Scopes listed in RPO over an upward walk. The outermost scope is first.
289
297
var scopes = SingleInlineArray < ExtendableScope > ( )
290
298
@@ -383,6 +391,23 @@ private struct ExtendableScope {
383
391
}
384
392
}
385
393
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
+
386
411
// Allow scope extension as long as `beginInst` is scoped instruction and does not define a variable scope.
387
412
init ? ( _ scope: LifetimeDependence . Scope , beginInst: Instruction ? ) {
388
413
self . scope = scope
@@ -742,9 +767,9 @@ extension ScopeExtension {
742
767
// Extend the scopes that actually required extension.
743
768
//
744
769
// 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 ) {
748
773
var deadInsts = [ Instruction] ( )
749
774
for extScope in scopesToExtend {
750
775
// Extend 'useRange' to to cover this scope's end instructions. 'useRange' cannot be extended until the
@@ -772,6 +797,9 @@ extension ScopeExtension {
772
797
773
798
// Delete original end instructions.
774
799
for deadInst in deadInsts {
800
+ if deadInst is DeallocStackInst {
801
+ mustFixStackNesting = true
802
+ }
775
803
context. erase ( instruction: deadInst)
776
804
}
777
805
}
@@ -790,8 +818,8 @@ extension ExtendableScope {
790
818
// A yield is already considered nested within the coroutine.
791
819
break
792
820
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
795
823
}
796
824
}
797
825
return true
@@ -823,28 +851,25 @@ extension ExtendableScope {
823
851
return true
824
852
}
825
853
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
-
836
854
/// Extend this scope over the 'range' boundary. Return the old scope ending instructions to be deleted.
837
855
func extend( over range: inout InstructionRange , _ context: some MutatingContext ) -> [ Instruction ] {
838
856
// Collect the original end instructions and extend the range to to cover them. The resulting access scope
839
857
// 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 .
842
860
var unreusedEnds = InstructionSet ( context)
843
- for end in endsToErase {
861
+ for end in originalScopeEnds {
844
862
assert ( range. inclusiveRangeContains ( end) )
845
863
unreusedEnds. insert ( end)
846
864
}
847
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
+
848
873
for end in range. ends {
849
874
let location = end. location. asAutoGenerated
850
875
switch end {
@@ -857,45 +882,49 @@ extension ExtendableScope {
857
882
// function argument.
858
883
let builder = Builder ( before: end, location: location, context)
859
884
// 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) )
861
886
continue
862
887
default :
863
888
break
864
889
}
890
+ // If this range ending instruction was also scope-ending, then mark it as reused by removing it from the set.
865
891
if unreusedEnds. contains ( end) {
866
892
unreusedEnds. erase ( end)
867
893
assert ( !unreusedEnds. contains ( end) )
868
894
continue
869
895
}
870
896
Builder . insert ( after: end, location: location, context) {
871
- range. insert ( createEndInstruction ( $0, context) )
897
+ range. insert ( contentsOf : createEndInstructions ( $0, context) )
872
898
}
873
899
}
874
900
for exitInst in range. exits {
875
901
let location = exitInst. location. asAutoGenerated
876
902
let builder = Builder ( before: exitInst, location: location, context)
877
- range. insert ( createEndInstruction ( builder, context) )
903
+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
878
904
}
879
- return endsToErase. filter { unreusedEnds. contains ( $0) }
905
+ endsToErase. append ( contentsOf: originalScopeEnds. filter { unreusedEnds. contains ( $0) } )
906
+ return endsToErase
880
907
}
881
908
882
909
/// 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 > {
884
911
switch self . scope {
885
912
case let . access( beginAccess) :
886
- return builder. createEndAccess ( beginAccess: beginAccess)
913
+ return SingleInlineArray ( element : builder. createEndAccess ( beginAccess: beginAccess) )
887
914
case let . borrowed( beginBorrow) :
888
- return builder. createEndBorrow ( of: beginBorrow. value)
915
+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow. value) )
889
916
case let . yield( yieldedValue) :
890
917
let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
891
918
// 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) !)
893
920
case let . initialized( initializer) :
894
921
switch initializer {
895
922
case let . store( initializingStore: store, initialAddress: _) :
896
923
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
899
928
}
900
929
break
901
930
case . argument, . yield:
0 commit comments