diff --git a/include/llvm/Analysis/LazyCallGraph.h b/include/llvm/Analysis/LazyCallGraph.h index e02f3ab2de1..d3bd2d19d7e 100644 --- a/include/llvm/Analysis/LazyCallGraph.h +++ b/include/llvm/Analysis/LazyCallGraph.h @@ -104,9 +104,79 @@ class LazyCallGraph { public: class Node; class SCC; - class iterator; - typedef SmallVector, 4> NodeVectorT; - typedef SmallVectorImpl> NodeVectorImplT; + class edge_iterator; + + /// A class used to represent edges in the call graph. + /// + /// The lazy call graph models both *call* edges and *reference* edges. Call + /// edges are much what you would expect, and exist when there is a 'call' or + /// 'invoke' instruction of some function. Reference edges are also tracked + /// along side these, and exist whenever any instruction (transitively + /// through its operands) references a function. All call edges are + /// 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 + /// structure: essentially a tagged pointer. + class Edge { + public: + /// The kind of edge in the graph. + 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. + /// + /// This happens when an edge has been deleted. We leave the edge objects + /// around but clear them. + operator bool() const; + + /// Test whether the edge represents a direct call to a function. + /// + /// 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. + /// + /// 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; + + /// Get the call graph node for this edge, building it if necessary. + /// + /// 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); + + private: + PointerIntPair, 1, Kind> Value; + }; + + typedef SmallVector EdgeVectorT; + typedef SmallVectorImpl EdgeVectorImplT; /// A node in the call graph. /// @@ -125,31 +195,33 @@ public: int DFSNumber; int LowLink; - mutable NodeVectorT Callees; - DenseMap CalleeIndexMap; + mutable EdgeVectorT Edges; + DenseMap EdgeIndexMap; - /// Basic constructor implements the scanning of F into Callees and - /// CalleeIndexMap. + /// Basic constructor implements the scanning of F into Edges and + /// EdgeIndexMap. Node(LazyCallGraph &G, Function &F); - /// Internal helper to insert a callee. - void insertEdgeInternal(Function &Callee); + /// Internal helper to insert an edge to a function. + void insertEdgeInternal(Function &ChildF, Edge::Kind EK); - /// Internal helper to insert a callee. - void insertEdgeInternal(Node &CalleeN); + /// Internal helper to insert an edge to a node. + void insertEdgeInternal(Node &ChildN, Edge::Kind EK); - /// Internal helper to remove a callee from this node. - void removeEdgeInternal(Function &Callee); + /// Internal helper to remove the edge to the given function. + void removeEdgeInternal(Function &ChildF); public: - typedef LazyCallGraph::iterator iterator; + typedef LazyCallGraph::edge_iterator edge_iterator; + + LazyCallGraph &getGraph() const { return *G; } Function &getFunction() const { return F; } - iterator begin() const { - return iterator(*G, Callees.begin(), Callees.end()); + edge_iterator begin() const { + return edge_iterator(Edges.begin(), Edges.end()); } - iterator end() const { return iterator(*G, Callees.end(), Callees.end()); } + edge_iterator end() const { return edge_iterator(Edges.end(), Edges.end()); } /// Equality is defined as address equality. bool operator==(const Node &N) const { return this == &N; } @@ -162,42 +234,68 @@ public: /// 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 iterator - : public iterator_adaptor_base { + class edge_iterator + : public iterator_adaptor_base { friend class LazyCallGraph; friend class LazyCallGraph::Node; - LazyCallGraph *G; - NodeVectorImplT::iterator E; + EdgeVectorImplT::iterator E; - // Build the iterator for a specific position in a node list. - iterator(LazyCallGraph &G, NodeVectorImplT::iterator NI, - NodeVectorImplT::iterator E) - : iterator_adaptor_base(NI), G(&G), E(E) { - while (I != E && I->isNull()) + // 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; } public: - iterator() {} + edge_iterator() {} using iterator_adaptor_base::operator++; - iterator &operator++() { + edge_iterator &operator++() { do { ++I; - } while (I != E && I->isNull()); + } while (I != E && !*I); return *this; } + }; - reference operator*() const { - if (I->is()) - return *I->get(); + /// A lazy iterator over specifically call edges. + /// + /// 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 { + friend class LazyCallGraph; + friend class LazyCallGraph::Node; - Function *F = I->get(); - Node &ChildN = G->get(*F); - *I = &ChildN; - return ChildN; + EdgeVectorImplT::iterator E; + + /// Advance the iterator to the next valid, call edge. + void advanceToNextEdge() { + while (I != E && (!*I || !I->isCall())) + ++I; + } + + // 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(); + } + + public: + call_edge_iterator() {} + + using iterator_adaptor_base::operator++; + call_edge_iterator &operator++() { + ++I; + advanceToNextEdge(); + return *this; } }; @@ -219,7 +317,7 @@ public: void insert(Node &N); void - internalDFS(SmallVectorImpl> &DFSStack, + internalDFS(SmallVectorImpl> &DFSStack, SmallVectorImpl &PendingSCCStack, Node *N, SmallVectorImpl &ResultSCCs); @@ -271,14 +369,14 @@ public: /// /// By the definition of an SCC, this does not change the nature or make-up /// of any SCCs. - void insertIntraSCCEdge(Node &CallerN, Node &CalleeN); + void insertIntraSCCEdge(Node &ParentN, Node &ChildN, Edge::Kind EK); /// Insert an edge whose tail is in this SCC and head is in some child SCC. /// /// There must be an existing path from the caller to the callee. This /// operation is inexpensive and does not change the set of SCCs in the /// graph. - void insertOutgoingEdge(Node &CallerN, Node &CalleeN); + void insertOutgoingEdge(Node &ParentN, Node &ChildN, Edge::Kind EK); /// Insert an edge whose tail is in a descendant SCC and head is in this /// SCC. @@ -294,7 +392,8 @@ public: /// FIXME: We could possibly optimize this quite a bit for cases where the /// caller and callee are very nearby in the graph. See comments in the /// implementation for details, but that use case might impact users. - SmallVector insertIncomingEdge(Node &CallerN, Node &CalleeN); + SmallVector insertIncomingEdge(Node &ParentN, Node &ChildN, + Edge::Kind EK); /// Remove an edge whose source is in this SCC and target is *not*. /// @@ -306,11 +405,11 @@ public: /// SCCs and so is very inexpensive. It may change the connectivity graph /// of the SCCs though, so be careful calling this while iterating over /// them. - void removeInterSCCEdge(Node &CallerN, Node &CalleeN); + void removeInterSCCEdge(Node &ParentN, Node &ChildN); /// Remove an edge which is entirely within this SCC. /// - /// Both the \a Caller and the \a Callee must be within this SCC. Removing + /// Both the \a ParentN and the \a ChildN must be within this SCC. Removing /// such an edge make break cycles that form this SCC and thus this /// operation may change the SCC graph significantly. In particular, this /// operation will re-form new SCCs based on the remaining connectivity of @@ -340,7 +439,7 @@ public: /// this SCC and edges from this SCC to child SCCs. Some effort has been /// made to minimize the overhead of common cases such as self-edges and /// edge removals which result in a spanning tree with no more cycles. - SmallVector removeIntraSCCEdge(Node &CallerN, Node &CalleeN); + SmallVector removeIntraSCCEdge(Node &ParentN, Node &ChildN); ///@} }; @@ -396,10 +495,12 @@ public: LazyCallGraph(LazyCallGraph &&G); LazyCallGraph &operator=(LazyCallGraph &&RHS); - iterator begin() { - return iterator(*this, EntryNodes.begin(), EntryNodes.end()); + edge_iterator begin() { + return edge_iterator(EntryEdges.begin(), EntryEdges.end()); + } + edge_iterator end() { + return edge_iterator(EntryEdges.end(), EntryEdges.end()); } - iterator end() { return iterator(*this, EntryNodes.end(), EntryNodes.end()); } postorder_scc_iterator postorder_scc_begin() { return postorder_scc_iterator(*this); @@ -442,11 +543,11 @@ public: /// mutation of the graph via the SCC methods. /// Update the call graph after inserting a new edge. - void insertEdge(Node &Caller, Function &Callee); + void insertEdge(Node &Caller, Function &Callee, Edge::Kind EK); /// Update the call graph after inserting a new edge. - void insertEdge(Function &Caller, Function &Callee) { - return insertEdge(get(Caller), Callee); + void insertEdge(Function &Caller, Function &Callee, Edge::Kind EK) { + return insertEdge(get(Caller), Callee, EK); } /// Update the call graph after deleting an edge. @@ -470,9 +571,9 @@ private: /// /// These nodes are reachable through "external" means. Put another way, they /// escape at the module scope. - NodeVectorT EntryNodes; + EdgeVectorT EntryEdges; - /// Map of the entry nodes in the graph to their indices in \c EntryNodes. + /// Map of the entry nodes in the graph to their indices in \c EntryEdges. DenseMap EntryIndexMap; /// Allocator that holds all the call graph SCCs. @@ -487,7 +588,7 @@ private: SmallVector LeafSCCs; /// Stack of nodes in the DFS walk. - SmallVector, 4> DFSStack; + SmallVector, 4> DFSStack; /// Set of entry nodes not-yet-processed into SCCs. SmallVector SCCEntryNodes; @@ -513,10 +614,52 @@ private: SCC *getNextSCCInPostOrder(); }; +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 bool LazyCallGraph::Edge::isCall() const { + assert(*this && "Queried a null edge!"); + return Value.getInt() == Call; +} + +inline Function &LazyCallGraph::Edge::getFunction() const { + assert(*this && "Queried a null edge!"); + auto P = Value.getPointer(); + if (auto *F = P.dyn_cast()) + return *F; + + return P.get()->getFunction(); +} + +inline LazyCallGraph::Node *LazyCallGraph::Edge::getNode() const { + assert(*this && "Queried a null edge!"); + auto P = Value.getPointer(); + if (auto *N = P.dyn_cast()) + return N; + + return nullptr; +} + +inline LazyCallGraph::Node &LazyCallGraph::Edge::getNode(LazyCallGraph &G) { + assert(*this && "Queried a null edge!"); + auto P = Value.getPointer(); + if (auto *N = P.dyn_cast()) + return *N; + + Node &N = G.get(*P.get()); + Value.setPointer(&N); + return N; +} + // Provide GraphTraits specializations for call graphs. template <> struct GraphTraits { typedef LazyCallGraph::Node NodeType; - typedef LazyCallGraph::iterator ChildIteratorType; + typedef LazyCallGraph::edge_iterator ChildIteratorType; static NodeType *getEntryNode(NodeType *N) { return N; } static ChildIteratorType child_begin(NodeType *N) { return N->begin(); } @@ -524,7 +667,7 @@ template <> struct GraphTraits { }; template <> struct GraphTraits { typedef LazyCallGraph::Node NodeType; - typedef LazyCallGraph::iterator ChildIteratorType; + typedef LazyCallGraph::edge_iterator ChildIteratorType; static NodeType *getEntryNode(NodeType *N) { return N; } static ChildIteratorType child_begin(NodeType *N) { return N->begin(); } diff --git a/lib/Analysis/LazyCallGraph.cpp b/lib/Analysis/LazyCallGraph.cpp index 0f0f31e62ac..11e1166001a 100644 --- a/lib/Analysis/LazyCallGraph.cpp +++ b/lib/Analysis/LazyCallGraph.cpp @@ -20,30 +20,36 @@ using namespace llvm; #define DEBUG_TYPE "lcg" -static void findCallees( - SmallVectorImpl &Worklist, SmallPtrSetImpl &Visited, - SmallVectorImpl> &Callees, - DenseMap &CalleeIndexMap) { +static void addEdge(SmallVectorImpl &Edges, + DenseMap &EdgeIndexMap, Function &F, + LazyCallGraph::Edge::Kind EK) { + // Note that we consider *any* function with a definition to be a viable + // edge. Even if the function's definition is subject to replacement by + // some other module (say, a weak definition) there may still be + // optimizations which essentially speculate based on the definition and + // a way to check that the specific definition is in fact the one being + // used. For example, this could be done by moving the weak definition to + // a strong (internal) definition and making the weak definition be an + // alias. Then a test of the address of the weak function against the new + // strong definition's address would be an effective way to determine the + // safety of optimizing a direct call edge. + if (!F.isDeclaration() && + EdgeIndexMap.insert(std::make_pair(&F, Edges.size())).second) { + DEBUG(dbgs() << " Added callable function: " << F.getName() << "\n"); + Edges.emplace_back(LazyCallGraph::Edge(F, EK)); + } +} + +static void findReferences( + SmallVectorImpl &Worklist, + SmallPtrSetImpl &Visited, + SmallVectorImpl &Edges, + DenseMap &EdgeIndexMap) { while (!Worklist.empty()) { Constant *C = Worklist.pop_back_val(); if (Function *F = dyn_cast(C)) { - // Note that we consider *any* function with a definition to be a viable - // edge. Even if the function's definition is subject to replacement by - // some other module (say, a weak definition) there may still be - // optimizations which essentially speculate based on the definition and - // a way to check that the specific definition is in fact the one being - // used. For example, this could be done by moving the weak definition to - // a strong (internal) definition and making the weak definition be an - // alias. Then a test of the address of the weak function against the new - // strong definition's address would be an effective way to determine the - // safety of optimizing a direct call edge. - if (!F->isDeclaration() && - CalleeIndexMap.insert(std::make_pair(F, Callees.size())).second) { - DEBUG(dbgs() << " Added callable function: " << F->getName() - << "\n"); - Callees.push_back(F); - } + addEdge(Edges, EdgeIndexMap, *F, LazyCallGraph::Edge::Ref); continue; } @@ -59,42 +65,55 @@ LazyCallGraph::Node::Node(LazyCallGraph &G, Function &F) << "' to the graph.\n"); SmallVector Worklist; + SmallPtrSet Callees; SmallPtrSet Visited; - // Find all the potential callees in this function. First walk the - // instructions and add every operand which is a constant to the worklist. + + // Find all the potential call graph edges in this function. We track both + // actual call edges and indirect references to functions. The direct calls + // are trivially added, but to accumulate the latter we walk the instructions + // and add every operand which is a constant to the worklist to process + // afterward. for (BasicBlock &BB : F) - for (Instruction &I : BB) + for (Instruction &I : BB) { + if (auto CS = CallSite(&I)) + if (Function *Callee = CS.getCalledFunction()) + if (Callees.insert(Callee).second) { + Visited.insert(Callee); + addEdge(Edges, EdgeIndexMap, *Callee, LazyCallGraph::Edge::Call); + } + for (Value *Op : I.operand_values()) if (Constant *C = dyn_cast(Op)) if (Visited.insert(C).second) Worklist.push_back(C); + } // We've collected all the constant (and thus potentially function or // function containing) operands to all of the instructions in the function. // Process them (recursively) collecting every function found. - findCallees(Worklist, Visited, Callees, CalleeIndexMap); + findReferences(Worklist, Visited, Edges, EdgeIndexMap); } -void LazyCallGraph::Node::insertEdgeInternal(Function &Callee) { - if (Node *N = G->lookup(Callee)) - return insertEdgeInternal(*N); +void LazyCallGraph::Node::insertEdgeInternal(Function &Child, Edge::Kind EK) { + if (Node *N = G->lookup(Child)) + return insertEdgeInternal(*N, EK); - CalleeIndexMap.insert(std::make_pair(&Callee, Callees.size())); - Callees.push_back(&Callee); + EdgeIndexMap.insert(std::make_pair(&Child, Edges.size())); + Edges.emplace_back(Child, EK); } -void LazyCallGraph::Node::insertEdgeInternal(Node &CalleeN) { - CalleeIndexMap.insert(std::make_pair(&CalleeN.getFunction(), Callees.size())); - Callees.push_back(&CalleeN); +void LazyCallGraph::Node::insertEdgeInternal(Node &ChildN, Edge::Kind EK) { + EdgeIndexMap.insert(std::make_pair(&ChildN.getFunction(), Edges.size())); + Edges.emplace_back(ChildN, EK); } -void LazyCallGraph::Node::removeEdgeInternal(Function &Callee) { - auto IndexMapI = CalleeIndexMap.find(&Callee); - assert(IndexMapI != CalleeIndexMap.end() && - "Callee not in the callee set for this caller?"); +void LazyCallGraph::Node::removeEdgeInternal(Function &Child) { + auto IndexMapI = EdgeIndexMap.find(&Child); + assert(IndexMapI != EdgeIndexMap.end() && + "Child not in the edge set for this caller?"); - Callees[IndexMapI->second] = nullptr; - CalleeIndexMap.erase(IndexMapI); + Edges[IndexMapI->second] = Edge(); + EdgeIndexMap.erase(IndexMapI); } LazyCallGraph::LazyCallGraph(Module &M) : NextDFSNumber(0) { @@ -102,10 +121,10 @@ LazyCallGraph::LazyCallGraph(Module &M) : NextDFSNumber(0) { << "\n"); for (Function &F : M) if (!F.isDeclaration() && !F.hasLocalLinkage()) - if (EntryIndexMap.insert(std::make_pair(&F, EntryNodes.size())).second) { + if (EntryIndexMap.insert(std::make_pair(&F, EntryEdges.size())).second) { DEBUG(dbgs() << " Adding '" << F.getName() << "' to entry set of the graph.\n"); - EntryNodes.push_back(&F); + EntryEdges.emplace_back(F, Edge::Ref); } // Now add entry nodes for functions reachable via initializers to globals. @@ -118,21 +137,15 @@ LazyCallGraph::LazyCallGraph(Module &M) : NextDFSNumber(0) { DEBUG(dbgs() << " Adding functions referenced by global initializers to the " "entry set.\n"); - findCallees(Worklist, Visited, EntryNodes, EntryIndexMap); + findReferences(Worklist, Visited, EntryEdges, EntryIndexMap); - for (auto &Entry : EntryNodes) { - assert(!Entry.isNull() && - "We can't have removed edges before we finish the constructor!"); - if (Function *F = Entry.dyn_cast()) - SCCEntryNodes.push_back(F); - else - SCCEntryNodes.push_back(&Entry.get()->getFunction()); - } + for (const Edge &E : EntryEdges) + SCCEntryNodes.push_back(&E.getFunction()); } LazyCallGraph::LazyCallGraph(LazyCallGraph &&G) : BPA(std::move(G.BPA)), NodeMap(std::move(G.NodeMap)), - EntryNodes(std::move(G.EntryNodes)), + EntryEdges(std::move(G.EntryEdges)), EntryIndexMap(std::move(G.EntryIndexMap)), SCCBPA(std::move(G.SCCBPA)), SCCMap(std::move(G.SCCMap)), LeafSCCs(std::move(G.LeafSCCs)), DFSStack(std::move(G.DFSStack)), @@ -144,7 +157,7 @@ LazyCallGraph::LazyCallGraph(LazyCallGraph &&G) LazyCallGraph &LazyCallGraph::operator=(LazyCallGraph &&G) { BPA = std::move(G.BPA); NodeMap = std::move(G.NodeMap); - EntryNodes = std::move(G.EntryNodes); + EntryEdges = std::move(G.EntryEdges); EntryIndexMap = std::move(G.EntryIndexMap); SCCBPA = std::move(G.SCCBPA); SCCMap = std::move(G.SCCMap); @@ -177,43 +190,46 @@ bool LazyCallGraph::SCC::isDescendantOf(const SCC &C) const { return false; } -void LazyCallGraph::SCC::insertIntraSCCEdge(Node &CallerN, Node &CalleeN) { +void LazyCallGraph::SCC::insertIntraSCCEdge(Node &ParentN, Node &ChildN, + Edge::Kind EK) { // First insert it into the caller. - CallerN.insertEdgeInternal(CalleeN); + ParentN.insertEdgeInternal(ChildN, EK); - assert(G->SCCMap.lookup(&CallerN) == this && "Caller must be in this SCC."); - assert(G->SCCMap.lookup(&CalleeN) == this && "Callee must be in this SCC."); + assert(G->SCCMap.lookup(&ParentN) == this && "Parent must be in this SCC."); + assert(G->SCCMap.lookup(&ChildN) == this && "Child must be in this SCC."); // Nothing changes about this SCC or any other. } -void LazyCallGraph::SCC::insertOutgoingEdge(Node &CallerN, Node &CalleeN) { +void LazyCallGraph::SCC::insertOutgoingEdge(Node &ParentN, Node &ChildN, + Edge::Kind EK) { // First insert it into the caller. - CallerN.insertEdgeInternal(CalleeN); + ParentN.insertEdgeInternal(ChildN, EK); - assert(G->SCCMap.lookup(&CallerN) == this && "Caller must be in this SCC."); + assert(G->SCCMap.lookup(&ParentN) == this && "Parent must be in this SCC."); - SCC &CalleeC = *G->SCCMap.lookup(&CalleeN); - assert(&CalleeC != this && "Callee must not be in this SCC."); - assert(CalleeC.isDescendantOf(*this) && - "Callee must be a descendant of the Caller."); + SCC &ChildC = *G->SCCMap.lookup(&ChildN); + assert(&ChildC != this && "Child must not be in this SCC."); + assert(ChildC.isDescendantOf(*this) && + "Child must be a descendant of the Parent."); // The only change required is to add this SCC to the parent set of the // callee. - CalleeC.ParentSCCs.insert(this); + ChildC.ParentSCCs.insert(this); } SmallVector -LazyCallGraph::SCC::insertIncomingEdge(Node &CallerN, Node &CalleeN) { +LazyCallGraph::SCC::insertIncomingEdge(Node &ParentN, Node &ChildN, + Edge::Kind EK) { // First insert it into the caller. - CallerN.insertEdgeInternal(CalleeN); + ParentN.insertEdgeInternal(ChildN, EK); - assert(G->SCCMap.lookup(&CalleeN) == this && "Callee must be in this SCC."); + assert(G->SCCMap.lookup(&ChildN) == this && "Child must be in this SCC."); - SCC &CallerC = *G->SCCMap.lookup(&CallerN); - assert(&CallerC != this && "Caller must not be in this SCC."); - assert(CallerC.isDescendantOf(*this) && - "Caller must be a descendant of the Callee."); + SCC &ParentC = *G->SCCMap.lookup(&ParentN); + assert(&ParentC != this && "Parent must not be in this SCC."); + assert(ParentC.isDescendantOf(*this) && + "Parent must be a descendant of the Child."); // The algorithm we use for merging SCCs based on the cycle introduced here // is to walk the SCC inverted DAG formed by the parent SCC sets. The inverse @@ -231,7 +247,7 @@ LazyCallGraph::SCC::insertIncomingEdge(Node &CallerN, Node &CalleeN) { // participate in the merged connected component. SmallPtrSet ConnectedSCCs; ConnectedSCCs.insert(this); - ConnectedSCCs.insert(&CallerC); + ConnectedSCCs.insert(&ParentC); // We build up a DFS stack of the parents chains. SmallVector, 8> DFSSCCs; @@ -298,8 +314,9 @@ LazyCallGraph::SCC::insertIncomingEdge(Node &CallerN, Node &CalleeN) { C->ParentSCCs.clear(); for (Node *N : *C) { - for (Node &ChildN : *N) { - SCC &ChildC = *G->SCCMap.lookup(&ChildN); + for (Edge &E : *N) { + assert(E.getNode() && "Cannot have a null node within a visited SCC!"); + SCC &ChildC = *G->SCCMap.lookup(E.getNode()); if (&ChildC != C) ChildC.ParentSCCs.erase(C); } @@ -309,8 +326,9 @@ LazyCallGraph::SCC::insertIncomingEdge(Node &CallerN, Node &CalleeN) { C->Nodes.clear(); } for (auto I = Nodes.begin() + NewNodeBeginIdx, E = Nodes.end(); I != E; ++I) - for (Node &ChildN : **I) { - SCC &ChildC = *G->SCCMap.lookup(&ChildN); + for (Edge &E : **I) { + assert(E.getNode() && "Cannot have a null node within a visited SCC!"); + SCC &ChildC = *G->SCCMap.lookup(E.getNode()); if (&ChildC != this) ChildC.ParentSCCs.insert(this); } @@ -322,64 +340,65 @@ LazyCallGraph::SCC::insertIncomingEdge(Node &CallerN, Node &CalleeN) { return SmallVector(ConnectedSCCs.begin(), ConnectedSCCs.end()); } -void LazyCallGraph::SCC::removeInterSCCEdge(Node &CallerN, Node &CalleeN) { +void LazyCallGraph::SCC::removeInterSCCEdge(Node &ParentN, Node &ChildN) { // First remove it from the node. - CallerN.removeEdgeInternal(CalleeN.getFunction()); + ParentN.removeEdgeInternal(ChildN.getFunction()); - assert(G->SCCMap.lookup(&CallerN) == this && + assert(G->SCCMap.lookup(&ParentN) == this && "The caller must be a member of this SCC."); - SCC &CalleeC = *G->SCCMap.lookup(&CalleeN); - assert(&CalleeC != this && + SCC &ChildC = *G->SCCMap.lookup(&ChildN); + assert(&ChildC != this && "This API only supports the rmoval of inter-SCC edges."); assert(std::find(G->LeafSCCs.begin(), G->LeafSCCs.end(), this) == G->LeafSCCs.end() && "Cannot have a leaf SCC caller with a different SCC callee."); - bool HasOtherCallToCalleeC = false; - bool HasOtherCallOutsideSCC = false; + bool HasOtherEdgeToChildC = false; + bool HasOtherChildC = false; for (Node *N : *this) { - for (Node &OtherCalleeN : *N) { - SCC &OtherCalleeC = *G->SCCMap.lookup(&OtherCalleeN); - if (&OtherCalleeC == &CalleeC) { - HasOtherCallToCalleeC = true; + for (Edge &E : *N) { + assert(E.getNode() && "Cannot have a missing node in a visited SCC!"); + SCC &OtherChildC = *G->SCCMap.lookup(E.getNode()); + if (&OtherChildC == &ChildC) { + HasOtherEdgeToChildC = true; break; } - if (&OtherCalleeC != this) - HasOtherCallOutsideSCC = true; + if (&OtherChildC != this) + HasOtherChildC = true; } - if (HasOtherCallToCalleeC) + if (HasOtherEdgeToChildC) break; } // Because the SCCs form a DAG, deleting such an edge cannot change the set // of SCCs in the graph. However, it may cut an edge of the SCC DAG, making - // the caller no longer a parent of the callee. Walk the other call edges - // in the caller to tell. - if (!HasOtherCallToCalleeC) { - bool Removed = CalleeC.ParentSCCs.erase(this); + // the parent SCC no longer connected to the child SCC. If so, we need to + // update the child SCC's map of its parents. + if (!HasOtherEdgeToChildC) { + bool Removed = ChildC.ParentSCCs.erase(this); (void)Removed; assert(Removed && - "Did not find the caller SCC in the callee SCC's parent list!"); + "Did not find the parent SCC in the child SCC's parent list!"); // It may orphan an SCC if it is the last edge reaching it, but that does // not violate any invariants of the graph. - if (CalleeC.ParentSCCs.empty()) - DEBUG(dbgs() << "LCG: Update removing " << CallerN.getFunction().getName() - << " -> " << CalleeN.getFunction().getName() + if (ChildC.ParentSCCs.empty()) + DEBUG(dbgs() << "LCG: Update removing " << ParentN.getFunction().getName() + << " -> " << ChildN.getFunction().getName() << " edge orphaned the callee's SCC!\n"); } - // It may make the Caller SCC a leaf SCC. - if (!HasOtherCallOutsideSCC) + // It may make the Parent SCC a leaf SCC. + if (!HasOtherChildC) G->LeafSCCs.push_back(this); } void LazyCallGraph::SCC::internalDFS( - SmallVectorImpl> &DFSStack, + SmallVectorImpl> &DFSStack, SmallVectorImpl &PendingSCCStack, Node *N, SmallVectorImpl &ResultSCCs) { - Node::iterator I = N->begin(); + auto I = N->begin(); N->LowLink = N->DFSNumber = 1; int NextDFSNumber = 2; for (;;) { @@ -387,9 +406,9 @@ void LazyCallGraph::SCC::internalDFS( "before processing a node."); // We simulate recursion by popping out of the nested loop and continuing. - Node::iterator E = N->end(); + auto E = N->end(); while (I != E) { - Node &ChildN = *I; + Node &ChildN = I->getNode(*G); if (SCC *ChildSCC = G->SCCMap.lookup(&ChildN)) { // Check if we have reached a node in the new (known connected) set of // this SCC. If so, the entire stack is necessarily in that set and we @@ -455,15 +474,15 @@ void LazyCallGraph::SCC::internalDFS( } SmallVector -LazyCallGraph::SCC::removeIntraSCCEdge(Node &CallerN, Node &CalleeN) { +LazyCallGraph::SCC::removeIntraSCCEdge(Node &ParentN, Node &ChildN) { // First remove it from the node. - CallerN.removeEdgeInternal(CalleeN.getFunction()); + ParentN.removeEdgeInternal(ChildN.getFunction()); // We return a list of the resulting *new* SCCs in postorder. SmallVector ResultSCCs; // Direct recursion doesn't impact the SCC graph at all. - if (&CallerN == &CalleeN) + if (&ParentN == &ChildN) return ResultSCCs; // The worklist is every node in the original SCC. @@ -478,16 +497,16 @@ LazyCallGraph::SCC::removeIntraSCCEdge(Node &CallerN, Node &CalleeN) { assert(Worklist.size() > 1 && "We have to have at least two nodes to have an " "edge between them that is within the SCC."); - // The callee can already reach every node in this SCC (by definition). It is + // The child can already reach every node in this SCC (by definition). It is // the only node we know will stay inside this SCC. Everything which - // transitively reaches Callee will also remain in the SCC. To model this we + // transitively reaches Child will also remain in the SCC. To model this we // incrementally add any chain of nodes which reaches something in the new // node set to the new node set. This short circuits one side of the Tarjan's // walk. - insert(CalleeN); + insert(ChildN); // We're going to do a full mini-Tarjan's walk using a local stack here. - SmallVector, 4> DFSStack; + SmallVector, 4> DFSStack; SmallVector PendingSCCStack; do { Node *N = Worklist.pop_back_val(); @@ -501,8 +520,9 @@ LazyCallGraph::SCC::removeIntraSCCEdge(Node &CallerN, Node &CalleeN) { // Now we need to reconnect the current SCC to the graph. bool IsLeafSCC = true; for (Node *N : Nodes) { - for (Node &ChildN : *N) { - SCC &ChildSCC = *G->SCCMap.lookup(&ChildN); + for (Edge &E : *N) { + assert(E.getNode() && "Cannot have a missing node in a visited SCC!"); + SCC &ChildSCC = *G->SCCMap.lookup(E.getNode()); if (&ChildSCC == this) continue; ChildSCC.ParentSCCs.insert(this); @@ -528,18 +548,18 @@ LazyCallGraph::SCC::removeIntraSCCEdge(Node &CallerN, Node &CalleeN) { return ResultSCCs; } -void LazyCallGraph::insertEdge(Node &CallerN, Function &Callee) { +void LazyCallGraph::insertEdge(Node &ParentN, Function &Child, Edge::Kind EK) { assert(SCCMap.empty() && DFSStack.empty() && "This method cannot be called after SCCs have been formed!"); - return CallerN.insertEdgeInternal(Callee); + return ParentN.insertEdgeInternal(Child, EK); } -void LazyCallGraph::removeEdge(Node &CallerN, Function &Callee) { +void LazyCallGraph::removeEdge(Node &ParentN, Function &Child) { assert(SCCMap.empty() && DFSStack.empty() && "This method cannot be called after SCCs have been formed!"); - return CallerN.removeEdgeInternal(Callee); + return ParentN.removeEdgeInternal(Child); } LazyCallGraph::Node &LazyCallGraph::insertInto(Function &F, Node *&MappedN) { @@ -550,17 +570,16 @@ void LazyCallGraph::updateGraphPtrs() { // Process all nodes updating the graph pointers. { SmallVector Worklist; - for (auto &Entry : EntryNodes) - if (Node *EntryN = Entry.dyn_cast()) + for (Edge &E : EntryEdges) + if (Node *EntryN = E.getNode()) Worklist.push_back(EntryN); while (!Worklist.empty()) { Node *N = Worklist.pop_back_val(); N->G = this; - for (auto &Callee : N->Callees) - if (!Callee.isNull()) - if (Node *CalleeN = Callee.dyn_cast()) - Worklist.push_back(CalleeN); + for (Edge &E : N->Edges) + if (Node *ChildN = E.getNode()) + Worklist.push_back(ChildN); } } @@ -596,8 +615,9 @@ LazyCallGraph::SCC *LazyCallGraph::formSCC(Node *RootN, // its children. bool IsLeafSCC = true; for (Node *SCCN : NewSCC->Nodes) - for (Node &SCCChildN : *SCCN) { - SCC &ChildSCC = *SCCMap.lookup(&SCCChildN); + for (Edge &E : *SCCN) { + assert(E.getNode() && "Cannot have a missing node in a visited SCC!"); + SCC &ChildSCC = *SCCMap.lookup(E.getNode()); if (&ChildSCC == NewSCC) continue; ChildSCC.ParentSCCs.insert(NewSCC); @@ -613,7 +633,7 @@ LazyCallGraph::SCC *LazyCallGraph::formSCC(Node *RootN, LazyCallGraph::SCC *LazyCallGraph::getNextSCCInPostOrder() { Node *N; - Node::iterator I; + Node::edge_iterator I; if (!DFSStack.empty()) { N = DFSStack.back().first; I = DFSStack.back().second; @@ -635,9 +655,9 @@ LazyCallGraph::SCC *LazyCallGraph::getNextSCCInPostOrder() { assert(N->DFSNumber != 0 && "We should always assign a DFS number " "before placing a node onto the stack."); - Node::iterator E = N->end(); + auto E = N->end(); while (I != E) { - Node &ChildN = *I; + Node &ChildN = I->getNode(*this); if (ChildN.DFSNumber == 0) { // Mark that we should start at this child when next this node is the // top of the stack. We don't start at the next child to ensure this @@ -686,14 +706,19 @@ LazyCallGraphPrinterPass::LazyCallGraphPrinterPass(raw_ostream &OS) : OS(OS) {} static void printNodes(raw_ostream &OS, LazyCallGraph::Node &N, SmallPtrSetImpl &Printed) { + LazyCallGraph &G = N.getGraph(); + // Recurse depth first through the nodes. - for (LazyCallGraph::Node &ChildN : N) + for (LazyCallGraph::Edge &E : N) { + LazyCallGraph::Node &ChildN = E.getNode(G); if (Printed.insert(&ChildN).second) printNodes(OS, ChildN, Printed); + } - OS << " Call edges in function: " << N.getFunction().getName() << "\n"; - for (LazyCallGraph::iterator I = N.begin(), E = N.end(); I != E; ++I) - OS << " -> " << I->getFunction().getName() << "\n"; + OS << " Edges in function: " << N.getFunction().getName() << "\n"; + for (const LazyCallGraph::Edge &E : N) + OS << " " << (E.isCall() ? "call" : "ref ") << " -> " + << E.getFunction().getName() << "\n"; OS << "\n"; } @@ -716,9 +741,11 @@ PreservedAnalyses LazyCallGraphPrinterPass::run(Module &M, << "\n\n"; SmallPtrSet Printed; - for (LazyCallGraph::Node &N : G) + for (LazyCallGraph::Edge &E : G) { + LazyCallGraph::Node &N = E.getNode(G); if (Printed.insert(&N).second) printNodes(OS, N, Printed); + } for (LazyCallGraph::SCC &SCC : G.postorder_sccs()) printSCC(OS, SCC); diff --git a/test/Analysis/LazyCallGraph/basic.ll b/test/Analysis/LazyCallGraph/basic.ll index fce453bc15d..418559578b4 100644 --- a/test/Analysis/LazyCallGraph/basic.ll +++ b/test/Analysis/LazyCallGraph/basic.ll @@ -3,7 +3,7 @@ ; Basic validation of the call graph analysis used in the new pass manager. define void @f() { -; CHECK-LABEL: Call edges in function: f +; CHECK-LABEL: Edges in function: f ; CHECK-NOT: -> entry: @@ -51,8 +51,8 @@ define void @f12() { declare i32 @__gxx_personality_v0(...) define void @test0() { -; CHECK-LABEL: Call edges in function: test0 -; CHECK-NEXT: -> f +; CHECK-LABEL: Edges in function: test0 +; CHECK-NEXT: call -> f ; CHECK-NOT: -> entry: @@ -64,19 +64,19 @@ entry: } define void ()* @test1(void ()** %x) personality i32 (...)* @__gxx_personality_v0 { -; CHECK-LABEL: Call edges in function: test1 -; CHECK-NEXT: -> f12 -; CHECK-NEXT: -> f11 -; CHECK-NEXT: -> f10 -; CHECK-NEXT: -> f7 -; CHECK-NEXT: -> f9 -; CHECK-NEXT: -> f8 -; CHECK-NEXT: -> f6 -; CHECK-NEXT: -> f5 -; CHECK-NEXT: -> f4 -; CHECK-NEXT: -> f3 -; CHECK-NEXT: -> f2 -; CHECK-NEXT: -> f1 +; CHECK-LABEL: Edges in function: test1 +; CHECK-NEXT: call -> f6 +; CHECK-NEXT: call -> f10 +; CHECK-NEXT: ref -> f12 +; CHECK-NEXT: ref -> f11 +; CHECK-NEXT: ref -> f7 +; CHECK-NEXT: ref -> f9 +; CHECK-NEXT: ref -> f8 +; CHECK-NEXT: ref -> f5 +; CHECK-NEXT: ref -> f4 +; CHECK-NEXT: ref -> f3 +; CHECK-NEXT: ref -> f2 +; CHECK-NEXT: ref -> f1 ; CHECK-NOT: -> entry: @@ -108,14 +108,14 @@ unwind: @h = constant void ()* @f7 define void @test2() { -; CHECK-LABEL: Call edges in function: test2 -; CHECK-NEXT: -> f7 -; CHECK-NEXT: -> f6 -; CHECK-NEXT: -> f5 -; CHECK-NEXT: -> f4 -; CHECK-NEXT: -> f3 -; CHECK-NEXT: -> f2 -; CHECK-NEXT: -> f1 +; CHECK-LABEL: Edges in function: test2 +; CHECK-NEXT: ref -> f7 +; CHECK-NEXT: ref -> f6 +; CHECK-NEXT: ref -> f5 +; CHECK-NEXT: ref -> f4 +; CHECK-NEXT: ref -> f3 +; CHECK-NEXT: ref -> f2 +; CHECK-NEXT: ref -> f1 ; CHECK-NOT: -> load i8*, i8** bitcast (void ()** @g to i8**) @@ -152,15 +152,15 @@ define void @test2() { ; CHECK-NEXT: test2 ; ; CHECK-LABEL: SCC with 1 functions: +; CHECK-NEXT: f10 +; +; CHECK-LABEL: SCC with 1 functions: ; CHECK-NEXT: f12 ; ; CHECK-LABEL: SCC with 1 functions: ; CHECK-NEXT: f11 ; ; CHECK-LABEL: SCC with 1 functions: -; CHECK-NEXT: f10 -; -; CHECK-LABEL: SCC with 1 functions: ; CHECK-NEXT: f9 ; ; CHECK-LABEL: SCC with 1 functions: diff --git a/test/Analysis/LazyCallGraph/non-leaf-intrinsics.ll b/test/Analysis/LazyCallGraph/non-leaf-intrinsics.ll index 89a21e542f7..ce6d63b7aff 100644 --- a/test/Analysis/LazyCallGraph/non-leaf-intrinsics.ll +++ b/test/Analysis/LazyCallGraph/non-leaf-intrinsics.ll @@ -8,7 +8,7 @@ define private void @f() { } define void @calls_statepoint(i8 addrspace(1)* %arg) gc "statepoint-example" { -; CHECK: Call edges in function: calls_statepoint +; CHECK: Edges in function: calls_statepoint ; CHECK-NEXT: -> f entry: %cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)* @@ -17,7 +17,7 @@ entry: } define void @calls_patchpoint() { -; CHECK: Call edges in function: calls_patchpoint +; CHECK: Edges in function: calls_patchpoint ; CHECK-NEXT: -> f entry: %c = bitcast void()* @f to i8* diff --git a/unittests/Analysis/LazyCallGraphTest.cpp b/unittests/Analysis/LazyCallGraphTest.cpp index 6caccb89239..fb3eeb7d234 100644 --- a/unittests/Analysis/LazyCallGraphTest.cpp +++ b/unittests/Analysis/LazyCallGraphTest.cpp @@ -128,29 +128,29 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { // the IR, and everything in our module is an entry node, so just directly // build variables for each node. auto I = CG.begin(); - LazyCallGraph::Node &A1 = *I++; + LazyCallGraph::Node &A1 = (I++)->getNode(CG); EXPECT_EQ("a1", A1.getFunction().getName()); - LazyCallGraph::Node &A2 = *I++; + LazyCallGraph::Node &A2 = (I++)->getNode(CG); EXPECT_EQ("a2", A2.getFunction().getName()); - LazyCallGraph::Node &A3 = *I++; + LazyCallGraph::Node &A3 = (I++)->getNode(CG); EXPECT_EQ("a3", A3.getFunction().getName()); - LazyCallGraph::Node &B1 = *I++; + LazyCallGraph::Node &B1 = (I++)->getNode(CG); EXPECT_EQ("b1", B1.getFunction().getName()); - LazyCallGraph::Node &B2 = *I++; + LazyCallGraph::Node &B2 = (I++)->getNode(CG); EXPECT_EQ("b2", B2.getFunction().getName()); - LazyCallGraph::Node &B3 = *I++; + LazyCallGraph::Node &B3 = (I++)->getNode(CG); EXPECT_EQ("b3", B3.getFunction().getName()); - LazyCallGraph::Node &C1 = *I++; + LazyCallGraph::Node &C1 = (I++)->getNode(CG); EXPECT_EQ("c1", C1.getFunction().getName()); - LazyCallGraph::Node &C2 = *I++; + LazyCallGraph::Node &C2 = (I++)->getNode(CG); EXPECT_EQ("c2", C2.getFunction().getName()); - LazyCallGraph::Node &C3 = *I++; + LazyCallGraph::Node &C3 = (I++)->getNode(CG); EXPECT_EQ("c3", C3.getFunction().getName()); - LazyCallGraph::Node &D1 = *I++; + LazyCallGraph::Node &D1 = (I++)->getNode(CG); EXPECT_EQ("d1", D1.getFunction().getName()); - LazyCallGraph::Node &D2 = *I++; + LazyCallGraph::Node &D2 = (I++)->getNode(CG); EXPECT_EQ("d2", D2.getFunction().getName()); - LazyCallGraph::Node &D3 = *I++; + LazyCallGraph::Node &D3 = (I++)->getNode(CG); EXPECT_EQ("d3", D3.getFunction().getName()); EXPECT_EQ(CG.end(), I); @@ -158,8 +158,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { // independent of order. std::vector Nodes; - for (LazyCallGraph::Node &N : A1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : A1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("a2", Nodes[0]); EXPECT_EQ("b2", Nodes[1]); @@ -171,8 +171,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ(A3.end(), std::next(A3.begin())); EXPECT_EQ("a1", A3.begin()->getFunction().getName()); - for (LazyCallGraph::Node &N : B1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : B1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("b2", Nodes[0]); EXPECT_EQ("d3", Nodes[1]); @@ -183,8 +183,8 @@ TEST(LazyCallGraphTest, BasicGraphFormation) { EXPECT_EQ(B3.end(), std::next(B3.begin())); EXPECT_EQ("b1", B3.begin()->getFunction().getName()); - for (LazyCallGraph::Node &N : C1) - Nodes.push_back(N.getFunction().getName()); + for (LazyCallGraph::Edge &E : C1) + Nodes.push_back(E.getFunction().getName()); std::sort(Nodes.begin(), Nodes.end()); EXPECT_EQ("c2", Nodes[0]); EXPECT_EQ("d2", Nodes[1]); @@ -298,23 +298,23 @@ TEST(LazyCallGraphTest, BasicGraphMutation) { EXPECT_EQ(2, std::distance(A.begin(), A.end())); EXPECT_EQ(0, std::distance(B.begin(), B.end())); - CG.insertEdge(B, lookupFunction(*M, "c")); + CG.insertEdge(B, lookupFunction(*M, "c"), LazyCallGraph::Edge::Call); EXPECT_EQ(1, std::distance(B.begin(), B.end())); - LazyCallGraph::Node &C = *B.begin(); + LazyCallGraph::Node &C = B.begin()->getNode(CG); EXPECT_EQ(0, std::distance(C.begin(), C.end())); - CG.insertEdge(C, B.getFunction()); + CG.insertEdge(C, B.getFunction(), LazyCallGraph::Edge::Call); EXPECT_EQ(1, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, &*C.begin()); + EXPECT_EQ(&B, C.begin()->getNode()); - CG.insertEdge(C, C.getFunction()); + CG.insertEdge(C, C.getFunction(), LazyCallGraph::Edge::Call); EXPECT_EQ(2, std::distance(C.begin(), C.end())); - EXPECT_EQ(&B, &*C.begin()); - EXPECT_EQ(&C, &*std::next(C.begin())); + EXPECT_EQ(&B, C.begin()->getNode()); + EXPECT_EQ(&C, std::next(C.begin())->getNode()); CG.removeEdge(C, B.getFunction()); EXPECT_EQ(1, std::distance(C.begin(), C.end())); - EXPECT_EQ(&C, &*C.begin()); + EXPECT_EQ(&C, C.begin()->getNode()); CG.removeEdge(C, C.getFunction()); EXPECT_EQ(0, std::distance(C.begin(), C.end())); @@ -417,7 +417,7 @@ TEST(LazyCallGraphTest, OutgoingSCCEdgeInsertion) { EXPECT_TRUE(DC.isDescendantOf(CC)); EXPECT_EQ(2, std::distance(A.begin(), A.end())); - AC.insertOutgoingEdge(A, D); + AC.insertOutgoingEdge(A, D, LazyCallGraph::Edge::Call); EXPECT_EQ(3, std::distance(A.begin(), A.end())); EXPECT_TRUE(AC.isParentOf(DC)); EXPECT_EQ(&AC, CG.lookupSCC(A)); @@ -489,7 +489,7 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertion) { // a1 | // / \ | // a3--a2 | - CC.insertIncomingEdge(D2, C2); + CC.insertIncomingEdge(D2, C2, LazyCallGraph::Edge::Call); // Make sure we connected the nodes. EXPECT_EQ(2, std::distance(D2.begin(), D2.end())); @@ -551,7 +551,7 @@ TEST(LazyCallGraphTest, IncomingSCCEdgeInsertionMidTraversal) { ASSERT_EQ(&DC, CG.lookupSCC(D3)); ASSERT_EQ(1, std::distance(D2.begin(), D2.end())); - CC.insertIncomingEdge(D2, C2); + CC.insertIncomingEdge(D2, C2, LazyCallGraph::Edge::Call); EXPECT_EQ(2, std::distance(D2.begin(), D2.end())); // Make sure we have the correct nodes in the SCC sets. @@ -646,14 +646,14 @@ TEST(LazyCallGraphTest, IntraSCCEdgeInsertion) { EXPECT_EQ(&SCC, CG1.lookupSCC(C)); // Insert an edge from 'a' to 'c'. Nothing changes about the SCCs. - SCC.insertIntraSCCEdge(A, C); + SCC.insertIntraSCCEdge(A, C, LazyCallGraph::Edge::Call); EXPECT_EQ(2, std::distance(A.begin(), A.end())); EXPECT_EQ(&SCC, CG1.lookupSCC(A)); EXPECT_EQ(&SCC, CG1.lookupSCC(B)); EXPECT_EQ(&SCC, CG1.lookupSCC(C)); // Insert a self edge from 'a' back to 'a'. - SCC.insertIntraSCCEdge(A, A); + SCC.insertIntraSCCEdge(A, A, LazyCallGraph::Edge::Call); EXPECT_EQ(3, std::distance(A.begin(), A.end())); EXPECT_EQ(&SCC, CG1.lookupSCC(A)); EXPECT_EQ(&SCC, CG1.lookupSCC(B));