2323
2424import SIL
2525
26+ private let verbose = false
27+
28+ private func log( _ message: @autoclosure ( ) -> String ) {
29+ if verbose {
30+ print ( " ### \( message ( ) ) " )
31+ }
32+ }
33+
2634/// Compute liveness and return a range, which the caller must deinitialize.
2735///
2836/// `definingValue` must introduce an OSSA lifetime. It may be either
@@ -66,15 +74,13 @@ typealias InnerScopeHandler = (Value) -> WalkResult
6674
6775/// Compute liveness and return a range, which the caller must deinitialize.
6876///
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.
7884///
7985/// Invariants:
8086///
@@ -83,39 +89,100 @@ typealias InnerScopeHandler = (Value) -> WalkResult
8389/// - Liveness does not extend beyond lifetime-ending operations
8490/// (a.k.a. affine lifetimes).
8591///
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:
10798 break
108- case let . escaping( operand ) :
99+ case let . escaping( operands ) :
109100 fatalError ( """
110101 check findPointerEscape() before computing interior liveness.
111- Pointer escape: \( operand . instruction)
102+ Pointer escape: \( operands [ 0 ] . instruction)
112103 """ )
113104 case let . unknown( operand) :
114105 fatalError ( " Unrecognized SIL address user \( operand. instruction) " )
115106 }
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+ }
119186}
120187
121188/// Classify ownership uses. This reduces operand ownership to a
@@ -455,6 +522,7 @@ struct InteriorUseWalker {
455522 var context : Context { functionContext }
456523
457524 let definingValue : Value
525+ let ignoreEscape : Bool
458526 let useVisitor : ( Operand ) -> WalkResult
459527
460528 var innerScopeHandler : InnerScopeHandler ? = nil
@@ -470,33 +538,20 @@ struct InteriorUseWalker {
470538
471539 var function : Function { definingValue. parentFunction }
472540
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
488542
489543 private var visited : ValueSet
490544
491545 mutating func deinitialize( ) {
492546 visited. deinitialize ( )
493547 }
494548
495- init ( definingValue: Value , _ context: FunctionPassContext ,
549+ init ( definingValue: Value , ignoreEscape : Bool , _ context: FunctionPassContext ,
496550 visitor: @escaping ( Operand ) -> WalkResult ) {
497551 assert ( !definingValue. type. isAddress, " address values have no ownership " )
498552 self . functionContext = context
499553 self . definingValue = definingValue
554+ self . ignoreEscape = ignoreEscape
500555 self . useVisitor = visitor
501556 self . visited = ValueSet ( context)
502557 }
@@ -598,8 +653,8 @@ extension InteriorUseWalker: OwnershipUseVisitor {
598653 if useVisitor ( operand) == . abortWalk {
599654 return . abortWalk
600655 }
601- pointerStatus = . escaping ( operand)
602- return . abortWalk
656+ pointerStatus. setEscaping ( operand : operand)
657+ return ignoreEscape ? . continueWalk : . abortWalk
603658 }
604659
605660 // Call the innerScopeHandler before visiting the scope-ending uses.
@@ -695,8 +750,8 @@ extension InteriorUseWalker: AddressUseVisitor {
695750 }
696751
697752 mutating func escapingAddressUse( of operand: Operand ) -> WalkResult {
698- pointerStatus = . escaping ( operand)
699- return . abortWalk
753+ pointerStatus. setEscaping ( operand : operand)
754+ return ignoreEscape ? . continueWalk : . abortWalk
700755 }
701756
702757 mutating func unknownAddressUse( of operand: Operand ) -> WalkResult {
@@ -894,7 +949,7 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") {
894949 var range = InstructionRange ( for: value, context)
895950 defer { range. deinitialize ( ) }
896951
897- var visitor = InteriorUseWalker ( definingValue: value, context) {
952+ var visitor = InteriorUseWalker ( definingValue: value, ignoreEscape : true , context) {
898953 range. insert ( $0. instruction)
899954 return . continueWalk
900955 }
@@ -903,10 +958,12 @@ let interiorLivenessTest = FunctionTest("interior_liveness_swift") {
903958 let success = visitor. visitUses ( )
904959
905960 switch visitor. pointerStatus {
906- case . nonEscaping :
961+ case . nonescaping :
907962 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+ }
910967 case let . unknown( operand) :
911968 print ( " Unrecognized SIL address user \( operand. instruction) " )
912969 }
0 commit comments