@@ -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,27 +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
- 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 {
843
862
assert ( range. inclusiveRangeContains ( end) )
844
- unusedEnds . insert ( end)
863
+ unreusedEnds . insert ( end)
845
864
}
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
+
847
873
for end in range. ends {
848
874
let location = end. location. asAutoGenerated
849
875
switch end {
@@ -856,60 +882,64 @@ extension ExtendableScope {
856
882
// function argument.
857
883
let builder = Builder ( before: end, location: location, context)
858
884
// 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) )
860
886
continue
861
887
default :
862
888
break
863
889
}
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) )
867
894
continue
868
895
}
869
896
Builder . insert ( after: end, location: location, context) {
870
- range. insert ( createEndInstruction ( $0, context) )
897
+ range. insert ( contentsOf : createEndInstructions ( $0, context) )
871
898
}
872
899
}
873
900
for exitInst in range. exits {
874
901
let location = exitInst. location. asAutoGenerated
875
902
let builder = Builder ( before: exitInst, location: location, context)
876
- range. insert ( createEndInstruction ( builder, context) )
903
+ range. insert ( contentsOf : createEndInstructions ( builder, context) )
877
904
}
878
- return endsToErase. filter { unusedEnds. contains ( $0) }
905
+ endsToErase. append ( contentsOf: originalScopeEnds. filter { unreusedEnds. contains ( $0) } )
906
+ return endsToErase
879
907
}
880
908
881
909
/// 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 > {
883
911
switch self . scope {
884
912
case let . access( beginAccess) :
885
- return builder. createEndAccess ( beginAccess: beginAccess)
913
+ return SingleInlineArray ( element : builder. createEndAccess ( beginAccess: beginAccess) )
886
914
case let . borrowed( beginBorrow) :
887
- return builder. createEndBorrow ( of: beginBorrow. value)
915
+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow. value) )
888
916
case let . yield( yieldedValue) :
889
917
let beginApply = yieldedValue. definingInstruction as! BeginApplyInst
890
918
// 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) !)
892
920
case let . initialized( initializer) :
893
921
switch initializer {
894
922
case let . store( initializingStore: store, initialAddress: _) :
895
923
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
898
928
}
899
929
break
900
930
case . argument, . yield:
901
931
// TODO: extend indirectly yielded scopes.
902
932
break
903
933
}
904
934
case let . owned( value) :
905
- return builder. createDestroyValue ( operand: value)
935
+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: value) )
906
936
case let . local( varInst) :
907
937
switch varInst {
908
938
case let . beginBorrow( beginBorrow) :
909
939
// FIXME: we may need to rewrite the dealloc_stack.
910
- return builder. createEndBorrow ( of: beginBorrow)
940
+ return SingleInlineArray ( element : builder. createEndBorrow ( of: beginBorrow) )
911
941
case let . moveValue( moveValue) :
912
- return builder. createDestroyValue ( operand: moveValue)
942
+ return SingleInlineArray ( element : builder. createDestroyValue ( operand: moveValue) )
913
943
}
914
944
default :
915
945
break
0 commit comments