Puyan Lotfi | 68f29da | 2019-06-20 16:59:48 +0000 | [diff] [blame^] | 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 | class InterfaceStubFunctionsConsumer : public ASTConsumer { |
| 19 | CompilerInstance &Instance; |
| 20 | StringRef InFile; |
| 21 | StringRef Format; |
| 22 | std::set<std::string> ParsedTemplates; |
| 23 | |
| 24 | enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; |
| 25 | struct MangledSymbol { |
| 26 | std::string ParentName; |
| 27 | uint8_t Type; |
| 28 | uint8_t Binding; |
| 29 | std::vector<std::string> Names; |
| 30 | MangledSymbol() = delete; |
| 31 | |
| 32 | MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, |
| 33 | std::vector<std::string> Names) |
| 34 | : ParentName(ParentName), Type(Type), Binding(Binding), Names(Names) {} |
| 35 | }; |
| 36 | using MangledSymbols = std::map<const NamedDecl *, MangledSymbol>; |
| 37 | |
| 38 | bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { |
| 39 | // Here we filter out anything that's not set to DefaultVisibility. |
| 40 | // DefaultVisibility is set on a decl when -fvisibility is not specified on |
| 41 | // the command line (or specified as default) and the decl does not have |
| 42 | // __attribute__((visibility("hidden"))) set or when the command line |
| 43 | // argument is set to hidden but the decl explicitly has |
| 44 | // __attribute__((visibility ("default"))) set. We do this so that the user |
| 45 | // can have fine grain control of what they want to expose in the stub. |
| 46 | auto isVisible = [](const NamedDecl *ND) -> bool { |
| 47 | return ND->getVisibility() == DefaultVisibility; |
| 48 | }; |
| 49 | |
| 50 | auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { |
| 51 | if (!isVisible(ND)) |
| 52 | return true; |
| 53 | |
| 54 | if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) |
| 55 | if ((VD->getStorageClass() == StorageClass::SC_Extern) || |
| 56 | (VD->getStorageClass() == StorageClass::SC_Static && |
| 57 | VD->getParentFunctionOrMethod() == nullptr)) |
| 58 | return true; |
| 59 | |
| 60 | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { |
| 61 | if (FD->isInlined() && !isa<CXXMethodDecl>(FD) && |
| 62 | !Instance.getLangOpts().GNUInline) |
| 63 | return true; |
| 64 | if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { |
| 65 | if (const auto *RC = dyn_cast<CXXRecordDecl>(MD->getParent())) |
| 66 | if (isa<ClassTemplateDecl>(RC->getParent()) || !isVisible(RC)) |
| 67 | return true; |
| 68 | if (MD->isDependentContext() || !MD->hasBody()) |
| 69 | return true; |
| 70 | } |
| 71 | if (FD->getStorageClass() == StorageClass::SC_Static) |
| 72 | return true; |
| 73 | } |
| 74 | return false; |
| 75 | }; |
| 76 | |
| 77 | auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { |
| 78 | if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) |
| 79 | if (const auto *FD = |
| 80 | dyn_cast_or_null<FunctionDecl>(VD->getParentFunctionOrMethod())) |
| 81 | return FD; |
| 82 | return nullptr; |
| 83 | }; |
| 84 | |
| 85 | auto getMangledNames = [](const NamedDecl *ND) -> std::vector<std::string> { |
| 86 | if (!ND) |
| 87 | return {""}; |
| 88 | ASTNameGenerator NameGen(ND->getASTContext()); |
| 89 | std::vector<std::string> MangledNames = NameGen.getAllManglings(ND); |
| 90 | if (isa<CXXConstructorDecl>(ND) || isa<CXXDestructorDecl>(ND)) |
| 91 | return MangledNames; |
| 92 | #ifdef EXPENSIVE_CHECKS |
| 93 | assert(MangledNames.size() <= 1 && "Expected only one name mangling."); |
| 94 | #endif |
| 95 | return {NameGen.getName(ND)}; |
| 96 | }; |
| 97 | |
| 98 | if (!(RDO & FromTU)) |
| 99 | return true; |
| 100 | if (Symbols.find(ND) != Symbols.end()) |
| 101 | return true; |
| 102 | // - Currently have not figured out how to produce the names for FieldDecls. |
| 103 | // - Do not want to produce symbols for function paremeters. |
| 104 | if (isa<FieldDecl>(ND) || isa<ParmVarDecl>(ND)) |
| 105 | return true; |
| 106 | |
| 107 | const NamedDecl *ParentDecl = getParentFunctionDecl(ND); |
| 108 | if ((ParentDecl && ignoreDecl(ParentDecl)) || ignoreDecl(ND)) |
| 109 | return true; |
| 110 | |
| 111 | if (RDO & IsLate) { |
| 112 | Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) |
| 113 | << "Generating Interface Stubs is not supported with " |
| 114 | "delayed template parsing."; |
| 115 | } else { |
| 116 | if (const auto *FD = dyn_cast<FunctionDecl>(ND)) |
| 117 | if (FD->isDependentContext()) |
| 118 | return true; |
| 119 | |
| 120 | const bool IsWeak = (ND->hasAttr<WeakAttr>() || |
| 121 | ND->hasAttr<WeakRefAttr>() || ND->isWeakImported()); |
| 122 | |
| 123 | Symbols.insert(std::make_pair( |
| 124 | ND, |
| 125 | MangledSymbol(getMangledNames(ParentDecl).front(), |
| 126 | // Type: |
| 127 | isa<VarDecl>(ND) ? llvm::ELF::STT_OBJECT |
| 128 | : llvm::ELF::STT_FUNC, |
| 129 | // Binding: |
| 130 | IsWeak ? llvm::ELF::STB_WEAK : llvm::ELF::STB_GLOBAL, |
| 131 | getMangledNames(ND)))); |
| 132 | } |
| 133 | return true; |
| 134 | } |
| 135 | |
| 136 | void |
| 137 | HandleDecls(const llvm::iterator_range<DeclContext::decl_iterator> &Decls, |
| 138 | MangledSymbols &Symbols, int RDO) { |
| 139 | for (const auto *D : Decls) |
| 140 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
| 141 | } |
| 142 | |
| 143 | void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, |
| 144 | MangledSymbols &Symbols, int RDO) { |
| 145 | for (const auto *D : FTD.specializations()) |
| 146 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
| 147 | } |
| 148 | |
| 149 | void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, |
| 150 | MangledSymbols &Symbols, int RDO) { |
| 151 | for (const auto *D : CTD.specializations()) |
| 152 | HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); |
| 153 | } |
| 154 | |
| 155 | bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { |
| 156 | if (!ND) |
| 157 | return false; |
| 158 | |
| 159 | switch (ND->getKind()) { |
| 160 | default: |
| 161 | break; |
| 162 | case Decl::Kind::Namespace: |
| 163 | HandleDecls(cast<NamespaceDecl>(ND)->decls(), Symbols, RDO); |
| 164 | return true; |
| 165 | case Decl::Kind::CXXRecord: |
| 166 | HandleDecls(cast<CXXRecordDecl>(ND)->decls(), Symbols, RDO); |
| 167 | return true; |
| 168 | case Decl::Kind::ClassTemplateSpecialization: |
| 169 | HandleDecls(cast<ClassTemplateSpecializationDecl>(ND)->decls(), Symbols, |
| 170 | RDO); |
| 171 | return true; |
| 172 | case Decl::Kind::ClassTemplate: |
| 173 | HandleTemplateSpecializations(*cast<ClassTemplateDecl>(ND), Symbols, RDO); |
| 174 | return true; |
| 175 | case Decl::Kind::FunctionTemplate: |
| 176 | HandleTemplateSpecializations(*cast<FunctionTemplateDecl>(ND), Symbols, |
| 177 | RDO); |
| 178 | return true; |
| 179 | case Decl::Kind::TemplateTypeParm: |
| 180 | return true; |
| 181 | case Decl::Kind::Var: |
| 182 | case Decl::Kind::ParmVar: |
| 183 | case Decl::Kind::CXXMethod: |
| 184 | case Decl::Kind::CXXConstructor: |
| 185 | case Decl::Kind::CXXDestructor: |
| 186 | case Decl::Kind::Function: |
| 187 | case Decl::Kind::Field: |
| 188 | if (WriteNamedDecl(ND, Symbols, RDO)) |
| 189 | return true; |
| 190 | } |
| 191 | |
| 192 | // While interface stubs are in the development stage, it's probably best to |
| 193 | // catch anything that's not a VarDecl or Template/FunctionDecl. |
| 194 | Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) |
| 195 | << "Expected a function or function template decl."; |
| 196 | return false; |
| 197 | } |
| 198 | |
| 199 | public: |
| 200 | InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, |
| 201 | StringRef Format) |
| 202 | : Instance(Instance), InFile(InFile), Format(Format) {} |
| 203 | |
| 204 | void HandleTranslationUnit(ASTContext &context) override { |
| 205 | struct Visitor : public RecursiveASTVisitor<Visitor> { |
| 206 | bool VisitNamedDecl(NamedDecl *ND) { |
| 207 | if (const auto *FD = dyn_cast<FunctionDecl>(ND)) |
| 208 | if (FD->isLateTemplateParsed()) { |
| 209 | LateParsedDecls.insert(FD); |
| 210 | return true; |
| 211 | } |
| 212 | |
| 213 | if (const auto *VD = dyn_cast<ValueDecl>(ND)) { |
| 214 | ValueDecls.insert(VD); |
| 215 | return true; |
| 216 | } |
| 217 | |
| 218 | NamedDecls.insert(ND); |
| 219 | return true; |
| 220 | } |
| 221 | |
| 222 | std::set<const NamedDecl *> LateParsedDecls; |
| 223 | std::set<NamedDecl *> NamedDecls; |
| 224 | std::set<const ValueDecl *> ValueDecls; |
| 225 | } v; |
| 226 | |
| 227 | v.TraverseDecl(context.getTranslationUnitDecl()); |
| 228 | |
| 229 | MangledSymbols Symbols; |
| 230 | auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); |
| 231 | if (!OS) |
| 232 | return; |
| 233 | |
| 234 | if (Instance.getLangOpts().DelayedTemplateParsing) { |
| 235 | clang::Sema &S = Instance.getSema(); |
| 236 | for (const auto *FD : v.LateParsedDecls) { |
| 237 | clang::LateParsedTemplate &LPT = |
| 238 | *S.LateParsedTemplateMap.find(cast<FunctionDecl>(FD))->second; |
| 239 | S.LateTemplateParser(S.OpaqueParser, LPT); |
| 240 | HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | for (const NamedDecl *ND : v.ValueDecls) |
| 245 | HandleNamedDecl(ND, Symbols, FromTU); |
| 246 | for (const NamedDecl *ND : v.NamedDecls) |
| 247 | HandleNamedDecl(ND, Symbols, FromTU); |
| 248 | |
| 249 | auto writeIfoYaml = [this](const llvm::Triple &T, |
| 250 | const MangledSymbols &Symbols, |
| 251 | const ASTContext &context, StringRef Format, |
| 252 | raw_ostream &OS) -> void { |
| 253 | OS << "--- !" << Format << "\n"; |
| 254 | OS << "FileHeader:\n"; |
| 255 | OS << " Class: ELFCLASS"; |
| 256 | OS << (T.isArch64Bit() ? "64" : "32"); |
| 257 | OS << "\n"; |
| 258 | OS << " Data: ELFDATA2"; |
| 259 | OS << (T.isLittleEndian() ? "LSB" : "MSB"); |
| 260 | OS << "\n"; |
| 261 | OS << " Type: ET_REL\n"; |
| 262 | OS << " Machine: " |
| 263 | << llvm::StringSwitch<llvm::StringRef>(T.getArchName()) |
| 264 | .Case("x86_64", "EM_X86_64") |
| 265 | .Case("i386", "EM_386") |
| 266 | .Case("i686", "EM_386") |
| 267 | .Case("aarch64", "EM_AARCH64") |
| 268 | .Case("amdgcn", "EM_AMDGPU") |
| 269 | .Case("r600", "EM_AMDGPU") |
| 270 | .Case("arm", "EM_ARM") |
| 271 | .Case("thumb", "EM_ARM") |
| 272 | .Case("avr", "EM_AVR") |
| 273 | .Case("mips", "EM_MIPS") |
| 274 | .Case("mipsel", "EM_MIPS") |
| 275 | .Case("mips64", "EM_MIPS") |
| 276 | .Case("mips64el", "EM_MIPS") |
| 277 | .Case("msp430", "EM_MSP430") |
| 278 | .Case("ppc", "EM_PPC") |
| 279 | .Case("ppc64", "EM_PPC64") |
| 280 | .Case("ppc64le", "EM_PPC64") |
| 281 | .Case("x86", T.isOSIAMCU() ? "EM_IAMCU" : "EM_386") |
| 282 | .Case("x86_64", "EM_X86_64") |
| 283 | .Default("EM_NONE") |
| 284 | << "\nSymbols:\n"; |
| 285 | for (const auto &E : Symbols) { |
| 286 | const MangledSymbol &Symbol = E.second; |
| 287 | for (auto Name : Symbol.Names) { |
| 288 | OS << " - Name: " |
| 289 | << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus |
| 290 | ? "" |
| 291 | : (Symbol.ParentName + ".")) |
| 292 | << Name << "\n" |
| 293 | << " Type: STT_"; |
| 294 | switch (Symbol.Type) { |
| 295 | default: |
| 296 | case llvm::ELF::STT_NOTYPE: |
| 297 | OS << "NOTYPE"; |
| 298 | break; |
| 299 | case llvm::ELF::STT_OBJECT: |
| 300 | OS << "OBJECT"; |
| 301 | break; |
| 302 | case llvm::ELF::STT_FUNC: |
| 303 | OS << "FUNC"; |
| 304 | break; |
| 305 | } |
| 306 | OS << "\n Binding: STB_" |
| 307 | << ((Symbol.Binding == llvm::ELF::STB_WEAK) ? "WEAK" : "GLOBAL") |
| 308 | << "\n"; |
| 309 | } |
| 310 | } |
| 311 | OS << "...\n"; |
| 312 | OS.flush(); |
| 313 | }; |
| 314 | |
| 315 | auto writeIfoElfAbiYaml = |
| 316 | [this](const llvm::Triple &T, const MangledSymbols &Symbols, |
| 317 | const ASTContext &context, StringRef Format, |
| 318 | raw_ostream &OS) -> void { |
| 319 | OS << "--- !" << Format << "\n"; |
| 320 | OS << "TbeVersion: 1.0\n"; |
| 321 | OS << "Arch: " << T.getArchName() << "\n"; |
| 322 | OS << "Symbols:\n"; |
| 323 | for (const auto &E : Symbols) { |
| 324 | const MangledSymbol &Symbol = E.second; |
| 325 | for (auto Name : Symbol.Names) { |
| 326 | OS << " " |
| 327 | << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus |
| 328 | ? "" |
| 329 | : (Symbol.ParentName + ".")) |
| 330 | << Name << ": { Type: "; |
| 331 | switch (Symbol.Type) { |
| 332 | default: |
| 333 | llvm_unreachable( |
| 334 | "clang -emit-iterface-stubs: Unexpected symbol type."); |
| 335 | case llvm::ELF::STT_NOTYPE: |
| 336 | OS << "NoType"; |
| 337 | break; |
| 338 | case llvm::ELF::STT_OBJECT: { |
| 339 | auto VD = cast<ValueDecl>(E.first)->getType(); |
| 340 | OS << "Object, Size: " |
| 341 | << context.getTypeSizeInChars(VD).getQuantity(); |
| 342 | break; |
| 343 | } |
| 344 | case llvm::ELF::STT_FUNC: |
| 345 | OS << "Func"; |
| 346 | break; |
| 347 | } |
| 348 | if (Symbol.Binding == llvm::ELF::STB_WEAK) |
| 349 | OS << ", Weak: true"; |
| 350 | OS << " }\n"; |
| 351 | } |
| 352 | } |
| 353 | OS << "...\n"; |
| 354 | OS.flush(); |
| 355 | }; |
| 356 | |
| 357 | if (Format == "experimental-yaml-elf-v1") |
| 358 | writeIfoYaml(Instance.getTarget().getTriple(), Symbols, context, Format, |
| 359 | *OS); |
| 360 | else |
| 361 | writeIfoElfAbiYaml(Instance.getTarget().getTriple(), Symbols, context, |
| 362 | Format, *OS); |
| 363 | } |
| 364 | }; |
| 365 | |
| 366 | std::unique_ptr<ASTConsumer> |
| 367 | GenerateInterfaceYAMLExpV1Action::CreateASTConsumer(CompilerInstance &CI, |
| 368 | StringRef InFile) { |
| 369 | return llvm::make_unique<InterfaceStubFunctionsConsumer>( |
| 370 | CI, InFile, "experimental-yaml-elf-v1"); |
| 371 | } |
| 372 | |
| 373 | std::unique_ptr<ASTConsumer> |
| 374 | GenerateInterfaceTBEExpV1Action::CreateASTConsumer(CompilerInstance &CI, |
| 375 | StringRef InFile) { |
| 376 | return llvm::make_unique<InterfaceStubFunctionsConsumer>( |
| 377 | CI, InFile, "experimental-tapi-elf-v1"); |
| 378 | } |