Skip to content

Commit 18f118a

Browse files
authored
Merge pull request #19445 from gottesmm/ownership-kind-set-classifer
[ownership] Extract out from SILOwnershipVerifier the OperandOwnershi…
2 parents 5e2c815 + 157091d commit 18f118a

File tree

15 files changed

+1198
-829
lines changed

15 files changed

+1198
-829
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/SILModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ class SILModule {
378378
return wholeModule;
379379
}
380380

381+
bool isStdlibModule() const;
382+
381383
/// Returns true if it is the optimized OnoneSupport module.
382384
bool isOptimizedOnoneSupportModule() const;
383385

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

lib/SIL/SILModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,10 @@ void SILModule::setOptRecordStream(
660660
OptRecordRawStream = std::move(RawStream);
661661
}
662662

663+
bool SILModule::isStdlibModule() const {
664+
return TheSwiftModule->isStdlibModule();
665+
}
666+
663667
SILProperty *SILProperty::create(SILModule &M,
664668
bool Serialized,
665669
AbstractStorageDecl *Decl,

0 commit comments

Comments
 (0)