/// \file GraphAlgorithms.cpp // // This file is distributed under the MIT License. See LICENSE.md for details. // #define BOOST_TEST_MODULE GraphAlgorithms bool init_unit_test(); #include "boost/test/unit_test.hpp" #include "llvm/ADT/SmallSet.h" #include "revng/ADT/GenericGraph.h" #include "revng/Support/GraphAlgorithms.h" struct MyForwardNode { MyForwardNode(int Index) : Index(Index) {} int Index; int getIndex() { return Index; } }; struct MyBidirectionalNode : MyForwardNode { MyBidirectionalNode(int Index) : MyForwardNode(Index) {} }; template struct LoopGraph { using Node = NodeType; GenericGraph Graph; Node *Entry; Node *LoopLatch; Node *Exit; }; template static LoopGraph createLGGraph() { LoopGraph LG; auto &Graph = LG.Graph; // Create nodes LG.Entry = Graph.addNode(1); LG.LoopLatch = Graph.addNode(2); LG.Exit = Graph.addNode(3); // Set entry node Graph.setEntryNode(LG.Entry); // Create edges LG.Entry->addSuccessor(LG.LoopLatch); LG.LoopLatch->addSuccessor(LG.Entry); LG.Entry->addSuccessor(LG.Exit); return LG; } template struct NestedLoopGraph { using Node = NodeType; GenericGraph Graph; Node *Entry; Node *LoopHeader; Node *SecondLoopHeader; Node *LoopLatch; Node *SecondLoopLatch; Node *Exit; }; template static NestedLoopGraph createNLGGraph() { NestedLoopGraph NLG; auto &Graph = NLG.Graph; // Create nodes NLG.Entry = Graph.addNode(1); NLG.LoopHeader = Graph.addNode(2); NLG.SecondLoopHeader = Graph.addNode(3); NLG.LoopLatch = Graph.addNode(4); NLG.SecondLoopLatch = Graph.addNode(5); NLG.Exit = Graph.addNode(6); // Create edges NLG.Entry->addSuccessor(NLG.LoopHeader); NLG.LoopHeader->addSuccessor(NLG.SecondLoopHeader); NLG.SecondLoopHeader->addSuccessor(NLG.LoopLatch); NLG.LoopLatch->addSuccessor(NLG.SecondLoopLatch); NLG.LoopLatch->addSuccessor(NLG.SecondLoopHeader); NLG.SecondLoopLatch->addSuccessor(NLG.LoopHeader); NLG.SecondLoopLatch->addSuccessor(NLG.Exit); return NLG; } template struct NonCanonicalLoopGraph { using Node = NodeType; GenericGraph Graph; Node *LoopHeader; Node *SecondLoopHeader; Node *LoopLatch; Node *LoopSuccessor; Node *SecondLoopSuccessor; Node *SecondLoopLatch; }; template static NonCanonicalLoopGraph createNCLGGraph() { NonCanonicalLoopGraph NCLG; auto &Graph = NCLG.Graph; // Create nodes NCLG.LoopHeader = Graph.addNode(1); NCLG.SecondLoopHeader = Graph.addNode(2); NCLG.LoopLatch = Graph.addNode(3); NCLG.LoopSuccessor = Graph.addNode(4); NCLG.SecondLoopSuccessor = Graph.addNode(5); NCLG.SecondLoopLatch = Graph.addNode(6); // Create edges NCLG.LoopHeader->addSuccessor(NCLG.SecondLoopHeader); NCLG.SecondLoopHeader->addSuccessor(NCLG.LoopLatch); NCLG.SecondLoopHeader->addSuccessor(NCLG.LoopSuccessor); NCLG.LoopLatch->addSuccessor(NCLG.LoopSuccessor); NCLG.LoopSuccessor->addSuccessor(NCLG.SecondLoopSuccessor); NCLG.LoopSuccessor->addSuccessor(NCLG.SecondLoopLatch); NCLG.SecondLoopSuccessor->addSuccessor(NCLG.SecondLoopLatch); NCLG.LoopLatch->addSuccessor(NCLG.LoopHeader); NCLG.SecondLoopLatch->addSuccessor(NCLG.SecondLoopHeader); return NCLG; } template struct NonCanonicalAlternateLoopGraph { using Node = NodeType; GenericGraph Graph; Node *LoopHeader; Node *SecondLoopHeader; Node *LoopLatch; Node *LoopSuccessor; Node *SecondLoopSuccessor; Node *SecondLoopLatch; }; template static NonCanonicalAlternateLoopGraph createNCALGGraph() { NonCanonicalAlternateLoopGraph NCALG; auto &Graph = NCALG.Graph; // Create nodes NCALG.LoopHeader = Graph.addNode(1); NCALG.SecondLoopHeader = Graph.addNode(2); NCALG.LoopLatch = Graph.addNode(3); NCALG.SecondLoopLatch = Graph.addNode(4); // Create edges NCALG.LoopHeader->addSuccessor(NCALG.SecondLoopHeader); NCALG.SecondLoopHeader->addSuccessor(NCALG.LoopLatch); NCALG.LoopLatch->addSuccessor(NCALG.SecondLoopLatch); NCALG.LoopLatch->addSuccessor(NCALG.LoopHeader); NCALG.SecondLoopLatch->addSuccessor(NCALG.SecondLoopHeader); return NCALG; } template struct ReverseGraph { using Node = NodeType; GenericGraph Graph; Node *InitialBlock; Node *SmallerBlock; Node *EndBlock; }; template static ReverseGraph createRGraph() { ReverseGraph RG; auto &Graph = RG.Graph; // Create nodes RG.InitialBlock = Graph.addNode(1); RG.SmallerBlock = Graph.addNode(2); RG.EndBlock = Graph.addNode(3); // Create edges RG.InitialBlock->addSuccessor(RG.SmallerBlock); RG.InitialBlock->addSuccessor(RG.EndBlock); RG.SmallerBlock->addSuccessor(RG.EndBlock); return RG; } BOOST_AUTO_TEST_CASE(GetBackedgesTest) { // Create the graph using NodeType = BidirectionalNode; auto LG = createLGGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; // Compute the backedges set EdgeSet Backedges = getBackedges(LG.Entry); // Check that the only backedge present revng_check(Backedges.size() == 1); EdgeDescriptor Backedge = *Backedges.begin(); NodeType *Source = Backedge.first; NodeType *Target = Backedge.second; revng_check(Source == LG.LoopLatch); revng_check(Target == LG.Entry); // Check the reachability set described by the only backedge present BlockSet Reachables = nodesBetween(Target, Source); revng_check(Reachables.size() == 2); revng_check(Reachables.contains(LG.Entry)); revng_check(Reachables.contains(LG.LoopLatch)); revng_check(LG.Entry != LG.LoopLatch); revng_check(Reachables[0] == LG.LoopLatch); revng_check(Reachables[1] == LG.Entry); } BOOST_AUTO_TEST_CASE(NestedLoopTest) { // Create the graph using NodeType = BidirectionalNode; auto NLG = createNLGGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; using BlockSetVect = llvm::SmallVector; // Compute the backedges set EdgeSet Backedges = getBackedges(NLG.Entry); revng_check(Backedges.size() == 2); revng_check(Backedges[0].first == NLG.LoopLatch); revng_check(Backedges[0].second == NLG.SecondLoopHeader); revng_check(Backedges[1].first == NLG.SecondLoopLatch); revng_check(Backedges[1].second == NLG.LoopHeader); // Obtain the nodes reachable from the identified backedges BlockSetVect Loops; for (EdgeDescriptor Backedge : Backedges) { BlockSet LoopNodes = nodesBetween(Backedge.second, Backedge.first); Loops.push_back(std::move(LoopNodes)); } // We want to test that the nodes reachable from the extremities of a // backedge, compose the whole body of the loop. Two loops (one which is // completely nested inside the other) compose the test. revng_check(Loops.size() == 2); revng_check(Loops[0][0] == NLG.LoopLatch); revng_check(Loops[0][1] == NLG.SecondLoopHeader); revng_check(Loops[1][0] == NLG.LoopLatch); revng_check(Loops[1][1] == NLG.SecondLoopHeader); revng_check(Loops[1][2] == NLG.SecondLoopLatch); revng_check(Loops[1][3] == NLG.LoopHeader); } BOOST_AUTO_TEST_CASE(NonCanonicalLoopTest) { // Create the graph using NodeType = BidirectionalNode; auto NCLG = createNCLGGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; using BlockSetVect = llvm::SmallVector; // Compute the backedges set EdgeSet Backedges = getBackedges(NCLG.LoopHeader); revng_check(Backedges.size() == 2); revng_check(Backedges[0].first == NCLG.LoopLatch); revng_check(Backedges[0].second == NCLG.LoopHeader); revng_check(Backedges[1].first == NCLG.SecondLoopLatch); revng_check(Backedges[1].second == NCLG.SecondLoopHeader); // Obtain the nodes reachable from the identified backedges BlockSetVect Loops; for (EdgeDescriptor Backedge : Backedges) { BlockSet LoopNodes = nodesBetween(Backedge.second, Backedge.first); Loops.push_back(std::move(LoopNodes)); } // We want to test that the nodes reachable from the extremities of a // backedge, compose the whole body of the loop. In particular, we want to // ensure that both `LoopSuccessor` and `SecondLoopSuccessor` are correctly // included in the first loop. revng_check(Loops.size() == 2); revng_check(Loops[0][0] == NCLG.SecondLoopHeader); revng_check(Loops[0][1] == NCLG.SecondLoopLatch); revng_check(Loops[0][2] == NCLG.LoopSuccessor); revng_check(Loops[0][3] == NCLG.SecondLoopSuccessor); revng_check(Loops[0][4] == NCLG.LoopLatch); revng_check(Loops[0][5] == NCLG.LoopHeader); revng_check(Loops[1][0] == NCLG.LoopSuccessor); revng_check(Loops[1][1] == NCLG.LoopLatch); revng_check(Loops[1][2] == NCLG.SecondLoopSuccessor); revng_check(Loops[1][3] == NCLG.SecondLoopLatch); revng_check(Loops[1][4] == NCLG.SecondLoopHeader); } BOOST_AUTO_TEST_CASE(NonCanonicalAlternateLoopTest) { // Create the graph using NodeType = BidirectionalNode; auto NCALG = createNCALGGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; using BlockSetVect = llvm::SmallVector; // Compute the backedges set EdgeSet Backedges = getBackedges(NCALG.LoopHeader); revng_check(Backedges.size() == 2); revng_check(Backedges[0].first == NCALG.LoopLatch); revng_check(Backedges[0].second == NCALG.LoopHeader); revng_check(Backedges[1].first == NCALG.SecondLoopLatch); revng_check(Backedges[1].second == NCALG.SecondLoopHeader); // Obtain the nodes reachable from the identified backedges BlockSetVect Loops; for (EdgeDescriptor Backedge : Backedges) { BlockSet LoopNodes = nodesBetween(Backedge.second, Backedge.first); Loops.push_back(std::move(LoopNodes)); } // We want to test that the nodes reachable from the extremities of a // backedge, compose the whole body of the loop. The two loops are partially // overlapping, but we don't want to trepass the head of the loop when // invoking the `nodesBetween`. revng_check(Loops.size() == 2); revng_check(Loops[0][0] == NCALG.SecondLoopHeader); revng_check(Loops[0][1] == NCALG.LoopLatch); revng_check(Loops[0][2] == NCALG.LoopHeader); revng_check(Loops[1][0] == NCALG.LoopLatch); revng_check(Loops[1][1] == NCALG.SecondLoopLatch); revng_check(Loops[1][2] == NCALG.SecondLoopHeader); } BOOST_AUTO_TEST_CASE(ReverseTest) { // Create the graph using NodeType = BidirectionalNode; auto RG = createRGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; // Compute the backedges set EdgeSet Backedges = getBackedges(RG.InitialBlock); revng_check(Backedges.size() == 0); // Test the reverse `nodesBetween` BlockSet ReachableSet = nodesBetweenReverse(RG.SmallerBlock, RG.InitialBlock); revng_check(ReachableSet.size() == 2); revng_check(ReachableSet[0] == RG.InitialBlock); revng_check(ReachableSet[1] == RG.SmallerBlock); } BOOST_AUTO_TEST_CASE(NullTargetTest) { // Create the graph using NodeType = BidirectionalNode; auto RG = createRGraph(); using EdgeDescriptor = revng::detail::EdgeDescriptor; using EdgeSet = llvm::SmallSetVector; using BlockSet = llvm::SmallSetVector; // Compute the backedges set EdgeSet Backedges = getBackedges(RG.InitialBlock); revng_check(Backedges.size() == 0); // Test the reverse `nodesBetween` BlockSet ReachableSet = findReachableNodes(RG.InitialBlock); revng_check(ReachableSet.size() == 3); revng_check(ReachableSet[0] == RG.InitialBlock); revng_check(ReachableSet[1] == RG.SmallerBlock); revng_check(ReachableSet[2] == RG.EndBlock); }