diff options
Diffstat (limited to 'include/llvm/Analysis/LazyCallGraph.h')
-rw-r--r-- | include/llvm/Analysis/LazyCallGraph.h | 493 |
1 files changed, 259 insertions, 234 deletions
diff --git a/include/llvm/Analysis/LazyCallGraph.h b/include/llvm/Analysis/LazyCallGraph.h index bca0aebe2eef..ad7f5c80549f 100644 --- a/include/llvm/Analysis/LazyCallGraph.h +++ b/include/llvm/Analysis/LazyCallGraph.h @@ -106,6 +106,7 @@ class raw_ostream; class LazyCallGraph { public: class Node; + class EdgeSequence; class SCC; class RefSCC; class edge_iterator; @@ -121,16 +122,6 @@ public: /// inherently reference edges, and so the reference graph forms a superset /// of the formal call graph. /// - /// Furthermore, edges also may point to raw \c Function objects when those - /// functions have not been scanned and incorporated into the graph (yet). - /// This is one of the primary ways in which the graph can be lazy. When - /// functions are scanned and fully incorporated into the graph, all of the - /// edges referencing them are updated to point to the graph \c Node objects - /// instead of to the raw \c Function objects. This class even provides - /// methods to trigger this scan on-demand by attempting to get the target - /// node of the graph and providing a reference back to the graph in order to - /// lazily build it if necessary. - /// /// All of these forms of edges are fundamentally represented as outgoing /// edges. The edges are stored in the source node and point at the target /// node. This allows the edge structure itself to be a very compact data @@ -141,7 +132,6 @@ public: enum Kind : bool { Ref = false, Call = true }; Edge(); - explicit Edge(Function &F, Kind K); explicit Edge(Node &N, Kind K); /// Test whether the edge is null. @@ -158,197 +148,251 @@ public: /// This requires that the edge is not null. bool isCall() const; - /// Get the function referenced by this edge. - /// - /// This requires that the edge is not null, but will succeed whether we - /// have built a graph node for the function yet or not. - Function &getFunction() const; - - /// Get the call graph node referenced by this edge if one exists. + /// Get the call graph node referenced by this edge. /// - /// This requires that the edge is not null. If we have built a graph node - /// for the function this edge points to, this will return that node, - /// otherwise it will return null. - Node *getNode() const; + /// This requires that the edge is not null. + Node &getNode() const; - /// Get the call graph node for this edge, building it if necessary. + /// Get the function referenced by this edge. /// - /// This requires that the edge is not null. If we have not yet built - /// a graph node for the function this edge points to, this will first ask - /// the graph to build that node, inserting it into all the relevant - /// structures. - Node &getNode(LazyCallGraph &G); + /// This requires that the edge is not null. + Function &getFunction() const; private: - friend class LazyCallGraph::Node; + friend class LazyCallGraph::EdgeSequence; friend class LazyCallGraph::RefSCC; - PointerIntPair<PointerUnion<Function *, Node *>, 1, Kind> Value; + PointerIntPair<Node *, 1, Kind> Value; void setKind(Kind K) { Value.setInt(K); } }; - typedef SmallVector<Edge, 4> EdgeVectorT; - typedef SmallVectorImpl<Edge> EdgeVectorImplT; - - /// A node in the call graph. + /// The edge sequence object. /// - /// This represents a single node. It's primary roles are to cache the list of - /// callees, de-duplicate and provide fast testing of whether a function is - /// a callee, and facilitate iteration of child nodes in the graph. - class Node { + /// This typically exists entirely within the node but is exposed as + /// a separate type because a node doesn't initially have edges. An explicit + /// population step is required to produce this sequence at first and it is + /// then cached in the node. It is also used to represent edges entering the + /// graph from outside the module to model the graph's roots. + /// + /// The sequence itself both iterable and indexable. The indexes remain + /// stable even as the sequence mutates (including removal). + class EdgeSequence { friend class LazyCallGraph; - friend class LazyCallGraph::SCC; + friend class LazyCallGraph::Node; friend class LazyCallGraph::RefSCC; - LazyCallGraph *G; - Function &F; + typedef SmallVector<Edge, 4> VectorT; + typedef SmallVectorImpl<Edge> VectorImplT; - // We provide for the DFS numbering and Tarjan walk lowlink numbers to be - // stored directly within the node. These are both '-1' when nodes are part - // of an SCC (or RefSCC), or '0' when not yet reached in a DFS walk. - int DFSNumber; - int LowLink; + public: + /// An iterator used for the edges to both entry nodes and child nodes. + class iterator + : public iterator_adaptor_base<iterator, VectorImplT::iterator, + std::forward_iterator_tag> { + friend class LazyCallGraph; + friend class LazyCallGraph::Node; + + VectorImplT::iterator E; + + // Build the iterator for a specific position in the edge list. + iterator(VectorImplT::iterator BaseI, VectorImplT::iterator E) + : iterator_adaptor_base(BaseI), E(E) { + while (I != E && !*I) + ++I; + } - mutable EdgeVectorT Edges; - DenseMap<Function *, int> EdgeIndexMap; + public: + iterator() {} - /// Basic constructor implements the scanning of F into Edges and - /// EdgeIndexMap. - Node(LazyCallGraph &G, Function &F); + using iterator_adaptor_base::operator++; + iterator &operator++() { + do { + ++I; + } while (I != E && !*I); + return *this; + } + }; - /// Internal helper to insert an edge to a function. - void insertEdgeInternal(Function &ChildF, Edge::Kind EK); + /// An iterator over specifically call edges. + /// + /// This has the same iteration properties as the \c iterator, but + /// restricts itself to edges which represent actual calls. + class call_iterator + : public iterator_adaptor_base<call_iterator, VectorImplT::iterator, + std::forward_iterator_tag> { + friend class LazyCallGraph; + friend class LazyCallGraph::Node; + + VectorImplT::iterator E; + + /// Advance the iterator to the next valid, call edge. + void advanceToNextEdge() { + while (I != E && (!*I || !I->isCall())) + ++I; + } - /// Internal helper to insert an edge to a node. - void insertEdgeInternal(Node &ChildN, Edge::Kind EK); + // Build the iterator for a specific position in the edge list. + call_iterator(VectorImplT::iterator BaseI, VectorImplT::iterator E) + : iterator_adaptor_base(BaseI), E(E) { + advanceToNextEdge(); + } - /// Internal helper to change an edge kind. - void setEdgeKind(Function &ChildF, Edge::Kind EK); + public: + call_iterator() {} - /// Internal helper to remove the edge to the given function. - void removeEdgeInternal(Function &ChildF); + using iterator_adaptor_base::operator++; + call_iterator &operator++() { + ++I; + advanceToNextEdge(); + return *this; + } + }; - void clear() { - Edges.clear(); - EdgeIndexMap.clear(); - } + iterator begin() { return iterator(Edges.begin(), Edges.end()); } + iterator end() { return iterator(Edges.end(), Edges.end()); } - /// Print the name of this node's function. - friend raw_ostream &operator<<(raw_ostream &OS, const Node &N) { - return OS << N.F.getName(); + Edge &operator[](int i) { return Edges[i]; } + Edge &operator[](Node &N) { + assert(EdgeIndexMap.find(&N) != EdgeIndexMap.end() && "No such edge!"); + return Edges[EdgeIndexMap.find(&N)->second]; } - - /// Dump the name of this node's function to stderr. - void dump() const; - - public: - LazyCallGraph &getGraph() const { return *G; } - - Function &getFunction() const { return F; } - - edge_iterator begin() const { - return edge_iterator(Edges.begin(), Edges.end()); + Edge *lookup(Node &N) { + auto EI = EdgeIndexMap.find(&N); + return EI != EdgeIndexMap.end() ? &Edges[EI->second] : nullptr; } - edge_iterator end() const { return edge_iterator(Edges.end(), Edges.end()); } - const Edge &operator[](int i) const { return Edges[i]; } - const Edge &operator[](Function &F) const { - assert(EdgeIndexMap.find(&F) != EdgeIndexMap.end() && "No such edge!"); - return Edges[EdgeIndexMap.find(&F)->second]; + call_iterator call_begin() { + return call_iterator(Edges.begin(), Edges.end()); } - const Edge &operator[](Node &N) const { return (*this)[N.getFunction()]; } + call_iterator call_end() { return call_iterator(Edges.end(), Edges.end()); } - const Edge *lookup(Function &F) const { - auto EI = EdgeIndexMap.find(&F); - return EI != EdgeIndexMap.end() ? &Edges[EI->second] : nullptr; + iterator_range<call_iterator> calls() { + return make_range(call_begin(), call_end()); } - call_edge_iterator call_begin() const { - return call_edge_iterator(Edges.begin(), Edges.end()); - } - call_edge_iterator call_end() const { - return call_edge_iterator(Edges.end(), Edges.end()); - } + bool empty() { + for (auto &E : Edges) + if (E) + return false; - iterator_range<call_edge_iterator> calls() const { - return make_range(call_begin(), call_end()); + return true; } - /// Equality is defined as address equality. - bool operator==(const Node &N) const { return this == &N; } - bool operator!=(const Node &N) const { return !operator==(N); } - }; + private: + VectorT Edges; + DenseMap<Node *, int> EdgeIndexMap; - /// A lazy iterator used for both the entry nodes and child nodes. - /// - /// When this iterator is dereferenced, if not yet available, a function will - /// be scanned for "calls" or uses of functions and its child information - /// will be constructed. All of these results are accumulated and cached in - /// the graph. - class edge_iterator - : public iterator_adaptor_base<edge_iterator, EdgeVectorImplT::iterator, - std::forward_iterator_tag> { - friend class LazyCallGraph; - friend class LazyCallGraph::Node; + EdgeSequence() = default; - EdgeVectorImplT::iterator E; + /// Internal helper to insert an edge to a node. + void insertEdgeInternal(Node &ChildN, Edge::Kind EK); - // Build the iterator for a specific position in the edge list. - edge_iterator(EdgeVectorImplT::iterator BaseI, - EdgeVectorImplT::iterator E) - : iterator_adaptor_base(BaseI), E(E) { - while (I != E && !*I) - ++I; - } + /// Internal helper to change an edge kind. + void setEdgeKind(Node &ChildN, Edge::Kind EK); - public: - edge_iterator() {} + /// Internal helper to remove the edge to the given function. + bool removeEdgeInternal(Node &ChildN); - using iterator_adaptor_base::operator++; - edge_iterator &operator++() { - do { - ++I; - } while (I != E && !*I); - return *this; - } + /// Internal helper to replace an edge key with a new one. + /// + /// This should be used when the function for a particular node in the + /// graph gets replaced and we are updating all of the edges to that node + /// to use the new function as the key. + void replaceEdgeKey(Function &OldTarget, Function &NewTarget); }; - /// A lazy iterator over specifically call edges. + /// A node in the call graph. + /// + /// This represents a single node. It's primary roles are to cache the list of + /// callees, de-duplicate and provide fast testing of whether a function is + /// a callee, and facilitate iteration of child nodes in the graph. /// - /// This has the same iteration properties as the \c edge_iterator, but - /// restricts itself to edges which represent actual calls. - class call_edge_iterator - : public iterator_adaptor_base<call_edge_iterator, - EdgeVectorImplT::iterator, - std::forward_iterator_tag> { + /// The node works much like an optional in order to lazily populate the + /// edges of each node. Until populated, there are no edges. Once populated, + /// you can access the edges by dereferencing the node or using the `->` + /// operator as if the node was an `Optional<EdgeSequence>`. + class Node { friend class LazyCallGraph; - friend class LazyCallGraph::Node; + friend class LazyCallGraph::RefSCC; - EdgeVectorImplT::iterator E; + public: + LazyCallGraph &getGraph() const { return *G; } - /// Advance the iterator to the next valid, call edge. - void advanceToNextEdge() { - while (I != E && (!*I || !I->isCall())) - ++I; + Function &getFunction() const { return *F; } + + StringRef getName() const { return F->getName(); } + + /// Equality is defined as address equality. + bool operator==(const Node &N) const { return this == &N; } + bool operator!=(const Node &N) const { return !operator==(N); } + + /// Tests whether the node has been populated with edges. + operator bool() const { return Edges.hasValue(); } + + // We allow accessing the edges by dereferencing or using the arrow + // operator, essentially wrapping the internal optional. + EdgeSequence &operator*() const { + // Rip const off because the node itself isn't changing here. + return const_cast<EdgeSequence &>(*Edges); } + EdgeSequence *operator->() const { return &**this; } - // Build the iterator for a specific position in the edge list. - call_edge_iterator(EdgeVectorImplT::iterator BaseI, - EdgeVectorImplT::iterator E) - : iterator_adaptor_base(BaseI), E(E) { - advanceToNextEdge(); + /// Populate the edges of this node if necessary. + /// + /// The first time this is called it will populate the edges for this node + /// in the graph. It does this by scanning the underlying function, so once + /// this is done, any changes to that function must be explicitly reflected + /// in updates to the graph. + /// + /// \returns the populated \c EdgeSequence to simplify walking it. + /// + /// This will not update or re-scan anything if called repeatedly. Instead, + /// the edge sequence is cached and returned immediately on subsequent + /// calls. + EdgeSequence &populate() { + if (Edges) + return *Edges; + + return populateSlow(); } - public: - call_edge_iterator() {} + private: + LazyCallGraph *G; + Function *F; - using iterator_adaptor_base::operator++; - call_edge_iterator &operator++() { - ++I; - advanceToNextEdge(); - return *this; + // We provide for the DFS numbering and Tarjan walk lowlink numbers to be + // stored directly within the node. These are both '-1' when nodes are part + // of an SCC (or RefSCC), or '0' when not yet reached in a DFS walk. + int DFSNumber; + int LowLink; + + Optional<EdgeSequence> Edges; + + /// Basic constructor implements the scanning of F into Edges and + /// EdgeIndexMap. + Node(LazyCallGraph &G, Function &F) + : G(&G), F(&F), DFSNumber(0), LowLink(0) {} + + /// Implementation of the scan when populating. + EdgeSequence &populateSlow(); + + /// Internal helper to directly replace the function with a new one. + /// + /// This is used to facilitate tranfsormations which need to replace the + /// formal Function object but directly move the body and users from one to + /// the other. + void replaceFunction(Function &NewF); + + void clear() { Edges.reset(); } + + /// Print the name of this node's function. + friend raw_ostream &operator<<(raw_ostream &OS, const Node &N) { + return OS << N.F->getName(); } + + /// Dump the name of this node's function to stderr. + void dump() const; }; /// An SCC of the call graph. @@ -789,19 +833,26 @@ public: /// already existing edges. void insertTrivialRefEdge(Node &SourceN, Node &TargetN); + /// Directly replace a node's function with a new function. + /// + /// This should be used when moving the body and users of a function to + /// a new formal function object but not otherwise changing the call graph + /// structure in any way. + /// + /// It requires that the old function in the provided node have zero uses + /// and the new function must have calls and references to it establishing + /// an equivalent graph. + void replaceNodeFunction(Node &N, Function &NewF); + ///@} }; /// A post-order depth-first RefSCC iterator over the call graph. /// - /// This iterator triggers the Tarjan DFS-based formation of the RefSCC (and - /// SCC) DAG for the call graph, walking it lazily in depth-first post-order. - /// That is, it always visits RefSCCs for the target of a reference edge - /// prior to visiting the RefSCC for a source of the edge (when they are in - /// different RefSCCs). - /// - /// When forming each RefSCC, the call edges within it are used to form SCCs - /// within it, so iterating this also controls the lazy formation of SCCs. + /// This iterator walks the cached post-order sequence of RefSCCs. However, + /// it trades stability for flexibility. It is restricted to a forward + /// iterator but will survive mutations which insert new RefSCCs and continue + /// to point to the same RefSCC even if it moves in the post-order sequence. class postorder_ref_scc_iterator : public iterator_facade_base<postorder_ref_scc_iterator, std::forward_iterator_tag, RefSCC> { @@ -825,12 +876,9 @@ public: /// populating it if necessary. static RefSCC *getRC(LazyCallGraph &G, int Index) { if (Index == (int)G.PostOrderRefSCCs.size()) - if (!G.buildNextRefSCCInPostOrder()) - // We're at the end. - return nullptr; + // We're at the end. + return nullptr; - assert(Index < (int)G.PostOrderRefSCCs.size() && - "Built the next post-order RefSCC without growing list!"); return G.PostOrderRefSCCs[Index]; } @@ -859,17 +907,21 @@ public: LazyCallGraph(LazyCallGraph &&G); LazyCallGraph &operator=(LazyCallGraph &&RHS); - edge_iterator begin() { - return edge_iterator(EntryEdges.begin(), EntryEdges.end()); - } - edge_iterator end() { - return edge_iterator(EntryEdges.end(), EntryEdges.end()); - } + EdgeSequence::iterator begin() { return EntryEdges.begin(); } + EdgeSequence::iterator end() { return EntryEdges.end(); } + + void buildRefSCCs(); postorder_ref_scc_iterator postorder_ref_scc_begin() { + if (!EntryEdges.empty()) + assert(!PostOrderRefSCCs.empty() && + "Must form RefSCCs before iterating them!"); return postorder_ref_scc_iterator(*this); } postorder_ref_scc_iterator postorder_ref_scc_end() { + if (!EntryEdges.empty()) + assert(!PostOrderRefSCCs.empty() && + "Must form RefSCCs before iterating them!"); return postorder_ref_scc_iterator(*this, postorder_ref_scc_iterator::IsAtEndT()); } @@ -920,19 +972,19 @@ public: /// below. /// Update the call graph after inserting a new edge. - void insertEdge(Node &Caller, Function &Callee, Edge::Kind EK); + void insertEdge(Node &SourceN, Node &TargetN, Edge::Kind EK); /// Update the call graph after inserting a new edge. - void insertEdge(Function &Caller, Function &Callee, Edge::Kind EK) { - return insertEdge(get(Caller), Callee, EK); + void insertEdge(Function &Source, Function &Target, Edge::Kind EK) { + return insertEdge(get(Source), get(Target), EK); } /// Update the call graph after deleting an edge. - void removeEdge(Node &Caller, Function &Callee); + void removeEdge(Node &SourceN, Node &TargetN); /// Update the call graph after deleting an edge. - void removeEdge(Function &Caller, Function &Callee) { - return removeEdge(get(Caller), Callee); + void removeEdge(Function &Source, Function &Target) { + return removeEdge(get(Source), get(Target)); } ///@} @@ -1013,14 +1065,11 @@ private: /// Maps function->node for fast lookup. DenseMap<const Function *, Node *> NodeMap; - /// The entry nodes to the graph. + /// The entry edges into the graph. /// - /// These nodes are reachable through "external" means. Put another way, they + /// These edges are from "external" sources. Put another way, they /// escape at the module scope. - EdgeVectorT EntryEdges; - - /// Map of the entry nodes in the graph to their indices in \c EntryEdges. - DenseMap<Function *, int> EntryIndexMap; + EdgeSequence EntryEdges; /// Allocator that holds all the call graph SCCs. SpecificBumpPtrAllocator<SCC> SCCBPA; @@ -1045,18 +1094,6 @@ private: /// These are all of the RefSCCs which have no children. SmallVector<RefSCC *, 4> LeafRefSCCs; - /// Stack of nodes in the DFS walk. - SmallVector<std::pair<Node *, edge_iterator>, 4> DFSStack; - - /// Set of entry nodes not-yet-processed into RefSCCs. - SmallVector<Function *, 4> RefSCCEntryNodes; - - /// Stack of nodes the DFS has walked but not yet put into a RefSCC. - SmallVector<Node *, 4> PendingRefSCCStack; - - /// Counter for the next DFS number to assign. - int NextDFSNumber; - /// Helper to insert a new function, with an already looked-up entry in /// the NodeMap. Node &insertInto(Function &F, Node *&MappedN); @@ -1078,6 +1115,23 @@ private: return new (RefSCCBPA.Allocate()) RefSCC(std::forward<Ts>(Args)...); } + /// Common logic for building SCCs from a sequence of roots. + /// + /// This is a very generic implementation of the depth-first walk and SCC + /// formation algorithm. It uses a generic sequence of roots and generic + /// callbacks for each step. This is designed to be used to implement both + /// the RefSCC formation and SCC formation with shared logic. + /// + /// Currently this is a relatively naive implementation of Tarjan's DFS + /// algorithm to form the SCCs. + /// + /// FIXME: We should consider newer variants such as Nuutila. + template <typename RootsT, typename GetBeginT, typename GetEndT, + typename GetNodeT, typename FormSCCCallbackT> + static void buildGenericSCCs(RootsT &&Roots, GetBeginT &&GetBegin, + GetEndT &&GetEnd, GetNodeT &&GetNode, + FormSCCCallbackT &&FormSCC); + /// Build the SCCs for a RefSCC out of a list of nodes. void buildSCCs(RefSCC &RC, node_stack_range Nodes); @@ -1098,22 +1152,12 @@ private: "Index does not point back at RC!"); return IndexIt->second; } - - /// Builds the next node in the post-order RefSCC walk of the call graph and - /// appends it to the \c PostOrderRefSCCs vector. - /// - /// Returns true if a new RefSCC was successfully constructed, and false if - /// there are no more RefSCCs to build in the graph. - bool buildNextRefSCCInPostOrder(); }; inline LazyCallGraph::Edge::Edge() : Value() {} -inline LazyCallGraph::Edge::Edge(Function &F, Kind K) : Value(&F, K) {} inline LazyCallGraph::Edge::Edge(Node &N, Kind K) : Value(&N, K) {} -inline LazyCallGraph::Edge::operator bool() const { - return !Value.getPointer().isNull(); -} +inline LazyCallGraph::Edge::operator bool() const { return Value.getPointer(); } inline LazyCallGraph::Edge::Kind LazyCallGraph::Edge::getKind() const { assert(*this && "Queried a null edge!"); @@ -1125,51 +1169,32 @@ inline bool LazyCallGraph::Edge::isCall() const { return getKind() == Call; } -inline Function &LazyCallGraph::Edge::getFunction() const { +inline LazyCallGraph::Node &LazyCallGraph::Edge::getNode() const { assert(*this && "Queried a null edge!"); - auto P = Value.getPointer(); - if (auto *F = P.dyn_cast<Function *>()) - return *F; - - return P.get<Node *>()->getFunction(); + return *Value.getPointer(); } -inline LazyCallGraph::Node *LazyCallGraph::Edge::getNode() const { - assert(*this && "Queried a null edge!"); - auto P = Value.getPointer(); - if (auto *N = P.dyn_cast<Node *>()) - return N; - - return nullptr; -} - -inline LazyCallGraph::Node &LazyCallGraph::Edge::getNode(LazyCallGraph &G) { +inline Function &LazyCallGraph::Edge::getFunction() const { assert(*this && "Queried a null edge!"); - auto P = Value.getPointer(); - if (auto *N = P.dyn_cast<Node *>()) - return *N; - - Node &N = G.get(*P.get<Function *>()); - Value.setPointer(&N); - return N; + return getNode().getFunction(); } // Provide GraphTraits specializations for call graphs. template <> struct GraphTraits<LazyCallGraph::Node *> { typedef LazyCallGraph::Node *NodeRef; - typedef LazyCallGraph::edge_iterator ChildIteratorType; + typedef LazyCallGraph::EdgeSequence::iterator ChildIteratorType; static NodeRef getEntryNode(NodeRef N) { return N; } - static ChildIteratorType child_begin(NodeRef N) { return N->begin(); } - static ChildIteratorType child_end(NodeRef N) { return N->end(); } + static ChildIteratorType child_begin(NodeRef N) { return (*N)->begin(); } + static ChildIteratorType child_end(NodeRef N) { return (*N)->end(); } }; template <> struct GraphTraits<LazyCallGraph *> { typedef LazyCallGraph::Node *NodeRef; - typedef LazyCallGraph::edge_iterator ChildIteratorType; + typedef LazyCallGraph::EdgeSequence::iterator ChildIteratorType; static NodeRef getEntryNode(NodeRef N) { return N; } - static ChildIteratorType child_begin(NodeRef N) { return N->begin(); } - static ChildIteratorType child_end(NodeRef N) { return N->end(); } + static ChildIteratorType child_begin(NodeRef N) { return (*N)->begin(); } + static ChildIteratorType child_end(NodeRef N) { return (*N)->end(); } }; /// An analysis pass which computes the call graph for a module. |