Skip to content

Commit b454c78

Browse files
authored
Merge pull request #7443 from gottesmm/ownership_unreachable_code
2 parents c5b74af + ed67bf9 commit b454c78

File tree

8 files changed

+306
-30
lines changed

8 files changed

+306
-30
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
7676
/// This method unlinks 'self' from the containing SILFunction and deletes it.
7777
void eraseFromParent();
7878

79-
/// Returns true if this BB is the entry BB of its parent.
80-
bool isEntry() const;
81-
8279
//===--------------------------------------------------------------------===//
8380
// SILInstruction List Inspection and Manipulation
8481
//===--------------------------------------------------------------------===//
@@ -314,6 +311,17 @@ public llvm::ilist_node<SILBasicBlock>, public SILAllocated<SILBasicBlock> {
314311
return const_cast<SILBasicBlock *>(this)->getSinglePredecessorBlock();
315312
}
316313

314+
//===--------------------------------------------------------------------===//
315+
// Utility
316+
//===--------------------------------------------------------------------===//
317+
318+
/// Returns true if this BB is the entry BB of its parent.
319+
bool isEntry() const;
320+
321+
/// Returns true if this block ends in an unreachable or an apply of a
322+
/// no-return apply or builtin.
323+
bool isNoReturn() const;
324+
317325
//===--------------------------------------------------------------------===//
318326
// Debugging
319327
//===--------------------------------------------------------------------===//

include/swift/SIL/SILInstruction.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4889,7 +4889,7 @@ class CondBranchInst : public TermInst {
48894889

48904890
auto Operands = getTrueOperands();
48914891
return Operands.front().getOperandNumber() <= OpIndex &&
4892-
Operands.back().getOperandNumber() <= OpIndex;
4892+
OpIndex <= Operands.back().getOperandNumber();
48934893
}
48944894

48954895
/// Is \p OpIndex an operand associated with the false case?
@@ -4901,7 +4901,7 @@ class CondBranchInst : public TermInst {
49014901

49024902
auto Operands = getFalseOperands();
49034903
return Operands.front().getOperandNumber() <= OpIndex &&
4904-
Operands.back().getOperandNumber() <= OpIndex;
4904+
OpIndex <= Operands.back().getOperandNumber();
49054905
}
49064906

49074907
/// Returns the argument on the cond_br terminator that will be passed to

include/swift/SIL/SILValue.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@
2828
namespace swift {
2929

3030
class DominanceInfo;
31+
class PostOrderFunctionInfo;
32+
class ReversePostOrderInfo;
3133
class Operand;
3234
class SILBasicBlock;
3335
class SILFunction;
3436
class SILInstruction;
3537
class SILLocation;
3638
class SILModule;
39+
class TransitivelyUnreachableBlocksInfo;
3740
class ValueBaseUseIterator;
3841
class ValueUseIterator;
3942

@@ -278,7 +281,8 @@ class SILValue {
278281
ValueOwnershipKind getOwnershipKind() const;
279282

280283
/// Verify that this SILValue and its uses respects ownership invariants.
281-
void verifyOwnership(SILModule &Mod) const;
284+
void verifyOwnership(SILModule &Mod,
285+
TransitivelyUnreachableBlocksInfo *TUB = nullptr) const;
282286
};
283287

284288
/// A formal SIL reference to a value, suitable for use as a stored

lib/SIL/SILBasicBlock.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,22 @@ SILBasicBlock::getFunctionArguments() const {
305305
};
306306
return makeTransformArrayRef(getArguments(), F);
307307
}
308+
309+
/// Returns true if this block ends in an unreachable or an apply of a
310+
/// no-return apply or builtin.
311+
bool SILBasicBlock::isNoReturn() const {
312+
if (isa<UnreachableInst>(getTerminator()))
313+
return true;
314+
315+
auto Iter = prev_or_begin(getTerminator()->getIterator(), begin());
316+
FullApplySite FAS = FullApplySite::isa(const_cast<SILInstruction *>(&*Iter));
317+
if (FAS && FAS.isCalleeNoReturn()) {
318+
return true;
319+
}
320+
321+
if (auto *BI = dyn_cast<BuiltinInst>(&*Iter)) {
322+
return BI->getModule().isNoReturnBuiltinOrIntrinsic(BI->getName());
323+
}
324+
325+
return false;
326+
}

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#define DEBUG_TYPE "sil-ownership-verifier"
1414

15+
#include "TransitivelyUnreachableBlocks.h"
1516
#include "swift/AST/ASTContext.h"
1617
#include "swift/AST/AnyFunctionRef.h"
1718
#include "swift/AST/Decl.h"
@@ -38,6 +39,7 @@
3839
#include "llvm/ADT/StringSet.h"
3940
#include "llvm/Support/CommandLine.h"
4041
#include "llvm/Support/Debug.h"
42+
#include <algorithm>
4143

4244
using namespace swift;
4345

@@ -1043,6 +1045,10 @@ class SILValueOwnershipChecker {
10431045
/// The module that we are in.
10441046
SILModule &Mod;
10451047

1048+
/// A cache of unreachable function blocks that we use to determine if we can
1049+
/// ignore "leaks".
1050+
const TransitivelyUnreachableBlocksInfo &TUB;
1051+
10461052
/// The value whose ownership we will check.
10471053
SILValue Value;
10481054

@@ -1065,7 +1071,10 @@ class SILValueOwnershipChecker {
10651071
llvm::SmallPtrSet<SILBasicBlock *, 8> SuccessorBlocksThatMustBeVisited;
10661072

10671073
public:
1068-
SILValueOwnershipChecker(SILModule &M, SILValue V) : Mod(M), Value(V) {}
1074+
SILValueOwnershipChecker(SILModule &M,
1075+
const TransitivelyUnreachableBlocksInfo &TUB,
1076+
SILValue V)
1077+
: Mod(M), TUB(TUB), Value(V) {}
10691078

10701079
~SILValueOwnershipChecker() = default;
10711080
SILValueOwnershipChecker(SILValueOwnershipChecker &) = delete;
@@ -1084,6 +1093,7 @@ class SILValueOwnershipChecker {
10841093
private:
10851094
bool checkUses();
10861095
void checkDataflow();
1096+
void checkDataflowEndConditions();
10871097
void
10881098
gatherUsers(llvm::SmallVectorImpl<SILInstruction *> &LifetimeEndingUsers,
10891099
llvm::SmallVectorImpl<SILInstruction *> &NonLifetimeEndingUsers);
@@ -1106,6 +1116,8 @@ class SILValueOwnershipChecker {
11061116
SILBasicBlock *UserBlock);
11071117

11081118
bool checkValueWithoutLifetimeEndingUses();
1119+
1120+
bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *Arg);
11091121
};
11101122

11111123
} // end anonymous namespace
@@ -1248,8 +1260,8 @@ void SILValueOwnershipChecker::uniqueNonLifetimeEndingUsers(
12481260
}
12491261
}
12501262

1251-
static bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *Arg,
1252-
SILModule &Mod) {
1263+
bool SILValueOwnershipChecker::checkFunctionArgWithoutLifetimeEndingUses(
1264+
SILFunctionArgument *Arg) {
12531265
switch (Arg->getOwnershipKind()) {
12541266
case ValueOwnershipKind::Guaranteed:
12551267
case ValueOwnershipKind::Unowned:
@@ -1262,6 +1274,9 @@ static bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *Arg,
12621274
break;
12631275
}
12641276

1277+
if (TUB.isUnreachable(Arg->getParent()))
1278+
return true;
1279+
12651280
llvm::errs() << "Function: '" << Arg->getFunction()->getName() << "'\n"
12661281
<< " Owned function parameter without life "
12671282
"ending uses!\n"
@@ -1274,7 +1289,7 @@ static bool checkFunctionArgWithoutLifetimeEndingUses(SILFunctionArgument *Arg,
12741289
bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() {
12751290
DEBUG(llvm::dbgs() << " No lifetime ending users?! Bailing early.\n");
12761291
if (auto *Arg = dyn_cast<SILFunctionArgument>(Value)) {
1277-
if (checkFunctionArgWithoutLifetimeEndingUses(Arg, Mod)) {
1292+
if (checkFunctionArgWithoutLifetimeEndingUses(Arg)) {
12781293
return true;
12791294
}
12801295
}
@@ -1290,6 +1305,17 @@ bool SILValueOwnershipChecker::checkValueWithoutLifetimeEndingUses() {
12901305
if (Value.getOwnershipKind() == ValueOwnershipKind::Unowned)
12911306
return true;
12921307

1308+
if (auto *ParentBlock = Value->getParentBlock()) {
1309+
if (TUB.isUnreachable(ParentBlock)) {
1310+
DEBUG(llvm::dbgs() << " Ignoring transitively unreachable value "
1311+
<< "without users!\n"
1312+
<< " Function: '" << Value->getFunction()->getName()
1313+
<< "'\n"
1314+
<< " Value: " << *Value << '\n');
1315+
return true;
1316+
}
1317+
}
1318+
12931319
if (!isValueAddressOrTrivial(Value, Mod)) {
12941320
llvm::errs() << "Function: '" << Value->getFunction()->getName() << "'\n"
12951321
<< "Non trivial values, non address values, and non "
@@ -1425,6 +1451,12 @@ bool SILValueOwnershipChecker::checkUses() {
14251451
llvm_unreachable("triggering standard assertion failure routine");
14261452
}
14271453

1454+
// If this user is in the same block as the value, do not visit
1455+
// predecessors. We must be extra tolerant here since we allow for
1456+
// unreachable code.
1457+
if (UserBlock == Value->getParentBlock())
1458+
continue;
1459+
14281460
// Then for each predecessor of this block...
14291461
for (auto *Pred : UserBlock->getPredecessorBlocks()) {
14301462
// If this block is not a block that we have already put on the list, add
@@ -1491,15 +1523,23 @@ void SILValueOwnershipChecker::checkDataflow() {
14911523
// users.
14921524
BlocksWithNonLifetimeEndingUses.erase(BB);
14931525

1494-
// Ok, now we know that we do not have an overconsume. So now we need to
1495-
// update our state for our successors to make sure by the end of the
1496-
// traversal we visit them.
1526+
// Ok, now we know that we do not have an overconsume. If this block does
1527+
// not end in a no return function, we need to update our state for our
1528+
// successors to make sure by the end of the traversal we visit them.
1529+
//
1530+
// We must consider such no-return blocks since we may be running during
1531+
// SILGen before NoReturn folding has run.
14971532
for (SILBasicBlock *SuccBlock : BB->getSuccessorBlocks()) {
14981533
// If we already visited the successor, there is nothing to do since we
14991534
// already visited the successor.
15001535
if (VisitedBlocks.count(SuccBlock))
15011536
continue;
15021537

1538+
// Then check if the successor is a transitively unreachable block. In
1539+
// such a case, we ignore it since we are going to leak along that path.
1540+
if (TUB.isUnreachable(SuccBlock))
1541+
continue;
1542+
15031543
// Otherwise, add the successor to our SuccessorBlocksThatMustBeVisited
15041544
// set to ensure that we assert if we do not visit it by the end of the
15051545
// algorithm.
@@ -1530,6 +1570,7 @@ void SILValueOwnershipChecker::checkDataflow() {
15301570
if (VisitedBlocks.count(PredBlock)) {
15311571
continue;
15321572
}
1573+
15331574
VisitedBlocks.insert(PredBlock);
15341575
Worklist.push_back(PredBlock);
15351576
}
@@ -1601,8 +1642,15 @@ void SILInstruction::verifyOperandOwnership() const {
16011642
#endif
16021643
}
16031644

1604-
void SILValue::verifyOwnership(SILModule &Mod) const {
1645+
void SILValue::verifyOwnership(SILModule &Mod,
1646+
TransitivelyUnreachableBlocksInfo *TUB) const {
16051647
#ifndef NDEBUG
1606-
SILValueOwnershipChecker(Mod, *this).check();
1648+
if (TUB) {
1649+
SILValueOwnershipChecker(Mod, *TUB, *this).check();
1650+
} else {
1651+
PostOrderFunctionInfo NewPOFI((*this)->getFunction());
1652+
TransitivelyUnreachableBlocksInfo TUB(NewPOFI);
1653+
SILValueOwnershipChecker(Mod, TUB, *this).check();
1654+
}
16071655
#endif
16081656
}

lib/SIL/SILVerifier.cpp

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,33 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
#define DEBUG_TYPE "silverifier"
14-
#include "swift/SIL/SILDebugScope.h"
15-
#include "swift/SIL/SILFunction.h"
16-
#include "swift/SIL/SILModule.h"
17-
#include "swift/SIL/SILOpenedArchetypesTracker.h"
18-
#include "swift/SIL/SILVisitor.h"
19-
#include "swift/SIL/SILVTable.h"
20-
#include "swift/SIL/Dominance.h"
21-
#include "swift/SIL/DynamicCasts.h"
22-
#include "swift/AST/AnyFunctionRef.h"
13+
#define DEBUG_TYPE "sil-verifier"
14+
#include "TransitivelyUnreachableBlocks.h"
2315
#include "swift/AST/ASTContext.h"
16+
#include "swift/AST/AnyFunctionRef.h"
2417
#include "swift/AST/Decl.h"
2518
#include "swift/AST/GenericEnvironment.h"
2619
#include "swift/AST/Module.h"
2720
#include "swift/AST/ProtocolConformance.h"
2821
#include "swift/AST/Types.h"
22+
#include "swift/Basic/Range.h"
23+
#include "swift/ClangImporter/ClangModule.h"
24+
#include "swift/SIL/Dominance.h"
25+
#include "swift/SIL/DynamicCasts.h"
26+
#include "swift/SIL/PostOrder.h"
2927
#include "swift/SIL/PrettyStackTrace.h"
28+
#include "swift/SIL/SILDebugScope.h"
29+
#include "swift/SIL/SILFunction.h"
30+
#include "swift/SIL/SILModule.h"
31+
#include "swift/SIL/SILOpenedArchetypesTracker.h"
32+
#include "swift/SIL/SILVTable.h"
33+
#include "swift/SIL/SILVisitor.h"
3034
#include "swift/SIL/TypeLowering.h"
31-
#include "swift/ClangImporter/ClangModule.h"
32-
#include "swift/Basic/Range.h"
33-
#include "llvm/Support/Debug.h"
3435
#include "llvm/ADT/DenseSet.h"
3536
#include "llvm/ADT/PostOrderIterator.h"
3637
#include "llvm/ADT/StringSet.h"
3738
#include "llvm/Support/CommandLine.h"
39+
#include "llvm/Support/Debug.h"
3840
using namespace swift;
3941

4042
using Lowering::AbstractionPattern;
@@ -108,6 +110,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
108110
SILOpenedArchetypesTracker OpenedArchetypes;
109111
const SILInstruction *CurInstruction = nullptr;
110112
DominanceInfo *Dominance = nullptr;
113+
llvm::Optional<PostOrderFunctionInfo> PostOrderInfo;
114+
llvm::Optional<TransitivelyUnreachableBlocksInfo> UnreachableBlockInfo;
111115
bool SingleFunction = true;
112116

113117
SILVerifier(const SILVerifier&) = delete;
@@ -413,6 +417,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
413417
: M(F.getModule().getSwiftModule()), F(F),
414418
fnConv(F.getLoweredFunctionType(), F.getModule()),
415419
TC(F.getModule().Types), OpenedArchetypes(F), Dominance(nullptr),
420+
PostOrderInfo(), UnreachableBlockInfo(),
416421
SingleFunction(SingleFunction) {
417422
if (F.isExternalDeclaration())
418423
return;
@@ -425,7 +430,11 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
425430
"Basic blocks must end with a terminator instruction");
426431
}
427432

428-
Dominance = new DominanceInfo(const_cast<SILFunction*>(&F));
433+
Dominance = new DominanceInfo(const_cast<SILFunction *>(&F));
434+
if (isSILOwnershipEnabled()) {
435+
PostOrderInfo.emplace(const_cast<SILFunction *>(&F));
436+
UnreachableBlockInfo.emplace(PostOrderInfo.getValue());
437+
}
429438

430439
auto *DebugScope = F.getDebugScope();
431440
require(DebugScope, "All SIL functions must have a debug scope");
@@ -476,7 +485,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
476485
// ownership.
477486
if (!F->hasQualifiedOwnership())
478487
return;
479-
SILValue(V).verifyOwnership(F->getModule());
488+
SILValue(V).verifyOwnership(F->getModule(),
489+
&UnreachableBlockInfo.getValue());
480490
}
481491

482492
void checkSILInstruction(SILInstruction *I) {

0 commit comments

Comments
 (0)