mirror of https://github.com/microsoft/clang.git
[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 <iostream> 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
This commit is contained in:
parent
bb573cbb0f
commit
3b75c09ec6
|
@ -312,6 +312,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
|||
/// definitions of that entity.
|
||||
llvm::DenseMap<NamedDecl*, llvm::TinyPtrVector<Module*>> 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<Decl*, 4> Initializers;
|
||||
llvm::SmallVector<uint32_t, 4> LazyInitializers;
|
||||
|
||||
void resolve(ASTContext &Ctx);
|
||||
};
|
||||
llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers;
|
||||
|
||||
public:
|
||||
/// \brief A type synonym for the TemplateOrInstantiation mapping.
|
||||
typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>
|
||||
|
@ -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<uint32_t> IDs);
|
||||
|
||||
/// Get the initializations to perform when importing a module, if any.
|
||||
ArrayRef<Decl*> getModuleInitializers(Module *M);
|
||||
|
||||
TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; }
|
||||
|
||||
ExternCContextDecl *getExternCContextDecl() const;
|
||||
|
|
|
@ -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<ModuleScope, 16> ModuleScopes;
|
||||
|
||||
VisibleModuleSet VisibleModules;
|
||||
llvm::SmallVector<VisibleModuleSet, 16> VisibleModulesStack;
|
||||
|
||||
Module *CachedFakeTopLevelModule;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<ImportDecl>(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<ImportDecl>(OnlyDecl))
|
||||
D = OnlyDecl;
|
||||
}
|
||||
}
|
||||
|
||||
auto *&Inits = ModuleInitializers[M];
|
||||
if (!Inits)
|
||||
Inits = new (*this) PerModuleInitializers;
|
||||
Inits->Initializers.push_back(D);
|
||||
}
|
||||
|
||||
void ASTContext::addLazyModuleInitializers(Module *M, ArrayRef<uint32_t> IDs) {
|
||||
auto *&Inits = ModuleInitializers[M];
|
||||
if (!Inits)
|
||||
Inits = new (*this) PerModuleInitializers;
|
||||
Inits->LazyInitializers.insert(Inits->LazyInitializers.end(),
|
||||
IDs.begin(), IDs.end());
|
||||
}
|
||||
|
||||
ArrayRef<Decl*> 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<OMPDeclareReductionDecl>(D))
|
||||
return !D->getDeclContext()->isDependentContext();
|
||||
else if (isa<ImportDecl>(D))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
|
|
|
@ -3910,13 +3910,19 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
|
|||
case Decl::Import: {
|
||||
auto *Import = cast<ImportDecl>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -10473,6 +10473,11 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
|
|||
// Require the destructor.
|
||||
if (const RecordType *recordType = baseType->getAs<RecordType>())
|
||||
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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -4673,6 +4673,13 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
|
|||
UnresolvedModuleRefs.push_back(Unresolved);
|
||||
break;
|
||||
}
|
||||
|
||||
case SUBMODULE_INITIALIZERS:
|
||||
SmallVector<uint32_t, 16> Inits;
|
||||
for (auto &ID : Record)
|
||||
Inits.push_back(getGlobalDeclID(F, ID));
|
||||
Context.addLazyModuleInitializers(CurrentModule, Inits);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ImportDecl>(D) || isa<VarDecl>(D)) && Ctx.DeclMustBeEmitted(D) &&
|
||||
D->getImportedOwningModule())
|
||||
return false;
|
||||
|
||||
if (isa<FileScopeAsmDecl>(D) ||
|
||||
isa<ObjCProtocolDecl>(D) ||
|
||||
isa<ObjCImplDecl>(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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -2122,11 +2122,11 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
|
|||
D->hasAttr<OMPDeclareTargetDeclAttr>())
|
||||
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<ImportDecl>(D) && !WritingModule)
|
||||
return true;
|
||||
if (WritingModule && (isa<VarDecl>(D) || isa<ImportDecl>(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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
struct Init { Init(); ~Init(); } init;
|
|
@ -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" } }
|
|
@ -0,0 +1 @@
|
|||
// other.h
|
|
@ -0,0 +1 @@
|
|||
// unused.h
|
|
@ -0,0 +1,2 @@
|
|||
// used.h
|
||||
#include "init.h"
|
|
@ -15,9 +15,9 @@ bool b = F<int>{0} == F<int>{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'}}
|
||||
|
|
|
@ -12,10 +12,10 @@ void testInlineRedeclEarly() {
|
|||
|
||||
@import templates_right;
|
||||
|
||||
// CHECK-DAG: @list_left = global %class.List { %"struct.List<int>::node"* null, i32 8 }, align 8
|
||||
// CHECK-DAG: @list_right = global %class.List { %"struct.List<int>::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() {
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue