1ffe9f00cSFangrui Song //===-- HelperDeclRefGraph.cpp - AST-based call graph for helper decls ----===// 23626516bSHaojian Wu // 32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information. 52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 63626516bSHaojian Wu // 73626516bSHaojian Wu //===----------------------------------------------------------------------===// 83626516bSHaojian Wu 93626516bSHaojian Wu #include "HelperDeclRefGraph.h" 1071ebc9ebSNico Weber #include "Move.h" 113626516bSHaojian Wu #include "clang/AST/Decl.h" 124775ce56SHaojian Wu #include "llvm/Support/Debug.h" 133626516bSHaojian Wu #include <vector> 143626516bSHaojian Wu 154775ce56SHaojian Wu #define DEBUG_TYPE "clang-move" 164775ce56SHaojian Wu 173626516bSHaojian Wu namespace clang { 183626516bSHaojian Wu namespace move { 193626516bSHaojian Wu 203626516bSHaojian Wu void HelperDeclRefGraph::print(raw_ostream &OS) const { 213626516bSHaojian Wu OS << " --- Call graph Dump --- \n"; 223626516bSHaojian Wu for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) { 233626516bSHaojian Wu const CallGraphNode *N = (I->second).get(); 243626516bSHaojian Wu 253626516bSHaojian Wu OS << " Declarations: "; 263626516bSHaojian Wu N->print(OS); 273626516bSHaojian Wu OS << " (" << N << ") "; 283626516bSHaojian Wu OS << " calls: "; 293626516bSHaojian Wu for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) { 30*d68c7b8eSRoman Lebedev CI->Callee->print(OS); 313626516bSHaojian Wu OS << " (" << CI << ") "; 323626516bSHaojian Wu } 333626516bSHaojian Wu OS << '\n'; 343626516bSHaojian Wu } 353626516bSHaojian Wu OS.flush(); 363626516bSHaojian Wu } 373626516bSHaojian Wu 383626516bSHaojian Wu void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) { 393626516bSHaojian Wu assert(Caller); 403626516bSHaojian Wu assert(Callee); 413626516bSHaojian Wu 423626516bSHaojian Wu // Ignore the case where Caller equals Callee. This happens in the static 433626516bSHaojian Wu // class member definitions in global namespace like "int CLASS::static_var = 443626516bSHaojian Wu // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS" 453626516bSHaojian Wu // CXXRecordDecl. 463626516bSHaojian Wu if (Caller == Callee) return; 473626516bSHaojian Wu 483626516bSHaojian Wu // Allocate a new node, mark it as root, and process it's calls. 493626516bSHaojian Wu CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller)); 503626516bSHaojian Wu CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee)); 51*d68c7b8eSRoman Lebedev CallerNode->addCallee({CalleeNode, /*CallExpr=*/nullptr}); 523626516bSHaojian Wu } 533626516bSHaojian Wu 543626516bSHaojian Wu void HelperDeclRefGraph::dump() const { print(llvm::errs()); } 553626516bSHaojian Wu 563626516bSHaojian Wu CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) { 573626516bSHaojian Wu F = F->getCanonicalDecl(); 583626516bSHaojian Wu std::unique_ptr<CallGraphNode> &Node = DeclMap[F]; 593626516bSHaojian Wu if (Node) 603626516bSHaojian Wu return Node.get(); 613626516bSHaojian Wu 621c705d9cSJonas Devlieghere Node = std::make_unique<CallGraphNode>(F); 633626516bSHaojian Wu return Node.get(); 643626516bSHaojian Wu } 653626516bSHaojian Wu 663626516bSHaojian Wu CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const { 673626516bSHaojian Wu auto I = DeclMap.find(D->getCanonicalDecl()); 683626516bSHaojian Wu return I == DeclMap.end() ? nullptr : I->second.get(); 693626516bSHaojian Wu } 703626516bSHaojian Wu 713626516bSHaojian Wu llvm::DenseSet<const CallGraphNode *> 723626516bSHaojian Wu HelperDeclRefGraph::getReachableNodes(const Decl *Root) const { 733626516bSHaojian Wu const auto *RootNode = getNode(Root); 743626516bSHaojian Wu if (!RootNode) 753626516bSHaojian Wu return {}; 763626516bSHaojian Wu llvm::DenseSet<const CallGraphNode *> ConnectedNodes; 773626516bSHaojian Wu std::function<void(const CallGraphNode *)> VisitNode = 783626516bSHaojian Wu [&](const CallGraphNode *Node) { 793626516bSHaojian Wu if (ConnectedNodes.count(Node)) 803626516bSHaojian Wu return; 813626516bSHaojian Wu ConnectedNodes.insert(Node); 823626516bSHaojian Wu for (auto It = Node->begin(), End = Node->end(); It != End; ++It) 833626516bSHaojian Wu VisitNode(*It); 843626516bSHaojian Wu }; 853626516bSHaojian Wu 863626516bSHaojian Wu VisitNode(RootNode); 873626516bSHaojian Wu return ConnectedNodes; 883626516bSHaojian Wu } 893626516bSHaojian Wu 903626516bSHaojian Wu const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) { 913626516bSHaojian Wu const auto *DC = D->getDeclContext(); 923626516bSHaojian Wu const auto *Result = D; 933626516bSHaojian Wu while (DC) { 943626516bSHaojian Wu if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) 953626516bSHaojian Wu Result = RD; 963626516bSHaojian Wu else if (const auto *FD = dyn_cast<FunctionDecl>(DC)) 973626516bSHaojian Wu Result = FD; 983626516bSHaojian Wu DC = DC->getParent(); 993626516bSHaojian Wu } 1003626516bSHaojian Wu return Result; 1013626516bSHaojian Wu } 1023626516bSHaojian Wu 1033626516bSHaojian Wu void HelperDeclRGBuilder::run( 1043626516bSHaojian Wu const ast_matchers::MatchFinder::MatchResult &Result) { 1053626516bSHaojian Wu // Construct the graph by adding a directed edge from caller to callee. 1063626516bSHaojian Wu // 1073626516bSHaojian Wu // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it 1083626516bSHaojian Wu // might be not the targetted Caller Decl, we always use the outmost enclosing 1093626516bSHaojian Wu // FunctionDecl/CXXRecordDecl of "dc". For example, 1103626516bSHaojian Wu // 1113626516bSHaojian Wu // int MoveClass::F() { int a = helper(); return a; } 1123626516bSHaojian Wu // 1133626516bSHaojian Wu // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST 1143626516bSHaojian Wu // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller. 1153626516bSHaojian Wu if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { 1163626516bSHaojian Wu const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); 1173626516bSHaojian Wu assert(DC); 1180efd5248SNicola Zaghen LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: " 1194775ce56SHaojian Wu << FuncRef->getDecl()->getNameAsString() << " (" 1204775ce56SHaojian Wu << FuncRef->getDecl() << ")\n"); 1214775ce56SHaojian Wu RG->addEdge( 1224775ce56SHaojian Wu getOutmostClassOrFunDecl(DC->getCanonicalDecl()), 1234775ce56SHaojian Wu getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl())); 1243626516bSHaojian Wu } else if (const auto *UsedClass = 1253626516bSHaojian Wu Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) { 1263626516bSHaojian Wu const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); 1273626516bSHaojian Wu assert(DC); 1280efd5248SNicola Zaghen LLVM_DEBUG(llvm::dbgs() 1290efd5248SNicola Zaghen << "Find helper class usage: " << UsedClass->getNameAsString() 1300efd5248SNicola Zaghen << " (" << UsedClass << ")\n"); 1313626516bSHaojian Wu RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); 1323626516bSHaojian Wu } 1333626516bSHaojian Wu } 1343626516bSHaojian Wu 1353626516bSHaojian Wu } // namespace move 1363626516bSHaojian Wu } // namespace clang 137