/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Frontend/InterfaceStubFunctionsConsumer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===// |
2 | | // |
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 |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "clang/AST/Mangle.h" |
10 | | #include "clang/AST/RecursiveASTVisitor.h" |
11 | | #include "clang/Basic/TargetInfo.h" |
12 | | #include "clang/Frontend/CompilerInstance.h" |
13 | | #include "clang/Frontend/FrontendActions.h" |
14 | | #include "clang/Sema/TemplateInstCallback.h" |
15 | | #include "llvm/BinaryFormat/ELF.h" |
16 | | |
17 | | using namespace clang; |
18 | | |
19 | | namespace { |
20 | | class InterfaceStubFunctionsConsumer : public ASTConsumer { |
21 | | CompilerInstance &Instance; |
22 | | StringRef InFile; |
23 | | StringRef Format; |
24 | | std::set<std::string> ParsedTemplates; |
25 | | |
26 | | enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; |
27 | | struct MangledSymbol { |
28 | | std::string ParentName; |
29 | | uint8_t Type; |
30 | | uint8_t Binding; |
31 | | std::vector<std::string> Names; |
32 | | MangledSymbol() = delete; |
33 | | |
34 | | MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, |
35 | | std::vector<std::string> Names) |
36 | 113 | : ParentName(ParentName), Type(Type), Binding(Binding), Names(Names) {} |
37 | | }; |
38 | | using MangledSymbols = std::map<const NamedDecl *, MangledSymbol>; |
39 | | |
40 | 341 | bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { |
41 | | // Here we filter out anything that's not set to DefaultVisibility. |
42 | | // DefaultVisibility is set on a decl when -fvisibility is not specified on |
43 | | // the command line (or specified as default) and the decl does not have |
44 | | // __attribute__((visibility("hidden"))) set or when the command line |
45 | | // argument is set to hidden but the decl explicitly has |
46 | | // __attribute__((visibility ("default"))) set. We do this so that the user |
47 | | // can have fine grain control of what they want to expose in the stub. |
48 | 436 | auto isVisible = [](const NamedDecl *ND) -> bool { |
49 | 436 | return ND->getVisibility() == DefaultVisibility; |
50 | 436 | }; |
51 | | |
52 | 317 | auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { |
53 | 317 | if (!isVisible(ND)) |
54 | 73 | return true; |
55 | | |
56 | 244 | if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { |
57 | 43 | if (const auto *Parent = VD->getParentFunctionOrMethod()) |
58 | 18 | if (isa<BlockDecl>(Parent) || isa<CXXMethodDecl>(Parent)17 ) |
59 | 2 | return true; |
60 | | |
61 | 41 | if ((VD->getStorageClass() == StorageClass::SC_Extern) || |
62 | 39 | (VD->getStorageClass() == StorageClass::SC_Static && |
63 | 13 | VD->getParentFunctionOrMethod() == nullptr)) |
64 | 5 | return true; |
65 | 237 | } |
66 | | |
67 | 237 | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { |
68 | 201 | if (FD->isInlined() && !isa<CXXMethodDecl>(FD)119 && |
69 | 16 | !Instance.getLangOpts().GNUInline) |
70 | 4 | return true; |
71 | 197 | if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { |
72 | 119 | if (const auto *RC = dyn_cast<CXXRecordDecl>(MD->getParent())) |
73 | 119 | if (isa<ClassTemplateDecl>(RC->getParent()) || !isVisible(RC)) |
74 | 0 | return true; |
75 | 119 | if (MD->isDependentContext() || !MD->hasBody()101 ) |
76 | 94 | return true; |
77 | 103 | } |
78 | 103 | if (FD->getStorageClass() == StorageClass::SC_Static) |
79 | 5 | return true; |
80 | 134 | } |
81 | 134 | return false; |
82 | 134 | }; |
83 | | |
84 | 300 | auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { |
85 | 300 | if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) |
86 | 47 | if (const auto *FD = |
87 | 21 | dyn_cast_or_null<FunctionDecl>(VD->getParentFunctionOrMethod())) |
88 | 21 | return FD; |
89 | 279 | return nullptr; |
90 | 279 | }; |
91 | | |
92 | 226 | auto getMangledNames = [](const NamedDecl *ND) -> std::vector<std::string> { |
93 | 226 | if (!ND) |
94 | 97 | return {""}; |
95 | 129 | ASTNameGenerator NameGen(ND->getASTContext()); |
96 | 129 | std::vector<std::string> MangledNames = NameGen.getAllManglings(ND); |
97 | 129 | if (isa<CXXConstructorDecl>(ND) || isa<CXXDestructorDecl>(ND)119 ) |
98 | 16 | return MangledNames; |
99 | | #ifdef EXPENSIVE_CHECKS |
100 | | assert(MangledNames.size() <= 1 && "Expected only one name mangling."); |
101 | | #endif |
102 | 113 | return {NameGen.getName(ND)}; |
103 | 113 | }; |
104 | | |
105 | 341 | if (!(RDO & FromTU)) |
106 | 0 | return true; |
107 | 341 | if (Symbols.find(ND) != Symbols.end()) |
108 | 15 | return true; |
109 | | // - Currently have not figured out how to produce the names for FieldDecls. |
110 | | // - Do not want to produce symbols for function paremeters. |
111 | 326 | if (isa<FieldDecl>(ND) || isa<ParmVarDecl>(ND)320 ) |
112 | 26 | return true; |
113 | | |
114 | 300 | const NamedDecl *ParentDecl = getParentFunctionDecl(ND); |
115 | 300 | if ((ParentDecl && ignoreDecl(ParentDecl)21 ) || ignoreDecl(ND)296 ) |
116 | 183 | return true; |
117 | | |
118 | 117 | if (RDO & IsLate) { |
119 | 0 | Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) |
120 | 0 | << "Generating Interface Stubs is not supported with " |
121 | 0 | "delayed template parsing."; |
122 | 117 | } else { |
123 | 117 | if (const auto *FD = dyn_cast<FunctionDecl>(ND)) |
124 | 81 | if (FD->isDependentContext()) |
125 | 4 | return true; |
126 | | |
127 | 113 | const bool IsWeak = (ND->hasAttr<WeakAttr>() || |
128 | 110 | ND->hasAttr<WeakRefAttr>() || ND->isWeakImported()); |
129 | | |
130 | 113 | Symbols.insert(std::make_pair( |
131 | 113 | ND, |
132 | 113 | MangledSymbol(getMangledNames(ParentDecl).front(), |
133 | | // Type: |
134 | 36 | isa<VarDecl>(ND) ? llvm::ELF::STT_OBJECT |
135 | 77 | : llvm::ELF::STT_FUNC, |
136 | | // Binding: |
137 | 110 | IsWeak ? llvm::ELF::STB_WEAK3 : llvm::ELF::STB_GLOBAL, |
138 | 113 | getMangledNames(ND)))); |
139 | 113 | } |
140 | 113 | return true; |
141 | 117 | } |
142 | | |
143 | | void |
144 | | HandleDecls(const llvm::iterator_range<DeclContext::decl_iterator> &Decls, |
145 | 96 | MangledSymbols &Symbols, int RDO) { |
146 | 96 | for (const auto *D : Decls) |
147 | 222 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
148 | 96 | } |
149 | | |
150 | | void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, |
151 | 5 | MangledSymbols &Symbols, int RDO) { |
152 | 5 | for (const auto *D : FTD.specializations()) |
153 | 6 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
154 | 5 | } |
155 | | |
156 | | void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, |
157 | 18 | MangledSymbols &Symbols, int RDO) { |
158 | 18 | for (const auto *D : CTD.specializations()) |
159 | 7 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
160 | 18 | } |
161 | | |
162 | 530 | bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { |
163 | 530 | if (!ND) |
164 | 6 | return false; |
165 | | |
166 | 524 | switch (ND->getKind()) { |
167 | 0 | default: |
168 | 0 | break; |
169 | 6 | case Decl::Kind::Namespace: |
170 | 6 | HandleDecls(cast<NamespaceDecl>(ND)->decls(), Symbols, RDO); |
171 | 6 | return true; |
172 | 83 | case Decl::Kind::CXXRecord: |
173 | 83 | HandleDecls(cast<CXXRecordDecl>(ND)->decls(), Symbols, RDO); |
174 | 83 | return true; |
175 | 7 | case Decl::Kind::ClassTemplateSpecialization: |
176 | 7 | HandleDecls(cast<ClassTemplateSpecializationDecl>(ND)->decls(), Symbols, |
177 | 7 | RDO); |
178 | 7 | return true; |
179 | 18 | case Decl::Kind::ClassTemplate: |
180 | 18 | HandleTemplateSpecializations(*cast<ClassTemplateDecl>(ND), Symbols, RDO); |
181 | 18 | return true; |
182 | 5 | case Decl::Kind::FunctionTemplate: |
183 | 5 | HandleTemplateSpecializations(*cast<FunctionTemplateDecl>(ND), Symbols, |
184 | 5 | RDO); |
185 | 5 | return true; |
186 | 1 | case Decl::Kind::Record: |
187 | 3 | case Decl::Kind::Typedef: |
188 | 4 | case Decl::Kind::Enum: |
189 | 5 | case Decl::Kind::EnumConstant: |
190 | 27 | case Decl::Kind::TemplateTypeParm: |
191 | 33 | case Decl::Kind::NonTypeTemplateParm: |
192 | 35 | case Decl::Kind::CXXConversion: |
193 | 37 | case Decl::Kind::UnresolvedUsingValue: |
194 | 40 | case Decl::Kind::Using: |
195 | 40 | case Decl::Kind::UsingShadow: |
196 | 41 | case Decl::Kind::TypeAliasTemplate: |
197 | 44 | case Decl::Kind::TypeAlias: |
198 | 45 | case Decl::Kind::VarTemplate: |
199 | 46 | case Decl::Kind::VarTemplateSpecialization: |
200 | 47 | case Decl::Kind::UsingDirective: |
201 | 48 | case Decl::Kind::TemplateTemplateParm: |
202 | 49 | case Decl::Kind::ClassTemplatePartialSpecialization: |
203 | 50 | case Decl::Kind::IndirectField: |
204 | 53 | case Decl::Kind::ConstructorUsingShadow: |
205 | 54 | case Decl::Kind::CXXDeductionGuide: |
206 | 55 | case Decl::Kind::NamespaceAlias: |
207 | 57 | case Decl::Kind::UnresolvedUsingTypename: |
208 | 57 | return true; |
209 | 54 | case Decl::Kind::Var: { |
210 | | // Bail on any VarDecl that either has no named symbol. |
211 | 54 | if (!ND->getIdentifier()) |
212 | 1 | return true; |
213 | 53 | const auto *VD = cast<VarDecl>(ND); |
214 | | // Bail on any VarDecl that is a dependent or templated type. |
215 | 53 | if (VD->isTemplated() || VD->getType()->isDependentType()47 ) |
216 | 6 | return true; |
217 | 47 | if (WriteNamedDecl(ND, Symbols, RDO)) |
218 | 47 | return true; |
219 | 0 | break; |
220 | 0 | } |
221 | 20 | case Decl::Kind::ParmVar: |
222 | 116 | case Decl::Kind::CXXMethod: |
223 | 181 | case Decl::Kind::CXXConstructor: |
224 | 214 | case Decl::Kind::CXXDestructor: |
225 | 288 | case Decl::Kind::Function: |
226 | 294 | case Decl::Kind::Field: |
227 | 294 | if (WriteNamedDecl(ND, Symbols, RDO)) |
228 | 294 | return true; |
229 | 0 | } |
230 | | |
231 | | // While interface stubs are in the development stage, it's probably best to |
232 | | // catch anything that's not a VarDecl or Template/FunctionDecl. |
233 | 0 | Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) |
234 | 0 | << "Expected a function or function template decl."; |
235 | 0 | return false; |
236 | 0 | } |
237 | | |
238 | | public: |
239 | | InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, |
240 | | StringRef Format) |
241 | 70 | : Instance(Instance), InFile(InFile), Format(Format) {} |
242 | | |
243 | 70 | void HandleTranslationUnit(ASTContext &context) override { |
244 | 70 | struct Visitor : public RecursiveASTVisitor<Visitor> { |
245 | 295 | bool VisitNamedDecl(NamedDecl *ND) { |
246 | 295 | if (const auto *FD = dyn_cast<FunctionDecl>(ND)) |
247 | 111 | if (FD->isLateTemplateParsed()) { |
248 | 0 | LateParsedDecls.insert(FD); |
249 | 0 | return true; |
250 | 0 | } |
251 | | |
252 | 295 | if (const auto *VD = dyn_cast<ValueDecl>(ND)) { |
253 | 193 | ValueDecls.insert(VD); |
254 | 193 | return true; |
255 | 193 | } |
256 | | |
257 | 102 | NamedDecls.insert(ND); |
258 | 102 | return true; |
259 | 102 | } |
260 | | |
261 | 70 | std::set<const NamedDecl *> LateParsedDecls; |
262 | 70 | std::set<NamedDecl *> NamedDecls; |
263 | 70 | std::set<const ValueDecl *> ValueDecls; |
264 | 70 | } v; |
265 | | |
266 | 70 | v.TraverseDecl(context.getTranslationUnitDecl()); |
267 | | |
268 | 70 | MangledSymbols Symbols; |
269 | 70 | auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); |
270 | 70 | if (!OS) |
271 | 0 | return; |
272 | | |
273 | 70 | if (Instance.getLangOpts().DelayedTemplateParsing) { |
274 | 1 | clang::Sema &S = Instance.getSema(); |
275 | 0 | for (const auto *FD : v.LateParsedDecls) { |
276 | 0 | clang::LateParsedTemplate &LPT = |
277 | 0 | *S.LateParsedTemplateMap.find(cast<FunctionDecl>(FD))->second; |
278 | 0 | S.LateTemplateParser(S.OpaqueParser, LPT); |
279 | 0 | HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); |
280 | 0 | } |
281 | 1 | } |
282 | | |
283 | 70 | for (const NamedDecl *ND : v.ValueDecls) |
284 | 193 | HandleNamedDecl(ND, Symbols, FromTU); |
285 | 70 | for (const NamedDecl *ND : v.NamedDecls) |
286 | 102 | HandleNamedDecl(ND, Symbols, FromTU); |
287 | | |
288 | 70 | auto writeIfsV1 = [this](const llvm::Triple &T, |
289 | 70 | const MangledSymbols &Symbols, |
290 | 70 | const ASTContext &context, StringRef Format, |
291 | 70 | raw_ostream &OS) -> void { |
292 | 70 | OS << "--- !" << Format << "\n"; |
293 | 70 | OS << "IfsVersion: 2.0\n"; |
294 | 70 | OS << "Triple: " << T.str() << "\n"; |
295 | 70 | OS << "ObjectFileFormat: " |
296 | 70 | << "ELF" |
297 | 70 | << "\n"; // TODO: For now, just ELF. |
298 | 70 | OS << "Symbols:\n"; |
299 | 113 | for (const auto &E : Symbols) { |
300 | 113 | const MangledSymbol &Symbol = E.second; |
301 | 135 | for (auto Name : Symbol.Names) { |
302 | 135 | OS << " - { Name: \"" |
303 | 135 | << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus16 |
304 | 125 | ? "" |
305 | 10 | : (Symbol.ParentName + ".")) |
306 | 135 | << Name << "\", Type: "; |
307 | 135 | switch (Symbol.Type) { |
308 | 0 | default: |
309 | 0 | llvm_unreachable( |
310 | 0 | "clang -emit-interface-stubs: Unexpected symbol type."); |
311 | 0 | case llvm::ELF::STT_NOTYPE: |
312 | 0 | OS << "NoType"; |
313 | 0 | break; |
314 | 36 | case llvm::ELF::STT_OBJECT: { |
315 | 36 | auto VD = cast<ValueDecl>(E.first)->getType(); |
316 | 36 | OS << "Object, Size: " |
317 | 36 | << context.getTypeSizeInChars(VD).getQuantity(); |
318 | 36 | break; |
319 | 0 | } |
320 | 99 | case llvm::ELF::STT_FUNC: |
321 | 99 | OS << "Func"; |
322 | 99 | break; |
323 | 135 | } |
324 | 135 | if (Symbol.Binding == llvm::ELF::STB_WEAK) |
325 | 3 | OS << ", Weak: true"; |
326 | 135 | OS << " }\n"; |
327 | 135 | } |
328 | 113 | } |
329 | 70 | OS << "...\n"; |
330 | 70 | OS.flush(); |
331 | 70 | }; |
332 | | |
333 | 70 | assert(Format == "experimental-ifs-v2" && "Unexpected IFS Format."); |
334 | 70 | writeIfsV1(Instance.getTarget().getTriple(), Symbols, context, Format, *OS); |
335 | 70 | } |
336 | | }; |
337 | | } // namespace |
338 | | |
339 | | std::unique_ptr<ASTConsumer> |
340 | | GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance &CI, |
341 | 70 | StringRef InFile) { |
342 | 70 | return std::make_unique<InterfaceStubFunctionsConsumer>( |
343 | 70 | CI, InFile, "experimental-ifs-v2"); |
344 | 70 | } |