Skip to content

Commit 157091d

Browse files
committed
[ownership] Extract out from SILOwnershipVerifier the OperandOwnershipKindMapClassifier
NOTE: This is not the final form of how operand ownership restraints will be represented. This patch is instead an incremental change that extracts out this functionality from the ownership verifier as a pure refactor. rdar://44667493
1 parent 2e63b4c commit 157091d

File tree

12 files changed

+1188
-828
lines changed

12 files changed

+1188
-828
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- ApplySite.h ------------------------------------------------------===//
1+
//===--- ApplySite.h -------------------------------------*- mode: c++ -*--===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -459,6 +459,18 @@ class FullApplySite : public ApplySite {
459459
return getArguments().slice(getNumIndirectSILResults());
460460
}
461461

462+
/// Returns true if \p op is the callee operand of this apply site
463+
/// and not an argument operand.
464+
bool isCalleeOperand(const Operand &op) const {
465+
return op.getOperandNumber() < getOperandIndexOfFirstArgument();
466+
}
467+
468+
/// Returns true if \p op is an operand that passes an indirect
469+
/// result argument to the apply site.
470+
bool isIndirectResultOperand(const Operand &op) const {
471+
return getCalleeArgIndex(op) < getNumIndirectSILResults();
472+
}
473+
462474
static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }
463475

464476
static bool classof(const SILInstruction *inst) {

include/swift/SIL/OwnershipUtils.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,26 +64,26 @@ struct ErrorBehaviorKind {
6464
/// so types/etc do not leak.
6565
struct OwnershipChecker {
6666
/// The list of regular users from the last run of the checker.
67-
SmallVector<SILInstruction *, 16> RegularUsers;
67+
SmallVector<SILInstruction *, 16> regularUsers;
6868

6969
/// The list of regular users from the last run of the checker.
70-
SmallVector<SILInstruction *, 16> LifetimeEndingUsers;
70+
SmallVector<SILInstruction *, 16> lifetimeEndingUsers;
7171

7272
/// The live blocks for the SILValue we processed. This can be used to
7373
/// determine if a block is in the "live" region of our SILInstruction.
74-
SmallPtrSet<SILBasicBlock *, 32> LiveBlocks;
74+
SmallPtrSet<SILBasicBlock *, 32> liveBlocks;
7575

7676
/// The list of implicit regular users from the last run of the checker.
7777
///
7878
/// This is used to encode end of scope like instructions.
79-
SmallVector<SILInstruction *, 4> ImplicitRegularUsers;
79+
SmallVector<SILInstruction *, 4> endScopeRegularUsers;
8080

8181
/// The module that we are in.
82-
SILModule &Mod;
82+
SILModule &mod;
8383

8484
/// A cache of dead-end basic blocks that we use to determine if we can
8585
/// ignore "leaks".
86-
DeadEndBlocks &DEBlocks;
86+
DeadEndBlocks &deadEndBlocks;
8787

8888
bool checkValue(SILValue Value);
8989
};
@@ -98,7 +98,7 @@ bool valueHasLinearLifetime(SILValue value,
9898
ArrayRef<BranchPropagatedUser> consumingUses,
9999
ArrayRef<BranchPropagatedUser> nonConsumingUses,
100100
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
101-
DeadEndBlocks &deBlocks,
101+
DeadEndBlocks &deadEndBlocks,
102102
ownership::ErrorBehaviorKind errorBehavior);
103103

104104
} // namespace swift

include/swift/SIL/SILValue.h

Lines changed: 206 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,33 @@ static inline llvm::hash_code hash_value(ValueKind K) {
5555
return llvm::hash_value(size_t(K));
5656
}
5757

58+
/// What constraint does the given use of an SSA value put on the lifetime of
59+
/// the given SSA value.
60+
///
61+
/// There are two possible constraints: MustBeLive and
62+
/// MustBeInvalidated. MustBeLive means that the SSA value must be able to be
63+
/// used in a valid way at the given use point. MustBeInvalidated means that any
64+
/// use of given SSA value after this instruction on any path through this
65+
/// instruction.
66+
enum class UseLifetimeConstraint {
67+
/// This use requires the SSA value to be live after the given instruction's
68+
/// execution.
69+
MustBeLive,
70+
71+
/// This use invalidates the given SSA value.
72+
///
73+
/// This means that the given SSA value can not have any uses that are
74+
/// reachable from this instruction. When a value has owned semantics this
75+
/// means the SSA value is destroyed at this point. When a value has
76+
/// guaranteed (i.e. shared borrow) semantics this means that the program
77+
/// has left the scope of the borrowed SSA value and said value can not be
78+
/// used.
79+
MustBeInvalidated,
80+
};
81+
82+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
83+
UseLifetimeConstraint constraint);
84+
5885
/// A value representing the specific ownership semantics that a SILValue may
5986
/// have.
6087
struct ValueOwnershipKind {
@@ -64,27 +91,23 @@ struct ValueOwnershipKind {
6491
/// with Trivial ownership kind can be used. Other side effects (e.g. Memory
6592
/// dependencies) must still be respected. A SILValue with Trivial ownership
6693
/// kind must be of Trivial SILType (i.e. SILType::isTrivial(SILModule &)
67-
/// must
68-
/// return true).
94+
/// must return true).
6995
///
7096
/// Some examples of SIL types with Trivial ownership are: Builtin.Int32,
7197
/// Builtin.RawPointer, aggregates containing all trivial types.
7298
Trivial,
7399

74100
/// A SILValue with `Unowned` ownership kind is an independent value that
75-
/// has
76-
/// a lifetime that is only guaranteed to last until the next program
77-
/// visible
78-
/// side-effect. To maintain the lifetime of an unowned value, it must be
79-
/// converted to an owned representation via a copy_value.
101+
/// has a lifetime that is only guaranteed to last until the next program
102+
/// visible side-effect. To maintain the lifetime of an unowned value, it
103+
/// must be converted to an owned representation via a copy_value.
80104
///
81105
/// Unowned ownership kind occurs mainly along method/function boundaries in
82106
/// between Swift and Objective-C code.
83107
Unowned,
84108

85109
/// A SILValue with `Owned` ownership kind is an independent value that has
86-
/// an
87-
/// ownership independent of any other ownership imbued within it. The
110+
/// an ownership independent of any other ownership imbued within it. The
88111
/// SILValue must be paired with a consuming operation that ends the SSA
89112
/// value's lifetime exactly once along all paths through the program.
90113
Owned,
@@ -105,10 +128,9 @@ struct ValueOwnershipKind {
105128
Guaranteed,
106129

107130
/// A SILValue with undefined ownership. It can pair with /Any/ ownership
108-
/// kinds . This means that it could take on /any/ ownership semantics. This
131+
/// kinds. This means that it could take on /any/ ownership semantics. This
109132
/// is meant only to model SILUndef and to express certain situations where
110-
/// we
111-
/// use unqualified ownership. Expected to tighten over time.
133+
/// we use unqualified ownership. Expected to tighten over time.
112134
Any,
113135

114136
LastValueOwnershipKind = Any,
@@ -151,6 +173,23 @@ struct ValueOwnershipKind {
151173
ValueOwnershipKind getProjectedOwnershipKind(SILModule &M,
152174
SILType Proj) const;
153175

176+
/// Return the lifetime constraint semantics for this
177+
/// ValueOwnershipKind when forwarding ownership.
178+
///
179+
/// This is MustBeInvalidated for Owned and MustBeLive for all other ownership
180+
/// kinds.
181+
UseLifetimeConstraint getForwardingLifetimeConstraint() const {
182+
switch (Value) {
183+
case ValueOwnershipKind::Trivial:
184+
case ValueOwnershipKind::Any:
185+
case ValueOwnershipKind::Guaranteed:
186+
case ValueOwnershipKind::Unowned:
187+
return UseLifetimeConstraint::MustBeLive;
188+
case ValueOwnershipKind::Owned:
189+
return UseLifetimeConstraint::MustBeInvalidated;
190+
}
191+
}
192+
154193
/// Returns true if \p Other can be merged successfully with this, implying
155194
/// that the two ownership kinds are "compatibile".
156195
///
@@ -347,6 +386,149 @@ class SILValue {
347386
DeadEndBlocks *DEBlocks = nullptr) const;
348387
};
349388

389+
/// A map from a ValueOwnershipKind that an operand can accept to a
390+
/// UseLifetimeConstraint that describes the effect that the operand's use has
391+
/// on the underlying value. If a ValueOwnershipKind is not in this map then
392+
/// matching an operand with the value results in an ill formed program.
393+
///
394+
/// So for instance, a map could specify that if a value is used as an owned
395+
/// parameter, then the use implies that the original value is destroyed at that
396+
/// point. In contrast, if the value is used as a guaranteed parameter, then the
397+
/// liveness constraint just requires that the value remains alive at the use
398+
/// point.
399+
struct OperandOwnershipKindMap {
400+
// One bit for if a value exists and if the value exists, what the
401+
// ownership constraint is. These are stored as pairs.
402+
//
403+
// NOTE: We are burning 1 bit per unset value. But this is without
404+
// matter since we are always going to need less bits than 64, so we
405+
// should always have a small case SmallBitVector, so there is no
406+
// difference in size.
407+
static constexpr unsigned NUM_DATA_BITS =
408+
2 * (unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1);
409+
410+
/// A bit vector representing our "map". Given a ValueOwnershipKind k, if the
411+
/// operand can accept k, the unsigned(k)*2 bit will be set to true. Assuming
412+
/// that bit is set, the unsigned(k)*2+1 bit is set to the use lifetime
413+
/// constraint provided by the value.
414+
SmallBitVector data;
415+
416+
OperandOwnershipKindMap() : data(NUM_DATA_BITS) {}
417+
OperandOwnershipKindMap(ValueOwnershipKind kind,
418+
UseLifetimeConstraint constraint)
419+
: data(NUM_DATA_BITS) {
420+
add(kind, constraint);
421+
}
422+
423+
/// Return the OperandOwnershipKindMap that tests for compatibility with
424+
/// ValueOwnershipKind kind. This means that it will accept a element whose
425+
/// ownership is ValueOwnershipKind::Any.
426+
static OperandOwnershipKindMap
427+
compatibilityMap(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
428+
OperandOwnershipKindMap set;
429+
set.addCompatibilityConstraint(kind, constraint);
430+
return set;
431+
}
432+
433+
/// Return a map that is compatible with any and all ValueOwnershipKinds
434+
/// except for \p kind.
435+
static OperandOwnershipKindMap
436+
compatibleWithAllExcept(ValueOwnershipKind kind) {
437+
OperandOwnershipKindMap map;
438+
unsigned index = 0;
439+
unsigned end = unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1;
440+
for (; index != end; ++index) {
441+
if (ValueOwnershipKind(index) == kind) {
442+
continue;
443+
}
444+
map.add(ValueOwnershipKind(index), UseLifetimeConstraint::MustBeLive);
445+
}
446+
return map;
447+
}
448+
449+
/// Create a map that has compatibility constraints for each of the
450+
/// ValueOwnershipKind, UseLifetimeConstraints in \p args.
451+
static OperandOwnershipKindMap
452+
compatibilityMap(std::initializer_list<
453+
std::pair<ValueOwnershipKind, UseLifetimeConstraint>>
454+
args) {
455+
OperandOwnershipKindMap map;
456+
for (auto &p : args) {
457+
map.addCompatibilityConstraint(p.first, p.second);
458+
}
459+
return map;
460+
}
461+
462+
/// Return a map that states that an operand can take any ownership with each
463+
/// ownership having a must be live constraint.
464+
static OperandOwnershipKindMap allLive() {
465+
OperandOwnershipKindMap map;
466+
unsigned index = 0;
467+
unsigned end = unsigned(ValueOwnershipKind::LastValueOwnershipKind) + 1;
468+
while (index != end) {
469+
map.add(ValueOwnershipKind(index), UseLifetimeConstraint::MustBeLive);
470+
++index;
471+
}
472+
return map;
473+
}
474+
475+
/// Specify that the operand associated with this set can accept a value with
476+
/// ValueOwnershipKind \p kind. The value provided by the operand will have a
477+
/// new ownership enforced constraint defined by \p constraint.
478+
void add(ValueOwnershipKind kind, UseLifetimeConstraint constraint) {
479+
unsigned index = unsigned(kind);
480+
unsigned kindOffset = index * 2;
481+
unsigned constraintOffset = index * 2 + 1;
482+
483+
// If we have already put this kind into the map, we require the constraint
484+
// offset to be the same, i.e. we only allow for a kind to be added twice if
485+
// the constraint is idempotent. We assert otherwise.
486+
assert((!data[kindOffset] || UseLifetimeConstraint(bool(
487+
data[constraintOffset])) == constraint) &&
488+
"Adding kind twice to the map with different constraints?!");
489+
data[kindOffset] = true;
490+
data[constraintOffset] = bool(constraint);
491+
}
492+
493+
void addCompatibilityConstraint(ValueOwnershipKind kind,
494+
UseLifetimeConstraint constraint) {
495+
add(ValueOwnershipKind::Any, UseLifetimeConstraint::MustBeLive);
496+
add(kind, constraint);
497+
}
498+
499+
bool canAcceptKind(ValueOwnershipKind kind) const {
500+
unsigned index = unsigned(kind);
501+
unsigned kindOffset = index * 2;
502+
return data[kindOffset];
503+
}
504+
505+
UseLifetimeConstraint getLifetimeConstraint(ValueOwnershipKind kind) const;
506+
507+
void print(llvm::raw_ostream &os) const;
508+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in a debugger");
509+
};
510+
511+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
512+
OperandOwnershipKindMap map) {
513+
map.print(os);
514+
return os;
515+
}
516+
517+
// Out of line to work around lack of forward declaration for operator <<.
518+
inline UseLifetimeConstraint
519+
OperandOwnershipKindMap::getLifetimeConstraint(ValueOwnershipKind kind) const {
520+
#ifndef NDEBUG
521+
if (!canAcceptKind(kind)) {
522+
llvm::errs() << "Can not lookup lifetime constraint: " << kind
523+
<< ". Not in map!\n"
524+
<< *this;
525+
llvm_unreachable("standard error assertion");
526+
}
527+
#endif
528+
unsigned constraintOffset = unsigned(kind) * 2 + 1;
529+
return UseLifetimeConstraint(data[constraintOffset]);
530+
}
531+
350532
/// A formal SIL reference to a value, suitable for use as a stored
351533
/// operand.
352534
class Operand {
@@ -413,10 +595,20 @@ class Operand {
413595
SILInstruction *getUser() { return Owner; }
414596
const SILInstruction *getUser() const { return Owner; }
415597

416-
/// getOperandNumber - Return which operand this is in the operand list of the
417-
/// using instruction.
598+
/// Return which operand this is in the operand list of the using instruction.
418599
unsigned getOperandNumber() const;
419600

601+
/// Return the static map of ValueOwnershipKinds that this operand can
602+
/// potentially have to the UseLifetimeConstraint associated with that
603+
/// ownership kind
604+
///
605+
/// NOTE: This is implemented in OperandOwnershipKindMapClassifier.cpp.
606+
///
607+
/// NOTE: The default argument isSubValue is a temporary staging flag that
608+
/// will be removed once borrow scoping is checked by the normal verifier.
609+
OperandOwnershipKindMap
610+
getOwnershipKindMap(bool isForwardingSubValue = false) const;
611+
420612
private:
421613
void removeFromCurrent() {
422614
if (!Back) return;

include/swift/SILOptimizer/Utils/Local.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ class ValueLifetimeAnalysis {
246246
/// In this case, if \p mode is AllowToModifyCFG, those critical edges are
247247
/// split, otherwise nothing is done and the returned \p Fr is not valid.
248248
///
249-
/// If \p DEBlocks is provided, all dead-end blocks are ignored. This prevents
250-
/// unreachable-blocks to be included in the frontier.
249+
/// If \p deadEndBlocks is provided, all dead-end blocks are ignored. This
250+
/// prevents unreachable-blocks to be included in the frontier.
251251
bool computeFrontier(Frontier &Fr, Mode mode,
252252
DeadEndBlocks *DEBlocks = nullptr);
253253

0 commit comments

Comments
 (0)