Skip to content

Commit b4a47b6

Browse files
committed
Fix findInnerTransitiveUsesForAddress; add AddressUseKind
findInnerTransitiveUsesForAddress was incorrectly returning true for pointer escapes. Introduce enum AddressUseKind { NonEscaping, PointerEscape, Unknown }; Clients need to handle each of these cases differently.
1 parent 014b155 commit b4a47b6

File tree

5 files changed

+98
-51
lines changed

5 files changed

+98
-51
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -663,12 +663,19 @@ bool getAllBorrowIntroducingValues(SILValue value,
663663
/// introducer, then we return a .some(BorrowScopeIntroducingValue).
664664
BorrowedValue getSingleBorrowIntroducingValue(SILValue inputValue);
665665

666+
enum class AddressUseKind { NonEscaping, PointerEscape, Unknown };
667+
668+
inline AddressUseKind meet(AddressUseKind lhs, AddressUseKind rhs) {
669+
return (lhs > rhs) ? lhs : rhs;
670+
}
671+
666672
/// The algorithm that is used to determine what the verifier will consider to
667673
/// be transitive uses of the given address. Used to implement \see
668674
/// findTransitiveUses.
669-
bool findTransitiveUsesForAddress(
670-
SILValue address, SmallVectorImpl<Operand *> *foundUses = nullptr,
671-
std::function<void(Operand *)> *onError = nullptr);
675+
AddressUseKind
676+
findTransitiveUsesForAddress(SILValue address,
677+
SmallVectorImpl<Operand *> *foundUses = nullptr,
678+
std::function<void(Operand *)> *onError = nullptr);
672679

673680
class InteriorPointerOperandKind {
674681
public:
@@ -845,8 +852,9 @@ struct InteriorPointerOperand {
845852
/// of the instruction's operand. The uses of that address act as liveness
846853
/// requirements to ensure that the underlying class is alive at all use
847854
/// points.
848-
bool findTransitiveUses(SmallVectorImpl<Operand *> *foundUses = nullptr,
849-
std::function<void(Operand *)> *onError = nullptr) {
855+
AddressUseKind
856+
findTransitiveUses(SmallVectorImpl<Operand *> *foundUses = nullptr,
857+
std::function<void(Operand *)> *onError = nullptr) {
850858
return findTransitiveUsesForAddress(getProjectedAddress(), foundUses,
851859
onError);
852860
}
@@ -916,7 +924,7 @@ struct AddressOwnership {
916924
}
917925

918926
/// Transitively compute uses of this base address.
919-
bool findTransitiveUses(SmallVectorImpl<Operand *> &foundUses) {
927+
AddressUseKind findTransitiveUses(SmallVectorImpl<Operand *> &foundUses) {
920928
return findTransitiveUsesForAddress(base.getBaseAddress(), &foundUses);
921929
}
922930

include/swift/SIL/SILValue.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,12 @@ class Operand {
10581058
/// Returns true if this ends the lifetime of an owned operand.
10591059
bool isConsuming() const;
10601060

1061+
bool endsLocalBorrowScope() const {
1062+
auto ownership = getOperandOwnership();
1063+
return ownership == OperandOwnership::EndBorrow
1064+
|| ownership == OperandOwnership::Reborrow;
1065+
}
1066+
10611067
SILBasicBlock *getParentBlock() const;
10621068
SILFunction *getParentFunction() const;
10631069

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SIL/OwnershipUtils.h"
14+
#include "swift/Basic/DAGNodeWorklist.h"
1415
#include "swift/Basic/Defer.h"
1516
#include "swift/Basic/DAGNodeWorklist.h"
1617
#include "swift/Basic/SmallPtrSetVector.h"
@@ -159,8 +160,7 @@ bool swift::canOpcodeForwardOwnedValues(Operand *use) {
159160
//
160161
// Skip over nested borrow scopes. Their scope-ending instructions are their use
161162
// points. Transitively find all nested scope-ending instructions by looking
162-
// through nested reborrows. Nested reborrows are not use points and \p
163-
// visitReborrow is not called for them.
163+
// through nested reborrows. Nested reborrows are not use points.
164164
bool swift::findInnerTransitiveGuaranteedUses(
165165
SILValue guaranteedValue, SmallVectorImpl<Operand *> *usePoints) {
166166

@@ -175,10 +175,10 @@ bool swift::findInnerTransitiveGuaranteedUses(
175175
//
176176
// TODO: The worklist can be a simple vector without any a membership check if
177177
// destructures are changed to be represented as reborrows. Currently a
178-
// destructure forwards multiple results! This means that usePoints could grow
179-
// exponentially without the membership check. It's fine to do this membership
180-
// check locally in this function (within a borrow scope) because it isn't
181-
// needed for the immediate uses, only the transitive uses.
178+
// destructure forwards multiple results! This means that the worklist could
179+
// grow exponentially without the membership check. It's fine to do this
180+
// membership check locally in this function (within a borrow scope) because
181+
// it isn't needed for the immediate uses, only the transitive uses.
182182
DAGNodeWorklist<Operand *, 8> worklist;
183183
for (Operand *use : guaranteedValue->getUses()) {
184184
if (use->getOperandOwnership() != OperandOwnership::NonUse)
@@ -220,7 +220,8 @@ bool swift::findInnerTransitiveGuaranteedUses(
220220
// If our base guaranteed value does not have any consuming uses (consider
221221
// function arguments), we need to be sure to include interior pointer
222222
// operands since we may not get a use from a end_scope instruction.
223-
if (!InteriorPointerOperand(use).findTransitiveUses(usePoints)) {
223+
if (InteriorPointerOperand(use).findTransitiveUses(usePoints)
224+
!= AddressUseKind::NonEscaping) {
224225
return false;
225226
}
226227
#endif
@@ -252,6 +253,7 @@ bool swift::findInnerTransitiveGuaranteedUses(
252253
leafUse(endUse);
253254
return true;
254255
});
256+
break;
255257
}
256258
}
257259
return true;
@@ -720,17 +722,35 @@ bool BorrowedValue::visitInteriorPointerOperandHelper(
720722
return true;
721723
}
722724

723-
bool swift::findTransitiveUsesForAddress(
724-
SILValue projectedAddress, SmallVectorImpl<Operand *> *foundUses,
725-
std::function<void(Operand *)> *onError) {
725+
AddressUseKind
726+
swift::findTransitiveUsesForAddress(SILValue projectedAddress,
727+
SmallVectorImpl<Operand *> *foundUses,
728+
std::function<void(Operand *)> *onError) {
729+
// If the projectedAddress is dead, it is itself a leaf use. Since we don't
730+
// have an operand for it, simply bail. Dead projectedAddress is unexpected.
731+
//
732+
// TODO: store_borrow is currently an InteriorPointer with no uses, so we end
733+
// up bailing. It should be in a dependence scope instead. It's not clear why
734+
// it produces an address at all.
735+
if (projectedAddress->use_empty())
736+
return AddressUseKind::PointerEscape;
737+
726738
SmallVector<Operand *, 8> worklist(projectedAddress->getUses());
727739

728-
bool foundError = false;
740+
AddressUseKind result = AddressUseKind::NonEscaping;
729741

730742
auto leafUse = [foundUses](Operand *use) {
731743
if (foundUses)
732744
foundUses->push_back(use);
733745
};
746+
auto transitiveResultUses = [&](Operand *use) {
747+
auto *svi = cast<SingleValueInstruction>(use->getUser());
748+
if (svi->use_empty()) {
749+
leafUse(use);
750+
} else {
751+
worklist.append(svi->use_begin(), svi->use_end());
752+
}
753+
};
734754

735755
while (!worklist.empty()) {
736756
auto *op = worklist.pop_back_val();
@@ -744,37 +764,44 @@ bool swift::findTransitiveUsesForAddress(
744764
// loop that we didn't recognize the user.
745765
auto *user = op->getUser();
746766

767+
// TODO: Partial apply should be NonEscaping, but then we need to consider
768+
// the apply to be a use point.
769+
if (isa<PartialApplyInst>(user) || isa<AddressToPointerInst>(user)) {
770+
result = meet(result, AddressUseKind::PointerEscape);
771+
continue;
772+
}
747773
// First, eliminate "end point uses" that we just need to check liveness at
748774
// and do not need to check transitive uses of.
749-
if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) ||
750-
isIncidentalUse(user) || isa<StoreInst>(user) ||
751-
isa<PartialApplyInst>(user) || isa<DestroyAddrInst>(user) ||
752-
isa<AssignInst>(user) || isa<AddressToPointerInst>(user) ||
753-
isa<YieldInst>(user) || isa<LoadUnownedInst>(user) ||
754-
isa<StoreUnownedInst>(user) || isa<EndApplyInst>(user) ||
755-
isa<LoadWeakInst>(user) || isa<StoreWeakInst>(user) ||
756-
isa<AssignByWrapperInst>(user) || isa<BeginUnpairedAccessInst>(user) ||
757-
isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user) ||
758-
isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user) ||
759-
isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user) ||
760-
isa<IsUniqueInst>(user)) {
775+
if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) || isIncidentalUse(user)
776+
|| isa<StoreInst>(user) || isa<DestroyAddrInst>(user)
777+
|| isa<AssignInst>(user) || isa<YieldInst>(user)
778+
|| isa<LoadUnownedInst>(user) || isa<StoreUnownedInst>(user)
779+
|| isa<EndApplyInst>(user) || isa<LoadWeakInst>(user)
780+
|| isa<StoreWeakInst>(user) || isa<AssignByWrapperInst>(user)
781+
|| isa<BeginUnpairedAccessInst>(user)
782+
|| isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user)
783+
|| isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user)
784+
|| isa<SelectEnumAddrInst>(user) || isa<InjectEnumAddrInst>(user)
785+
|| isa<IsUniqueInst>(user)) {
761786
leafUse(op);
762787
continue;
763788
}
764789

790+
if (isa<UnconditionalCheckedCastAddrInst>(user)
791+
|| isa<MarkFunctionEscapeInst>(user)) {
792+
assert(!user->hasResults());
793+
continue;
794+
}
795+
765796
// Then handle users that we need to look at transitive uses of.
766797
if (Projection::isAddressProjection(user) ||
767798
isa<ProjectBlockStorageInst>(user) ||
768799
isa<OpenExistentialAddrInst>(user) ||
769800
isa<InitExistentialAddrInst>(user) || isa<InitEnumDataAddrInst>(user) ||
770801
isa<BeginAccessInst>(user) || isa<TailAddrInst>(user) ||
771802
isa<IndexAddrInst>(user) || isa<StoreBorrowInst>(user) ||
772-
isa<UnconditionalCheckedCastAddrInst>(user) ||
773-
isa<UncheckedAddrCastInst>(user)
774-
|| isa<MarkFunctionEscapeInst>(user)) {
775-
for (SILValue r : user->getResults()) {
776-
llvm::copy(r->getUses(), std::back_inserter(worklist));
777-
}
803+
isa<UncheckedAddrCastInst>(user)) {
804+
transitiveResultUses(op);
778805
continue;
779806
}
780807

@@ -790,17 +817,26 @@ bool swift::findTransitiveUsesForAddress(
790817
// If we have a load_borrow, add it's end scope to the liveness requirement.
791818
if (auto *lbi = dyn_cast<LoadBorrowInst>(user)) {
792819
if (foundUses) {
793-
transform(lbi->getEndBorrows(), std::back_inserter(*foundUses),
794-
[](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; });
820+
for (Operand *use : lbi->getUses()) {
821+
if (use->endsLocalBorrowScope()) {
822+
leafUse(use);
823+
}
824+
}
795825
}
796826
continue;
797827
}
798828

799829
// TODO: Merge this into the full apply site code below.
800830
if (auto *beginApply = dyn_cast<BeginApplyInst>(user)) {
801831
if (foundUses) {
802-
llvm::copy(beginApply->getTokenResult()->getUses(),
803-
std::back_inserter(*foundUses));
832+
// TODO: the empty check should not be needed when dead begin_apply is
833+
// disallowed.
834+
if (beginApply->getTokenResult()->use_empty()) {
835+
leafUse(op);
836+
} else {
837+
llvm::copy(beginApply->getTokenResult()->getUses(),
838+
std::back_inserter(*foundUses));
839+
}
804840
}
805841
continue;
806842
}
@@ -818,20 +854,17 @@ bool swift::findTransitiveUsesForAddress(
818854
}
819855

820856
// If we are the value use, look through it.
821-
llvm::copy(mdi->getUses(), std::back_inserter(worklist));
857+
transitiveResultUses(op);
822858
continue;
823859
}
824860

825861
// We were unable to recognize this user, so return true that we failed.
826862
if (onError) {
827863
(*onError)(op);
828864
}
829-
foundError = true;
865+
result = AddressUseKind::Unknown;
830866
}
831-
832-
// We were able to recognize all of the uses of the address, so return false
833-
// that we did not find any errors.
834-
return !foundError;
867+
return result;
835868
}
836869

837870
//===----------------------------------------------------------------------===//

lib/SIL/Verifier/SILOwnershipVerifier.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,8 +376,9 @@ bool SILValueOwnershipChecker::gatherUsers(
376376
<< "Address User: " << *op->getUser();
377377
});
378378
};
379-
foundError |= !interiorPointerOperand.findTransitiveUses(
380-
&nonLifetimeEndingUsers, &onError);
379+
foundError |= (interiorPointerOperand.findTransitiveUses(
380+
&nonLifetimeEndingUsers, &onError)
381+
== AddressUseKind::Unknown);
381382
}
382383

383384
// Finally add the op to the non lifetime ending user list.

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,15 +1142,15 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
11421142
return;
11431143

11441144
ctx->extraAddressFixupInfo.base = addressOwnership.base;
1145+
SILValue baseAddress = ctx->extraAddressFixupInfo.base.getBaseAddress();
11451146

11461147
// For now, just gather up uses
11471148
//
11481149
// FIXME: get rid of allAddressUsesFromOldValue. Shouldn't this already be
11491150
// included in guaranteedUsePoints?
11501151
auto &oldValueUses = ctx->extraAddressFixupInfo.allAddressUsesFromOldValue;
1151-
// FIXME: The return value of findTransitiveUsesForAddress is currently
1152-
// inverted.
1153-
if (!findTransitiveUsesForAddress(oldValue, &oldValueUses)) {
1152+
if (findTransitiveUsesForAddress(oldValue, &oldValueUses)
1153+
!= AddressUseKind::NonEscaping) {
11541154
invalidate();
11551155
return;
11561156
}
@@ -1161,7 +1161,6 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
11611161
}
11621162
// This cloner check must match the later cloner invocation in
11631163
// getReplacementAddress()
1164-
SILValue baseAddress = ctx->extraAddressFixupInfo.base.getBaseAddress();
11651164
auto *baseInst = cast<SingleValueInstruction>(baseAddress);
11661165
auto checkBase = [&](SILValue srcAddr) {
11671166
return (srcAddr == baseInst) ? SILValue(baseInst) : SILValue();

0 commit comments

Comments
 (0)