mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-29 00:21:14 +00:00
[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
This commit is contained in:
parent
1d4849379f
commit
d68c7b8e3e
@ -27,7 +27,7 @@ void HelperDeclRefGraph::print(raw_ostream &OS) const {
|
||||
OS << " (" << N << ") ";
|
||||
OS << " calls: ";
|
||||
for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
|
||||
(*CI)->print(OS);
|
||||
CI->Callee->print(OS);
|
||||
OS << " (" << CI << ") ";
|
||||
}
|
||||
OS << '\n';
|
||||
@ -48,7 +48,7 @@ void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
|
||||
// Allocate a new node, mark it as root, and process it's calls.
|
||||
CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
|
||||
CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
|
||||
CallerNode->addCallee(CalleeNode);
|
||||
CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr});
|
||||
}
|
||||
|
||||
void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include <memory>
|
||||
|
||||
namespace clang {
|
||||
@ -136,14 +137,23 @@ public:
|
||||
private:
|
||||
/// Add the given declaration to the call graph.
|
||||
void addNodeForDecl(Decl *D, bool IsGlobal);
|
||||
|
||||
/// Allocate a new node in the graph.
|
||||
CallGraphNode *allocateNewNode(Decl *);
|
||||
};
|
||||
|
||||
class CallGraphNode {
|
||||
public:
|
||||
using CallRecord = CallGraphNode *;
|
||||
struct CallRecord {
|
||||
CallGraphNode *Callee;
|
||||
Expr *CallExpr;
|
||||
|
||||
CallRecord() = default;
|
||||
|
||||
CallRecord(CallGraphNode *Callee_, Expr *CallExpr_)
|
||||
: Callee(Callee_), CallExpr(CallExpr_) {}
|
||||
|
||||
// The call destination is the only important data here,
|
||||
// allow to transparently unwrap into it.
|
||||
operator CallGraphNode *() const { return Callee; }
|
||||
};
|
||||
|
||||
private:
|
||||
/// The function/method declaration.
|
||||
@ -164,12 +174,18 @@ public:
|
||||
const_iterator begin() const { return CalledFunctions.begin(); }
|
||||
const_iterator end() const { return CalledFunctions.end(); }
|
||||
|
||||
/// Iterator access to callees/children of the node.
|
||||
llvm::iterator_range<iterator> callees() {
|
||||
return llvm::make_range(begin(), end());
|
||||
}
|
||||
llvm::iterator_range<const_iterator> callees() const {
|
||||
return llvm::make_range(begin(), end());
|
||||
}
|
||||
|
||||
bool empty() const { return CalledFunctions.empty(); }
|
||||
unsigned size() const { return CalledFunctions.size(); }
|
||||
|
||||
void addCallee(CallGraphNode *N) {
|
||||
CalledFunctions.push_back(N);
|
||||
}
|
||||
void addCallee(CallRecord Call) { CalledFunctions.push_back(Call); }
|
||||
|
||||
Decl *getDecl() const { return FD; }
|
||||
|
||||
@ -177,11 +193,44 @@ public:
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
// NOTE: we are comparing based on the callee only. So different call records
|
||||
// (with different call expressions) to the same callee will compare equal!
|
||||
inline bool operator==(const CallGraphNode::CallRecord &LHS,
|
||||
const CallGraphNode::CallRecord &RHS) {
|
||||
return LHS.Callee == RHS.Callee;
|
||||
}
|
||||
|
||||
} // namespace clang
|
||||
|
||||
// Graph traits for iteration, viewing.
|
||||
namespace llvm {
|
||||
|
||||
// Specialize DenseMapInfo for clang::CallGraphNode::CallRecord.
|
||||
template <> struct DenseMapInfo<clang::CallGraphNode::CallRecord> {
|
||||
static inline clang::CallGraphNode::CallRecord getEmptyKey() {
|
||||
return clang::CallGraphNode::CallRecord(
|
||||
DenseMapInfo<clang::CallGraphNode *>::getEmptyKey(),
|
||||
DenseMapInfo<clang::Expr *>::getEmptyKey());
|
||||
}
|
||||
|
||||
static inline clang::CallGraphNode::CallRecord getTombstoneKey() {
|
||||
return clang::CallGraphNode::CallRecord(
|
||||
DenseMapInfo<clang::CallGraphNode *>::getTombstoneKey(),
|
||||
DenseMapInfo<clang::Expr *>::getTombstoneKey());
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const clang::CallGraphNode::CallRecord &Val) {
|
||||
// NOTE: we are comparing based on the callee only.
|
||||
// Different call records with the same callee will compare equal!
|
||||
return DenseMapInfo<clang::CallGraphNode *>::getHashValue(Val.Callee);
|
||||
}
|
||||
|
||||
static bool isEqual(const clang::CallGraphNode::CallRecord &LHS,
|
||||
const clang::CallGraphNode::CallRecord &RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
};
|
||||
|
||||
// Graph traits for iteration, viewing.
|
||||
template <> struct GraphTraits<clang::CallGraphNode*> {
|
||||
using NodeType = clang::CallGraphNode;
|
||||
using NodeRef = clang::CallGraphNode *;
|
||||
|
@ -66,16 +66,16 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void addCalledDecl(Decl *D) {
|
||||
void addCalledDecl(Decl *D, Expr *CallExpr) {
|
||||
if (G->includeInGraph(D)) {
|
||||
CallGraphNode *CalleeNode = G->getOrInsertNode(D);
|
||||
CallerNode->addCallee(CalleeNode);
|
||||
CallerNode->addCallee({CalleeNode, CallExpr});
|
||||
}
|
||||
}
|
||||
|
||||
void VisitCallExpr(CallExpr *CE) {
|
||||
if (Decl *D = getDeclFromCall(CE))
|
||||
addCalledDecl(D);
|
||||
addCalledDecl(D, CE);
|
||||
VisitChildren(CE);
|
||||
}
|
||||
|
||||
@ -89,14 +89,14 @@ public:
|
||||
|
||||
void VisitCXXNewExpr(CXXNewExpr *E) {
|
||||
if (FunctionDecl *FD = E->getOperatorNew())
|
||||
addCalledDecl(FD);
|
||||
addCalledDecl(FD, E);
|
||||
VisitChildren(E);
|
||||
}
|
||||
|
||||
void VisitCXXConstructExpr(CXXConstructExpr *E) {
|
||||
CXXConstructorDecl *Ctor = E->getConstructor();
|
||||
if (FunctionDecl *Def = Ctor->getDefinition())
|
||||
addCalledDecl(Def);
|
||||
addCalledDecl(Def, E);
|
||||
VisitChildren(E);
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ public:
|
||||
else
|
||||
D = IDecl->lookupPrivateClassMethod(Sel);
|
||||
if (D) {
|
||||
addCalledDecl(D);
|
||||
addCalledDecl(D, ME);
|
||||
NumObjCCallEdges++;
|
||||
}
|
||||
}
|
||||
@ -207,7 +207,7 @@ CallGraphNode *CallGraph::getOrInsertNode(Decl *F) {
|
||||
Node = std::make_unique<CallGraphNode>(F);
|
||||
// Make Root node a parent of all functions to make sure all are reachable.
|
||||
if (F)
|
||||
Root->addCallee(Node.get());
|
||||
Root->addCallee({Node.get(), /*Call=*/nullptr});
|
||||
return Node.get();
|
||||
}
|
||||
|
||||
@ -230,8 +230,8 @@ void CallGraph::print(raw_ostream &OS) const {
|
||||
OS << " calls: ";
|
||||
for (CallGraphNode::const_iterator CI = N->begin(),
|
||||
CE = N->end(); CI != CE; ++CI) {
|
||||
assert(*CI != Root && "No one can call the root node.");
|
||||
(*CI)->print(OS);
|
||||
assert(CI->Callee != Root && "No one can call the root node.");
|
||||
CI->Callee->print(OS);
|
||||
OS << " ";
|
||||
}
|
||||
OS << '\n';
|
||||
|
Loading…
Reference in New Issue
Block a user