diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -623,6 +623,8 @@ 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_ifso : Flag<["-"], "emit-ifso">, Flags<[CC1Option]>, Group, + HelpText<"Generate Inteface Library File.">; def exported__symbols__list : Separate<["-"], "exported_symbols_list">; def e : JoinedOrSeparate<["-"], "e">, Group; def fPIC : Flag<["-"], "fPIC">, Group; diff --git a/clang/include/clang/Driver/Types.def b/clang/include/clang/Driver/Types.def --- a/clang/include/clang/Driver/Types.def +++ b/clang/include/clang/Driver/Types.def @@ -88,6 +88,7 @@ // Misc. TYPE("ast", AST, INVALID, "ast", "u") +TYPE("ifo", IFO, INVALID, "ifo", "u") TYPE("pcm", ModuleFile, INVALID, "pcm", "u") TYPE("plist", Plist, INVALID, "plist", "") TYPE("rewritten-objc", RewrittenObjC,INVALID, "cpp", "") diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h --- a/clang/include/clang/Frontend/FrontendActions.h +++ b/clang/include/clang/Frontend/FrontendActions.h @@ -119,6 +119,18 @@ bool hasASTFileSupport() const override { return false; } }; +class GenerateIFSOAction : public ASTFrontendAction { +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + + TranslationUnitKind getTranslationUnitKind() override { + return TU_Module; + } + + bool hasASTFileSupport() const override { return false; } +}; + class GenerateModuleFromModuleMapAction : public GenerateModuleAction { private: bool BeginSourceFileAction(CompilerInstance &CI) override; diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -87,6 +87,9 @@ /// Generate pre-compiled header. GeneratePCH, + /// Generate Interface Library. + GenerateIFSO, + /// Only execute frontend initialization. InitOnly, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -285,6 +285,7 @@ (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_ifso)) || (PhaseArg = DAL.getLastArg(options::OPT__analyze, options::OPT__analyze_auto)) || (PhaseArg = DAL.getLastArg(options::OPT_emit_ast))) { @@ -3445,6 +3446,8 @@ 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_ifso)) + return C.MakeAction(Input, types::TY_IFO); return C.MakeAction(Input, types::TY_LLVM_BC); } case phases::Backend: { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3582,6 +3582,8 @@ } 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_IFO) { + CmdArgs.push_back("-emit-ifso"); } else if (JA.getType() == types::TY_PP_Asm) { CmdArgs.push_back("-S"); } else if (JA.getType() == types::TY_AST) { diff --git a/clang/lib/Frontend/CMakeLists.txt b/clang/lib/Frontend/CMakeLists.txt --- a/clang/lib/Frontend/CMakeLists.txt +++ b/clang/lib/Frontend/CMakeLists.txt @@ -54,6 +54,7 @@ clangAST clangBasic clangDriver + clangIndex clangEdit clangLex clangParse diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1645,6 +1645,8 @@ Opts.ProgramAction = frontend::GenerateHeaderModule; break; case OPT_emit_pch: Opts.ProgramAction = frontend::GeneratePCH; break; + case OPT_emit_ifso: + Opts.ProgramAction = frontend::GenerateIFSO; break; case OPT_init_only: Opts.ProgramAction = frontend::InitOnly; break; case OPT_fsyntax_only: @@ -3066,6 +3068,7 @@ case frontend::GenerateModuleInterface: case frontend::GenerateHeaderModule: case frontend::GeneratePCH: + case frontend::GenerateIFSO: case frontend::ParseSyntaxOnly: case frontend::ModuleFileInfo: case frontend::VerifyPCH: diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -20,11 +20,14 @@ #include "clang/Sema/TemplateInstCallback.h" #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/ASTWriter.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/YAMLTraits.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Index/CodegenNameGenerator.h" #include #include @@ -158,6 +161,291 @@ return true; } +class IFSOFunctionsConsumer : public ASTConsumer { + CompilerInstance &Instance; + StringRef InFile = ""; + std::set ParsedTemplates; + + enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; + struct MangledSymbol { + std::string Name = ""; + uint8_t Type = llvm::ELF::STT_NOTYPE; + uint8_t Binding = llvm::ELF::STB_LOCAL; + MangledSymbol(const std::string &Name, uint8_t Type, uint8_t Binding) + : Name(Name), Type(Type), Binding(Binding) {} + }; + using MangledSymbols = std::map; + + template + bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + if (!isa(ND)) + return false; + if (Symbols.find(ND) != Symbols.end()) + return true; + if (isa(ND)) + return true; + if (ND->getVisibility() != DefaultVisibility) + return true; + // If this is a FunctionDecl that is dependent on a template parameter, then + // don't get the symbol because we can only export specializations. + bool IsRDOLate = (RDO & IsLate); + if (const auto *FD = dyn_cast(ND)) + if (FD->isDependentContext() && !IsRDOLate) + return true; + index::CodegenNameGenerator CGNameGen(ND->getASTContext()); + std::string MangledName = CGNameGen.getName(ND); + uint8_t Type = llvm::ELF::STT_FUNC; + uint8_t Binding = llvm::ELF::STB_GLOBAL; + if (ND->hasAttr() || ND->hasAttr() || + ND->isWeakImported()) + Binding = llvm::ELF::STB_WEAK; + if (isa(ND)) + Type = llvm::ELF::STT_OBJECT; + + Symbols.insert(std::pair( + ND, MangledSymbol(MangledName, Type, Binding))); + // For now, lets just dump the -fdelayed-template-parsing decls until we + // decide how to handle them. + if (IsRDOLate) { + llvm::errs() << "LATE DECL:\n"; + ND->dump(); + } + return true; + } + + template + bool HandleSomeDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + if (!isa(ND)) + return false; + for (auto *I : cast(ND)->decls()) + HandleNamedDecl(dyn_cast(I), Symbols, RDO); + return true; + } + + template + bool HandleSomeDeclSpec(const NamedDecl *ND, MangledSymbols &Symbols, + int RDO) { + if (!isa(ND)) + return false; + for (auto *I : cast(ND)->specializations()) + HandleNamedDecl(dyn_cast(I), Symbols, RDO); + return true; + } + + bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + if (!ND) + return false; + // Handle NamespaceDecls first so that we fully recurse to every decl. + if (HandleSomeDecl(ND, Symbols, RDO) || + HandleSomeDecl(ND, Symbols, RDO) || + HandleSomeDeclSpec(ND, Symbols, RDO) || + HandleSomeDeclSpec(ND, Symbols, RDO)) + return true; + if (!(RDO & FromTU)) + return true; + if (const auto *TTPD = dyn_cast(ND)) + return true; + if (WriteNamedDecl(ND, Symbols, RDO) || + WriteNamedDecl(ND, Symbols, RDO) || + WriteNamedDecl(ND, Symbols, RDO)) + return true; + // While IFSOs are in the development stage, it's probably best to catch + // anything that's not a VarDecl or Template/FunctionDecl. + ND->dump(); + llvm_unreachable("ifso: Expected a function or function template decl."); + return false; + } + +public: + IFSOFunctionsConsumer(CompilerInstance &Instance, StringRef InFile) + : Instance(Instance), InFile(InFile) {} + + void HandleTranslationUnit(ASTContext &context) override { + struct Visitor : public RecursiveASTVisitor { + bool VisitNamedDecl(NamedDecl *ND) { + if (auto *FD = dyn_cast(ND)) + (FD->isLateTemplateParsed() ? LateParsedDecls : NamedDecls) + .insert(FD); + else if (auto *VD = dyn_cast(ND)) + ValueDecls.insert(VD); + else + 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, "ifo"); + if (!OS) + return; + + if (Instance.getLangOpts().DelayedTemplateParsing) { + clang::Sema &sema = Instance.getSema(); + for (const auto *FD : v.LateParsedDecls) { + clang::LateParsedTemplate &LPT = + *sema.LateParsedTemplateMap.find(cast(FD))->second; + sema.LateTemplateParser(sema.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 = [](const llvm::Triple &T, const MangledSymbols &Symbols, + raw_ostream &OS) -> void { + OS << "--- !ifo-elf-v1\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") + << "\n"; + OS << "Sections:\n"; + OS << " - Name: .text\n"; + OS << " Type: SHT_PROGBITS\n"; + OS << " Flags: [ SHF_ALLOC, SHF_EXECINSTR ]\n"; + OS << " - Name: .data\n"; + OS << " Type: SHT_PROGBITS\n"; + OS << " Flags: [ SHF_WRITE, SHF_ALLOC ]\n"; + OS << "Symbols:\n"; + for (auto E : Symbols) { + const MangledSymbol &Symbol = E.second; + OS << " - Name: " << Symbol.Name << "\n" + << " Type: "; + switch (Symbol.Type) { + default: + case llvm::ELF::STT_NOTYPE: + OS << "STT_NOTYPE\n"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_OBJECT: + OS << "STT_OBJECT\n"; + OS << " Section: .data\n"; + break; + case llvm::ELF::STT_FUNC: + OS << "STT_FUNC\n"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_SECTION: + OS << "STT_SECTION\n"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_FILE: + OS << "STT_FILEn"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_COMMON: + OS << "STT_COMMON\n"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_TLS: + OS << "STT_TLS\n"; + OS << " Section: .text\n"; + break; + case llvm::ELF::STT_GNU_IFUNC: + OS << "STT_GNU_IFUNC\n"; + OS << " Section: .text\n"; + break; + } + OS << " Binding: "; + switch (Symbol.Binding) { + case llvm::ELF::STB_LOCAL: + OS << "STB_LOCAL\n"; + break; + case llvm::ELF::STB_GLOBAL: + OS << "STB_GLOBAL\n"; + break; + case llvm::ELF::STB_WEAK: + OS << "STB_WEAK\n"; + break; + case llvm::ELF::STB_GNU_UNIQUE: + OS << "STB_GNU_UNIQUE\n"; + break; + } + } + OS << "...\n"; + OS.flush(); + }; + + auto writeIfoElfAbiYaml = [](const llvm::Triple &T, + const MangledSymbols &Symbols, + raw_ostream &OS) -> void { + OS << "--- !ifo-elfabi-v1\n"; + OS << "Arch: " << T.getArchName() << "\n"; + OS << "Endian: " << (T.isLittleEndian() ? "little" : "big") << "\n"; + OS << "Symbols:\n"; + for (auto E : Symbols) { + const MangledSymbol &Symbol = E.second; + std::string Weak = ((Symbol.Binding != llvm::ELF::STB_WEAK) + ? "" + : " Weak: true\n"); + OS << " - Name: " << Symbol.Name << "\n" + << Weak << " Type: "; + switch (Symbol.Type) { + default: + llvm_unreachable("clang -emit-ifso: Unexpected symbol type."); + case llvm::ELF::STT_NOTYPE: + OS << "NoType\n"; + break; + case llvm::ELF::STT_OBJECT: + OS << "Object\n"; + break; + case llvm::ELF::STT_FUNC: + OS << "Function\n"; + break; + case llvm::ELF::STT_TLS: + OS << "TLS\n"; + break; + } + } + OS << "...\n"; + OS.flush(); + }; + + auto writeIfo = writeIfoElfAbiYaml; + writeIfo(Instance.getTarget().getTriple(), Symbols, *OS); + } +}; + +std::unique_ptr +GenerateIFSOAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique(CI, InFile); +} + std::unique_ptr GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -64,6 +64,7 @@ case GenerateHeaderModule: return llvm::make_unique(); case GeneratePCH: return llvm::make_unique(); + case GenerateIFSO: 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/clang/test/IFSO/foo-inline.h b/clang/test/IFSO/foo-inline.h new file mode 100644 --- /dev/null +++ b/clang/test/IFSO/foo-inline.h @@ -0,0 +1,6 @@ + +inline int fvih() { +static int fortytwo = 42; + return fortytwo; +} + diff --git a/clang/test/IFSO/foo.cpp b/clang/test/IFSO/foo.cpp new file mode 100644 --- /dev/null +++ b/clang/test/IFSO/foo.cpp @@ -0,0 +1,56 @@ +// RUN: %clang -emit-ifso -fvisibility=hidden %s -o - | FileCheck --check-prefix=CHECK-HIDDEN %s +// RUN: %clang -emit-ifso %s -o - | FileCheck %s + +// CHECK-HIDDEN-NOT: __Z4fbarff +// CHECK: __Z4fbarff + + + + +// CHECK-HIDDEN-NOT: __Z3fooii +// CHECK-NOT: __Z3fooii + + + +#include "foo-inline.h" + +__attribute__ ((visibility ("hidden"))) int foo(int a, int b) { return a + b; } +__attribute__ ((visibility ("default"))) int foo_default_visi(int a, int b) { return a + b; } + + +__attribute__ ((visibility ("default"))) int fvih_1(int a, int b) { return a + fvih(); } + + +__attribute__((weak)) int someWeakFunc() { return 42; } + +int dataA = 34; + +namespace baz { + template + T add(T a, T b) { + return a + b; + } +} + +namespace n { + template + struct __attribute__((__visibility__("default"))) S { + S() = default; + ~S() = default; + int __attribute__((__visibility__(("default")))) func() const { return 32; } + int __attribute__((__visibility__(("hidden")))) operator()() const { return 53; } + }; +} + +template T neverUsed(T t) { return t + 2; } + +template<> int neverUsed(int t); + +void g() { n::S()(); } + +namespace qux { +int bar(int a, int b) { return baz::add(a, b); } +} + +float fbar(float a, float b) { return baz::add(a, b); } +