23
23
24
24
import SIL
25
25
26
+ private let verbose = false
27
+
28
+ private func log( _ message: @autoclosure ( ) -> String ) {
29
+ if verbose {
30
+ print ( " ### \( message ( ) ) " )
31
+ }
32
+ }
33
+
26
34
/// Compute liveness and return a range, which the caller must deinitialize.
27
35
///
28
36
/// `definingValue` must introduce an OSSA lifetime. It may be either
@@ -66,15 +74,13 @@ typealias InnerScopeHandler = (Value) -> WalkResult
66
74
67
75
/// Compute liveness and return a range, which the caller must deinitialize.
68
76
///
69
- /// An OSSA lifetime begins with a single "defining" value, which must
70
- /// be owned, or must begin a borrow scope. A complete OSSA lifetime
71
- /// has a linear lifetime, meaning that it has a lifetime-ending use
72
- /// on all paths. Interior liveness computes liveness without assuming
73
- /// the lifetime is complete. To do this, it must find all "use
74
- /// points" and prove that the defining value is never propagated
75
- /// beyond those points. This is used to initially complete OSSA
76
- /// lifetimes and fix them after transformations that's don't preserve
77
- /// OSSA.
77
+ /// An OSSA lifetime begins with a single "defining" value, which must be owned, or must begin a borrow scope. A
78
+ /// complete OSSA lifetime has a linear lifetime, meaning that it has a lifetime-ending use on all paths. Interior
79
+ /// liveness computes liveness without assuming the lifetime is complete. To do this, it must find all "use points" and
80
+ /// prove that the defining value is never propagated beyond those points. This is used to initially complete OSSA
81
+ /// lifetimes and fix them after transformations that's don't preserve OSSA.
82
+ ///
83
+ /// The caller must check that `definingValue` has no pointer escape before calling this.
78
84
///
79
85
/// Invariants:
80
86
///
@@ -83,39 +89,100 @@ typealias InnerScopeHandler = (Value) -> WalkResult
83
89
/// - Liveness does not extend beyond lifetime-ending operations
84
90
/// (a.k.a. affine lifetimes).
85
91
///
86
- /// - All inner scopes are complete. (Use `innerScopeHandler` to
87
- /// complete them or bail-out).
88
- func computeInteriorLiveness( for definingValue: Value ,
89
- _ context: FunctionPassContext ,
90
- innerScopeHandler: InnerScopeHandler ? = nil ) -> InstructionRange {
91
-
92
- assert ( definingValue. ownership == . owned
93
- || BeginBorrowValue ( definingValue) != nil ,
94
- " value must define an OSSA lifetime " )
95
-
96
- var range = InstructionRange ( for: definingValue, context)
97
-
98
- var visitor = InteriorUseWalker ( definingValue: definingValue, context) {
99
- range. insert ( $0. instruction)
100
- return . continueWalk
101
- }
102
- defer { visitor. deinitialize ( ) }
103
- visitor. innerScopeHandler = innerScopeHandler
104
- let success = visitor. visitUses ( )
105
- switch visitor. pointerStatus {
106
- case . nonEscaping:
92
+ /// - All inner scopes are complete. (Use `innerScopeHandler` to complete them or bail-out).
93
+ func computeInteriorLiveness( for definingValue: Value , _ context: FunctionPassContext ,
94
+ innerScopeHandler: InnerScopeHandler ? = nil ) -> InstructionRange {
95
+ let result = InteriorLivenessResult . compute ( for: definingValue, ignoreEscape: false , context)
96
+ switch result. pointerStatus {
97
+ case . nonescaping:
107
98
break
108
- case let . escaping( operand ) :
99
+ case let . escaping( operands ) :
109
100
fatalError ( """
110
101
check findPointerEscape() before computing interior liveness.
111
- Pointer escape: \( operand . instruction)
102
+ Pointer escape: \( operands [ 0 ] . instruction)
112
103
""" )
113
104
case let . unknown( operand) :
114
105
fatalError ( " Unrecognized SIL address user \( operand. instruction) " )
115
106
}
116
- assert ( success == . continueWalk, " our visitor never fails " )
117
- assert ( visitor. unenclosedPhis. isEmpty, " missing adjacent phis " )
118
- return range
107
+ return result. range
108
+ }
109
+
110
+ /// Compute known liveness and return a range, which the caller must deinitialize.
111
+ ///
112
+ /// This computes a minimal liveness, ignoring pointer escaping uses.
113
+ func computeKnownLiveness( for definingValue: Value , _ context: FunctionPassContext ) -> InstructionRange {
114
+ return InteriorLivenessResult . compute ( for: definingValue, ignoreEscape: true , context) . range
115
+ }
116
+
117
+ /// If any interior pointer may escape, then record the first instance here. If 'ignoseEscape' is true, this
118
+ /// immediately aborts the walk, so further instances are unavailable.
119
+ ///
120
+ /// .escaping may either be a non-address operand with
121
+ /// .pointerEscape ownership, or and address operand that escapes
122
+ /// the address (address_to_pointer).
123
+ ///
124
+ /// .unknown is an address operand whose user is unrecognized.
125
+ enum InteriorPointerStatus : CustomDebugStringConvertible {
126
+ case nonescaping
127
+ case escaping( SingleInlineArray < Operand > )
128
+ case unknown( Operand )
129
+
130
+ mutating func setEscaping( operand: Operand ) {
131
+ switch self {
132
+ case . nonescaping:
133
+ self = . escaping( SingleInlineArray ( element: operand) )
134
+ case let . escaping( oldOperands) :
135
+ var newOperands = SingleInlineArray < Operand > ( )
136
+ newOperands. append ( contentsOf: oldOperands)
137
+ newOperands. append ( operand)
138
+ self = . escaping( newOperands)
139
+ case . unknown:
140
+ break
141
+ }
142
+ }
143
+
144
+ var debugDescription : String {
145
+ switch self {
146
+ case . nonescaping:
147
+ return " No pointer escape "
148
+ case let . escaping( operands) :
149
+ return " Pointer escapes: " + operands. map ( { " \( $0) " } ) . joined ( separator: " \n " )
150
+ case let . unknown( operand) :
151
+ return " Unknown use: \( operand) "
152
+ }
153
+ }
154
+ }
155
+
156
+ struct InteriorLivenessResult : CustomDebugStringConvertible {
157
+ let success : WalkResult
158
+ let range : InstructionRange
159
+ let pointerStatus : InteriorPointerStatus
160
+
161
+ static func compute( for definingValue: Value , ignoreEscape: Bool = false ,
162
+ _ context: FunctionPassContext ,
163
+ innerScopeHandler: InnerScopeHandler ? = nil ) -> InteriorLivenessResult {
164
+
165
+ assert ( definingValue. ownership == . owned || BeginBorrowValue ( definingValue) != nil ,
166
+ " value must define an OSSA lifetime " )
167
+
168
+ var range = InstructionRange ( for: definingValue, context)
169
+
170
+ var visitor = InteriorUseWalker ( definingValue: definingValue, ignoreEscape: ignoreEscape, context) {
171
+ range. insert ( $0. instruction)
172
+ return . continueWalk
173
+ }
174
+ defer { visitor. deinitialize ( ) }
175
+ visitor. innerScopeHandler = innerScopeHandler
176
+ let success = visitor. visitUses ( )
177
+ assert ( visitor. unenclosedPhis. isEmpty, " missing adjacent phis " )
178
+ let result = InteriorLivenessResult ( success: success, range: range, pointerStatus: visitor. pointerStatus)
179
+ log ( " Interior liveness for: \( definingValue) \n \( result) " )
180
+ return result
181
+ }
182
+
183
+ var debugDescription : String {
184
+ " \( success) \n \( range) \n \( pointerStatus) "
185
+ }
119
186
}
120
187
121
188
/// Classify ownership uses. This reduces operand ownership to a
@@ -455,6 +522,7 @@ struct InteriorUseWalker {
455
522
var context : Context { functionContext }
456
523
457
524
let definingValue : Value
525
+ let ignoreEscape : Bool
458
526
let useVisitor : ( Operand ) -> WalkResult
459
527
460
528
var innerScopeHandler : InnerScopeHandler ? = nil
@@ -470,33 +538,20 @@ struct InteriorUseWalker {
470
538
471
539
var function : Function { definingValue. parentFunction }
472
540
473
- /// If any interior pointer may escape, then record the first instance
474
- /// here. This immediately aborts the walk, so further instances are
475
- /// unavailable.
476
- ///
477
- /// .escaping may either be a non-address operand with
478
- /// .pointerEscape ownership, or and address operand that escapes
479
- /// the address (address_to_pointer).
480
- ///
481
- /// .unknown is an address operand whose user is unrecognized.
482
- enum InteriorPointerStatus {
483
- case nonEscaping
484
- case escaping( Operand )
485
- case unknown( Operand )
486
- }
487
- var pointerStatus : InteriorPointerStatus = . nonEscaping
541
+ var pointerStatus : InteriorPointerStatus = . nonescaping
488
542
489
543
private var visited : ValueSet
490
544
491
545
mutating func deinitialize( ) {
492
546
visited. deinitialize ( )
493
547
}
494
548
495
- init ( definingValue: Value , _ context: FunctionPassContext ,
549
+ init ( definingValue: Value , ignoreEscape : Bool , _ context: FunctionPassContext ,
496
550
visitor: @escaping ( Operand ) -> WalkResult ) {
497
551
assert ( !definingValue. type. isAddress, " address values have no ownership " )
498
552
self . functionContext = context
499
553
self . definingValue = definingValue
554
+ self . ignoreEscape = ignoreEscape
500
555
self . useVisitor = visitor
501
556
self . visited = ValueSet ( context)
502
557
}
@@ -598,8 +653,8 @@ extension InteriorUseWalker: OwnershipUseVisitor {
598
653
if useVisitor ( operand) == . abortWalk {
599
654
return . abortWalk
600
655
}
601
- pointerStatus = . escaping ( operand)
602
- return . abortWalk
656
+ pointerStatus. setEscaping ( operand : operand)
657
+ return ignoreEscape ? . continueWalk : . abortWalk
603
658
}
604
659
605
660
// Call the innerScopeHandler before visiting the scope-ending uses.
@@ -695,8 +750,8 @@ extension InteriorUseWalker: AddressUseVisitor {
695
750
}
696
751
697
752
mutating func escapingAddressUse( of operand: Operand ) -> WalkResult {
698
- pointerStatus = . escaping ( operand)
699
- return . abortWalk
753
+ pointerStatus. setEscaping ( operand : operand)
754
+ return ignoreEscape ? . continueWalk : . abortWalk
700
755
}
701
756
702
757
mutating func unknownAddressUse( of operand: Operand ) -> WalkResult {
@@ -894,7 +949,7 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") {
894
949
var range = InstructionRange ( for: value, context)
895
950
defer { range. deinitialize ( ) }
896
951
897
- var visitor = InteriorUseWalker ( definingValue: value, context) {
952
+ var visitor = InteriorUseWalker ( definingValue: value, ignoreEscape : true , context) {
898
953
range. insert ( $0. instruction)
899
954
return . continueWalk
900
955
}
@@ -903,10 +958,12 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") {
903
958
let success = visitor. visitUses ( )
904
959
905
960
switch visitor. pointerStatus {
906
- case . nonEscaping :
961
+ case . nonescaping :
907
962
break
908
- case let . escaping( operand) :
909
- print ( " Pointer escape: \( operand. instruction) " )
963
+ case let . escaping( operands) :
964
+ for operand in operands {
965
+ print ( " Pointer escape: \( operand. instruction) " )
966
+ }
910
967
case let . unknown( operand) :
911
968
print ( " Unrecognized SIL address user \( operand. instruction) " )
912
969
}
0 commit comments