Skip to content

Commit 3ccc69a

Browse files
authored
Merge pull request #10759 from gottesmm/guaranteed_ownership_transforming_terminators
2 parents a5edbf3 + 69393d5 commit 3ccc69a

File tree

4 files changed

+504
-22
lines changed

4 files changed

+504
-22
lines changed

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 137 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ static bool compatibleOwnershipKinds(ValueOwnershipKind K1,
174174
return K1.merge(K2).hasValue();
175175
}
176176

177+
/// Returns true if \p Kind is trivial or if \p Kind is compatible with \p
178+
/// ComparisonKind.
179+
static bool
180+
trivialOrCompatibleOwnershipKinds(ValueOwnershipKind Kind,
181+
ValueOwnershipKind ComparisonKind) {
182+
return compatibleOwnershipKinds(Kind, ValueOwnershipKind::Trivial) ||
183+
compatibleOwnershipKinds(Kind, ComparisonKind);
184+
}
185+
177186
static bool isValueAddressOrTrivial(SILValue V, SILModule &M) {
178187
return V->getType().isAddress() ||
179188
V.getOwnershipKind() == ValueOwnershipKind::Trivial ||
@@ -198,6 +207,8 @@ static bool isOwnershipForwardingValueKind(ValueKind K) {
198207
case ValueKind::UncheckedEnumDataInst:
199208
case ValueKind::MarkUninitializedInst:
200209
case ValueKind::SelectEnumInst:
210+
case ValueKind::SwitchEnumInst:
211+
case ValueKind::CheckedCastBranchInst:
201212
return true;
202213
default:
203214
return false;
@@ -327,6 +338,12 @@ class OwnershipCompatibilityUseChecker
327338
OwnershipUseCheckerResult visitForwardingInst(SILInstruction *I) {
328339
return visitForwardingInst(I, I->getAllOperands());
329340
}
341+
342+
/// Visit a terminator instance that performs a transform like
343+
/// operation. E.x.: switch_enum, checked_cast_br. This does not include br or
344+
/// cond_br.
345+
OwnershipUseCheckerResult visitTransformingTerminatorInst(TermInst *TI);
346+
330347
OwnershipUseCheckerResult
331348
visitApplyArgument(ValueOwnershipKind RequiredConvention, bool ShouldCheck);
332349
OwnershipUseCheckerResult
@@ -420,7 +437,6 @@ NO_OPERAND_INST(KeyPath)
420437
return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP), \
421438
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
422439
}
423-
CONSTANT_OWNERSHIP_INST(Guaranteed, true, EndBorrowArgument)
424440
CONSTANT_OWNERSHIP_INST(Guaranteed, false, RefElementAddr)
425441
CONSTANT_OWNERSHIP_INST(Owned, true, AutoreleaseValue)
426442
CONSTANT_OWNERSHIP_INST(Owned, true, DeallocBox)
@@ -511,9 +527,7 @@ CONSTANT_OWNERSHIP_INST(Trivial, false, DeallocValueBuffer)
511527
return {compatibleWithOwnership(ValueOwnershipKind::OWNERSHIP), \
512528
SHOULD_CHECK_FOR_DATAFLOW_VIOLATIONS}; \
513529
}
514-
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, CheckedCastBranch)
515530
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, CheckedCastValueBranch)
516-
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, SwitchEnum)
517531
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, InitExistentialOpaque)
518532
CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Owned, true, DeinitExistentialOpaque)
519533
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
@@ -655,6 +669,17 @@ FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, TupleExtract)
655669
FORWARD_CONSTANT_OR_TRIVIAL_OWNERSHIP_INST(Guaranteed, false, StructExtract)
656670
#undef CONSTANT_OR_TRIVIAL_OWNERSHIP_INST
657671

672+
OwnershipUseCheckerResult
673+
OwnershipCompatibilityUseChecker::visitEndBorrowArgumentInst(EndBorrowArgumentInst *I) {
674+
// If we are currently checking an end_borrow_argument as a subobject, then we
675+
// treat this as just a use.
676+
if (isCheckingSubObject())
677+
return {true, false};
678+
679+
// Otherwise, we must be checking an actual argument. Make sure it is guaranteed!
680+
return {true, compatibleWithOwnership(ValueOwnershipKind::Guaranteed)};
681+
}
682+
658683
OwnershipUseCheckerResult
659684
OwnershipCompatibilityUseChecker::visitSelectEnumInst(SelectEnumInst *I) {
660685
if (getValue() == I->getEnumOperand()) {
@@ -724,6 +749,59 @@ OwnershipCompatibilityUseChecker::visitCondBranchInst(CondBranchInst *CBI) {
724749
getOperandIndex() - FalseOffset);
725750
}
726751

752+
OwnershipUseCheckerResult
753+
OwnershipCompatibilityUseChecker::visitSwitchEnumInst(SwitchEnumInst *SEI) {
754+
return visitTransformingTerminatorInst(SEI);
755+
}
756+
757+
OwnershipUseCheckerResult
758+
OwnershipCompatibilityUseChecker::visitCheckedCastBranchInst(
759+
CheckedCastBranchInst *SEI) {
760+
return visitTransformingTerminatorInst(SEI);
761+
}
762+
763+
OwnershipUseCheckerResult
764+
OwnershipCompatibilityUseChecker::visitTransformingTerminatorInst(
765+
TermInst *TI) {
766+
// If our operand was trivial, return early.
767+
if (compatibleWithOwnership(ValueOwnershipKind::Trivial))
768+
return {true, false};
769+
770+
// Then we need to go through all of our destinations and make sure that if
771+
// they have a payload, the payload's convention matches our
772+
// convention.
773+
//
774+
// *NOTE* we assume that all of our types line up and are checked by the
775+
// normal verifier.
776+
for (auto *Succ : TI->getParent()->getSuccessorBlocks()) {
777+
// This must be a no-payload case... continue.
778+
if (Succ->args_size() == 0)
779+
continue;
780+
781+
// If we have a trivial value or a value with ownership kind that matches
782+
// the switch_enum, then continue.
783+
auto OwnershipKind = Succ->getArgument(0)->getOwnershipKind();
784+
if (OwnershipKind == ValueOwnershipKind::Trivial ||
785+
compatibleWithOwnership(OwnershipKind))
786+
continue;
787+
788+
// Otherwise, emit an error.
789+
handleError([&]() {
790+
llvm::errs()
791+
<< "Function: '" << Succ->getParent()->getName() << "'\n"
792+
<< "Error! Argument ownership kind does not match terminator!\n"
793+
<< "Terminator: " << *TI << "Argument: " << *Succ->getArgument(0)
794+
<< "Expected convention: " << getOwnershipKind() << ".\n"
795+
<< "Actual convention: " << OwnershipKind << '\n'
796+
<< '\n';
797+
});
798+
}
799+
800+
// Finally, if everything lines up, emit that we match and are a lifetime
801+
// ending point if we are owned.
802+
return {true, compatibleWithOwnership(ValueOwnershipKind::Owned)};
803+
}
804+
727805
OwnershipUseCheckerResult
728806
OwnershipCompatibilityUseChecker::visitReturnInst(ReturnInst *RI) {
729807
SILModule &M = RI->getModule();
@@ -1454,8 +1532,8 @@ void SILValueOwnershipChecker::gatherUsers(
14541532
// we need to look through subobject uses for more uses. Otherwise, if we are
14551533
// forwarding, we do not create any lifetime ending users/non lifetime ending
14561534
// users since we verify against our base.
1457-
bool IsGuaranteed =
1458-
Value.getOwnershipKind() == ValueOwnershipKind::Guaranteed;
1535+
auto OwnershipKind = Value.getOwnershipKind();
1536+
bool IsGuaranteed = OwnershipKind == ValueOwnershipKind::Guaranteed;
14591537

14601538
if (IsGuaranteed && isOwnershipForwardingValue(Value))
14611539
return;
@@ -1512,19 +1590,63 @@ void SILValueOwnershipChecker::gatherUsers(
15121590

15131591
// At this point, we know that we must have a forwarded subobject. Since the
15141592
// base type is guaranteed, we know that the subobject is either guaranteed
1515-
// or trivial. The trivial case is not interesting for ARC verification, so
1516-
// if the user has a trivial ownership kind, continue.
1517-
if (SILValue(User).getOwnershipKind() == ValueOwnershipKind::Trivial) {
1593+
// or trivial. We now split into two cases, if the user is a terminator or
1594+
// not. If we do not have a terminator, then just add User->getUses() to the
1595+
// worklist.
1596+
auto *TI = dyn_cast<TermInst>(User);
1597+
if (!TI) {
1598+
if (SILValue(User).getOwnershipKind() == ValueOwnershipKind::Trivial) {
1599+
continue;
1600+
}
1601+
1602+
// Now, we /must/ have a guaranteed subobject, so lets assert that the
1603+
// user
1604+
// is actually guaranteed and add the subobject's users to our worklist.
1605+
assert(SILValue(User).getOwnershipKind() ==
1606+
ValueOwnershipKind::Guaranteed &&
1607+
"Our value is guaranteed and this is a forwarding instruction. "
1608+
"Should have guaranteed ownership as well.");
1609+
std::copy(User->use_begin(), User->use_end(), std::back_inserter(Users));
15181610
continue;
15191611
}
15201612

1521-
// Now, we /must/ have a guaranteed subobject, so lets assert that the user
1522-
// is actually guaranteed and add the subobject's users to our worklist.
1523-
assert(SILValue(User).getOwnershipKind() ==
1524-
ValueOwnershipKind::Guaranteed &&
1525-
"Our value is guaranteed and this is a forwarding instruction. "
1526-
"Should have guaranteed ownership as well.");
1527-
std::copy(User->use_begin(), User->use_end(), std::back_inserter(Users));
1613+
// Otherwise if we have a terminator, add any as uses any
1614+
// end_borrow_argument to ensure that the subscope is completely enclsed
1615+
// within the super scope. all of the arguments to the work list. We require
1616+
// all of our arguments to be either trivial or guaranteed.
1617+
for (auto &Succ : TI->getSuccessors()) {
1618+
auto *BB = Succ.getBB();
1619+
1620+
// If we do not have any arguments, then continue.
1621+
if (BB->args_empty())
1622+
continue;
1623+
1624+
// Otherwise, make sure that all arguments are trivial or guaranteed. If
1625+
// we fail, emit an error.
1626+
//
1627+
// TODO: We could ignore this error and emit a more specific error on the
1628+
// actual terminator.
1629+
for (auto *BBArg : BB->getArguments()) {
1630+
// *NOTE* We do not emit an error here since we want to allow for more
1631+
// specific errors to be found during use_verification.
1632+
//
1633+
// TODO: Add a flag that associates the terminator instruction with
1634+
// needing to be verified. If it isn't verified appropriately, assert
1635+
// when the verifier is destroyed.
1636+
if (!trivialOrCompatibleOwnershipKinds(BBArg->getOwnershipKind(),
1637+
OwnershipKind)) {
1638+
// This is where the error would go.
1639+
continue;
1640+
}
1641+
1642+
// If we have a trivial value, just continue.
1643+
if (BBArg->getOwnershipKind() == ValueOwnershipKind::Trivial)
1644+
continue;
1645+
1646+
// Otherwise,
1647+
std::copy(BBArg->use_begin(), BBArg->use_end(), std::back_inserter(Users));
1648+
}
1649+
}
15281650
}
15291651
}
15301652

lib/SILGen/SILGenPattern.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
using namespace swift;
3636
using namespace Lowering;
3737

38+
//===----------------------------------------------------------------------===//
39+
// Pattern Utilities
40+
//===----------------------------------------------------------------------===//
41+
42+
// TODO: These routines should probably be refactored into their own file since
43+
// they have nothing to do with the implementation of SILGenPattern
44+
// specifically.
45+
3846
/// Shallow-dump a pattern node one level deep for debug purposes.
3947
static void dumpPattern(const Pattern *p, llvm::raw_ostream &os) {
4048
if (!p) {
@@ -290,6 +298,10 @@ static Pattern *getSimilarSpecializingPattern(Pattern *p, Pattern *first) {
290298
llvm_unreachable("Unhandled PatternKind in switch.");
291299
}
292300

301+
//===----------------------------------------------------------------------===//
302+
// SILGenPattern Emission
303+
//===----------------------------------------------------------------------===//
304+
293305
namespace {
294306

295307
/// A row which we intend to specialize.
@@ -467,6 +479,10 @@ class PatternMatchEmission {
467479

468480
/// A handle to a row in a clause matrix. Does not own memory; use of the
469481
/// ClauseRow must be dominated by its originating ClauseMatrix.
482+
///
483+
/// TODO: This should be refactored into a more general formulation that uses a
484+
/// child template pattern to inject our logic. This will then allow us to
485+
/// inject "mock" objects in a unittest file.
470486
class ClauseRow {
471487
friend class ClauseMatrix;
472488

@@ -872,7 +888,7 @@ class ArgUnforwarder {
872888
assert(operand.getFinalConsumption() !=
873889
CastConsumptionKind::TakeOnSuccess &&
874890
"When compiling with sil ownership take on success is disabled");
875-
// No unforwarding is needed, we always copy.
891+
// No unforwarding is needed, we always borrow/copy.
876892
return false;
877893
}
878894

@@ -1378,12 +1394,14 @@ static ConsumableManagedValue
13781394
getManagedSubobject(SILGenFunction &SGF, SILValue value,
13791395
const TypeLowering &valueTL,
13801396
CastConsumptionKind consumption) {
1381-
if (consumption != CastConsumptionKind::CopyOnSuccess) {
1382-
return {SGF.emitManagedRValueWithCleanup(value, valueTL),
1383-
consumption};
1384-
} else {
1397+
if (consumption == CastConsumptionKind::CopyOnSuccess) {
13851398
return {ManagedValue::forUnmanaged(value), consumption};
13861399
}
1400+
1401+
assert((!SGF.F.getModule().getOptions().EnableSILOwnership ||
1402+
consumption != CastConsumptionKind::TakeOnSuccess) &&
1403+
"TakeOnSuccess should never be used when sil ownership is enabled");
1404+
return {SGF.emitManagedRValueWithCleanup(value, valueTL), consumption};
13871405
}
13881406

13891407
static ConsumableManagedValue

0 commit comments

Comments
 (0)