[LCG] Build an edge abstraction for the LazyCallGraph and use it to
differentiate between indirect references to functions an direct calls.
This doesn't do a whole lot yet other than change the print out produced
by the analysis, but it lays the groundwork for a very major change I'm
working on next: teaching the call graph to actually be a call graph,
modeling *both* the indirect reference graph and the call graph
simultaneously. More details on that in the next patch though.
The rest of this is essentially a bunch of over-engineering that won't
be interesting until the next patch. But this also isolates essentially
all of the churn necessary to introduce the edge abstraction from the
very important behavior change necessary in order to separately model
the two graphs. So it should make review of the subsequent patch a bit
easier at the cost of making this patch seem poorly motivated. ;]
Differential Revision: http://reviews.llvm.org/D16038
llvm-svn: 259463
diff --git a/llvm/lib/Analysis/LazyCallGraph.cpp b/llvm/lib/Analysis/LazyCallGraph.cpp
index 0f0f31e..11e1166 100644
--- a/llvm/lib/Analysis/LazyCallGraph.cpp
+++ b/llvm/lib/Analysis/LazyCallGraph.cpp
@@ -20,30 +20,36 @@
#define DEBUG_TYPE "lcg"
-static void findCallees(
- SmallVectorImpl<Constant *> &Worklist, SmallPtrSetImpl<Constant *> &Visited,
- SmallVectorImpl<PointerUnion<Function *, LazyCallGraph::Node *>> &Callees,
- DenseMap<Function *, size_t> &CalleeIndexMap) {
+static void addEdge(SmallVectorImpl<LazyCallGraph::Edge> &Edges,
+ DenseMap<Function *, size_t> &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<Constant *> &Worklist,
+ SmallPtrSetImpl<Constant *> &Visited,
+ SmallVectorImpl<LazyCallGraph::Edge> &Edges,
+ DenseMap<Function *, size_t> &EdgeIndexMap) {
while (!Worklist.empty()) {
Constant *C = Worklist.pop_back_val();
if (Function *F = dyn_cast<Function>(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 @@
<< "' to the graph.\n");
SmallVector<Constant *, 16> Worklist;
+ SmallPtrSet<Function *, 4> Callees;
SmallPtrSet<Constant *, 16> 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<Constant>(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 @@
<< "\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 @@
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<Function *>())
- SCCEntryNodes.push_back(F);
- else
- SCCEntryNodes.push_back(&Entry.get<Node *>()->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::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 @@
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 *, 1>
-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 @@
// participate in the merged connected component.
SmallPtrSet<SCC *, 8> ConnectedSCCs;
ConnectedSCCs.insert(this);
- ConnectedSCCs.insert(&CallerC);
+ ConnectedSCCs.insert(&ParentC);
// We build up a DFS stack of the parents chains.
SmallVector<std::pair<SCC *, SCC::parent_iterator>, 8> DFSSCCs;
@@ -298,8 +314,9 @@
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 @@
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 @@
return SmallVector<SCC *, 1>(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<std::pair<Node *, Node::iterator>> &DFSStack,
+ SmallVectorImpl<std::pair<Node *, Node::edge_iterator>> &DFSStack,
SmallVectorImpl<Node *> &PendingSCCStack, Node *N,
SmallVectorImpl<SCC *> &ResultSCCs) {
- Node::iterator I = N->begin();
+ auto I = N->begin();
N->LowLink = N->DFSNumber = 1;
int NextDFSNumber = 2;
for (;;) {
@@ -387,9 +406,9 @@
"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 @@
}
SmallVector<LazyCallGraph::SCC *, 1>
-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<SCC *, 1> 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 @@
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<std::pair<Node *, Node::iterator>, 4> DFSStack;
+ SmallVector<std::pair<Node *, Node::edge_iterator>, 4> DFSStack;
SmallVector<Node *, 4> PendingSCCStack;
do {
Node *N = Worklist.pop_back_val();
@@ -501,8 +520,9 @@
// 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 @@
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 @@
// Process all nodes updating the graph pointers.
{
SmallVector<Node *, 16> Worklist;
- for (auto &Entry : EntryNodes)
- if (Node *EntryN = Entry.dyn_cast<Node *>())
+ 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<Node *>())
- Worklist.push_back(CalleeN);
+ for (Edge &E : N->Edges)
+ if (Node *ChildN = E.getNode())
+ Worklist.push_back(ChildN);
}
}
@@ -596,8 +615,9 @@
// 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::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 @@
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 @@
static void printNodes(raw_ostream &OS, LazyCallGraph::Node &N,
SmallPtrSetImpl<LazyCallGraph::Node *> &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 @@
<< "\n\n";
SmallPtrSet<LazyCallGraph::Node *, 16> 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);