Skip to content

Commit fda5392

Browse files
committed
Add support for borrowed-from to BorrowingInstruction.
All instructions with a "Borrow" operand ownership must be valid BorrowingInstructions.
1 parent 0871c96 commit fda5392

File tree

2 files changed

+72
-55
lines changed

2 files changed

+72
-55
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,18 @@ import SIL
142142
///
143143
/// Note: This must handle all instructions with a .borrow operand ownership.
144144
///
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.
149148
///
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.
151152
///
152-
/// TODO: Add non-escaping MarkDependence.
153+
/// TODO: replace BorrowIntroducingInstruction with this.
153154
enum BorrowingInstruction : CustomStringConvertible, Hashable {
154155
case beginBorrow(BeginBorrowInst)
156+
case borrowedFrom(BorrowedFromInst)
155157
case storeBorrow(StoreBorrowInst)
156158
case beginApply(BeginApplyInst)
157159
case partialApply(PartialApplyInst)
@@ -162,13 +164,18 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
162164
switch inst {
163165
case let bbi as BeginBorrowInst:
164166
self = .beginBorrow(bbi)
167+
case let bfi as BorrowedFromInst:
168+
self = .borrowedFrom(bfi)
165169
case let sbi as StoreBorrowInst:
166170
self = .storeBorrow(sbi)
167171
case let bai as BeginApplyInst:
168172
self = .beginApply(bai)
169173
case let pai as PartialApplyInst where !pai.mayEscape:
170174
self = .partialApply(pai)
171175
case let mdi as MarkDependenceInst:
176+
guard mdi.isNonEscaping else {
177+
return nil
178+
}
172179
self = .markDependence(mdi)
173180
case let bi as BuiltinInst
174181
where bi.id == .StartAsyncLetWithLocalBuffer:
@@ -182,6 +189,8 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
182189
switch self {
183190
case .beginBorrow(let bbi):
184191
return bbi
192+
case .borrowedFrom(let bfi):
193+
return bfi
185194
case .storeBorrow(let sbi):
186195
return sbi
187196
case .beginApply(let bai):
@@ -197,61 +206,36 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
197206

198207
/// Visit the operands that end the local borrow scope.
199208
///
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).
210213
///
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.
213217
///
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 {
229221
switch self {
230222
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
234227
}
228+
return visitEndBorrows(value: instruction as! SingleValueInstruction, context, visitor)
235229
case .beginApply(let bai):
236230
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
254237
}
238+
return visitOwnedDependent(value: mdi, context, visitor)
255239
case .startAsyncLet(let builtin):
256240
return builtin.uses.walk {
257241
if let builtinUser = $0.instruction as? BuiltinInst,
@@ -262,6 +246,34 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
262246
}
263247
}
264248
}
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+
}
265277

266278
var description: String { instruction.description }
267279
}
@@ -338,9 +350,12 @@ enum BeginBorrowValue {
338350
init?(resultOf borrowInstruction: BorrowingInstruction) {
339351
switch borrowInstruction {
340352
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)
342357
case let .beginApply(beginApply):
343-
self = BeginBorrowValue(beginApply.token)!
358+
self.init(beginApply.token)
344359
case .storeBorrow, .partialApply, .markDependence, .startAsyncLet:
345360
return nil
346361
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,8 @@ extension LifetimeDependenceDefUseWalker {
771771
switch borrowInst {
772772
case let .beginBorrow(bbi):
773773
return walkDownUses(of: bbi, using: operand)
774+
case let .borrowedFrom(bfi):
775+
return walkDownUses(of: bfi, using: operand)
774776
case let .storeBorrow(sbi):
775777
return walkDownAddressUses(of: sbi)
776778
case .beginApply:

0 commit comments

Comments
 (0)