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