1 //===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
17 using namespace clang
;
20 class InterfaceStubFunctionsConsumer
: public ASTConsumer
{
21 CompilerInstance
&Instance
;
24 std::set
<std::string
> ParsedTemplates
;
26 enum RootDeclOrigin
{ TopLevel
= 0, FromTU
= 1, IsLate
= 2 };
27 struct MangledSymbol
{
28 std::string ParentName
;
31 std::vector
<std::string
> Names
;
32 MangledSymbol() = delete;
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
)) {}
39 using MangledSymbols
= std::map
<const NamedDecl
*, MangledSymbol
>;
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
;
53 auto ignoreDecl
= [this, isVisible
](const NamedDecl
*ND
) -> bool {
57 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(ND
)) {
58 if (const auto *Parent
= VD
->getParentFunctionOrMethod())
59 if (isa
<BlockDecl
>(Parent
) || isa
<CXXMethodDecl
>(Parent
))
62 if ((VD
->getStorageClass() == StorageClass::SC_Extern
) ||
63 (VD
->getStorageClass() == StorageClass::SC_Static
&&
64 VD
->getParentFunctionOrMethod() == nullptr))
68 if (const FunctionDecl
*FD
= dyn_cast
<FunctionDecl
>(ND
)) {
69 if (FD
->isInlined() && !isa
<CXXMethodDecl
>(FD
) &&
70 !Instance
.getLangOpts().GNUInline
)
72 if (const CXXMethodDecl
*MD
= dyn_cast
<CXXMethodDecl
>(FD
)) {
73 if (const auto *RC
= dyn_cast
<CXXRecordDecl
>(MD
->getParent()))
74 if (isa
<ClassTemplateDecl
>(RC
->getParent()) || !isVisible(RC
))
76 if (MD
->isDependentContext() || !MD
->hasBody())
79 if (FD
->getStorageClass() == StorageClass::SC_Static
)
85 auto getParentFunctionDecl
= [](const NamedDecl
*ND
) -> const NamedDecl
* {
86 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(ND
))
88 dyn_cast_or_null
<FunctionDecl
>(VD
->getParentFunctionOrMethod()))
93 auto getMangledNames
= [](const NamedDecl
*ND
) -> std::vector
<std::string
> {
96 ASTNameGenerator
NameGen(ND
->getASTContext());
97 std::vector
<std::string
> MangledNames
= NameGen
.getAllManglings(ND
);
98 if (isa
<CXXConstructorDecl
>(ND
) || isa
<CXXDestructorDecl
>(ND
))
100 #ifdef EXPENSIVE_CHECKS
101 assert(MangledNames
.size() <= 1 && "Expected only one name mangling.");
103 return {NameGen
.getName(ND
)};
108 if (Symbols
.find(ND
) != Symbols
.end())
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
>(ND
) || isa
<ParmVarDecl
>(ND
))
115 const NamedDecl
*ParentDecl
= getParentFunctionDecl(ND
);
116 if ((ParentDecl
&& ignoreDecl(ParentDecl
)) || ignoreDecl(ND
))
120 Instance
.getDiagnostics().Report(diag::err_asm_invalid_type_in_input
)
121 << "Generating Interface Stubs is not supported with "
122 "delayed template parsing.";
124 if (const auto *FD
= dyn_cast
<FunctionDecl
>(ND
))
125 if (FD
->isDependentContext())
128 const bool IsWeak
= (ND
->hasAttr
<WeakAttr
>() ||
129 ND
->hasAttr
<WeakRefAttr
>() || ND
->isWeakImported());
131 Symbols
.insert(std::make_pair(
133 MangledSymbol(getMangledNames(ParentDecl
).front(),
135 isa
<VarDecl
>(ND
) ? llvm::ELF::STT_OBJECT
136 : llvm::ELF::STT_FUNC
,
138 IsWeak
? llvm::ELF::STB_WEAK
: llvm::ELF::STB_GLOBAL
,
139 getMangledNames(ND
))));
145 HandleDecls(const llvm::iterator_range
<DeclContext::decl_iterator
> &Decls
,
146 MangledSymbols
&Symbols
, int RDO
) {
147 for (const auto *D
: Decls
)
148 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
151 void HandleTemplateSpecializations(const FunctionTemplateDecl
&FTD
,
152 MangledSymbols
&Symbols
, int RDO
) {
153 for (const auto *D
: FTD
.specializations())
154 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
157 void HandleTemplateSpecializations(const ClassTemplateDecl
&CTD
,
158 MangledSymbols
&Symbols
, int RDO
) {
159 for (const auto *D
: CTD
.specializations())
160 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
163 bool HandleNamedDecl(const NamedDecl
*ND
, MangledSymbols
&Symbols
, int RDO
) {
167 switch (ND
->getKind()) {
170 case Decl::Kind::Namespace
:
171 HandleDecls(cast
<NamespaceDecl
>(ND
)->decls(), Symbols
, RDO
);
173 case Decl::Kind::CXXRecord
:
174 HandleDecls(cast
<CXXRecordDecl
>(ND
)->decls(), Symbols
, RDO
);
176 case Decl::Kind::ClassTemplateSpecialization
:
177 HandleDecls(cast
<ClassTemplateSpecializationDecl
>(ND
)->decls(), Symbols
,
180 case Decl::Kind::ClassTemplate
:
181 HandleTemplateSpecializations(*cast
<ClassTemplateDecl
>(ND
), Symbols
, RDO
);
183 case Decl::Kind::FunctionTemplate
:
184 HandleTemplateSpecializations(*cast
<FunctionTemplateDecl
>(ND
), Symbols
,
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
:
210 case Decl::Kind::Var
: {
211 // Bail on any VarDecl that either has no named symbol.
212 if (!ND
->getIdentifier())
214 const auto *VD
= cast
<VarDecl
>(ND
);
215 // Bail on any VarDecl that is a dependent or templated type.
216 if (VD
->isTemplated() || VD
->getType()->isDependentType())
218 if (WriteNamedDecl(ND
, Symbols
, RDO
))
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
))
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(diag::err_asm_invalid_type_in_input
)
235 << "Expected a function or function template decl.";
240 InterfaceStubFunctionsConsumer(CompilerInstance
&Instance
, StringRef InFile
,
242 : Instance(Instance
), InFile(InFile
), Format(Format
) {}
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
>(ND
))
248 if (FD
->isLateTemplateParsed()) {
249 LateParsedDecls
.insert(FD
);
253 if (const auto *VD
= dyn_cast
<ValueDecl
>(ND
)) {
254 ValueDecls
.insert(VD
);
258 NamedDecls
.insert(ND
);
262 std::set
<const NamedDecl
*> LateParsedDecls
;
263 std::set
<NamedDecl
*> NamedDecls
;
264 std::set
<const ValueDecl
*> ValueDecls
;
267 v
.TraverseDecl(context
.getTranslationUnitDecl());
269 MangledSymbols Symbols
;
270 auto OS
= Instance
.createDefaultOutputFile(/*Binary=*/false, InFile
, "ifs");
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(cast
<FunctionDecl
>(FD
))->second
;
279 S
.LateTemplateParser(S
.OpaqueParser
, LPT
);
280 HandleNamedDecl(FD
, Symbols
, (FromTU
| IsLate
));
284 for (const NamedDecl
*ND
: v
.ValueDecls
)
285 HandleNamedDecl(ND
, Symbols
, FromTU
);
286 for (const NamedDecl
*ND
: v
.NamedDecls
)
287 HandleNamedDecl(ND
, Symbols
, FromTU
);
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";
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
303 : (Symbol
.ParentName
+ "."))
304 << Name
<< "\", Type: ";
305 switch (Symbol
.Type
) {
308 "clang -emit-interface-stubs: Unexpected symbol type.");
309 case llvm::ELF::STT_NOTYPE
:
312 case llvm::ELF::STT_OBJECT
: {
313 auto VD
= cast
<ValueDecl
>(E
.first
)->getType();
314 OS
<< "Object, Size: "
315 << context
.getTypeSizeInChars(VD
).getQuantity();
318 case llvm::ELF::STT_FUNC
:
322 if (Symbol
.Binding
== llvm::ELF::STB_WEAK
)
323 OS
<< ", Weak: true";
331 assert(Format
== "ifs-v1" && "Unexpected IFS Format.");
332 writeIfsV1(Instance
.getTarget().getTriple(), Symbols
, context
, Format
, *OS
);
337 std::unique_ptr
<ASTConsumer
>
338 GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance
&CI
,
340 return std::make_unique
<InterfaceStubFunctionsConsumer
>(CI
, InFile
, "ifs-v1");