Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2494,7 +2494,7 @@ /// /// \returns true if the function/var must be CodeGen'ed/deserialized even if /// it is not used. - bool DeclMustBeEmitted(const Decl *D); + bool DeclMustBeEmitted(const Decl *D, bool WritingModule = false); const CXXConstructorDecl * getCopyConstructorForExceptionObject(CXXRecordDecl *RD); Index: include/clang/AST/ExternalASTSource.h =================================================================== --- include/clang/AST/ExternalASTSource.h +++ include/clang/AST/ExternalASTSource.h @@ -16,6 +16,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/DeclBase.h" +#include "clang/Basic/Module.h" #include "llvm/ADT/DenseMap.h" namespace clang { @@ -169,6 +170,10 @@ /// Return a descriptor for the corresponding module, if one exists. virtual llvm::Optional getSourceDescriptor(unsigned ID); + enum ExtKind { EK_Always, EK_Never, EK_ReplyHazy }; + + virtual ExtKind hasExternalDefinitions(unsigned ID); + /// \brief Finds all declarations lexically contained within the given /// DeclContext, after applying an optional filter predicate. /// Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -201,6 +201,7 @@ LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(NewAlignOverride , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'") LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts") +BENIGN_LANGOPT(ModularCodegen , 1, 0, "Modular codegen") BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision") BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records") BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form") Index: include/clang/Basic/Module.h =================================================================== --- include/clang/Basic/Module.h +++ include/clang/Basic/Module.h @@ -205,6 +205,8 @@ /// and headers from used modules. unsigned NoUndeclaredIncludes : 1; + unsigned WithCodegen : 2; + /// \brief Describes the visibility of the various names within a /// particular module. enum NameVisibilityKind { Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -432,6 +432,10 @@ Flag<["-"], "fmodules-local-submodule-visibility">, HelpText<"Enforce name visibility rules across submodules of the same " "top-level module.">; +def fmodule_codegen : + Flag<["-"], "fmodule-codegen">, + HelpText<"Generate code for uses of this module that assumes an explicit " + "object file will be built for the module">; def fmodule_format_EQ : Joined<["-"], "fmodule-format=">, HelpText<"Select the container format for clang modules and PCH. " "Supported options are 'raw' and 'obj'.">; Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -591,6 +591,8 @@ /// \brief Record code for declarations associated with OpenCL extensions. OPENCL_EXTENSION_DECLS = 59, + + MODULAR_CODEGEN_DECLS = 60, }; /// \brief Record types used within a source manager block. Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -715,6 +715,8 @@ /// the consumer eagerly. SmallVector EagerlyDeserializedDecls; + SmallVector ModularCodegenDecls; + /// \brief The IDs of all tentative definitions stored in the chain. /// /// Sema keeps track of all tentative definitions in a TU because it has to @@ -1968,6 +1970,8 @@ /// \brief Return a descriptor for the corresponding module. llvm::Optional getSourceDescriptor(unsigned ID) override; + ExtKind hasExternalDefinitions(unsigned ID) override; + /// \brief Retrieve a selector from the given module with its local ID /// number. Selector getLocalSelector(ModuleFile &M, unsigned LocalID); Index: include/clang/Serialization/ASTWriter.h =================================================================== --- include/clang/Serialization/ASTWriter.h +++ include/clang/Serialization/ASTWriter.h @@ -365,6 +365,7 @@ /// IDs, since they will be written out to an EAGERLY_DESERIALIZED_DECLS /// record. SmallVector EagerlyDeserializedDecls; + SmallVector ModularCodegenDecls; /// \brief DeclContexts that have received extensions since their serialized /// form. Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8882,8 +8882,22 @@ } GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) const { - return adjustGVALinkageForAttributes( - *this, basicGVALinkageForFunction(*this, FD), FD); + auto L = adjustGVALinkageForAttributes( + *this, basicGVALinkageForFunction(*this, FD), FD); + auto EK = ExternalASTSource::EK_ReplyHazy; + if (auto *Ext = getExternalSource()) + EK = Ext->hasExternalDefinitions(FD->getOwningModuleID()); + switch (EK) { + case ExternalASTSource::EK_Never: + if (L == GVA_DiscardableODR) + return GVA_StrongODR; + break; + case ExternalASTSource::EK_Always: + return GVA_AvailableExternally; + case ExternalASTSource::EK_ReplyHazy: + break; + } + return L; } static GVALinkage basicGVALinkageForVariable(const ASTContext &Context, @@ -8968,7 +8982,7 @@ *this, basicGVALinkageForVariable(*this, VD), VD); } -bool ASTContext::DeclMustBeEmitted(const Decl *D) { +bool ASTContext::DeclMustBeEmitted(const Decl *D, bool WritingModule) { if (const VarDecl *VD = dyn_cast(D)) { if (!VD->isFileVarDecl()) return false; @@ -9032,6 +9046,17 @@ } } + GVALinkage Linkage = GetGVALinkageForFunction(FD); + + if (Linkage == GVA_DiscardableODR) { + if (WritingModule) + return true; + if (auto *Ext = getExternalSource()) + if (Ext->hasExternalDefinitions(FD->getOwningModuleID()) == + ExternalASTSource::EK_Never) + return true; + } + // static, static inline, always_inline, and extern inline functions can // always be deferred. Normal inline functions can be deferred in C99/C++. // Implicit template instantiations can also be deferred in C++. Index: lib/AST/ExternalASTSource.cpp =================================================================== --- lib/AST/ExternalASTSource.cpp +++ lib/AST/ExternalASTSource.cpp @@ -28,6 +28,11 @@ return None; } +ExternalASTSource::ExtKind +ExternalASTSource::hasExternalDefinitions(unsigned ID) { + return EK_ReplyHazy; +} + ExternalASTSource::ASTSourceDescriptor::ASTSourceDescriptor(const Module &M) : Signature(M.Signature), ClangModule(&M) { if (M.Directory) Index: lib/Basic/Module.cpp =================================================================== --- lib/Basic/Module.cpp +++ lib/Basic/Module.cpp @@ -33,7 +33,8 @@ IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), - NoUndeclaredIncludes(false), NameVisibility(Hidden) { + NoUndeclaredIncludes(false), WithCodegen(false), + NameVisibility(Hidden) { if (Parent) { if (!Parent->isAvailable()) IsAvailable = false; Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -2796,7 +2796,7 @@ // We are guaranteed to have a strong definition somewhere else, // so we can use available_externally linkage. if (Linkage == GVA_AvailableExternally) - return llvm::Function::AvailableExternallyLinkage; + return llvm::GlobalValue::AvailableExternallyLinkage; // Note that Apple's kernel linker doesn't support symbol // coalescing, so we need to avoid linkonce and weak linkages there. Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1955,6 +1955,7 @@ Args.hasArg(OPT_fmodules_decluse) || Opts.ModulesStrictDeclUse; Opts.ModulesLocalVisibility = Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS; + Opts.ModularCodegen = Args.hasArg(OPT_fmodule_codegen); Opts.ModulesSearchAll = Opts.Modules && !Args.hasArg(OPT_fno_modules_search_all) && Args.hasArg(OPT_fmodules_search_all); Index: lib/Lex/ModuleMap.cpp =================================================================== --- lib/Lex/ModuleMap.cpp +++ lib/Lex/ModuleMap.cpp @@ -91,6 +91,7 @@ HeaderInfo(HeaderInfo), BuiltinIncludeDir(nullptr), SourceModule(nullptr), NumCreatedModules(0) { MMapLangOpts.LineComment = true; + MMapLangOpts.ModularCodegen = LangOpts.ModularCodegen; } ModuleMap::~ModuleMap() { @@ -554,16 +555,17 @@ return Context->findSubmodule(Name); } -std::pair -ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, - bool IsExplicit) { +std::pair ModuleMap::findOrCreateModule(StringRef Name, + Module *Parent, + bool IsFramework, + bool IsExplicit) { // Try to find an existing module with this name. if (Module *Sub = lookupModuleQualified(Name, Parent)) return std::make_pair(Sub, false); // Create a new module with this name. - Module *Result = new Module(Name, SourceLocation(), Parent, - IsFramework, IsExplicit, NumCreatedModules++); + Module *Result = new Module(Name, SourceLocation(), Parent, IsFramework, + IsExplicit, NumCreatedModules++); if (!Parent) { if (LangOpts.CurrentModule == Name) SourceModule = Result; @@ -1499,6 +1501,7 @@ (!ActiveModule->Parent && ModuleName == "Darwin")) ActiveModule->NoUndeclaredIncludes = true; ActiveModule->Directory = Directory; + ActiveModule->WithCodegen = L.getLangOpts().ModularCodegen; if (!ActiveModule->Parent) { StringRef MapFileName(ModuleMapFile->getName()); Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -2607,7 +2607,8 @@ break; case SUBMODULE_BLOCK_ID: - if (ASTReadResult Result = ReadSubmoduleBlock(F, ClientLoadCapabilities)) + if (ASTReadResult Result = + ReadSubmoduleBlock(F, ClientLoadCapabilities)) return Result; break; @@ -2772,6 +2773,14 @@ EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I])); break; + case MODULAR_CODEGEN_DECLS: + // FIXME: Skip reading this record if our ASTConsumer doesn't care about + // them (ie: if we're not codegenerating this module). + if (F.Kind == MK_MainFile) + for (unsigned I = 0, N = Record.size(); I != N; ++I) + EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I])); + break; + case SPECIAL_TYPES: if (SpecialTypes.empty()) { for (unsigned I = 0, N = Record.size(); I != N; ++I) @@ -4630,6 +4639,7 @@ bool InferExplicitSubmodules = Record[Idx++]; bool InferExportWildcard = Record[Idx++]; bool ConfigMacrosExhaustive = Record[Idx++]; + bool WithCodegen = Record[Idx++]; Module *ParentModule = nullptr; if (Parent) @@ -4637,8 +4647,9 @@ // Retrieve this (sub)module from the module map, creating it if // necessary. - CurrentModule = ModMap.findOrCreateModule(Name, ParentModule, IsFramework, - IsExplicit).first; + CurrentModule = + ModMap.findOrCreateModule(Name, ParentModule, IsFramework, IsExplicit) + .first; // FIXME: set the definition loc for CurrentModule, or call // ModMap.setInferredModuleAllowedBy() @@ -4674,6 +4685,7 @@ CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive; + CurrentModule->WithCodegen = WithCodegen; if (DeserializationListener) DeserializationListener->ModuleRead(GlobalID, CurrentModule); @@ -7901,6 +7913,18 @@ return None; } +ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(unsigned ID) { + const Module *M = getSubmodule(ID); + if (!M || !M->WithCodegen) + return EK_ReplyHazy; + + ModuleFile *MF = ModuleMgr.lookup(M->getASTFile()); + assert(MF); // ? + if (MF->Kind == ModuleKind::MK_MainFile) + return EK_Never; + return EK_Always; +} + Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) { return DecodeSelector(getGlobalSelectorID(M, LocalID)); } Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1044,6 +1044,7 @@ RECORD(IDENTIFIER_OFFSET); RECORD(IDENTIFIER_TABLE); RECORD(EAGERLY_DESERIALIZED_DECLS); + RECORD(MODULAR_CODEGEN_DECLS); RECORD(SPECIAL_TYPES); RECORD(STATISTICS); RECORD(TENTATIVE_DEFINITIONS); @@ -2589,6 +2590,7 @@ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ConfigMacrosExh... + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // WithCodegen Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned DefinitionAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); @@ -2681,7 +2683,7 @@ SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit, Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules, Mod->InferExplicitSubmodules, Mod->InferExportWildcard, - Mod->ConfigMacrosExhaustive}; + Mod->ConfigMacrosExhaustive, Mod->WithCodegen}; Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); } @@ -4694,6 +4696,9 @@ if (!EagerlyDeserializedDecls.empty()) Stream.EmitRecord(EAGERLY_DESERIALIZED_DECLS, EagerlyDeserializedDecls); + if (Context.getLangOpts().ModularCodegen) + Stream.EmitRecord(MODULAR_CODEGEN_DECLS, ModularCodegenDecls); + // Write the record containing tentative definitions. if (!TentativeDefinitions.empty()) Stream.EmitRecord(TENTATIVE_DEFINITIONS, TentativeDefinitions); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -2153,7 +2153,7 @@ /// relatively painless since they would presumably only do it for top-level /// decls. static bool isRequiredDecl(const Decl *D, ASTContext &Context, - bool WritingModule) { + bool WritingModule, bool ModularCode) { // An ObjCMethodDecl is never considered as "required" because its // implementation container always is. @@ -2169,7 +2169,7 @@ return false; } - return Context.DeclMustBeEmitted(D); + return Context.DeclMustBeEmitted(D, ModularCode); } void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) { @@ -2213,8 +2213,11 @@ // Note declarations that should be deserialized eagerly so that we can add // them to a record in the AST file later. - if (isRequiredDecl(D, Context, WritingModule)) + if (isRequiredDecl(D, Context, WritingModule, false)) EagerlyDeserializedDecls.push_back(ID); + else if (Context.getLangOpts().ModularCodegen && WritingModule && + isRequiredDecl(D, Context, true, true)) + ModularCodegenDecls.push_back(ID); } void ASTRecordWriter::AddFunctionDefinition(const FunctionDecl *FD) { Index: test/Modules/Inputs/codegen/bar.h =================================================================== --- /dev/null +++ test/Modules/Inputs/codegen/bar.h @@ -0,0 +1,2 @@ +#include "foo.h" +inline void bar() { foo(); } Index: test/Modules/Inputs/codegen/bar.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/codegen/bar.modulemap @@ -0,0 +1 @@ +module bar { header "bar.h" } Index: test/Modules/Inputs/codegen/foo.h =================================================================== --- /dev/null +++ test/Modules/Inputs/codegen/foo.h @@ -0,0 +1,10 @@ +void f1(int&); +static void f2() { } +inline void foo() { + static int i; + f1(i); + f2(); +} +inline void foo2() { +} +void foo_ext() {} Index: test/Modules/Inputs/codegen/foo.modulemap =================================================================== --- /dev/null +++ test/Modules/Inputs/codegen/foo.modulemap @@ -0,0 +1 @@ +module foo { header "foo.h" } Index: test/Modules/Inputs/codegen/use.cpp =================================================================== --- /dev/null +++ test/Modules/Inputs/codegen/use.cpp @@ -0,0 +1,2 @@ +#include "bar.h" +int main() { bar(); } Index: test/Modules/codegen.test =================================================================== --- /dev/null +++ test/Modules/codegen.test @@ -0,0 +1,64 @@ +RUN: rm -rf %t + +RUN: %clang_cc1 -fmodule-codegen -x c++ -fmodules -emit-module -fmodule-name=foo %S/Inputs/codegen/foo.modulemap -o %t/foo.pcm +RUN: %clang_cc1 -fmodule-codegen -x c++ -fmodules -emit-module -fmodule-name=bar %S/Inputs/codegen/bar.modulemap -o %t/bar.pcm -fmodule-file=%t/foo.pcm + +RUN: %clang_cc1 -emit-llvm %t/foo.pcm -o - | FileCheck --check-prefix=FOO %s +RUN: %clang_cc1 -emit-llvm %t/bar.pcm -o - -fmodule-file=%t/foo.pcm | FileCheck --check-prefix=BAR-CMN --check-prefix=BAR %s +RUN: %clang_cc1 -fmodules -fmodule-file=%t/foo.pcm -fmodule-file=%t/bar.pcm %S/Inputs/codegen/use.cpp -emit-llvm -o - | FileCheck --check-prefix=USE-CMN --check-prefix=USE %s + +RUN: %clang_cc1 -O2 -disable-llvm-passes -emit-llvm %t/foo.pcm -o - | FileCheck --check-prefix=FOO %s +RUN: %clang_cc1 -O2 -disable-llvm-passes -emit-llvm %t/bar.pcm -o - -fmodule-file=%t/foo.pcm | FileCheck --check-prefix=BAR-CMN --check-prefix=BAR-OPT %s +RUN: %clang_cc1 -O2 -disable-llvm-passes -fmodules -fmodule-file=%t/foo.pcm -fmodule-file=%t/bar.pcm %S/Inputs/codegen/use.cpp -emit-llvm -o - | FileCheck --check-prefix=USE-CMN --check-prefix=USE-OPT %s + +FOO-NOT: comdat +FOO: $_Z3foov = comdat any +FOO: $_Z4foo2v = comdat any +FOO: $_ZZ3foovE1i = comdat any +FOO: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat +FOO-NOT: {{comdat|define|declare}} +FOO: define void @_Z7foo_extv() +FOO-NOT: {{define|declare}} +FOO: define weak_odr void @_Z3foov() #{{[0-9]+}} comdat +FOO-NOT: {{define|declare}} +FOO: declare void @_Z2f1Ri(i32* +FOO-NOT: {{define|declare}} + +FIXME: this internal function should be weak_odr, comdat, and with a new mangling +FOO: define internal void @_ZL2f2v() #{{[0-9]+}} +FOO-NOT: {{define|declare}} + +FOO: define weak_odr void @_Z4foo2v() #{{[0-9]+}} comdat +FOO-NOT: {{define|declare}} + + +BAR-CMN-NOT: comdat +BAR-CMN: $_Z3barv = comdat any +BAR-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat +BAR-CMN-NOT: {{comdat|define|declare}} +BAR-CMN: define weak_odr void @_Z3barv() #{{[0-9]+}} comdat +BAR-CMN-NOT: {{define|declare}} +BAR: declare void @_Z3foov() +Include all the available_externally definitions required for bar (foo -> f2) +BAR-OPT: define available_externally void @_Z3foov() +BAR-CMN-NOT: {{define|declare}} +BAR-OPT: declare void @_Z2f1Ri(i32* +BAR-OPT-NOT: {{define|declare}} +BAR-OPT: define available_externally void @_ZL2f2v() +BAR-OPT-NOT: {{define|declare}} + + +USE-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat +USE-CMN-NOT: {{comdat|define|declare}} +USE-CMN: define i32 @main() +USE-CMN-NOT: {{define|declare}} +USE: declare void @_Z3barv() +Include all the available_externally definitions required for main (bar -> foo -> f2) +USE-OPT: define available_externally void @_Z3barv() +USE-CMN-NOT: {{define|declare}} +USE-OPT: define available_externally void @_Z3foov() +USE-OPT-NOT: {{define|declare}} +USE-OPT: declare void @_Z2f1Ri(i32* +USE-OPT-NOT: {{define|declare}} +USE-OPT: define available_externally void @_ZL2f2v() +USE-OPT-NOT: {{define|declare}}