Skip to content

Commit 3c58cd5

Browse files
committed
[passmanager] Change the verifier analysis to use function names instead of SILFunction pointers for its internal state.
This enables us to have state independent of the liveness of the SILFunction's that we are tracking. I also changed the verifier to implement only verifyFull instead of verify to ensure that when we run with sil-verify-all this only runs at the end of pass manager pipelines. rdar://42301529
1 parent fc41370 commit 3c58cd5

File tree

2 files changed

+52
-18
lines changed

2 files changed

+52
-18
lines changed

include/swift/SILOptimizer/Analysis/PassManagerVerifierAnalysis.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/SIL/SILFunction.h"
1717
#include "swift/SILOptimizer/Analysis/Analysis.h"
1818
#include "llvm/ADT/DenseSet.h"
19+
#include "llvm/ADT/StringSet.h"
1920

2021
namespace swift {
2122

@@ -28,10 +29,12 @@ class PassManagerVerifierAnalysis : public SILAnalysis {
2829
LLVM_ATTRIBUTE_UNUSED
2930
SILModule &mod;
3031

31-
/// The set of "live" functions that we are tracking.
32+
/// The set of "live" functions that we are tracking. We store the names of
33+
/// the functions so that if a function is deleted we do not need to touch its
34+
/// memory to get its name.
3235
///
3336
/// All functions in mod must be in liveFunctions and vis-a-versa.
34-
llvm::DenseSet<SILFunction *> liveFunctions;
37+
llvm::StringSet<> liveFunctionNames;
3538

3639
public:
3740
PassManagerVerifierAnalysis(SILModule *mod);
@@ -58,7 +61,7 @@ class PassManagerVerifierAnalysis : public SILAnalysis {
5861
void invalidateFunctionTables() override final;
5962

6063
/// Run the entire verification.
61-
void verify() const override final;
64+
void verifyFull() const override final;
6265
};
6366

6467
} // namespace swift

lib/SILOptimizer/Analysis/PassManagerVerifierAnalysis.cpp

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ PassManagerVerifierAnalysis::PassManagerVerifierAnalysis(SILModule *mod)
3131
for (auto &fn : *mod) {
3232
LLVM_DEBUG(llvm::dbgs() << "PMVerifierAnalysis. Add: " << fn.getName()
3333
<< '\n');
34-
liveFunctions.insert(&fn);
34+
liveFunctionNames.insert(fn.getName());
3535
}
3636
#endif
3737
}
@@ -52,7 +52,7 @@ void PassManagerVerifierAnalysis::notifyAddedOrModifiedFunction(
5252
return;
5353
LLVM_DEBUG(llvm::dbgs() << "PMVerifierAnalysis. Add|Mod: " << f->getName()
5454
<< '\n');
55-
liveFunctions.insert(f);
55+
liveFunctionNames.insert(f->getName());
5656
#endif
5757
}
5858

@@ -63,9 +63,13 @@ void PassManagerVerifierAnalysis::notifyWillDeleteFunction(SILFunction *f) {
6363
return;
6464
LLVM_DEBUG(llvm::dbgs() << "PMVerifierAnalysis. Delete: " << f->getName()
6565
<< '\n');
66-
assert(liveFunctions.count(f) &&
67-
"Tried to delete function that analysis was not aware of?!");
68-
liveFunctions.erase(f);
66+
if (liveFunctionNames.erase(f->getName()))
67+
return;
68+
69+
llvm::errs()
70+
<< "Error! Tried to delete function that analysis was not aware of: "
71+
<< f->getName() << '\n';
72+
llvm_unreachable("triggering standard assertion failure routine");
6973
#endif
7074
}
7175

@@ -74,22 +78,49 @@ void PassManagerVerifierAnalysis::notifyWillDeleteFunction(SILFunction *f) {
7478
void PassManagerVerifierAnalysis::invalidateFunctionTables() {}
7579

7680
/// Run the entire verification.
77-
void PassManagerVerifierAnalysis::verify() const {
81+
void PassManagerVerifierAnalysis::verifyFull() const {
7882
#ifndef NDEBUG
7983
if (!EnableVerifier)
8084
return;
8185

82-
// We check that all functions in the module are in liveFunctions /and/ then
83-
// make sure that liveFunctions has the same number of elements. If we have
84-
// too many elements, this means we missed a delete event.
85-
unsigned funcCount = 0;
86+
// We check that liveFunctionNames is in sync with the module's function list
87+
// by going through the module's function list and attempting to remove all
88+
// functions in the module. If we fail to remove fn, then we know that a
89+
// function was added to the module without an appropriate message being sent
90+
// by the pass manager.
91+
bool foundError = false;
92+
93+
unsigned count = 0;
8694
for (auto &fn : mod) {
87-
++funcCount;
88-
assert(liveFunctions.count(&fn) &&
89-
"Found function in module that verifier is not aware of?!");
95+
if (liveFunctionNames.count(fn.getName())) {
96+
++count;
97+
continue;
98+
}
99+
llvm::errs() << "Found function in module that was not added to verifier: "
100+
<< fn.getName() << '\n';
101+
foundError = true;
90102
}
91-
assert(liveFunctions.size() == funcCount &&
92-
"Analysis has state for deleted functions?!");
103+
104+
// Ok, so now we know that function(mod) is a subset of
105+
// liveFunctionNames. Relying on the uniqueness provided by the module's
106+
// function list, we know that liveFunction should be exactly count in
107+
// size. Otherwise, we must have an error. If and only if we detect this
108+
// error, do the expensive work of finding the missing deletes. This is an
109+
// important performance optimization to avoid a large copy on the hot path.
110+
if (liveFunctionNames.size() != count) {
111+
auto liveFunctionNamesCopy = llvm::StringSet<>(liveFunctionNames);
112+
for (auto &fn : mod) {
113+
liveFunctionNamesCopy.erase(fn.getName());
114+
}
115+
for (auto &iter : liveFunctionNamesCopy) {
116+
llvm::errs() << "Missing delete message for function: " << iter.first()
117+
<< '\n';
118+
foundError = true;
119+
}
120+
}
121+
122+
// We assert here so we emit /all/ errors before asserting.
123+
assert(!foundError && "triggering standard assertion failure routine");
93124
#endif
94125
}
95126

0 commit comments

Comments
 (0)