Skip to content

Commit ffb4fb7

Browse files
committed
[Dominators] Introduce DomTree verification levels
Summary: Currently, there are 2 ways to verify a DomTree: * `DT.verify()` -- runs full tree verification and checks all the properties and gives a reason why the tree is incorrect. This is run by when EXPENSIVE_CHECKS are enabled or when `-verify-dom-info` flag is set. * `DT.verifyDominatorTree()` -- constructs a fresh tree and compares it against the old one. This does not check any other tree properties (DFS number, levels), nor ensures that the construction algorithm is correct. Used by some passes inside assertions. This patch introduces DomTree verification levels, that try to close the gape between the two ways of checking trees by introducing 3 verification levels: - Full -- checks all properties, but can be slow (O(N^3)). Used when manually requested (e.g. `assert(DT.verify())`) or when `-verify-dom-info` is set. - Basic -- checks all properties except the sibling property, and compares the current tree with a freshly constructed one instead. This should catch almost all errors, but does not guarantee that the construction algorithm is correct. Used when EXPENSIVE checks are enabled. - Fast -- checks only basic properties (reachablility, dfs numbers, levels, roots), and compares with a fresh tree. This is meant to replace the legacy `DT.verifyDominatorTree()` and in my tests doesn't cause any noticeable performance impact even in the most pessimistic examples. When used to verify dom tree wrapper pass analysis on sqlite3, the 3 new levels make `opt -O3` take the following amount of time on my machine: - no verification: 8.3s - `DT.verify(VerificationLevel::Fast)`: 10.1s - `DT.verify(VerificationLevel::Basic)`: 44.8s - `DT.verify(VerificationLevel::Full)`: 1m 46.2s (and the previous `DT.verifyDominatorTree()` is within the noise of the Fast level) This patch makes `DT.verifyDominatorTree()` pick between the 3 verification levels depending on EXPENSIVE_CHECKS and `-verify-dom-info`. Reviewers: dberlin, brzycki, davide, grosser, dmgreen Reviewed By: dberlin, brzycki Subscribers: MatzeB, llvm-commits Differential Revision: https://reviews.llvm.org/D42337 llvm-svn: 323298
1 parent 432a587 commit ffb4fb7

File tree

4 files changed

+102
-46
lines changed

4 files changed

+102
-46
lines changed

llvm/include/llvm/IR/Dominators.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ extern template void DeleteEdge<BBPostDomTree>(BBPostDomTree &DT,
6363
extern template void ApplyUpdates<BBDomTree>(BBDomTree &DT, BBUpdates);
6464
extern template void ApplyUpdates<BBPostDomTree>(BBPostDomTree &DT, BBUpdates);
6565

66-
extern template bool Verify<BBDomTree>(const BBDomTree &DT);
67-
extern template bool Verify<BBPostDomTree>(const BBPostDomTree &DT);
66+
extern template bool Verify<BBDomTree>(const BBDomTree &DT,
67+
BBDomTree::VerificationLevel VL);
68+
extern template bool Verify<BBPostDomTree>(const BBPostDomTree &DT,
69+
BBPostDomTree::VerificationLevel VL);
6870
} // namespace DomTreeBuilder
6971

7072
using DomTreeNode = DomTreeNodeBase<BasicBlock>;
@@ -148,15 +150,6 @@ class DominatorTree : public DominatorTreeBase<BasicBlock, false> {
148150
bool invalidate(Function &F, const PreservedAnalyses &PA,
149151
FunctionAnalysisManager::Invalidator &);
150152

151-
/// \brief Returns *false* if the other dominator tree matches this dominator
152-
/// tree.
153-
inline bool compare(const DominatorTree &Other) const {
154-
const DomTreeNode *R = getRootNode();
155-
const DomTreeNode *OtherR = Other.getRootNode();
156-
return !R || !OtherR || R->getBlock() != OtherR->getBlock() ||
157-
Base::compare(Other);
158-
}
159-
160153
// Ensure base-class overloads are visible.
161154
using Base::dominates;
162155

llvm/include/llvm/Support/GenericDomTree.h

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ void ApplyUpdates(DomTreeT &DT,
234234
ArrayRef<typename DomTreeT::UpdateType> Updates);
235235

236236
template <typename DomTreeT>
237-
bool Verify(const DomTreeT &DT);
237+
bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL);
238238
} // namespace DomTreeBuilder
239239

240240
/// \brief Core dominator tree base class.
@@ -259,7 +259,9 @@ class DominatorTreeBase {
259259
static constexpr UpdateKind Insert = UpdateKind::Insert;
260260
static constexpr UpdateKind Delete = UpdateKind::Delete;
261261

262-
protected:
262+
enum class VerificationLevel { Fast, Basic, Full };
263+
264+
protected:
263265
// Dominators always have a single root, postdominators can have more.
264266
SmallVector<NodeT *, IsPostDom ? 4 : 1> Roots;
265267

@@ -316,6 +318,12 @@ class DominatorTreeBase {
316318
bool compare(const DominatorTreeBase &Other) const {
317319
if (Parent != Other.Parent) return true;
318320

321+
if (Roots.size() != Other.Roots.size())
322+
return false;
323+
324+
if (!std::is_permutation(Roots.begin(), Roots.end(), Other.Roots.begin()))
325+
return false;
326+
319327
const DomTreeNodeMapType &OtherDomTreeNodes = Other.DomTreeNodes;
320328
if (DomTreeNodes.size() != OtherDomTreeNodes.size())
321329
return true;
@@ -750,10 +758,25 @@ class DominatorTreeBase {
750758
DomTreeBuilder::Calculate(*this);
751759
}
752760

753-
/// verify - check parent and sibling property
754-
bool verify() const { return DomTreeBuilder::Verify(*this); }
761+
/// verify - checks if the tree is correct. There are 3 level of verification:
762+
/// - Full -- verifies if the tree is correct by making sure all the
763+
/// properties (including the parent and the sibling property)
764+
/// hold.
765+
/// Takes O(N^3) time.
766+
///
767+
/// - Basic -- checks if the tree is correct, but compares it to a freshly
768+
/// constructed tree instead of checking the sibling property.
769+
/// Takes O(N^2) time.
770+
///
771+
/// - Fast -- checks basic tree structure and compares it with a freshly
772+
/// constructed tree.
773+
/// Takes O(N^2) time worst case, but is faster in practise (same
774+
/// as tree construction).
775+
bool verify(VerificationLevel VL = VerificationLevel::Full) const {
776+
return DomTreeBuilder::Verify(*this, VL);
777+
}
755778

756-
protected:
779+
protected:
757780
void addRoot(NodeT *BB) { this->Roots.push_back(BB); }
758781

759782
void reset() {

llvm/include/llvm/Support/GenericDomTreeConstruction.h

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,7 @@ struct SemiNCAInfo {
12811281
// root which is the function's entry node. A PostDominatorTree can have
12821282
// multiple roots - one for each node with no successors and for infinite
12831283
// loops.
1284+
// Running time: O(N).
12841285
bool verifyRoots(const DomTreeT &DT) {
12851286
if (!DT.Parent && !DT.Roots.empty()) {
12861287
errs() << "Tree has no parent but has roots!\n";
@@ -1321,6 +1322,7 @@ struct SemiNCAInfo {
13211322
}
13221323

13231324
// Checks if the tree contains all reachable nodes in the input graph.
1325+
// Running time: O(N).
13241326
bool verifyReachability(const DomTreeT &DT) {
13251327
clear();
13261328
doFullDFSWalk(DT, AlwaysDescend);
@@ -1356,6 +1358,7 @@ struct SemiNCAInfo {
13561358

13571359
// Check if for every parent with a level L in the tree all of its children
13581360
// have level L + 1.
1361+
// Running time: O(N).
13591362
static bool VerifyLevels(const DomTreeT &DT) {
13601363
for (auto &NodeToTN : DT.DomTreeNodes) {
13611364
const TreeNodePtr TN = NodeToTN.second.get();
@@ -1387,6 +1390,7 @@ struct SemiNCAInfo {
13871390

13881391
// Check if the computed DFS numbers are correct. Note that DFS info may not
13891392
// be valid, and when that is the case, we don't verify the numbers.
1393+
// Running time: O(N log(N)).
13901394
static bool VerifyDFSNumbers(const DomTreeT &DT) {
13911395
if (!DT.DFSInfoValid || !DT.Parent)
13921396
return true;
@@ -1517,10 +1521,10 @@ struct SemiNCAInfo {
15171521
// linear time, but the algorithms are complex. Instead, we do it in a
15181522
// straightforward N^2 and N^3 way below, using direct path reachability.
15191523

1520-
15211524
// Checks if the tree has the parent property: if for all edges from V to W in
15221525
// the input graph, such that V is reachable, the parent of W in the tree is
15231526
// an ancestor of V in the tree.
1527+
// Running time: O(N^2).
15241528
//
15251529
// This means that if a node gets disconnected from the graph, then all of
15261530
// the nodes it dominated previously will now become unreachable.
@@ -1553,6 +1557,7 @@ struct SemiNCAInfo {
15531557

15541558
// Check if the tree has sibling property: if a node V does not dominate a
15551559
// node W for all siblings V and W in the tree.
1560+
// Running time: O(N^3).
15561561
//
15571562
// This means that if a node gets disconnected from the graph, then all of its
15581563
// siblings will now still be reachable.
@@ -1587,6 +1592,30 @@ struct SemiNCAInfo {
15871592

15881593
return true;
15891594
}
1595+
1596+
// Check if the given tree is the same as a freshly computed one for the same
1597+
// Parent.
1598+
// Running time: O(N^2), but faster in practise (same as tree construction).
1599+
//
1600+
// Note that this does not check if that the tree construction algorithm is
1601+
// correct and should be only used for fast (but possibly unsound)
1602+
// verification.
1603+
static bool IsSameAsFreshTree(const DomTreeT &DT) {
1604+
DomTreeT FreshTree;
1605+
FreshTree.recalculate(*DT.Parent);
1606+
const bool Different = DT.compare(FreshTree);
1607+
1608+
if (Different) {
1609+
errs() << "DominatorTree is different than a freshly computed one!\n"
1610+
<< "\tCurrent:\n";
1611+
DT.print(errs());
1612+
errs() << "\n\tFreshly computed tree:\n";
1613+
FreshTree.print(errs());
1614+
errs().flush();
1615+
}
1616+
1617+
return !Different;
1618+
}
15901619
};
15911620

15921621
template <class DomTreeT>
@@ -1615,11 +1644,27 @@ void ApplyUpdates(DomTreeT &DT,
16151644
}
16161645

16171646
template <class DomTreeT>
1618-
bool Verify(const DomTreeT &DT) {
1647+
bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL) {
16191648
SemiNCAInfo<DomTreeT> SNCA(nullptr);
1620-
return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) &&
1621-
SNCA.VerifyLevels(DT) && SNCA.verifyParentProperty(DT) &&
1622-
SNCA.verifySiblingProperty(DT) && SNCA.VerifyDFSNumbers(DT);
1649+
const bool InitialChecks = SNCA.verifyRoots(DT) &&
1650+
SNCA.verifyReachability(DT) &&
1651+
SNCA.VerifyLevels(DT) && SNCA.VerifyDFSNumbers(DT);
1652+
1653+
if (!InitialChecks)
1654+
return false;
1655+
1656+
switch (VL) {
1657+
case DomTreeT::VerificationLevel::Fast:
1658+
return SNCA.IsSameAsFreshTree(DT);
1659+
1660+
case DomTreeT::VerificationLevel::Basic:
1661+
return SNCA.verifyParentProperty(DT) && SNCA.IsSameAsFreshTree(DT);
1662+
1663+
case DomTreeT::VerificationLevel::Full:
1664+
return SNCA.verifyParentProperty(DT) && SNCA.verifySiblingProperty(DT);
1665+
}
1666+
1667+
llvm_unreachable("Unhandled DomTree VerificationLevel");
16231668
}
16241669

16251670
} // namespace DomTreeBuilder

llvm/lib/IR/Dominators.cpp

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@
2828
#include <algorithm>
2929
using namespace llvm;
3030

31-
// Always verify dominfo if expensive checking is enabled.
32-
#ifdef EXPENSIVE_CHECKS
33-
bool llvm::VerifyDomInfo = true;
34-
#else
3531
bool llvm::VerifyDomInfo = false;
36-
#endif
3732
static cl::opt<bool, true>
3833
VerifyDomInfoX("verify-dom-info", cl::location(VerifyDomInfo), cl::Hidden,
3934
cl::desc("Verify dominator info (time consuming)"));
4035

36+
#ifdef EXPENSIVE_CHECKS
37+
static constexpr bool ExpensiveChecksEnabled = true;
38+
#else
39+
static constexpr bool ExpensiveChecksEnabled = false;
40+
#endif
41+
4142
bool BasicBlockEdge::isSingleEdge() const {
4243
const TerminatorInst *TI = Start->getTerminator();
4344
unsigned NumEdgesToEnd = 0;
@@ -88,9 +89,11 @@ template void llvm::DomTreeBuilder::ApplyUpdates<DomTreeBuilder::BBPostDomTree>(
8889
DomTreeBuilder::BBPostDomTree &DT, DomTreeBuilder::BBUpdates);
8990

9091
template bool llvm::DomTreeBuilder::Verify<DomTreeBuilder::BBDomTree>(
91-
const DomTreeBuilder::BBDomTree &DT);
92+
const DomTreeBuilder::BBDomTree &DT,
93+
DomTreeBuilder::BBDomTree::VerificationLevel VL);
9294
template bool llvm::DomTreeBuilder::Verify<DomTreeBuilder::BBPostDomTree>(
93-
const DomTreeBuilder::BBPostDomTree &DT);
95+
const DomTreeBuilder::BBPostDomTree &DT,
96+
DomTreeBuilder::BBPostDomTree::VerificationLevel VL);
9497

9598
bool DominatorTree::invalidate(Function &F, const PreservedAnalyses &PA,
9699
FunctionAnalysisManager::Invalidator &) {
@@ -305,24 +308,16 @@ bool DominatorTree::isReachableFromEntry(const Use &U) const {
305308

306309
void DominatorTree::verifyDomTree() const {
307310
// Perform the expensive checks only when VerifyDomInfo is set.
308-
if (VerifyDomInfo && !verify()) {
309-
errs() << "\n~~~~~~~~~~~\n\t\tDomTree verification failed!\n~~~~~~~~~~~\n";
310-
print(errs());
311-
abort();
312-
}
313-
314-
Function &F = *getRoot()->getParent();
311+
VerificationLevel VL = VerificationLevel::Fast;
312+
if (VerifyDomInfo)
313+
VL = VerificationLevel::Full;
314+
else if (ExpensiveChecksEnabled)
315+
VL = VerificationLevel::Basic;
315316

316-
DominatorTree OtherDT;
317-
OtherDT.recalculate(F);
318-
if (compare(OtherDT)) {
319-
errs() << "DominatorTree for function " << F.getName()
320-
<< " is not up to date!\nComputed:\n";
321-
print(errs());
322-
errs() << "\nActual:\n";
323-
OtherDT.print(errs());
317+
if (!verify(VL)) {
318+
errs() << "\n~~~~~~~~~~~\n\t\tDomTree verification failed!\n~~~~~~~~~~~\n";
324319
errs() << "\nCFG:\n";
325-
F.print(errs());
320+
getRoot()->getParent()->print(errs());
326321
errs().flush();
327322
abort();
328323
}
@@ -382,8 +377,8 @@ bool DominatorTreeWrapperPass::runOnFunction(Function &F) {
382377
}
383378

384379
void DominatorTreeWrapperPass::verifyAnalysis() const {
385-
if (VerifyDomInfo)
386-
DT.verifyDomTree();
380+
if (ExpensiveChecksEnabled || VerifyDomInfo)
381+
DT.verifyDomTree();
387382
}
388383

389384
void DominatorTreeWrapperPass::print(raw_ostream &OS, const Module *) const {

0 commit comments

Comments
 (0)