Skip to content

Commit cd9218c

Browse files
authored
Merge pull request #34683 from gottesmm/pr-d6205adb8f293c5e7c3fad8419163cc936cc4adc
[ownership] Eliminate OperandOwnershipKindMap in favor of OwnershipConstraint
2 parents d74c261 + a294ab6 commit cd9218c

File tree

10 files changed

+372
-437
lines changed

10 files changed

+372
-437
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ class BorrowingOperandKind {
8989
BeginBorrow,
9090
BeginApply,
9191
Branch,
92+
Apply,
93+
TryApply,
94+
Yield,
9295
};
9396

9497
private:
@@ -109,6 +112,12 @@ class BorrowingOperandKind {
109112
return BorrowingOperandKind(BeginApply);
110113
case SILInstructionKind::BranchInst:
111114
return BorrowingOperandKind(Branch);
115+
case SILInstructionKind::ApplyInst:
116+
return BorrowingOperandKind(Apply);
117+
case SILInstructionKind::TryApplyInst:
118+
return BorrowingOperandKind(TryApply);
119+
case SILInstructionKind::YieldInst:
120+
return BorrowingOperandKind(Yield);
112121
}
113122
}
114123

@@ -142,7 +151,8 @@ struct BorrowingOperand {
142151
return *this;
143152
}
144153

145-
/// If value is a borrow introducer return it after doing some checks.
154+
/// If \p op is a borrow introducing operand return it after doing some
155+
/// checks.
146156
static Optional<BorrowingOperand> get(Operand *op) {
147157
auto *user = op->getUser();
148158
auto kind = BorrowingOperandKind::get(user->getKind());
@@ -151,6 +161,19 @@ struct BorrowingOperand {
151161
return BorrowingOperand(*kind, op);
152162
}
153163

164+
/// If \p op is a borrow introducing operand return it after doing some
165+
/// checks.
166+
static Optional<BorrowingOperand> get(const Operand *op) {
167+
return get(const_cast<Operand *>(op));
168+
}
169+
170+
/// If this borrowing operand results in the underlying value being borrowed
171+
/// over a region of code instead of just for a single instruction, visit
172+
/// those uses.
173+
///
174+
/// Example: An apply performs an instantaneous recursive borrow of a
175+
/// guaranteed value but a begin_apply borrows the value over the entire
176+
/// region of code corresponding to the coroutine.
154177
void visitLocalEndScopeInstructions(function_ref<void(Operand *)> func) const;
155178

156179
/// Returns true if this borrow scope operand consumes guaranteed
@@ -159,6 +182,9 @@ struct BorrowingOperand {
159182
switch (kind) {
160183
case BorrowingOperandKind::BeginBorrow:
161184
case BorrowingOperandKind::BeginApply:
185+
case BorrowingOperandKind::Apply:
186+
case BorrowingOperandKind::TryApply:
187+
case BorrowingOperandKind::Yield:
162188
return false;
163189
case BorrowingOperandKind::Branch:
164190
return true;
@@ -170,8 +196,14 @@ struct BorrowingOperand {
170196
/// for owned values.
171197
bool canAcceptOwnedValues() const {
172198
switch (kind) {
199+
// begin_borrow can take any parameter
173200
case BorrowingOperandKind::BeginBorrow:
201+
// Yield can implicit borrow owned values.
202+
case BorrowingOperandKind::Yield:
203+
// FullApplySites can implicit borrow owned values.
174204
case BorrowingOperandKind::BeginApply:
205+
case BorrowingOperandKind::Apply:
206+
case BorrowingOperandKind::TryApply:
175207
return true;
176208
case BorrowingOperandKind::Branch:
177209
return false;
@@ -189,6 +221,9 @@ struct BorrowingOperand {
189221
case BorrowingOperandKind::Branch:
190222
return true;
191223
case BorrowingOperandKind::BeginApply:
224+
case BorrowingOperandKind::Apply:
225+
case BorrowingOperandKind::TryApply:
226+
case BorrowingOperandKind::Yield:
192227
return false;
193228
}
194229
llvm_unreachable("Covered switch isn't covered?!");

include/swift/SIL/SILValue.h

Lines changed: 47 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,13 @@ struct OwnershipKind {
219219
return OwnershipKind::None;
220220
return *this;
221221
}
222+
223+
/// Convert this ownership kind to a StringRef.
224+
StringRef asString() const;
222225
};
223226

227+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const OwnershipKind &kind);
228+
224229
/// A value representing the specific ownership semantics that a SILValue may
225230
/// have.
226231
struct ValueOwnershipKind {
@@ -566,150 +571,50 @@ inline bool ValueOwnershipKind::isCompatibleWith(SILValue other) const {
566571
return isCompatibleWith(other.getOwnershipKind());
567572
}
568573

569-
/// A map from a ValueOwnershipKind that an operand can accept to a
570-
/// UseLifetimeConstraint that describes the effect that the operand's use has
571-
/// on the underlying value. If a ValueOwnershipKind is not in this map then
572-
/// matching an operand with the value results in an ill formed program.
573-
///
574-
/// So for instance, a map could specify that if a value is used as an owned
575-
/// parameter, then the use implies that the original value is destroyed at that
576-
/// point. In contrast, if the value is used as a guaranteed parameter, then the
577-
/// liveness constraint just requires that the value remains alive at the use
578-
/// point.
579-
struct OperandOwnershipKindMap {
580-
// One bit for if a value exists and if the value exists, what the
581-
// ownership constraint is. These are stored as pairs.
582-
//
583-
// NOTE: We are burning 1 bit per unset value. But this is without
584-
// matter since we are always going to need less bits than 64, so we
585-
// should always have a small case SmallBitVector, so there is no
586-
// difference in size.
587-
static constexpr unsigned NUM_DATA_BITS =
588-
2 * (unsigned(OwnershipKind::LastValueOwnershipKind) + 1);
589-
590-
/// A bit vector representing our "map". Given a ValueOwnershipKind k, if the
591-
/// operand can accept k, the unsigned(k)*2 bit will be set to true. Assuming
592-
/// that bit is set, the unsigned(k)*2+1 bit is set to the use lifetime
593-
/// constraint provided by the value.
594-
SmallBitVector data;
595-
596-
OperandOwnershipKindMap() : data(NUM_DATA_BITS) {}
597-
OperandOwnershipKindMap(ValueOwnershipKind kind,
598-
UseLifetimeConstraint constraint)
599-
: data(NUM_DATA_BITS) {
600-
add(kind, constraint);
601-
}
574+
class OwnershipConstraint {
575+
OwnershipKind ownershipKind;
576+
UseLifetimeConstraint lifetimeConstraint;
602577

603-
/// Return the OperandOwnershipKindMap that tests for compatibility with
604-
/// ValueOwnershipKind kind. This means that it will accept a element whose
605-
/// ownership is OwnershipKind::None.
606-
static OperandOwnershipKindMap
607-
compatibilityMap(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
608-
OperandOwnershipKindMap set;
609-
set.addCompatibilityConstraint(kind, constraint);
610-
return set;
578+
public:
579+
OwnershipConstraint(OwnershipKind inputOwnershipKind,
580+
UseLifetimeConstraint inputLifetimeConstraint)
581+
: ownershipKind(inputOwnershipKind),
582+
lifetimeConstraint(inputLifetimeConstraint) {
583+
assert((ownershipKind != OwnershipKind::None ||
584+
lifetimeConstraint == UseLifetimeConstraint::NonLifetimeEnding) &&
585+
"ValueOwnershipKind::None can never have their lifetime ended");
611586
}
612587

613-
/// Return a map that is compatible with any and all ValueOwnershipKinds
614-
/// except for \p kind.
615-
static OperandOwnershipKindMap
616-
compatibleWithAllExcept(ValueOwnershipKind kind) {
617-
OperandOwnershipKindMap map;
618-
unsigned index = 0;
619-
unsigned end = unsigned(OwnershipKind::LastValueOwnershipKind) + 1;
620-
for (; index != end; ++index) {
621-
if (ValueOwnershipKind(index) == kind) {
622-
continue;
623-
}
624-
map.add(ValueOwnershipKind(index),
625-
UseLifetimeConstraint::NonLifetimeEnding);
626-
}
627-
return map;
588+
OwnershipKind getPreferredKind() const {
589+
return ownershipKind;
628590
}
629591

630-
/// Create a map that has compatibility constraints for each of the
631-
/// ValueOwnershipKind, UseLifetimeConstraints in \p args.
632-
static OperandOwnershipKindMap
633-
compatibilityMap(std::initializer_list<
634-
std::pair<ValueOwnershipKind, UseLifetimeConstraint>>
635-
args) {
636-
OperandOwnershipKindMap map;
637-
for (auto &p : args) {
638-
map.addCompatibilityConstraint(p.first, p.second);
639-
}
640-
return map;
592+
bool isLifetimeEnding() const {
593+
return lifetimeConstraint == UseLifetimeConstraint::LifetimeEnding;
641594
}
642595

643-
/// Return a map that states that an operand can take any ownership with each
644-
/// ownership having a must be live constraint.
645-
static OperandOwnershipKindMap allLive() {
646-
OperandOwnershipKindMap map;
647-
unsigned index = 0;
648-
unsigned end = unsigned(OwnershipKind::LastValueOwnershipKind) + 1;
649-
while (index != end) {
650-
map.add(ValueOwnershipKind(index),
651-
UseLifetimeConstraint::NonLifetimeEnding);
652-
++index;
653-
}
654-
return map;
596+
UseLifetimeConstraint getLifetimeConstraint() const {
597+
return lifetimeConstraint;
655598
}
656599

657-
/// Specify that the operand associated with this set can accept a value with
658-
/// ValueOwnershipKind \p kind. The value provided by the operand will have a
659-
/// new ownership enforced constraint defined by \p constraint.
660-
void add(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
661-
unsigned index = unsigned(kind);
662-
unsigned kindOffset = index * 2;
663-
unsigned constraintOffset = index * 2 + 1;
664-
665-
// If we have already put this kind into the map, we require the constraint
666-
// offset to be the same, i.e. we only allow for a kind to be added twice if
667-
// the constraint is idempotent. We assert otherwise.
668-
assert((!data[kindOffset] || UseLifetimeConstraint(bool(
669-
data[constraintOffset])) == constraint) &&
670-
"Adding kind twice to the map with different constraints?!");
671-
data[kindOffset] = true;
672-
data[constraintOffset] = bool(constraint);
600+
static OwnershipConstraint anyValueAcceptingConstraint() {
601+
return {OwnershipKind::Any, UseLifetimeConstraint::NonLifetimeEnding};
673602
}
674603

675-
void addCompatibilityConstraint(ValueOwnershipKind kind,
676-
UseLifetimeConstraint constraint) {
677-
add(OwnershipKind::None, UseLifetimeConstraint::NonLifetimeEnding);
678-
add(kind, constraint);
679-
}
604+
bool satisfiedBy(const Operand *use) const;
680605

681-
bool canAcceptKind(ValueOwnershipKind kind) const {
682-
unsigned index = unsigned(kind);
683-
unsigned kindOffset = index * 2;
684-
return data[kindOffset];
606+
bool satisfiesConstraint(ValueOwnershipKind testKind) const {
607+
return ownershipKind.join(testKind) == testKind;
685608
}
686609

687-
UseLifetimeConstraint getLifetimeConstraint(ValueOwnershipKind kind) const;
688-
689-
void print(llvm::raw_ostream &os) const;
690-
SWIFT_DEBUG_DUMP;
610+
bool operator==(const OwnershipConstraint &other) const {
611+
return ownershipKind == other.ownershipKind &&
612+
isLifetimeEnding() == other.isLifetimeEnding();
613+
}
691614
};
692615

693-
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
694-
OperandOwnershipKindMap map) {
695-
map.print(os);
696-
return os;
697-
}
698-
699-
// Out of line to work around lack of forward declaration for operator <<.
700-
inline UseLifetimeConstraint
701-
OperandOwnershipKindMap::getLifetimeConstraint(ValueOwnershipKind kind) const {
702-
#ifndef NDEBUG
703-
if (!canAcceptKind(kind)) {
704-
llvm::errs() << "Can not lookup lifetime constraint: " << kind
705-
<< ". Not in map!\n"
706-
<< *this;
707-
llvm_unreachable("standard error assertion");
708-
}
709-
#endif
710-
unsigned constraintOffset = unsigned(kind) * 2 + 1;
711-
return UseLifetimeConstraint(data[constraintOffset]);
712-
}
616+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
617+
OwnershipConstraint constraint);
713618

714619
/// A formal SIL reference to a value, suitable for use as a stored
715620
/// operand.
@@ -788,24 +693,25 @@ class Operand {
788693
/// Return which operand this is in the operand list of the using instruction.
789694
unsigned getOperandNumber() const;
790695

791-
/// Return the static map of ValueOwnershipKinds that this operand can
792-
/// potentially have to the UseLifetimeConstraint associated with that
793-
/// ownership kind
696+
/// Return the ownership constraint that restricts what types of values this
697+
/// Operand can contain. Returns none if the operand is a type dependent
698+
/// operand.
794699
///
795700
/// NOTE: This is implemented in OperandOwnership.cpp.
796-
OperandOwnershipKindMap getOwnershipKindMap() const;
701+
Optional<OwnershipConstraint> getOwnershipConstraint() const;
702+
703+
/// Returns true if changing the operand to use a value with the given
704+
/// ownership kind would not cause the operand to violate the operand's
705+
/// ownership constraints. Returns false otherwise.
706+
bool canAcceptKind(ValueOwnershipKind kind) const;
707+
708+
/// Returns true if this operand and its value satisfy the operand's
709+
/// operand constraint.
710+
bool satisfiesConstraints() const;
797711

798712
/// Returns true if this operand acts as a use that consumes its associated
799713
/// value.
800-
bool isLifetimeEnding() const {
801-
// Type dependent uses can never be consuming and do not have valid
802-
// ownership maps since they do not participate in the ownership system.
803-
if (isTypeDependent())
804-
return false;
805-
auto map = getOwnershipKindMap();
806-
auto constraint = map.getLifetimeConstraint(get().getOwnershipKind());
807-
return constraint == UseLifetimeConstraint::LifetimeEnding;
808-
}
714+
bool isLifetimeEnding() const;
809715

810716
SILBasicBlock *getParentBlock() const;
811717
SILFunction *getParentFunction() const;

0 commit comments

Comments
 (0)