Skip to content

Commit 006e5ea

Browse files
committed
MemoryLifetimeVerifier: use CalleeCache instead of AliasAnalysis
To verify if a function may read from an indirect argument, don't use AliasAnalysis. Instead use the CalleeCache to get the list of callees of an apply instruction. Then use a simple call-back into the swift Function to check if a callee has any relevant memory effect set. This avoids a dependency from SIL to the Optimizer. It fixes a linker error when building some unit tests in debug.
1 parent 6a28452 commit 006e5ea

File tree

13 files changed

+107
-54
lines changed

13 files changed

+107
-54
lines changed

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,24 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
368368
{ (f: BridgedFunction, observeRetains: Bool) -> BridgedMemoryBehavior in
369369
let e = f.function.getSideEffects()
370370
return e.getMemBehavior(observeRetains: observeRetains)
371+
},
372+
// argumentMayRead (used by the MemoryLifetimeVerifier)
373+
{ (f: BridgedFunction, bridgedArgOp: BridgedOperand, bridgedAddr: BridgedValue) -> Bool in
374+
let argOp = Operand(bridged: bridgedArgOp)
375+
let addr = bridgedAddr.value
376+
let applySite = argOp.instruction as! FullApplySite
377+
let addrPath = addr.accessPath
378+
let argIdx = argOp.index - ApplyOperands.firstArgumentIndex
379+
let calleeArgIdx = applySite.calleeArgIndex(callerArgIndex: argIdx)
380+
let convention = applySite.getArgumentConvention(calleeArgIndex: calleeArgIdx)
381+
assert(convention.isIndirectIn || convention.isInout)
382+
let argPath = argOp.value.accessPath
383+
assert(!argPath.isDistinct(from: addrPath))
384+
let path = argPath.getProjection(to: addrPath) ?? SmallProjectionPath()
385+
let effects = f.function.getSideEffects(forArgument: argOp.value.at(path),
386+
atIndex: calleeArgIdx,
387+
withConvention: convention)
388+
return effects.memory.read
371389
}
372390
)
373391
}

include/swift/SIL/SILBridging.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,15 @@ struct BridgedFunction {
373373
typedef SwiftInt (* _Nonnull CopyEffectsFn)(BridgedFunction, BridgedFunction);
374374
typedef EffectInfo (* _Nonnull GetEffectInfoFn)(BridgedFunction, SwiftInt);
375375
typedef BridgedMemoryBehavior (* _Nonnull GetMemBehaviorFn)(BridgedFunction, bool);
376+
typedef bool (* _Nonnull ArgumentMayReadFn)(BridgedFunction, BridgedOperand, BridgedValue);
376377

377378
static void registerBridging(SwiftMetatype metatype,
378379
RegisterFn initFn, RegisterFn destroyFn,
379380
WriteFn writeFn, ParseFn parseFn,
380381
CopyEffectsFn copyEffectsFn,
381382
GetEffectInfoFn effectInfoFn,
382-
GetMemBehaviorFn memBehaviorFn);
383+
GetMemBehaviorFn memBehaviorFn,
384+
ArgumentMayReadFn argumentMayReadFn);
383385
};
384386

385387
struct OptionalBridgedFunction {

include/swift/SIL/SILFunction.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class SILProfiler;
3939
class BasicBlockBitfield;
4040
class NodeBitfield;
4141
class OperandBitfield;
42-
class SILPassManager;
42+
class CalleeCache;
4343

4444
namespace Lowering {
4545
class TypeLowering;
@@ -1141,6 +1141,9 @@ class SILFunction
11411141
void visitArgEffects(std::function<void(int, int, bool)> c) const;
11421142
MemoryBehavior getMemoryBehavior(bool observeRetains);
11431143

1144+
// Used by the MemoryLifetimeVerifier
1145+
bool argumentMayRead(Operand *argOp, SILValue addr);
1146+
11441147
Purpose getSpecialPurpose() const { return specialPurpose; }
11451148

11461149
/// Get this function's global_init attribute.
@@ -1497,19 +1500,19 @@ class SILFunction
14971500

14981501
/// verify - Run the SIL verifier to make sure that the SILFunction follows
14991502
/// invariants.
1500-
void verify(SILPassManager *passManager = nullptr,
1503+
void verify(CalleeCache *calleeCache = nullptr,
15011504
bool SingleFunction = true,
15021505
bool isCompleteOSSA = true,
15031506
bool checkLinearLifetime = true) const;
15041507

15051508
/// Run the SIL verifier without assuming OSSA lifetimes end at dead end
15061509
/// blocks.
15071510
void verifyIncompleteOSSA() const {
1508-
verify(/*passManager*/nullptr, /*SingleFunction=*/true, /*completeOSSALifetimes=*/false);
1511+
verify(/*calleeCache*/nullptr, /*SingleFunction=*/true, /*completeOSSALifetimes=*/false);
15091512
}
15101513

15111514
/// Verifies the lifetime of memory locations in the function.
1512-
void verifyMemoryLifetime(SILPassManager *passManager);
1515+
void verifyMemoryLifetime(CalleeCache *calleeCache);
15131516

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

include/swift/SIL/SILModule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ class SILModule {
926926

927927
/// Run the SIL verifier to make sure that all Functions follow
928928
/// invariants.
929-
void verify(SILPassManager *passManager,
929+
void verify(CalleeCache *calleeCache,
930930
bool isCompleteOSSA = true,
931931
bool checkLinearLifetime = true) const;
932932

include/swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ class BasicCalleeAnalysis : public SILAnalysis {
9797
}
9898

9999
MemoryBehavior getMemoryBehavior(ApplySite as, bool observeRetains);
100+
101+
CalleeCache *getCalleeCache() { return Cache.get(); }
100102
};
101103

102104
bool isDeinitBarrier(SILInstruction *const instruction,

lib/SIL/IR/SILFunction.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ static BridgedFunction::ParseFn parseFunction = nullptr;
146146
static BridgedFunction::CopyEffectsFn copyEffectsFunction = nullptr;
147147
static BridgedFunction::GetEffectInfoFn getEffectInfoFunction = nullptr;
148148
static BridgedFunction::GetMemBehaviorFn getMemBehvaiorFunction = nullptr;
149+
static BridgedFunction::ArgumentMayReadFn argumentMayReadFunction = nullptr;
149150

150151
SILFunction::SILFunction(
151152
SILModule &Module, SILLinkage Linkage, StringRef Name,
@@ -1010,7 +1011,8 @@ void BridgedFunction::registerBridging(SwiftMetatype metatype,
10101011
WriteFn writeFn, ParseFn parseFn,
10111012
CopyEffectsFn copyEffectsFn,
10121013
GetEffectInfoFn effectInfoFn,
1013-
GetMemBehaviorFn memBehaviorFn) {
1014+
GetMemBehaviorFn memBehaviorFn,
1015+
ArgumentMayReadFn argumentMayReadFn) {
10141016
functionMetatype = metatype;
10151017
initFunction = initFn;
10161018
destroyFunction = destroyFn;
@@ -1019,6 +1021,7 @@ void BridgedFunction::registerBridging(SwiftMetatype metatype,
10191021
copyEffectsFunction = copyEffectsFn;
10201022
getEffectInfoFunction = effectInfoFn;
10211023
getMemBehvaiorFunction = memBehaviorFn;
1024+
argumentMayReadFunction = argumentMayReadFn;
10221025
}
10231026

10241027
std::pair<const char *, int> SILFunction::
@@ -1111,3 +1114,11 @@ MemoryBehavior SILFunction::getMemoryBehavior(bool observeRetains) {
11111114
auto b = getMemBehvaiorFunction({this}, observeRetains);
11121115
return (MemoryBehavior)b;
11131116
}
1117+
1118+
// Used by the MemoryLifetimeVerifier
1119+
bool SILFunction::argumentMayRead(Operand *argOp, SILValue addr) {
1120+
if (!argumentMayReadFunction)
1121+
return true;
1122+
1123+
return argumentMayReadFunction({this}, {argOp}, {addr});
1124+
}

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@
1313
#define DEBUG_TYPE "sil-memory-lifetime-verifier"
1414
#include "swift/SIL/MemoryLocations.h"
1515
#include "swift/SIL/BitDataflow.h"
16+
#include "swift/SIL/CalleeCache.h"
1617
#include "swift/SIL/SILBasicBlock.h"
1718
#include "swift/SIL/SILFunction.h"
1819
#include "swift/SIL/ApplySite.h"
1920
#include "swift/SIL/BasicBlockDatastructures.h"
20-
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
21-
#include "swift/SILOptimizer/PassManager/PassManager.h"
2221
#include "llvm/Support/CommandLine.h"
2322

2423
using namespace swift;
@@ -42,7 +41,7 @@ class MemoryLifetimeVerifier {
4241
using BlockState = BitDataflow::BlockState;
4342

4443
SILFunction *function;
45-
AliasAnalysis *aliasAnalysis;
44+
CalleeCache *calleeCache;
4645
MemoryLocations locations;
4746

4847
/// alloc_stack memory locations which are used for store_borrow.
@@ -80,7 +79,9 @@ class MemoryLifetimeVerifier {
8079
/// \p addr, are set in \p bits.
8180
void requireBitsSet(const Bits &bits, SILValue addr, SILInstruction *where);
8281

83-
void requireBitsSetForArgument(const Bits &bits, SILValue addr, SILInstruction *applyInst);
82+
void requireBitsSetForArgument(const Bits &bits, Operand *argOp);
83+
84+
bool applyMayRead(Operand *argOp, SILValue addr);
8485

8586
bool isStoreBorrowLocation(SILValue addr) {
8687
auto *loc = locations.getLocation(addr);
@@ -137,10 +138,9 @@ class MemoryLifetimeVerifier {
137138
}
138139

139140
public:
140-
MemoryLifetimeVerifier(SILFunction *function, SILPassManager *passManager) :
141+
MemoryLifetimeVerifier(SILFunction *function, CalleeCache *calleeCache) :
141142
function(function),
142-
aliasAnalysis(passManager ? passManager->getAnalysis<AliasAnalysis>(function)
143-
: nullptr),
143+
calleeCache(calleeCache),
144144
locations(/*handleNonTrivialProjections*/ true,
145145
/*handleTrivialLocations*/ true) {}
146146

@@ -277,30 +277,41 @@ void MemoryLifetimeVerifier::requireBitsSet(const Bits &bits, SILValue addr,
277277
}
278278
}
279279

280-
void MemoryLifetimeVerifier::requireBitsSetForArgument(const Bits &bits, SILValue addr,
281-
SILInstruction *applyInst) {
282-
// Optimizations can rely on alias analysis to know that an in-argument (or
283-
// parts of it) is not actually read.
284-
// We have to do the same in the verifier: if alias analysis says that an in-
285-
// argument is not read, there is no need that the memory location is initialized.
286-
287-
// Not all calls to the verifier provide the alias analysis.
288-
if (!aliasAnalysis)
289-
return;
290-
291-
if (auto *loc = locations.getLocation(addr)) {
280+
void MemoryLifetimeVerifier::requireBitsSetForArgument(const Bits &bits, Operand *argOp) {
281+
if (auto *loc = locations.getLocation(argOp->get())) {
292282
Bits missingBits = ~bits & loc->subLocations;
293283
for (int errorLocIdx = missingBits.find_first(); errorLocIdx >= 0;
294284
errorLocIdx = missingBits.find_next(errorLocIdx)) {
295285
auto *errorLoc = locations.getLocation(errorLocIdx);
296-
if (aliasAnalysis->mayReadFromMemory(applyInst, errorLoc->representativeValue)) {
286+
287+
if (applyMayRead(argOp, errorLoc->representativeValue)) {
297288
reportError("memory is not initialized, but should be",
298-
errorLocIdx, applyInst);
289+
errorLocIdx, argOp->getUser());
299290
}
300291
}
301292
}
302293
}
303294

295+
bool MemoryLifetimeVerifier::applyMayRead(Operand *argOp, SILValue addr) {
296+
FullApplySite as(argOp->getUser());
297+
CalleeList callees;
298+
if (calleeCache) {
299+
callees = calleeCache->getCalleeList(as);
300+
if (callees.isIncomplete())
301+
return true;
302+
} else if (auto *callee = as.getReferencedFunctionOrNull()) {
303+
callees = CalleeList(callee);
304+
} else {
305+
return false;
306+
}
307+
308+
for (SILFunction *callee : callees) {
309+
if (callee->argumentMayRead(argOp, addr))
310+
return true;
311+
}
312+
return false;
313+
}
314+
304315
void MemoryLifetimeVerifier::requireNoStoreBorrowLocation(
305316
SILValue addr, SILInstruction *where) {
306317
if (isa<StoreBorrowInst>(addr)) {
@@ -864,7 +875,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
864875

865876
switch (argumentConvention) {
866877
case SILArgumentConvention::Indirect_In:
867-
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
878+
requireBitsSetForArgument(bits, &argumentOp);
868879
locations.clearBits(bits, argumentOp.get());
869880
break;
870881
case SILArgumentConvention::Indirect_Out:
@@ -874,7 +885,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
874885
break;
875886
case SILArgumentConvention::Indirect_In_Guaranteed:
876887
case SILArgumentConvention::Indirect_Inout:
877-
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
888+
requireBitsSetForArgument(bits, &argumentOp);
878889
break;
879890
case SILArgumentConvention::Indirect_InoutAliasable:
880891
// We don't require any locations to be initialized for a partial_apply
@@ -883,7 +894,7 @@ void MemoryLifetimeVerifier::checkFuncArgument(Bits &bits, Operand &argumentOp,
883894
// closures capture the whole "self". When this is done in an initializer
884895
// it can happen that not all fields of "self" are initialized, yet.
885896
if (!isa<PartialApplyInst>(applyInst))
886-
requireBitsSetForArgument(bits, argumentOp.get(), applyInst);
897+
requireBitsSetForArgument(bits, &argumentOp);
887898
break;
888899
case SILArgumentConvention::Direct_Owned:
889900
case SILArgumentConvention::Direct_Unowned:
@@ -919,7 +930,7 @@ void MemoryLifetimeVerifier::verify() {
919930

920931
} // anonymous namespace
921932

922-
void SILFunction::verifyMemoryLifetime(SILPassManager *passManager) {
923-
MemoryLifetimeVerifier verifier(this, passManager);
933+
void SILFunction::verifyMemoryLifetime(CalleeCache *calleeCache) {
934+
MemoryLifetimeVerifier verifier(this, calleeCache);
924935
verifier.verify();
925936
}

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/SIL/ApplySite.h"
3030
#include "swift/SIL/BasicBlockBits.h"
3131
#include "swift/SIL/BasicBlockUtils.h"
32+
#include "swift/SIL/CalleeCache.h"
3233
#include "swift/SIL/DebugUtils.h"
3334
#include "swift/SIL/Dominance.h"
3435
#include "swift/SIL/DynamicCasts.h"
@@ -745,7 +746,7 @@ static void checkAddressWalkerCanVisitAllTransitiveUses(SILValue address) {
745746
class SILVerifier : public SILVerifierBase<SILVerifier> {
746747
ModuleDecl *M;
747748
const SILFunction &F;
748-
SILPassManager *passManager;
749+
CalleeCache *calleeCache;
749750
SILFunctionConventions fnConv;
750751
Lowering::TypeConverter &TC;
751752

@@ -1015,10 +1016,10 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
10151016
return numInsts;
10161017
}
10171018

1018-
SILVerifier(const SILFunction &F, SILPassManager *passManager,
1019+
SILVerifier(const SILFunction &F, CalleeCache *calleeCache,
10191020
bool SingleFunction, bool checkLinearLifetime)
10201021
: M(F.getModule().getSwiftModule()), F(F),
1021-
passManager(passManager),
1022+
calleeCache(calleeCache),
10221023
fnConv(F.getConventionsInContext()), TC(F.getModule().Types),
10231024
SingleFunction(SingleFunction),
10241025
checkLinearLifetime(checkLinearLifetime),
@@ -6796,7 +6797,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
67966797

67976798
if (F->hasOwnership() && F->shouldVerifyOwnership() &&
67986799
!mod.getASTContext().hadError()) {
6799-
F->verifyMemoryLifetime(passManager);
6800+
F->verifyMemoryLifetime(calleeCache);
68006801
}
68016802
}
68026803

@@ -6836,7 +6837,7 @@ static bool verificationEnabled(const SILModule &M) {
68366837

68376838
/// verify - Run the SIL verifier to make sure that the SILFunction follows
68386839
/// invariants.
6839-
void SILFunction::verify(SILPassManager *passManager,
6840+
void SILFunction::verify(CalleeCache *calleeCache,
68406841
bool SingleFunction, bool isCompleteOSSA,
68416842
bool checkLinearLifetime) const {
68426843
if (!verificationEnabled(getModule()))
@@ -6845,15 +6846,15 @@ void SILFunction::verify(SILPassManager *passManager,
68456846
// Please put all checks in visitSILFunction in SILVerifier, not here. This
68466847
// ensures that the pretty stack trace in the verifier is included with the
68476848
// back trace when the verifier crashes.
6848-
SILVerifier verifier(*this, passManager, SingleFunction, checkLinearLifetime);
6849+
SILVerifier verifier(*this, calleeCache, SingleFunction, checkLinearLifetime);
68496850
verifier.verify(isCompleteOSSA);
68506851
}
68516852

68526853
void SILFunction::verifyCriticalEdges() const {
68536854
if (!verificationEnabled(getModule()))
68546855
return;
68556856

6856-
SILVerifier(*this, /*passManager=*/nullptr,
6857+
SILVerifier(*this, /*calleeCache=*/nullptr,
68576858
/*SingleFunction=*/true,
68586859
/*checkLinearLifetime=*/ false).verifyBranches(this);
68596860
}
@@ -6973,7 +6974,7 @@ void SILVTable::verify(const SILModule &M) const {
69736974

69746975
if (M.getStage() != SILStage::Lowered &&
69756976
!M.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
6976-
SILVerifier(*entry.getImplementation(), /*passManager=*/nullptr,
6977+
SILVerifier(*entry.getImplementation(), /*calleeCache=*/nullptr,
69776978
/*SingleFunction=*/true,
69786979
/*checkLinearLifetime=*/ false)
69796980
.requireABICompatibleFunctionTypes(
@@ -7118,12 +7119,12 @@ void SILGlobalVariable::verify() const {
71187119
}
71197120

71207121
void SILModule::verify(bool isCompleteOSSA, bool checkLinearLifetime) const {
7121-
SILPassManager pm(const_cast<SILModule *>(this), /*isMandatory=*/false, /*IRMod=*/nullptr);
7122-
verify(&pm, isCompleteOSSA, checkLinearLifetime);
7122+
CalleeCache calleeCache(*const_cast<SILModule *>(this));
7123+
verify(&calleeCache, isCompleteOSSA, checkLinearLifetime);
71237124
}
71247125

71257126
/// Verify the module.
7126-
void SILModule::verify(SILPassManager *passManager,
7127+
void SILModule::verify(CalleeCache *calleeCache,
71277128
bool isCompleteOSSA, bool checkLinearLifetime) const {
71287129
if (!verificationEnabled(*this))
71297130
return;
@@ -7139,7 +7140,7 @@ void SILModule::verify(SILPassManager *passManager,
71397140
llvm::errs() << "Symbol redefined: " << f.getName() << "!\n";
71407141
assert(false && "triggering standard assertion failure routine");
71417142
}
7142-
f.verify(passManager, /*singleFunction*/ false, isCompleteOSSA, checkLinearLifetime);
7143+
f.verify(calleeCache, /*singleFunction*/ false, isCompleteOSSA, checkLinearLifetime);
71437144
}
71447145

71457146
// Check all globals.

lib/SILOptimizer/LoopTransforms/ArrayBoundsCheckOpts.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
2525
#include "swift/SILOptimizer/Analysis/Analysis.h"
2626
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
27+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
2728
#include "swift/SILOptimizer/Analysis/DestructorAnalysis.h"
2829
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
2930
#include "swift/SILOptimizer/Analysis/IVAnalysis.h"
@@ -1319,7 +1320,7 @@ bool ABCOpt::processLoop(SILLoop *Loop) {
13191320
Changed |= hoistChecksInLoop(DT->getNode(Header), ABC, IndVars, Preheader,
13201321
Header, SingleExitingBlk, /*recursionDepth*/ 0);
13211322
if (Changed) {
1322-
Preheader->getParent()->verify(getPassManager());
1323+
Preheader->getParent()->verify(getAnalysis<BasicCalleeAnalysis>()->getCalleeCache());
13231324
}
13241325
return Changed;
13251326
}

0 commit comments

Comments
 (0)