Skip to content

Commit f857f2c

Browse files
committed
[ownership-verifier] Add proper subobject borrowing verification.
Previously, we would require an end_borrow for guaranteed subobject borrows. This was not the end design and of course would have triggered asserts since end_borrow only supports ending borrows of the same type (by design). This commit finishes the design by: 1. Verifying that subobject borrows do not have an end_borrow. This is done by asserting that subobject borrows do not have lifetime ending uses. 2. Treating uses of a subobject borrow as normal liveness uses of the base borrowed object. This means that the verifier will assert if there are any uses of borrowed subobjects after the base's end_value. rdar://29791263
1 parent 60449d5 commit f857f2c

File tree

2 files changed

+356
-52
lines changed

2 files changed

+356
-52
lines changed

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 201 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
#include "swift/AST/Module.h"
2020
#include "swift/AST/Types.h"
2121
#include "swift/Basic/Range.h"
22+
#include "swift/Basic/STLExtras.h"
2223
#include "swift/ClangImporter/ClangModule.h"
2324
#include "swift/SIL/Dominance.h"
2425
#include "swift/SIL/DynamicCasts.h"
2526
#include "swift/SIL/PrettyStackTrace.h"
27+
#include "swift/SIL/Projection.h"
2628
#include "swift/SIL/SILDebugScope.h"
2729
#include "swift/SIL/SILFunction.h"
2830
#include "swift/SIL/SILModule.h"
@@ -72,8 +74,37 @@ static bool isValueAddressOrTrivial(SILValue V, SILModule &M) {
7274
return V->getType().isAddress() || V->getType().isTrivial(M);
7375
}
7476

77+
static bool isOwnershipForwardingValueKind(ValueKind K) {
78+
switch (K) {
79+
case ValueKind::TupleInst:
80+
case ValueKind::StructInst:
81+
case ValueKind::EnumInst:
82+
case ValueKind::OpenExistentialRefInst:
83+
case ValueKind::UpcastInst:
84+
case ValueKind::UncheckedRefCastInst:
85+
case ValueKind::ConvertFunctionInst:
86+
case ValueKind::RefToBridgeObjectInst:
87+
case ValueKind::BridgeObjectToRefInst:
88+
case ValueKind::UnconditionalCheckedCastInst:
89+
case ValueKind::TupleExtractInst:
90+
case ValueKind::StructExtractInst:
91+
case ValueKind::UncheckedEnumDataInst:
92+
return true;
93+
default:
94+
return false;
95+
}
96+
}
97+
98+
static bool isOwnershipForwardingValue(SILValue V) {
99+
return isOwnershipForwardingValueKind(V->getKind());
100+
}
101+
102+
static bool isOwnershipForwardingInst(SILInstruction *I) {
103+
return isOwnershipForwardingValueKind(I->getKind());
104+
}
105+
75106
//===----------------------------------------------------------------------===//
76-
// OwnershipCompatibilityCheckerVisitor
107+
// OwnershipCompatibilityUseChecker
77108
//===----------------------------------------------------------------------===//
78109

79110
namespace {
@@ -88,14 +119,34 @@ class OwnershipCompatibilityUseChecker
88119
OwnershipUseCheckerResult> {
89120
SILModule &Mod;
90121
const Operand &Op;
122+
SILValue BaseValue;
91123

92124
public:
93-
OwnershipCompatibilityUseChecker(SILModule &M, const Operand &Op)
94-
: Mod(M), Op(Op) {}
125+
/// Create a new OwnershipCompatibilityUseChecker.
126+
///
127+
/// In most cases, one should only pass in \p Op and \p BaseValue will be set
128+
/// to Op.get(). In cases where one is trying to verify subobjects, Op.get()
129+
/// should be the subobject and Value should be the parent object. An example
130+
/// of where one would want to do this is in the case of value projections
131+
/// like struct_extract.
132+
OwnershipCompatibilityUseChecker(SILModule &M, const Operand &Op,
133+
SILValue BaseValue)
134+
: Mod(M), Op(Op), BaseValue(BaseValue) {
135+
assert((BaseValue == Op.get() ||
136+
BaseValue.getOwnershipKind() == ValueOwnershipKind::Guaranteed) &&
137+
"Guaranteed values are the only values allowed to have subobject");
138+
// We only support subobjects on objects.
139+
assert((BaseValue->getType().isObject() || !isCheckingSubObject()) &&
140+
"Checking a subobject, but do not have an object base value?!");
141+
}
142+
143+
bool isCheckingSubObject() const { return Op.get() != BaseValue; }
95144

96145
SILValue getValue() const { return Op.get(); }
97146

98147
ValueOwnershipKind getOwnershipKind() const {
148+
assert(getValue().getOwnershipKind() == Op.get().getOwnershipKind() &&
149+
"Expected ownership kind of parent value and operand");
99150
return getValue().getOwnershipKind();
100151
}
101152

@@ -125,8 +176,9 @@ class OwnershipCompatibilityUseChecker
125176
if (!Result.HasCompatibleOwnership) {
126177
llvm::errs() << "Function: '" << User->getFunction()->getName() << "'\n"
127178
<< "Have operand with incompatible ownership?!\n"
128-
<< "Value: " << *getValue() << "User: " << *User
129-
<< "Conv: " << getOwnershipKind() << "\n\n";
179+
<< "Value: " << *getValue() << "BaseValue: " << *BaseValue
180+
<< "User: " << *User << "Conv: " << getOwnershipKind()
181+
<< "\n\n";
130182
if (IsSILOwnershipVerifierTestingEnabled)
131183
return false;
132184
llvm_unreachable("triggering standard assertion failure routine");
@@ -212,10 +264,9 @@ CONSTANT_OWNERSHIP_INST(Owned, true, ReleaseValue)
212264
CONSTANT_OWNERSHIP_INST(Owned, true, StrongRelease)
213265
CONSTANT_OWNERSHIP_INST(Owned, true, StrongUnpin)
214266
CONSTANT_OWNERSHIP_INST(Owned, true, SwitchEnum)
267+
CONSTANT_OWNERSHIP_INST(Owned, true, CheckedCastBranch)
215268
CONSTANT_OWNERSHIP_INST(Owned, true, UnownedRelease)
216269
CONSTANT_OWNERSHIP_INST(Owned, true, InitExistentialRef)
217-
CONSTANT_OWNERSHIP_INST(Guaranteed, true,
218-
OpenExistentialRef) // We may need a take here.
219270
CONSTANT_OWNERSHIP_INST(Trivial, false, AddressToPointer)
220271
CONSTANT_OWNERSHIP_INST(Trivial, false, BindMemory)
221272
CONSTANT_OWNERSHIP_INST(Trivial, false, CheckedCastAddrBranch)
@@ -271,30 +322,6 @@ CONSTANT_OWNERSHIP_INST(Trivial, false, UnconditionalCheckedCastAddr)
271322
CONSTANT_OWNERSHIP_INST(Trivial, false, UnmanagedToRef)
272323
#undef CONSTANT_OWNERSHIP_INST
273324

274-
#define CONSTANT_OR_TRIVIAL_OWNERSHIP_INST( \
275-
OWNERSHIP, SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS, INST) \
276-
OwnershipUseCheckerResult \
277-
OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) { \
278-
assert(I->getNumOperands() && "Expected to have non-zero operands"); \
279-
if (ValueOwnershipKind::OWNERSHIP != ValueOwnershipKind::Trivial && \
280-
getOwnershipKind() == ValueOwnershipKind::Trivial) { \
281-
assert(isAddressOrTrivialType() && \
282-
"Trivial ownership requires a trivial type or an address"); \
283-
return {true, false}; \
284-
} \
285-
if (ValueOwnershipKind::OWNERSHIP == ValueOwnershipKind::Trivial) { \
286-
assert(isAddressOrTrivialType() && \
287-
"Trivial ownership requires a trivial type or an address"); \
288-
} \
289-
\
290-
return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP), \
291-
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
292-
}
293-
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, TupleExtract)
294-
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, StructExtract)
295-
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, UncheckedEnumData)
296-
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
297-
298325
#define ACCEPTS_ANY_OWNERSHIP_INST(INST) \
299326
OwnershipUseCheckerResult \
300327
OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) { \
@@ -347,6 +374,8 @@ ACCEPTS_ANY_NONTRIVIAL_OWNERSHIP(false, CopyUnownedValue)
347374
OwnershipUseCheckerResult
348375
OwnershipCompatibilityUseChecker::visitForwardingInst(SILInstruction *I) {
349376
assert(I->getNumOperands() && "Expected to have non-zero operands");
377+
assert(isOwnershipForwardingInst(I) &&
378+
"Expected to have an ownership forwarding inst");
350379
ArrayRef<Operand> Ops = I->getAllOperands();
351380
ValueOwnershipKind Base = getOwnershipKind();
352381
for (const Operand &Op : Ops) {
@@ -358,27 +387,73 @@ OwnershipCompatibilityUseChecker::visitForwardingInst(SILInstruction *I) {
358387
return {true, !isAddressOrTrivialType()};
359388
}
360389

361-
#define FORWARD_OWNERSHIP_INST(INST) \
390+
#define FORWARD_ANY_OWNERSHIP_INST(INST) \
362391
OwnershipUseCheckerResult \
363392
OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) { \
364393
return visitForwardingInst(I); \
365394
}
395+
FORWARD_ANY_OWNERSHIP_INST(Tuple)
396+
FORWARD_ANY_OWNERSHIP_INST(Struct)
397+
FORWARD_ANY_OWNERSHIP_INST(Enum)
398+
FORWARD_ANY_OWNERSHIP_INST(OpenExistentialRef)
399+
FORWARD_ANY_OWNERSHIP_INST(Upcast)
400+
FORWARD_ANY_OWNERSHIP_INST(UncheckedRefCast)
401+
FORWARD_ANY_OWNERSHIP_INST(ConvertFunction)
402+
FORWARD_ANY_OWNERSHIP_INST(RefToBridgeObject)
403+
FORWARD_ANY_OWNERSHIP_INST(BridgeObjectToRef)
404+
FORWARD_ANY_OWNERSHIP_INST(UnconditionalCheckedCast)
405+
FORWARD_ANY_OWNERSHIP_INST(Branch)
406+
#undef FORWARD_ANY_OWNERSHIP_INST
407+
408+
// An instruction that forwards a constant ownership or trivial ownership.
409+
#define FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST( \
410+
OWNERSHIP, SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS, INST) \
411+
OwnershipUseCheckerResult \
412+
OwnershipCompatibilityUseChecker::visit##INST##Inst(INST##Inst *I) { \
413+
assert(I->getNumOperands() && "Expected to have non-zero operands"); \
414+
assert(isOwnershipForwardingInst(I) && \
415+
"Expected an ownership forwarding inst"); \
416+
if (ValueOwnershipKind::OWNERSHIP != ValueOwnershipKind::Trivial && \
417+
getOwnershipKind() == ValueOwnershipKind::Trivial) { \
418+
assert(isAddressOrTrivialType() && \
419+
"Trivial ownership requires a trivial type or an address"); \
420+
return {true, false}; \
421+
} \
422+
if (ValueOwnershipKind::OWNERSHIP == ValueOwnershipKind::Trivial) { \
423+
assert(isAddressOrTrivialType() && \
424+
"Trivial ownership requires a trivial type or an address"); \
425+
} \
426+
\
427+
return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP), \
428+
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
429+
}
430+
FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, TupleExtract)
431+
FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, StructExtract)
432+
FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, UncheckedEnumData)
433+
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
434+
435+
// This ANY_should be based off of the argument.
436+
437+
OwnershipUseCheckerResult
438+
OwnershipCompatibilityUseChecker::visitCondBranchInst(CondBranchInst *CBI) {
439+
ValueOwnershipKind Base = getOwnershipKind();
440+
441+
for (SILValue TrueArg : CBI->getTrueArgs()) {
442+
auto MergedValue = Base.merge(TrueArg.getOwnershipKind());
443+
if (!MergedValue.hasValue())
444+
return {false, true};
445+
Base = MergedValue.getValue();
446+
}
366447

367-
FORWARD_OWNERSHIP_INST(Tuple)
368-
FORWARD_OWNERSHIP_INST(Struct)
369-
FORWARD_OWNERSHIP_INST(Enum)
370-
// All of these should really have take falgs and be guaranteed otherwise.
371-
FORWARD_OWNERSHIP_INST(Upcast)
372-
FORWARD_OWNERSHIP_INST(UncheckedRefCast)
373-
FORWARD_OWNERSHIP_INST(ConvertFunction)
374-
FORWARD_OWNERSHIP_INST(RefToBridgeObject)
375-
FORWARD_OWNERSHIP_INST(BridgeObjectToRef)
376-
FORWARD_OWNERSHIP_INST(UnconditionalCheckedCast)
377-
// This should be based off of the argument.
378-
FORWARD_OWNERSHIP_INST(Branch)
379-
FORWARD_OWNERSHIP_INST(CondBranch)
380-
FORWARD_OWNERSHIP_INST(CheckedCastBranch)
381-
#undef FORWARD_OWNERSHIP_INST
448+
for (SILValue FalseArg : CBI->getFalseArgs()) {
449+
auto MergedValue = Base.merge(FalseArg.getOwnershipKind());
450+
if (!MergedValue.hasValue())
451+
return {false, true};
452+
Base = MergedValue.getValue();
453+
}
454+
455+
return {true, !isAddressOrTrivialType()};
456+
}
382457

383458
OwnershipUseCheckerResult
384459
OwnershipCompatibilityUseChecker::visitReturnInst(ReturnInst *RI) {
@@ -678,7 +753,7 @@ bool SILValueOwnershipChecker::doesBlockContainUseAfterFree(
678753
<< "Value: " << *Value
679754
<< "Consuming User: " << *LifetimeEndingUser
680755
<< "Non Consuming User: " << *Iter->second << "Block: bb"
681-
<< UserBlock->getDebugID() << "\n";
756+
<< UserBlock->getDebugID() << "\n\n";
682757
return true;
683758
}
684759

@@ -707,15 +782,55 @@ bool SILValueOwnershipChecker::doesBlockDoubleConsume(
707782
void SILValueOwnershipChecker::gatherUsers(
708783
llvm::SmallVectorImpl<SILInstruction *> &LifetimeEndingUsers,
709784
llvm::SmallVectorImpl<SILInstruction *> &NonLifetimeEndingUsers) {
710-
for (Operand *Op : Value->getUses()) {
785+
786+
// See if Value is guaranteed. If we are guaranteed and not forwarding, then
787+
// we need to look through subobject uses for more uses. Otherwise, if we are
788+
// forwarding, we do not create any lifetime ending users/non lifetime ending
789+
// users since we verify against our base.
790+
bool IsGuaranteed =
791+
Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed;
792+
793+
if (IsGuaranteed && isOwnershipForwardingValue(Value))
794+
return;
795+
796+
// Then gather up our initial list of users.
797+
llvm::SmallVector<Operand *, 8> Users;
798+
std::copy(Value->use_begin(), Value->use_end(), std::back_inserter(Users));
799+
800+
while (!Users.empty()) {
801+
Operand *Op = Users.pop_back_val();
802+
711803
auto *User = Op->getUser();
712-
if (OwnershipCompatibilityUseChecker(Mod, *Op).check(User)) {
804+
if (OwnershipCompatibilityUseChecker(Mod, *Op, Value).check(User)) {
713805
DEBUG(llvm::dbgs() << " Lifetime Ending User: " << *User);
714806
LifetimeEndingUsers.push_back(User);
715807
} else {
716808
DEBUG(llvm::dbgs() << " Regular User: " << *User);
717809
NonLifetimeEndingUsers.push_back(User);
718810
}
811+
812+
// If our base value is not guaranteed or our intermediate value is not an
813+
// ownership forwarding inst, continue. We do not want to visit any
814+
// subobjects recursively.
815+
if (!IsGuaranteed || !isOwnershipForwardingInst(User)) {
816+
continue;
817+
}
818+
819+
// At this point, we know that we must have a forwarded subobject. Since the
820+
// base type is guaranteed, we know that the subobject is either guaranteed
821+
// or trivial. The trivial case is not interesting for ARC verification, so
822+
// if the user has a trivial ownership kind, continue.
823+
if (SILValue(User).getOwnershipKind() == ValueOwnershipKind::Trivial) {
824+
continue;
825+
}
826+
827+
// Now, we /must/ have a guaranteed subobject, so lets assert that the user
828+
// is actually guaranteed and add the subobject's users to our worklist.
829+
assert(SILValue(User).getOwnershipKind() ==
830+
ValueOwnershipKind::Guaranteed &&
831+
"Our value is guaranteed and this is a forwarding instruction. "
832+
"Should have guaranteed ownership as well.");
833+
std::copy(User->use_begin(), User->use_end(), std::back_inserter(Users));
719834
}
720835
}
721836

@@ -784,6 +899,13 @@ bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() {
784899
}
785900
}
786901

902+
// Check if we are a guaranteed subobject. In such a case, we should never
903+
// have lifetime ending uses, since our lifetime is guaranteed by our
904+
// operand, so there is nothing further to do. So just return true.
905+
if (isOwnershipForwardingValue(Value) &&
906+
Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed)
907+
return true;
908+
787909
if (!isValueAddressOrTrivial(Value, Mod)) {
788910
llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n"
789911
<< "Non trivial values, non address values, and non "
@@ -816,6 +938,21 @@ static bool isGuaranteedFunctionArgWithLifetimeEndingUses(
816938
llvm_unreachable("triggering standard assertion failure routine");
817939
}
818940

941+
static bool isSubobjectProjectionWithLifetimeEndingUses(
942+
SILValue Value,
943+
const llvm::SmallVectorImpl<SILInstruction *> &LifetimeEndingUsers) {
944+
llvm::errs() << " Function: '" << Value->getFunction()->getName() << "'\n"
945+
<< " Subobject projection with life ending uses!\n"
946+
<< " Value: " << *Value;
947+
for (auto *U : LifetimeEndingUsers) {
948+
llvm::errs() << " Lifetime Ending User: " << *U;
949+
}
950+
llvm::errs() << '\n';
951+
if (IsSILOwnershipVerifierTestingEnabled)
952+
return false;
953+
llvm_unreachable("triggering standard assertion failure routine");
954+
}
955+
819956
bool SILValueOwnershipChecker::checkUses() {
820957
DEBUG(llvm::dbgs() << " Gathering and classifying uses!\n");
821958

@@ -857,6 +994,17 @@ bool SILValueOwnershipChecker::checkUses() {
857994
}
858995
}
859996

997+
// Check if we are an instruction that forwards ownership that forwards
998+
// guaranteed ownership. In such a case, we are a subobject projection. We
999+
// should not have any lifetime ending uses.
1000+
if (isOwnershipForwardingValue(Value) &&
1001+
Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed) {
1002+
if (!isSubobjectProjectionWithLifetimeEndingUses(Value,
1003+
LifetimeEndingUsers)) {
1004+
return false;
1005+
}
1006+
}
1007+
8601008
// Then add our non lifetime ending users and their blocks to the
8611009
// BlocksWithNonLifetimeEndingUses map. While we do this, if we have multiple
8621010
// uses in the same block, we only accept the last use since from a liveness
@@ -1015,8 +1163,9 @@ void SILValueOwnershipChecker::checkDataflow() {
10151163
<< "Value: " << *Value << " Remaining Users:\n";
10161164
for (auto &Pair : BlocksWithNonLifetimeEndingUses) {
10171165
llvm::errs() << "User:" << *Pair.second << "Block: bb"
1018-
<< Pair.first->getDebugID() << "\n\n";
1166+
<< Pair.first->getDebugID() << "\n";
10191167
}
1168+
llvm::errs() << "\n";
10201169
if (IsSILOwnershipVerifierTestingEnabled)
10211170
return;
10221171
llvm_unreachable("triggering standard assertion failure routine");
@@ -1040,7 +1189,7 @@ void SILInstruction::verifyOperandOwnership() const {
10401189
return;
10411190
auto *Self = const_cast<SILInstruction *>(this);
10421191
for (const Operand &Op : getAllOperands()) {
1043-
OwnershipCompatibilityUseChecker(getModule(), Op).check(Self);
1192+
OwnershipCompatibilityUseChecker(getModule(), Op, Op.get()).check(Self);
10441193
}
10451194
#endif
10461195
}

0 commit comments

Comments
 (0)