2019-03-01 17:52:53 +08:00
|
|
|
//===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===//
|
2017-01-03 17:00:51 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-01-03 17:00:51 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "HelperDeclRefGraph.h"
|
2019-03-23 00:34:39 +08:00
|
|
|
#include "Move.h"
|
2017-01-03 17:00:51 +08:00
|
|
|
#include "clang/AST/Decl.h"
|
2017-01-17 21:22:37 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2017-01-03 17:00:51 +08:00
|
|
|
#include <vector>
|
|
|
|
|
2017-01-17 21:22:37 +08:00
|
|
|
#define DEBUG_TYPE "clang-move"
|
|
|
|
|
2017-01-03 17:00:51 +08:00
|
|
|
namespace clang {
|
|
|
|
namespace move {
|
|
|
|
|
|
|
|
void HelperDeclRefGraph::print(raw_ostream &OS) const {
|
|
|
|
OS << " --- Call graph Dump --- \n";
|
|
|
|
for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
|
|
|
|
const CallGraphNode *N = (I->second).get();
|
|
|
|
|
|
|
|
OS << " Declarations: ";
|
|
|
|
N->print(OS);
|
|
|
|
OS << " (" << N << ") ";
|
|
|
|
OS << " calls: ";
|
|
|
|
for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
|
2020-02-14 04:34:11 +08:00
|
|
|
CI->Callee->print(OS);
|
2017-01-03 17:00:51 +08:00
|
|
|
OS << " (" << CI << ") ";
|
|
|
|
}
|
|
|
|
OS << '\n';
|
|
|
|
}
|
|
|
|
OS.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
|
|
|
|
assert(Caller);
|
|
|
|
assert(Callee);
|
|
|
|
|
|
|
|
// Ignore the case where Caller equals Callee. This happens in the static
|
|
|
|
// class member definitions in global namespace like "int CLASS::static_var =
|
|
|
|
// 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
|
|
|
|
// CXXRecordDecl.
|
|
|
|
if (Caller == Callee) return;
|
|
|
|
|
|
|
|
// 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));
|
2020-02-14 04:34:11 +08:00
|
|
|
CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr});
|
2017-01-03 17:00:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
|
|
|
|
|
|
|
|
CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
|
|
|
|
F = F->getCanonicalDecl();
|
|
|
|
std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
|
|
|
|
if (Node)
|
|
|
|
return Node.get();
|
|
|
|
|
2019-08-15 07:52:23 +08:00
|
|
|
Node = std::make_unique<CallGraphNode>(F);
|
2017-01-03 17:00:51 +08:00
|
|
|
return Node.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
|
|
|
|
auto I = DeclMap.find(D->getCanonicalDecl());
|
|
|
|
return I == DeclMap.end() ? nullptr : I->second.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::DenseSet<const CallGraphNode *>
|
|
|
|
HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
|
|
|
|
const auto *RootNode = getNode(Root);
|
|
|
|
if (!RootNode)
|
|
|
|
return {};
|
|
|
|
llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
|
|
|
|
std::function<void(const CallGraphNode *)> VisitNode =
|
|
|
|
[&](const CallGraphNode *Node) {
|
|
|
|
if (ConnectedNodes.count(Node))
|
|
|
|
return;
|
|
|
|
ConnectedNodes.insert(Node);
|
|
|
|
for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
|
|
|
|
VisitNode(*It);
|
|
|
|
};
|
|
|
|
|
|
|
|
VisitNode(RootNode);
|
|
|
|
return ConnectedNodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
|
|
|
|
const auto *DC = D->getDeclContext();
|
|
|
|
const auto *Result = D;
|
|
|
|
while (DC) {
|
|
|
|
if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
|
|
|
|
Result = RD;
|
|
|
|
else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
|
|
|
|
Result = FD;
|
|
|
|
DC = DC->getParent();
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HelperDeclRGBuilder::run(
|
|
|
|
const ast_matchers::MatchFinder::MatchResult &Result) {
|
|
|
|
// Construct the graph by adding a directed edge from caller to callee.
|
|
|
|
//
|
|
|
|
// "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
|
|
|
|
// might be not the targetted Caller Decl, we always use the outmost enclosing
|
|
|
|
// FunctionDecl/CXXRecordDecl of "dc". For example,
|
|
|
|
//
|
|
|
|
// int MoveClass::F() { int a = helper(); return a; }
|
|
|
|
//
|
|
|
|
// The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
|
|
|
|
// to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
|
|
|
|
if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
|
|
|
|
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
|
|
|
assert(DC);
|
2018-05-16 00:37:45 +08:00
|
|
|
LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: "
|
2020-08-05 18:48:09 +08:00
|
|
|
<< FuncRef->getDecl()->getDeclName() << " ("
|
2018-05-16 00:37:45 +08:00
|
|
|
<< FuncRef->getDecl() << ")\n");
|
2017-01-17 21:22:37 +08:00
|
|
|
RG->addEdge(
|
|
|
|
getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
|
|
|
|
getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
|
2017-01-03 17:00:51 +08:00
|
|
|
} else if (const auto *UsedClass =
|
|
|
|
Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
|
|
|
|
const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
|
|
|
|
assert(DC);
|
2018-05-16 00:37:45 +08:00
|
|
|
LLVM_DEBUG(llvm::dbgs()
|
2020-08-05 18:48:09 +08:00
|
|
|
<< "Find helper class usage: " << UsedClass->getDeclName()
|
2018-05-16 00:37:45 +08:00
|
|
|
<< " (" << UsedClass << ")\n");
|
2017-01-03 17:00:51 +08:00
|
|
|
RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace move
|
|
|
|
} // namespace clang
|