Skip to content

Commit d68c7b8

Browse files
committed
[clang][Analysis] CallGraph: store the actual call Expr* in the CallGraphNode::CallRecord
Summary: Storing not just the callee, but the actual call may be interesting for some use-cases. In particular, D72362 would like that to better pretty-print the cycles in call graph. Reviewers: NoQ, erichkeane Reviewed By: NoQ Subscribers: martong, Charusso, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D74081
1 parent 1d48493 commit d68c7b8

File tree

3 files changed

+68
-19
lines changed

3 files changed

+68
-19
lines changed

clang-tools-extra/clang-move/HelperDeclRefGraph.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void HelperDeclRefGraph::print(raw_ostream &OS) const {
2727
OS << " (" << N << ") ";
2828
OS << " calls: ";
2929
for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
30-
(*CI)->print(OS);
30+
CI->Callee->print(OS);
3131
OS << " (" << CI << ") ";
3232
}
3333
OS << '\n';
@@ -48,7 +48,7 @@ void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
4848
// Allocate a new node, mark it as root, and process it's calls.
4949
CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
5050
CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
51-
CallerNode->addCallee(CalleeNode);
51+
CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr});
5252
}
5353

5454
void HelperDeclRefGraph::dump() const { print(llvm::errs()); }

clang/include/clang/Analysis/CallGraph.h

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/STLExtras.h"
2525
#include "llvm/ADT/SetVector.h"
2626
#include "llvm/ADT/SmallVector.h"
27+
#include "llvm/ADT/iterator_range.h"
2728
#include <memory>
2829

2930
namespace clang {
@@ -136,14 +137,23 @@ class CallGraph : public RecursiveASTVisitor<CallGraph> {
136137
private:
137138
/// Add the given declaration to the call graph.
138139
void addNodeForDecl(Decl *D, bool IsGlobal);
139-
140-
/// Allocate a new node in the graph.
141-
CallGraphNode *allocateNewNode(Decl *);
142140
};
143141

144142
class CallGraphNode {
145143
public:
146-
using CallRecord = CallGraphNode *;
144+
struct CallRecord {
145+
CallGraphNode *Callee;
146+
Expr *CallExpr;
147+
148+
CallRecord() = default;
149+
150+
CallRecord(CallGraphNode *Callee_, Expr *CallExpr_)
151+
: Callee(Callee_), CallExpr(CallExpr_) {}
152+
153+
// The call destination is the only important data here,
154+
// allow to transparently unwrap into it.
155+
operator CallGraphNode *() const { return Callee; }
156+
};
147157

148158
private:
149159
/// The function/method declaration.
@@ -164,24 +174,63 @@ class CallGraphNode {
164174
const_iterator begin() const { return CalledFunctions.begin(); }
165175
const_iterator end() const { return CalledFunctions.end(); }
166176

177+
/// Iterator access to callees/children of the node.
178+
llvm::iterator_range<iterator> callees() {
179+
return llvm::make_range(begin(), end());
180+
}
181+
llvm::iterator_range<const_iterator> callees() const {
182+
return llvm::make_range(begin(), end());
183+
}
184+
167185
bool empty() const { return CalledFunctions.empty(); }
168186
unsigned size() const { return CalledFunctions.size(); }
169187

170-
void addCallee(CallGraphNode *N) {
171-
CalledFunctions.push_back(N);
172-
}
188+
void addCallee(CallRecord Call) { CalledFunctions.push_back(Call); }
173189

174190
Decl *getDecl() const { return FD; }
175191

176192
void print(raw_ostream &os) const;
177193
void dump() const;
178194
};
179195

196+
// NOTE: we are comparing based on the callee only. So different call records
197+
// (with different call expressions) to the same callee will compare equal!
198+
inline bool operator==(const CallGraphNode::CallRecord &LHS,
199+
const CallGraphNode::CallRecord &RHS) {
200+
return LHS.Callee == RHS.Callee;
201+
}
202+
180203
} // namespace clang
181204

182-
// Graph traits for iteration, viewing.
183205
namespace llvm {
184206

207+
// Specialize DenseMapInfo for clang::CallGraphNode::CallRecord.
208+
template <> struct DenseMapInfo<clang::CallGraphNode::CallRecord> {
209+
static inline clang::CallGraphNode::CallRecord getEmptyKey() {
210+
return clang::CallGraphNode::CallRecord(
211+
DenseMapInfo<clang::CallGraphNode *>::getEmptyKey(),
212+
DenseMapInfo<clang::Expr *>::getEmptyKey());
213+
}
214+
215+
static inline clang::CallGraphNode::CallRecord getTombstoneKey() {
216+
return clang::CallGraphNode::CallRecord(
217+
DenseMapInfo<clang::CallGraphNode *>::getTombstoneKey(),
218+
DenseMapInfo<clang::Expr *>::getTombstoneKey());
219+
}
220+
221+
static unsigned getHashValue(const clang::CallGraphNode::CallRecord &Val) {
222+
// NOTE: we are comparing based on the callee only.
223+
// Different call records with the same callee will compare equal!
224+
return DenseMapInfo<clang::CallGraphNode *>::getHashValue(Val.Callee);
225+
}
226+
227+
static bool isEqual(const clang::CallGraphNode::CallRecord &LHS,
228+
const clang::CallGraphNode::CallRecord &RHS) {
229+
return LHS == RHS;
230+
}
231+
};
232+
233+
// Graph traits for iteration, viewing.
185234
template <> struct GraphTraits<clang::CallGraphNode*> {
186235
using NodeType = clang::CallGraphNode;
187236
using NodeRef = clang::CallGraphNode *;

clang/lib/Analysis/CallGraph.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,16 @@ class CGBuilder : public StmtVisitor<CGBuilder> {
6666
return nullptr;
6767
}
6868

69-
void addCalledDecl(Decl *D) {
69+
void addCalledDecl(Decl *D, Expr *CallExpr) {
7070
if (G->includeInGraph(D)) {
7171
CallGraphNode *CalleeNode = G->getOrInsertNode(D);
72-
CallerNode->addCallee(CalleeNode);
72+
CallerNode->addCallee({CalleeNode, CallExpr});
7373
}
7474
}
7575

7676
void VisitCallExpr(CallExpr *CE) {
7777
if (Decl *D = getDeclFromCall(CE))
78-
addCalledDecl(D);
78+
addCalledDecl(D, CE);
7979
VisitChildren(CE);
8080
}
8181

@@ -89,14 +89,14 @@ class CGBuilder : public StmtVisitor<CGBuilder> {
8989

9090
void VisitCXXNewExpr(CXXNewExpr *E) {
9191
if (FunctionDecl *FD = E->getOperatorNew())
92-
addCalledDecl(FD);
92+
addCalledDecl(FD, E);
9393
VisitChildren(E);
9494
}
9595

9696
void VisitCXXConstructExpr(CXXConstructExpr *E) {
9797
CXXConstructorDecl *Ctor = E->getConstructor();
9898
if (FunctionDecl *Def = Ctor->getDefinition())
99-
addCalledDecl(Def);
99+
addCalledDecl(Def, E);
100100
VisitChildren(E);
101101
}
102102

@@ -122,7 +122,7 @@ class CGBuilder : public StmtVisitor<CGBuilder> {
122122
else
123123
D = IDecl->lookupPrivateClassMethod(Sel);
124124
if (D) {
125-
addCalledDecl(D);
125+
addCalledDecl(D, ME);
126126
NumObjCCallEdges++;
127127
}
128128
}
@@ -207,7 +207,7 @@ CallGraphNode *CallGraph::getOrInsertNode(Decl *F) {
207207
Node = std::make_unique<CallGraphNode>(F);
208208
// Make Root node a parent of all functions to make sure all are reachable.
209209
if (F)
210-
Root->addCallee(Node.get());
210+
Root->addCallee({Node.get(), /*Call=*/nullptr});
211211
return Node.get();
212212
}
213213

@@ -230,8 +230,8 @@ void CallGraph::print(raw_ostream &OS) const {
230230
OS << " calls: ";
231231
for (CallGraphNode::const_iterator CI = N->begin(),
232232
CE = N->end(); CI != CE; ++CI) {
233-
assert(*CI != Root && "No one can call the root node.");
234-
(*CI)->print(OS);
233+
assert(CI->Callee != Root && "No one can call the root node.");
234+
CI->Callee->print(OS);
235235
OS << " ";
236236
}
237237
OS << '\n';

0 commit comments

Comments
 (0)