342 lines · cpp
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-exception6//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 17using namespace clang;18 19namespace {20class 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 on44 // the command line (or specified as default) and the decl does not have45 // __attribute__((visibility("hidden"))) set or when the command line46 // argument is set to hidden but the decl explicitly has47 // __attribute__((visibility ("default"))) set. We do this so that the user48 // 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>(ND)) {58 if (const auto *Parent = VD->getParentFunctionOrMethod())59 if (isa<BlockDecl>(Parent) || isa<CXXMethodDecl>(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>(ND)) {69 if (FD->isInlined() && !isa<CXXMethodDecl>(FD) &&70 !Instance.getLangOpts().GNUInline)71 return true;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))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>(ND))87 if (const auto *FD =88 dyn_cast_or_null<FunctionDecl>(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(ND);98 if (isa<CXXConstructorDecl>(ND) || isa<CXXDestructorDecl>(ND))99 return MangledNames;100#ifdef EXPENSIVE_CHECKS101 assert(MangledNames.size() <= 1 && "Expected only one name mangling.");102#endif103 return {NameGen.getName(ND)};104 };105 106 if (!(RDO & FromTU))107 return true;108 if (Symbols.find(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>(ND) || isa<ParmVarDecl>(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(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>(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(std::make_pair(132 ND,133 MangledSymbol(getMangledNames(ParentDecl).front(),134 // Type:135 isa<VarDecl>(ND) ? llvm::ELF::STT_OBJECT136 : 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 void145 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);149 }150 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);155 }156 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);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(cast<NamespaceDecl>(ND)->decls(), Symbols, RDO);172 return true;173 case Decl::Kind::CXXRecord:174 HandleDecls(cast<CXXRecordDecl>(ND)->decls(), Symbols, RDO);175 return true;176 case Decl::Kind::ClassTemplateSpecialization:177 HandleDecls(cast<ClassTemplateSpecializationDecl>(ND)->decls(), Symbols,178 RDO);179 return true;180 case Decl::Kind::ClassTemplate:181 HandleTemplateSpecializations(*cast<ClassTemplateDecl>(ND), Symbols, RDO);182 return true;183 case Decl::Kind::FunctionTemplate:184 HandleTemplateSpecializations(*cast<FunctionTemplateDecl>(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>(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 to233 // 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.";236 return false;237 }238 239public: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>(ND))248 if (FD->isLateTemplateParsed()) {249 LateParsedDecls.insert(FD);250 return true;251 }252 253 if (const auto *VD = dyn_cast<ValueDecl>(ND)) {254 ValueDecls.insert(VD);255 return true;256 }257 258 NamedDecls.insert(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(context.getTranslationUnitDecl());268 269 MangledSymbols Symbols;270 auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "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(cast<FunctionDecl>(FD))->second;279 S.LateTemplateParser(S.OpaqueParser, LPT);280 HandleNamedDecl(FD, Symbols, (FromTU | IsLate));281 }282 }283 284 for (const NamedDecl *ND : v.ValueDecls)285 HandleNamedDecl(ND, Symbols, FromTU);286 for (const NamedDecl *ND : v.NamedDecls)287 HandleNamedDecl(ND, Symbols, 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().CPlusPlus302 ? ""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>(E.first)->getType();314 OS << "Object, Size: "315 << context.getTypeSizeInChars(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} // namespace336 337std::unique_ptr<ASTConsumer>338GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance &CI,339 StringRef InFile) {340 return std::make_unique<InterfaceStubFunctionsConsumer>(CI, InFile, "ifs-v1");341}342