Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 1 | //===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "HelperDeclRefGraph.h" |
| 10 | #include "ClangMove.h" |
| 11 | #include "clang/AST/Decl.h" |
Haojian Wu | 4775ce5 | 2017-01-17 13:22:37 +0000 | [diff] [blame] | 12 | #include "llvm/Support/Debug.h" |
Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 13 | #include <vector> |
| 14 | |
Haojian Wu | 4775ce5 | 2017-01-17 13:22:37 +0000 | [diff] [blame] | 15 | #define DEBUG_TYPE "clang-move" |
| 16 | |
Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 17 | namespace clang { |
| 18 | namespace move { |
| 19 | |
| 20 | void HelperDeclRefGraph::print(raw_ostream &OS) const { |
| 21 | OS << " --- Call graph Dump --- \n"; |
| 22 | for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) { |
| 23 | const CallGraphNode *N = (I->second).get(); |
| 24 | |
| 25 | OS << " Declarations: "; |
| 26 | N->print(OS); |
| 27 | OS << " (" << N << ") "; |
| 28 | OS << " calls: "; |
| 29 | for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) { |
| 30 | (*CI)->print(OS); |
| 31 | OS << " (" << CI << ") "; |
| 32 | } |
| 33 | OS << '\n'; |
| 34 | } |
| 35 | OS.flush(); |
| 36 | } |
| 37 | |
| 38 | void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) { |
| 39 | assert(Caller); |
| 40 | assert(Callee); |
| 41 | |
| 42 | // Ignore the case where Caller equals Callee. This happens in the static |
| 43 | // class member definitions in global namespace like "int CLASS::static_var = |
| 44 | // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS" |
| 45 | // CXXRecordDecl. |
| 46 | if (Caller == Callee) return; |
| 47 | |
| 48 | // Allocate a new node, mark it as root, and process it's calls. |
| 49 | CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller)); |
| 50 | CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee)); |
| 51 | CallerNode->addCallee(CalleeNode); |
| 52 | } |
| 53 | |
| 54 | void HelperDeclRefGraph::dump() const { print(llvm::errs()); } |
| 55 | |
| 56 | CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) { |
| 57 | F = F->getCanonicalDecl(); |
| 58 | std::unique_ptr<CallGraphNode> &Node = DeclMap[F]; |
| 59 | if (Node) |
| 60 | return Node.get(); |
| 61 | |
| 62 | Node = llvm::make_unique<CallGraphNode>(F); |
| 63 | return Node.get(); |
| 64 | } |
| 65 | |
| 66 | CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const { |
| 67 | auto I = DeclMap.find(D->getCanonicalDecl()); |
| 68 | return I == DeclMap.end() ? nullptr : I->second.get(); |
| 69 | } |
| 70 | |
| 71 | llvm::DenseSet<const CallGraphNode *> |
| 72 | HelperDeclRefGraph::getReachableNodes(const Decl *Root) const { |
| 73 | const auto *RootNode = getNode(Root); |
| 74 | if (!RootNode) |
| 75 | return {}; |
| 76 | llvm::DenseSet<const CallGraphNode *> ConnectedNodes; |
| 77 | std::function<void(const CallGraphNode *)> VisitNode = |
| 78 | [&](const CallGraphNode *Node) { |
| 79 | if (ConnectedNodes.count(Node)) |
| 80 | return; |
| 81 | ConnectedNodes.insert(Node); |
| 82 | for (auto It = Node->begin(), End = Node->end(); It != End; ++It) |
| 83 | VisitNode(*It); |
| 84 | }; |
| 85 | |
| 86 | VisitNode(RootNode); |
| 87 | return ConnectedNodes; |
| 88 | } |
| 89 | |
| 90 | const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) { |
| 91 | const auto *DC = D->getDeclContext(); |
| 92 | const auto *Result = D; |
| 93 | while (DC) { |
| 94 | if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) |
| 95 | Result = RD; |
| 96 | else if (const auto *FD = dyn_cast<FunctionDecl>(DC)) |
| 97 | Result = FD; |
| 98 | DC = DC->getParent(); |
| 99 | } |
| 100 | return Result; |
| 101 | } |
| 102 | |
| 103 | void HelperDeclRGBuilder::run( |
| 104 | const ast_matchers::MatchFinder::MatchResult &Result) { |
| 105 | // Construct the graph by adding a directed edge from caller to callee. |
| 106 | // |
| 107 | // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it |
| 108 | // might be not the targetted Caller Decl, we always use the outmost enclosing |
| 109 | // FunctionDecl/CXXRecordDecl of "dc". For example, |
| 110 | // |
| 111 | // int MoveClass::F() { int a = helper(); return a; } |
| 112 | // |
| 113 | // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST |
| 114 | // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller. |
| 115 | if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { |
| 116 | const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); |
| 117 | assert(DC); |
Nicola Zaghen | 0efd524 | 2018-05-15 16:37:45 +0000 | [diff] [blame] | 118 | LLVM_DEBUG(llvm::dbgs() << "Find helper function usage: " |
| 119 | << FuncRef->getDecl()->getNameAsString() << " (" |
| 120 | << FuncRef->getDecl() << ")\n"); |
Haojian Wu | 4775ce5 | 2017-01-17 13:22:37 +0000 | [diff] [blame] | 121 | RG->addEdge( |
| 122 | getOutmostClassOrFunDecl(DC->getCanonicalDecl()), |
| 123 | getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl())); |
Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 124 | } else if (const auto *UsedClass = |
| 125 | Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) { |
| 126 | const auto *DC = Result.Nodes.getNodeAs<Decl>("dc"); |
| 127 | assert(DC); |
Nicola Zaghen | 0efd524 | 2018-05-15 16:37:45 +0000 | [diff] [blame] | 128 | LLVM_DEBUG(llvm::dbgs() |
| 129 | << "Find helper class usage: " << UsedClass->getNameAsString() |
| 130 | << " (" << UsedClass << ")\n"); |
Haojian Wu | 3626516 | 2017-01-03 09:00:51 +0000 | [diff] [blame] | 131 | RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | } // namespace move |
| 136 | } // namespace clang |