From d9cb70af7cf60498423d776efcca80e30a3e233a Mon Sep 17 00:00:00 2001 From: Puyan Lotfi Date: Mon, 17 Jun 2019 22:46:54 +0000 Subject: [PATCH] [clang-ifs] Clang Interface Stubs, first version. Clang interface stubs (previously referred to as clang-ifsos) is a new frontend action in clang that allows the generation of stub files that contain mangled name info that can be used to produce a stub library. These stub libraries can be useful for breaking up build dependencies and controlling access to a library's internal symbols. Generation of these stubs can be invoked by: clang -fvisibility= -emit-interface-stubs \ -interface-stub-version= Notice that -fvisibility (along with use of visibility attributes) can be used to control what symbols get generated. Currently the interface format is experimental but there are a wide range of possibilities here. Differential Revision: https://reviews.llvm.org/D60974 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@363626 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../clang/Basic/DiagnosticFrontendKinds.td | 2 + include/clang/Driver/Options.td | 3 + include/clang/Driver/Types.def | 1 + include/clang/Frontend/FrontendActions.h | 20 + include/clang/Frontend/FrontendOptions.h | 4 + lib/Driver/Driver.cpp | 3 + lib/Driver/ToolChains/Clang.cpp | 19 + lib/Frontend/CMakeLists.txt | 2 + lib/Frontend/CompilerInvocation.cpp | 21 + .../InterfaceStubFunctionsConsumer.cpp | 379 ++++++++++++++++++ .../ExecuteCompilerInvocation.cpp | 4 + test/InterfaceStubs/bad-format.cpp | 7 + .../class-template-specialization.cpp | 42 ++ test/InterfaceStubs/externstatic.c | 29 ++ .../function-template-specialization.cpp | 41 ++ .../hidden-class-inheritance.cpp | 153 +++++++ test/InterfaceStubs/inline.c | 67 ++++ test/InterfaceStubs/inline.h | 4 + test/InterfaceStubs/object.cpp | 13 + .../template-namespace-function.cpp | 31 ++ test/InterfaceStubs/virtual.cpp | 41 ++ test/InterfaceStubs/visibility.cpp | 45 +++ test/InterfaceStubs/weak.cpp | 27 ++ 23 files changed, 958 insertions(+) create mode 100644 lib/Frontend/InterfaceStubFunctionsConsumer.cpp create mode 100644 test/InterfaceStubs/bad-format.cpp create mode 100644 test/InterfaceStubs/class-template-specialization.cpp create mode 100644 test/InterfaceStubs/externstatic.c create mode 100644 test/InterfaceStubs/function-template-specialization.cpp create mode 100644 test/InterfaceStubs/hidden-class-inheritance.cpp create mode 100644 test/InterfaceStubs/inline.c create mode 100644 test/InterfaceStubs/inline.h create mode 100644 test/InterfaceStubs/object.cpp create mode 100644 test/InterfaceStubs/template-namespace-function.cpp create mode 100644 test/InterfaceStubs/virtual.cpp create mode 100644 test/InterfaceStubs/visibility.cpp create mode 100644 test/InterfaceStubs/weak.cpp diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index e2d98b421b..7a990164b0 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -220,6 +220,8 @@ def err_module_header_file_not_found : def err_module_header_file_invalid : Error<"unexpected module header file input '%0'">, DefaultFatal; +def err_interface_stubs : Error<"clang-ifs (-emit-iterface-stubs): %0">; + def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 628fa4435d..bd51dbf953 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -623,6 +623,9 @@ def emit_ast : Flag<["-"], "emit-ast">, HelpText<"Emit Clang AST files for source inputs">; def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group, HelpText<"Use the LLVM representation for assembler and object files">; +def emit_iterface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group, + HelpText<"Generate Inteface Stub Files.">; +def iterface_stub_version_EQ : JoinedOrSeparate<["-"], "interface-stub-version=">, Flags<[CC1Option]>; def exported__symbols__list : Separate<["-"], "exported_symbols_list">; def e : JoinedOrSeparate<["-"], "e">, Group; def fPIC : Flag<["-"], "fPIC">, Group; diff --git a/include/clang/Driver/Types.def b/include/clang/Driver/Types.def index d2aaf58f15..b45789d4b3 100644 --- a/include/clang/Driver/Types.def +++ b/include/clang/Driver/Types.def @@ -88,6 +88,7 @@ TYPE("lto-bc", LTO_BC, INVALID, "o", "") // Misc. TYPE("ast", AST, INVALID, "ast", "u") +TYPE("ifs", IFS, INVALID, "ifs", "u") TYPE("pcm", ModuleFile, INVALID, "pcm", "u") TYPE("plist", Plist, INVALID, "plist", "") TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", "") diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index 846b26897c..6c7bc6046f 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -119,6 +119,26 @@ protected: bool hasASTFileSupport() const override { return false; } }; +class GenerateInterfaceStubAction : public ASTFrontendAction { +protected: + TranslationUnitKind getTranslationUnitKind() override { return TU_Module; } + + bool hasASTFileSupport() const override { return false; } +}; + +// Support different interface stub formats this way: +class GenerateInterfaceYAMLExpV1Action : public GenerateInterfaceStubAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; +}; + +class GenerateInterfaceTBEExpV1Action : public GenerateInterfaceStubAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; +}; + class GenerateModuleFromModuleMapAction : public GenerateModuleAction { private: bool BeginSourceFileAction(CompilerInstance &CI) override; diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 66fbe6ae6c..a0acb1f066 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -88,6 +88,10 @@ enum ActionKind { /// Generate pre-compiled header. GeneratePCH, + /// Generate Interface Stub Files. + GenerateInterfaceYAMLExpV1, + GenerateInterfaceTBEExpV1, + /// Only execute frontend initialization. InitOnly, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 3bda71597e..925ded71e1 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -285,6 +285,7 @@ phases::ID Driver::getFinalPhase(const DerivedArgList &DAL, (PhaseArg = DAL.getLastArg(options::OPT_rewrite_objc)) || (PhaseArg = DAL.getLastArg(options::OPT_rewrite_legacy_objc)) || (PhaseArg = DAL.getLastArg(options::OPT__migrate)) || + (PhaseArg = DAL.getLastArg(options::OPT_emit_iterface_stubs)) || (PhaseArg = DAL.getLastArg(options::OPT__analyze, options::OPT__analyze_auto)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) { @@ -3472,6 +3473,8 @@ Action *Driver::ConstructPhaseAction( return C.MakeAction(Input, types::TY_ModuleFile); if (Args.hasArg(options::OPT_verify_pch)) return C.MakeAction(Input, types::TY_Nothing); + if (Args.hasArg(options::OPT_emit_iterface_stubs)) + return C.MakeAction(Input, types::TY_IFS); return C.MakeAction(Input, types::TY_LLVM_BC); } case phases::Backend: { diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp index 9ec6f7d9a9..82d4da414a 100644 --- a/lib/Driver/ToolChains/Clang.cpp +++ b/lib/Driver/ToolChains/Clang.cpp @@ -3613,6 +3613,25 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } else if (JA.getType() == types::TY_LLVM_BC || JA.getType() == types::TY_LTO_BC) { CmdArgs.push_back("-emit-llvm-bc"); + } else if (JA.getType() == types::TY_IFS) { + StringRef StubFormat = + llvm::StringSwitch( + Args.hasArg(options::OPT_iterface_stub_version_EQ) + ? Args.getLastArgValue(options::OPT_iterface_stub_version_EQ) + : "") + .Case("experimental-yaml-elf-v1", "experimental-yaml-elf-v1") + .Case("experimental-tapi-elf-v1", "experimental-tapi-elf-v1") + .Default(""); + + if (StubFormat.empty()) + D.Diag(diag::err_drv_invalid_value) + << "Must specify a valid interface stub format type using " + << "-interface-stub-version="; + + CmdArgs.push_back("-emit-interface-stubs"); + CmdArgs.push_back( + Args.MakeArgString(Twine("-interface-stub-version=") + StubFormat)); } else if (JA.getType() == types::TY_PP_Asm) { CmdArgs.push_back("-S"); } else if (JA.getType() == types::TY_AST) { diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 3bd159537b..6598017cdd 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -45,6 +45,7 @@ add_clang_library(clangFrontend TextDiagnosticBuffer.cpp TextDiagnosticPrinter.cpp VerifyDiagnosticConsumer.cpp + InterfaceStubFunctionsConsumer.cpp DEPENDS ClangDriverOptions @@ -54,6 +55,7 @@ add_clang_library(clangFrontend clangAST clangBasic clangDriver + clangIndex clangEdit clangLex clangParse diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index fc49aa6f86..0ec8e11dab 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1681,6 +1681,25 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::GenerateHeaderModule; break; case OPT_emit_pch: Opts.ProgramAction = frontend::GeneratePCH; break; + case OPT_emit_iterface_stubs: { + llvm::Optional ProgramAction = + llvm::StringSwitch>( + Args.hasArg(OPT_iterface_stub_version_EQ) + ? Args.getLastArgValue(OPT_iterface_stub_version_EQ) + : "") + .Case("experimental-yaml-elf-v1", + frontend::GenerateInterfaceYAMLExpV1) + .Case("experimental-tapi-elf-v1", + frontend::GenerateInterfaceTBEExpV1) + .Default(llvm::None); + if (!ProgramAction) + Diags.Report(diag::err_drv_invalid_value) + << "Must specify a valid interface stub format type using " + << "-interface-stub-version="; + Opts.ProgramAction = *ProgramAction; + break; + } case OPT_init_only: Opts.ProgramAction = frontend::InitOnly; break; case OPT_fsyntax_only: @@ -3113,6 +3132,8 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { case frontend::GenerateModuleInterface: case frontend::GenerateHeaderModule: case frontend::GeneratePCH: + case frontend::GenerateInterfaceYAMLExpV1: + case frontend::GenerateInterfaceTBEExpV1: case frontend::ParseSyntaxOnly: case frontend::ModuleFileInfo: case frontend::VerifyPCH: diff --git a/lib/Frontend/InterfaceStubFunctionsConsumer.cpp b/lib/Frontend/InterfaceStubFunctionsConsumer.cpp new file mode 100644 index 0000000000..4a544963aa --- /dev/null +++ b/lib/Frontend/InterfaceStubFunctionsConsumer.cpp @@ -0,0 +1,379 @@ +//===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Index/CodegenNameGenerator.h" +#include "clang/Sema/TemplateInstCallback.h" +#include "llvm/BinaryFormat/ELF.h" + +using namespace clang; + +class InterfaceStubFunctionsConsumer : public ASTConsumer { + CompilerInstance &Instance; + StringRef InFile; + StringRef Format; + std::set ParsedTemplates; + + enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; + struct MangledSymbol { + std::string ParentName; + uint8_t Type; + uint8_t Binding; + std::vector Names; + MangledSymbol() = delete; + + MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, + std::vector Names) + : ParentName(ParentName), Type(Type), Binding(Binding), Names(Names) {} + }; + using MangledSymbols = std::map; + + bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + // Here we filter out anything that's not set to DefaultVisibility. + // DefaultVisibility is set on a decl when -fvisibility is not specified on + // the command line (or specified as default) and the decl does not have + // __attribute__((visibility("hidden"))) set or when the command line + // argument is set to hidden but the decl explicitly has + // __attribute__((visibility ("default"))) set. We do this so that the user + // can have fine grain control of what they want to expose in the stub. + auto isVisible = [](const NamedDecl *ND) -> bool { + return ND->getVisibility() == DefaultVisibility; + }; + + auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { + if (!isVisible(ND)) + return true; + + if (const VarDecl *VD = dyn_cast(ND)) + if ((VD->getStorageClass() == StorageClass::SC_Extern) || + (VD->getStorageClass() == StorageClass::SC_Static && + VD->getParentFunctionOrMethod() == nullptr)) + return true; + + if (const FunctionDecl *FD = dyn_cast(ND)) { + if (FD->isInlined() && !isa(FD) && + !Instance.getLangOpts().GNUInline) + return true; + if (const CXXMethodDecl *MD = dyn_cast(FD)) { + if (const auto *RC = dyn_cast(MD->getParent())) + if (isa(RC->getParent()) || !isVisible(RC)) + return true; + if (MD->isDependentContext() || !MD->hasBody()) + return true; + } + if (FD->getStorageClass() == StorageClass::SC_Static) + return true; + } + return false; + }; + + auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { + if (const VarDecl *VD = dyn_cast(ND)) + if (const auto *FD = + dyn_cast_or_null(VD->getParentFunctionOrMethod())) + return FD; + return nullptr; + }; + + auto getMangledNames = [](const NamedDecl *ND) -> std::vector { + if (!ND) + return {""}; + index::CodegenNameGenerator CGNameGen(ND->getASTContext()); + std::string MangledName = CGNameGen.getName(ND); + std::vector MangledNames = CGNameGen.getAllManglings(ND); + if (isa(ND) || isa(ND)) + return MangledNames; +#ifdef EXPENSIVE_CHECKS + assert(MangledNames.size() <= 1 && "Expected only one name mangling."); +#endif + return {MangledName}; + }; + + if (!(RDO & FromTU)) + return true; + if (Symbols.find(ND) != Symbols.end()) + return true; + // - Currently have not figured out how to produce the names for FieldDecls. + // - Do not want to produce symbols for function paremeters. + if (isa(ND) || isa(ND)) + return true; + + const NamedDecl *ParentDecl = getParentFunctionDecl(ND); + if ((ParentDecl && ignoreDecl(ParentDecl)) || ignoreDecl(ND)) + return true; + + if (RDO & IsLate) { + Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) + << "Generating Interface Stubs is not supported with " + "delayed template parsing."; + } else { + if (const auto *FD = dyn_cast(ND)) + if (FD->isDependentContext()) + return true; + + const bool IsWeak = (ND->hasAttr() || + ND->hasAttr() || ND->isWeakImported()); + + Symbols.insert(std::make_pair( + ND, + MangledSymbol(getMangledNames(ParentDecl).front(), + // Type: + isa(ND) ? llvm::ELF::STT_OBJECT + : llvm::ELF::STT_FUNC, + // Binding: + IsWeak ? llvm::ELF::STB_WEAK : llvm::ELF::STB_GLOBAL, + getMangledNames(ND)))); + } + return true; + } + + void + HandleDecls(const llvm::iterator_range &Decls, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : Decls) + HandleNamedDecl(dyn_cast(D), Symbols, RDO); + } + + void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : FTD.specializations()) + HandleNamedDecl(dyn_cast(D), Symbols, RDO); + } + + void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : CTD.specializations()) + HandleNamedDecl(dyn_cast(D), Symbols, RDO); + } + + bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + if (!ND) + return false; + + switch (ND->getKind()) { + default: + break; + case Decl::Kind::Namespace: + HandleDecls(cast(ND)->decls(), Symbols, RDO); + return true; + case Decl::Kind::CXXRecord: + HandleDecls(cast(ND)->decls(), Symbols, RDO); + return true; + case Decl::Kind::ClassTemplateSpecialization: + HandleDecls(cast(ND)->decls(), Symbols, + RDO); + return true; + case Decl::Kind::ClassTemplate: + HandleTemplateSpecializations(*cast(ND), Symbols, RDO); + return true; + case Decl::Kind::FunctionTemplate: + HandleTemplateSpecializations(*cast(ND), Symbols, + RDO); + return true; + case Decl::Kind::TemplateTypeParm: + return true; + case Decl::Kind::Var: + case Decl::Kind::ParmVar: + case Decl::Kind::CXXMethod: + case Decl::Kind::CXXConstructor: + case Decl::Kind::CXXDestructor: + case Decl::Kind::Function: + case Decl::Kind::Field: + if (WriteNamedDecl(ND, Symbols, RDO)) + return true; + } + + // While interface stubs are in the development stage, it's probably best to + // catch anything that's not a VarDecl or Template/FunctionDecl. + Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) + << "Expected a function or function template decl."; + return false; + } + +public: + InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, + StringRef Format) + : Instance(Instance), InFile(InFile), Format(Format) {} + + void HandleTranslationUnit(ASTContext &context) override { + struct Visitor : public RecursiveASTVisitor { + bool VisitNamedDecl(NamedDecl *ND) { + if (const auto *FD = dyn_cast(ND)) + if (FD->isLateTemplateParsed()) { + LateParsedDecls.insert(FD); + return true; + } + + if (const auto *VD = dyn_cast(ND)) { + ValueDecls.insert(VD); + return true; + } + + NamedDecls.insert(ND); + return true; + } + + std::set LateParsedDecls; + std::set NamedDecls; + std::set ValueDecls; + } v; + + v.TraverseDecl(context.getTranslationUnitDecl()); + + MangledSymbols Symbols; + auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); + if (!OS) + return; + + if (Instance.getLangOpts().DelayedTemplateParsing) { + clang::Sema &S = Instance.getSema(); + for (const auto *FD : v.LateParsedDecls) { + clang::LateParsedTemplate &LPT = + *S.LateParsedTemplateMap.find(cast(FD))->second; + S.LateTemplateParser(S.OpaqueParser, LPT); + HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); + } + } + + for (const NamedDecl *ND : v.ValueDecls) + HandleNamedDecl(ND, Symbols, FromTU); + for (const NamedDecl *ND : v.NamedDecls) + HandleNamedDecl(ND, Symbols, FromTU); + + auto writeIfoYaml = [this](const llvm::Triple &T, + const MangledSymbols &Symbols, + const ASTContext &context, StringRef Format, + raw_ostream &OS) -> void { + OS << "--- !" << Format << "\n"; + OS << "FileHeader:\n"; + OS << " Class: ELFCLASS"; + OS << (T.isArch64Bit() ? "64" : "32"); + OS << "\n"; + OS << " Data: ELFDATA2"; + OS << (T.isLittleEndian() ? "LSB" : "MSB"); + OS << "\n"; + OS << " Type: ET_REL\n"; + OS << " Machine: " + << llvm::StringSwitch(T.getArchName()) + .Case("x86_64", "EM_X86_64") + .Case("i386", "EM_386") + .Case("i686", "EM_386") + .Case("aarch64", "EM_AARCH64") + .Case("amdgcn", "EM_AMDGPU") + .Case("r600", "EM_AMDGPU") + .Case("arm", "EM_ARM") + .Case("thumb", "EM_ARM") + .Case("avr", "EM_AVR") + .Case("mips", "EM_MIPS") + .Case("mipsel", "EM_MIPS") + .Case("mips64", "EM_MIPS") + .Case("mips64el", "EM_MIPS") + .Case("msp430", "EM_MSP430") + .Case("ppc", "EM_PPC") + .Case("ppc64", "EM_PPC64") + .Case("ppc64le", "EM_PPC64") + .Case("x86", T.isOSIAMCU() ? "EM_IAMCU" : "EM_386") + .Case("x86_64", "EM_X86_64") + .Default("EM_NONE") + << "\nSymbols:\n"; + for (const auto &E : Symbols) { + const MangledSymbol &Symbol = E.second; + for (auto Name : Symbol.Names) { + OS << " - Name: " + << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus + ? "" + : (Symbol.ParentName + ".")) + << Name << "\n" + << " Type: STT_"; + switch (Symbol.Type) { + default: + case llvm::ELF::STT_NOTYPE: + OS << "NOTYPE"; + break; + case llvm::ELF::STT_OBJECT: + OS << "OBJECT"; + break; + case llvm::ELF::STT_FUNC: + OS << "FUNC"; + break; + } + OS << "\n Binding: STB_" + << ((Symbol.Binding == llvm::ELF::STB_WEAK) ? "WEAK" : "GLOBAL") + << "\n"; + } + } + OS << "...\n"; + OS.flush(); + }; + + auto writeIfoElfAbiYaml = + [this](const llvm::Triple &T, const MangledSymbols &Symbols, + const ASTContext &context, StringRef Format, + raw_ostream &OS) -> void { + OS << "--- !" << Format << "\n"; + OS << "TbeVersion: 1.0\n"; + OS << "Arch: " << T.getArchName() << "\n"; + OS << "Symbols:\n"; + for (const auto &E : Symbols) { + const MangledSymbol &Symbol = E.second; + for (auto Name : Symbol.Names) { + OS << " " + << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus + ? "" + : (Symbol.ParentName + ".")) + << Name << ": { Type: "; + switch (Symbol.Type) { + default: + llvm_unreachable( + "clang -emit-iterface-stubs: Unexpected symbol type."); + case llvm::ELF::STT_NOTYPE: + OS << "NoType"; + break; + case llvm::ELF::STT_OBJECT: { + auto VD = cast(E.first)->getType(); + OS << "Object, Size: " + << context.getTypeSizeInChars(VD).getQuantity(); + break; + } + case llvm::ELF::STT_FUNC: + OS << "Func"; + break; + } + if (Symbol.Binding == llvm::ELF::STB_WEAK) + OS << ", Weak: true"; + OS << " }\n"; + } + } + OS << "...\n"; + OS.flush(); + }; + + if (Format == "experimental-yaml-elf-v1") + writeIfoYaml(Instance.getTarget().getTriple(), Symbols, context, Format, + *OS); + else + writeIfoElfAbiYaml(Instance.getTarget().getTriple(), Symbols, context, + Format, *OS); + } +}; + +std::unique_ptr +GenerateInterfaceYAMLExpV1Action::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique( + CI, InFile, "experimental-yaml-elf-v1"); +} + +std::unique_ptr +GenerateInterfaceTBEExpV1Action::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique( + CI, InFile, "experimental-tapi-elf-v1"); +} diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index b6a20a7bb6..69e773658c 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -64,6 +64,10 @@ CreateFrontendBaseAction(CompilerInstance &CI) { case GenerateHeaderModule: return llvm::make_unique(); case GeneratePCH: return llvm::make_unique(); + case GenerateInterfaceYAMLExpV1: + return llvm::make_unique(); + case GenerateInterfaceTBEExpV1: + return llvm::make_unique(); case InitOnly: return llvm::make_unique(); case ParseSyntaxOnly: return llvm::make_unique(); case ModuleFileInfo: return llvm::make_unique(); diff --git a/test/InterfaceStubs/bad-format.cpp b/test/InterfaceStubs/bad-format.cpp new file mode 100644 index 0000000000..d40b1f965b --- /dev/null +++ b/test/InterfaceStubs/bad-format.cpp @@ -0,0 +1,7 @@ +// RUN: not %clang -target x86_64-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=bar-format %s 2>&1 | FileCheck %s + +// CHECK: error: invalid value +// CHECK: '-interface-stub-version=' in 'Must specify a valid interface +// CHECK: stub format type using diff --git a/test/InterfaceStubs/class-template-specialization.cpp b/test/InterfaceStubs/class-template-specialization.cpp new file mode 100644 index 0000000000..d1f1d067cc --- /dev/null +++ b/test/InterfaceStubs/class-template-specialization.cpp @@ -0,0 +1,42 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-TAPI %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-TAPI2 %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | \ +// RUN: llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// For the following: +// g() +// n::S::S() +// n::S::~S() +// n::S::func() const +// n::S::S(n::S const&) + +// We expect these manglings: +// CHECK-TAPI: Symbols: +// CHECK-TAPI-NOT: _ZNK1n1SIiEclEv +// CHECK-TAPI2: Symbols: +// CHECK-TAPI2: _Z1g + +// CHECK-SYMBOLS-DAG: FUNC GLOBAL DEFAULT {{[0-9]}} _Z1g +// CHECK-SYMBOLS-DAG: FUNC WEAK HIDDEN {{[0-9]}} _ZNK1n1SIiEclEv + +namespace n { +template +struct __attribute__((__visibility__("default"))) S { + S() = default; + ~S() = default; + int __attribute__((__visibility__(("default")))) func() const { + return 1844; + } + int __attribute__((__visibility__(("hidden")))) operator()() const { + return 1863; + } +}; +} // namespace n + +void g() { n::S()(); } diff --git a/test/InterfaceStubs/externstatic.c b/test/InterfaceStubs/externstatic.c new file mode 100644 index 0000000000..9224581ee0 --- /dev/null +++ b/test/InterfaceStubs/externstatic.c @@ -0,0 +1,29 @@ +// RUN: %clang -DSTORAGE="extern" -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-EXTERN %s +// RUN: %clang -DSTORAGE="extern" -target x86_64-linux-gnu -O0 -o - -c -std=c99 \ +// RUN: -xc %s | llvm-nm - 2>&1 | FileCheck -check-prefix=CHECK-EXTERN %s + +// RUN: %clang -DSTORAGE="extern" -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-EXTERN2 %s +// RUN: %clang -DSTORAGE="extern" -target x86_64-linux-gnu -O0 -o - -c -std=c99 \ +// RUN: -xc %s | llvm-nm - 2>&1 | FileCheck -check-prefix=CHECK-EXTERN2 %s + +// RUN: %clang -DSTORAGE="static" -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-STATIC %s +// RUN: %clang -DSTORAGE="static" -target x86_64-linux-gnu -O0 -o - -c -std=c99 \ +// RUN: -xc %s | llvm-nm - 2>&1 | FileCheck -check-prefix=CHECK-STATIC %s + +// CHECK-EXTERN-NOT: foo +// CHECK-STATIC-NOT: foo +// CHECK-STATIC-NOT: bar + +// We want to emit extern function symbols. +// CHECK-EXTERN2: bar +STORAGE int foo; +STORAGE int bar() { return 42; } diff --git a/test/InterfaceStubs/function-template-specialization.cpp b/test/InterfaceStubs/function-template-specialization.cpp new file mode 100644 index 0000000000..b6cf64c602 --- /dev/null +++ b/test/InterfaceStubs/function-template-specialization.cpp @@ -0,0 +1,41 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | FileCheck %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DUSE_TEMPLATE_FUNCTION=1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-USES-TEMPLATE-FUNCTION %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DSPECIALIZE_TEMPLATE_FUNCTION=1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-SPECIALIZES-TEMPLATE-FUNCTION %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: %s | llvm-nm - 2>&1 | FileCheck %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DUSE_TEMPLATE_FUNCTION=1 %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-USES-TEMPLATE-FUNCTION %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DSPECIALIZE_TEMPLATE_FUNCTION=1 %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SPECIALIZES-TEMPLATE-FUNCTION %s + +// CHECK-NOT: _Z16templateFunctionIiET_S0_ +// CHECK-USES-TEMPLATE-FUNCTION-DAG: _Z16templateFunctionIiET_S0_ +// CHECK-SPECIALIZES-TEMPLATE-FUNCTION-DAG: _Z16templateFunctionIiET_S0_ +template +T templateFunction(T t) { return t; } + +#ifdef USE_TEMPLATE_FUNCTION +int FortyTwo = templateFunction(42); +#endif + +#ifdef SPECIALIZE_TEMPLATE_FUNCTION +template <> +int templateFunction(int t); +// TODO: Make it so that -emit-interface-stubs does not emit +// _Z16templateFunctionIiET_S0_ if there is no user of the specialization. +int foo() { return templateFunction(42); } +#endif diff --git a/test/InterfaceStubs/hidden-class-inheritance.cpp b/test/InterfaceStubs/hidden-class-inheritance.cpp new file mode 100644 index 0000000000..de4c2993c7 --- /dev/null +++ b/test/InterfaceStubs/hidden-class-inheritance.cpp @@ -0,0 +1,153 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY="" -DCHILD_CLASS_VISIBILITY="" \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-X %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DPARENT_CLASS_VISIBILITY="" -DCHILD_CLASS_VISIBILITY="" \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-X-RE %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY=HIDDEN -DCHILD_CLASS_VISIBILITY="" \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-HP %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY=HIDDEN -DCHILD_CLASS_VISIBILITY="" \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-HP2 %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DPARENT_CLASS_VISIBILITY=HIDDEN -DCHILD_CLASS_VISIBILITY="" \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-HP-RE %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY="" -DCHILD_CLASS_VISIBILITY=HIDDEN \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-HC %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY="" -DCHILD_CLASS_VISIBILITY=HIDDEN \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-HC2 %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DPARENT_CLASS_VISIBILITY="" -DCHILD_CLASS_VISIBILITY=HIDDEN \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-HC-RE %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -DPARENT_CLASS_VISIBILITY=HIDDEN -DCHILD_CLASS_VISIBILITY=HIDDEN \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: FileCheck -check-prefix=CHECK-HP-HC %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c \ +// RUN: -DPARENT_CLASS_VISIBILITY=HIDDEN -DCHILD_CLASS_VISIBILITY=HIDDEN \ +// RUN: -DPARENT_METHOD_VISIBILITY="" -DCHILD_METHOD_VISIBILITY="" %s | \ +// RUN: llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-HP-HC-RE %s + +// CHECK-X-DAG: _ZN1CC2Ev +// CHECK-X-DAG: _ZN1CD0Ev +// CHECK-X-DAG: _ZN1CD2Ev +// CHECK-X-DAG: _ZN1SC2Ev +// CHECK-X-DAG: _ZN1SD0Ev +// CHECK-X-DAG: _ZN1SD2Ev +// CHECK-X-DAG: _ZN1C1mEv +// CHECK-X-DAG: _ZN1S1nEv + +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1C1mEv +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CC2Ev +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CD0Ev +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CD2Ev +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1S1nEv +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SC2Ev +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SD0Ev +// CHECK-X-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SD2Ev + +// CHECK-HP2-DAG: _ZN1CC2Ev +// CHECK-HP2-DAG: _ZN1CD0Ev +// CHECK-HP2-DAG: _ZN1CD2Ev +// CHECK-HP2-DAG: _ZN1C1mEv + +// CHECK-HP-NOT: _ZN1S1nEv +// CHECK-HP-NOT: _ZN1SC2Ev +// CHECK-HP-NOT: _ZN1SD0Ev +// CHECK-HP-NOT: _ZN1SD2Ev + +// CHECK-HP-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1C1mEv +// CHECK-HP-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CC2Ev +// CHECK-HP-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CD0Ev +// CHECK-HP-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1CD2Ev +// CHECK-HP-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1S1nEv +// CHECK-HP-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SC2Ev +// CHECK-HP-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SD0Ev +// CHECK-HP-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SD2Ev + +// CHECK-HC2-DAG: _ZN1SC2Ev +// CHECK-HC2-DAG: _ZN1SD0Ev +// CHECK-HC2-DAG: _ZN1SD2Ev +// CHECK-HC2-DAG: _ZN1S1nEv + +// CHECK-HC-NOT: _ZN1C1mEv +// CHECK-HC-NOT: _ZN1CC2Ev +// CHECK-HC-NOT: _ZN1CD0Ev +// CHECK-HC-NOT: _ZN1CD2Ev + +// CHECK-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1C1mEv +// CHECK-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CC2Ev +// CHECK-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CD0Ev +// CHECK-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CD2Ev +// CHECK-HC-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1S1nEv +// CHECK-HC-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SC2Ev +// CHECK-HC-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SD0Ev +// CHECK-HC-RE: FUNC WEAK DEFAULT {{[0-9]+}} _ZN1SD2Ev + +// CHECK-HP-HC-NOT: _ZN1CC2Ev +// CHECK-HP-HC-NOT: _ZN1CD0Ev +// CHECK-HP-HC-NOT: _ZN1CD2Ev +// CHECK-HP-HC-NOT: _ZN1SC2Ev +// CHECK-HP-HC-NOT: _ZN1SD0Ev +// CHECK-HP-HC-NOT: _ZN1SD2Ev +// CHECK-HP-HC-NOT: _ZN1C1mEv +// CHECK-HP-HC-NOT: _ZN1S1nEv + +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1C1mEv +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CC2Ev +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CD0Ev +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1CD2Ev +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1S1nEv +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SC2Ev +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SD0Ev +// CHECK-HP-HC-RE: FUNC WEAK HIDDEN {{[0-9]+}} _ZN1SD2Ev + +// TODO: clang+llvm does not materialize complete ctors and dtors for the +// Itanium abi. Figure out why and add the check-not for these: +// _ZN1CC1Ev +// _ZN1CD1Ev +// _ZN1SC1Ev +// _ZN1SD1Ev + +#define HIDDEN __attribute__((__visibility__("hidden"))) +#define DEFAULT __attribute__((__visibility__("default"))) + +struct PARENT_CLASS_VISIBILITY S { + virtual ~S() {} + virtual PARENT_METHOD_VISIBILITY void n() {} +}; + +class CHILD_CLASS_VISIBILITY C : public S { +public: + virtual CHILD_METHOD_VISIBILITY void m() {} +}; + +void f() { + C c; + c.m(); + c.n(); +} diff --git a/test/InterfaceStubs/inline.c b/test/InterfaceStubs/inline.c new file mode 100644 index 0000000000..c64771185f --- /dev/null +++ b/test/InterfaceStubs/inline.c @@ -0,0 +1,67 @@ +// RUN: %clang -DINLINE=inline -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=gnu89 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-GNU %s +// RUN: %clang -DINLINE=inline -target x86_64-linux-gnu -O0 -o - -c \ +// RUN: -std=gnu89 -xc %s | llvm-nm - | FileCheck -check-prefix=CHECK-GNU %s + +// RUN: %clang -DINLINE="__attribute__((always_inline))" \ +// RUN: -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-GNU %s +// RUN: %clang -DINLINE="__attribute__((always_inline))" \ +// RUN: -target x86_64-linux-gnu -O0 -o - -c -xc %s | \ +// RUN: llvm-nm - | FileCheck -check-prefix=CHECK-GNU %s + +// RUN: %clang -DINLINE=inline -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-STD %s +// RUN: %clang -DINLINE=inline -target x86_64-linux-gnu -O0 -o - -c -std=c99 \ +// RUN: -xc %s | llvm-nm - 2>&1 | FileCheck -check-prefix=CHECK-STD %s + +// RUN: %clang -DINLINE="__attribute__((noinline))" \ +// RUN: -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-NOINLINE %s +// RUN: %clang -DINLINE="__attribute__((noinline))" -target x86_64-linux-gnu \ +// RUN: -O0 -o - -c -std=c99 -xc %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-NOINLINE %s + +// RUN: %clang -DINLINE="static" -target x86_64-unknown-linux-gnu -o - \ +// RUN: -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -std=c99 -xc %s | \ +// RUN: FileCheck -check-prefix=CHECK-STATIC %s +// RUN: %clang -DINLINE="static" -target x86_64-linux-gnu -O0 -o - -c \ +// RUN: -std=c99 -xc %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-STATIC %s + +// CHECK-GNU-DAG: foo +// CHECK-GNU-DAG: foo.var +// CHECK-NOINLINE-DAG: foo +// CHECK-NOINLINE-DAG: foo.var +// CHECK-STATIC-NOT: foo +// CHECK-STATIC-NOT: foo.var +// CHECK-STD-NOT: foo +#pragma clang diagnostic ignored "-Wstatic-local-in-inline" +INLINE int foo() { + static int var = 42; + return var; +} + +// RUN: %clang -DINLINE=inline -target x86_64-linux-gnu -o - \ +// RUN: -emit-interface-stubs -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -std=gnu89 -xc %s | FileCheck -check-prefix=CHECK-TAPI %s + +// RUN: %clang -DINLINE=inline -target x86_64-linux-gnu -o - \ +// RUN: -emit-interface-stubs -interface-stub-version=experimental-tapi-elf-v1 \ +// RUN: -std=gnu89 -xc %s | FileCheck -check-prefix=CHECK-SYMBOLS %s +// RUN: %clang -DINLINE=inline -target x86_64-linux-gnu -o - \ +// RUN: -c -std=gnu89 -xc %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// CHECK-TAPI-DAG: foo: { Type: Func } +// CHECK-TAPI-DAG: foo.var: { Type: Object, Size: 4 } +// CHECK-SYMBOLS-DAG: foo +// CHECK-SYMBOLS-DAG: foo.var +#include "inline.h" diff --git a/test/InterfaceStubs/inline.h b/test/InterfaceStubs/inline.h new file mode 100644 index 0000000000..fe16519e94 --- /dev/null +++ b/test/InterfaceStubs/inline.h @@ -0,0 +1,4 @@ +INLINE int bar() { + static int var = 42; + return var; +} diff --git a/test/InterfaceStubs/object.cpp b/test/InterfaceStubs/object.cpp new file mode 100644 index 0000000000..68b5ee781c --- /dev/null +++ b/test/InterfaceStubs/object.cpp @@ -0,0 +1,13 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-TAPI %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// CHECK-TAPI: data: { Type: Object, Size: 4 } +// CHECK-SYMBOLS: data +int data = 42; diff --git a/test/InterfaceStubs/template-namespace-function.cpp b/test/InterfaceStubs/template-namespace-function.cpp new file mode 100644 index 0000000000..879318122b --- /dev/null +++ b/test/InterfaceStubs/template-namespace-function.cpp @@ -0,0 +1,31 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// CHECK: Symbols: +// CHECK-DAG: _ZN3qux3barEii: { Type: Func } +// CHECK-DAG: _ZN3baz3addIiEET_S1_S1_: { Type: Func } +// CHECK-DAG: _Z4fbarff: { Type: Func } +// CHECK-DAG: _ZN3baz3addIfEET_S1_S1_: { Type: Func } + +// Same symbols just different order. +// CHECK-SYMBOLS-DAG: _Z4fbarff +// CHECK-SYMBOLS-DAG: _ZN3baz3addIfEET_S1_S1_ +// CHECK-SYMBOLS-DAG: _ZN3baz3addIiEET_S1_S1_ +// CHECK-SYMBOLS-DAG: _ZN3qux3barEii + +namespace baz { +template +T add(T a, T b) { + return a + b; +} +} // namespace baz + +namespace qux { +int bar(int a, int b) { return baz::add(a, b); } +} // namespace qux + +float fbar(float a, float b) { return baz::add(a, b); } diff --git a/test/InterfaceStubs/virtual.cpp b/test/InterfaceStubs/virtual.cpp new file mode 100644 index 0000000000..b861b7e802 --- /dev/null +++ b/test/InterfaceStubs/virtual.cpp @@ -0,0 +1,41 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-TAPI %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck -check-prefix=CHECK-TAPI2 %s +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | \ +// RUN: llvm-readelf -s - 2>&1 | FileCheck -check-prefix=CHECK-SYMBOLS %s + +#define HIDDEN __attribute__((__visibility__(("hidden")))) +#define DEFAULT __attribute__((__visibility__(("default")))) + +// CHECK-TAPI-NOT: _ZNK1Q5func1Ev +// CHECK-TAPI-NOT: _ZNK1Q5func2Ev +// CHECK-SYMBOLS-DAG: NOTYPE GLOBAL HIDDEN {{.*}} _ZNK1Q5func1Ev +// CHECK-SYMBOLS-DAG: NOTYPE GLOBAL DEFAULT {{.*}} _ZNK1Q5func2Ev +struct Q { + virtual HIDDEN int func1() const; + virtual DEFAULT int func2() const; +} q; + +// CHECK-TAPI-NOT: _ZNK1S5func1Ev +// CHECK-TAPI2-DAG: _ZNK1S5func2Ev +// CHECK-SYMBOLS-DAG: FUNC WEAK HIDDEN {{.*}} _ZNK1S5func1Ev +// CHECK-SYMBOLS-DAG: FUNC WEAK DEFAULT {{.*}} _ZNK1S5func2Ev +struct S { + virtual HIDDEN int func1() const { return 42; } + virtual DEFAULT int func2() const { return 42; } +} s; + +// CHECK-TAPI-NOT: _ZNK1R5func1Ev +// CHECK-TAPI-NOT: _ZNK1R5func2Ev +// CHECK-SYMBOLS-NOT: _ZNK1R5func1Ev +// CHECK-SYMBOLS-NOT: _ZNK1R5func2Ev +struct R { + virtual HIDDEN int func1() const = 0; + virtual DEFAULT int func2() const = 0; +}; + +int a = q.func1() + q.func2(); + diff --git a/test/InterfaceStubs/visibility.cpp b/test/InterfaceStubs/visibility.cpp new file mode 100644 index 0000000000..87ea7ea7bd --- /dev/null +++ b/test/InterfaceStubs/visibility.cpp @@ -0,0 +1,45 @@ +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 -fvisibility=hidden \ +// RUN: %s | FileCheck --check-prefix=CHECK-CMD-HIDDEN %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 -fvisibility=hidden \ +// RUN: %s | FileCheck --check-prefix=CHECK-CMD-HIDDEN %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck --check-prefix=CHECK-CMD %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 %s | \ +// RUN: FileCheck --check-prefix=CHECK-CMD %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck --check-prefix=CHECK-CMD2 %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 %s | \ +// RUN: FileCheck --check-prefix=CHECK-CMD2 %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | llvm-readelf -s - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// Always Be Hidden: +// CHECK-CMD-HIDDEN-NOT: _Z6hiddenv +// CHECK-CMD2-NOT: _Z6hiddenv +__attribute__((visibility("hidden"))) void hidden() {} + +// Always Be Visible: +// CHECK-CMD-HIDDEN: _Z9nothiddenv +// CHECK-CMD-DAG: _Z9nothiddenv +__attribute__((visibility("default"))) void nothidden() {} + +// Do Whatever -fvisibility says: +// CHECK-CMD-HIDDEN-NOT: _Z10cmdVisiblev +// CHECK-CMD-DAG: _Z10cmdVisiblev +void cmdVisible() {} + +// CHECK-SYMBOLS-DAG: DEFAULT {{.*}} _Z10cmdVisiblev +// CHECK-SYMBOLS-DAG: HIDDEN {{.*}} _Z6hiddenv +// CHECK-SYMBOLS-DAG: DEFAULT {{.*}} _Z9nothiddenv diff --git a/test/InterfaceStubs/weak.cpp b/test/InterfaceStubs/weak.cpp new file mode 100644 index 0000000000..e089225b5d --- /dev/null +++ b/test/InterfaceStubs/weak.cpp @@ -0,0 +1,27 @@ +// RUN: %clang -target x86_64-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-tapi-elf-v1 %s | \ +// RUN: FileCheck %s + +// RUN: %clang -target x86_64-linux-gnu -o - -emit-interface-stubs \ +// RUN: -interface-stub-version=experimental-yaml-elf-v1 %s | \ +// RUN: FileCheck --check-prefix=CHECK-YAML %s + +// RUN: %clang -target x86_64-unknown-linux-gnu -o - -c %s | llvm-nm - 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-SYMBOLS %s + +// CHECK: Symbols: +// CHECK-DAG: _Z8weakFuncv: { Type: Func, Weak: true } +// CHECK-DAG: _Z10strongFuncv: { Type: Func } + +// CHECK-YAML: Symbols: +// CHECK-YAML-DAG: - Name: _Z8weakFuncv +// CHECK-YAML-DAG: Type: STT_FUNC +// CHECK-YAML-DAG: Binding: STB_WEAK +// CHECK-YAML-DAG: - Name: _Z10strongFuncv +// CHECK-YAML-DAG: Type: STT_FUNC +// CHECK-YAML-DAG: Binding: STB_GLOBAL + +// CHECK-SYMBOLS-DAG: _Z10strongFuncv +// CHECK-SYMBOLS-DAG: _Z8weakFuncv +__attribute__((weak)) void weakFunc() {} +int strongFunc() { return 42; } -- 2.40.0