Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/ImmutableGraph.h
35294 views
//==========-- ImmutableGraph.h - A fast DAG implementation ---------=========//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7/// \file8/// Description: ImmutableGraph is a fast DAG implementation that cannot be9/// modified, except by creating a new ImmutableGraph. ImmutableGraph is10/// implemented as two arrays: one containing nodes, and one containing edges.11/// The advantages to this implementation are two-fold:12/// 1. Iteration and traversal operations benefit from cache locality.13/// 2. Operations on sets of nodes/edges are efficient, and representations of14/// those sets in memory are compact. For instance, a set of edges is15/// implemented as a bit vector, wherein each bit corresponds to one edge in16/// the edge array. This implies a lower bound of 64x spatial improvement17/// over, e.g., an llvm::DenseSet or llvm::SmallSet. It also means that18/// insert/erase/contains operations complete in negligible constant time:19/// insert and erase require one load and one store, and contains requires20/// just one load.21///22//===----------------------------------------------------------------------===//2324#ifndef LLVM_LIB_TARGET_X86_IMMUTABLEGRAPH_H25#define LLVM_LIB_TARGET_X86_IMMUTABLEGRAPH_H2627#include "llvm/ADT/BitVector.h"28#include "llvm/ADT/GraphTraits.h"29#include "llvm/ADT/STLExtras.h"30#include <algorithm>31#include <iterator>32#include <utility>33#include <vector>3435namespace llvm {3637template <typename NodeValueT, typename EdgeValueT> class ImmutableGraph {38using Traits = GraphTraits<ImmutableGraph<NodeValueT, EdgeValueT> *>;39template <typename> friend class ImmutableGraphBuilder;4041public:42using node_value_type = NodeValueT;43using edge_value_type = EdgeValueT;44using size_type = int;45class Node;46class Edge {47friend class ImmutableGraph;48template <typename> friend class ImmutableGraphBuilder;4950const Node *Dest;51edge_value_type Value;5253public:54const Node *getDest() const { return Dest; };55const edge_value_type &getValue() const { return Value; }56};57class Node {58friend class ImmutableGraph;59template <typename> friend class ImmutableGraphBuilder;6061const Edge *Edges;62node_value_type Value;6364public:65const node_value_type &getValue() const { return Value; }6667const Edge *edges_begin() const { return Edges; }68// Nodes are allocated sequentially. Edges for a node are stored together.69// The end of this Node's edges is the beginning of the next node's edges.70// An extra node was allocated to hold the end pointer for the last real71// node.72const Edge *edges_end() const { return (this + 1)->Edges; }73ArrayRef<Edge> edges() const {74return ArrayRef(edges_begin(), edges_end());75}76};7778protected:79ImmutableGraph(std::unique_ptr<Node[]> Nodes, std::unique_ptr<Edge[]> Edges,80size_type NodesSize, size_type EdgesSize)81: Nodes(std::move(Nodes)), Edges(std::move(Edges)), NodesSize(NodesSize),82EdgesSize(EdgesSize) {}83ImmutableGraph(const ImmutableGraph &) = delete;84ImmutableGraph(ImmutableGraph &&) = delete;85ImmutableGraph &operator=(const ImmutableGraph &) = delete;86ImmutableGraph &operator=(ImmutableGraph &&) = delete;8788public:89ArrayRef<Node> nodes() const { return ArrayRef(Nodes.get(), NodesSize); }90const Node *nodes_begin() const { return nodes().begin(); }91const Node *nodes_end() const { return nodes().end(); }9293ArrayRef<Edge> edges() const { return ArrayRef(Edges.get(), EdgesSize); }94const Edge *edges_begin() const { return edges().begin(); }95const Edge *edges_end() const { return edges().end(); }9697size_type nodes_size() const { return NodesSize; }98size_type edges_size() const { return EdgesSize; }99100// Node N must belong to this ImmutableGraph.101size_type getNodeIndex(const Node &N) const {102return std::distance(nodes_begin(), &N);103}104// Edge E must belong to this ImmutableGraph.105size_type getEdgeIndex(const Edge &E) const {106return std::distance(edges_begin(), &E);107}108109// FIXME: Could NodeSet and EdgeSet be templated to share code?110class NodeSet {111const ImmutableGraph &G;112BitVector V;113114public:115NodeSet(const ImmutableGraph &G, bool ContainsAll = false)116: G{G}, V{static_cast<unsigned>(G.nodes_size()), ContainsAll} {}117bool insert(const Node &N) {118size_type Idx = G.getNodeIndex(N);119bool AlreadyExists = V.test(Idx);120V.set(Idx);121return !AlreadyExists;122}123void erase(const Node &N) {124size_type Idx = G.getNodeIndex(N);125V.reset(Idx);126}127bool contains(const Node &N) const {128size_type Idx = G.getNodeIndex(N);129return V.test(Idx);130}131void clear() { V.reset(); }132size_type empty() const { return V.none(); }133/// Return the number of elements in the set134size_type count() const { return V.count(); }135/// Return the size of the set's domain136size_type size() const { return V.size(); }137/// Set union138NodeSet &operator|=(const NodeSet &RHS) {139assert(&this->G == &RHS.G);140V |= RHS.V;141return *this;142}143/// Set intersection144NodeSet &operator&=(const NodeSet &RHS) {145assert(&this->G == &RHS.G);146V &= RHS.V;147return *this;148}149/// Set disjoint union150NodeSet &operator^=(const NodeSet &RHS) {151assert(&this->G == &RHS.G);152V ^= RHS.V;153return *this;154}155156using index_iterator = typename BitVector::const_set_bits_iterator;157index_iterator index_begin() const { return V.set_bits_begin(); }158index_iterator index_end() const { return V.set_bits_end(); }159void set(size_type Idx) { V.set(Idx); }160void reset(size_type Idx) { V.reset(Idx); }161162class iterator {163const NodeSet &Set;164size_type Current;165166void advance() {167assert(Current != -1);168Current = Set.V.find_next(Current);169}170171public:172iterator(const NodeSet &Set, size_type Begin)173: Set{Set}, Current{Begin} {}174iterator operator++(int) {175iterator Tmp = *this;176advance();177return Tmp;178}179iterator &operator++() {180advance();181return *this;182}183Node *operator*() const {184assert(Current != -1);185return Set.G.nodes_begin() + Current;186}187bool operator==(const iterator &other) const {188assert(&this->Set == &other.Set);189return this->Current == other.Current;190}191bool operator!=(const iterator &other) const { return !(*this == other); }192};193194iterator begin() const { return iterator{*this, V.find_first()}; }195iterator end() const { return iterator{*this, -1}; }196};197198class EdgeSet {199const ImmutableGraph &G;200BitVector V;201202public:203EdgeSet(const ImmutableGraph &G, bool ContainsAll = false)204: G{G}, V{static_cast<unsigned>(G.edges_size()), ContainsAll} {}205bool insert(const Edge &E) {206size_type Idx = G.getEdgeIndex(E);207bool AlreadyExists = V.test(Idx);208V.set(Idx);209return !AlreadyExists;210}211void erase(const Edge &E) {212size_type Idx = G.getEdgeIndex(E);213V.reset(Idx);214}215bool contains(const Edge &E) const {216size_type Idx = G.getEdgeIndex(E);217return V.test(Idx);218}219void clear() { V.reset(); }220bool empty() const { return V.none(); }221/// Return the number of elements in the set222size_type count() const { return V.count(); }223/// Return the size of the set's domain224size_type size() const { return V.size(); }225/// Set union226EdgeSet &operator|=(const EdgeSet &RHS) {227assert(&this->G == &RHS.G);228V |= RHS.V;229return *this;230}231/// Set intersection232EdgeSet &operator&=(const EdgeSet &RHS) {233assert(&this->G == &RHS.G);234V &= RHS.V;235return *this;236}237/// Set disjoint union238EdgeSet &operator^=(const EdgeSet &RHS) {239assert(&this->G == &RHS.G);240V ^= RHS.V;241return *this;242}243244using index_iterator = typename BitVector::const_set_bits_iterator;245index_iterator index_begin() const { return V.set_bits_begin(); }246index_iterator index_end() const { return V.set_bits_end(); }247void set(size_type Idx) { V.set(Idx); }248void reset(size_type Idx) { V.reset(Idx); }249250class iterator {251const EdgeSet &Set;252size_type Current;253254void advance() {255assert(Current != -1);256Current = Set.V.find_next(Current);257}258259public:260iterator(const EdgeSet &Set, size_type Begin)261: Set{Set}, Current{Begin} {}262iterator operator++(int) {263iterator Tmp = *this;264advance();265return Tmp;266}267iterator &operator++() {268advance();269return *this;270}271Edge *operator*() const {272assert(Current != -1);273return Set.G.edges_begin() + Current;274}275bool operator==(const iterator &other) const {276assert(&this->Set == &other.Set);277return this->Current == other.Current;278}279bool operator!=(const iterator &other) const { return !(*this == other); }280};281282iterator begin() const { return iterator{*this, V.find_first()}; }283iterator end() const { return iterator{*this, -1}; }284};285286private:287std::unique_ptr<Node[]> Nodes;288std::unique_ptr<Edge[]> Edges;289size_type NodesSize;290size_type EdgesSize;291};292293template <typename GraphT> class ImmutableGraphBuilder {294using node_value_type = typename GraphT::node_value_type;295using edge_value_type = typename GraphT::edge_value_type;296static_assert(297std::is_base_of<ImmutableGraph<node_value_type, edge_value_type>,298GraphT>::value,299"Template argument to ImmutableGraphBuilder must derive from "300"ImmutableGraph<>");301using size_type = typename GraphT::size_type;302using NodeSet = typename GraphT::NodeSet;303using Node = typename GraphT::Node;304using EdgeSet = typename GraphT::EdgeSet;305using Edge = typename GraphT::Edge;306using BuilderEdge = std::pair<edge_value_type, size_type>;307using EdgeList = std::vector<BuilderEdge>;308using BuilderVertex = std::pair<node_value_type, EdgeList>;309using VertexVec = std::vector<BuilderVertex>;310311public:312using BuilderNodeRef = size_type;313314BuilderNodeRef addVertex(const node_value_type &V) {315auto I = AdjList.emplace(AdjList.end(), V, EdgeList{});316return std::distance(AdjList.begin(), I);317}318319void addEdge(const edge_value_type &E, BuilderNodeRef From,320BuilderNodeRef To) {321AdjList[From].second.emplace_back(E, To);322}323324bool empty() const { return AdjList.empty(); }325326template <typename... ArgT> std::unique_ptr<GraphT> get(ArgT &&... Args) {327size_type VertexSize = AdjList.size(), EdgeSize = 0;328for (const auto &V : AdjList) {329EdgeSize += V.second.size();330}331auto VertexArray =332std::make_unique<Node[]>(VertexSize + 1 /* terminator node */);333auto EdgeArray = std::make_unique<Edge[]>(EdgeSize);334size_type VI = 0, EI = 0;335for (; VI < VertexSize; ++VI) {336VertexArray[VI].Value = std::move(AdjList[VI].first);337VertexArray[VI].Edges = &EdgeArray[EI];338auto NumEdges = static_cast<size_type>(AdjList[VI].second.size());339for (size_type VEI = 0; VEI < NumEdges; ++VEI, ++EI) {340auto &E = AdjList[VI].second[VEI];341EdgeArray[EI].Value = std::move(E.first);342EdgeArray[EI].Dest = &VertexArray[E.second];343}344}345assert(VI == VertexSize && EI == EdgeSize && "ImmutableGraph malformed");346VertexArray[VI].Edges = &EdgeArray[EdgeSize]; // terminator node347return std::make_unique<GraphT>(std::move(VertexArray),348std::move(EdgeArray), VertexSize, EdgeSize,349std::forward<ArgT>(Args)...);350}351352template <typename... ArgT>353static std::unique_ptr<GraphT> trim(const GraphT &G, const NodeSet &TrimNodes,354const EdgeSet &TrimEdges,355ArgT &&... Args) {356size_type NewVertexSize = G.nodes_size() - TrimNodes.count();357size_type NewEdgeSize = G.edges_size() - TrimEdges.count();358auto NewVertexArray =359std::make_unique<Node[]>(NewVertexSize + 1 /* terminator node */);360auto NewEdgeArray = std::make_unique<Edge[]>(NewEdgeSize);361362// Walk the nodes and determine the new index for each node.363size_type NewNodeIndex = 0;364std::vector<size_type> RemappedNodeIndex(G.nodes_size());365for (const Node &N : G.nodes()) {366if (TrimNodes.contains(N))367continue;368RemappedNodeIndex[G.getNodeIndex(N)] = NewNodeIndex++;369}370assert(NewNodeIndex == NewVertexSize &&371"Should have assigned NewVertexSize indices");372373size_type VertexI = 0, EdgeI = 0;374for (const Node &N : G.nodes()) {375if (TrimNodes.contains(N))376continue;377NewVertexArray[VertexI].Value = N.getValue();378NewVertexArray[VertexI].Edges = &NewEdgeArray[EdgeI];379for (const Edge &E : N.edges()) {380if (TrimEdges.contains(E))381continue;382NewEdgeArray[EdgeI].Value = E.getValue();383size_type DestIdx = G.getNodeIndex(*E.getDest());384size_type NewIdx = RemappedNodeIndex[DestIdx];385assert(NewIdx < NewVertexSize);386NewEdgeArray[EdgeI].Dest = &NewVertexArray[NewIdx];387++EdgeI;388}389++VertexI;390}391assert(VertexI == NewVertexSize && EdgeI == NewEdgeSize &&392"Gadget graph malformed");393NewVertexArray[VertexI].Edges = &NewEdgeArray[NewEdgeSize]; // terminator394return std::make_unique<GraphT>(std::move(NewVertexArray),395std::move(NewEdgeArray), NewVertexSize,396NewEdgeSize, std::forward<ArgT>(Args)...);397}398399private:400VertexVec AdjList;401};402403template <typename NodeValueT, typename EdgeValueT>404struct GraphTraits<ImmutableGraph<NodeValueT, EdgeValueT> *> {405using GraphT = ImmutableGraph<NodeValueT, EdgeValueT>;406using NodeRef = typename GraphT::Node const *;407using EdgeRef = typename GraphT::Edge const &;408409static NodeRef edge_dest(EdgeRef E) { return E.getDest(); }410using ChildIteratorType =411mapped_iterator<typename GraphT::Edge const *, decltype(&edge_dest)>;412413static NodeRef getEntryNode(GraphT *G) { return G->nodes_begin(); }414static ChildIteratorType child_begin(NodeRef N) {415return {N->edges_begin(), &edge_dest};416}417static ChildIteratorType child_end(NodeRef N) {418return {N->edges_end(), &edge_dest};419}420421static NodeRef getNode(typename GraphT::Node const &N) { return NodeRef{&N}; }422using nodes_iterator =423mapped_iterator<typename GraphT::Node const *, decltype(&getNode)>;424static nodes_iterator nodes_begin(GraphT *G) {425return {G->nodes_begin(), &getNode};426}427static nodes_iterator nodes_end(GraphT *G) {428return {G->nodes_end(), &getNode};429}430431using ChildEdgeIteratorType = typename GraphT::Edge const *;432433static ChildEdgeIteratorType child_edge_begin(NodeRef N) {434return N->edges_begin();435}436static ChildEdgeIteratorType child_edge_end(NodeRef N) {437return N->edges_end();438}439static typename GraphT::size_type size(GraphT *G) { return G->nodes_size(); }440};441442} // end namespace llvm443444#endif // LLVM_LIB_TARGET_X86_IMMUTABLEGRAPH_H445446447