@@ -134,27 +134,26 @@ import SIL
134134
135135/// A scoped instruction that borrows one or more operands.
136136///
137- /// If this instruction produces a borrowed value, then
138- /// BeginBorrowValue(resultOf: self) != nil.
137+ /// If this instruction produces a borrowed value, then BeginBorrowValue(resultOf: self) != nil.
139138///
140- /// This does not include instructions like `apply` and `try_apply` that
141- /// instantaneously borrow a value from the caller.
139+ /// This does not include instructions like `apply` and `try_apply` that instantaneously borrow a value from the caller.
142140///
143- /// This does not include `load_borrow` because it borrows a memory
144- /// location, not the value of its operand.
141+ /// This does not include `load_borrow` because it borrows a memory location, not the value of its operand.
145142///
146143/// Note: This must handle all instructions with a .borrow operand ownership.
147144///
148- /// Note: mark_dependence is a BorrowingInstruction because it creates
149- /// a borrow scope for its base operand. Its result, however, is not a
150- /// BeginBorrowValue. It is instead a ForwardingInstruction relative
151- /// to its value operand.
145+ /// Note: borrowed_from is a BorrowingInstruction because it creates a borrow scope for its enclosing operands. Its
146+ /// result, however, is only a BeginBorrowValue (.reborrow) if it forwards a reborrow phi. Otherwise, it simply forwards
147+ /// a guaranteed value and does not introduce a separate borrow scope.
152148///
153- /// TODO: replace BorrowIntroducingInstruction
149+ /// Note: mark_dependence [nonescaping] is a BorrowingInstruction because it creates a borrow scope for its base
150+ /// operand. Its result, however, is not a BeginBorrowValue. Instead it is a ForwardingInstruction relative to its value
151+ /// operand.
154152///
155- /// TODO: Add non-escaping MarkDependence .
153+ /// TODO: replace BorrowIntroducingInstruction with this .
156154enum BorrowingInstruction : CustomStringConvertible , Hashable {
157155 case beginBorrow( BeginBorrowInst )
156+ case borrowedFrom( BorrowedFromInst )
158157 case storeBorrow( StoreBorrowInst )
159158 case beginApply( BeginApplyInst )
160159 case partialApply( PartialApplyInst )
@@ -165,13 +164,18 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
165164 switch inst {
166165 case let bbi as BeginBorrowInst :
167166 self = . beginBorrow( bbi)
167+ case let bfi as BorrowedFromInst :
168+ self = . borrowedFrom( bfi)
168169 case let sbi as StoreBorrowInst :
169170 self = . storeBorrow( sbi)
170171 case let bai as BeginApplyInst :
171172 self = . beginApply( bai)
172173 case let pai as PartialApplyInst where !pai. mayEscape:
173174 self = . partialApply( pai)
174175 case let mdi as MarkDependenceInst :
176+ guard mdi. isNonEscaping else {
177+ return nil
178+ }
175179 self = . markDependence( mdi)
176180 case let bi as BuiltinInst
177181 where bi. id == . StartAsyncLetWithLocalBuffer:
@@ -185,6 +189,8 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
185189 switch self {
186190 case . beginBorrow( let bbi) :
187191 return bbi
192+ case . borrowedFrom( let bfi) :
193+ return bfi
188194 case . storeBorrow( let sbi) :
189195 return sbi
190196 case . beginApply( let bai) :
@@ -198,65 +204,88 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
198204 }
199205 }
200206
201- /// Visit the operands that end the local borrow scope.
202- ///
203- /// Note: When this instruction's result is BeginBorrowValue the
204- /// scopeEndingOperand may include reborrows. To find all uses that
205- /// contribute to liveness, the caller needs to determine whether an
206- /// incoming value dominates or is consumed by an outer adjacent
207- /// phi. See InteriorLiveness.
208- ///
209- /// FIXME: To generate conservatively correct liveness, this should return
210- /// .abortWalk if this is a mark_dependence and the scope-ending use is not
211- /// the last in the function (e.g. a store rather than a destroy or return).
212- /// The client needs to use LifetimeDependenceDefUseWalker to do better.
213- ///
214- /// TODO: to handle reborrow-extended uses, migrate ExtendedLiveness
215- /// to SwiftCompilerSources.
216- ///
217- /// TODO: Handle .partialApply and .markDependence forwarded uses
218- /// that are phi operands. Currently, partial_apply [on_stack]
219- /// and mark_dependence [nonescaping] cannot be cloned, so walking
220- /// through the phi safely returns dominated scope-ending operands.
221- /// Instead, this could report the phi as a scope-ending use, and
222- /// the client could decide whether to walk through them or to
223- /// construct reborrow-extended liveness.
224- ///
225- /// TODO: For instructions that are not a BeginBorrowValue, verify
226- /// that scope ending instructions exist on all paths. These
227- /// instructions should be complete after SILGen and never cloned to
228- /// produce phis.
229- func visitScopeEndingOperands( _ context: Context ,
230- visitor: @escaping ( Operand ) -> WalkResult )
231- -> WalkResult {
207+ var innerValue : Value ? {
208+ if let dependent = dependentValue {
209+ return dependent
210+ }
211+ return scopedValue
212+ }
213+
214+ var dependentValue : Value ? {
215+ switch self {
216+ case . borrowedFrom( let bfi) :
217+ let phi = bfi. borrowedPhi
218+ if phi. isReborrow {
219+ return nil
220+ }
221+ return phi. value
222+ case . markDependence( let mdi) :
223+ if mdi. hasScopedLifetime {
224+ return nil
225+ }
226+ return mdi
227+ case . beginBorrow, . storeBorrow, . beginApply, . partialApply, . startAsyncLet:
228+ return nil
229+ }
230+ }
231+
232+ /// If this is valid, then visitScopeEndingOperands succeeds.
233+ var scopedValue : Value ? {
232234 switch self {
233235 case . beginBorrow, . storeBorrow:
234- let svi = instruction as! SingleValueInstruction
235- return svi. uses. filterUsers ( ofType: EndBorrowInst . self) . walk {
236- visitor ( $0)
236+ return instruction as! SingleValueInstruction
237+ case let . borrowedFrom( bfi) :
238+ let phi = bfi. borrowedPhi
239+ guard phi. isReborrow else {
240+ return nil
237241 }
242+ return phi. value
238243 case . beginApply( let bai) :
239- return bai. token. uses. walk { return visitor ( $0) }
240- case . partialApply, . markDependence:
241- let svi = instruction as! SingleValueInstruction
242- assert ( svi. ownership == . owned)
243- return visitForwardedUses ( introducer: svi, context) {
244- switch $0 {
245- case let . operand( operand) :
246- if operand. endsLifetime {
247- return visitor ( operand)
248- }
249- return . continueWalk
250- case let . deadValue( _, operand) :
251- if let operand = operand {
252- assert ( !operand. endsLifetime,
253- " a dead forwarding instruction cannot end a lifetime " )
254- }
255- return . continueWalk
256- }
244+ return bai. token
245+ case . partialApply( let pai) :
246+ // We currently assume that closure lifetimes are always complete (destroyed on all paths).
247+ return pai
248+ case . markDependence( let mdi) :
249+ guard mdi. hasScopedLifetime else {
250+ return nil
257251 }
252+ return mdi
258253 case . startAsyncLet( let builtin) :
259- return builtin. uses. walk {
254+ return builtin
255+ }
256+ }
257+
258+ /// Visit the operands that end the local borrow scope.
259+ ///
260+ /// Returns .abortWalk if the borrow scope cannot be determined from lifetime-ending uses. For example:
261+ /// - borrowed_from where 'borrowedPhi.isReborrow == false'
262+ /// - non-owned mark_dependence [nonescaping]
263+ /// - owned mark_dependence [nonescaping] with a ~Escapable result (LifetimeDependenceDefUseWalker is needed).
264+ ///
265+ /// Note: .partialApply and .markDependence cannot currently be forwarded to phis because partial_apply [on_stack] and
266+ /// mark_dependence [nonescaping] cannot be cloned. Walking through the phi therefore safely returns dominated
267+ /// scope-ending operands. Handling phis here requires the equivalent of borrowed_from for owned values.
268+ ///
269+ /// TODO: For instructions that are not a BeginBorrowValue, verify that scope ending instructions exist on all
270+ /// paths. These instructions should be complete after SILGen and never cloned to produce phis.
271+ func visitScopeEndingOperands( _ context: Context , visitor: @escaping ( Operand ) -> WalkResult ) -> WalkResult {
272+ guard let val = scopedValue else {
273+ return . abortWalk
274+ }
275+ switch self {
276+ case . beginBorrow, . storeBorrow:
277+ return visitEndBorrows ( value: val, context, visitor)
278+ case . borrowedFrom:
279+ return visitEndBorrows ( value: val, context, visitor)
280+ case . beginApply:
281+ return val. uses. walk { return visitor ( $0) }
282+ case . partialApply:
283+ // We currently assume that closure lifetimes are always complete (destroyed on all paths).
284+ return visitOwnedDependent ( value: val, context, visitor)
285+ case . markDependence:
286+ return visitOwnedDependent ( value: val, context, visitor)
287+ case . startAsyncLet:
288+ return val. uses. walk {
260289 if let builtinUser = $0. instruction as? BuiltinInst ,
261290 builtinUser. id == . EndAsyncLetLifetime {
262291 return visitor ( $0)
@@ -265,6 +294,34 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
265294 }
266295 }
267296 }
297+ }
298+
299+ extension BorrowingInstruction {
300+ private func visitEndBorrows( value: Value , _ context: Context , _ visitor: @escaping ( Operand ) -> WalkResult )
301+ -> WalkResult {
302+ return value. lookThroughBorrowedFromUser. uses. filterUsers ( ofType: EndBorrowInst . self) . walk {
303+ visitor ( $0)
304+ }
305+ }
306+
307+ private func visitOwnedDependent( value: Value , _ context: Context , _ visitor: @escaping ( Operand ) -> WalkResult )
308+ -> WalkResult {
309+ return visitForwardedUses ( introducer: value, context) {
310+ switch $0 {
311+ case let . operand( operand) :
312+ if operand. endsLifetime {
313+ return visitor ( operand)
314+ }
315+ return . continueWalk
316+ case let . deadValue( _, operand) :
317+ if let operand = operand {
318+ assert ( !operand. endsLifetime,
319+ " a dead forwarding instruction cannot end a lifetime " )
320+ }
321+ return . continueWalk
322+ }
323+ }
324+ }
268325
269326 var description : String { instruction. description }
270327}
@@ -341,9 +398,12 @@ enum BeginBorrowValue {
341398 init ? ( resultOf borrowInstruction: BorrowingInstruction ) {
342399 switch borrowInstruction {
343400 case let . beginBorrow( beginBorrow) :
344- self = BeginBorrowValue ( beginBorrow) !
401+ self . init ( beginBorrow)
402+ case let . borrowedFrom( borrowedFrom) :
403+ // only returns non-nil if borrowedPhi is a reborrow
404+ self . init ( borrowedFrom. borrowedPhi. value)
345405 case let . beginApply( beginApply) :
346- self = BeginBorrowValue ( beginApply. token) !
406+ self . init ( beginApply. token)
347407 case . storeBorrow, . partialApply, . markDependence, . startAsyncLet:
348408 return nil
349409 }
0 commit comments