#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include "llvm/Support/GraphWriter.h" #include "revng/ADT/STLExtras.h" #include "revng/Support/Debug.h" #include "revng/Support/Error.h" /// GenericGraph is our implementation of a universal graph architecture. /// First and foremost, it's tailored for the revng codebase, but maybe you /// can find it useful as well, that's why it's released under the MIT License. /// /// To use the Graph, you need to make you choice of the node type. We're /// currently supporting three architectures: /// /// - ForwardNode - a trivially-simple single-linked node. It uses the least /// memory, because each node only stores the list of its successors. /// This makes backwards iteration impossible without the reference to /// the whole graph (and really expensive even in those cases). /// /// - BidirectionalNode - a simple double-linked node. It stores the both /// lists of its successors and predecessors. Take note, that for the sake /// of implementation simplicity this node stores COPIES of the labels. /// It's only suitable to be used with cheap to copy labels which are never /// mutated. There are plans on making them explicitly immutable (TODO), /// as such you can consider label mutation a deprecated behavior. /// /// - MutableEdgeNode - a double-linked node with dynamically allocated /// labels. It is similar to BidirectionalNode except it stores edge labels /// on the heap. It's slower and uses more memory but allows for safe label /// modification as well as controls that nodes and edges are removed /// safely, with the other "halves" cleaned up as well. /// Note that it's disallowed to have a mutable edge graph without edge /// labels. Use `BidirectionalNode` in those cases. /// /// - The node you're going to write - No-one knows the needs of your /// project better than you. That's why the best data structure is the one /// you are going to write. So just inherit one of our nodes, or even copy /// it and modify it so that it suits your graphs as nicely as possible. /// /// On the side-note, we're providing a couple of helpful concepts to help /// differentiate different node types. This is helpful in the projects that /// use multiple different graph architectures side by side. template concept SpecializationOfForwardNode = requires { T::is_forward_node; }; template concept StrictSpecializationOfBidirectionalNode = requires { T::is_bidirectional_node; typename llvm::Inverse; }; template concept StrictSpecializationOfMutableEdgeNode = requires { T::is_mutable_edge_node; typename llvm::Inverse; }; template concept SpecializationOfGenericGraph = requires { T::is_generic_graph; typename T::Node; }; struct Empty { bool operator==(const Empty &) const { return true; } }; template class GenericGraph; template class Parent : public BaseType { public: template explicit Parent(ArgTypes &&...Args) : BaseType(std::forward(Args)...), TheParent(nullptr) {} Parent(const Parent &) = default; Parent(Parent &&) = default; private: T *TheParent = nullptr; public: T *getParent() const { return TheParent; } void setParent(T *Parent) { this->TheParent = Parent; } }; /// Data structure for edge labels template struct Edge : public EdgeLabel { Edge(Node *Neighbor) : Neighbor(Neighbor) {} Edge(Node *Neighbor, EdgeLabel EL) : EdgeLabel(EL), Neighbor(Neighbor) {} Node *Neighbor = nullptr; bool operator==(const Edge &Other) const { return Other.Neighbor == Neighbor and EdgeLabel::operator==(Other); } }; // // Structured binding support for Edge // template auto &get(Edge &E) { if constexpr (I == 0) return E.Neighbor; else return static_cast(E); } template auto &get(const Edge &E) { if constexpr (I == 0) return E.Neighbor; else return static_cast(E); } template auto &&get(Edge &&E) { if constexpr (I == 0) return std::move(E.Neighbor); else return std::move(static_cast(E)); } namespace std { template struct std::tuple_size> : std::integral_constant {}; template struct std::tuple_element<0, Edge> { using type = Node *; }; template struct std::tuple_element<1, Edge> { using type = EdgeLabel; }; } // namespace std namespace revng::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 /// declared, provided that only the real ForwardEdge is used as this /// argument. template class ForwardEdge, typename FinalType, size_t ParentSmallSize, bool ParentHasEntryNode> struct ForwardNodeBaseTCalc { static constexpr bool NoDerivation = std::is_same_v; using FWNode = ForwardEdge; using DerivedType = std::conditional_t; using GenericGraph = GenericGraph; using ParentType = Parent; using Result = std::conditional_t; }; } // namespace revng::detail /// Basic nodes type, only forward edges, possibly with parent template class ForwardNode : public revng::detail::ForwardNodeBaseTCalc::Result { public: static constexpr bool is_forward_node = true; static constexpr bool HasParent = NeedsParent; using TypeCalc = revng::detail::ForwardNodeBaseTCalc; using DerivedType = typename TypeCalc::DerivedType; using Base = typename TypeCalc::Result; using Edge = Edge; using NodeData = Node; public: template explicit ForwardNode(ArgTypes &&...Args) : Base(std::forward(Args)...) {} ForwardNode(const ForwardNode &) = default; ForwardNode(ForwardNode &&) = default; ForwardNode &operator=(const ForwardNode &) = default; ForwardNode &operator=(ForwardNode &&) = default; NodeData &data() { return *this; } const NodeData &data() const { return *this; } NodeData copyData() const { return *this; } NodeData &&moveData() { return std::move(*this); } 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; using child_iterator = llvm::mapped_iterator; using const_child_iterator = llvm::mapped_iterator; 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); } void clearSuccessors() { Successors.clear(); } public: llvm::iterator_range successors() const { return toNeighborRange(Successors); } llvm::iterator_range successors() { return toNeighborRange(Successors); } llvm::iterator_range successor_edges() const { return llvm::make_range(Successors.begin(), Successors.end()); } llvm::iterator_range successor_edges() { return llvm::make_range(Successors.begin(), Successors.end()); } bool hasSuccessors() const { return Successors.size() != 0; } size_t successorCount() const { return Successors.size(); } bool hasSuccessor(DerivedType const *S) const { auto Iterator = llvm::find_if(Successors, [S](auto &N) { return N.Neighbor == S; }); return Iterator != Successors.end(); } protected: static llvm::iterator_range toNeighborRange(NeighborContainer &Neighbors) { auto Range = llvm::make_range(Neighbors.begin(), Neighbors.end()); return llvm::map_range(Range, &getNeighbor); } static llvm::iterator_range toNeighborRange(const NeighborContainer &Neighbors) { auto Range = llvm::make_range(Neighbors.begin(), Neighbors.end()); return llvm::map_range(Range, &getConstNeighbor); } protected: NeighborContainer Successors; }; namespace revng::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 /// BidirectionalEdge that will be declared later. This allows us to use /// it as if it was declared, provided that only the real /// BidirectionalEdge is used as this argument. template class BidirectionalNode> struct BidirectionalNodeBaseTCalc { using BDNode = BidirectionalNode; using Result = ForwardNode; }; } // namespace revng::detail using revng::detail::BidirectionalNodeBaseTCalc; /// Same as ForwardNode, but with backward links too // TODO: Make edge labels immutable template class BidirectionalNode : public BidirectionalNodeBaseTCalc::Result { public: static const bool is_bidirectional_node = true; public: using NodeData = Node; using EdgeLabelData = EdgeLabel; using Base = ForwardNode; 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 explicit BidirectionalNode(ArgTypes &&...Args) : Base(std::forward(Args)...) {} BidirectionalNode(const BidirectionalNode &) = default; BidirectionalNode(BidirectionalNode &&) = default; NodeData &data() { return *this; } const NodeData &data() const { return *this; } NodeData copyData() const { return *this; } NodeData &&moveData() { return std::move(*this); } public: void addSuccessor(BidirectionalNode *NewSuccessor) { Base::Successors.emplace_back(NewSuccessor); NewSuccessor->Predecessors.emplace_back(this); } void addSuccessor(BidirectionalNode *NewSuccessor, EdgeLabel EL) { Base::Successors.emplace_back(NewSuccessor, EL); NewSuccessor->Predecessors.emplace_back(this, EL); } void addPredecessor(BidirectionalNode *NewPredecessor) { Predecessors.emplace_back(NewPredecessor); NewPredecessor->Successors.emplace_back(this); } void addPredecessor(BidirectionalNode *NewPredecessor, EdgeLabel EL) { Predecessors.emplace_back(NewPredecessor, EL); NewPredecessor->Successors.emplace_back(this, EL); } public: void removePredecessor(child_iterator It) { BidirectionalNode *Predecessor = *It; bool Found = false; auto PredecessorSuccessors = Predecessor->successor_edges(); for (auto PredecessorIt = PredecessorSuccessors.begin(), Last = PredecessorSuccessors.end(); PredecessorIt != Last;) { auto Edge = *PredecessorIt; Edge.Neighbor = this; if (Edge == *PredecessorIt) { Predecessor->Successors.erase(PredecessorIt); Found = true; break; } else { ++PredecessorIt; } } revng_assert(Found); Predecessors.erase(It.getCurrent()); } void removePredecessorEdge(edge_iterator It) { Edge Edge = *It; bool Found = false; // Extract predecessor auto Predecessor = Edge.Neighbor; // Invert the edge direction Edge.Neighbor = this; auto PredecessorSuccessors = Predecessor->successor_edges(); for (auto PredecessorIt = PredecessorSuccessors.begin(), Last = PredecessorSuccessors.end(); PredecessorIt != Last;) { if (Edge == *PredecessorIt) { Predecessor->Successors.erase(PredecessorIt); Found = true; break; } else { ++PredecessorIt; } } revng_assert(Found); Predecessors.erase(It); } void clearPredecessors() { while (Predecessors.size()) removePredecessorEdge(Predecessors.begin()); } void removeSuccessor(child_iterator It) { BidirectionalNode *Successor = *It; bool Found = false; auto SuccessorPredecessors = Successor->predecessor_edges(); for (auto SuccessorIt = SuccessorPredecessors.begin(), Last = SuccessorPredecessors.end(); SuccessorIt != Last;) { auto Edge = *SuccessorIt; Edge.Neighbor = this; if (Edge == *SuccessorIt) { Successor->Predecessors.erase(SuccessorIt); Found = true; break; } else { ++SuccessorIt; } } revng_assert(Found); Base::Successors.erase(It.getCurrent()); } void removeSuccessorEdge(edge_iterator It) { auto Edge = *It; bool Found = false; // Extract successor auto Successor = Edge.Neighbor; // Invert edge direction Edge.Neighbor = this; auto SuccessorPredecessors = Successor->predecessor_edges(); for (auto SuccessorIt = SuccessorPredecessors.begin(), Last = SuccessorPredecessors.end(); SuccessorIt != Last;) { if (Edge == *SuccessorIt) { Successor->Predecessors.erase(SuccessorIt); Found = true; break; } else { ++SuccessorIt; } } revng_assert(Found); this->Successors.erase(It); } void clearSuccessors() { while (Base::Successors.size()) removeSuccessorEdge(Base::Successors.begin()); } public: llvm::iterator_range predecessors() const { return this->toNeighborRange(Predecessors); } llvm::iterator_range predecessors() { return this->toNeighborRange(Predecessors); } llvm::iterator_range predecessor_edges() const { return llvm::make_range(Predecessors.begin(), Predecessors.end()); } llvm::iterator_range predecessor_edges() { return llvm::make_range(Predecessors.begin(), Predecessors.end()); } bool hasPredecessors() const { return Predecessors.size() != 0; } size_t predecessorCount() const { return Predecessors.size(); } bool hasPredecessor(BidirectionalNode const *P) const { auto Iterator = llvm::find_if(Predecessors, [P](auto &N) { return N.Neighbor == P; }); return Iterator != Predecessors.end(); } private: NeighborContainer Predecessors; }; namespace revng::detail { /// The parameters deciding specifics of the base type `MutableEdgeNode` /// extends are non-trivial. That's why those decision were wrapped /// inside this struct to minimize clutter. /// /// \note At this step `TheNode` has not been declared yet, thus we accept a /// template parameter that has the same signature as `TheNode` that /// will be declared later. This allows us to use it as if it was /// declared, provided that only the real `TheNode` is used as this /// argument. template class TheNode, typename FinalType, size_t ParentSmallSize, bool ParentHasEntryNode> struct MutableEdgeNodeBaseTCalc { static constexpr bool NoDerivation = std::is_same_v; using NodeType = TheNode; using DerivedType = std::conditional_t; using GenericGraph = GenericGraph; using ParentType = Parent; using Result = std::conditional_t; }; template struct OwningEdge { NodeType *Neighbor = nullptr; std::unique_ptr Label; }; template struct NonOwningEdge { NodeType *Neighbor = nullptr; LabelType *Label = nullptr; }; template struct EdgeView { NodeType *Neighbor = nullptr; LabelType *Label = nullptr; explicit EdgeView(OwningEdge &E) : Neighbor(E.Neighbor), Label(E.Label.get()) {} explicit EdgeView(NonOwningEdge &E) : Neighbor(E.Neighbor), Label(E.Label) {} }; template struct ConstEdgeView { NodeType const *Neighbor; LabelType const *Label; explicit ConstEdgeView(OwningEdge const &E) : Neighbor(E.Neighbor), Label(E.Label.get()) {} explicit ConstEdgeView(NonOwningEdge const &E) : Neighbor(E.Neighbor), Label(E.Label) {} }; } // namespace revng::detail /// A node type suitable for graphs where the edge labels are not cheap /// to copy or need to be modified often. template class MutableEdgeNode : public revng::detail::MutableEdgeNodeBaseTCalc::Result { public: static constexpr bool is_mutable_edge_node = true; static constexpr bool HasParent = NeedsParent; using TypeCalc = revng::detail::MutableEdgeNodeBaseTCalc; using DerivedType = typename TypeCalc::DerivedType; using Base = typename TypeCalc::Result; using NodeData = Node; using EdgeLabelData = EdgeLabel; public: using Edge = EdgeLabel; using EdgeView = revng::detail::EdgeView; using ConstEdgeView = revng::detail::ConstEdgeView; protected: using OwningEdge = revng::detail::OwningEdge; using NonOwningEdge = revng::detail::NonOwningEdge; using EdgeOwnerContainer = llvm::SmallVector; using EdgeViewContainer = llvm::SmallVector; public: template explicit MutableEdgeNode(ArgTypes &&...Args) : Base(std::forward(Args)...) {} MutableEdgeNode(const MutableEdgeNode &) = default; MutableEdgeNode(MutableEdgeNode &&) = default; MutableEdgeNode &operator=(const MutableEdgeNode &) = default; MutableEdgeNode &operator=(MutableEdgeNode &&) = default; NodeData &data() { return *this; } const NodeData &data() const { return *this; } NodeData copyData() const { return *this; } NodeData &&moveData() { return std::move(*this); } public: // This stuff is needed by the DominatorTree implementation void printAsOperand(llvm::raw_ostream &, bool) const { revng_abort(); } public: EdgeView addSuccessor(MutableEdgeNode *NewSuccessor, EdgeLabel EL = {}) { auto &&[Owner, View] = constructEdge(this, NewSuccessor, std::move(EL)); auto &Output = Successors.emplace_back(std::move(Owner)); NewSuccessor->Predecessors.emplace_back(std::move(View)); return EdgeView(Output); } EdgeView addPredecessor(MutableEdgeNode *NewPredecessor, EdgeLabel EL = {}) { auto &&[Owner, View] = constructEdge(NewPredecessor, this, std::move(EL)); auto &Output = NewPredecessor->Successors.emplace_back(std::move(Owner)); Predecessors.emplace_back(std::move(View)); return EdgeView(Output); } protected: struct SuccessorFilters { static EdgeView toView(OwningEdge &E) { return EdgeView(E); } static ConstEdgeView toConstView(OwningEdge const &E) { return ConstEdgeView(E); } static DerivedType *&toNeighbor(OwningEdge &E) { return E.Neighbor; } static DerivedType const *const &toConstNeighbor(OwningEdge const &E) { return E.Neighbor; } }; struct PredecessorFilters { static EdgeView toView(NonOwningEdge &E) { return EdgeView(E); } static ConstEdgeView toConstView(NonOwningEdge const &E) { return ConstEdgeView(E); } static DerivedType *&toNeighbor(NonOwningEdge &E) { return E.Neighbor; } static DerivedType const *const &toConstNeighbor(NonOwningEdge const &E) { return E.Neighbor; } }; private: template using mapped = revng::mapped_iterator; using SuccPointer = OwningEdge *; using CSuccPointer = const OwningEdge *; using PredPointer = NonOwningEdge *; using CPredPointer = const NonOwningEdge *; using RSuccPointer = std::reverse_iterator; using CRSuccPointer = std::reverse_iterator; using RPredPointer = std::reverse_iterator; using CRPredPointer = std::reverse_iterator; using SuccV = std::decay_t; using CSuccV = std::decay_t; using SuccN = std::decay_t; using CSuccN = std::decay_t; using PredV = std::decay_t; using CPredV = std::decay_t; using PredN = std::decay_t; using CPredN = std::decay_t; public: using SuccessorEdgeIterator = mapped; using ConstSuccessorEdgeIterator = mapped; using SuccessorIterator = mapped; using ConstSuccessorIterator = mapped; using PredecessorEdgeIterator = mapped; using ConstPredecessorEdgeIterator = mapped; using PredecessorIterator = mapped; using ConstPredecessorIterator = mapped; using ReverseSuccessorEdgeIterator = mapped; using ConstReverseSuccessorEdgeIterator = mapped; using ReverseSuccessorIterator = mapped; using ConstReverseSuccessorIterator = mapped; using ReversePredecessorEdgeIterator = mapped; using ConstReversePredecessorEdgeIterator = mapped; using ReversePredecessorIterator = mapped; using ConstReversePredecessorIterator = mapped; public: SuccessorEdgeIterator successor_edges_begin() { return revng::map_iterator(Successors.begin(), SuccessorFilters::toView); } ConstSuccessorEdgeIterator successor_edges_begin() const { return revng::map_iterator(Successors.begin(), SuccessorFilters::toConstView); } ConstSuccessorEdgeIterator successor_edges_cbegin() const { return revng::map_iterator(Successors.cbegin(), SuccessorFilters::toConstView); } ReverseSuccessorEdgeIterator successor_edges_rbegin() { return revng::map_iterator(Successors.rbegin(), SuccessorFilters::toView); } ConstReverseSuccessorEdgeIterator successor_edges_rbegin() const { return revng::map_iterator(Successors.rbegin(), SuccessorFilters::toConstView); } ConstReverseSuccessorEdgeIterator successor_edges_crbegin() const { return revng::map_iterator(Successors.crbegin(), SuccessorFilters::toConstView); } SuccessorEdgeIterator successor_edges_end() { return revng::map_iterator(Successors.end(), SuccessorFilters::toView); } ConstSuccessorEdgeIterator successor_edges_end() const { return revng::map_iterator(Successors.end(), SuccessorFilters::toConstView); } ConstSuccessorEdgeIterator successor_edges_cend() const { return revng::map_iterator(Successors.cend(), SuccessorFilters::toConstView); } ReverseSuccessorEdgeIterator successor_edges_rend() { return revng::map_iterator(Successors.rend(), SuccessorFilters::toView); } ConstReverseSuccessorEdgeIterator successor_edges_rend() const { return revng::map_iterator(Successors.rend(), SuccessorFilters::toConstView); } ConstReverseSuccessorEdgeIterator successor_edges_crend() const { return revng::map_iterator(Successors.crend(), SuccessorFilters::toConstView); } public: SuccessorIterator successors_begin() { return revng::map_iterator(Successors.begin(), SuccessorFilters::toNeighbor); } ConstSuccessorIterator successors_begin() const { return revng::map_iterator(Successors.begin(), SuccessorFilters::toConstNeighbor); } ConstSuccessorIterator successors_cbegin() const { return revng::map_iterator(Successors.cbegin(), SuccessorFilters::toConstNeighbor); } ReverseSuccessorIterator successors_rbegin() { return revng::map_iterator(Successors.rbegin(), SuccessorFilters::toNeighbor); } ConstReverseSuccessorIterator successors_rbegin() const { return revng::map_iterator(Successors.rbegin(), SuccessorFilters::toConstNeighbor); } ConstReverseSuccessorIterator successors_crbegin() const { return revng::map_iterator(Successors.crbegin(), SuccessorFilters::toConstNeighbor); } SuccessorIterator successors_end() { return revng::map_iterator(Successors.end(), SuccessorFilters::toNeighbor); } ConstSuccessorIterator successors_end() const { return revng::map_iterator(Successors.end(), SuccessorFilters::toConstNeighbor); } ConstSuccessorIterator successors_cend() const { return revng::map_iterator(Successors.cend(), SuccessorFilters::toConstNeighbor); } ReverseSuccessorIterator successors_rend() { return revng::map_iterator(Successors.rend(), SuccessorFilters::toNeighbor); } ConstReverseSuccessorIterator successors_rend() const { return revng::map_iterator(Successors.rend(), SuccessorFilters::toConstNeighbor); } ConstReverseSuccessorIterator successors_crend() const { return revng::map_iterator(Successors.crend(), SuccessorFilters::toConstNeighbor); } public: PredecessorEdgeIterator predecessor_edges_begin() { return revng::map_iterator(Predecessors.begin(), PredecessorFilters::toView); } ConstPredecessorEdgeIterator predecessor_edges_begin() const { return revng::map_iterator(Predecessors.begin(), PredecessorFilters::toConstView); } ConstPredecessorEdgeIterator predecessor_edges_cbegin() const { return revng::map_iterator(Predecessors.cbegin(), PredecessorFilters::toConstView); } ReversePredecessorEdgeIterator predecessor_edges_rbegin() { return revng::map_iterator(Predecessors.rbegin(), PredecessorFilters::toView); } ConstReversePredecessorEdgeIterator predecessor_edges_rbegin() const { return revng::map_iterator(Predecessors.rbegin(), PredecessorFilters::toConstView); } ConstReversePredecessorEdgeIterator predecessor_edges_crbegin() const { return revng::map_iterator(Predecessors.crbegin(), PredecessorFilters::toConstView); } PredecessorEdgeIterator predecessor_edges_end() { return revng::map_iterator(Predecessors.end(), PredecessorFilters::toView); } ConstPredecessorEdgeIterator predecessor_edges_end() const { return revng::map_iterator(Predecessors.end(), PredecessorFilters::toConstView); } ConstPredecessorEdgeIterator predecessor_edges_cend() const { return revng::map_iterator(Predecessors.cend(), PredecessorFilters::toConstView); } ReversePredecessorEdgeIterator predecessor_edges_rend() { return revng::map_iterator(Predecessors.rend(), PredecessorFilters::toView); } ConstReversePredecessorEdgeIterator predecessor_edges_rend() const { return revng::map_iterator(Predecessors.rend(), PredecessorFilters::toConstView); } ConstReversePredecessorEdgeIterator predecessor_edges_crend() const { return revng::map_iterator(Predecessors.crend(), PredecessorFilters::toConstView); } public: PredecessorIterator predecessors_begin() { return revng::map_iterator(Predecessors.begin(), PredecessorFilters::toNeighbor); } ConstPredecessorIterator predecessors_begin() const { return revng::map_iterator(Predecessors.begin(), PredecessorFilters::toConstNeighbor); } ConstPredecessorIterator predecessors_cbegin() const { return revng::map_iterator(Predecessors.cbegin(), PredecessorFilters::toConstNeighbor); } ReversePredecessorIterator predecessors_rbegin() { return revng::map_iterator(Predecessors.rbegin(), PredecessorFilters::toNeighbor); } ConstReversePredecessorIterator predecessors_rbegin() const { return revng::map_iterator(Predecessors.rbegin(), PredecessorFilters::toConstNeighbor); } ConstReversePredecessorIterator predecessors_crbegin() const { return revng::map_iterator(Predecessors.crbegin(), PredecessorFilters::toConstNeighbor); } PredecessorIterator predecessors_end() { return revng::map_iterator(Predecessors.end(), PredecessorFilters::toNeighbor); } ConstPredecessorIterator predecessors_end() const { return revng::map_iterator(Predecessors.end(), PredecessorFilters::toConstNeighbor); } ConstPredecessorIterator predecessors_cend() const { return revng::map_iterator(Predecessors.cend(), PredecessorFilters::toConstNeighbor); } ReversePredecessorIterator predecessors_rend() { return revng::map_iterator(Predecessors.rend(), PredecessorFilters::toNeighbor); } ConstReversePredecessorIterator predecessors_rend() const { return revng::map_iterator(Predecessors.rend(), PredecessorFilters::toConstNeighbor); } ConstReversePredecessorIterator predecessors_crend() const { return revng::map_iterator(Predecessors.crend(), PredecessorFilters::toConstNeighbor); } public: llvm::iterator_range successor_edges() { return llvm::make_range(successor_edges_begin(), successor_edges_end()); } llvm::iterator_range successor_edges() const { return llvm::make_range(successor_edges_begin(), successor_edges_end()); } llvm::iterator_range successors() { return llvm::make_range(successors_begin(), successors_end()); } llvm::iterator_range successors() const { return llvm::make_range(successors_begin(), successors_end()); } llvm::iterator_range predecessor_edges() { return llvm::make_range(predecessor_edges_begin(), predecessor_edges_end()); } llvm::iterator_range predecessor_edges() const { return llvm::make_range(predecessor_edges_begin(), predecessor_edges_end()); } llvm::iterator_range predecessors() { return llvm::make_range(predecessors_begin(), predecessors_end()); } llvm::iterator_range predecessors() const { return llvm::make_range(predecessors_begin(), predecessors_end()); } private: template static auto findImpl(DerivedType const *N, IteratorType FromIterator, IteratorType ToIterator) { auto Comparator = [N](auto const &Edge) { return Edge.Neighbor == N; }; return std::find_if(FromIterator, ToIterator, Comparator); } template static auto findImpl(DerivedType const *N, ContainerType &&Where) { return findImpl(N, Where.begin(), Where.end()); } static auto findSuccessorHalf(typename EdgeOwnerContainer::iterator Edge, EdgeViewContainer &Halves) { auto Comparator = [Edge](auto const &Half) { return Half.Label == Edge->Label.get(); }; return std::find_if(Halves.begin(), Halves.end(), Comparator); } static auto findPredecessorHalf(typename EdgeViewContainer::iterator Edge, EdgeOwnerContainer &Halves) { auto Comparator = [Edge](auto const &Half) { return Half.Label.get() == Edge->Label; }; return std::find_if(Halves.begin(), Halves.end(), Comparator); } public: SuccessorEdgeIterator findSuccessorEdge(DerivedType const *S) { return SuccessorEdgeIterator(findImpl(S, Successors), SuccessorFilters::toView); } ConstSuccessorEdgeIterator findSuccessorEdge(DerivedType const *S) const { return ConstSuccessorEdgeIterator(findImpl(S, Successors), SuccessorFilters::toConstView); } PredecessorEdgeIterator findPredecessorEdge(DerivedType const *P) { return PredecessorEdgeIterator(findImpl(P, Predecessors), PredecessorFilters::toView); } ConstPredecessorEdgeIterator findPredecessorEdge(DerivedType const *P) const { return ConstPredecessorEdgeIterator(findImpl(P, Predecessors), PredecessorFilters::toConstView); } SuccessorIterator findSuccessor(DerivedType const *S) { return SuccessorIterator(findImpl(S, Successors), SuccessorFilters::toNeighbor); } ConstSuccessorIterator findSuccessor(DerivedType const *S) const { return ConstSuccessorIterator(findImpl(S, Successors), SuccessorFilters::toConstNeighbor); } PredecessorIterator findPredecessor(DerivedType const *P) { return PredecessorIterator(findImpl(P, Predecessors), PredecessorFilters::toNeighbor); } ConstPredecessorIterator findPredecessor(DerivedType const *P) const { return ConstPredecessorIterator(findImpl(P, Predecessors), PredecessorFilters::toConstNeighbor); } public: bool hasSuccessor(DerivedType const *S) const { return findImpl(S, Successors) != Successors.end(); } bool hasPredecessor(DerivedType const *P) const { return findImpl(P, Predecessors) != Predecessors.end(); } public: size_t successorCount() const { return Successors.size(); } size_t predecessorCount() const { return Predecessors.size(); } bool hasSuccessors() const { return Successors.size() != 0; } bool hasPredecessors() const { return Predecessors.size() != 0; } protected: using InternalOwnerIt = typename EdgeOwnerContainer::const_iterator; using InternalViewIt = typename EdgeViewContainer::const_iterator; using InternalOwnerRIt = typename EdgeOwnerContainer::const_reverse_iterator; using InternalViewRIt = typename EdgeViewContainer::const_reverse_iterator; protected: auto removeSuccessorImpl(InternalOwnerIt InputIterator) { if (Successors.empty()) return Successors.end(); auto Iterator = Successors.begin(); std::advance(Iterator, std::distance(Iterator, InputIterator)); revng_assert(Iterator != Successors.end()); // Maybe we should do some extra checks as to whether `Iterator` is valid. auto *Successor = Iterator->Neighbor; revng_assert(!Successor->Predecessors.empty(), "Half of an edge is missing, graph layout is broken."); auto PredecessorIt = findSuccessorHalf(Iterator, Successor->Predecessors); revng_assert(PredecessorIt != Successor->Predecessors.end(), "Half of an edge is missing, graph layout is broken."); std::swap(*PredecessorIt, Successor->Predecessors.back()); Successor->Predecessors.pop_back(); auto AssertHelper = findSuccessorHalf(Iterator, Successor->Predecessors); revng_assert(AssertHelper == Successor->Predecessors.end(), "More than one half is found for a single edge."); std::swap(*Iterator, Successors.back()); Successors.pop_back(); return Iterator; } auto removePredecessorImpl(InternalViewIt InputIterator) { if (Predecessors.empty()) return Predecessors.end(); auto Iterator = Predecessors.begin(); std::advance(Iterator, std::distance(Iterator, InputIterator)); revng_assert(Iterator != Predecessors.end()); // Maybe we should do some extra checks as to whether `Iterator` is valid. auto *Predecessor = Iterator->Neighbor; revng_assert(!Predecessor->Successors.empty(), "Half of an edge is missing, graph layout is broken."); auto SuccessorIt = findPredecessorHalf(Iterator, Predecessor->Successors); revng_assert(SuccessorIt != Predecessor->Successors.end(), "Half of an edge is missing, graph layout is broken."); std::swap(*SuccessorIt, Predecessor->Successors.back()); Predecessor->Successors.pop_back(); auto AssertHelper = findPredecessorHalf(Iterator, Predecessor->Successors); revng_assert(AssertHelper == Predecessor->Successors.end(), "More than one half is found for a single edge."); std::swap(*Iterator, Predecessors.back()); Predecessors.pop_back(); return Iterator; } protected: auto removeSuccessorImpl(InternalOwnerRIt InputIterator) { auto Result = removeSuccessorImpl(std::prev(InputIterator.base())); return std::reverse_iterator(Result); } auto removePredecessorImpl(InternalViewRIt InputIterator) { auto Result = removePredecessorImpl(std::prev(InputIterator.base())); return std::reverse_iterator(Result); } public: auto removeSuccessor(ConstSuccessorEdgeIterator Iterator) { auto Result = removeSuccessorImpl(Iterator.getCurrent()); return SuccessorEdgeIterator(Result, SuccessorFilters::toView); } auto removeSuccessor(ConstSuccessorIterator Iterator) { auto Result = removeSuccessorImpl(Iterator.getCurrent()); return SuccessorIterator(Result, SuccessorFilters::toNeighbor); } auto removeSuccessor(ConstReverseSuccessorEdgeIterator Iterator) { auto Result = removeSuccessorImpl(Iterator.getCurrent()); return ReverseSuccessorEdgeIterator(Result, SuccessorFilters::toView); } auto removeSuccessor(ConstReverseSuccessorIterator Iterator) { auto Result = removeSuccessorImpl(Iterator.getCurrent()); return ReverseSuccessorIterator(Result, SuccessorFilters::toNeighbor); } auto removePredecessor(ConstPredecessorEdgeIterator Iterator) { auto Result = removePredecessorImpl(Iterator.getCurrent()); return ReverseSuccessorEdgeIterator(Result, PredecessorFilters::toView); } auto removePredecessor(ConstPredecessorIterator Iterator) { auto Result = removePredecessorImpl(Iterator.getCurrent()); return ReverseSuccessorEdgeIterator(Result, PredecessorFilters::toNeighbor); } auto removePredecessor(ConstReversePredecessorEdgeIterator Iterator) { auto Result = removePredecessorImpl(Iterator.getCurrent()); return ReversePredecessorEdgeIterator(Result, PredecessorFilters::toView); } auto removePredecessor(ConstReversePredecessorIterator Iterator) { auto Result = removePredecessorImpl(Iterator.getCurrent()); return ReversePredecessorIterator(Result, PredecessorFilters::toNeighbor); } public: auto removeSuccessor(SuccessorEdgeIterator Iterator) { auto Converted = ConstSuccessorEdgeIterator(Iterator.getCurrent(), SuccessorFilters::toConstView); return removeSuccessor(Converted); } auto removeSuccessor(SuccessorIterator Iterator) { auto Converted = ConstSuccessorIterator(Iterator.getCurrent(), SuccessorFilters::toConstNeighbor); return removeSuccessor(Converted); } auto removePredecessor(PredecessorEdgeIterator Iterator) { auto &F = PredecessorFilters::toConstView; auto Converted = ConstPredecessorEdgeIterator(Iterator.getCurrent(), F); return removePredecessor(Converted); } auto removePredecessor(PredecessorIterator Iterator) { auto Conv = ConstPredecessorIterator(Iterator.getCurrent(), PredecessorFilters::toConstNeighbor); return removePredecessor(Conv); } public: auto removeSuccessor(ReverseSuccessorEdgeIterator Iterator) { auto &F = SuccessorFilters::toConstView; auto Conv = ConstReverseSuccessorEdgeIterator(Iterator.getCurrent(), F); return removeSuccessor(Conv); } auto removeSuccessor(ReverseSuccessorIterator Iterator) { auto &F = SuccessorFilters::toConstNeighbor; auto Converted = ConstReverseSuccessorIterator(Iterator.getCurrent(), F); return removeSuccessor(Converted); } auto removePredecessor(ReversePredecessorEdgeIterator Iterator) { auto &F = PredecessorFilters::toConstView; auto Conv = ConstReversePredecessorEdgeIterator(Iterator.getCurrent(), F); return removePredecessor(Conv); } auto removePredecessor(ReversePredecessorIterator Iterator) { auto &F = PredecessorFilters::toConstNeighbor; auto Converted = ConstReversePredecessorIterator(Iterator.getCurrent(), F); return removePredecessor(Converted); } public: void removeSuccessors(DerivedType const *S) { auto Iterator = findImpl(S, Successors); while (Iterator != Successors.end()) { Iterator = removeSuccessorImpl(Iterator); Iterator = findImpl(S, Iterator, Successors.end()); } } void removePredecessors(DerivedType const *P) { auto Iterator = findImpl(P, Predecessors); while (Iterator != Predecessors.end()) { Iterator = removePredecessorImpl(Iterator); Iterator = findImpl(P, Iterator, Predecessors.end()); } } public: void removeSuccessors() { for (auto It = Successors.begin(); It != Successors.end();) It = removeSuccessorImpl(It); revng_assert(Successors.empty()); } void removePredecessors() { for (auto It = Predecessors.begin(); It != Predecessors.end();) It = removePredecessorImpl(It); revng_assert(Predecessors.empty()); } public: MutableEdgeNode &disconnect() { removeSuccessors(); removePredecessors(); return *this; } protected: std::tuple constructEdge(DerivedType *From, DerivedType *To, EdgeLabel &&EL) { OwningEdge O{ To, std::make_unique(std::move(EL)) }; NonOwningEdge V{ From, O.Label.get() }; return { std::move(O), std::move(V) }; } private: EdgeOwnerContainer Successors; EdgeViewContainer Predecessors; }; /// Simple data structure to hold the EntryNode of a GenericGraph template 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 class GenericGraph : public std::conditional_t, Empty> { public: static const bool is_generic_graph = true; using NodesContainer = llvm::SmallVector, 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: GenericGraph() = default; GenericGraph(const GenericGraph &) = delete; GenericGraph(GenericGraph &&) = default; GenericGraph &operator=(const GenericGraph &) = delete; GenericGraph &operator=(GenericGraph &&) = default; llvm::Error verify() const debug_function { llvm::SmallPtrSet ValidNodes; // Collect all valid nodes and ensure there are no nullptr for (const std::unique_ptr &Node : Nodes) { if (Node.get() == nullptr) { return revng::createError("Graph contains a nullptr node"); } ValidNodes.insert(Node.get()); } // Ensure we only point to valid nodes for (const std::unique_ptr &Node : Nodes) { for (NodeT *Successor : Node->successors()) { if (not ValidNodes.contains(Successor)) { return revng::createError("A node contains an unknown successor"); } } if constexpr (StrictSpecializationOfBidirectionalNode) { for (NodeT *Predecessor : Node->predecessors()) { if (not ValidNodes.contains(Predecessor)) { return revng::createError("A node contains an unknown predecessor"); } } } } return llvm::Error::success(); } void dumpGraph() const debug_function { llvm::WriteGraph(this, ""); } public: static NodeT *getNode(std::unique_ptr &E) { return E.get(); } static const NodeT *getConstNode(const std::unique_ptr &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; using const_nodes_iterator = llvm::mapped_iterator; llvm::iterator_range nodes() { return llvm::map_range(llvm::make_range(Nodes.begin(), Nodes.end()), getNode); } llvm::iterator_range nodes() const { return llvm::map_range(llvm::make_range(Nodes.begin(), Nodes.end()), getConstNode); } size_t size() const { return Nodes.size(); } bool empty() const { return Nodes.size() == 0; } public: nodes_iterator findNode(Node const *NodePtr) { auto Comparator = [&NodePtr](auto &N) { return N.get() == NodePtr; }; auto InternalIt = std::find_if(Nodes.begin(), Nodes.end(), Comparator); return nodes_iterator(InternalIt, getNode); } const_nodes_iterator findNode(Node const *NodePtr) const { auto Comparator = [&NodePtr](auto &N) { return N.get() == NodePtr; }; auto InternalIt = std::find_if(Nodes.begin(), Nodes.end(), Comparator); return nodes_iterator(InternalIt, getConstNode); } public: bool hasNodes() const { return Nodes.size() != 0; } bool hasNode(Node const *NodePtr) const { return findNode(NodePtr) != Nodes.end(); } public: NodeT *addNode(std::unique_ptr &&Ptr) { Nodes.emplace_back(std::move(Ptr)); if constexpr (NodeT::HasParent) Nodes.back()->setParent(this); return Nodes.back().get(); } template NodeT *addNode(ArgTypes &&...A) { Nodes.push_back(std::make_unique(std::forward(A)...)); if constexpr (NodeT::HasParent) Nodes.back()->setParent(this); return Nodes.back().get(); } nodes_iterator removeNode(nodes_iterator It) { if constexpr (StrictSpecializationOfMutableEdgeNode) (*It.getCurrent())->disconnect(); auto InternalIt = Nodes.erase(It.getCurrent()); return nodes_iterator(InternalIt, getNode); } nodes_iterator removeNode(Node const *NodePtr) { return removeNode(findNode(NodePtr)); } public: nodes_iterator insertNode(nodes_iterator Where, std::unique_ptr &&Ptr) { auto InternalIt = Nodes.insert(Where.getCurrent(), std::move(Ptr)); return nodes_iterator(InternalIt, getNode); } template nodes_iterator insertNode(nodes_iterator Where, ArgTypes &&...A) { auto Pointer = std::make_unique(std::forward(A)...); auto InternalIt = Nodes.insert(Where.getCurrent(), std::move(Pointer)); return nodes_iterator(InternalIt, getNode); } public: void reserve(size_t Size) { Nodes.reserve(Size); } void clear() { Nodes.clear(); } protected: NodesContainer Nodes; }; // // GraphTraits implementation for GenericGraph // namespace llvm { /// Specializes GraphTraits *> template struct GraphTraits { public: using NodeRef = T *; using ChildIteratorType = std::conditional_t, typename T::const_child_iterator, typename T::child_iterator>; using EdgeRef = typename T::Edge &; template using if_const_t = std::conditional_t, True, False>; using ChildEdgeIteratorType = if_const_t; 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; }; }; /// Specializes GraphTraits *>> template struct GraphTraits> { public: using NodeRef = T *; using ChildIteratorType = std::conditional_t, typename T::const_child_iterator, typename T::child_iterator>; using EdgeRef = typename T::Edge &; template using if_const_t = std::conditional_t, True, False>; using ChildEdgeIteratorType = if_const_t; public: static ChildIteratorType child_begin(NodeRef N) { return N->predecessors().begin(); } static ChildIteratorType child_end(NodeRef N) { return N->predecessors().end(); } static ChildEdgeIteratorType child_edge_begin(NodeRef N) { return N->predecessor_edges().begin(); } static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->predecessor_edges().end(); } static NodeRef edge_dest(EdgeRef Edge) { return Edge.Neighbor; } static NodeRef getEntryNode(llvm::Inverse N) { return N.Graph; }; }; // TODO: implement const version of GraphTraits /// Specializes GraphTraits *> template struct GraphTraits { public: using NodeRef = T *; using EdgeRef = typename T::EdgeView; private: using ChildNodeIt = decltype(std::declval().successors().begin()); using ChildEdgeIt = decltype(std::declval().successor_edges().begin()); public: using ChildIteratorType = ChildNodeIt; using ChildEdgeIteratorType = ChildEdgeIt; public: static auto child_begin(T *N) { return N->successors().begin(); } static auto child_end(T *N) { return N->successors().end(); } static auto child_edge_begin(T *N) { return N->successor_edges().begin(); } static auto child_edge_end(T *N) { return N->successor_edges().end(); } static T *edge_dest(EdgeRef Edge) { return Edge.Neighbor; } static T *getEntryNode(T *N) { return N; }; }; // TODO: implement const version of GraphTraits /// Specializes GraphTraits *>> template struct GraphTraits> { public: using NodeRef = T *; using EdgeRef = typename T::EdgeView; private: using ChildNodeIt = decltype(std::declval().predecessors().begin()); using ChildEdgeIt = decltype(std::declval().predecessor_edges().begin()); public: using ChildIteratorType = ChildNodeIt; using ChildEdgeIteratorType = ChildEdgeIt; public: static auto child_begin(T *N) { return N->predecessors().begin(); } static auto child_end(T *N) { return N->predecessors().end(); } static auto child_edge_begin(T *N) { return N->predecessor_edges().begin(); } static auto child_edge_end(T *N) { return N->predecessor_edges().end(); } static T *edge_dest(EdgeRef Edge) { return &Edge.Neighbor; } static T *getEntryNode(llvm::Inverse N) { return N.Graph; }; }; /// Specializes GraphTraits *>> template struct GraphTraits : public GraphTraits, const typename T::Node *, typename T::Node *>> { using NodeRef = std::conditional_t, const typename T::Node *, typename T::Node *>; using nodes_iterator = std::conditional_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(); } }; /// Specializes GraphTraits *>>> template struct GraphTraits> : public GraphTraits< llvm::Inverse, const typename T::Node *, typename T::Node *>>> { using NodeRef = std::conditional_t, const typename T::Node *, typename T::Node *>; using nodes_iterator = std::conditional_t, typename T::const_nodes_iterator, typename T::nodes_iterator>; static NodeRef getEntryNode(llvm::Inverse Inv) { // TODO: we might want to consider an option of having optional // `ExitNode`s as well, for consistency. return Inv.Graph->getEntryNode(); } static nodes_iterator nodes_begin(llvm::Inverse Inv) { return Inv.Graph->nodes().begin(); } static nodes_iterator nodes_end(llvm::Inverse Inv) { return Inv.Graph->nodes().end(); } static size_t size(T *G) { return G->size(); } }; } // namespace llvm