llvm.org GIT mirror llvm / a48e1da
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323298 91177308-0d34-0410-b5e6-96231b3b80d8 Jakub Kuderski 2 years ago
4 changed file(s) with 103 addition(s) and 47 deletion(s). Raw diff Collapse all Expand all
6262 extern template void ApplyUpdates(BBDomTree &DT, BBUpdates);
6363 extern template void ApplyUpdates(BBPostDomTree &DT, BBUpdates);
6464
65 extern template bool Verify(const BBDomTree &DT);
66 extern template bool VerifyPostDomTree>(const BBPostDomTree &DT);
65 extern template bool VerifyDomTree>(const BBDomTree &DT,
66 BBDomTree::VerificationLevel VL);
67 extern template bool Verify(const BBPostDomTree &DT,
68 BBPostDomTree::VerificationLevel VL);
6769 } // namespace DomTreeBuilder
6870
6971 using DomTreeNode = DomTreeNodeBase;
146148 /// Handle invalidation explicitly.
147149 bool invalidate(Function &F, const PreservedAnalyses &PA,
148150 FunctionAnalysisManager::Invalidator &);
149
150 /// \brief Returns *false* if the other dominator tree matches this dominator
151 /// tree.
152 inline bool compare(const DominatorTree &Other) const {
153 const DomTreeNode *R = getRootNode();
154 const DomTreeNode *OtherR = Other.getRootNode();
155 return !R || !OtherR || R->getBlock() != OtherR->getBlock() ||
156 Base::compare(Other);
157 }
158151
159152 // Ensure base-class overloads are visible.
160153 using Base::dominates;
233233 ArrayRef Updates);
234234
235235 template
236 bool Verify(const DomTreeT &DT);
236 bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL);
237237 } // namespace DomTreeBuilder
238238
239239 /// \brief Core dominator tree base class.
258258 static constexpr UpdateKind Insert = UpdateKind::Insert;
259259 static constexpr UpdateKind Delete = UpdateKind::Delete;
260260
261 protected:
261 enum class VerificationLevel { Fast, Basic, Full };
262
263 protected:
262264 // Dominators always have a single root, postdominators can have more.
263265 SmallVector Roots;
264266
314316 /// dominator tree base. Otherwise return true.
315317 bool compare(const DominatorTreeBase &Other) const {
316318 if (Parent != Other.Parent) return true;
319
320 if (Roots.size() != Other.Roots.size())
321 return false;
322
323 if (!std::is_permutation(Roots.begin(), Roots.end(), Other.Roots.begin()))
324 return false;
317325
318326 const DomTreeNodeMapType &OtherDomTreeNodes = Other.DomTreeNodes;
319327 if (DomTreeNodes.size() != OtherDomTreeNodes.size())
749757 DomTreeBuilder::Calculate(*this);
750758 }
751759
752 /// verify - check parent and sibling property
753 bool verify() const { return DomTreeBuilder::Verify(*this); }
754
755 protected:
760 /// verify - checks if the tree is correct. There are 3 level of verification:
761 /// - Full -- verifies if the tree is correct by making sure all the
762 /// properties (including the parent and the sibling property)
763 /// hold.
764 /// Takes O(N^3) time.
765 ///
766 /// - Basic -- checks if the tree is correct, but compares it to a freshly
767 /// constructed tree instead of checking the sibling property.
768 /// Takes O(N^2) time.
769 ///
770 /// - Fast -- checks basic tree structure and compares it with a freshly
771 /// constructed tree.
772 /// Takes O(N^2) time worst case, but is faster in practise (same
773 /// as tree construction).
774 bool verify(VerificationLevel VL = VerificationLevel::Full) const {
775 return DomTreeBuilder::Verify(*this, VL);
776 }
777
778 protected:
756779 void addRoot(NodeT *BB) { this->Roots.push_back(BB); }
757780
758781 void reset() {
12801280 // root which is the function's entry node. A PostDominatorTree can have
12811281 // multiple roots - one for each node with no successors and for infinite
12821282 // loops.
1283 // Running time: O(N).
12831284 bool verifyRoots(const DomTreeT &DT) {
12841285 if (!DT.Parent && !DT.Roots.empty()) {
12851286 errs() << "Tree has no parent but has roots!\n";
13201321 }
13211322
13221323 // Checks if the tree contains all reachable nodes in the input graph.
1324 // Running time: O(N).
13231325 bool verifyReachability(const DomTreeT &DT) {
13241326 clear();
13251327 doFullDFSWalk(DT, AlwaysDescend);
13551357
13561358 // Check if for every parent with a level L in the tree all of its children
13571359 // have level L + 1.
1360 // Running time: O(N).
13581361 static bool VerifyLevels(const DomTreeT &DT) {
13591362 for (auto &NodeToTN : DT.DomTreeNodes) {
13601363 const TreeNodePtr TN = NodeToTN.second.get();
13861389
13871390 // Check if the computed DFS numbers are correct. Note that DFS info may not
13881391 // be valid, and when that is the case, we don't verify the numbers.
1392 // Running time: O(N log(N)).
13891393 static bool VerifyDFSNumbers(const DomTreeT &DT) {
13901394 if (!DT.DFSInfoValid || !DT.Parent)
13911395 return true;
15161520 // linear time, but the algorithms are complex. Instead, we do it in a
15171521 // straightforward N^2 and N^3 way below, using direct path reachability.
15181522
1519
15201523 // Checks if the tree has the parent property: if for all edges from V to W in
15211524 // the input graph, such that V is reachable, the parent of W in the tree is
15221525 // an ancestor of V in the tree.
1526 // Running time: O(N^2).
15231527 //
15241528 // This means that if a node gets disconnected from the graph, then all of
15251529 // the nodes it dominated previously will now become unreachable.
15521556
15531557 // Check if the tree has sibling property: if a node V does not dominate a
15541558 // node W for all siblings V and W in the tree.
1559 // Running time: O(N^3).
15551560 //
15561561 // This means that if a node gets disconnected from the graph, then all of its
15571562 // siblings will now still be reachable.
15861591
15871592 return true;
15881593 }
1594
1595 // Check if the given tree is the same as a freshly computed one for the same
1596 // Parent.
1597 // Running time: O(N^2), but faster in practise (same as tree construction).
1598 //
1599 // Note that this does not check if that the tree construction algorithm is
1600 // correct and should be only used for fast (but possibly unsound)
1601 // verification.
1602 static bool IsSameAsFreshTree(const DomTreeT &DT) {
1603 DomTreeT FreshTree;
1604 FreshTree.recalculate(*DT.Parent);
1605 const bool Different = DT.compare(FreshTree);
1606
1607 if (Different) {
1608 errs() << "DominatorTree is different than a freshly computed one!\n"
1609 << "\tCurrent:\n";
1610 DT.print(errs());
1611 errs() << "\n\tFreshly computed tree:\n";
1612 FreshTree.print(errs());
1613 errs().flush();
1614 }
1615
1616 return !Different;
1617 }
15891618 };
15901619
15911620 template
16141643 }
16151644
16161645 template
1617 bool Verify(const DomTreeT &DT) {
1646 bool Verify(const DomTreeT &DT, typename DomTreeT::VerificationLevel VL) {
16181647 SemiNCAInfo SNCA(nullptr);
1619 return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) &&
1620 SNCA.VerifyLevels(DT) && SNCA.verifyParentProperty(DT) &&
1621 SNCA.verifySiblingProperty(DT) && SNCA.VerifyDFSNumbers(DT);
1648 const bool InitialChecks = SNCA.verifyRoots(DT) &&
1649 SNCA.verifyReachability(DT) &&
1650 SNCA.VerifyLevels(DT) && SNCA.VerifyDFSNumbers(DT);
1651
1652 if (!InitialChecks)
1653 return false;
1654
1655 switch (VL) {
1656 case DomTreeT::VerificationLevel::Fast:
1657 return SNCA.IsSameAsFreshTree(DT);
1658
1659 case DomTreeT::VerificationLevel::Basic:
1660 return SNCA.verifyParentProperty(DT) && SNCA.IsSameAsFreshTree(DT);
1661
1662 case DomTreeT::VerificationLevel::Full:
1663 return SNCA.verifyParentProperty(DT) && SNCA.verifySiblingProperty(DT);
1664 }
1665
1666 llvm_unreachable("Unhandled DomTree VerificationLevel");
16221667 }
16231668
16241669 } // namespace DomTreeBuilder
2727 #include
2828 using namespace llvm;
2929
30 // Always verify dominfo if expensive checking is enabled.
31 #ifdef EXPENSIVE_CHECKS
32 bool llvm::VerifyDomInfo = true;
33 #else
3430 bool llvm::VerifyDomInfo = false;
35 #endif
3631 static cl::opt
3732 VerifyDomInfoX("verify-dom-info", cl::location(VerifyDomInfo), cl::Hidden,
3833 cl::desc("Verify dominator info (time consuming)"));
34
35 #ifdef EXPENSIVE_CHECKS
36 static constexpr bool ExpensiveChecksEnabled = true;
37 #else
38 static constexpr bool ExpensiveChecksEnabled = false;
39 #endif
3940
4041 bool BasicBlockEdge::isSingleEdge() const {
4142 const TerminatorInst *TI = Start->getTerminator();
8788 DomTreeBuilder::BBPostDomTree &DT, DomTreeBuilder::BBUpdates);
8889
8990 template bool llvm::DomTreeBuilder::Verify(
90 const DomTreeBuilder::BBDomTree &DT);
91 const DomTreeBuilder::BBDomTree &DT,
92 DomTreeBuilder::BBDomTree::VerificationLevel VL);
9193 template bool llvm::DomTreeBuilder::Verify(
92 const DomTreeBuilder::BBPostDomTree &DT);
94 const DomTreeBuilder::BBPostDomTree &DT,
95 DomTreeBuilder::BBPostDomTree::VerificationLevel VL);
9396
9497 bool DominatorTree::invalidate(Function &F, const PreservedAnalyses &PA,
9598 FunctionAnalysisManager::Invalidator &) {
304307
305308 void DominatorTree::verifyDomTree() const {
306309 // Perform the expensive checks only when VerifyDomInfo is set.
307 if (VerifyDomInfo && !verify()) {
310 VerificationLevel VL = VerificationLevel::Fast;
311 if (VerifyDomInfo)
312 VL = VerificationLevel::Full;
313 else if (ExpensiveChecksEnabled)
314 VL = VerificationLevel::Basic;
315
316 if (!verify(VL)) {
308317 errs() << "\n~~~~~~~~~~~\n\t\tDomTree verification failed!\n~~~~~~~~~~~\n";
309 print(errs());
310 abort();
311 }
312
313 Function &F = *getRoot()->getParent();
314
315 DominatorTree OtherDT;
316 OtherDT.recalculate(F);
317 if (compare(OtherDT)) {
318 errs() << "DominatorTree for function " << F.getName()
319 << " is not up to date!\nComputed:\n";
320 print(errs());
321 errs() << "\nActual:\n";
322 OtherDT.print(errs());
323318 errs() << "\nCFG:\n";
324 F.print(errs());
319 getRoot()->getParent()->print(errs());
325320 errs().flush();
326321 abort();
327322 }
381376 }
382377
383378 void DominatorTreeWrapperPass::verifyAnalysis() const {
384 if (VerifyDomInfo)
385 DT.verifyDomTree();
379 if (ExpensiveChecksEnabled || VerifyDomInfo)
380 DT.verifyDomTree();
386381 }
387382
388383 void DominatorTreeWrapperPass::print(raw_ostream &OS, const Module *) const {