From 3b75c09ec6e3dbf1722c6aa551b51202f2e8c22d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 20 Jul 2016 19:10:16 +0000 Subject: [PATCH] [modules] Don't emit initializers for VarDecls within a module eagerly whenever we first touch any part of that module. Instead, defer them until the first time that module is (transitively) imported. The initializer step for a module then recursively initializes modules that its own headers imported. For example, this avoids running the global initializer in programs that don't actually use iostreams, but do use other parts of the standard library. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@276159 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 23 +++++++ include/clang/Sema/Sema.h | 8 ++- include/clang/Serialization/ASTBitCodes.h | 3 + lib/AST/ASTContext.cpp | 63 +++++++++++++++++++ lib/CodeGen/CodeGenModule.cpp | 16 +++-- lib/Sema/SemaDecl.cpp | 35 +++++++---- lib/Sema/SemaLookup.cpp | 5 +- lib/Serialization/ASTReader.cpp | 7 +++ lib/Serialization/ASTReaderDecl.cpp | 14 +++-- lib/Serialization/ASTWriter.cpp | 23 ++++++- lib/Serialization/ASTWriterDecl.cpp | 10 +-- test/Modules/Inputs/unused-global-init/init.h | 1 + .../unused-global-init/module.modulemap | 3 + .../Modules/Inputs/unused-global-init/other.h | 1 + .../Inputs/unused-global-init/unused.h | 1 + test/Modules/Inputs/unused-global-init/used.h | 2 + test/Modules/odr.cpp | 8 +-- test/Modules/templates.mm | 8 +-- test/Modules/unused-global-init.cpp | 37 +++++++++++ 19 files changed, 229 insertions(+), 39 deletions(-) create mode 100644 test/Modules/Inputs/unused-global-init/init.h create mode 100644 test/Modules/Inputs/unused-global-init/module.modulemap create mode 100644 test/Modules/Inputs/unused-global-init/other.h create mode 100644 test/Modules/Inputs/unused-global-init/unused.h create mode 100644 test/Modules/Inputs/unused-global-init/used.h create mode 100644 test/Modules/unused-global-init.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 1d223f47a9..a55762622e 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -312,6 +312,18 @@ class ASTContext : public RefCountedBase { /// definitions of that entity. llvm::DenseMap> MergedDefModules; + /// \brief Initializers for a module, in order. Each Decl will be either + /// something that has a semantic effect on startup (such as a variable with + /// a non-constant initializer), or an ImportDecl (which recursively triggers + /// initialization of another module). + struct PerModuleInitializers { + llvm::SmallVector Initializers; + llvm::SmallVector LazyInitializers; + + void resolve(ASTContext &Ctx); + }; + llvm::DenseMap ModuleInitializers; + public: /// \brief A type synonym for the TemplateOrInstantiation mapping. typedef llvm::PointerUnion @@ -883,6 +895,17 @@ public: return MergedIt->second; } + /// Add a declaration to the list of declarations that are initialized + /// for a module. This will typically be a global variable (with internal + /// linkage) that runs module initializers, such as the iostream initializer, + /// or an ImportDecl nominating another module that has initializers. + void addModuleInitializer(Module *M, Decl *Init); + + void addLazyModuleInitializers(Module *M, ArrayRef IDs); + + /// Get the initializations to perform when importing a module, if any. + ArrayRef getModuleInitializers(Module *M); + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; } ExternCContextDecl *getExternCContextDecl() const; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 25aa805dac..93c413597a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1390,8 +1390,14 @@ private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); + struct ModuleScope { + clang::Module *Module; + VisibleModuleSet OuterVisibleModules; + }; + /// The modules we're currently parsing. + llvm::SmallVector ModuleScopes; + VisibleModuleSet VisibleModules; - llvm::SmallVector VisibleModulesStack; Module *CachedFakeTopLevelModule; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 79c6a06222..4ce7e21bc4 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -684,6 +684,9 @@ namespace clang { /// \brief Specifies a header that is private to this submodule but /// must be textually included. SUBMODULE_PRIVATE_TEXTUAL_HEADER = 15, + /// \brief Specifies some declarations with initializers that must be + /// emitted to initialize the module. + SUBMODULE_INITIALIZERS = 16, }; /// \brief Record types used within a comments block. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e54c88fc05..fadcda0c4b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -901,6 +901,67 @@ void ASTContext::deduplicateMergedDefinitonsFor(NamedDecl *ND) { Merged.erase(std::remove(Merged.begin(), Merged.end(), nullptr), Merged.end()); } +void ASTContext::PerModuleInitializers::resolve(ASTContext &Ctx) { + if (LazyInitializers.empty()) + return; + + auto *Source = Ctx.getExternalSource(); + assert(Source && "lazy initializers but no external source"); + + auto LazyInits = std::move(LazyInitializers); + LazyInitializers.clear(); + + for (auto ID : LazyInits) + Initializers.push_back(Source->GetExternalDecl(ID)); + + assert(LazyInitializers.empty() && + "GetExternalDecl for lazy module initializer added more inits"); +} + +void ASTContext::addModuleInitializer(Module *M, Decl *D) { + // One special case: if we add a module initializer that imports another + // module, and that module's only initializer is an ImportDecl, simplify. + if (auto *ID = dyn_cast(D)) { + auto It = ModuleInitializers.find(ID->getImportedModule()); + + // Maybe the ImportDecl does nothing at all. (Common case.) + if (It == ModuleInitializers.end()) + return; + + // Maybe the ImportDecl only imports another ImportDecl. + auto &Imported = *It->second; + if (Imported.Initializers.size() + Imported.LazyInitializers.size() == 1) { + Imported.resolve(*this); + auto *OnlyDecl = Imported.Initializers.front(); + if (isa(OnlyDecl)) + D = OnlyDecl; + } + } + + auto *&Inits = ModuleInitializers[M]; + if (!Inits) + Inits = new (*this) PerModuleInitializers; + Inits->Initializers.push_back(D); +} + +void ASTContext::addLazyModuleInitializers(Module *M, ArrayRef IDs) { + auto *&Inits = ModuleInitializers[M]; + if (!Inits) + Inits = new (*this) PerModuleInitializers; + Inits->LazyInitializers.insert(Inits->LazyInitializers.end(), + IDs.begin(), IDs.end()); +} + +ArrayRef ASTContext::getModuleInitializers(Module *M) { + auto It = ModuleInitializers.find(M); + if (It == ModuleInitializers.end()) + return None; + + auto *Inits = It->second; + Inits->resolve(*this); + return Inits->Initializers; +} + ExternCContextDecl *ASTContext::getExternCContextDecl() const { if (!ExternCContext) ExternCContext = ExternCContextDecl::Create(*this, getTranslationUnitDecl()); @@ -8575,6 +8636,8 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return !D->getDeclContext()->isDependentContext(); else if (isa(D)) return !D->getDeclContext()->isDependentContext(); + else if (isa(D)) + return true; else return false; diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 0ef5b74124..f73e85dd42 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -3910,13 +3910,19 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::Import: { auto *Import = cast(D); - // Ignore import declarations that come from imported modules. - if (Import->getImportedOwningModule()) + // If we've already imported this module, we're done. + if (!ImportedModules.insert(Import->getImportedModule())) break; - if (CGDebugInfo *DI = getModuleDebugInfo()) - DI->EmitImportDecl(*Import); - ImportedModules.insert(Import->getImportedModule()); + // Emit debug information for direct imports. + if (!Import->getImportedOwningModule()) { + if (CGDebugInfo *DI = getModuleDebugInfo()) + DI->EmitImportDecl(*Import); + } + + // Emit the module initializers. + for (auto *D : Context.getModuleInitializers(Import->getImportedModule())) + EmitTopLevelDecl(D); break; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 32c02e1771..683905b148 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -10473,6 +10473,11 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // Require the destructor. if (const RecordType *recordType = baseType->getAs()) FinalizeVarWithDestructor(var, recordType); + + // If this variable must be emitted, add it as an initializer for the current + // module. + if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, var); } /// \brief Determines if a variable's alignment is dependent. @@ -15095,11 +15100,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, IdentifierLocs.push_back(Path[I].second); } - ImportDecl *Import = ImportDecl::Create(Context, - Context.getTranslationUnitDecl(), + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + ImportDecl *Import = ImportDecl::Create(Context, TU, AtLoc.isValid()? AtLoc : ImportLoc, Mod, IdentifierLocs); - Context.getTranslationUnitDecl()->addDecl(Import); + if (!ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, Import); + TU->addDecl(Import); return Import; } @@ -15115,13 +15122,7 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { TUKind == TU_Module && getSourceManager().isWrittenInMainFile(DirectiveLoc); - // Similarly, if we're in the implementation of a module, don't - // synthesize an illegal module import. FIXME: Why not? - bool ShouldAddImport = - !IsInModuleIncludes && - (getLangOpts().CompilingModule || - getLangOpts().CurrentModule.empty() || - getLangOpts().CurrentModule != Mod->getTopLevelModuleName()); + bool ShouldAddImport = !IsInModuleIncludes; // If this module import was due to an inclusion directive, create an // implicit import declaration to capture it in the AST. @@ -15130,6 +15131,8 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, DirectiveLoc, Mod, DirectiveLoc); + if (!ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, ImportD); TU->addDecl(ImportD); Consumer.HandleImplicitImportDecl(ImportD); } @@ -15141,8 +15144,11 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) { checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); + ModuleScopes.push_back({}); + ModuleScopes.back().Module = Mod; if (getLangOpts().ModulesLocalVisibility) - VisibleModulesStack.push_back(std::move(VisibleModules)); + ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules); + VisibleModules.setVisible(Mod, DirectiveLoc); } @@ -15150,8 +15156,11 @@ void Sema::ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod) { checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); if (getLangOpts().ModulesLocalVisibility) { - VisibleModules = std::move(VisibleModulesStack.back()); - VisibleModulesStack.pop_back(); + assert(!ModuleScopes.empty() && ModuleScopes.back().Module == Mod && + "left the wrong module scope"); + VisibleModules = std::move(ModuleScopes.back().OuterVisibleModules); + ModuleScopes.pop_back(); + VisibleModules.setVisible(Mod, DirectiveLoc); // Leaving a module hides namespace names, so our visible namespace cache // is now out of date. diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index a41eef39ed..19df1e3042 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1367,8 +1367,9 @@ Module *Sema::getOwningModule(Decl *Entity) { auto &SrcMgr = PP.getSourceManager(); SourceLocation StartLoc = SrcMgr.getLocForStartOfFile(SrcMgr.getMainFileID()); - auto &TopLevel = - VisibleModulesStack.empty() ? VisibleModules : VisibleModulesStack[0]; + auto &TopLevel = ModuleScopes.empty() + ? VisibleModules + : ModuleScopes[0].OuterVisibleModules; TopLevel.setVisible(CachedFakeTopLevelModule, StartLoc); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b35bd7bd32..6bfae07b82 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4673,6 +4673,13 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { UnresolvedModuleRefs.push_back(Unresolved); break; } + + case SUBMODULE_INITIALIZERS: + SmallVector Inits; + for (auto &ID : Record) + Inits.push_back(getGlobalDeclID(F, ID)); + Context.addLazyModuleInitializers(CurrentModule, Inits); + break; } } } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 9094a01ad7..184e7efe84 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -2493,10 +2493,16 @@ inline void ASTReader::LoadedDecl(unsigned Index, Decl *D) { /// This routine should return true for anything that might affect /// code generation, e.g., inline function definitions, Objective-C /// declarations with metadata, etc. -static bool isConsumerInterestedIn(Decl *D, bool HasBody) { +static bool isConsumerInterestedIn(ASTContext &Ctx, Decl *D, bool HasBody) { // An ObjCMethodDecl is never considered as "interesting" because its // implementation container always is. + // An ImportDecl or VarDecl imported from a module will get emitted when + // we import the relevant module. + if ((isa(D) || isa(D)) && Ctx.DeclMustBeEmitted(D) && + D->getImportedOwningModule()) + return false; + if (isa(D) || isa(D) || isa(D) || @@ -3473,7 +3479,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // AST consumer might need to know about, queue it. // We don't pass it to the consumer immediately because we may be in recursive // loading, and some declarations may still be initializing. - if (isConsumerInterestedIn(D, Reader.hasPendingBody())) + if (isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) InterestingDecls.push_back(D); return D; @@ -3488,7 +3494,7 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { auto UpdateOffsets = std::move(UpdI->second); DeclUpdateOffsets.erase(UpdI); - bool WasInteresting = isConsumerInterestedIn(D, false); + bool WasInteresting = isConsumerInterestedIn(Context, D, false); for (auto &FileAndOffset : UpdateOffsets) { ModuleFile *F = FileAndOffset.first; uint64_t Offset = FileAndOffset.second; @@ -3509,7 +3515,7 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { // We might have made this declaration interesting. If so, remember that // we need to hand it off to the consumer. if (!WasInteresting && - isConsumerInterestedIn(D, Reader.hasPendingBody())) { + isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) { InterestingDecls.push_back(D); WasInteresting = true; } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 314eee0d43..dbba5fd191 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1017,6 +1017,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(SUBMODULE_PRIVATE_HEADER); RECORD(SUBMODULE_TEXTUAL_HEADER); RECORD(SUBMODULE_PRIVATE_TEXTUAL_HEADER); + RECORD(SUBMODULE_INITIALIZERS); // Comments Block. BLOCK(COMMENTS_BLOCK); @@ -2417,7 +2418,9 @@ unsigned ASTWriter::getLocalOrImportedSubmoduleID(Module *Mod) { if (Known != SubmoduleIDs.end()) return Known->second; - if (Mod->getTopLevelModule() != WritingModule) + auto *Top = Mod->getTopLevelModule(); + if (Top != WritingModule && + !Top->fullModuleNameIs(StringRef(getLangOpts().CurrentModule))) return 0; return SubmoduleIDs[Mod] = NextSubmoduleID++; @@ -2649,6 +2652,13 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Stream.EmitRecordWithBlob(ConfigMacroAbbrev, Record, CM); } + // Emit the initializers, if any. + RecordData Inits; + for (Decl *D : Context->getModuleInitializers(Mod)) + Inits.push_back(GetDeclRef(D)); + if (!Inits.empty()) + Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits); + // Queue up the submodules of this module. for (auto *M : Mod->submodules()) Q.push(M); @@ -4514,6 +4524,17 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // If we're emitting a module, write out the submodule information. if (WritingModule) WriteSubmodules(WritingModule); + else if (!getLangOpts().CurrentModule.empty()) { + // If we're building a PCH in the implementation of a module, we may need + // the description of the current module. + // + // FIXME: We may need other modules that we did not load from an AST file, + // such as if a module declares a 'conflicts' on a different module. + Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule( + getLangOpts().CurrentModule); + if (M && !M->IsFromModuleFile) + WriteSubmodules(M); + } Stream.EmitRecord(SPECIAL_TYPES, SpecialTypes); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 50c538558e..8205d60ca7 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -2122,11 +2122,11 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context, D->hasAttr()) return true; - // ImportDecl is used by codegen to determine the set of imported modules to - // search for inputs for automatic linking; include it if it has a semantic - // effect. - if (isa(D) && !WritingModule) - return true; + if (WritingModule && (isa(D) || isa(D))) { + // These declarations are part of the module initializer, and are emitted + // if and when the module is imported, rather than being emitted eagerly. + return false; + } return Context.DeclMustBeEmitted(D); } diff --git a/test/Modules/Inputs/unused-global-init/init.h b/test/Modules/Inputs/unused-global-init/init.h new file mode 100644 index 0000000000..29a932ae65 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/init.h @@ -0,0 +1 @@ +struct Init { Init(); ~Init(); } init; diff --git a/test/Modules/Inputs/unused-global-init/module.modulemap b/test/Modules/Inputs/unused-global-init/module.modulemap new file mode 100644 index 0000000000..c40f0efeb5 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/module.modulemap @@ -0,0 +1,3 @@ +module used { header "used.h" } +module unused { header "unused.h" } +module init { module a { header "init.h" } module b { header "other.h" } } diff --git a/test/Modules/Inputs/unused-global-init/other.h b/test/Modules/Inputs/unused-global-init/other.h new file mode 100644 index 0000000000..c6be1ad4c4 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/other.h @@ -0,0 +1 @@ +// other.h diff --git a/test/Modules/Inputs/unused-global-init/unused.h b/test/Modules/Inputs/unused-global-init/unused.h new file mode 100644 index 0000000000..06c2a44ba2 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/unused.h @@ -0,0 +1 @@ +// unused.h diff --git a/test/Modules/Inputs/unused-global-init/used.h b/test/Modules/Inputs/unused-global-init/used.h new file mode 100644 index 0000000000..689a13f4c8 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/used.h @@ -0,0 +1,2 @@ +// used.h +#include "init.h" diff --git a/test/Modules/odr.cpp b/test/Modules/odr.cpp index 30143968ff..9cdbb4f080 100644 --- a/test/Modules/odr.cpp +++ b/test/Modules/odr.cpp @@ -15,9 +15,9 @@ bool b = F{0} == F{1}; int x = f() + g(); // expected-note@a.h:5 {{definition has no member 'e2'}} -// expected-note@b.h:3 {{declaration of 'f' does not match}} -// expected-note@b.h:1 {{definition has no member 'n'}} +// expected-note@a.h:3 {{declaration of 'f' does not match}} +// expected-note@a.h:1 {{definition has no member 'm'}} // expected-error@b.h:5 {{'E::e2' from module 'b' is not present in definition of 'E' in module 'a'}} -// expected-error@a.h:3 {{'Y::f' from module 'a' is not present in definition of 'Y' in module 'b'}} -// expected-error@a.h:2 {{'Y::n' from module 'a' is not present in definition of 'Y' in module 'b'}} +// expected-error@b.h:3 {{'Y::f' from module 'b' is not present in definition of 'Y' in module 'a'}} +// expected-error@b.h:2 {{'Y::m' from module 'b' is not present in definition of 'Y' in module 'a'}} diff --git a/test/Modules/templates.mm b/test/Modules/templates.mm index 190084ad2b..cd80e2480b 100644 --- a/test/Modules/templates.mm +++ b/test/Modules/templates.mm @@ -12,10 +12,10 @@ void testInlineRedeclEarly() { @import templates_right; -// CHECK-DAG: @list_left = global %class.List { %"struct.List::node"* null, i32 8 }, align 8 -// CHECK-DAG: @list_right = global %class.List { %"struct.List::node"* null, i32 12 }, align 8 -// CHECK-DAG: @_ZZ15testMixedStructvE1l = {{.*}} constant %class.List { %{{.*}}* null, i32 1 }, align 8 -// CHECK-DAG: @_ZZ15testMixedStructvE1r = {{.*}} constant %class.List { %{{.*}}* null, i32 2 }, align 8 +// CHECK-DAG: @list_left = global %[[LIST:.*]] { %[[LISTNODE:.*]]* null, i32 8 }, align 8 +// CHECK-DAG: @list_right = global %[[LIST]] { %[[LISTNODE]]* null, i32 12 }, align 8 +// CHECK-DAG: @_ZZ15testMixedStructvE1l = {{.*}} constant %[[LIST]] { %{{.*}}* null, i32 1 }, align 8 +// CHECK-DAG: @_ZZ15testMixedStructvE1r = {{.*}} constant %[[LIST]] { %{{.*}}* null, i32 2 }, align 8 // CHECK-DAG: @_ZN29WithUndefinedStaticDataMemberIA_iE9undefinedE = external global void testTemplateClasses() { diff --git a/test/Modules/unused-global-init.cpp b/test/Modules/unused-global-init.cpp new file mode 100644 index 0000000000..3bd7a7f5a6 --- /dev/null +++ b/test/Modules/unused-global-init.cpp @@ -0,0 +1,37 @@ +// RUN: rm -rf %t +// +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=init -o %t/init.pcm +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=unused -o %t/unused.pcm -fmodule-file=%t/init.pcm +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=used -o %t/used.pcm -fmodule-file=%t/init.pcm +// +// No module file: init.h performs init. +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DINIT | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DUSED | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DOTHER -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s +// +// With module files: if there is a transitive import of any part of the +// module, we run its global initializers (even if the imported piece is not +// visible here). +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DINIT | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DOTHER | FileCheck --check-prefix=CHECK-NO-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUSED | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s + +#ifdef INIT +#include "init.h" +#endif + +#ifdef OTHER +#include "other.h" +#endif + +#ifdef USED +#include "used.h" +#endif + +#ifdef UNUSED +#include "unused.h" +#endif + +// CHECK-INIT: call {{.*}}@_ZN4InitC +// CHECK-NO-INIT-NOT: call {{.*}}@_ZN4InitC -- 2.50.1