Skip to content

Commit 37171e6

Browse files
committed
Handle mark_dependence [nonescaping] like a borrowing instruction.
1 parent d663924 commit 37171e6

File tree

10 files changed

+154
-40
lines changed

10 files changed

+154
-40
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ import SIL
145145
///
146146
/// Note: This must handle all instructions with a .borrow operand ownership.
147147
///
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.
152+
///
148153
/// TODO: replace BorrowIntroducingInstruction
149154
///
150155
/// TODO: Add non-escaping MarkDependence.
@@ -153,15 +158,21 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
153158
case storeBorrow(StoreBorrowInst)
154159
case beginApply(BeginApplyInst)
155160
case partialApply(PartialApplyInst)
161+
case markDependence(MarkDependenceInst)
156162
case startAsyncLet(BuiltinInst)
157163

158164
init?(_ inst: Instruction) {
159165
switch inst {
160-
case let bbi as BeginBorrowInst: self = .beginBorrow(bbi)
161-
case let sbi as StoreBorrowInst: self = .storeBorrow(sbi)
162-
case let bai as BeginApplyInst: self = .beginApply(bai)
166+
case let bbi as BeginBorrowInst:
167+
self = .beginBorrow(bbi)
168+
case let sbi as StoreBorrowInst:
169+
self = .storeBorrow(sbi)
170+
case let bai as BeginApplyInst:
171+
self = .beginApply(bai)
163172
case let pai as PartialApplyInst where pai.isOnStack:
164173
self = .partialApply(pai)
174+
case let mdi as MarkDependenceInst:
175+
self = .markDependence(mdi)
165176
case let bi as BuiltinInst
166177
where bi.id == .StartAsyncLetWithLocalBuffer:
167178
self = .startAsyncLet(bi)
@@ -172,11 +183,18 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
172183

173184
var instruction: Instruction {
174185
switch self {
175-
case .beginBorrow(let bbi): return bbi
176-
case .storeBorrow(let sbi): return sbi
177-
case .beginApply(let bai): return bai
178-
case .partialApply(let pai): return pai
179-
case .startAsyncLet(let bi): return bi
186+
case .beginBorrow(let bbi):
187+
return bbi
188+
case .storeBorrow(let sbi):
189+
return sbi
190+
case .beginApply(let bai):
191+
return bai
192+
case .partialApply(let pai):
193+
return pai
194+
case .markDependence(let mdi):
195+
return mdi
196+
case .startAsyncLet(let bi):
197+
return bi
180198
}
181199
}
182200

@@ -188,9 +206,17 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
188206
/// incoming value dominates or is consumed by an outer adjacent
189207
/// phi. See InteriorLiveness.
190208
///
191-
/// TODO: to hande reborrow-extended uses migrate ExtendedLiveness
209+
/// TODO: to hande reborrow-extended uses, migrate ExtendedLiveness
192210
/// to SwiftCompilerSources.
193211
///
212+
/// TODO: Handle .partialApply and .markDependence forwarded uses
213+
/// that are phi operands. Currently, partial_apply [on_stack]
214+
/// and mark_dependence [nonescaping] cannot be cloned, so walking
215+
/// through the phi safely returns dominated scope-ending operands.
216+
/// Instead, this could report the phi as a scope-ending use, and
217+
/// the client could decide whether to walk through them or to
218+
/// construct reborrow-extended liveness.
219+
///
194220
/// TODO: For instructions that are not a BeginBorrowValue, verify
195221
/// that scope ending instructions exist on all paths. These
196222
/// instructions should be complete after SILGen and never cloned to
@@ -206,8 +232,10 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
206232
}
207233
case .beginApply(let bai):
208234
return bai.token.uses.walk { return visitor($0) }
209-
case .partialApply(let pai):
210-
return visitForwardedUses(introducer: pai, context) {
235+
case .partialApply, .markDependence:
236+
let svi = instruction as! SingleValueInstruction
237+
assert(svi.ownership == .owned)
238+
return visitForwardedUses(introducer: svi, context) {
211239
switch $0 {
212240
case let .operand(operand):
213241
if operand.endsLifetime {
@@ -306,7 +334,7 @@ enum BeginBorrowValue {
306334
self = BeginBorrowValue(beginBorrow)!
307335
case let .beginApply(beginApply):
308336
self = BeginBorrowValue(beginApply.token)!
309-
case .storeBorrow, .partialApply, .startAsyncLet:
337+
case .storeBorrow, .partialApply, .markDependence, .startAsyncLet:
310338
return nil
311339
}
312340
}

SwiftCompilerSources/Sources/Optimizer/Utilities/OwnershipLiveness.swift

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ func computeInteriorLiveness(for definingValue: Value,
142142
/// - forwardingUse(of:isInnerlifetime:)
143143
/// - interiorPointerUse(of:into:)
144144
/// - pointerEscapingUse(of:)
145+
/// - dependentUse(of:into:)
145146
/// - borrowingUse(of:by:)
146147
/// - reborrowingUse(of:isInnerlifetime:)
147148
///
@@ -195,9 +196,6 @@ protocol OwnershipUseVisitor {
195196
mutating func interiorPointerUse(of: Operand, into address: Value)
196197
-> WalkResult
197198

198-
/// A use that creates a dependent value.
199-
mutating func dependentUse(of: Operand, into value: Value) -> WalkResult
200-
201199
/// A use that escapes information from its operand's value.
202200
///
203201
/// Note: this may not find all relevant pointer escapes, such as
@@ -206,6 +204,14 @@ protocol OwnershipUseVisitor {
206204
/// implement this as a fatalError.
207205
mutating func pointerEscapingUse(of operand: Operand) -> WalkResult
208206

207+
/// A use that creates an implicit borrow scope over the lifetime of
208+
/// an owned dependent value. The operand owership is .borrow, but
209+
/// there are no explicit scope-ending operations. Instead
210+
/// BorrowingInstruction.scopeEndingOperands will return the final
211+
/// consumes in the dependent value's forwaring chain.
212+
mutating func dependentUse(of operand: Operand, into value: Value)
213+
-> WalkResult
214+
209215
/// A use that is scoped to an inner borrow scope.
210216
///
211217
/// Call `visitInnerBorrowUses(of:)` to recursively classify any
@@ -334,8 +340,7 @@ extension OwnershipUseVisitor {
334340
return ownershipLeafUse(of: operand, isInnerLifetime: false)
335341

336342
case .borrow:
337-
return borrowingUse(of: operand,
338-
by: BorrowingInstruction(operand.instruction)!)
343+
return visitBorrowingUse(of: operand)
339344

340345
// TODO: Eventually, visit owned InteriorPointers as implicit borrows.
341346
case .interiorPointer, .trivialUse, .endBorrow, .reborrow,
@@ -365,8 +370,7 @@ extension OwnershipUseVisitor {
365370
return forwardingUse(of: operand, isInnerLifetime: false)
366371

367372
case .borrow:
368-
return borrowingUse(of: operand,
369-
by: BorrowingInstruction(operand.instruction)!)
373+
return visitBorrowingUse(of: operand)
370374

371375
case .interiorPointer:
372376
return visitInteriorPointerUse(of: operand)
@@ -376,6 +380,21 @@ extension OwnershipUseVisitor {
376380
}
377381
}
378382

383+
private mutating func visitBorrowingUse(of operand: Operand)
384+
-> WalkResult {
385+
switch operand.instruction {
386+
case let pai as PartialApplyInst:
387+
assert(pai.isOnStack)
388+
return dependentUse(of: operand, into: pai)
389+
case let mdi as MarkDependenceInst:
390+
assert(operand == mdi.baseOperand && mdi.isNonEscaping)
391+
return dependentUse(of: operand, into: mdi)
392+
default:
393+
return borrowingUse(of: operand,
394+
by: BorrowingInstruction(operand.instruction)!)
395+
}
396+
}
397+
379398
// TODO: Change ProjectBox ownership to InteriorPointer and allow
380399
// owned interior pointers.
381400
private mutating func visitInteriorPointerUse(of operand: Operand)
@@ -385,9 +404,6 @@ extension OwnershipUseVisitor {
385404
is OpenExistentialBoxInst:
386405
let svi = operand.instruction as! SingleValueInstruction
387406
return interiorPointerUse(of: operand, into: svi)
388-
case let mdi as MarkDependenceInst:
389-
assert(operand == mdi.baseOperand && mdi.isNonEscaping)
390-
return dependentUse(of: operand, into: mdi)
391407
default:
392408
return pointerEscapingUse(of: operand)
393409
}
@@ -554,6 +570,19 @@ extension InteriorUseWalker: OwnershipUseVisitor {
554570
return walkDownAddressUses(of: address)
555571
}
556572

573+
// Handle partial_apply [on_stack] and mark_dependence [nonescaping].
574+
//
575+
// TODO: Rather than walking down the owned uses, this could call
576+
// visitInnerBorrowUses, but we need to ensure all dependent values
577+
// are complete first:
578+
//
579+
// if let svi = borrowInst as! SingleValueInstruction,
580+
// svi.ownership == .owned {
581+
// if handleInner(borrowed: beginBorrow.value) == .abortWalk {
582+
// return .abortWalk
583+
// }
584+
// return visitInnerBorrowUses(of: borrowInst)
585+
// }
557586
mutating func dependentUse(of operand: Operand, into value: Value)
558587
-> WalkResult {
559588
// OSSA lifetime ignores trivial types.
@@ -665,7 +694,8 @@ extension InteriorUseWalker: AddressUseVisitor {
665694
}
666695

667696
private mutating func walkDownAddressUses(of address: Value) -> WalkResult {
668-
address.uses.ignoreTypeDependence.walk {
697+
assert(address.type.isAddress)
698+
return address.uses.ignoreTypeDependence.walk {
669699
// Record all uses
670700
if useVisitor($0) == .abortWalk {
671701
return .abortWalk
@@ -690,9 +720,9 @@ extension InteriorUseWalker {
690720
if phi.value.ownership == .guaranteed {
691721
return walkDown(guaranteedPhi: phi)
692722
}
693-
// This is a phi of a dependent value.
694-
// On-stack partial apply cannot be cloned, so all dependent
695-
// phis must be dominated.
723+
// This is a phi of a dependent value. partial_apply [on_stack]
724+
// and mark_dependence [nonescaping] cannot be cloned, so all
725+
// dependent phis must be dominated.
696726
assert(definingValue.parentBlock.dominates(phi.successor,
697727
functionContext.dominatorTree),
698728
"on-stack partial apply cannot be cloned")

docs/SIL.rst

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5523,15 +5523,14 @@ the dependency is on the current value stored in the address.
55235523
The optional ``nonescaping`` attribute indicates that no value derived
55245524
from ``%value`` escapes the lifetime of ``%base``. As with escaping
55255525
``mark_dependence``, all values transitively forwarded from ``%value``
5526-
must be destroyed within the lifetime of ``%base``. Unlike escaping
5526+
must be destroyed within the lifetime of ` `%base``. Unlike escaping
55275527
``mark_dependence``, this must be statically verifiable. Additionally,
55285528
unlike escaping ``mark_dependence``, derived values include copies of
55295529
``%value`` and values transitively forwarded from those copies. If
5530-
``%base`` is identical to ``%value`` this simply means that copies of
5531-
``%value`` do not outlive the original OSSA lifetime of
5532-
``%value``. Furthermore, unlike escaping ``mark_dependence``, no value
5533-
derived from ``%value`` may have a bitwise escape (conversion to
5534-
UnsafePointer) or pointer escape (unknown use).
5530+
``%base`` must not be identical to ``%value``. Unlike escaping
5531+
``mark_dependence``, no value derived from ``%value`` may have a
5532+
bitwise escape (conversion to UnsafePointer) or pointer escape
5533+
(unknown use).
55355534

55365535
is_unique
55375536
`````````

include/swift/SIL/OwnershipUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ class BorrowingOperandKind {
261261
TryApply,
262262
Yield,
263263
PartialApplyStack,
264+
MarkDependenceNonEscaping,
264265
BeginAsyncLet,
265266
};
266267

@@ -292,6 +293,8 @@ class BorrowingOperandKind {
292293
return Kind::Yield;
293294
case SILInstructionKind::PartialApplyInst:
294295
return Kind::PartialApplyStack;
296+
case SILInstructionKind::MarkDependenceInst:
297+
return Kind::MarkDependenceNonEscaping;
295298
case SILInstructionKind::BuiltinInst: {
296299
auto bi = cast<BuiltinInst>(i);
297300
if (bi->getBuiltinKind() == BuiltinValueKind::StartAsyncLetWithLocalBuffer) {
@@ -395,6 +398,7 @@ struct BorrowingOperand {
395398
case BorrowingOperandKind::TryApply:
396399
case BorrowingOperandKind::Yield:
397400
case BorrowingOperandKind::PartialApplyStack:
401+
case BorrowingOperandKind::MarkDependenceNonEscaping:
398402
case BorrowingOperandKind::BeginAsyncLet:
399403
return false;
400404
case BorrowingOperandKind::Branch:
@@ -428,6 +432,7 @@ struct BorrowingOperand {
428432
case BorrowingOperandKind::TryApply:
429433
case BorrowingOperandKind::Yield:
430434
case BorrowingOperandKind::PartialApplyStack:
435+
case BorrowingOperandKind::MarkDependenceNonEscaping:
431436
case BorrowingOperandKind::BeginAsyncLet:
432437
return false;
433438
}

include/swift/SIL/SILInstruction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8370,6 +8370,10 @@ class MarkDependenceInst
83708370
bool isNonEscaping() const {
83718371
return sharedUInt8().MarkDependenceInst.nonEscaping;
83728372
}
8373+
8374+
/// Visit the instructions that end the lifetime of an OSSA on-stack closure.
8375+
bool visitNonEscapingLifetimeEnds(llvm::function_ref<bool (Operand*)> func)
8376+
const;
83738377
};
83748378

83758379
/// Promote an Objective-C block that is on the stack to the heap, or simply

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,10 +654,15 @@ OperandOwnership OperandOwnershipClassifier::visitCopyBlockWithoutEscapingInst(
654654
OperandOwnership
655655
OperandOwnershipClassifier::visitMarkDependenceInst(MarkDependenceInst *mdi) {
656656
// If we are analyzing "the value", we forward ownership.
657-
if (getValue() == mdi->getValue()) {
657+
if (getOperandIndex() == MarkDependenceInst::Value) {
658658
return getOwnershipKind().getForwardingOperandOwnership(
659659
/*allowUnowned*/true);
660660
}
661+
if (getOperandIndex() == MarkDependenceInst::Base && mdi->isNonEscaping()) {
662+
// This creates a "dependent value", just like on-stack partial_apply, which
663+
// we treat like a borrow.
664+
return OperandOwnership::Borrow;
665+
}
661666
// FIXME: Add an end_dependence instruction so we can treat mark_dependence as
662667
// a borrow of the base (mark_dependence %base -> end_dependence is analogous
663668
// to a borrow scope).

lib/SIL/IR/SILInstruction.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,12 @@ bool SILInstruction::isTriviallyDuplicatable() const {
14011401
if (auto *PA = dyn_cast<PartialApplyInst>(this)) {
14021402
return !PA->isOnStack();
14031403
}
1404+
// Like partial_apply [onstack], mark_dependence [nonescaping] creates a
1405+
// borrow scope. We currently assume that a set of dominated scope-ending uses
1406+
// can be found.
1407+
if (auto *MD = dyn_cast<MarkDependenceInst>(this)) {
1408+
return !MD->isNonEscaping();
1409+
}
14041410

14051411
if (isa<OpenExistentialAddrInst>(this) || isa<OpenExistentialRefInst>(this) ||
14061412
isa<OpenExistentialMetatypeInst>(this) ||
@@ -1831,6 +1837,18 @@ PartialApplyInst::visitOnStackLifetimeEnds(
18311837
return !noUsers;
18321838
}
18331839

1840+
bool MarkDependenceInst::
1841+
visitNonEscapingLifetimeEnds(llvm::function_ref<bool (Operand *)> func) const {
1842+
assert(getFunction()->hasOwnership() && isNonEscaping()
1843+
&& "only meaningful for nonescaping dependencies");
1844+
bool noUsers = true;
1845+
1846+
if (!visitRecursivelyLifetimeEndingUses(this, noUsers, func)) {
1847+
return false;
1848+
}
1849+
return !noUsers;
1850+
}
1851+
18341852
PartialApplyInst *
18351853
DestroyValueInst::getNonescapingClosureAllocation() const {
18361854
SILValue operand = getOperand();

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,9 @@ void BorrowingOperandKind::print(llvm::raw_ostream &os) const {
621621
case Kind::PartialApplyStack:
622622
os << "PartialApply [stack]";
623623
return;
624+
case Kind::MarkDependenceNonEscaping:
625+
os << "MarkDependence [nonescaping]";
626+
return;
624627
case Kind::BeginAsyncLet:
625628
os << "BeginAsyncLet";
626629
return;
@@ -655,7 +658,8 @@ bool BorrowingOperand::hasEmptyRequiredEndingUses() const {
655658
case BorrowingOperandKind::StoreBorrow:
656659
case BorrowingOperandKind::BeginApply:
657660
case BorrowingOperandKind::BeginAsyncLet:
658-
case BorrowingOperandKind::PartialApplyStack: {
661+
case BorrowingOperandKind::PartialApplyStack:
662+
case BorrowingOperandKind::MarkDependenceNonEscaping: {
659663
return op->getUser()->hasUsesOfAnyResult();
660664
}
661665
case BorrowingOperandKind::Branch: {
@@ -716,13 +720,18 @@ bool BorrowingOperand::visitScopeEndingUses(
716720
return !deadApply;
717721
}
718722
case BorrowingOperandKind::PartialApplyStack: {
719-
auto user = cast<PartialApplyInst>(op->getUser());
723+
auto *user = cast<PartialApplyInst>(op->getUser());
720724
assert(user->isOnStack() && "escaping closures can't borrow");
721725
// The closure's borrow lifetimes end when the closure itself ends its
722726
// lifetime. That may happen transitively through conversions that forward
723727
// ownership of the closure.
724728
return user->visitOnStackLifetimeEnds(func);
725729
}
730+
case BorrowingOperandKind::MarkDependenceNonEscaping: {
731+
auto *user = cast<MarkDependenceInst>(op->getUser());
732+
assert(user->isNonEscaping() && "escaping dependencies don't borrow");
733+
return user->visitNonEscapingLifetimeEnds(func);
734+
}
726735
case BorrowingOperandKind::BeginAsyncLet: {
727736
auto user = cast<BuiltinInst>(op->getUser());
728737
// The async let ends its borrow when the task is ended.
@@ -780,6 +789,7 @@ BorrowedValue BorrowingOperand::getBorrowIntroducingUserResult() const {
780789
case BorrowingOperandKind::BeginApply:
781790
case BorrowingOperandKind::Yield:
782791
case BorrowingOperandKind::PartialApplyStack:
792+
case BorrowingOperandKind::MarkDependenceNonEscaping:
783793
case BorrowingOperandKind::BeginAsyncLet:
784794
case BorrowingOperandKind::StoreBorrow:
785795
return BorrowedValue();
@@ -810,6 +820,7 @@ SILValue BorrowingOperand::getScopeIntroducingUserResult() {
810820

811821
case BorrowingOperandKind::BeginAsyncLet:
812822
case BorrowingOperandKind::PartialApplyStack:
823+
case BorrowingOperandKind::MarkDependenceNonEscaping:
813824
case BorrowingOperandKind::BeginBorrow:
814825
case BorrowingOperandKind::StoreBorrow:
815826
return cast<SingleValueInstruction>(op->getUser());

0 commit comments

Comments
 (0)