@@ -142,16 +142,18 @@ import SIL
142
142
///
143
143
/// Note: This must handle all instructions with a .borrow operand ownership.
144
144
///
145
- /// Note: mark_dependence is a BorrowingInstruction because it creates
146
- /// a borrow scope for its base operand. Its result, however, is not a
147
- /// BeginBorrowValue. It is instead a ForwardingInstruction relative
148
- /// to its value operand.
145
+ /// Note: borrowed_from is a BorrowingInstruction because it creates a borrow scope for its base operand. Its result,
146
+ /// however, is only a BeginBorrowValue (.reborrow) if it forwards a reborrow phi. Otherwise, it simply forwards a
147
+ /// guaranteed value and does not introduce a separate borrow scope.
149
148
///
150
- /// 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.
151
152
///
152
- /// TODO: Add non-escaping MarkDependence .
153
+ /// TODO: replace BorrowIntroducingInstruction with this .
153
154
enum BorrowingInstruction : CustomStringConvertible , Hashable {
154
155
case beginBorrow( BeginBorrowInst )
156
+ case borrowedFrom( BorrowedFromInst )
155
157
case storeBorrow( StoreBorrowInst )
156
158
case beginApply( BeginApplyInst )
157
159
case partialApply( PartialApplyInst )
@@ -162,13 +164,18 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
162
164
switch inst {
163
165
case let bbi as BeginBorrowInst :
164
166
self = . beginBorrow( bbi)
167
+ case let bfi as BorrowedFromInst :
168
+ self = . borrowedFrom( bfi)
165
169
case let sbi as StoreBorrowInst :
166
170
self = . storeBorrow( sbi)
167
171
case let bai as BeginApplyInst :
168
172
self = . beginApply( bai)
169
173
case let pai as PartialApplyInst where !pai. mayEscape:
170
174
self = . partialApply( pai)
171
175
case let mdi as MarkDependenceInst :
176
+ guard mdi. isNonEscaping else {
177
+ return nil
178
+ }
172
179
self = . markDependence( mdi)
173
180
case let bi as BuiltinInst
174
181
where bi. id == . StartAsyncLetWithLocalBuffer:
@@ -182,6 +189,8 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
182
189
switch self {
183
190
case . beginBorrow( let bbi) :
184
191
return bbi
192
+ case . borrowedFrom( let bfi) :
193
+ return bfi
185
194
case . storeBorrow( let sbi) :
186
195
return sbi
187
196
case . beginApply( let bai) :
@@ -197,61 +206,36 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
197
206
198
207
/// Visit the operands that end the local borrow scope.
199
208
///
200
- /// Note: When this instruction's result is BeginBorrowValue the
201
- /// scopeEndingOperand may include reborrows. To find all uses that
202
- /// contribute to liveness, the caller needs to determine whether an
203
- /// incoming value dominates or is consumed by an outer adjacent
204
- /// phi. See InteriorLiveness.
205
- ///
206
- /// FIXME: To generate conservatively correct liveness, this should return
207
- /// .abortWalk if this is a mark_dependence and the scope-ending use is not
208
- /// the last in the function (e.g. a store rather than a destroy or return).
209
- /// The client needs to use LifetimeDependenceDefUseWalker to do better.
209
+ /// Returns .abortWalk if the borrow scope cannot be determined from lifetime-ending uses. For example:
210
+ /// - borrowed_from where 'borrowedPhi.isReborrow == false'
211
+ /// - non-owned mark_dependence [nonescaping]
212
+ /// - owned mark_dependence [nonescaping] with a ~Escapable result (LifetimeDependenceDefUseWalker is needed).
210
213
///
211
- /// TODO: to handle reborrow-extended uses, migrate ExtendedLiveness
212
- /// to SwiftCompilerSources.
214
+ /// Note: .partialApply and .markDependence cannot currently be forwarded to phis because partial_apply [on_stack] and
215
+ /// mark_dependence [nonescaping] cannot be cloned. Walking through the phi therefore safely returns dominated
216
+ /// scope-ending operands. Handling phis here requires the equivalent of borrowed_from for owned values.
213
217
///
214
- /// TODO: Handle .partialApply and .markDependence forwarded uses
215
- /// that are phi operands. Currently, partial_apply [on_stack]
216
- /// and mark_dependence [nonescaping] cannot be cloned, so walking
217
- /// through the phi safely returns dominated scope-ending operands.
218
- /// Instead, this could report the phi as a scope-ending use, and
219
- /// the client could decide whether to walk through them or to
220
- /// construct reborrow-extended liveness.
221
- ///
222
- /// TODO: For instructions that are not a BeginBorrowValue, verify
223
- /// that scope ending instructions exist on all paths. These
224
- /// instructions should be complete after SILGen and never cloned to
225
- /// produce phis.
226
- func visitScopeEndingOperands( _ context: Context ,
227
- visitor: @escaping ( Operand ) -> WalkResult )
228
- -> WalkResult {
218
+ /// TODO: For instructions that are not a BeginBorrowValue, verify that scope ending instructions exist on all
219
+ /// paths. These instructions should be complete after SILGen and never cloned to produce phis.
220
+ func visitScopeEndingOperands( _ context: Context , visitor: @escaping ( Operand ) -> WalkResult ) -> WalkResult {
229
221
switch self {
230
222
case . beginBorrow, . storeBorrow:
231
- let svi = instruction as! SingleValueInstruction
232
- return svi. uses. filterUsers ( ofType: EndBorrowInst . self) . walk {
233
- visitor ( $0)
223
+ return visitEndBorrows ( value: instruction as! SingleValueInstruction , context, visitor)
224
+ case let . borrowedFrom( bfi) :
225
+ guard bfi. borrowedPhi. isReborrow else {
226
+ return . abortWalk
234
227
}
228
+ return visitEndBorrows ( value: instruction as! SingleValueInstruction , context, visitor)
235
229
case . beginApply( let bai) :
236
230
return bai. token. uses. walk { return visitor ( $0) }
237
- case . partialApply, . markDependence:
238
- let svi = instruction as! SingleValueInstruction
239
- assert ( svi. ownership == . owned)
240
- return visitForwardedUses ( introducer: svi, context) {
241
- switch $0 {
242
- case let . operand( operand) :
243
- if operand. endsLifetime {
244
- return visitor ( operand)
245
- }
246
- return . continueWalk
247
- case let . deadValue( _, operand) :
248
- if let operand = operand {
249
- assert ( !operand. endsLifetime,
250
- " a dead forwarding instruction cannot end a lifetime " )
251
- }
252
- return . continueWalk
253
- }
231
+ case . partialApply( let pai) :
232
+ // We currently assume that closure lifetimes are always complete (destroyed on all paths).
233
+ return visitOwnedDependent ( value: pai, context, visitor)
234
+ case . markDependence( let mdi) :
235
+ guard mdi. ownership == . owned, mdi. type. isEscapable ( in: mdi. parentFunction) else {
236
+ return . abortWalk
254
237
}
238
+ return visitOwnedDependent ( value: mdi, context, visitor)
255
239
case . startAsyncLet( let builtin) :
256
240
return builtin. uses. walk {
257
241
if let builtinUser = $0. instruction as? BuiltinInst ,
@@ -262,6 +246,34 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
262
246
}
263
247
}
264
248
}
249
+ }
250
+
251
+ extension BorrowingInstruction {
252
+ private func visitEndBorrows( value: Value , _ context: Context , _ visitor: @escaping ( Operand ) -> WalkResult )
253
+ -> WalkResult {
254
+ return value. uses. filterUsers ( ofType: EndBorrowInst . self) . walk {
255
+ visitor ( $0)
256
+ }
257
+ }
258
+
259
+ private func visitOwnedDependent( value: Value , _ context: Context , _ visitor: @escaping ( Operand ) -> WalkResult )
260
+ -> WalkResult {
261
+ return visitForwardedUses ( introducer: value, context) {
262
+ switch $0 {
263
+ case let . operand( operand) :
264
+ if operand. endsLifetime {
265
+ return visitor ( operand)
266
+ }
267
+ return . continueWalk
268
+ case let . deadValue( _, operand) :
269
+ if let operand = operand {
270
+ assert ( !operand. endsLifetime,
271
+ " a dead forwarding instruction cannot end a lifetime " )
272
+ }
273
+ return . continueWalk
274
+ }
275
+ }
276
+ }
265
277
266
278
var description : String { instruction. description }
267
279
}
@@ -338,9 +350,12 @@ enum BeginBorrowValue {
338
350
init ? ( resultOf borrowInstruction: BorrowingInstruction ) {
339
351
switch borrowInstruction {
340
352
case let . beginBorrow( beginBorrow) :
341
- self = BeginBorrowValue ( beginBorrow) !
353
+ self . init ( beginBorrow)
354
+ case let . borrowedFrom( borrowedFrom) :
355
+ // only returns non-nil if borrowedPhi is a reborrow
356
+ self . init ( borrowedFrom. borrowedPhi. value)
342
357
case let . beginApply( beginApply) :
343
- self = BeginBorrowValue ( beginApply. token) !
358
+ self . init ( beginApply. token)
344
359
case . storeBorrow, . partialApply, . markDependence, . startAsyncLet:
345
360
return nil
346
361
}
0 commit comments