Skip to content

Commit 8082ea0

Browse files
committed
[sil] Add a cache for SILVerifier::isOperandInValueUses.
While looking at the performance of the verifier running with -sil-verify-all on the stdlib, I noticed that we are spending ~30% of the total time in the verifier performing this check. Introducing the cache mitigates this issue. I believe the reason is that we were walking for each operand the use list of its associated value which I think is quadratic.
1 parent 2827d72 commit 8082ea0

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,31 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
682682
LoadBorrowImmutabilityAnalysis loadBorrowImmutabilityAnalysis;
683683
bool SingleFunction = true;
684684

685+
/// A cache of the isOperandInValueUse check. When we process an operand, we
686+
/// fix this for each of its uses.
687+
llvm::DenseSet<std::pair<SILValue, const Operand *>> isOperandInValueUsesCache;
688+
689+
/// Check that this operand appears in the use-chain of the value it uses.
690+
bool isOperandInValueUses(const Operand *operand) {
691+
SILValue value = operand->get();
692+
693+
// First check the cache.
694+
if (isOperandInValueUsesCache.contains({value, operand}))
695+
return true;
696+
697+
// Otherwise, compute the value and initialize the cache for each of the
698+
// operand's value uses.
699+
bool foundUse = false;
700+
for (auto *use : value->getUses()) {
701+
if (use == operand) {
702+
foundUse = true;
703+
}
704+
isOperandInValueUsesCache.insert({value, use});
705+
}
706+
707+
return foundUse;
708+
}
709+
685710
SILVerifier(const SILVerifier&) = delete;
686711
void operator=(const SILVerifier&) = delete;
687712
public:
@@ -1112,7 +1137,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
11121137

11131138
require(operand.getUser() == I,
11141139
"instruction's operand's owner isn't the instruction");
1115-
require(isInValueUses(&operand), "operand value isn't used by operand");
1140+
require(isOperandInValueUses(&operand), "operand value isn't used by operand");
11161141

11171142
if (operand.isTypeDependent()) {
11181143
require(isa<SILInstruction>(I),
@@ -1311,14 +1336,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
13111336
});
13121337
}
13131338

1314-
/// Check that this operand appears in the use-chain of the value it uses.
1315-
static bool isInValueUses(const Operand *operand) {
1316-
for (auto use : operand->get()->getUses())
1317-
if (use == operand)
1318-
return true;
1319-
return false;
1320-
}
1321-
13221339
/// \return True if all of the users of the AllocStack instruction \p ASI are
13231340
/// inside the same basic block.
13241341
static bool isSingleBlockUsage(AllocStackInst *ASI, DominanceInfo *Dominance){

0 commit comments

Comments
 (0)