Skip to content

Commit 767e094

Browse files
committed
Add AddressOwnership OSSA utility
This replaces the recent BorrowedAddress utility (the address may not be borrowed!) APIs: AddressOwnership::hasLocalOwnershipLifetime() - convenience on top of AccessBase::hasLocalOwnershipLifetime(). AddressOwnership::getOwnershipReferenceRoot() - convenience on top of AccessBase::getOwnershipReferenceRoot(). AddressOwnership::findTransitiveUses() - wrapper API over the internal helper findTransitiveUsesForAddress() AddressOwnership::areUsesWithinLifetime() - wrapper adds a quick check on top of BorrowedValue::areUsesWithinLifetime()
1 parent b1352b4 commit 767e094

File tree

3 files changed

+95
-70
lines changed

3 files changed

+95
-70
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,13 @@ bool getAllBorrowIntroducingValues(SILValue value,
662662
/// introducer, then we return a .some(BorrowScopeIntroducingValue).
663663
BorrowedValue getSingleBorrowIntroducingValue(SILValue inputValue);
664664

665+
/// The algorithm that is used to determine what the verifier will consider to
666+
/// be transitive uses of the given address. Used to implement \see
667+
/// findTransitiveUses.
668+
bool findTransitiveUsesForAddress(
669+
SILValue address, SmallVectorImpl<Operand *> &foundUses,
670+
std::function<void(Operand *)> *onError = nullptr);
671+
665672
class InteriorPointerOperandKind {
666673
public:
667674
enum Kind : uint8_t {
@@ -805,7 +812,7 @@ struct InteriorPointerOperand {
805812
}
806813

807814
/// Return the base BorrowedValue of the incoming value's operand.
808-
BorrowedValue getSingleBaseValue() const {
815+
BorrowedValue getSingleBorrowedValue() const {
809816
return getSingleBorrowIntroducingValue(operand->get());
810817
}
811818

@@ -840,14 +847,6 @@ struct InteriorPointerOperand {
840847
onError);
841848
}
842849

843-
/// The algorithm that is used to determine what the verifier will consider to
844-
/// be transitive uses of the given address. Used to implement \see
845-
/// findTransitiveUses.
846-
static bool
847-
findTransitiveUsesForAddress(SILValue address,
848-
SmallVectorImpl<Operand *> &foundUses,
849-
std::function<void(Operand *)> *onError = nullptr);
850-
851850
Operand *operator->() { return operand; }
852851
const Operand *operator->() const { return operand; }
853852
Operand *operator*() { return operand; }
@@ -860,23 +859,70 @@ struct InteriorPointerOperand {
860859
: operand(op), kind(kind) {}
861860
};
862861

863-
/// Utility to check if an address may originate from a borrowed value. If so,
862+
/// Utility to check if an address may originate from an OSSA value. If so,
864863
/// then uses of the address cannot be replaced without ensuring that they are
865-
/// also within the same scope.
864+
/// also within the same owned lifetime or borrow scope.
866865
///
867-
/// If mayBeBorrowed is false, then there is no enclosing borrow scope and
868-
/// interiorPointerOp is irrelevant.
866+
/// If hasOwnership() is false, then there is no enclosing lifetime or borrow
867+
/// scope and interiorPointerOp is irrelevant.
869868
///
870-
/// If mayBeBorrowed is true, then interiorPointerOp refers to the operand that
869+
/// If hasOwnership() is true, then interiorPointerOp refers to the operand that
871870
/// converts a non-address value into the address from which the contructor's
872871
/// address is derived. If the best-effort to find an InteriorPointerOperand
873872
/// fails, then interiorPointerOp remains invalid, and clients must be
874873
/// conservative.
875-
struct BorrowedAddress {
876-
bool mayBeBorrowed = true;
877-
InteriorPointerOperand interiorPointerOp;
874+
///
875+
/// TODO: Handle implicit borrow scopes once they are legal in SIL. The operand
876+
/// of the base will be owned but mayBeBorrowed will remain true.
877+
struct AddressOwnership {
878+
AccessBase base;
879+
880+
AddressOwnership() = default;
881+
882+
AddressOwnership(SILValue address) : base(AccessBase::compute(address)) {
883+
assert(address->getType().isAddress());
884+
}
878885

879-
BorrowedAddress(SILValue address);
886+
AddressOwnership(AccessBase base) : base(base) {}
887+
888+
operator bool() const { return bool(base); }
889+
890+
bool operator==(const AddressOwnership &other) const {
891+
return base.hasIdenticalAccessInfo(other.base);
892+
}
893+
894+
bool operator!=(const AddressOwnership &other) const {
895+
return !(*this == other);
896+
}
897+
898+
/// Return true if this address may be derived from a value with a local OSSA
899+
/// lifetime or borrow scope.
900+
bool hasLocalOwnershipLifetime() const {
901+
return base.hasLocalOwnershipLifetime();
902+
}
903+
904+
/// Return the OSSA value from which this address is derived. This may be
905+
/// invalid even if hasOSSALifetime() is true in cases where the
906+
/// InteriorPointerOperand is unrecognized.
907+
SILValue getOwnershipReferenceRoot() const {
908+
if (base.isReference())
909+
return base.getOwnershipReferenceRoot();
910+
911+
return SILValue();
912+
}
913+
914+
/// Transitively compute uses of this base address.
915+
bool findTransitiveUses(SmallVectorImpl<Operand *> &foundUses) {
916+
return findTransitiveUsesForAddress(base.getBaseAddress(), foundUses);
917+
}
918+
919+
/// Return true of all \p uses occur before the end of the address' lifetime
920+
/// or borrow scope.
921+
///
922+
/// Precondition: all \p uses are dominated by the beginning of the address'
923+
/// lifetime or borrow scope.
924+
bool areUsesWithinLifetime(ArrayRef<Operand *> uses,
925+
DeadEndBlocks &deadEndBlocks) const;
880926
};
881927

882928
class OwnedValueIntroducerKind {

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -704,11 +704,7 @@ bool BorrowedValue::visitInteriorPointerOperandHelper(
704704
return true;
705705
}
706706

707-
//===----------------------------------------------------------------------===//
708-
// InteriorPointerOperand
709-
//===----------------------------------------------------------------------===//
710-
711-
bool InteriorPointerOperand::findTransitiveUsesForAddress(
707+
bool swift::findTransitiveUsesForAddress(
712708
SILValue projectedAddress, SmallVectorImpl<Operand *> &foundUses,
713709
std::function<void(Operand *)> *onError) {
714710
SmallVector<Operand *, 8> worklist(projectedAddress->getUses());
@@ -814,44 +810,26 @@ bool InteriorPointerOperand::findTransitiveUsesForAddress(
814810
return foundError;
815811
}
816812

817-
// Determine whether \p address may be in a borrow scope and if so record the
818-
// InteriorPointerOperand that produces the address from a guaranteed value.
819-
BorrowedAddress::BorrowedAddress(SILValue address) {
820-
assert(address->getType().isAddress());
821-
822-
auto storageWithBase = AccessStorageWithBase::compute(address);
823-
switch (storageWithBase.storage.getKind()) {
824-
case AccessStorage::Argument:
825-
case AccessStorage::Stack:
826-
case AccessStorage::Global:
827-
// Unidentified storage is from an "escaped pointer", so it need not be
828-
// restricted to a borrow scope.
829-
case AccessStorage::Unidentified:
830-
this->mayBeBorrowed = false;
831-
return;
832-
case AccessStorage::Box:
833-
case AccessStorage::Yield:
834-
case AccessStorage::Class:
835-
case AccessStorage::Tail:
836-
// The base object might be within a borrow scope.
837-
break;
838-
case AccessStorage::Nested:
839-
llvm_unreachable("unexpected storage");
840-
};
841-
auto base = storageWithBase.base;
842-
if (!base)
843-
return; // bail on unexpected patterns
813+
//===----------------------------------------------------------------------===//
814+
// AddressOwnership
815+
//===----------------------------------------------------------------------===//
844816

845-
auto intPtrOp = InteriorPointerOperand::inferFromResult(base);
846-
if (!intPtrOp)
847-
return;
817+
bool AddressOwnership::areUsesWithinLifetime(
818+
ArrayRef<Operand *> uses, DeadEndBlocks &deadEndBlocks) const {
819+
if (!base.hasLocalOwnershipLifetime())
820+
return true;
848821

849-
SILValue nonAddressValue = intPtrOp.operand->get();
850-
if (nonAddressValue->getOwnershipKind() != OwnershipKind::Guaranteed) {
851-
this->mayBeBorrowed = false;
852-
return;
853-
}
854-
this->interiorPointerOp = intPtrOp;
822+
SILValue root = base.getOwnershipReferenceRoot();
823+
BorrowedValue borrow(root);
824+
if (borrow)
825+
return borrow.areUsesWithinLocalScope(uses, deadEndBlocks);
826+
827+
// --- A reference no borrow scope. Currently happens for project_box.
828+
829+
// Compute the reference value's liveness.
830+
PrunedLiveness liveness;
831+
liveness.computeSSALiveness(root);
832+
return liveness.areUsesWithinBoundary(uses, deadEndBlocks);
855833
}
856834

857835
//===----------------------------------------------------------------------===//
@@ -961,6 +939,7 @@ bool swift::getAllBorrowIntroducingValues(SILValue inputValue,
961939
return true;
962940
}
963941

942+
// FIXME: replace this logic with AccessBase::findOwnershipReferenceRoot.
964943
BorrowedValue swift::getSingleBorrowIntroducingValue(SILValue inputValue) {
965944
if (inputValue.getOwnershipKind() != OwnershipKind::Guaranteed)
966945
return {};

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,24 +1137,27 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
11371137
//
11381138
// NOTE: We also need to handle this here since a pointer_to_address is not a
11391139
// valid base value for an access path since it doesn't refer to any storage.
1140-
BorrowedAddress borrowedAddress(newValue);
1141-
if (!borrowedAddress.mayBeBorrowed)
1140+
AddressOwnership addressOwnership(newValue);
1141+
if (!addressOwnership.hasLocalOwnershipLifetime())
11421142
return;
11431143

1144-
if (!borrowedAddress.interiorPointerOp) {
1144+
auto intPtrOp =
1145+
InteriorPointerOperand::inferFromResult(
1146+
addressOwnership.base.getBaseAddress());
1147+
if (!intPtrOp) {
11451148
invalidate();
11461149
return;
11471150
}
11481151

1149-
ctx->extraAddressFixupInfo.intPtrOp = borrowedAddress.interiorPointerOp;
1150-
auto borrowedValue = borrowedAddress.interiorPointerOp.getSingleBaseValue();
1152+
ctx->extraAddressFixupInfo.intPtrOp = intPtrOp;
1153+
auto borrowedValue = intPtrOp.getSingleBorrowedValue();
11511154
if (!borrowedValue) {
11521155
invalidate();
11531156
return;
11541157
}
11551158

11561159
auto *intPtrInst =
1157-
cast<SingleValueInstruction>(borrowedAddress.interiorPointerOp.getUser());
1160+
cast<SingleValueInstruction>(intPtrOp.getUser());
11581161
auto checkBase = [&](SILValue srcAddr) {
11591162
return (srcAddr == intPtrInst) ? SILValue(intPtrInst) : SILValue();
11601163
};
@@ -1167,8 +1170,7 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
11671170

11681171
// For now, just gather up uses
11691172
auto &oldValueUses = ctx->extraAddressFixupInfo.allAddressUsesFromOldValue;
1170-
if (InteriorPointerOperand::findTransitiveUsesForAddress(oldValue,
1171-
oldValueUses)) {
1173+
if (findTransitiveUsesForAddress(oldValue, oldValueUses)) {
11721174
invalidate();
11731175
return;
11741176
}
@@ -1177,9 +1179,7 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
11771179
// need to perform the copy or not when we actually RAUW. So perform the is
11781180
// within region check. If we succeed, clear our extra state so we perform a
11791181
// normal RAUW.
1180-
SmallVector<Operand *, 8> scratchSpace;
1181-
if (borrowedValue.areUsesWithinScope(oldValueUses, scratchSpace,
1182-
ctx->deBlocks)) {
1182+
if (borrowedValue.areUsesWithinLocalScope(oldValueUses, ctx->deBlocks)) {
11831183
// We do not need to copy the base value! Clear the extra info we have.
11841184
ctx->extraAddressFixupInfo.clear();
11851185
}

0 commit comments

Comments
 (0)