Files
revng-revng/include/revng/Support/GraphAlgorithms.h
2021-04-22 18:05:39 +02:00

199 lines
5.2 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <set>
#include <vector>
#include "llvm/ADT/GraphTraits.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "revng/Support/Debug.h"
template<typename GT, typename NodeRef = typename GT::NodeRef>
inline llvm::SmallPtrSet<NodeRef, 4>
nodesBetweenImpl(NodeRef Source,
NodeRef Destination,
const llvm::SmallPtrSetImpl<NodeRef> *IgnoreList) {
using Iterator = typename GT::ChildIteratorType;
using NodeSet = llvm::SmallPtrSet<NodeRef, 4>;
auto HasSuccessors = [](const NodeRef Node) {
return GT::child_begin(Node) != GT::child_end(Node);
};
// Ensure Source has at least one successor
if (not HasSuccessors(Source)) {
if (Source == Destination)
return { Source };
else
return {};
}
NodeSet Selected = { Destination };
NodeSet VisitedNodes;
struct StackEntry {
StackEntry(NodeRef Node) :
Node(Node),
Set({ Node }),
NextSuccessorIt(GT::child_begin(Node)),
EndSuccessorIt(GT::child_end(Node)) {}
NodeRef Node;
NodeSet Set;
Iterator NextSuccessorIt;
Iterator EndSuccessorIt;
};
std::vector<StackEntry> Stack;
Stack.emplace_back(Source);
while (not Stack.empty()) {
StackEntry *Entry = &Stack.back();
NodeRef CurrentSuccessor = *Entry->NextSuccessorIt;
bool Visited = (VisitedNodes.count(CurrentSuccessor) != 0);
VisitedNodes.insert(CurrentSuccessor);
if (Selected.count(CurrentSuccessor) != 0) {
// We reached a selected node, select all the nodes on the stack
for (const StackEntry &E : Stack) {
Selected.insert(E.Set.begin(), E.Set.end());
}
} else if (Visited) {
// We already visited this node, do not proceed in this direction
auto End = Stack.end();
auto IsCurrent = [CurrentSuccessor](const StackEntry &E) {
return E.Set.count(CurrentSuccessor) != 0;
};
auto It = std::find_if(Stack.begin(), End, IsCurrent);
bool IsAlreadyOnStack = It != End;
if (IsAlreadyOnStack) {
// It's already on the stack, insert all those on stack until the top
StackEntry &Target = *It;
Target.Set.insert(CurrentSuccessor);
++It;
for (const StackEntry &E : llvm::make_range(It, End)) {
Target.Set.insert(E.Set.begin(), E.Set.end());
}
}
} else if (IgnoreList != nullptr
and IgnoreList->count(CurrentSuccessor) != 0) {
// Ignore
} else {
// We never visited this node, proceed to its successors, if any
if (HasSuccessors(CurrentSuccessor)) {
revng_assert(CurrentSuccessor != nullptr);
Stack.emplace_back(CurrentSuccessor);
}
continue;
}
bool TryNext = false;
do {
// Move to the next successor
++Entry->NextSuccessorIt;
// Are we done with this entry?
TryNext = (Entry->NextSuccessorIt == Entry->EndSuccessorIt);
if (TryNext) {
// Pop from the stack
Stack.pop_back();
// If there's another element process it
if (Stack.size() == 0) {
TryNext = false;
} else {
Entry = &Stack.back();
}
}
} while (TryNext);
}
return Selected;
}
template<class G>
inline llvm::SmallPtrSet<G, 4>
nodesBetween(G Source,
G Destination,
const llvm::SmallPtrSetImpl<G> *IgnoreList = nullptr) {
return nodesBetweenImpl<llvm::GraphTraits<G>>(Source,
Destination,
IgnoreList);
}
template<class G>
inline llvm::SmallPtrSet<G, 4>
nodesBetweenReverse(G Source,
G Destination,
const llvm::SmallPtrSetImpl<G> *IgnoreList = nullptr) {
using namespace llvm;
return nodesBetweenImpl<GraphTraits<Inverse<G>>>(Source,
Destination,
IgnoreList);
}
template<typename T>
struct scc_iterator_traits {
using iterator = llvm::scc_iterator<T, llvm::GraphTraits<T>>;
using iterator_category = std::forward_iterator_tag;
using reference = decltype(*llvm::scc_begin((T) nullptr));
using value_type = std::remove_reference_t<reference>;
using pointer = value_type *;
using difference_type = size_t;
};
template<typename NodeTy>
auto exitless_scc_range(NodeTy Entry) {
using namespace llvm;
auto Range = make_range(scc_begin(Entry), scc_end(Entry));
using NodesVector = std::vector<NodeTy>;
using GT = llvm::GraphTraits<NodeTy>;
auto Filter = [](const NodesVector &SCC) {
std::set<NodeTy> SCCNodes;
SCCNodes.clear();
for (NodeTy BB : SCC)
SCCNodes.insert(BB);
bool HasExit = false;
bool AtLeastOneEdge = false;
for (NodeTy BB : SCC) {
auto Successors = make_range(GT::child_begin(BB), GT::child_end(BB));
for (NodeTy Successor : Successors) {
AtLeastOneEdge = true;
if (SCCNodes.count(Successor) == 0) {
HasExit = true;
break;
}
}
if (HasExit)
break;
}
return (not HasExit) and AtLeastOneEdge;
};
return make_filter_range(Range, Filter);
}