}
/// findNearestCommonDominator - Find nearest common dominator basic block
- /// for basic block A and B. If there is no such block then return NULL.
+ /// for basic block A and B. If there is no such block then return nullptr.
NodeT *findNearestCommonDominator(NodeT *A, NodeT *B) const {
+ assert(A && B && "Pointers are not valid");
assert(A->getParent() == B->getParent() &&
"Two blocks are not in same function");
// If either A or B is a entry block then it is nearest common dominator
// (for forward-dominators).
- if (!this->isPostDominator()) {
+ if (!isPostDominator()) {
NodeT &Entry = A->getParent()->front();
if (A == &Entry || B == &Entry)
return &Entry;
}
DomTreeNodes.erase(BB);
+
+ if (!IsPostDom) return;
+
+ // Remember to update PostDominatorTree roots.
+ auto RIt = llvm::find(Roots, BB);
+ if (RIt != Roots.end()) {
+ std::swap(*RIt, Roots.back());
+ Roots.pop_back();
+ }
}
/// splitBlock - BB is split and now it has one successor. Update dominator
///
void print(raw_ostream &O) const {
O << "=============================--------------------------------\n";
- if (this->isPostDominator())
+ if (IsPostDominator)
O << "Inorder PostDominator Tree: ";
else
O << "Inorder Dominator Tree: ";
// The postdom tree can have a null root if there are no returns.
if (getRootNode()) PrintDomTree<NodeT>(getRootNode(), O, 1);
+ if (IsPostDominator) {
+ O << "Roots: ";
+ for (const NodePtr Block : Roots) {
+ Block->printAsOperand(O, false);
+ O << " ";
+ }
+ O << "\n";
+ }
}
public:
using NodePtr = typename DomTreeT::NodePtr;
using NodeT = typename DomTreeT::NodeType;
using TreeNodePtr = DomTreeNodeBase<NodeT> *;
+ using RootsT = decltype(DomTreeT::Roots);
static constexpr bool IsPostDom = DomTreeT::IsPostDominator;
// Information record used by Semi-NCA during tree construction.
// Custom DFS implementation which can skip nodes based on a provided
// predicate. It also collects ReverseChildren so that we don't have to spend
// time getting predecessors in SemiNCA.
- template <typename DescendCondition>
+ //
+ // If IsReverse is set to true, the DFS walk will be performed backwards
+ // relative to IsPostDom -- using reverse edges for dominators and forward
+ // edges for postdominators.
+ template <bool IsReverse = false, typename DescendCondition>
unsigned runDFS(NodePtr V, unsigned LastNum, DescendCondition Condition,
unsigned AttachToNum) {
assert(V);
BBInfo.Label = BB;
NumToNode.push_back(BB);
- for (const NodePtr Succ : ChildrenGetter<NodePtr, IsPostDom>::Get(BB)) {
+ constexpr bool Direction = IsReverse != IsPostDom; // XOR.
+ for (const NodePtr Succ : ChildrenGetter<NodePtr, Direction>::Get(BB)) {
const auto SIT = NodeToInfo.find(Succ);
// Don't visit nodes more than once but remember to collect
// ReverseChildren.
}
}
- template <typename DescendCondition>
- unsigned doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) {
- unsigned Num = 0;
+ // PostDominatorTree always has a virtual root that represents a virtual CFG
+ // node that serves as a single exit from the function. All the other exits
+ // (CFG nodes with terminators and nodes in infinite loops are logically
+ // connected to this virtual CFG exit node).
+ // This functions maps a nullptr CFG node to the virtual root tree node.
+ void addVirtualRoot() {
+ assert(IsPostDom && "Only postdominators have a virtual root");
+ assert(NumToNode.size() == 1 && "SNCAInfo must be freshly constructed");
- // If the DT is a PostDomTree, always add a virtual root.
- if (IsPostDom) {
- auto &BBInfo = NodeToInfo[nullptr];
- BBInfo.DFSNum = BBInfo.Semi = ++Num;
- BBInfo.Label = nullptr;
+ auto &BBInfo = NodeToInfo[nullptr];
+ BBInfo.DFSNum = BBInfo.Semi = 1;
+ BBInfo.Label = nullptr;
- NumToNode.push_back(nullptr); // NumToNode[n] = V;
- }
+ NumToNode.push_back(nullptr); // NumToNode[1] = nullptr;
+ }
- const unsigned InitialNum = Num;
- for (auto *Root : DT.Roots) Num = runDFS(Root, Num, DC, InitialNum);
+ // For postdominators, nodes with no forward successors are trivial roots that
+ // are always selected as tree roots. Roots with forward successors correspond
+ // to CFG nodes within infinite loops.
+ static bool HasForwardSuccessors(const NodePtr N) {
+ assert(N && "N must be a valid node");
+ using TraitsTy = GraphTraits<typename DomTreeT::ParentPtr>;
+ return TraitsTy::child_begin(N) != TraitsTy::child_end(N);
+ }
- return Num;
+ static NodePtr GetEntryNode(const DomTreeT &DT) {
+ assert(DT.Parent && "Parent not set");
+ return GraphTraits<typename DomTreeT::ParentPtr>::getEntryNode(DT.Parent);
}
- static void FindAndAddRoots(DomTreeT &DT) {
+ // Finds all roots without relaying on the set of roots already stored in the
+ // tree.
+ // We define roots to be some non-redundant set of the CFG nodes
+ static RootsT FindRoots(const DomTreeT &DT) {
assert(DT.Parent && "Parent pointer is not set");
- using TraitsTy = GraphTraits<typename DomTreeT::ParentPtr>;
+ RootsT Roots;
+ // For dominators, function entry CFG node is always a tree root node.
+ if (!IsPostDom) {
+ Roots.push_back(GetEntryNode(DT));
+ return Roots;
+ }
+
+ SemiNCAInfo SNCA;
+
+ // PostDominatorTree always has a virtual root.
+ SNCA.addVirtualRoot();
+ unsigned Num = 1;
+
+ DEBUG(dbgs() << "\t\tLooking for trivial roots\n");
+
+ // Step #1: Find all the trivial roots that are going to will definitely
+ // remain tree roots.
+ unsigned Total = 0;
+ for (const NodePtr N : nodes(DT.Parent)) {
+ ++Total;
+ // If it has no *successors*, it is definitely a root.
+ if (!HasForwardSuccessors(N)) {
+ Roots.push_back(N);
+ // Run DFS not to walk this part of CFG later.
+ Num = SNCA.runDFS(N, Num, AlwaysDescend, 1);
+ DEBUG(dbgs() << "Found a new trivial root: " << BlockNamePrinter(N)
+ << "\n");
+ DEBUG(dbgs() << "Last visited node: "
+ << BlockNamePrinter(SNCA.NumToNode[Num]) << "\n");
+ }
+ }
+
+ DEBUG(dbgs() << "\t\tLooking for non-trivial roots\n");
+
+ // Step #2: Find all non-trivial root candidates. Those are CFG nodes that
+ // are reverse-unreachable were not visited by previous DFS walks (i.e. CFG
+ // nodes in infinite loops).
+ bool HasNonTrivialRoots = false;
+ // Accounting for the virtual exit, see if we had any reverse-unreachable
+ // nodes.
+ if (Total + 1 != Num) {
+ HasNonTrivialRoots = true;
+ // Make another DFS pass over all other nodes to find the
+ // reverse-unreachable blocks, and find the furthest paths we'll be able
+ // to make.
+ // Note that this looks N^2, but it's really 2N worst case, if every node
+ // is unreachable. This is because we are still going to only visit each
+ // unreachable node once, we may just visit it in two directions,
+ // depending on how lucky we get.
+ SmallPtrSet<NodePtr, 4> ConnectToExitBlock;
+ for (const NodePtr I : nodes(DT.Parent)) {
+ if (SNCA.NodeToInfo.count(I) == 0) {
+ DEBUG(dbgs() << "\t\t\tVisiting node " << BlockNamePrinter(I)
+ << "\n");
+ // Find the furthest away we can get by following successors, then
+ // follow them in reverse. This gives us some reasonable answer about
+ // the post-dom tree inside any infinite loop. In particular, it
+ // guarantees we get to the farthest away point along *some*
+ // path. This also matches the GCC's behavior.
+ // If we really wanted a totally complete picture of dominance inside
+ // this infinite loop, we could do it with SCC-like algorithms to find
+ // the lowest and highest points in the infinite loop. In theory, it
+ // would be nice to give the canonical backedge for the loop, but it's
+ // expensive and does not always lead to a minimal set of roots.
+ DEBUG(dbgs() << "\t\t\tRunning forward DFS\n");
+
+ const unsigned NewNum = SNCA.runDFS<true>(I, Num, AlwaysDescend, Num);
+ const NodePtr FurthestAway = SNCA.NumToNode[NewNum];
+ DEBUG(dbgs() << "\t\t\tFound a new furthest away node "
+ << "(non-trivial root): "
+ << BlockNamePrinter(FurthestAway) << "\n");
+ ConnectToExitBlock.insert(FurthestAway);
+ Roots.push_back(FurthestAway);
+ DEBUG(dbgs() << "\t\t\tPrev DFSNum: " << Num << ", new DFSNum: "
+ << NewNum << "\n\t\t\tRemoving DFS info\n");
+ for (unsigned i = NewNum; i > Num; --i) {
+ const NodePtr N = SNCA.NumToNode[i];
+ DEBUG(dbgs() << "\t\t\t\tRemoving DFS info for "
+ << BlockNamePrinter(N) << "\n");
+ SNCA.NodeToInfo.erase(N);
+ SNCA.NumToNode.pop_back();
+ }
+ const unsigned PrevNum = Num;
+ DEBUG(dbgs() << "\t\t\tRunning reverse DFS\n");
+ Num = SNCA.runDFS(FurthestAway, Num, AlwaysDescend, 1);
+ for (unsigned i = PrevNum + 1; i <= Num; ++i)
+ DEBUG(dbgs() << "\t\t\t\tfound node "
+ << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
+ }
+ }
+ }
+
+ DEBUG(dbgs() << "Total: " << Total << ", Num: " << Num << "\n");
+ DEBUG(dbgs() << "Discovered CFG nodes:\n");
+ DEBUG(for (size_t i = 0; i <= Num; ++i) dbgs()
+ << i << ": " << BlockNamePrinter(SNCA.NumToNode[i]) << "\n");
+
+ assert((Total + 1 == Num) && "Everything should have been visited");
+
+ // Step #3: If we found some non-trivial roots, make them non-redundant.
+ if (HasNonTrivialRoots) RemoveRedundantRoots(DT, Roots);
+
+ DEBUG(dbgs() << "Found roots: ");
+ DEBUG(for (auto *Root : Roots) dbgs() << BlockNamePrinter(Root) << " ");
+ DEBUG(dbgs() << "\n");
+
+ return Roots;
+ }
+
+ // This function only makes sense for postdominators.
+ // We define roots to be some set of CFG nodes where (reverse) DFS walks have
+ // to start in order to visit all the CFG nodes (including the
+ // reverse-unreachable ones).
+ // When the search for non-trivial roots is done it may happen that some of
+ // the non-trivial roots are reverse-reachable from other non-trivial roots,
+ // which makes them redundant. This function removes them from the set of
+ // input roots.
+ static void RemoveRedundantRoots(const DomTreeT &DT, RootsT &Roots) {
+ assert(IsPostDom && "This function is for postdominators only");
+ DEBUG(dbgs() << "Removing redundant roots\n");
+
+ SemiNCAInfo SNCA;
+
+ for (unsigned i = 0; i < Roots.size(); ++i) {
+ auto &Root = Roots[i];
+ // Trivial roots are always non-redundant.
+ if (!HasForwardSuccessors(Root)) continue;
+ DEBUG(dbgs() << "\tChecking if " << BlockNamePrinter(Root)
+ << " remains a root\n");
+ SNCA.clear();
+ // Do a forward walk looking for the other roots.
+ const unsigned Num = SNCA.runDFS<true>(Root, 0, AlwaysDescend, 0);
+ // Skip the start node and begin from the second one (note that DFS uses
+ // 1-based indexing).
+ for (unsigned x = 2; x <= Num; ++x) {
+ const NodePtr N = SNCA.NumToNode[x];
+ // If we wound another root in a (forward) DFS walk, remove the current
+ // root from the set of roots, as it is reverse-reachable from the other
+ // one.
+ if (llvm::find(Roots, N) != Roots.end()) {
+ DEBUG(dbgs() << "\tForward DFS walk found another root "
+ << BlockNamePrinter(N) << "\n\tRemoving root "
+ << BlockNamePrinter(Root) << "\n");
+ std::swap(Root, Roots.back());
+ Roots.pop_back();
+
+ // Root at the back takes the current root's place.
+ // Start the next loop iteration with the same index.
+ --i;
+ break;
+ }
+ }
+ }
+ }
+
+ template <typename DescendCondition>
+ void doFullDFSWalk(const DomTreeT &DT, DescendCondition DC) {
if (!IsPostDom) {
- // Dominators have a single root that is the function's entry.
- NodeT *entry = TraitsTy::getEntryNode(DT.Parent);
- DT.addRoot(entry);
- } else {
- // Initialize the roots list for PostDominators.
- for (auto *Node : nodes(DT.Parent))
- if (TraitsTy::child_begin(Node) == TraitsTy::child_end(Node))
- DT.addRoot(Node);
+ assert(DT.Roots.size() == 1 && "Dominators should have a singe root");
+ runDFS(DT.Roots[0], 0, DC, 0);
+ return;
}
+
+ addVirtualRoot();
+ unsigned Num = 1;
+ for (const NodePtr Root : DT.Roots) Num = runDFS(Root, Num, DC, 0);
}
void calculateFromScratch(DomTreeT &DT) {
// Step #0: Number blocks in depth-first order and initialize variables used
// in later stages of the algorithm.
- FindAndAddRoots(DT);
+ DT.Roots = FindRoots(DT);
doFullDFSWalk(DT, AlwaysDescend);
runSemiNCA(DT);
NodePtr ImmDom = getIDom(W);
- // Get or calculate the node for the immediate dominator
+ // Get or calculate the node for the immediate dominator.
TreeNodePtr IDomNode = getNodeForBlock(ImmDom, DT);
// Add a new tree node for this BasicBlock, and link it as a child of
- // IDomNode
+ // IDomNode.
DT.DomTreeNodes[W] = IDomNode->addChild(
llvm::make_unique<DomTreeNodeBase<NodeT>>(W, IDomNode));
}
};
static void InsertEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
- assert(From && To && "Cannot connect nullptrs");
+ assert((From || IsPostDom) &&
+ "From has to be a valid CFG node or a virtual root");
+ assert(To && "Cannot be a nullptr");
DEBUG(dbgs() << "Inserting edge " << BlockNamePrinter(From) << " -> "
<< BlockNamePrinter(To) << "\n");
- const TreeNodePtr FromTN = DT.getNode(From);
-
- // Ignore edges from unreachable nodes.
- if (!FromTN) return;
+ TreeNodePtr FromTN = DT.getNode(From);
+
+ if (!FromTN) {
+ // Ignore edges from unreachable nodes for (forward) dominators.
+ if (!IsPostDom) return;
+
+ // The unreachable node becomes a new root -- a tree node for it.
+ TreeNodePtr VirtualRoot = DT.getNode(nullptr);
+ FromTN =
+ (DT.DomTreeNodes[From] = VirtualRoot->addChild(
+ llvm::make_unique<DomTreeNodeBase<NodeT>>(From, VirtualRoot)))
+ .get();
+ DT.Roots.push_back(From);
+ }
DT.DFSInfoValid = false;
InsertReachable(DT, FromTN, ToTN);
}
+ // Determines if some existing root becomes reverse-reachable after the
+ // insertion. Rebuilds the whole tree if that situation happens.
+ static bool UpdateRootsBeforeInsertion(DomTreeT &DT, const TreeNodePtr From,
+ const TreeNodePtr To) {
+ assert(IsPostDom && "This function is only for postdominators");
+ // Destination node is not attached to the virtual root, so it cannot be a
+ // root.
+ if (!DT.isVirtualRoot(To->getIDom())) return false;
+
+ auto RIt = llvm::find(DT.Roots, To->getBlock());
+ if (RIt == DT.Roots.end())
+ return false; // To is not a root, nothing to update.
+
+ DEBUG(dbgs() << "\t\tAfter the insertion, " << BlockNamePrinter(To)
+ << " is no longer a root\n\t\tRebuilding the tree!!!\n");
+
+ DT.recalculate(*DT.Parent);
+ return true;
+ }
+
+ // Updates the set of roots after insertion or deletion. This ensures that
+ // roots are the same when after a series of updates and when the tree would
+ // be built from scratch.
+ static void UpdateRootsAfterUpdate(DomTreeT &DT) {
+ assert(IsPostDom && "This function is only for postdominators");
+
+ // The tree has only trivial roots -- nothing to update.
+ if (std::none_of(DT.Roots.begin(), DT.Roots.end(), HasForwardSuccessors))
+ return;
+
+ // Recalculate the set of roots.
+ DT.Roots = FindRoots(DT);
+ for (const NodePtr R : DT.Roots) {
+ const TreeNodePtr TN = DT.getNode(R);
+ // A CFG node was selected as a tree root, but the corresponding tree node
+ // is not connected to the virtual root. This is because the incremental
+ // algorithm does not really know or use the set of roots and can make a
+ // different (implicit) decision about which nodes within an infinite loop
+ // becomes a root.
+ if (DT.isVirtualRoot(TN->getIDom())) {
+ DEBUG(dbgs() << "Root " << BlockNamePrinter(R)
+ << " is not virtual root's child\n"
+ << "The entire tree needs to be rebuilt\n");
+ // It should be possible to rotate the subtree instead of recalculating
+ // the whole tree, but this situation happens extremely rarely in
+ // practice.
+ DT.recalculate(*DT.Parent);
+ return;
+ }
+ }
+ }
+
// Handles insertion to a node already in the dominator tree.
static void InsertReachable(DomTreeT &DT, const TreeNodePtr From,
const TreeNodePtr To) {
DEBUG(dbgs() << "\tReachable " << BlockNamePrinter(From->getBlock())
<< " -> " << BlockNamePrinter(To->getBlock()) << "\n");
+ if (IsPostDom && UpdateRootsBeforeInsertion(DT, From, To)) return;
+ // DT.findNCD expects both pointers to be valid. When From is a virtual
+ // root, then its CFG block pointer is a nullptr, so we have to 'compute'
+ // the NCD manually.
const NodePtr NCDBlock =
- DT.findNearestCommonDominator(From->getBlock(), To->getBlock());
+ (From->getBlock() && To->getBlock())
+ ? DT.findNearestCommonDominator(From->getBlock(), To->getBlock())
+ : nullptr;
assert(NCDBlock || DT.isPostDominator());
const TreeNodePtr NCD = DT.getNode(NCDBlock);
assert(NCD);
}
UpdateLevelsAfterInsertion(II);
+ if (IsPostDom) UpdateRootsAfterUpdate(DT);
}
static void UpdateLevelsAfterInsertion(InsertionInfo &II) {
DEBUG(DT.print(dbgs()));
}
- // Checks if the tree contains all reachable nodes in the input graph.
- bool verifyReachability(const DomTreeT &DT) {
- clear();
- doFullDFSWalk(DT, AlwaysDescend);
-
- for (auto &NodeToTN : DT.DomTreeNodes) {
- const TreeNodePtr TN = NodeToTN.second.get();
- const NodePtr BB = TN->getBlock();
-
- // Virtual root has a corresponding virtual CFG node.
- if (DT.isVirtualRoot(TN)) continue;
-
- if (NodeToInfo.count(BB) == 0) {
- errs() << "DomTree node " << BlockNamePrinter(BB)
- << " not found by DFS walk!\n";
- errs().flush();
-
- return false;
- }
- }
-
- for (const NodePtr N : NumToNode) {
- if (N && !DT.getNode(N)) {
- errs() << "CFG node " << BlockNamePrinter(N)
- << " not found in the DomTree!\n";
- errs().flush();
-
- return false;
- }
- }
-
- return true;
- }
-
static void DeleteEdge(DomTreeT &DT, const NodePtr From, const NodePtr To) {
assert(From && To && "Cannot disconnect nullptrs");
DEBUG(dbgs() << "Deleting edge " << BlockNamePrinter(From) << " -> "
DeleteReachable(DT, FromTN, ToTN);
else
DeleteUnreachable(DT, ToTN);
+
+ if (IsPostDom) UpdateRootsAfterUpdate(DT);
}
// Handles deletions that leave destination nodes reachable.
assert(ToTN);
assert(ToTN->getBlock());
+ if (IsPostDom) {
+ // Deletion makes a region reverse-unreachable and creates a new root.
+ // Simulate that by inserting an edge from the virtual root to ToTN and
+ // adding it as a new root.
+ DEBUG(dbgs() << "\tDeletion made a region reverse-unreachable\n");
+ DEBUG(dbgs() << "\tAdding new root " << BlockNamePrinter(ToTN) << "\n");
+ DT.Roots.push_back(ToTN->getBlock());
+ InsertReachable(DT, DT.getNode(nullptr), ToTN);
+ return;
+ }
+
SmallVector<NodePtr, 16> AffectedQueue;
const unsigned Level = ToTN->getLevel();
//===--------------- DomTree correctness verification ---------------------===
//~~
+ // Check if the tree has correct roots. A DominatorTree always has a single
+ // root which is the function's entry node. A PostDominatorTree can have
+ // multiple roots - one for each node with no successors and for infinite
+ // loops.
+ bool verifyRoots(const DomTreeT &DT) {
+ if (!DT.Parent && !DT.Roots.empty()) {
+ errs() << "Tree has no parent but has roots!\n";
+ errs().flush();
+ return false;
+ }
+
+ if (!IsPostDom) {
+ if (DT.Roots.empty()) {
+ errs() << "Tree doesn't have a root!\n";
+ errs().flush();
+ return false;
+ }
+
+ if (DT.getRoot() != GetEntryNode(DT)) {
+ errs() << "Tree's root is not its parent's entry node!\n";
+ errs().flush();
+ return false;
+ }
+ }
+
+ RootsT ComputedRoots = FindRoots(DT);
+ if (DT.Roots.size() != ComputedRoots.size() ||
+ !std::is_permutation(DT.Roots.begin(), DT.Roots.end(),
+ ComputedRoots.begin())) {
+ errs() << "Tree has different roots than freshly computed ones!\n";
+ errs() << "\tPDT roots: ";
+ for (const NodePtr N : DT.Roots) errs() << BlockNamePrinter(N) << ", ";
+ errs() << "\n\tComputed roots: ";
+ for (const NodePtr N : ComputedRoots)
+ errs() << BlockNamePrinter(N) << ", ";
+ errs() << "\n";
+ errs().flush();
+ return false;
+ }
+
+ return true;
+ }
+
+ // Checks if the tree contains all reachable nodes in the input graph.
+ bool verifyReachability(const DomTreeT &DT) {
+ clear();
+ doFullDFSWalk(DT, AlwaysDescend);
+
+ for (auto &NodeToTN : DT.DomTreeNodes) {
+ const TreeNodePtr TN = NodeToTN.second.get();
+ const NodePtr BB = TN->getBlock();
+
+ // Virtual root has a corresponding virtual CFG node.
+ if (DT.isVirtualRoot(TN)) continue;
+
+ if (NodeToInfo.count(BB) == 0) {
+ errs() << "DomTree node " << BlockNamePrinter(BB)
+ << " not found by DFS walk!\n";
+ errs().flush();
+
+ return false;
+ }
+ }
+
+ for (const NodePtr N : NumToNode) {
+ if (N && !DT.getNode(N)) {
+ errs() << "CFG node " << BlockNamePrinter(N)
+ << " not found in the DomTree!\n";
+ errs().flush();
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
// Check if for every parent with a level L in the tree all of its children
// have level L + 1.
static bool VerifyLevels(const DomTreeT &DT) {
const NodePtr BB = TN->getBlock();
if (!BB || TN->getChildren().empty()) continue;
+ DEBUG(dbgs() << "Verifying parent property of node "
+ << BlockNamePrinter(TN) << "\n");
clear();
doFullDFSWalk(DT, [BB](NodePtr From, NodePtr To) {
return From != BB && To != BB;
template <class DomTreeT>
bool Verify(const DomTreeT &DT) {
SemiNCAInfo<DomTreeT> SNCA;
- return SNCA.verifyReachability(DT) && SNCA.VerifyLevels(DT) &&
- SNCA.verifyNCD(DT) && SNCA.verifyParentProperty(DT) &&
- SNCA.verifySiblingProperty(DT);
+ return SNCA.verifyRoots(DT) && SNCA.verifyReachability(DT) &&
+ SNCA.VerifyLevels(DT) && SNCA.verifyNCD(DT) &&
+ SNCA.verifyParentProperty(DT) && SNCA.verifySiblingProperty(DT);
}
} // namespace DomTreeBuilder
}
}
- // Mark blocks live if there is no path from the block to the
- // return of the function or a successor for which this is true.
- // This protects IDFCalculator which cannot handle such blocks.
- for (auto &BBInfoPair : BlockInfo) {
- auto &BBInfo = BBInfoPair.second;
- if (BBInfo.terminatorIsLive())
- continue;
- auto *BB = BBInfo.BB;
- if (!PDT.getNode(BB)) {
- DEBUG(dbgs() << "Not post-dominated by return: " << BB->getName()
+ // Mark blocks live if there is no path from the block to a
+ // return of the function.
+ // We do this by seeing which of the postdomtree root children exit the
+ // program, and for all others, mark the subtree live.
+ for (auto &PDTChild : children<DomTreeNode *>(PDT.getRootNode())) {
+ auto *BB = PDTChild->getBlock();
+ auto &Info = BlockInfo[BB];
+ // Real function return
+ if (isa<ReturnInst>(Info.Terminator)) {
+ DEBUG(dbgs() << "post-dom root child is a return: " << BB->getName()
<< '\n';);
- markLive(BBInfo.Terminator);
continue;
}
- for (auto *Succ : successors(BB))
- if (!PDT.getNode(Succ)) {
- DEBUG(dbgs() << "Successor not post-dominated by return: "
- << BB->getName() << '\n';);
- markLive(BBInfo.Terminator);
- break;
- }
+
+ // This child is something else, like an infinite loop.
+ for (auto DFNode : depth_first(PDTChild))
+ markLive(BlockInfo[DFNode->getBlock()].Terminator);
}
// Treat the entry block as always live
--- /dev/null
+; RUN: opt < %s -postdomtree -analyze | FileCheck %s
+; RUN: opt < %s -passes='print<postdomtree>' 2>&1 | FileCheck %s
+
+@a = external global i32, align 4
+
+define void @fn1() {
+entry:
+ store i32 5, i32* @a, align 4
+ %call = call i32 (...) @foo()
+ %tobool = icmp ne i32 %call, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ br label %loop
+
+loop: ; preds = %loop, %if.then
+ br label %loop
+
+if.end: ; preds = %entry
+ store i32 6, i32* @a, align 4
+ ret void
+}
+
+declare i32 @foo(...)
+
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK: [2] %loop
+; CHECK-NEXT: [3] %if.then
+; CHECK: Roots: %if.end %loop
--- /dev/null
+; RUN: opt < %s -postdomtree -analyze | FileCheck %s
+; RUN: opt < %s -passes='print<postdomtree>' 2>&1 | FileCheck %s
+
+@a = external global i32, align 4
+
+define void @fn1() {
+entry:
+ store i32 5, i32* @a, align 4
+ %call = call i32 (...) @foo()
+ %tobool = icmp ne i32 %call, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ br label %loop
+
+loop: ; preds = %loop, %if.then
+ %0 = load i32, i32* @a, align 4
+ call void @bar(i32 %0)
+ br label %loop
+
+if.end: ; preds = %entry
+ store i32 6, i32* @a, align 4
+ ret void
+}
+
+declare i32 @foo(...)
+declare void @bar(i32)
+
+
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK: [2] %loop
+; CHECK-NEXT: [3] %if.then
+; CHECK: Roots: %if.end %loop
--- /dev/null
+; RUN: opt < %s -postdomtree -analyze | FileCheck %s
+; RUN: opt < %s -passes='print<postdomtree>' 2>&1 | FileCheck %s
+
+@a = external global i32, align 4
+
+define void @fn1() {
+entry:
+ store i32 5, i32* @a, align 4
+ %call = call i32 (...) @foo()
+ %tobool = icmp ne i32 %call, 0
+ br i1 %tobool, label %if.then, label %if.end
+
+if.then: ; preds = %entry, %loop
+ br label %loop
+
+loop: ; preds = %loop, %if.then
+ %0 = load i32, i32* @a, align 4
+ call void @bar(i32 %0)
+ br i1 true, label %loop, label %if.then
+
+if.end: ; preds = %entry
+ store i32 6, i32* @a, align 4
+ ret void
+}
+
+declare i32 @foo(...)
+declare void @bar(i32)
+
+
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK: [2] %loop
+; CHECK-NEXT: [3] %if.then
+; CHECK: Roots: %if.end %loop
--- /dev/null
+; RUN: opt < %s -postdomtree -analyze | FileCheck %s
+; RUN: opt < %s -passes='print<postdomtree>' 2>&1 | FileCheck %s
+
+; Function Attrs: nounwind ssp uwtable
+define void @foo() {
+ br label %1
+
+; <label>:1 ; preds = %0, %1
+ br label %1
+ ; No predecessors!
+ ret void
+}
+
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK-NEXT: [2] %2
+; CHECK-NEXT: [2] %1
+; CHECK-NEXT: [3] %0
\ No newline at end of file
bb35:
ret void
}
-; CHECK: [3] %entry
+
+;CHECK:Inorder PostDominator Tree:
+;CHECK-NEXT: [1] <<exit node>>
+;CHECK-NEXT: [2] %bb35
+;CHECK-NEXT: [3] %bb35.loopexit3
+;CHECK-NEXT: [2] %entry
+;CHECK-NEXT: [2] %bb3.i
bb35:
ret void
}
-; CHECK: [4] %entry
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK-NEXT: [2] %bb35
+; CHECK-NEXT: [3] %bb35.loopexit3
+; CHECK-NEXT: [2] %a
+; CHECK-NEXT: [2] %entry
+; CHECK-NEXT: [2] %bb3.i
\ No newline at end of file
bb35:
ret void
}
-; CHECK: [3] %entry
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK-NEXT: [2] %bb35
+; CHECK-NEXT: [3] %bb
+; CHECK-NEXT: [3] %bb.i
+; CHECK-NEXT: [3] %_float32_unpack.exit
+; CHECK-NEXT: [3] %bb.i5
+; CHECK-NEXT: [3] %_float32_unpack.exit8
+; CHECK-NEXT: [3] %bb32.preheader
+; CHECK-NEXT: [3] %bb3
+; CHECK-NEXT: [3] %bb3.split.us
+; CHECK-NEXT: [3] %bb.i4.us
+; CHECK-NEXT: [3] %bb7.i.us
+; CHECK-NEXT: [3] %bb.i4.us.backedge
+; CHECK-NEXT: [3] %bb1.i.us
+; CHECK-NEXT: [3] %bb6.i.us
+; CHECK-NEXT: [3] %bb4.i.us
+; CHECK-NEXT: [3] %bb8.i.us
+; CHECK-NEXT: [3] %bb3.i.loopexit.us
+; CHECK-NEXT: [3] %bb.nph21
+; CHECK-NEXT: [3] %bb4
+; CHECK-NEXT: [3] %bb5
+; CHECK-NEXT: [3] %bb14.preheader
+; CHECK-NEXT: [3] %bb.nph18
+; CHECK-NEXT: [3] %bb8.us.preheader
+; CHECK-NEXT: [3] %bb8.preheader
+; CHECK-NEXT: [3] %bb8.us
+; CHECK-NEXT: [3] %bb8
+; CHECK-NEXT: [3] %bb15.loopexit
+; CHECK-NEXT: [3] %bb15.loopexit2
+; CHECK-NEXT: [3] %bb15
+; CHECK-NEXT: [3] %bb16
+; CHECK-NEXT: [3] %bb17.loopexit.split
+; CHECK-NEXT: [3] %bb.nph14
+; CHECK-NEXT: [3] %bb19
+; CHECK-NEXT: [3] %bb20
+; CHECK-NEXT: [3] %bb29.preheader
+; CHECK-NEXT: [3] %bb.nph
+; CHECK-NEXT: [3] %bb23.us.preheader
+; CHECK-NEXT: [3] %bb23.preheader
+; CHECK-NEXT: [3] %bb23.us
+; CHECK-NEXT: [3] %bb23
+; CHECK-NEXT: [3] %bb30.loopexit
+; CHECK-NEXT: [3] %bb30.loopexit1
+; CHECK-NEXT: [3] %bb30
+; CHECK-NEXT: [3] %bb31
+; CHECK-NEXT: [3] %bb35.loopexit
+; CHECK-NEXT: [3] %bb35.loopexit3
+; CHECK-NEXT: [2] %entry
+; CHECK-NEXT: [2] %bb3.i
+; CHECK-NEXT: Roots: %bb35 %bb3.i
\ No newline at end of file
bb35:
ret void
}
-; CHECK: [4] %entry
+; CHECK: Inorder PostDominator Tree:
+; CHECK-NEXT: [1] <<exit node>>
+; CHECK-NEXT: [2] %bb35
+; CHECK-NEXT: [3] %bb35.loopexit3
+; CHECK-NEXT: [2] %c
+; CHECK-NEXT: [3] %a
+; CHECK-NEXT: [3] %entry
+; CHECK-NEXT: [3] %b
+; CHECK-NEXT: [2] %bb3.i
\ No newline at end of file
}
; CHECK-NOT: =>
; CHECK: [0] 0 => <Function Return>
-; CHECK: [1] 1 => 4
-; STAT: 2 region - The # of regions
-; STAT: 1 region - The # of simple regions
+; STAT: 1 region - The # of regions
\ No newline at end of file
}
; CHECK-NOT: =>
; CHECK: [0] 0 => <Function Return>
-; CHECK: [1] 1 => 3
-; STAT: 2 region - The # of regions
-; STAT: 1 region - The # of simple regions
+; CHECK-NOT: [1]
+; STAT: 1 region - The # of regions
-; BBIT: 0, 1, 2, 5, 11, 6, 12, 3, 4,
-; BBIT: 1, 2, 5, 11, 6, 12,
+; BBIT: 0, 1, 2, 5, 11, 6, 12, 3, 4,
-; RNIT: 0, 1 => 3, 3, 4,
-; RNIT: 1, 2, 5, 11, 6, 12,
+; RNIT: 0, 1, 2, 5, 11, 6, 12, 3, 4,
ret void
}
; CHECK-NOT: =>
-; CHECK: [0] 0 => <Function Return>
-; CHECK-NEXT: [1] 1 => 3
-; CHECK-NEXT: [1] 7 => 1
-; STAT: 3 region - The # of regions
-; STAT: 2 region - The # of simple regions
+; CHECK:[0] 0 => <Function Return>
+; CHECK-NOT: [1]
+; STAT: 1 region - The # of regions
; BBIT: 0, 7, 1, 2, 5, 11, 6, 12, 3, 4, 8, 9, 13, 10, 14,
-; BBIT: 7, 8, 9, 13, 10, 14,
-; BBIT: 1, 2, 5, 11, 6, 12,
-; RNIT: 0, 7 => 1, 1 => 3, 3, 4,
-; RNIT: 7, 8, 9, 13, 10, 14,
-; RNIT: 1, 2, 5, 11, 6, 12,
+; RNIT: 0, 7, 1, 2, 5, 11, 6, 12, 3, 4, 8, 9, 13, 10, 14,
}
; CHECK-NOT: =>
; CHECK: [0] 0 => <Function Return>
-; CHECK-NEXT: [1] 7 => 3
-; STAT: 2 region - The # of regions
+; CHECK-NEXT: [1] 2 => 10
+; CHECK_NEXT: [2] 5 => 6
+; STAT: 3 region - The # of regions
; STAT: 1 region - The # of simple regions
; BBIT: 0, 7, 1, 2, 5, 11, 6, 10, 8, 9, 13, 14, 12, 3, 4,
-; BBIT: 7, 1, 2, 5, 11, 6, 10, 8, 9, 13, 14, 12,
-
-; RNIT: 0, 7 => 3, 3, 4,
-; RNIT: 7, 1, 2, 5, 11, 6, 10, 8, 9, 13, 14, 12,
+; BBIT: 2, 5, 11, 6, 12,
+; BBIT: 5, 11, 12,
+; RNIT: 0, 7, 1, 2 => 10, 10, 8, 9, 13, 14, 3, 4,
+; RNIT: 2, 5 => 6, 6,
+; RNIT: 5, 11, 12,
\ No newline at end of file
; CHECK: Region tree:
; CHECK-NEXT: [0] 0 => <Function Return>
-; CHECK-NEXT: [1] 7 => 3
; CHECK-NEXT: End region tree
-
; CHECK: Region tree:
; CHECK-NEXT: [0] 0 => <Function Return>
-; CHECK-NEXT: [1] 7 => 3
; CHECK-NEXT: End region tree
}
; si_mask_branch
-; s_cbranch_execz
-; s_branch
; GCN-LABEL: {{^}}analyze_mask_branch:
; GCN: v_cmp_lt_f32_e32 vcc
; GCN-NEXT: s_and_saveexec_b64 [[MASK:s\[[0-9]+:[0-9]+\]]], vcc
; GCN-NEXT: ; mask branch [[RET:BB[0-9]+_[0-9]+]]
-; GCN-NEXT: s_cbranch_execz [[BRANCH_SKIP:BB[0-9]+_[0-9]+]]
-; GCN-NEXT: s_branch [[LOOP_BODY:BB[0-9]+_[0-9]+]]
-; GCN-NEXT: [[BRANCH_SKIP]]: ; %entry
-; GCN-NEXT: s_getpc_b64 vcc
-; GCN-NEXT: s_add_u32 vcc_lo, vcc_lo, [[RET]]-([[BRANCH_SKIP]]+4)
-; GCN-NEXT: s_addc_u32 vcc_hi, vcc_hi, 0
-; GCN-NEXT: s_setpc_b64 vcc
-
-; GCN-NEXT: [[LOOP_BODY]]: ; %loop_body
-; GCN: s_mov_b64 vcc, -1{{$}}
+; GCN-NEXT: [[LOOP_BODY:BB[0-9]+_[0-9]+]]: ; %loop_body
; GCN: ;;#ASMSTART
; GCN: v_nop_e64
; GCN: v_nop_e64
; GCN: v_nop_e64
; GCN: v_nop_e64
; GCN: ;;#ASMEND
-; GCN-NEXT: s_cbranch_vccz [[RET]]
; GCN-NEXT: [[LONGBB:BB[0-9]+_[0-9]+]]: ; %loop_body
; GCN-NEXT: ; in Loop: Header=[[LOOP_BODY]] Depth=1
; GCN-NEXT: s_subb_u32 vcc_hi, vcc_hi, 0
; GCN-NEXT: s_setpc_b64 vcc
-; GCN-NEXT: [[RET]]: ; %Flow
+; GCN-NEXT: [[RET]]: ; %ret
; GCN-NEXT: s_or_b64 exec, exec, [[MASK]]
; GCN: buffer_store_dword
; GCN-NEXT: s_endpgm
; generated.
; PR16393
-; We expect the spill to be generated in %if.end230 and the reloads in
-; %if.end249 and %for.body285.
+; We expect 4-byte spill and reload to be generated.
; CHECK: set_stored_macroblock_parameters
-; CHECK: @ %if.end230
-; CHECK-NOT:@ %if.
-; CHECK-NOT:@ %for.
-; CHECK: str r{{.*}}, [sp, [[SLOT:#[0-9]+]]] @ 4-byte Spill
-; CHECK: @ %if.end249
-; CHECK-NOT:@ %if.
-; CHECK-NOT:@ %for.
-; CHECK: ldr r{{.*}}, [sp, [[SLOT]]] @ 4-byte Reload
-; CHECK: @ %for.body285
-; CHECK-NOT:@ %if.
-; CHECK-NOT:@ %for.
-; CHECK: ldr r{{.*}}, [sp, [[SLOT]]] @ 4-byte Reload
+; CHECK: str r{{.*}}, [sp, {{#[0-9]+}}] @ 4-byte Spill
+; CHECK: ldr r{{.*}}, [lr, {{#[0-9]+}}] @ 4-byte Reload
target triple = "armv7l-unknown-linux-gnueabihf"
; RUN: llc < %s -mtriple=thumbv8 -arm-atomic-cfg-tidy=0 | FileCheck %s
; RUN: llc < %s -mtriple=thumbv7 -arm-atomic-cfg-tidy=0 -arm-restrict-it | FileCheck %s
-; CHECK: it ne
+; CHECK: it ne
; CHECK-NEXT: cmpne
; CHECK-NEXT: bne [[JUMPTARGET:.LBB[0-9]+_[0-9]+]]
; CHECK: cbz
; CHECK-NEXT: b
; CHECK: [[JUMPTARGET]]:{{.*}}%if.else173
; CHECK-NEXT: mov.w
-; CHECK-NEXT: pop
-; CHECK-NEXT: %if.else145
+; CHECK-NEXT: bx lr
+; CHECK: %if.else145
; CHECK-NEXT: mov.w
+; CHECK: pop.w
%struct.hc = type { i32, i32, i32, i32 }
; CHECK-LABEL: @invert_branch_on_arg_inf_loop(
; CHECK: entry:
; CHECK: %arg.inv = xor i1 %arg, true
-; CHECK: phi i1 [ false, %Flow1 ], [ %arg.inv, %entry ]
define void @invert_branch_on_arg_inf_loop(i32 addrspace(1)* %out, i1 %arg) {
entry:
- br i1 %arg, label %for.end, label %for.body
+ br i1 %arg, label %for.end, label %sesestart
+sesestart:
+ br label %for.body
for.body: ; preds = %entry, %for.body
store i32 999, i32 addrspace(1)* %out, align 4
- br label %for.body
+ br i1 %arg, label %for.body, label %seseend
+seseend:
+ ret void
for.end: ; preds = %Flow
ret void
+; XFAIL: *
+
+; This test used to generate a region that caused it to delete the entry block,
+; but it does not anymore after the changes to handling of infinite loops in the
+; PostDominatorTree.
+; TODO: This should be either replaced with another IR or deleted completely.
+
; RUN: opt -S -o - -structurizecfg -verify-dom-info < %s | FileCheck %s
; CHECK-LABEL: @no_branch_to_entry_undef(
}
// Verify that the PDT is correctly updated in case an edge removal results
-// in a new unreachable CFG node.
+// in a new unreachable CFG node. Also make sure that the updated PDT is the
+// same as a freshly recalculated one.
//
// For the following input code and initial PDT:
//
// CFG' PDT-updated
//
// A Exit
-// | |
-// B D
+// | / | \
+// B C B D
// | \ |
-// v \ B
-// / D \
-// C \ A
-// | v
+// v \ A
+// / D
+// C \
+// | \
// unreachable Exit
//
-// WARNING: PDT-updated is inconsistent with PDT-recalculated, which is
-// constructed from CFG' when recalculating the PDT from scratch.
-//
-// PDT-recalculated
+// Both the blocks that end with ret and with unreachable become trivial
+// PostDomTree roots, as they have no successors.
//
-// Exit
-// / | \
-// C B D
-// |
-// A
-//
-// TODO: document the wanted behavior after resolving this inconsistency.
TEST(DominatorTree, DeletingEdgesIntroducesUnreachables) {
StringRef ModuleString =
"define void @f() {\n"
BasicBlock *C = &*FI++;
BasicBlock *D = &*FI++;
- assert(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ ASSERT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ EXPECT_TRUE(DT->verify());
+ EXPECT_TRUE(PDT->verify());
C->getTerminator()->eraseFromParent();
new UnreachableInst(C->getContext(), C);
DT->deleteEdge(C, B);
PDT->deleteEdge(C, B);
- EXPECT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
- EXPECT_EQ(PDT->getNode(C), nullptr);
-
- PDT->recalculate(F);
+ EXPECT_TRUE(DT->verify());
+ EXPECT_TRUE(PDT->verify());
EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
EXPECT_NE(PDT->getNode(C), nullptr);
+
+ DominatorTree NDT(F);
+ EXPECT_EQ(DT->compare(NDT), 0);
+
+ PostDomTree NPDT(F);
+ EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
// Verify that the PDT is correctly updated in case an edge removal results
-// in an infinite loop.
+// in an infinite loop. Also make sure that the updated PDT is the
+// same as a freshly recalculated one.
//
// Test case:
//
// After deleting the edge C->B, C is part of an infinite reverse-unreachable
// loop:
//
-// CFG' PDT'
+// CFG' PDT'
//
// A Exit
-// | |
-// B D
+// | / | \
+// B C B D
// | \ |
-// v \ B
-// / D \
-// C \ A
+// v \ A
+// / D
+// C \
// / \ v
// ^ v Exit
// \_/
//
-// In PDT, D post-dominates B. We verify that this post-dominance
-// relation is preserved _after_ deleting the edge C->B from CFG.
-//
-// As C now becomes reverse-unreachable, it is not anymore part of the
-// PDT. We also verify this property.
+// As C now becomes reverse-unreachable, it forms a new non-trivial root and
+// gets connected to the virtual exit.
+// D does not postdominate B anymore, because there are two forward paths from
+// B to the virtual exit:
+// - B -> C -> VirtualExit
+// - B -> D -> VirtualExit.
//
-// TODO: Can we change the PDT definition such that C remains part of the
-// CFG?
TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop) {
StringRef ModuleString =
"define void @f() {\n"
BasicBlock *C = &*FI++;
BasicBlock *D = &*FI++;
- assert(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ ASSERT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ EXPECT_TRUE(DT->verify());
+ EXPECT_TRUE(PDT->verify());
auto SwitchC = cast<SwitchInst>(C->getTerminator());
SwitchC->removeCase(SwitchC->case_begin());
DT->deleteEdge(C, B);
+ EXPECT_TRUE(DT->verify());
PDT->deleteEdge(C, B);
+ EXPECT_TRUE(PDT->verify());
- EXPECT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
- EXPECT_EQ(PDT->getNode(C), nullptr);
+ EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ EXPECT_NE(PDT->getNode(C), nullptr);
- PDT->recalculate(F);
+ DominatorTree NDT(F);
+ EXPECT_EQ(DT->compare(NDT), 0);
- EXPECT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
- EXPECT_EQ(PDT->getNode(C), nullptr);
+ PostDomTree NPDT(F);
+ EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
//
// A Exit
// | / | \
-// B-- C B D
-// | \ |
-// v \ A
+// B-- C2 B D
+// | \ / |
+// v \ C A
// / D
// C--C2 \
// / \ \ v
// After deleting the edge C->E, C is part of an infinite reverse-unreachable
// loop:
//
-// CFG' PDT'
+// CFG' PDT'
//
// A Exit
-// | |
-// B D
+// | / | \
+// B C B D
// | \ |
-// v \ B
-// / D \
-// C \ A
+// v \ A
+// / D
+// C \
// / \ v
// ^ v Exit
// \_/
//
-// In PDT, D does not post-dominate B. After the edge C->E is removed, a new
-// post-dominance relation is introduced.
-//
-// As C now becomes reverse-unreachable, it is not anymore part of the
-// PDT. We also verify this property.
+// In PDT, D does not post-dominate B. After the edge C -> C2 is removed,
+// C becomes a new nontrivial PDT root.
//
-// TODO: Can we change the PDT definition such that C remains part of the
-// CFG, at best without loosing the dominance relation D postdom B.
TEST(DominatorTree, DeletingEdgesIntroducesInfiniteLoop2) {
StringRef ModuleString =
"define void @f() {\n"
BasicBlock *C2 = &*FI++;
BasicBlock *D = &*FI++;
+ EXPECT_TRUE(DT->verify());
+ EXPECT_TRUE(PDT->verify());
+
auto SwitchC = cast<SwitchInst>(C->getTerminator());
SwitchC->removeCase(SwitchC->case_begin());
DT->deleteEdge(C, C2);
PDT->deleteEdge(C, C2);
- C2->eraseFromParent();
+ C2->removeFromParent();
EXPECT_EQ(DT->getNode(C2), nullptr);
PDT->eraseNode(C2);
+ delete C2;
+
+ EXPECT_TRUE(DT->verify());
+ EXPECT_TRUE(PDT->verify());
- EXPECT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
- EXPECT_EQ(PDT->getNode(C), nullptr);
- EXPECT_EQ(PDT->getNode(C2), nullptr);
+ EXPECT_FALSE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
+ EXPECT_NE(PDT->getNode(C), nullptr);
- PDT->recalculate(F);
+ DominatorTree NDT(F);
+ EXPECT_EQ(DT->compare(NDT), 0);
- EXPECT_TRUE(PDT->dominates(PDT->getNode(D), PDT->getNode(B)));
- EXPECT_EQ(PDT->getNode(C), nullptr);
- EXPECT_EQ(PDT->getNode(C2), nullptr);
+ PostDomTree NPDT(F);
+ EXPECT_EQ(PDT->compare(NPDT), 0);
});
}
}
}
+TEST(DominatorTree, InsertFromUnreachable) {
+ CFGHolder Holder;
+ std::vector<CFGBuilder::Arc> Arcs = {{"1", "2"}, {"2", "3"}, {"3", "4"}};
+
+ std::vector<CFGBuilder::Update> Updates = {{Insert, {"3", "5"}}};
+ CFGBuilder B(Holder.F, Arcs, Updates);
+ PostDomTree PDT(*Holder.F);
+ EXPECT_TRUE(PDT.verify());
+
+ Optional<CFGBuilder::Update> LastUpdate = B.applyUpdate();
+ EXPECT_TRUE(LastUpdate);
+
+ EXPECT_EQ(LastUpdate->Action, Insert);
+ BasicBlock *From = B.getOrAddBlock(LastUpdate->Edge.From);
+ BasicBlock *To = B.getOrAddBlock(LastUpdate->Edge.To);
+ PDT.insertEdge(From, To);
+ EXPECT_TRUE(PDT.verify());
+ EXPECT_TRUE(PDT.getRoots().size() == 2);
+ EXPECT_NE(PDT.getNode(B.getOrAddBlock("5")), nullptr);
+}
+
TEST(DominatorTree, InsertMixed) {
CFGHolder Holder;
std::vector<CFGBuilder::Arc> Arcs = {