Skip to content

Commit ddc0219

Browse files
authored
Merge pull request #65592 from eeckstein/fix-sil-verifier
MemoryLifetimeVerifier: be more precise with indirect function arguments.
2 parents 1df3912 + 4b33b99 commit ddc0219

File tree

18 files changed

+148
-63
lines changed

18 files changed

+148
-63
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ public struct CalleeAnalysis {
2323
return inst.instruction.isDeinitBarrier(bca.analysis)
2424
},
2525
// getMemBehaviorFn
26-
{ (bridgedCtxt: BridgedPassContext, bridgedApply: BridgedInstruction, observeRetains: Bool) -> swift.MemoryBehavior in
27-
let context = FunctionPassContext(_bridged: bridgedCtxt)
26+
{ (bridgedApply: BridgedInstruction, observeRetains: Bool, bca: BridgedCalleeAnalysis) -> swift.MemoryBehavior in
2827
let apply = bridgedApply.instruction as! ApplySite
29-
let e = context.calleeAnalysis.getSideEffects(of: apply)
28+
let e = bca.analysis.getSideEffects(of: apply)
3029
return e.getMemBehavior(observeRetains: observeRetains)
3130
}
3231
)

include/swift/SIL/SILFunction.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class SILFunctionBuilder;
3838
class SILProfiler;
3939
class BasicBlockBitfield;
4040
class NodeBitfield;
41+
class SILPassManager;
4142

4243
namespace Lowering {
4344
class TypeLowering;
@@ -1410,18 +1411,19 @@ class SILFunction
14101411

14111412
/// verify - Run the SIL verifier to make sure that the SILFunction follows
14121413
/// invariants.
1413-
void verify(bool SingleFunction = true,
1414+
void verify(SILPassManager *passManager = nullptr,
1415+
bool SingleFunction = true,
14141416
bool isCompleteOSSA = true,
14151417
bool checkLinearLifetime = true) const;
14161418

14171419
/// Run the SIL verifier without assuming OSSA lifetimes end at dead end
14181420
/// blocks.
14191421
void verifyIncompleteOSSA() const {
1420-
verify(/*SingleFunction=*/true, /*completeOSSALifetimes=*/false);
1422+
verify(/*passManager*/nullptr, /*SingleFunction=*/true, /*completeOSSALifetimes=*/false);
14211423
}
14221424

14231425
/// Verifies the lifetime of memory locations in the function.
1424-
void verifyMemoryLifetime();
1426+
void verifyMemoryLifetime(SILPassManager *passManager);
14251427

14261428
/// Run the SIL ownership verifier to check that all values with ownership
14271429
/// have a linear lifetime. Regular OSSA invariants are checked separately in

include/swift/SIL/SILModule.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,9 +902,13 @@ class SILModule {
902902
/// fetched in the given module?
903903
bool isTypeMetadataForLayoutAccessible(SILType type);
904904

905+
void verify(bool isCompleteOSSA = true,
906+
bool checkLinearLifetime = true) const;
907+
905908
/// Run the SIL verifier to make sure that all Functions follow
906909
/// invariants.
907-
void verify(bool isCompleteOSSA = true,
910+
void verify(SILPassManager *passManager,
911+
bool isCompleteOSSA = true,
908912
bool checkLinearLifetime = true) const;
909913

910914
/// Run the SIL verifier without assuming OSSA lifetimes end at dead end

include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ class CalleeCache {
183183

184184
class BasicCalleeAnalysis : public SILAnalysis {
185185
SILModule &M;
186-
SILPassManager *pm = nullptr;
187186
std::unique_ptr<CalleeCache> Cache;
188187

189188
public:
@@ -197,8 +196,6 @@ class BasicCalleeAnalysis : public SILAnalysis {
197196
return S->getKind() == SILAnalysisKind::BasicCallee;
198197
}
199198

200-
virtual void initialize(SILPassManager *pm) override { this->pm = pm; }
201-
202199
/// Invalidate all information in this analysis.
203200
virtual void invalidate() override {
204201
Cache.reset();

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ struct BridgedCalleeAnalysis {
6262

6363
typedef bool (* _Nonnull IsDeinitBarrierFn)(BridgedInstruction, BridgedCalleeAnalysis bca);
6464
typedef swift::MemoryBehavior (* _Nonnull GetMemBehvaiorFn)(
65-
BridgedPassContext context, BridgedInstruction apply, bool observeRetains);
65+
BridgedInstruction apply, bool observeRetains, BridgedCalleeAnalysis bca);
6666

6767
static void registerAnalysis(IsDeinitBarrierFn isDeinitBarrierFn,
6868
GetMemBehvaiorFn getEffectsFn);

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,11 @@ class SILPassManager {
221221

222222
std::chrono::nanoseconds totalPassRuntime = std::chrono::nanoseconds(0);
223223

224+
public:
224225
/// C'tor. It creates and registers all analysis passes, which are defined
225-
/// in Analysis.def. This is private as it should only be used by
226-
/// ExecuteSILPipelineRequest.
226+
/// in Analysis.def.
227227
SILPassManager(SILModule *M, bool isMandatory, irgen::IRGenModule *IRMod);
228228

229-
public:
230229
const SILOptions &getOptions() const;
231230

232231
/// Searches for an analysis of type T in the list of registered

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ ParseSILModuleRequest::evaluate(Evaluator &evaluator,
117117
"Failed to parse SIL but did not emit any errors!");
118118
return SILModule::createEmptyModule(desc.context, desc.conv, desc.opts);
119119
}
120+
// If SIL parsing succeeded, verify the generated SIL.
121+
if (!parser.Diags.hadAnyError() && !DisableInputVerify) {
122+
silMod->verify(/*SingleFunction=*/true, !ParseIncompleteOSSA);
123+
}
124+
120125
return silMod;
121126
}
122127

@@ -7046,10 +7051,6 @@ bool SILParserState::parseDeclSIL(Parser &P) {
70467051
if (FunctionState.diagnoseProblems())
70477052
return true;
70487053

7049-
// If SIL parsing succeeded, verify the generated SIL.
7050-
if (!P.Diags.hadAnyError() && !DisableInputVerify)
7051-
FunctionState.F->verify(/*SingleFunction=*/true, !ParseIncompleteOSSA);
7052-
70537054
return false;
70547055
}
70557056

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "swift/SIL/SILFunction.h"
1818
#include "swift/SIL/ApplySite.h"
1919
#include "swift/SIL/BasicBlockDatastructures.h"
20+
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
21+
#include "swift/SILOptimizer/PassManager/PassManager.h"
2022
#include "llvm/Support/CommandLine.h"
2123

2224
using namespace swift;
@@ -40,6 +42,7 @@ class MemoryLifetimeVerifier {
4042
using BlockState = BitDataflow::BlockState;
4143

4244
SILFunction *function;
45+
AliasAnalysis *aliasAnalysis;
4346
MemoryLocations locations;
4447

4548
/// alloc_stack memory locations which are used for store_borrow.
@@ -77,6 +80,8 @@ class MemoryLifetimeVerifier {
7780
/// \p addr, are set in \p bits.
7881
void requireBitsSet(const Bits &bits, SILValue addr, SILInstruction *where);
7982

83+
void requireBitsSetForArgument(const Bits &bits, SILValue addr, SILInstruction *applyInst);
84+
8085
bool isStoreBorrowLocation(SILValue addr) {
8186
auto *loc = locations.getLocation(addr);
8287
return loc && storeBorrowLocations.anyCommon(loc->subLocations);
@@ -132,9 +137,12 @@ class MemoryLifetimeVerifier {
132137
}
133138

134139
public:
135-
MemoryLifetimeVerifier(SILFunction *function) :
136-
function(function), locations(/*handleNonTrivialProjections*/ true,
137-
/*handleTrivialLocations*/ true) {}
140+
MemoryLifetimeVerifier(SILFunction *function, SILPassManager *passManager) :
141+
function(function),
142+
aliasAnalysis(passManager ? passManager->getAnalysis<AliasAnalysis>(function)
143+
: nullptr),
144+
locations(/*handleNonTrivialProjections*/ true,
145+
/*handleTrivialLocations*/ true) {}
138146

139147
/// The main entry point to verify the lifetime of all memory locations in
140148
/// the function.
@@ -274,6 +282,30 @@ void MemoryLifetimeVerifier::requireBitsSet(const Bits &bits, SILValue addr,
274282
}
275283
}
276284

285+
void MemoryLifetimeVerifier::requireBitsSetForArgument(const Bits &bits, SILValue addr,
286+
SILInstruction *applyInst) {
287+
// Optimizations can rely on alias analysis to know that an in-argument (or
288+
// parts of it) is not actually read.
289+
// We have to do the same in the verifier: if alias analysis says that an in-
290+
// argument is not read, there is no need that the memory location is initialized.
291+
292+
// Not all calls to the verifier provide the alias analysis.
293+
if (!aliasAnalysis)
294+
return;
295+
296+
if (auto *loc = locations.getLocation(addr)) {
297+
Bits missingBits = ~bits & loc->subLocations;
298+
for (int errorLocIdx = missingBits.find_first(); errorLocIdx >= 0;
299+
errorLocIdx = missingBits.find_next(errorLocIdx)) {
300+
auto *errorLoc = locations.getLocation(errorLocIdx);
301+
if (aliasAnalysis->mayReadFromMemory(applyInst, errorLoc->representativeValue)) {
302+
reportError("memory is not initialized, but should be",
303+
errorLocIdx, applyInst);
304+
}
305+
}
306+
}
307+
}
308+
277309
void MemoryLifetimeVerifier::requireNoStoreBorrowLocation(
278310
SILValue addr, SILInstruction *where) {
279311
if (isa<StoreBorrowInst>(addr)) {
@@ -815,7 +847,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
815847

816848
switch (argumentConvention) {
817849
case SILArgumentConvention::Indirect_In:
818-
requireBitsSet(bits, argumentOp.get(), applyInst);
850+
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
819851
locations.clearBits(bits, argumentOp.get());
820852
break;
821853
case SILArgumentConvention::Indirect_Out:
@@ -825,7 +857,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
825857
break;
826858
case SILArgumentConvention::Indirect_In_Guaranteed:
827859
case SILArgumentConvention::Indirect_Inout:
828-
requireBitsSet(bits, argumentOp.get(), applyInst);
860+
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
829861
break;
830862
case SILArgumentConvention::Indirect_InoutAliasable:
831863
// We don't require any locations to be initialized for a partial_apply
@@ -834,7 +866,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
834866
// closures capture the whole "self". When this is done in an initializer
835867
// it can happen that not all fields of "self" are initialized, yet.
836868
if (!isa<PartialApplyInst>(applyInst))
837-
requireBitsSet(bits, argumentOp.get(), applyInst);
869+
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
838870
break;
839871
case SILArgumentConvention::Direct_Owned:
840872
case SILArgumentConvention::Direct_Unowned:
@@ -870,7 +902,7 @@ void MemoryLifetimeVerifier::verify() {
870902

871903
} // anonymous namespace
872904

873-
void SILFunction::verifyMemoryLifetime() {
874-
MemoryLifetimeVerifier verifier(this);
905+
void SILFunction::verifyMemoryLifetime(SILPassManager *passManager) {
906+
MemoryLifetimeVerifier verifier(this, passManager);
875907
verifier.verify();
876908
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ static void checkAddressWalkerCanVisitAllTransitiveUses(SILValue address) {
724724
class SILVerifier : public SILVerifierBase<SILVerifier> {
725725
ModuleDecl *M;
726726
const SILFunction &F;
727+
SILPassManager *passManager;
727728
SILFunctionConventions fnConv;
728729
Lowering::TypeConverter &TC;
729730

@@ -993,8 +994,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
993994
return numInsts;
994995
}
995996

996-
SILVerifier(const SILFunction &F, bool SingleFunction, bool checkLinearLifetime)
997+
SILVerifier(const SILFunction &F, SILPassManager *passManager,
998+
bool SingleFunction, bool checkLinearLifetime)
997999
: M(F.getModule().getSwiftModule()), F(F),
1000+
passManager(passManager),
9981001
fnConv(F.getConventionsInContext()), TC(F.getModule().Types),
9991002
SingleFunction(SingleFunction),
10001003
checkLinearLifetime(checkLinearLifetime),
@@ -6532,7 +6535,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
65326535

65336536
if (F->hasOwnership() && F->shouldVerifyOwnership() &&
65346537
!mod.getASTContext().hadError()) {
6535-
F->verifyMemoryLifetime();
6538+
F->verifyMemoryLifetime(passManager);
65366539
}
65376540
}
65386541

@@ -6572,22 +6575,25 @@ static bool verificationEnabled(const SILModule &M) {
65726575

65736576
/// verify - Run the SIL verifier to make sure that the SILFunction follows
65746577
/// invariants.
6575-
void SILFunction::verify(bool SingleFunction, bool isCompleteOSSA,
6578+
void SILFunction::verify(SILPassManager *passManager,
6579+
bool SingleFunction, bool isCompleteOSSA,
65766580
bool checkLinearLifetime) const {
65776581
if (!verificationEnabled(getModule()))
65786582
return;
65796583

65806584
// Please put all checks in visitSILFunction in SILVerifier, not here. This
65816585
// ensures that the pretty stack trace in the verifier is included with the
65826586
// back trace when the verifier crashes.
6583-
SILVerifier(*this, SingleFunction, checkLinearLifetime).verify(isCompleteOSSA);
6587+
SILVerifier verifier(*this, passManager, SingleFunction, checkLinearLifetime);
6588+
verifier.verify(isCompleteOSSA);
65846589
}
65856590

65866591
void SILFunction::verifyCriticalEdges() const {
65876592
if (!verificationEnabled(getModule()))
65886593
return;
65896594

6590-
SILVerifier(*this, /*SingleFunction=*/true,
6595+
SILVerifier(*this, /*passManager=*/nullptr,
6596+
/*SingleFunction=*/true,
65916597
/*checkLinearLifetime=*/ false).verifyBranches(this);
65926598
}
65936599

@@ -6705,7 +6711,8 @@ void SILVTable::verify(const SILModule &M) const {
67056711
}
67066712

67076713
if (M.getStage() != SILStage::Lowered) {
6708-
SILVerifier(*entry.getImplementation(), /*SingleFunction=*/true,
6714+
SILVerifier(*entry.getImplementation(), /*passManager=*/nullptr,
6715+
/*SingleFunction=*/true,
67096716
/*checkLinearLifetime=*/ false)
67106717
.requireABICompatibleFunctionTypes(
67116718
baseInfo.getSILType().castTo<SILFunctionType>(),
@@ -6850,8 +6857,14 @@ void SILGlobalVariable::verify() const {
68506857
}
68516858
}
68526859

6853-
/// Verify the module.
68546860
void SILModule::verify(bool isCompleteOSSA, bool checkLinearLifetime) const {
6861+
SILPassManager pm(const_cast<SILModule *>(this), /*isMandatory=*/false, /*IRMod=*/nullptr);
6862+
verify(&pm, isCompleteOSSA, checkLinearLifetime);
6863+
}
6864+
6865+
/// Verify the module.
6866+
void SILModule::verify(SILPassManager *passManager,
6867+
bool isCompleteOSSA, bool checkLinearLifetime) const {
68556868
if (!verificationEnabled(*this))
68566869
return;
68576870

@@ -6866,7 +6879,7 @@ void SILModule::verify(bool isCompleteOSSA, bool checkLinearLifetime) const {
68666879
llvm::errs() << "Symbol redefined: " << f.getName() << "!\n";
68676880
assert(false && "triggering standard assertion failure routine");
68686881
}
6869-
f.verify(/*singleFunction*/ false, isCompleteOSSA, checkLinearLifetime);
6882+
f.verify(passManager, /*singleFunction*/ false, isCompleteOSSA, checkLinearLifetime);
68706883
}
68716884

68726885
// Check all globals.

lib/SILOptimizer/Analysis/BasicCalleeAnalysis.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,9 @@ void BridgedCalleeAnalysis::registerAnalysis(IsDeinitBarrierFn instructionIsDein
352352
MemoryBehavior BasicCalleeAnalysis::
353353
getMemoryBehavior(ApplySite as, bool observeRetains) {
354354
if (getMemBehvaiorFunction) {
355-
auto b = getMemBehvaiorFunction({pm->getSwiftPassInvocation()},
356-
{as.getInstruction()->asSILNode()},
357-
observeRetains);
355+
auto b = getMemBehvaiorFunction({as.getInstruction()->asSILNode()},
356+
observeRetains,
357+
{this});
358358
return (MemoryBehavior)b;
359359
}
360360
return MemoryBehavior::MayHaveSideEffects;

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1271,7 +1271,7 @@ bool ABCOpt::processLoop(SILLoop *Loop) {
12711271
Changed |= hoistChecksInLoop(DT->getNode(Header), ABC, IndVars, Preheader,
12721272
Header, SingleExitingBlk, /*recursionDepth*/ 0);
12731273
if (Changed) {
1274-
Preheader->getParent()->verify();
1274+
Preheader->getParent()->verify(getPassManager());
12751275
}
12761276
return Changed;
12771277
}

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ class ClosureLifetimeFixup : public SILFunctionTransform {
12511251
}
12521252
invalidateAnalysis(analysisInvalidationKind(modifiedCFG));
12531253
}
1254-
LLVM_DEBUG(getFunction()->verify());
1254+
LLVM_DEBUG(getFunction()->verify(getPassManager()));
12551255

12561256
}
12571257

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ struct OwnershipModelEliminator : SILFunctionTransform {
751751
"Found verification error when verifying before lowering "
752752
"ownership. Please re-run with -sil-verify-all to identify the "
753753
"actual pass that introduced the verification error.");
754-
f->verify();
754+
f->verify(getPassManager());
755755
}
756756

757757
if (stripOwnership(*f)) {

0 commit comments

Comments
 (0)