mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
871931b9cd
`TupleTreeDiff` was not being tested. This commit implements several `operator==` that are required to successfully instantiate `TupleTreeDiff`.
534 lines
17 KiB
C++
534 lines
17 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <type_traits>
|
|
|
|
#include "llvm/ADT/GraphTraits.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "revng/Support/Debug.h"
|
|
|
|
struct Empty {
|
|
bool operator==(const Empty &) const { return true; }
|
|
};
|
|
|
|
template<typename Node, size_t SmallSize = 16, bool HasEntryNode = true>
|
|
class GenericGraph;
|
|
|
|
template<typename T, typename BaseType>
|
|
class Parent : public BaseType {
|
|
public:
|
|
template<typename... Args>
|
|
explicit Parent(Args &&... args) :
|
|
BaseType(std::forward<Args>(args)...), TheParent(nullptr) {}
|
|
|
|
Parent(const Parent &) = default;
|
|
Parent(Parent &&) = default;
|
|
|
|
private:
|
|
T *TheParent;
|
|
|
|
public:
|
|
T *getParent() const { return TheParent; }
|
|
void setParent(T *Parent) { this->TheParent = Parent; }
|
|
};
|
|
|
|
/// Data structure for edge labels
|
|
template<typename Node, typename EdgeLabel>
|
|
struct Edge : public EdgeLabel {
|
|
Edge(Node *Neighbor) : Neighbor(Neighbor) {}
|
|
Edge(Node *Neighbor, EdgeLabel EL) : EdgeLabel(EL), Neighbor(Neighbor) {}
|
|
Node *Neighbor;
|
|
};
|
|
|
|
//
|
|
// Structured binding support for Edge
|
|
//
|
|
template<size_t I, typename Node, typename EdgeLabel>
|
|
auto &get(Edge<Node, EdgeLabel> &E) {
|
|
if constexpr (I == 0)
|
|
return E.Neighbor;
|
|
else
|
|
return static_cast<EdgeLabel &>(E);
|
|
}
|
|
|
|
template<size_t I, typename Node, typename EdgeLabel>
|
|
auto &get(const Edge<Node, EdgeLabel> &E) {
|
|
if constexpr (I == 0)
|
|
return E.Neighbor;
|
|
else
|
|
return static_cast<EdgeLabel &>(E);
|
|
}
|
|
|
|
template<size_t I, typename Node, typename EdgeLabel>
|
|
auto &&get(Edge<Node, EdgeLabel> &&E) {
|
|
if constexpr (I == 0)
|
|
return std::move(E.Neighbor);
|
|
else
|
|
return std::move(static_cast<EdgeLabel &&>(E));
|
|
}
|
|
|
|
namespace std {
|
|
template<typename Node, typename EdgeLabel>
|
|
struct std::tuple_size<Edge<Node, EdgeLabel>>
|
|
: std::integral_constant<size_t, 2> {};
|
|
|
|
template<typename Node, typename EdgeLabel>
|
|
struct std::tuple_element<0, Edge<Node, EdgeLabel>> {
|
|
using type = Node *;
|
|
};
|
|
|
|
template<typename Node, typename EdgeLabel>
|
|
struct std::tuple_element<1, Edge<Node, EdgeLabel>> {
|
|
using type = EdgeLabel;
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
namespace detail {
|
|
/// We require to operate some decision to select a base type that Forward node
|
|
/// will extend. Those decisions are wrapped inside this struct to remove
|
|
/// clutter.
|
|
///
|
|
/// \note At this step Forward edge has not been declared yet, thus we accept a
|
|
/// template parameter that has the same signature as ForwardEdge that
|
|
/// will be declared later. This allows us to use it as if it was
|
|
/// delcared, provided that only the real ForwardEdge is used as this
|
|
/// argument.
|
|
template<typename Node,
|
|
typename EdgeLabel,
|
|
bool HasParent,
|
|
size_t SmallSize,
|
|
template<typename, typename, bool, size_t, typename, size_t, bool>
|
|
class ForwardEdge,
|
|
typename FinalType,
|
|
size_t ParentSmallSize,
|
|
bool ParentHasEntryNode>
|
|
struct ForwardNodeBaseTCalc {
|
|
static constexpr bool
|
|
NoDerivation = std::is_same_v<FinalType, std::false_type>;
|
|
using FWNode = ForwardEdge<Node,
|
|
EdgeLabel,
|
|
HasParent,
|
|
SmallSize,
|
|
FinalType,
|
|
ParentSmallSize,
|
|
ParentHasEntryNode>;
|
|
using DerivedType = std::conditional_t<NoDerivation, FWNode, FinalType>;
|
|
using GenericGraph = GenericGraph<DerivedType,
|
|
ParentSmallSize,
|
|
ParentHasEntryNode>;
|
|
using ParentType = Parent<GenericGraph, Node>;
|
|
using Result = std::conditional_t<HasParent, ParentType, Node>;
|
|
};
|
|
} // namespace detail
|
|
|
|
/// Basic nodes type, only forward edges, possibly with parent
|
|
template<typename Node,
|
|
typename EdgeLabel = Empty,
|
|
bool HasParent = true,
|
|
size_t SmallSize = 2,
|
|
typename FinalType = std::false_type,
|
|
size_t ParentSmallSize = 16,
|
|
bool ParentHasEntryNode = true>
|
|
class ForwardNode
|
|
: public detail::ForwardNodeBaseTCalc<Node,
|
|
EdgeLabel,
|
|
HasParent,
|
|
SmallSize,
|
|
ForwardNode,
|
|
FinalType,
|
|
ParentSmallSize,
|
|
ParentHasEntryNode>::Result {
|
|
public:
|
|
static constexpr bool is_forward_node = true;
|
|
static constexpr bool has_parent = HasParent;
|
|
using TypeCalc = detail::ForwardNodeBaseTCalc<Node,
|
|
EdgeLabel,
|
|
HasParent,
|
|
SmallSize,
|
|
ForwardNode,
|
|
FinalType,
|
|
ParentSmallSize,
|
|
ParentHasEntryNode>;
|
|
using DerivedType = typename TypeCalc::DerivedType;
|
|
using Base = typename TypeCalc::Result;
|
|
using Edge = Edge<DerivedType, EdgeLabel>;
|
|
|
|
public:
|
|
template<typename... Args>
|
|
explicit ForwardNode(Args &&... args) : Base(std::forward<Args>(args)...) {}
|
|
|
|
ForwardNode(const ForwardNode &) = default;
|
|
ForwardNode(ForwardNode &&) = default;
|
|
|
|
public:
|
|
static DerivedType *&getNeighbor(Edge &E) { return E.Neighbor; }
|
|
static const DerivedType *const &getConstNeighbor(const Edge &E) {
|
|
return E.Neighbor;
|
|
}
|
|
|
|
static Edge *getEdgePointer(Edge &E) { return &E; }
|
|
static const Edge *getConstEdgePointer(const Edge &E) { return &E; }
|
|
|
|
private:
|
|
using iterator_filter = decltype(&getNeighbor);
|
|
using const_iterator_filter = decltype(&getConstNeighbor);
|
|
|
|
public:
|
|
using NeighborContainer = llvm::SmallVector<Edge, SmallSize>;
|
|
using child_iterator = llvm::mapped_iterator<Edge *, iterator_filter>;
|
|
using const_child_iterator = llvm::mapped_iterator<const Edge *,
|
|
const_iterator_filter>;
|
|
using edge_iterator = typename NeighborContainer::iterator;
|
|
using const_edge_iterator = typename NeighborContainer::const_iterator;
|
|
|
|
public:
|
|
// This stuff is needed by the DominatorTree implementation
|
|
void printAsOperand(llvm::raw_ostream &, bool) const { revng_abort(); }
|
|
|
|
public:
|
|
void addSuccessor(DerivedType *NewSuccessor) {
|
|
Successors.emplace_back(NewSuccessor);
|
|
}
|
|
|
|
void addSuccessor(DerivedType *NewSuccessor, EdgeLabel EL) {
|
|
Successors.emplace_back(NewSuccessor, EL);
|
|
}
|
|
|
|
public:
|
|
child_iterator removeSuccessor(child_iterator It) {
|
|
auto InternalIt = Successors.erase(It.getCurrent());
|
|
return child_iterator(InternalIt, getNeighbor);
|
|
}
|
|
|
|
edge_iterator removeSuccessorEdge(edge_iterator It) {
|
|
return Successors.erase(It);
|
|
}
|
|
|
|
public:
|
|
llvm::iterator_range<const_child_iterator> successors() const {
|
|
return toNeighborRange(Successors);
|
|
}
|
|
|
|
llvm::iterator_range<child_iterator> successors() {
|
|
return toNeighborRange(Successors);
|
|
}
|
|
|
|
llvm::iterator_range<const_edge_iterator> successor_edges() const {
|
|
return llvm::make_range(Successors.begin(), Successors.end());
|
|
}
|
|
|
|
llvm::iterator_range<edge_iterator> successor_edges() {
|
|
return llvm::make_range(Successors.begin(), Successors.end());
|
|
}
|
|
|
|
bool hasSuccessors() const { return Successors.size() != 0; }
|
|
|
|
protected:
|
|
static llvm::iterator_range<child_iterator>
|
|
toNeighborRange(NeighborContainer &Neighbors) {
|
|
auto Range = llvm::make_range(Neighbors.begin(), Neighbors.end());
|
|
return llvm::map_range(Range, &getNeighbor);
|
|
}
|
|
|
|
static llvm::iterator_range<const_child_iterator>
|
|
toNeighborRange(const NeighborContainer &Neighbors) {
|
|
auto Range = llvm::make_range(Neighbors.begin(), Neighbors.end());
|
|
return llvm::map_range(Range, &getConstNeighbor);
|
|
}
|
|
|
|
private:
|
|
NeighborContainer Successors;
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
/// To remove clutter from BidirectionalNode, the computation of some types are
|
|
/// done in this class.
|
|
///
|
|
/// \note At this step BidirectionalEdge has not been declared yet, thus we
|
|
/// accept a template parameter that has the same signature as
|
|
/// BidirecationEdge that will be declared later. This allows us to use it
|
|
/// as if it was delcared, provided that only the real BidirecationalEdge
|
|
/// is used as this argument.
|
|
template<typename Node,
|
|
typename EdgeLabel,
|
|
bool HasParent,
|
|
size_t SmallSize,
|
|
template<typename, typename, bool, size_t>
|
|
class BidirectionalNode>
|
|
struct BidirectionalNodeBaseTCalc {
|
|
using BDNode = BidirectionalNode<Node, EdgeLabel, HasParent, SmallSize>;
|
|
using Result = ForwardNode<Node, EdgeLabel, HasParent, SmallSize, BDNode>;
|
|
};
|
|
} // namespace detail
|
|
|
|
/// Same as ForwardNode, but with backward links too
|
|
template<typename Node,
|
|
typename EdgeLabel = Empty,
|
|
bool HasParent = true,
|
|
size_t SmallSize = 2>
|
|
class BidirectionalNode
|
|
: public detail::BidirectionalNodeBaseTCalc<Node,
|
|
EdgeLabel,
|
|
HasParent,
|
|
SmallSize,
|
|
BidirectionalNode>::Result {
|
|
public:
|
|
static const bool is_bidirectional_node = true;
|
|
|
|
public:
|
|
using NodeData = Node;
|
|
using EdgeLabelData = EdgeLabel;
|
|
using Base = ForwardNode<Node,
|
|
EdgeLabel,
|
|
HasParent,
|
|
SmallSize,
|
|
BidirectionalNode>;
|
|
using NeighborContainer = typename Base::NeighborContainer;
|
|
using child_iterator = typename Base::child_iterator;
|
|
using const_child_iterator = typename Base::const_child_iterator;
|
|
using edge_iterator = typename Base::edge_iterator;
|
|
using const_edge_iterator = typename Base::const_edge_iterator;
|
|
|
|
public:
|
|
template<typename... Args>
|
|
explicit BidirectionalNode(Args &&... args) :
|
|
Base(std::forward<Args>(args)...) {}
|
|
|
|
BidirectionalNode(const BidirectionalNode &) = default;
|
|
BidirectionalNode(BidirectionalNode &&) = default;
|
|
|
|
public:
|
|
void addSuccessor(BidirectionalNode *NewSuccessor) {
|
|
Base::addSuccessor({ NewSuccessor });
|
|
NewSuccessor->Predecessors.emplace_back(this);
|
|
}
|
|
|
|
void addSuccessor(BidirectionalNode *NewSuccessor, EdgeLabel EL) {
|
|
Base::addSuccessor(NewSuccessor, EL);
|
|
NewSuccessor->Predecessors.emplace_back(this, EL);
|
|
}
|
|
|
|
void addPredecessor(BidirectionalNode *NewPredecessor) {
|
|
Predecessors.emplace_back(NewPredecessor);
|
|
NewPredecessor->addSuccessor(this);
|
|
}
|
|
|
|
void addPredecessor(BidirectionalNode *NewPredecessor, EdgeLabel EL) {
|
|
Predecessors.emplace_back(NewPredecessor, EL);
|
|
NewPredecessor->addSuccessor(this, EL);
|
|
}
|
|
|
|
public:
|
|
child_iterator removePredecessor(child_iterator It) {
|
|
auto InternalIt = Predecessors.erase(It.getCurrent());
|
|
return child_iterator(InternalIt, Base::getNeighbor);
|
|
}
|
|
|
|
edge_iterator removePredecessorEdge(edge_iterator It) {
|
|
return Predecessors.erase(It);
|
|
}
|
|
|
|
public:
|
|
llvm::iterator_range<const_child_iterator> predecessors() const {
|
|
return this->toNeighborRange(Predecessors);
|
|
}
|
|
|
|
llvm::iterator_range<child_iterator> predecessors() {
|
|
return this->toNeighborRange(Predecessors);
|
|
}
|
|
|
|
llvm::iterator_range<const_edge_iterator> predecessor_edges() const {
|
|
return llvm::make_range(Predecessors.begin(), Predecessors.end());
|
|
}
|
|
|
|
llvm::iterator_range<edge_iterator> predecessor_edges() {
|
|
return llvm::make_range(Predecessors.begin(), Predecessors.end());
|
|
}
|
|
|
|
bool hasPredecessors() const { return Predecessors.size() != 0; }
|
|
|
|
private:
|
|
NeighborContainer Predecessors;
|
|
};
|
|
|
|
/// Simple data structure to hold the EntryNode of a GenericGraph
|
|
template<typename NodeT>
|
|
class EntryNode {
|
|
private:
|
|
NodeT *EntryNode = nullptr;
|
|
|
|
public:
|
|
NodeT *getEntryNode() const { return EntryNode; }
|
|
void setEntryNode(NodeT *EntryNode) { this->EntryNode = EntryNode; }
|
|
};
|
|
|
|
/// Generic graph parametrized in the node type
|
|
///
|
|
/// This graph owns its nodes (but not the edges).
|
|
/// It can optionally have an elected entry point.
|
|
template<typename NodeT, size_t SmallSize, bool HasEntryNode>
|
|
class GenericGraph
|
|
: public std::conditional_t<HasEntryNode, EntryNode<NodeT>, Empty> {
|
|
public:
|
|
static const bool is_generic_graph = true;
|
|
using NodesContainer = llvm::SmallVector<std::unique_ptr<NodeT>, SmallSize>;
|
|
using Node = NodeT;
|
|
static constexpr bool hasEntryNode = HasEntryNode;
|
|
|
|
private:
|
|
using nodes_iterator_impl = typename NodesContainer::iterator;
|
|
using const_nodes_iterator_impl = typename NodesContainer::const_iterator;
|
|
|
|
public:
|
|
static NodeT *getNode(std::unique_ptr<NodeT> &E) { return E.get(); }
|
|
static const NodeT *getConstNode(const std::unique_ptr<NodeT> &E) {
|
|
return E.get();
|
|
}
|
|
|
|
// TODO: these iterators will not work with llvm::filter_iterator,
|
|
// since the mapped type is not a reference
|
|
using nodes_iterator = llvm::mapped_iterator<nodes_iterator_impl,
|
|
decltype(&getNode)>;
|
|
using const_nodes_iterator = llvm::mapped_iterator<const_nodes_iterator_impl,
|
|
decltype(&getConstNode)>;
|
|
|
|
llvm::iterator_range<nodes_iterator> nodes() {
|
|
return llvm::map_range(llvm::make_range(Nodes.begin(), Nodes.end()),
|
|
getNode);
|
|
}
|
|
|
|
llvm::iterator_range<const_nodes_iterator> nodes() const {
|
|
return llvm::map_range(llvm::make_range(Nodes.begin(), Nodes.end()),
|
|
getConstNode);
|
|
}
|
|
|
|
size_t size() const { return Nodes.size(); }
|
|
|
|
public:
|
|
template<class... Args>
|
|
NodeT *addNode(Args &&... A) {
|
|
Nodes.push_back(std::make_unique<NodeT>(std::forward<Args>(A)...));
|
|
if constexpr (NodeT::has_parent)
|
|
Nodes.back()->setParent(this);
|
|
return Nodes.back().get();
|
|
}
|
|
|
|
nodes_iterator removeNode(nodes_iterator It) {
|
|
auto InternalIt = Nodes.erase(It.getCurrent());
|
|
return nodes_iterator(InternalIt, getNode);
|
|
}
|
|
|
|
private:
|
|
NodesContainer Nodes;
|
|
};
|
|
|
|
template<typename T>
|
|
concept IsForwardNode = requires {
|
|
T::is_forward_node;
|
|
};
|
|
|
|
template<typename T>
|
|
concept IsBidirectionalNode = requires {
|
|
T::is_bidirectional_node;
|
|
typename llvm::Inverse<T *>;
|
|
};
|
|
|
|
template<typename T>
|
|
concept IsGenericGraph = requires {
|
|
T::is_generic_graph;
|
|
typename T::Node;
|
|
};
|
|
|
|
//
|
|
// GraphTraits implementation for GenericGraph
|
|
//
|
|
namespace llvm {
|
|
|
|
/// Implement GraphTraits<ForwardNode>
|
|
template<IsForwardNode T>
|
|
struct GraphTraits<T *> {
|
|
public:
|
|
using NodeRef = T *;
|
|
using ChildIteratorType = std::conditional_t<std::is_const_v<T>,
|
|
typename T::const_child_iterator,
|
|
typename T::child_iterator>;
|
|
|
|
using EdgeRef = typename T::Edge &;
|
|
template<typename Ty, typename True, typename False>
|
|
using if_const = std::conditional_t<std::is_const_v<Ty>, True, False>;
|
|
using ChildEdgeIteratorType = if_const<T,
|
|
typename T::const_edge_iterator,
|
|
typename T::edge_iterator>;
|
|
|
|
public:
|
|
static ChildIteratorType child_begin(NodeRef N) {
|
|
return N->successors().begin();
|
|
}
|
|
|
|
static ChildIteratorType child_end(NodeRef N) {
|
|
return N->successors().end();
|
|
}
|
|
|
|
static ChildEdgeIteratorType child_edge_begin(NodeRef N) {
|
|
return N->successor_edges().begin();
|
|
}
|
|
|
|
static ChildEdgeIteratorType child_edge_end(NodeRef N) {
|
|
return N->successor_edges().end();
|
|
}
|
|
|
|
static NodeRef edge_dest(EdgeRef Edge) { return Edge.Neighbor; }
|
|
|
|
static NodeRef getEntryNode(NodeRef N) { return N; };
|
|
};
|
|
|
|
/// Implement GraphTraits<GenericGraph>
|
|
template<IsGenericGraph T>
|
|
struct GraphTraits<T *> : public GraphTraits<typename T::Node *> {
|
|
|
|
using NodeRef = std::conditional_t<std::is_const_v<T>,
|
|
const typename T::Node *,
|
|
typename T::Node *>;
|
|
using nodes_iterator = std::conditional_t<std::is_const_v<T>,
|
|
typename T::const_nodes_iterator,
|
|
typename T::nodes_iterator>;
|
|
|
|
static NodeRef getEntryNode(T *G) { return G->getEntryNode(); }
|
|
|
|
static nodes_iterator nodes_begin(T *G) { return G->nodes().begin(); }
|
|
|
|
static nodes_iterator nodes_end(T *G) { return G->nodes().end(); }
|
|
|
|
static size_t size(T *G) { return G->size(); }
|
|
};
|
|
|
|
/// Implement GraphTraits<Inverse<BidirectionalNode>>
|
|
template<IsBidirectionalNode T>
|
|
struct GraphTraits<llvm::Inverse<T *>> {
|
|
public:
|
|
using NodeRef = T *;
|
|
using ChildIteratorType = std::conditional_t<std::is_const_v<T>,
|
|
typename T::const_child_iterator,
|
|
typename T::child_iterator>;
|
|
|
|
public:
|
|
static ChildIteratorType child_begin(NodeRef N) {
|
|
return N->predecessors().begin();
|
|
}
|
|
|
|
static ChildIteratorType child_end(NodeRef N) {
|
|
return N->predecessors().end();
|
|
}
|
|
|
|
static NodeRef getEntryNode(llvm::Inverse<NodeRef> N) { return N.Graph; };
|
|
};
|
|
|
|
} // namespace llvm
|