Skip to content

Commit d7b9149

Browse files
committed
Fix visitNonEscapingLifetimeEnds to handle mark_dependence uses
Now it visits unknown uses separately rather than asserting.
1 parent f8b5bac commit d7b9149

File tree

11 files changed

+110
-96
lines changed

11 files changed

+110
-96
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/BorrowUtils.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ enum BorrowingInstruction : CustomStringConvertible, Hashable {
206206
/// incoming value dominates or is consumed by an outer adjacent
207207
/// phi. See InteriorLiveness.
208208
///
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+
///
209214
/// TODO: to hande reborrow-extended uses, migrate ExtendedLiveness
210215
/// to SwiftCompilerSources.
211216
///

include/swift/SIL/OwnershipUseVisitor.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,13 @@ bool OwnershipUseVisitor<Impl>::visitInnerBorrow(Operand *borrowingOperand) {
258258
return false;
259259

260260
return BorrowingOperand(borrowingOperand)
261-
.visitScopeEndingUses([&](Operand *borrowEnd) {
262-
return visitInnerBorrowScopeEnd(borrowEnd);
263-
});
261+
.visitScopeEndingUses(
262+
[&](Operand *borrowEnd) {
263+
return visitInnerBorrowScopeEnd(borrowEnd);
264+
},
265+
[&](Operand *unknownUse) {
266+
return asImpl().handlePointerEscape(unknownUse);
267+
});
264268
}
265269

266270
template <typename Impl>

include/swift/SIL/OwnershipUtils.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,16 @@ struct BorrowingOperand {
360360
/// over a region of code instead of just for a single instruction, visit
361361
/// those uses.
362362
///
363-
/// Returns false and early exits if the visitor \p func returns false.
363+
/// Returns false and early exits if the \p visitScopeEnd or \p
364+
/// visitUnknownUse returns false.
364365
///
365366
/// For an instantaneous borrow, such as apply, this visits no uses. For
366367
/// begin_apply it visits the end_apply uses. For borrow introducers, it
367368
/// visits the end of the introduced borrow scope.
368-
bool visitScopeEndingUses(function_ref<bool(Operand *)> func) const;
369+
bool visitScopeEndingUses(function_ref<bool(Operand *)> visitScopeEnd,
370+
function_ref<bool(Operand *)> visitUnknownUse
371+
= [](Operand *){ return false; })
372+
const;
369373

370374
/// Returns true for borrows that create a local borrow scope but have no
371375
/// scope-ending uses (presumably all paths are dead-end blocks). This does
@@ -381,7 +385,10 @@ struct BorrowingOperand {
381385
/// BorrowingOperand.
382386
///
383387
/// Returns false and early exits if the visitor \p func returns false.
384-
bool visitExtendedScopeEndingUses(function_ref<bool(Operand *)> func) const;
388+
bool visitExtendedScopeEndingUses(
389+
function_ref<bool(Operand *)> func,
390+
function_ref<bool(Operand *)> visitUnknownUse
391+
= [](Operand *){ return false; }) const;
385392

386393
/// Returns true if this borrow scope operand consumes guaranteed
387394
/// values and produces a new scope afterwards.

include/swift/SIL/PrunedLiveness.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,9 @@ class PrunedLiveBlocks {
289289
/// scope. Reborrows within nested borrows scopes are already summarized by the
290290
/// outer borrow scope.
291291
enum class InnerBorrowKind {
292-
Contained, // any borrows are fully contained within this live range
293-
Reborrowed // at least one immediately nested borrow is reborrowed
292+
Contained, // any borrows are fully contained within this live range
293+
Reborrowed, // at least one immediately nested borrow is reborrowed
294+
Escaped // the end of the borrow scope is indeterminate
294295
};
295296

296297
inline InnerBorrowKind meet(InnerBorrowKind lhs, InnerBorrowKind rhs) {

include/swift/SIL/SILInstruction.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8546,8 +8546,9 @@ class MarkDependenceInst
85468546
}
85478547

85488548
/// Visit the instructions that end the lifetime of an OSSA on-stack closure.
8549-
bool visitNonEscapingLifetimeEnds(llvm::function_ref<bool (Operand*)> func)
8550-
const;
8549+
bool visitNonEscapingLifetimeEnds(
8550+
llvm::function_ref<bool (Operand*)> visitScopeEnd,
8551+
llvm::function_ref<bool (Operand*)> visitUnknownUse) const;
85518552
};
85528553

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

lib/SIL/IR/SILInstruction.cpp

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,33 +1796,27 @@ bool SILInstruction::maySuspend() const {
17961796
}
17971797

17981798
static bool
1799-
visitRecursivelyLifetimeEndingUses(SILValue i, bool &noUsers,
1800-
llvm::function_ref<bool(Operand *)> func,
1801-
bool allowPhis) {
1799+
visitRecursivelyLifetimeEndingUses(
1800+
SILValue i, bool &noUsers,
1801+
llvm::function_ref<bool(Operand *)> visitScopeEnd,
1802+
llvm::function_ref<bool(Operand *)> visitUnknownUse) {
1803+
18021804
for (Operand *use : i->getConsumingUses()) {
18031805
noUsers = false;
18041806
if (isa<DestroyValueInst>(use->getUser())) {
1805-
if (!func(use)) {
1807+
if (!visitScopeEnd(use)) {
18061808
return false;
18071809
}
18081810
continue;
18091811
}
18101812
if (auto *ret = dyn_cast<ReturnInst>(use->getUser())) {
18111813
auto fnTy = ret->getFunction()->getLoweredFunctionType();
18121814
assert(!fnTy->getLifetimeDependenceInfo().empty());
1813-
if (!func(use)) {
1815+
if (!visitScopeEnd(use)) {
18141816
return false;
18151817
}
18161818
continue;
18171819
}
1818-
if (allowPhis) {
1819-
if (PhiOperand(use)) {
1820-
if (!func(use)) {
1821-
return false;
1822-
}
1823-
continue;
1824-
}
1825-
}
18261820
// FIXME: Handle store to indirect result
18271821

18281822
// There shouldn't be any dead-end consumptions of a nonescaping
@@ -1836,17 +1830,11 @@ visitRecursivelyLifetimeEndingUses(SILValue i, bool &noUsers,
18361830
// the structural requirements of on-stack partial_apply uses.
18371831
auto *user = use->getUser();
18381832
if (user->getNumResults() == 0) {
1839-
llvm::errs() << "partial_apply [on_stack] use:\n";
1840-
user->printInContext(llvm::errs());
1841-
if (isa<BranchInst>(user)) {
1842-
llvm::report_fatal_error("partial_apply [on_stack] cannot be cloned");
1843-
}
1844-
llvm::report_fatal_error("partial_apply [on_stack] must be directly "
1845-
"forwarded to a destroy_value");
1833+
return visitUnknownUse(use);
18461834
}
18471835
for (auto res : use->getUser()->getResults()) {
1848-
if (!visitRecursivelyLifetimeEndingUses(res, noUsers, func,
1849-
allowPhis)) {
1836+
if (!visitRecursivelyLifetimeEndingUses(res, noUsers, visitScopeEnd,
1837+
visitUnknownUse)) {
18501838
return false;
18511839
}
18521840
}
@@ -1862,21 +1850,36 @@ PartialApplyInst::visitOnStackLifetimeEnds(
18621850
&& "only meaningful for OSSA stack closures");
18631851
bool noUsers = true;
18641852

1853+
auto visitUnknownUse = [](Operand *unknownUse){
1854+
llvm::errs() << "partial_apply [on_stack] use:\n";
1855+
auto *user = unknownUse->getUser();
1856+
user->printInContext(llvm::errs());
1857+
if (isa<BranchInst>(user)) {
1858+
llvm::report_fatal_error("partial_apply [on_stack] cannot be cloned");
1859+
}
1860+
llvm::report_fatal_error("partial_apply [on_stack] must be directly "
1861+
"forwarded to a destroy_value");
1862+
return false;
1863+
};
18651864
if (!visitRecursivelyLifetimeEndingUses(this, noUsers, func,
1866-
/*allowPhis*/ false)) {
1865+
visitUnknownUse)) {
18671866
return false;
18681867
}
18691868
return !noUsers;
18701869
}
18711870

1872-
bool MarkDependenceInst::
1873-
visitNonEscapingLifetimeEnds(llvm::function_ref<bool (Operand *)> func) const {
1871+
// FIXME: Rather than recursing through all results, this should only recurse
1872+
// through ForwardingInstruction and OwnershipTransitionInstruction and the
1873+
// client should prove that any other uses cannot be upstream from a consume of
1874+
// the dependent value.
1875+
bool MarkDependenceInst::visitNonEscapingLifetimeEnds(
1876+
llvm::function_ref<bool (Operand *)> visitScopeEnd,
1877+
llvm::function_ref<bool (Operand *)> visitUnknownUse) const {
18741878
assert(getFunction()->hasOwnership() && isNonEscaping()
18751879
&& "only meaningful for nonescaping dependencies");
18761880
bool noUsers = true;
1877-
1878-
if (!visitRecursivelyLifetimeEndingUses(this, noUsers, func,
1879-
/*allowPhis*/ true)) {
1881+
if (!visitRecursivelyLifetimeEndingUses(this, noUsers, visitScopeEnd,
1882+
visitUnknownUse)) {
18801883
return false;
18811884
}
18821885
return !noUsers;

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -327,18 +327,19 @@ bool swift::findInnerTransitiveGuaranteedUses(
327327
//
328328
// FIXME: visit[Extended]ScopeEndingUses can't return false here once dead
329329
// borrows are disallowed.
330-
if (!BorrowingOperand(use).visitScopeEndingUses([&](Operand *endUse) {
331-
if (endUse->getOperandOwnership() == OperandOwnership::Reborrow) {
332-
foundPointerEscape = true;
333-
}
334-
if (PhiOperand(endUse)) {
335-
assert(endUse->getOperandOwnership() ==
336-
OperandOwnership::ForwardingConsume);
330+
if (!BorrowingOperand(use).visitScopeEndingUses(
331+
[&](Operand *endUse) {
332+
if (endUse->getOperandOwnership() == OperandOwnership::Reborrow) {
333+
foundPointerEscape = true;
334+
}
335+
leafUse(endUse);
336+
return true;
337+
},
338+
[&](Operand *unknownUse) {
337339
foundPointerEscape = true;
338-
}
339-
leafUse(endUse);
340-
return true;
341-
})) {
340+
leafUse(unknownUse);
341+
return true;
342+
})) {
342343
// Special case for dead borrows. This is dangerous because clients
343344
// don't expect a begin_borrow to be in the use list.
344345
leafUse(use);
@@ -451,16 +452,12 @@ bool swift::findExtendedUsesOfSimpleBorrowedValue(
451452
break;
452453
}
453454
case OperandOwnership::Borrow:
454-
// FIXME: visitExtendedScopeEndingUses can't return false here once dead
455-
// borrows are disallowed.
456455
if (!BorrowingOperand(use).visitExtendedScopeEndingUses(
457456
[&](Operand *endUse) {
458457
recordUse(endUse);
459458
return true;
460459
})) {
461-
// Special case for dead borrows. This is dangerous because clients
462-
// don't expect a begin_borrow to be in the use list.
463-
recordUse(use);
460+
return false;
464461
}
465462
break;
466463
}
@@ -480,11 +477,6 @@ bool swift::findUsesOfSimpleValue(SILValue value,
480477
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
481478
return false;
482479
}
483-
if (PhiOperand(use)) {
484-
assert(use->getOperandOwnership() ==
485-
OperandOwnership::ForwardingConsume);
486-
return false;
487-
}
488480
usePoints->push_back(end);
489481
return true;
490482
})) {
@@ -694,7 +686,8 @@ bool BorrowingOperand::hasEmptyRequiredEndingUses() const {
694686
}
695687

696688
bool BorrowingOperand::visitScopeEndingUses(
697-
function_ref<bool(Operand *)> func) const {
689+
function_ref<bool(Operand *)> visitScopeEnd,
690+
function_ref<bool(Operand *)> visitUnknownUse) const {
698691
switch (kind) {
699692
case BorrowingOperandKind::Invalid:
700693
llvm_unreachable("Using invalid case");
@@ -703,7 +696,7 @@ bool BorrowingOperand::visitScopeEndingUses(
703696
for (auto *use : cast<BeginBorrowInst>(op->getUser())->getUses()) {
704697
if (use->isLifetimeEnding()) {
705698
deadBorrow = false;
706-
if (!func(use))
699+
if (!visitScopeEnd(use))
707700
return false;
708701
}
709702
}
@@ -717,7 +710,7 @@ bool BorrowingOperand::visitScopeEndingUses(
717710
for (auto *use : cast<StoreBorrowInst>(op->getUser())->getUses()) {
718711
if (isa<EndBorrowInst>(use->getUser())) {
719712
deadBorrow = false;
720-
if (!func(use))
713+
if (!visitScopeEnd(use))
721714
return false;
722715
}
723716
}
@@ -731,7 +724,7 @@ bool BorrowingOperand::visitScopeEndingUses(
731724
auto *user = cast<BeginApplyInst>(op->getUser());
732725
for (auto *use : user->getTokenResult()->getUses()) {
733726
deadApply = false;
734-
if (!func(use))
727+
if (!visitScopeEnd(use))
735728
return false;
736729
}
737730
return !deadApply;
@@ -742,12 +735,12 @@ bool BorrowingOperand::visitScopeEndingUses(
742735
// The closure's borrow lifetimes end when the closure itself ends its
743736
// lifetime. That may happen transitively through conversions that forward
744737
// ownership of the closure.
745-
return user->visitOnStackLifetimeEnds(func);
738+
return user->visitOnStackLifetimeEnds(visitScopeEnd);
746739
}
747740
case BorrowingOperandKind::MarkDependenceNonEscaping: {
748741
auto *user = cast<MarkDependenceInst>(op->getUser());
749742
assert(user->isNonEscaping() && "escaping dependencies don't borrow");
750-
return user->visitNonEscapingLifetimeEnds(func);
743+
return user->visitNonEscapingLifetimeEnds(visitScopeEnd, visitUnknownUse);
751744
}
752745
case BorrowingOperandKind::BeginAsyncLet: {
753746
auto user = cast<BuiltinInst>(op->getUser());
@@ -760,7 +753,7 @@ bool BorrowingOperand::visitScopeEndingUses(
760753
|| builtinUser->getBuiltinKind() != BuiltinValueKind::EndAsyncLetLifetime)
761754
continue;
762755

763-
if (!func(use)) {
756+
if (!visitScopeEnd(use)) {
764757
return false;
765758
}
766759
}
@@ -778,7 +771,7 @@ bool BorrowingOperand::visitScopeEndingUses(
778771
for (auto *use : br->getArgForOperand(op)->getUses()) {
779772
if (use->isLifetimeEnding()) {
780773
deadBranch = false;
781-
if (!func(use))
774+
if (!visitScopeEnd(use))
782775
return false;
783776
}
784777
}
@@ -789,13 +782,14 @@ bool BorrowingOperand::visitScopeEndingUses(
789782
}
790783

791784
bool BorrowingOperand::visitExtendedScopeEndingUses(
792-
function_ref<bool(Operand *)> visitor) const {
785+
function_ref<bool(Operand *)> visitor,
786+
function_ref<bool(Operand *)> visitUnknownUse) const {
793787

794788
if (hasBorrowIntroducingUser()) {
795789
auto borrowedValue = getBorrowIntroducingUserResult();
796790
return borrowedValue.visitExtendedScopeEndingUses(visitor);
797791
}
798-
return visitScopeEndingUses(visitor);
792+
return visitScopeEndingUses(visitor, visitUnknownUse);
799793
}
800794

801795
BorrowedValue BorrowingOperand::getBorrowIntroducingUserResult() const {
@@ -857,10 +851,11 @@ void BorrowingOperand::getImplicitUses(
857851
SmallVectorImpl<Operand *> &foundUses) const {
858852
// FIXME: this visitScopeEndingUses should never return false once dead
859853
// borrows are disallowed.
860-
if (!visitScopeEndingUses([&](Operand *endOp) {
854+
auto handleUse = [&](Operand *endOp) {
861855
foundUses.push_back(endOp);
862856
return true;
863-
})) {
857+
};
858+
if (!visitScopeEndingUses(handleUse, handleUse)) {
864859
// Special-case for dead borrows.
865860
foundUses.push_back(op);
866861
}
@@ -988,9 +983,6 @@ bool BorrowedValue::visitExtendedScopeEndingUses(
988983
reborrows.insert(borrowedValue.value);
989984
return true;
990985
}
991-
if (auto phiOp = PhiOperand(scopeEndingUse)) {
992-
return false;
993-
}
994986
return visitor(scopeEndingUse);
995987
};
996988

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,23 @@ PrunedLiveRange<LivenessWithDefs>::updateForBorrowingOperand(Operand *operand) {
234234
//
235235
// Note: Ownership liveness should follow reborrows that are dominated by the
236236
// ownership definition.
237-
if (!BorrowingOperand(operand).visitScopeEndingUses([this](Operand *end) {
238-
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
239-
return false;
240-
}
241-
if (PhiOperand(end)) {
242-
assert(end->getOperandOwnership() ==
243-
OperandOwnership::ForwardingConsume);
244-
return false;
245-
}
246-
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
247-
return true;
248-
})) {
249-
return InnerBorrowKind::Reborrowed;
237+
auto innerBorrowKind = InnerBorrowKind::Contained;
238+
if (!BorrowingOperand(operand).visitScopeEndingUses(
239+
[&](Operand *end) {
240+
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
241+
innerBorrowKind = InnerBorrowKind::Reborrowed;
242+
}
243+
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
244+
return true;
245+
}, [&](Operand *unknownUse) {
246+
updateForUse(unknownUse->getUser(), /*lifetimeEnding*/ false);
247+
innerBorrowKind = InnerBorrowKind::Escaped;
248+
return true;
249+
})) {
250+
// Handle dead borrows.
251+
updateForUse(operand->getUser(), /*lifetimeEnding*/ false);
250252
}
251-
return InnerBorrowKind::Contained;
253+
return innerBorrowKind;
252254
}
253255

254256
template <typename LivenessWithDefs>

0 commit comments

Comments
 (0)