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
), Names(Names
) {}
38 using MangledSymbols
= std::map
<const NamedDecl
*, MangledSymbol
>;
40 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 auto isVisible
= [](const NamedDecl
*ND
) -> bool {
49 return ND
->getVisibility() == DefaultVisibility
;
52 auto ignoreDecl
= [this, isVisible
](const NamedDecl
*ND
) -> bool {
56 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(ND
)) {
57 if (const auto *Parent
= VD
->getParentFunctionOrMethod())
58 if (isa
<BlockDecl
>(Parent
) || isa
<CXXMethodDecl
>(Parent
))
61 if ((VD
->getStorageClass() == StorageClass::SC_Extern
) ||
62 (VD
->getStorageClass() == StorageClass::SC_Static
&&
63 VD
->getParentFunctionOrMethod() == nullptr))
67 if (const FunctionDecl
*FD
= dyn_cast
<FunctionDecl
>(ND
)) {
68 if (FD
->isInlined() && !isa
<CXXMethodDecl
>(FD
) &&
69 !Instance
.getLangOpts().GNUInline
)
71 if (const CXXMethodDecl
*MD
= dyn_cast
<CXXMethodDecl
>(FD
)) {
72 if (const auto *RC
= dyn_cast
<CXXRecordDecl
>(MD
->getParent()))
73 if (isa
<ClassTemplateDecl
>(RC
->getParent()) || !isVisible(RC
))
75 if (MD
->isDependentContext() || !MD
->hasBody())
78 if (FD
->getStorageClass() == StorageClass::SC_Static
)
84 auto getParentFunctionDecl
= [](const NamedDecl
*ND
) -> const NamedDecl
* {
85 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(ND
))
87 dyn_cast_or_null
<FunctionDecl
>(VD
->getParentFunctionOrMethod()))
92 auto getMangledNames
= [](const NamedDecl
*ND
) -> std::vector
<std::string
> {
95 ASTNameGenerator
NameGen(ND
->getASTContext());
96 std::vector
<std::string
> MangledNames
= NameGen
.getAllManglings(ND
);
97 if (isa
<CXXConstructorDecl
>(ND
) || isa
<CXXDestructorDecl
>(ND
))
99 #ifdef EXPENSIVE_CHECKS
100 assert(MangledNames
.size() <= 1 && "Expected only one name mangling.");
102 return {NameGen
.getName(ND
)};
107 if (Symbols
.find(ND
) != Symbols
.end())
109 // - Currently have not figured out how to produce the names for FieldDecls.
110 // - Do not want to produce symbols for function paremeters.
111 if (isa
<FieldDecl
>(ND
) || isa
<ParmVarDecl
>(ND
))
114 const NamedDecl
*ParentDecl
= getParentFunctionDecl(ND
);
115 if ((ParentDecl
&& ignoreDecl(ParentDecl
)) || ignoreDecl(ND
))
119 Instance
.getDiagnostics().Report(diag::err_asm_invalid_type_in_input
)
120 << "Generating Interface Stubs is not supported with "
121 "delayed template parsing.";
123 if (const auto *FD
= dyn_cast
<FunctionDecl
>(ND
))
124 if (FD
->isDependentContext())
127 const bool IsWeak
= (ND
->hasAttr
<WeakAttr
>() ||
128 ND
->hasAttr
<WeakRefAttr
>() || ND
->isWeakImported());
130 Symbols
.insert(std::make_pair(
132 MangledSymbol(getMangledNames(ParentDecl
).front(),
134 isa
<VarDecl
>(ND
) ? llvm::ELF::STT_OBJECT
135 : llvm::ELF::STT_FUNC
,
137 IsWeak
? llvm::ELF::STB_WEAK
: llvm::ELF::STB_GLOBAL
,
138 getMangledNames(ND
))));
144 HandleDecls(const llvm::iterator_range
<DeclContext::decl_iterator
> &Decls
,
145 MangledSymbols
&Symbols
, int RDO
) {
146 for (const auto *D
: Decls
)
147 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
150 void HandleTemplateSpecializations(const FunctionTemplateDecl
&FTD
,
151 MangledSymbols
&Symbols
, int RDO
) {
152 for (const auto *D
: FTD
.specializations())
153 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
156 void HandleTemplateSpecializations(const ClassTemplateDecl
&CTD
,
157 MangledSymbols
&Symbols
, int RDO
) {
158 for (const auto *D
: CTD
.specializations())
159 HandleNamedDecl(dyn_cast
<NamedDecl
>(D
), Symbols
, RDO
);
162 bool HandleNamedDecl(const NamedDecl
*ND
, MangledSymbols
&Symbols
, int RDO
) {
166 switch (ND
->getKind()) {
169 case Decl::Kind::Namespace
:
170 HandleDecls(cast
<NamespaceDecl
>(ND
)->decls(), Symbols
, RDO
);
172 case Decl::Kind::CXXRecord
:
173 HandleDecls(cast
<CXXRecordDecl
>(ND
)->decls(), Symbols
, RDO
);
175 case Decl::Kind::ClassTemplateSpecialization
:
176 HandleDecls(cast
<ClassTemplateSpecializationDecl
>(ND
)->decls(), Symbols
,
179 case Decl::Kind::ClassTemplate
:
180 HandleTemplateSpecializations(*cast
<ClassTemplateDecl
>(ND
), Symbols
, RDO
);
182 case Decl::Kind::FunctionTemplate
:
183 HandleTemplateSpecializations(*cast
<FunctionTemplateDecl
>(ND
), Symbols
,
186 case Decl::Kind::Record
:
187 case Decl::Kind::Typedef
:
188 case Decl::Kind::Enum
:
189 case Decl::Kind::EnumConstant
:
190 case Decl::Kind::TemplateTypeParm
:
191 case Decl::Kind::NonTypeTemplateParm
:
192 case Decl::Kind::CXXConversion
:
193 case Decl::Kind::UnresolvedUsingValue
:
194 case Decl::Kind::Using
:
195 case Decl::Kind::UsingShadow
:
196 case Decl::Kind::TypeAliasTemplate
:
197 case Decl::Kind::TypeAlias
:
198 case Decl::Kind::VarTemplate
:
199 case Decl::Kind::VarTemplateSpecialization
:
200 case Decl::Kind::UsingDirective
:
201 case Decl::Kind::TemplateTemplateParm
:
202 case Decl::Kind::ClassTemplatePartialSpecialization
:
203 case Decl::Kind::IndirectField
:
204 case Decl::Kind::ConstructorUsingShadow
:
205 case Decl::Kind::CXXDeductionGuide
:
206 case Decl::Kind::NamespaceAlias
:
207 case Decl::Kind::UnresolvedUsingTypename
:
209 case Decl::Kind::Var
: {
210 // Bail on any VarDecl that either has no named symbol.
211 if (!ND
->getIdentifier())
213 const auto *VD
= cast
<VarDecl
>(ND
);
214 // Bail on any VarDecl that is a dependent or templated type.
215 if (VD
->isTemplated() || VD
->getType()->isDependentType())
217 if (WriteNamedDecl(ND
, Symbols
, RDO
))
221 case Decl::Kind::ParmVar
:
222 case Decl::Kind::CXXMethod
:
223 case Decl::Kind::CXXConstructor
:
224 case Decl::Kind::CXXDestructor
:
225 case Decl::Kind::Function
:
226 case Decl::Kind::Field
:
227 if (WriteNamedDecl(ND
, Symbols
, RDO
))
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 Instance
.getDiagnostics().Report(diag::err_asm_invalid_type_in_input
)
234 << "Expected a function or function template decl.";
239 InterfaceStubFunctionsConsumer(CompilerInstance
&Instance
, StringRef InFile
,
241 : Instance(Instance
), InFile(InFile
), Format(Format
) {}
243 void HandleTranslationUnit(ASTContext
&context
) override
{
244 struct Visitor
: public RecursiveASTVisitor
<Visitor
> {
245 bool VisitNamedDecl(NamedDecl
*ND
) {
246 if (const auto *FD
= dyn_cast
<FunctionDecl
>(ND
))
247 if (FD
->isLateTemplateParsed()) {
248 LateParsedDecls
.insert(FD
);
252 if (const auto *VD
= dyn_cast
<ValueDecl
>(ND
)) {
253 ValueDecls
.insert(VD
);
257 NamedDecls
.insert(ND
);
261 std::set
<const NamedDecl
*> LateParsedDecls
;
262 std::set
<NamedDecl
*> NamedDecls
;
263 std::set
<const ValueDecl
*> ValueDecls
;
266 v
.TraverseDecl(context
.getTranslationUnitDecl());
268 MangledSymbols Symbols
;
269 auto OS
= Instance
.createDefaultOutputFile(/*Binary=*/false, InFile
, "ifs");
273 if (Instance
.getLangOpts().DelayedTemplateParsing
) {
274 clang::Sema
&S
= Instance
.getSema();
275 for (const auto *FD
: v
.LateParsedDecls
) {
276 clang::LateParsedTemplate
&LPT
=
277 *S
.LateParsedTemplateMap
.find(cast
<FunctionDecl
>(FD
))->second
;
278 S
.LateTemplateParser(S
.OpaqueParser
, LPT
);
279 HandleNamedDecl(FD
, Symbols
, (FromTU
| IsLate
));
283 for (const NamedDecl
*ND
: v
.ValueDecls
)
284 HandleNamedDecl(ND
, Symbols
, FromTU
);
285 for (const NamedDecl
*ND
: v
.NamedDecls
)
286 HandleNamedDecl(ND
, Symbols
, FromTU
);
288 auto writeIfsV1
= [this](const llvm::Triple
&T
,
289 const MangledSymbols
&Symbols
,
290 const ASTContext
&context
, StringRef Format
,
291 raw_ostream
&OS
) -> void {
292 OS
<< "--- !" << Format
<< "\n";
293 OS
<< "IfsVersion: 3.0\n";
294 OS
<< "Target: " << T
.str() << "\n";
296 for (const auto &E
: Symbols
) {
297 const MangledSymbol
&Symbol
= E
.second
;
298 for (auto Name
: Symbol
.Names
) {
299 OS
<< " - { Name: \""
300 << (Symbol
.ParentName
.empty() || Instance
.getLangOpts().CPlusPlus
302 : (Symbol
.ParentName
+ "."))
303 << Name
<< "\", Type: ";
304 switch (Symbol
.Type
) {
307 "clang -emit-interface-stubs: Unexpected symbol type.");
308 case llvm::ELF::STT_NOTYPE
:
311 case llvm::ELF::STT_OBJECT
: {
312 auto VD
= cast
<ValueDecl
>(E
.first
)->getType();
313 OS
<< "Object, Size: "
314 << context
.getTypeSizeInChars(VD
).getQuantity();
317 case llvm::ELF::STT_FUNC
:
321 if (Symbol
.Binding
== llvm::ELF::STB_WEAK
)
322 OS
<< ", Weak: true";
330 assert(Format
== "ifs-v1" && "Unexpected IFS Format.");
331 writeIfsV1(Instance
.getTarget().getTriple(), Symbols
, context
, Format
, *OS
);
336 std::unique_ptr
<ASTConsumer
>
337 GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance
&CI
,
339 return std::make_unique
<InterfaceStubFunctionsConsumer
>(CI
, InFile
, "ifs-v1");