|
10 | 10 | // |
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
| 13 | +#define DEBUG_TYPE "clang-decl-finder" |
| 14 | + |
13 | 15 | #include "IRGenModule.h" |
14 | 16 | #include "swift/AST/ASTContext.h" |
15 | 17 | #include "swift/AST/IRGenOptions.h" |
@@ -82,6 +84,116 @@ clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) { |
82 | 84 | return nullptr; |
83 | 85 | } |
84 | 86 |
|
| 87 | +auto walkCallGraphFromCtor( |
| 88 | + const clang::CXXConstructorDecl *toplevelCtor, |
| 89 | + llvm::SmallPtrSet<const clang::Decl *, 8> visited, |
| 90 | + llvm::SmallPtrSet<const clang::Decl *, 8> &functionDecls) { |
| 91 | + |
| 92 | + llvm::SmallVector<clang::Stmt *, 8> stack; |
| 93 | + auto recurseStmt = [&stack](clang::Stmt *s) { |
| 94 | + if (s) { |
| 95 | + stack.push_back(s); |
| 96 | + return true; |
| 97 | + } |
| 98 | + return false; |
| 99 | + }; |
| 100 | + |
| 101 | + // Keep track of decls we have already walked over so that we don't re-walk |
| 102 | + // over them redundantly. Returns true if the insertion took place. |
| 103 | + auto visitDecl = [&visited](const clang::Decl *decl) { |
| 104 | + return decl ? visited.insert(decl).second : false; |
| 105 | + }; |
| 106 | + |
| 107 | + auto handleCtor = [&recurseStmt, |
| 108 | + &functionDecls](const clang::CXXConstructorDecl *ctor) { |
| 109 | + functionDecls.insert(ctor); |
| 110 | + if (ctor->getParent()->getDestructor()) |
| 111 | + functionDecls.insert(ctor->getParent()->getDestructor()); |
| 112 | + recurseStmt(ctor->getBody()); |
| 113 | + for (auto *init : ctor->inits()) { |
| 114 | + recurseStmt(init->getInit()); |
| 115 | + if (clang::FieldDecl *field = init->getMember()) |
| 116 | + recurseStmt(field->getInClassInitializer()); |
| 117 | + } |
| 118 | + }; |
| 119 | + |
| 120 | + auto handleFunctionDecl = [&recurseStmt, &functionDecls]( |
| 121 | + clang::FunctionDecl *functionDecl) { |
| 122 | + LLVM_DEBUG({ |
| 123 | + llvm::dbgs() << "HANDLE FUNCTION DECL:\n"; |
| 124 | + llvm::dbgs() << "IS INLINE " |
| 125 | + << (functionDecl->isInlineSpecified() ? "YES" : "NO") |
| 126 | + << "\n"; |
| 127 | + llvm::dbgs() << "IS TEMPLATE INST " |
| 128 | + << (functionDecl->isTemplateInstantiation() ? "YES" : "NO") |
| 129 | + << "\n"; |
| 130 | + }); |
| 131 | + |
| 132 | + if (functionDecl->isInlineSpecified() || // is 'inline' specified |
| 133 | + functionDecl->isInlined() || // is inlined or constexpr |
| 134 | + functionDecl->isTemplateInstantiation()) // is template instance |
| 135 | + functionDecls.insert(functionDecl); |
| 136 | + |
| 137 | + // Even if this function is not inlined or a template instance we want to |
| 138 | + // recurse on it in case it calls something or calls something that calls |
| 139 | + // something that is. |
| 140 | + recurseStmt(functionDecl->getBody()); |
| 141 | + }; |
| 142 | + |
| 143 | + handleCtor(toplevelCtor); |
| 144 | + |
| 145 | + while (!stack.empty()) { |
| 146 | + auto *back = stack.pop_back_val(); |
| 147 | + |
| 148 | + LLVM_DEBUG({ |
| 149 | + llvm::dbgs() << "\nClang Decl Walker Candidate:\n"; |
| 150 | + back->dump(llvm::dbgs(), toplevelCtor->getASTContext()); |
| 151 | + }); |
| 152 | + |
| 153 | + // Handle if the expression is a callsite or a ExprWithCleanups. |
| 154 | + if (auto *fn = dyn_cast<clang::MemberExpr>(back)) { |
| 155 | + if (visitDecl(fn->getMemberDecl())) |
| 156 | + recurseStmt(fn->getMemberDecl()->getBody()); |
| 157 | + } else if (auto *fn = dyn_cast<clang::CallExpr>(back)) { |
| 158 | + auto *memberCall = dyn_cast<clang::CXXMemberCallExpr>(back); |
| 159 | + if (memberCall && visitDecl(memberCall->getMethodDecl())) { |
| 160 | + functionDecls.insert(memberCall->getMethodDecl()); |
| 161 | + recurseStmt(memberCall->getMethodDecl()->getBody()); |
| 162 | + } |
| 163 | + |
| 164 | + if (visitDecl(fn->getCalleeDecl())) { |
| 165 | + functionDecls.insert(fn->getCalleeDecl()); |
| 166 | + recurseStmt(fn->getCalleeDecl()->getBody()); |
| 167 | + } |
| 168 | + } else if (auto *fn = dyn_cast<clang::CXXConstructExpr>(back)) { |
| 169 | + |
| 170 | + // Use the constructor expression to traverse corresponding destructors. |
| 171 | + // This is done because default destructor decls are empty and have very |
| 172 | + // little to traverse. |
| 173 | + if (auto *dtor = fn->getConstructor() && fn->getConstructor()->getParent() |
| 174 | + ? fn->getConstructor()->getParent()->getDestructor() |
| 175 | + : nullptr) { |
| 176 | + handleFunctionDecl(dtor); |
| 177 | + if (dtor->getBody()) |
| 178 | + recurseStmt(dtor->getBody()); |
| 179 | + } |
| 180 | + |
| 181 | + for (auto *child : fn->children()) |
| 182 | + recurseStmt(child); |
| 183 | + |
| 184 | + if (visitDecl(fn->getConstructor())) |
| 185 | + handleCtor(fn->getConstructor()); |
| 186 | + } else if (auto *declRef = dyn_cast<clang::DeclRefExpr>(back)) { |
| 187 | + if (auto *fn = dyn_cast_or_null<clang::FunctionDecl>(declRef->getDecl())) |
| 188 | + handleFunctionDecl(fn); |
| 189 | + } |
| 190 | + |
| 191 | + // Walk the expression's children. |
| 192 | + for (clang::Stmt *child : back->children()) |
| 193 | + recurseStmt(child); |
| 194 | + } |
| 195 | +} |
| 196 | + |
85 | 197 | } // end anonymous namespace |
86 | 198 |
|
87 | 199 | void IRGenModule::emitClangDecl(const clang::Decl *decl) { |
@@ -120,6 +232,26 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) { |
120 | 232 | stack.push_back(D); |
121 | 233 | }); |
122 | 234 |
|
| 235 | + std::vector<const clang::Decl *> ctors; |
| 236 | + llvm::copy_if(stack, std::back_inserter(ctors), [](const clang::Decl *decl) { |
| 237 | + return isa<clang::CXXConstructorDecl>(decl); |
| 238 | + }); |
| 239 | + |
| 240 | + llvm::SmallPtrSet<const clang::Decl *, 8> visited; |
| 241 | + llvm::SmallPtrSet<const clang::Decl *, 8> functionDecls; |
| 242 | + for (auto *ctor : ctors) |
| 243 | + walkCallGraphFromCtor(cast<clang::CXXConstructorDecl>(ctor), visited, |
| 244 | + functionDecls); |
| 245 | + llvm::copy(functionDecls, std::back_inserter(stack)); |
| 246 | + |
| 247 | + LLVM_DEBUG({ |
| 248 | + llvm::dbgs() |
| 249 | + << "\nAdditional Function/Method decls found on constructor path:\n"; |
| 250 | + for (auto *functionDecl : functionDecls) |
| 251 | + functionDecl->dump(llvm::dbgs()); |
| 252 | + llvm::dbgs() << "\n"; |
| 253 | + }); |
| 254 | + |
123 | 255 | while (!stack.empty()) { |
124 | 256 | auto *next = const_cast<clang::Decl *>(stack.pop_back_val()); |
125 | 257 | if (clang::Decl *executableDecl = getDeclWithExecutableCode(next)) { |
|
0 commit comments