Skip to content

Commit 5830cea

Browse files
authored
Merge pull request #39638 from atrick/cleanup-addressownership
Add AddressOwnership utility and cleanup OSSA rauw
2 parents eabfce9 + 71cf3b6 commit 5830cea

File tree

8 files changed

+279
-191
lines changed

8 files changed

+279
-191
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 106 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515

1616
#include "swift/Basic/Debug.h"
1717
#include "swift/Basic/LLVM.h"
18+
#include "swift/SIL/MemAccessUtils.h"
1819
#include "swift/SIL/SILArgument.h"
19-
#include "swift/SIL/SILInstruction.h"
2020
#include "swift/SIL/SILBasicBlock.h"
21+
#include "swift/SIL/SILInstruction.h"
2122
#include "swift/SIL/SILValue.h"
2223
#include "llvm/ADT/SmallPtrSet.h"
2324
#include "llvm/ADT/SmallVector.h"
@@ -29,6 +30,7 @@ class SILInstruction;
2930
class SILModule;
3031
class SILValue;
3132
class DeadEndBlocks;
33+
class PrunedLiveness;
3234

3335
/// Returns true if v is an address or trivial.
3436
bool isValueAddressOrTrivial(SILValue v);
@@ -148,7 +150,7 @@ bool findTransitiveGuaranteedUses(SILValue guaranteedValue,
148150
/// 1. If \p guaranteedValue introduces a borrow scope (begin_borrow,
149151
/// load_borrow, or phi), then its only use points are the extended scope-ending
150152
/// uses, and this function returns true. This is, in fact, equivalent to
151-
/// calling BorrowedValue::visitExtendedLocalScopeEndingUses().
153+
/// calling BorrowedValue::visitExtendedScopeEndingUses().
152154
///
153155
/// 2. If \p guaranteedValue does not introduce a borrow scope (it is not a
154156
/// valid BorrowedValue), then its uses are discovered transitively by looking
@@ -172,8 +174,8 @@ class ForwardingOperand {
172174
return use->getOwnershipConstraint();
173175
}
174176

175-
ValueOwnershipKind getOwnershipKind() const;
176-
void setOwnershipKind(ValueOwnershipKind newKind) const;
177+
ValueOwnershipKind getForwardingOwnershipKind() const;
178+
void setForwardingOwnershipKind(ValueOwnershipKind newKind) const;
177179
void replaceOwnershipKind(ValueOwnershipKind oldKind,
178180
ValueOwnershipKind newKind) const;
179181

@@ -304,13 +306,20 @@ struct BorrowingOperand {
304306
/// visits the end of the introduced borrow scope.
305307
bool visitScopeEndingUses(function_ref<bool(Operand *)> func) const;
306308

309+
/// Returns true for borrows that create a local borrow scope but have no
310+
/// scope-ending uses (presumably all paths are dead-end blocks). This does
311+
/// not include instantaneous borrows, which don't require explicit scope
312+
/// ending uses.
313+
///
314+
/// FIXME: Borrow scopes should have scope-ending uses on all paths, even to
315+
/// dead end blocks. When the verifier enforces this, remove this check.
316+
bool hasEmptyRequiredEndingUses() const;
317+
307318
/// Visit the scope ending operands of the extended scope, after transitively
308319
/// searching through reborrows. These uses might not be dominated by this
309320
/// BorrowingOperand.
310321
///
311322
/// Returns false and early exits if the visitor \p func returns false.
312-
///
313-
/// Note: this does not visit the intermediate reborrows.
314323
bool visitExtendedScopeEndingUses(function_ref<bool(Operand *)> func) const;
315324

316325
/// Returns true if this borrow scope operand consumes guaranteed
@@ -493,7 +502,7 @@ struct InteriorPointerOperand;
493502
/// jointly post-dominate this value (see visitLocalScopeEndingUses()). The
494503
/// extended scope, including reborrows has end points that are not dominated by
495504
/// this value but still jointly post-dominate (see
496-
/// visitExtendedLocalScopeEndingUses()).
505+
/// visitExtendedScopeEndingUses()).
497506
struct BorrowedValue {
498507
SILValue value;
499508
BorrowedValueKind kind = BorrowedValueKind::Invalid;
@@ -535,20 +544,25 @@ struct BorrowedValue {
535544

536545
bool isLocalScope() const { return kind.isLocalScope(); }
537546

538-
/// Returns true if the passed in set of uses is completely within
539-
/// the lifetime of this borrow introducer.
547+
/// Add this scopes live blocks into the PrunedLiveness result.
548+
void computeLiveness(PrunedLiveness &liveness) const;
549+
550+
/// Returns true if \p uses are completely within this borrow introducer's
551+
/// local scope.
540552
///
541-
/// NOTE: Scratch space is used internally to this method to store the end
542-
/// borrow scopes if needed.
543-
bool areUsesWithinScope(ArrayRef<Operand *> uses,
544-
SmallVectorImpl<Operand *> &scratchSpace,
545-
DeadEndBlocks &deadEndBlocks) const;
553+
/// Precondition: \p uses are dominated by the local borrow introducer.
554+
///
555+
/// This ignores reborrows. The assumption is that, since \p uses are
556+
/// dominated by this local scope, checking the extended borrow scope should
557+
/// not be necessary to determine they are within the scope.
558+
bool areUsesWithinLocalScope(ArrayRef<Operand *> uses,
559+
DeadEndBlocks &deadEndBlocks) const;
546560

547561
/// Given a local borrow scope introducer, visit all non-forwarding consuming
548562
/// users. This means that this looks through guaranteed block arguments. \p
549563
/// visitor is *not* called on Reborrows, only on final scope ending uses.
550-
bool visitExtendedLocalScopeEndingUses(
551-
function_ref<bool(Operand *)> visitor) const;
564+
bool
565+
visitExtendedScopeEndingUses(function_ref<bool(Operand *)> visitor) const;
552566

553567
void print(llvm::raw_ostream &os) const;
554568
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
@@ -583,6 +597,14 @@ struct BorrowedValue {
583597
func, InteriorPointerOperandVisitorKind::YesNestedYesReborrows);
584598
}
585599

600+
bool hasReborrow() const {
601+
for (auto *op : value->getUses()) {
602+
if (op->getOperandOwnership() == OperandOwnership::Reborrow)
603+
return true;
604+
}
605+
return false;
606+
}
607+
586608
/// Visit all immediate uses of this borrowed value and if any of them are
587609
/// reborrows, place them in BorrowingOperand form into \p
588610
/// foundReborrows. Returns true if we appended any such reborrows to
@@ -591,12 +613,10 @@ struct BorrowedValue {
591613
&foundReborrows) const {
592614
bool foundAnyReborrows = false;
593615
for (auto *op : value->getUses()) {
594-
if (auto borrowingOperand = BorrowingOperand(op)) {
595-
if (borrowingOperand.isReborrow()) {
596-
foundReborrows.push_back(
597-
{value->getParentBlock(), op->getOperandNumber()});
598-
foundAnyReborrows = true;
599-
}
616+
if (op->getOperandOwnership() == OperandOwnership::Reborrow) {
617+
foundReborrows.push_back(
618+
{value->getParentBlock(), op->getOperandNumber()});
619+
foundAnyReborrows = true;
600620
}
601621
}
602622
return foundAnyReborrows;
@@ -642,6 +662,13 @@ bool getAllBorrowIntroducingValues(SILValue value,
642662
/// introducer, then we return a .some(BorrowScopeIntroducingValue).
643663
BorrowedValue getSingleBorrowIntroducingValue(SILValue inputValue);
644664

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+
645672
class InteriorPointerOperandKind {
646673
public:
647674
enum Kind : uint8_t {
@@ -785,7 +812,7 @@ struct InteriorPointerOperand {
785812
}
786813

787814
/// Return the base BorrowedValue of the incoming value's operand.
788-
BorrowedValue getSingleBaseValue() const {
815+
BorrowedValue getSingleBorrowedValue() const {
789816
return getSingleBorrowIntroducingValue(operand->get());
790817
}
791818

@@ -820,14 +847,6 @@ struct InteriorPointerOperand {
820847
onError);
821848
}
822849

823-
/// The algorithm that is used to determine what the verifier will consider to
824-
/// be transitive uses of the given address. Used to implement \see
825-
/// findTransitiveUses.
826-
static bool
827-
findTransitiveUsesForAddress(SILValue address,
828-
SmallVectorImpl<Operand *> &foundUses,
829-
std::function<void(Operand *)> *onError = nullptr);
830-
831850
Operand *operator->() { return operand; }
832851
const Operand *operator->() const { return operand; }
833852
Operand *operator*() { return operand; }
@@ -840,23 +859,70 @@ struct InteriorPointerOperand {
840859
: operand(op), kind(kind) {}
841860
};
842861

843-
/// 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,
844863
/// then uses of the address cannot be replaced without ensuring that they are
845-
/// also within the same scope.
864+
/// also within the same owned lifetime or borrow scope.
846865
///
847-
/// If mayBeBorrowed is false, then there is no enclosing borrow scope and
848-
/// interiorPointerOp is irrelevant.
866+
/// If hasOwnership() is false, then there is no enclosing lifetime or borrow
867+
/// scope and interiorPointerOp is irrelevant.
849868
///
850-
/// If mayBeBorrowed is true, then interiorPointerOp refers to the operand that
869+
/// If hasOwnership() is true, then interiorPointerOp refers to the operand that
851870
/// converts a non-address value into the address from which the contructor's
852871
/// address is derived. If the best-effort to find an InteriorPointerOperand
853872
/// fails, then interiorPointerOp remains invalid, and clients must be
854873
/// conservative.
855-
struct BorrowedAddress {
856-
bool mayBeBorrowed = true;
857-
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+
}
885+
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+
}
858897

859-
BorrowedAddress(SILValue address);
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;
860926
};
861927

862928
class OwnedValueIntroducerKind {

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,14 @@ struct OwnershipFixupContext {
7474
/// This is the interior pointer operand that the new value we want to RAUW
7575
/// is transitively derived from and enables us to know the underlying
7676
/// borrowed base value that we need to lifetime extend.
77-
InteriorPointerOperand intPtrOp;
77+
///
78+
/// This is only initialized when the interior pointer has uses that must be
79+
/// replaced.
80+
AccessBase base;
7881

7982
void clear() {
8083
allAddressUsesFromOldValue.clear();
81-
intPtrOp = InteriorPointerOperand();
84+
base = AccessBase();
8285
}
8386
};
8487
AddressFixupContext extraAddressFixupInfo;
@@ -90,14 +93,14 @@ struct OwnershipFixupContext {
9093
transitiveBorrowedUses.clear();
9194
recursiveReborrows.clear();
9295
extraAddressFixupInfo.allAddressUsesFromOldValue.clear();
93-
extraAddressFixupInfo.intPtrOp = InteriorPointerOperand();
96+
extraAddressFixupInfo.base = AccessBase();
9497
}
9598

9699
private:
97100
/// Helper method called to determine if we discovered we needed interior
98101
/// pointer fixups while simplifying.
99102
bool needsInteriorPointerFixups() const {
100-
return bool(extraAddressFixupInfo.intPtrOp);
103+
return bool(extraAddressFixupInfo.base);
101104
}
102105
};
103106

0 commit comments

Comments
 (0)