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