Files
revng-revng/tests/unit/FilteredGraphTraits.cpp
T
Pietro Fezzardi ddebd55629 Add FilteredGraphTraits with tests
This patch adds a two markers for llvm::GraphTraits:
 - NodePairFilteredGraph
 - EdgeFilteredGraph

Both these markers allow to specify a static predicate that is used to
filter edges.
This predicate is a boolean function such that:
  - for NodePairFilteredGraph, it takes a pair of const NodeRef & that
    are used to represent an edge, and it evaluates a given property of
    that pair;
  - for EdgeFilteredGraph, it takes a const EdgeRef & that represents an
    edge, and it evaluates a given property on the edge.
The filtered graph contains only the edges for which the predicate
evaluates true.

Notice that the predicate must have static lifetime, meaning that all
the edge properties must be entirely evaluated on the pair of node (for
NodePairFilteredGraph) or on the edge (for EdgeFilteredGraph).
This means that you cannot pass mutable state to the predicate at
runtime.

The new markers are designed to interoperate well with llvm::Inverse and
to allow you to traverse the marked graphs with llvm::depth_first,
llvm::inverse_depth_first, llvm::breadth_first, and to compute dominator
trees and post-dominator trees on filtered graphs.
2020-06-13 10:36:08 +02:00

833 lines
31 KiB
C++

/// \file FilteredGraphTraits.cpp
/// \brief Tests for the FilteredGraphTraits template
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Boost includes
#define BOOST_TEST_MODULE FilteredGraphTraits
bool init_unit_test();
#include <boost/test/unit_test.hpp>
// Standard includes
#include <functional>
#include <map>
#include <type_traits>
// LLVM includes
#include <llvm/ADT/BreadthFirstIterator.h>
#include <llvm/ADT/DepthFirstIterator.h>
#include <llvm/ADT/GraphTraits.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/Dominators.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/GenericDomTree.h>
#include <llvm/Support/GenericDomTreeConstruction.h>
// Local libraries includes
#include "revng/ADT/FilteredGraphTraits.h"
#include "revng/Support/Debug.h"
#include "revng/Support/IRHelpers.h"
#include "revng/UnitTestHelpers/DotGraphObject.h"
#include "revng/UnitTestHelpers/LLVMTestHelpers.h"
#include "revng/UnitTestHelpers/UnitTestHelpers.h"
using namespace llvm;
template<char Letter>
static bool
HasLetterInName(const llvm::GraphTraits<Function *>::NodeRef & /*Src*/,
const llvm::GraphTraits<Function *>::NodeRef &Tgt) {
if (not Tgt->hasName())
return false;
return Tgt->getName().contains(Letter);
}
template<char Letter>
static bool
EdgeHasLetterInName(const llvm::GraphTraits<DotGraph *>::EdgeRef &E) {
return E.second->getName().contains(Letter);
}
template<typename T>
static bool AlwaysTrue(const T & /*Src*/, const T & /*Tgt*/) {
return true;
}
template<typename NodeT>
static bool AlwaysTrueEdge(const typename GraphTraits<NodeT>::EdgeRef &) {
return true;
}
template<typename T>
static bool AlwaysFalse(const T & /*Src*/, const T & /*Tgt*/) {
return false;
}
template<typename NodeT>
static bool AlwaysFalseEdge(const typename GraphTraits<NodeT>::EdgeRef &) {
return false;
}
template<typename NodeT>
using TrueNPFG = NodePairFilteredGraph<NodeT, AlwaysTrue<NodeT>>;
template<typename NodeT>
using FalseNPFG = NodePairFilteredGraph<NodeT, AlwaysFalse<NodeT>>;
template<typename NodeT>
using TrueEFG = EdgeFilteredGraph<NodeT, AlwaysTrueEdge<NodeT>>;
template<typename NodeT>
using FalseEFG = EdgeFilteredGraph<NodeT, AlwaysFalseEdge<NodeT>>;
struct ArgsFixture {
int argc;
char **argv;
ArgsFixture() :
argc(boost::unit_test::framework::master_test_suite().argc),
argv(boost::unit_test::framework::master_test_suite().argv) {}
~ArgsFixture() {}
};
BOOST_FIXTURE_TEST_SUITE(TestSuite, ArgsFixture)
BOOST_AUTO_TEST_CASE(NodePairFilteredGraphTests) {
std::string Body{ R"LLVM(
br label %starter
starter:
%to_store = load i64, i64* @rax
%gt10 = icmp ugt i64 %to_store, 10
%lt15 = icmp ult i64 %to_store, 15
%in10_15 = and i1 %gt10, %lt15
br i1 %in10_15, label %end, label %false
false:
%gt30 = icmp ugt i64 %to_store, 30
%lt35 = icmp ult i64 %to_store, 35
%in30_35 = and i1 %gt30, %lt35
br i1 %in30_35, label %end, label %_xit
_xit:
unreachable
end:
store i64 %to_store, i64* @pc
unreachable
)LLVM" };
LLVMContext C;
std::unique_ptr<llvm::Module> M = loadModule(C, Body.data());
revng_check(not verifyModule(*M, &dbgs()));
Function *F = M->getFunction("main");
BasicBlock *BackBB = &F->getBasicBlockList().back();
// #### Depth first visits ####
std::vector<BasicBlock *> DefaultResults;
std::vector<std::string> DefaultResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
{ // Baseline, on unfiltered graph
for (auto &It : llvm::depth_first(F))
DefaultResults.push_back(It);
revng_check(DefaultResults.size() == DefaultResultsNames.size());
for (size_t I = 0; I < DefaultResults.size(); ++I)
revng_check(DefaultResults[I]->getName() == DefaultResultsNames[I]);
}
{ // Do-nothing filter
std::vector<std::string> ExpectedResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
std::vector<BasicBlock *> Results;
using FNPFG = NodePairFilteredGraph<Function *, AlwaysTrue<BasicBlock *>>;
for (auto &It : llvm::depth_first(FNPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
std::vector<std::string> ExpectedResultsNames{
"initial_block",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, AlwaysFalse<BasicBlock *>>;
for (auto &It : llvm::depth_first(NPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'a's
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"false",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, HasLetterInName<'a'>>;
for (auto &It : llvm::depth_first(NPFG(F)))
Results.push_back(It);
// Only the initial_block, starter, and false should be left
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'e's
// All but _xit should reached, in this order
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"end",
"false",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, HasLetterInName<'e'>>;
for (auto &It : llvm::depth_first(NPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
// #### Breadth first visits ####
{ // Baseline, on unfiltered graph
std::vector<std::string> ExpectedResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
std::vector<BasicBlock *> Results;
for (auto &It : llvm::breadth_first(F))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Do-nothing filter
std::vector<std::string> ExpectedResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, AlwaysTrue<BasicBlock *>>;
for (auto &It : llvm::breadth_first(NPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
std::vector<std::string> ExpectedResultsNames{
"initial_block",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, AlwaysFalse<BasicBlock *>>;
for (auto &It : llvm::breadth_first(NPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'a's
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, HasLetterInName<'a'>>;
for (auto &It : llvm::breadth_first(NPFG(F)))
Results.push_back(It);
// Only the initial_block, starter, and false should be left
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"false",
};
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'e's
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"end",
"false",
};
std::vector<BasicBlock *> Results;
using NPFG = NodePairFilteredGraph<Function *, HasLetterInName<'e'>>;
for (auto &It : llvm::breadth_first(NPFG(F)))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
// #### Inverse depth first visits ####
{
std::vector<std::string> ExpectedResultsNames{
"end",
"false",
"starter",
"initial_block",
};
std::vector<BasicBlock *> Results;
for (auto &It : llvm::inverse_depth_first(BackBB))
Results.push_back(It);
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I]->getName() == ExpectedResultsNames[I]);
}
{ // Do-nothing filter
std::vector<std::string> ExpectedResultsNames{
"end",
"false",
"starter",
"initial_block",
};
std::vector<BasicBlock *> FirstResults, SecondResults;
using NPFG = NodePairFilteredGraph<BasicBlock *, AlwaysTrue<BasicBlock *>>;
using InvBBNPFG = Inverse<NPFG>;
for (auto &It : llvm::depth_first(InvBBNPFG(BackBB)))
FirstResults.push_back(It);
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I]->getName() == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(NPFG(BackBB)))
SecondResults.push_back(It);
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
std::vector<std::string> ExpectedResultsNames{
"end",
};
std::vector<BasicBlock *> FirstResults, SecondResults;
using NPFG = NodePairFilteredGraph<BasicBlock *, AlwaysFalse<BasicBlock *>>;
using InvBBNPFG = Inverse<NPFG>;
for (auto &It : llvm::depth_first(InvBBNPFG(BackBB)))
FirstResults.push_back(It);
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I]->getName() == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(NPFG(BackBB)))
SecondResults.push_back(It);
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'a's
std::vector<std::string> ExpectedResultsNames{
"end",
};
std::vector<BasicBlock *> FirstResults, SecondResults;
using NPFG = NodePairFilteredGraph<BasicBlock *, HasLetterInName<'a'>>;
using InvBBNPFG = Inverse<NPFG>;
for (auto &It : llvm::depth_first(InvBBNPFG(BackBB)))
FirstResults.push_back(It);
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I]->getName() == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(NPFG(BackBB)))
SecondResults.push_back(It);
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I]->getName() == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards BB with name without 'e's
std::vector<std::string> ExpectedResultsNames{
"end",
"false",
"starter",
"initial_block",
};
std::vector<BasicBlock *> FirstResults, SecondResults;
using NPFG = NodePairFilteredGraph<BasicBlock *, HasLetterInName<'e'>>;
using InvBBNPFG = Inverse<NPFG>;
for (auto &It : llvm::depth_first(InvBBNPFG(BackBB)))
FirstResults.push_back(It);
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I]->getName() == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(NPFG(BackBB)))
SecondResults.push_back(It);
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I]->getName() == ExpectedResultsNames[I]);
}
// #### Dominator and PostDominator Trees ####
std::map<std::string, BasicBlock *> NamesToBlocks;
revng_check(DefaultResults.size() == DefaultResultsNames.size());
for (size_t I = 0ULL; I < DefaultResults.size(); ++I)
NamesToBlocks[DefaultResultsNames[I]] = DefaultResults[I];
const auto Dominates = [&NamesToBlocks](auto &DT,
const std::string &A,
const std::string &B) -> bool {
return DT.dominates(NamesToBlocks.at(A), NamesToBlocks.at(B));
};
{
using DT = DominatorTreeBase<BasicBlock, false>;
DT TheDomTree;
TheDomTree.recalculate(*F);
revng_check(TheDomTree.verify());
TheDomTree.print(errs());
revng_check(Dominates(TheDomTree, "initial_block", "starter"));
revng_check(Dominates(TheDomTree, "starter", "end"));
revng_check(Dominates(TheDomTree, "starter", "false"));
revng_check(Dominates(TheDomTree, "false", "_xit"));
}
{
using PostDT = DominatorTreeBase<BasicBlock, true>;
PostDT ThePostDomTree;
ThePostDomTree.recalculate(*F);
revng_check(ThePostDomTree.verify());
ThePostDomTree.print(errs());
const auto *Sink = ThePostDomTree.getRootNode();
const auto DTNodeDominates =
[&NamesToBlocks](auto &DT,
const DomTreeNodeBase<BasicBlock> *A,
const std::string &B) -> bool {
return DT.dominates(A, DT.getNode(NamesToBlocks.at(B)));
};
revng_check(DTNodeDominates(ThePostDomTree, Sink, "initial_block"));
revng_check(DTNodeDominates(ThePostDomTree, Sink, "starter"));
revng_check(DTNodeDominates(ThePostDomTree, Sink, "false"));
revng_check(DTNodeDominates(ThePostDomTree, Sink, "end"));
revng_check(DTNodeDominates(ThePostDomTree, Sink, "_xit"));
revng_check(Dominates(ThePostDomTree, "starter", "initial_block"));
}
{
using FiltDT = DominatorTreeOnView<BasicBlock, false, TrueNPFG>;
FiltDT TheFiltDomTree;
TheFiltDomTree.recalculate(*F);
revng_check(TheFiltDomTree.verify());
TheFiltDomTree.print(errs());
revng_check(Dominates(TheFiltDomTree, "initial_block", "starter"));
revng_check(Dominates(TheFiltDomTree, "starter", "end"));
revng_check(Dominates(TheFiltDomTree, "starter", "false"));
revng_check(Dominates(TheFiltDomTree, "false", "_xit"));
}
{
using FiltDT = DominatorTreeOnView<BasicBlock, false, FalseNPFG>;
FiltDT TheFiltDomTree;
TheFiltDomTree.recalculate(*F);
revng_check(TheFiltDomTree.verify());
TheFiltDomTree.print(errs());
revng_check(nullptr == TheFiltDomTree.getNode(NamesToBlocks.at("starter")));
revng_check(nullptr == TheFiltDomTree.getNode(NamesToBlocks.at("false")));
revng_check(nullptr == TheFiltDomTree.getNode(NamesToBlocks.at("end")));
revng_check(nullptr == TheFiltDomTree.getNode(NamesToBlocks.at("_xit")));
revng_check(TheFiltDomTree.getNode(NamesToBlocks.at("initial_block")));
revng_check(Dominates(TheFiltDomTree, "initial_block", "initial_block"));
}
{
using FiltDT = DominatorTreeOnView<BasicBlock, true, TrueNPFG>;
FiltDT TheFiltDomTree;
TheFiltDomTree.recalculate(*F);
revng_check(TheFiltDomTree.verify());
TheFiltDomTree.print(errs());
const auto *Sink = TheFiltDomTree.getRootNode();
const auto DTNodeDominates =
[&NamesToBlocks](auto &DT,
const DomTreeNodeOnView<BasicBlock, TrueNPFG> *A,
const std::string &B) -> bool {
return DT.dominates(A, DT.getNode(NamesToBlocks.at(B)));
};
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "initial_block"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "starter"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "false"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "end"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "_xit"));
revng_check(Dominates(TheFiltDomTree, "starter", "initial_block"));
}
{
using FiltDT = DominatorTreeOnView<BasicBlock, true, FalseNPFG>;
FiltDT TheFiltDomTree;
TheFiltDomTree.recalculate(*F);
revng_check(TheFiltDomTree.verify());
TheFiltDomTree.print(errs());
const auto *Sink = TheFiltDomTree.getRootNode();
const auto DTNodeDominates =
[&NamesToBlocks](auto &DT,
const DomTreeNodeOnView<BasicBlock, FalseNPFG> *A,
const std::string &B) -> bool {
return DT.dominates(A, DT.getNode(NamesToBlocks.at(B)));
};
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "initial_block"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "starter"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "false"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "end"));
revng_check(DTNodeDominates(TheFiltDomTree, Sink, "_xit"));
revng_check(not Dominates(TheFiltDomTree, "starter", "initial_block"));
}
// #### Edge Filtered Graphs ####
{ // Do-nothing filter
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, AlwaysTrueEdge<DotGraph *>>;
for (auto &It : llvm::depth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"initial_block",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, AlwaysFalseEdge<DotGraph *>>;
for (auto &It : llvm::depth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes with name without 'a's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"false",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, EdgeHasLetterInName<'a'>>;
for (auto &It : llvm::depth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes with name without 'e's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"end",
"false",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, EdgeHasLetterInName<'e'>>;
for (auto &It : llvm::depth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
// #### Breadth first visits ####
{ // Do-nothing filter
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"initial_block", "starter", "end", "false", "_xit",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, AlwaysTrueEdge<DotGraph *>>;
for (auto &It : llvm::breadth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"initial_block",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, AlwaysFalseEdge<DotGraph *>>;
for (auto &It : llvm::breadth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes with name without 'a's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"false",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, EdgeHasLetterInName<'a'>>;
for (auto &It : llvm::breadth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes with name without 'e's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames = {
"initial_block",
"starter",
"end",
"false",
};
std::vector<std::string> Results;
using EFG = EdgeFilteredGraph<DotGraph *, EdgeHasLetterInName<'e'>>;
for (auto &It : llvm::breadth_first(EFG(&Input)))
Results.push_back(It->getName());
revng_check(Results.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < Results.size(); ++I)
revng_check(Results[I] == ExpectedResultsNames[I]);
}
// #### Inverse depth first visits ####
{ // Do-nothing filter
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"end",
"starter",
"initial_block",
"false",
};
std::vector<std::string> FirstResults, SecondResults;
using EFG = EdgeFilteredGraph<DotNode *, AlwaysTrueEdge<DotNode *>>;
using InvEFG = llvm::Inverse<EFG>;
DotNode *Exit = Input.getNodeByName("end");
for (auto &It : llvm::depth_first(InvEFG(Exit)))
FirstResults.push_back(It->getName());
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I] == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(EFG(Exit)))
SecondResults.push_back(It->getName());
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I] == ExpectedResultsNames[I]);
}
{ // Filter all, leaving no edge
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"end",
};
std::vector<std::string> FirstResults, SecondResults;
using EFG = EdgeFilteredGraph<DotNode *, AlwaysFalseEdge<DotNode *>>;
using InvEFG = llvm::Inverse<EFG>;
DotNode *Exit = Input.getNodeByName("end");
for (auto &It : llvm::depth_first(InvEFG(Exit)))
FirstResults.push_back(It->getName());
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I] == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(EFG(Exit)))
SecondResults.push_back(It->getName());
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes with name without 'a's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"end",
};
std::vector<std::string> FirstResults, SecondResults;
using EFG = EdgeFilteredGraph<DotNode *, EdgeHasLetterInName<'a'>>;
using InvEFG = llvm::Inverse<EFG>;
DotNode *Exit = Input.getNodeByName("end");
for (auto &It : llvm::depth_first(InvEFG(Exit)))
FirstResults.push_back(It->getName());
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I] == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(EFG(Exit)))
SecondResults.push_back(It->getName());
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I] == ExpectedResultsNames[I]);
}
{ // Filter throwing away all the eges towards nodes BB with name without 'e's
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
std::vector<std::string> ExpectedResultsNames{
"end",
"starter",
"initial_block",
"false",
};
std::vector<std::string> FirstResults, SecondResults;
using EFG = EdgeFilteredGraph<DotNode *, EdgeHasLetterInName<'e'>>;
using InvEFG = llvm::Inverse<EFG>;
DotNode *Exit = Input.getNodeByName("end");
for (auto &It : llvm::depth_first(InvEFG(Exit)))
FirstResults.push_back(It->getName());
revng_check(FirstResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < FirstResults.size(); ++I)
revng_check(FirstResults[I] == ExpectedResultsNames[I]);
for (auto &It : llvm::inverse_depth_first(EFG(Exit)))
SecondResults.push_back(It->getName());
revng_check(SecondResults.size() == ExpectedResultsNames.size());
for (size_t I = 0; I < SecondResults.size(); ++I)
revng_check(SecondResults[I] == ExpectedResultsNames[I]);
}
// #### Dominator and PostDominator Trees ####
// These are only on GraphTraits<DotNode *>, because they do not work on
// GraphTraits<EdgeFilteredGraph<DotNode *, Filter>>
// This is due to some quirks of how dominator and post dominator trees are
// implemented that conflicts with how we expose predecessor and successor
// edges in GraphTrait<DotNode *>.
// Since nobody need this at the moment it has not been implemented, since it
// would require heavy scrambling of the templates, which is not a priority
// right now.
// We might consider to fix it in the future if someone ends up needing it.
DotGraph Input;
using namespace boost::unit_test::framework;
revng_check(master_test_suite().argc == 2);
std::string FileName = master_test_suite().argv[1];
FileName += "001.dot";
Input.parseDotFromFile(FileName, "initial_block");
const auto EFDominates =
[&Input](auto &DT, const std::string &A, const std::string &B) -> bool {
return DT.dominates(Input.getNodeByName(A), Input.getNodeByName(B));
};
{
using DT = DominatorTreeBase<DotNode, false>;
DT TheDomTree;
TheDomTree.recalculate(Input);
revng_check(TheDomTree.verify());
TheDomTree.print(errs());
revng_check(EFDominates(TheDomTree, "initial_block", "starter"));
revng_check(EFDominates(TheDomTree, "starter", "end"));
revng_check(EFDominates(TheDomTree, "starter", "false"));
revng_check(EFDominates(TheDomTree, "false", "_xit"));
}
{
using PostDT = DominatorTreeBase<DotNode, true>;
PostDT PDT;
PDT.recalculate(Input);
revng_check(PDT.verify());
PDT.print(errs());
const auto *Sink = PDT.getRootNode();
const auto DTNodeDominates = [&Input](auto &DT,
const DomTreeNodeBase<DotNode> *N,
const std::string &B) -> bool {
return DT.dominates(N, DT.getNode(Input.getNodeByName(B)));
};
revng_check(DTNodeDominates(PDT, Sink, "initial_block"));
revng_check(DTNodeDominates(PDT, Sink, "starter"));
revng_check(DTNodeDominates(PDT, Sink, "false"));
revng_check(DTNodeDominates(PDT, Sink, "end"));
revng_check(DTNodeDominates(PDT, Sink, "_xit"));
revng_check(EFDominates(PDT, "starter", "initial_block"));
}
}
BOOST_AUTO_TEST_SUITE_END()