diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -473,6 +473,9 @@ }; llvm::DenseMap ModuleInitializers; + /// For module code-gen cases, this is the top-level module we are building. + Module *TopLevelModule = nullptr; + static constexpr unsigned ConstantArrayTypesLog2InitSize = 8; static constexpr unsigned GeneralTypesLog2InitSize = 9; static constexpr unsigned FunctionProtoTypesLog2InitSize = 12; @@ -1076,6 +1079,12 @@ /// Get the initializations to perform when importing a module, if any. ArrayRef getModuleInitializers(Module *M); + /// Set the (C++20) module we are building. + void setModuleForCodeGen(Module *M) { TopLevelModule = M; } + + /// Get module under construction, nullptr if this is not a C++20 module. + Module *getModuleForCodeGen() const { return TopLevelModule; } + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl->getMostRecentDecl(); } diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -665,6 +665,18 @@ Module *findSubmodule(StringRef Name) const; Module *findOrInferSubmodule(StringRef Name); + /// Get the Global Module Fragment (sub-module) for this module, it there is + /// one. + /// + /// \returns The GMF sub-module if found, or NULL otherwise. + Module *getGlobalModuleFragment() { return findSubmodule(""); } + + /// Get the Private Module Fragment (sub-module) for this module, it there is + /// one. + /// + /// \returns The PMF sub-module if found, or NULL otherwise. + Module *getPrivateModuleFragment() { return findSubmodule(""); } + /// Determine whether the specified module would be visible to /// a lookup at the end of this module. /// diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2281,6 +2281,11 @@ return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; } + /// Is the module scope we are an interface? + bool currentModuleIsInterface() const { + return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface; + } + /// Get the module owning an entity. Module *getOwningModule(const Decl *Entity) { return Entity->getOwningModule(); diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -618,6 +618,130 @@ CXXThreadLocals.clear(); } +/* Build the initializer for a C++20 module: + This is arranged to be run only once regardless of how many times the module + might be included transitively. This arranged by using a control variable. + + First we call any initializers for imported modules. + We then call initializers for the Global Module Fragment (if present) + We then call initializers for the current module. + We then call initializers for the Private Module Fragment (if present) +*/ + +void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) { + while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) + CXXGlobalInits.pop_back(); + + // We create the function, even if it is empty, since an importer of this + // module will refer to it unconditionally (for the current implementation + // there is no way for the importer to know that an importee does not need + // an initializer to be run). + + // Module initializers for imported modules are emitted first. + // Collect the modules that we import + SmallVector AllImports; + // Ones that we export + for (auto I : Primary->Exports) + AllImports.push_back(I.getPointer()); + // Ones that we only import. + for (Module *M : Primary->Imports) + AllImports.push_back(M); + + SmallVector ModuleInits; + for (Module *M : AllImports) { + // No Itanium initializer in module map modules. + if (M->isModuleMapModule()) + continue; // TODO: warn of mixed use of module map modules and C++20? + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + cast(getCXXABI().getMangleContext()) + .mangleModuleInitializer(M, Out); + } + assert(!GetGlobalValue(FnName.str()) && + "We should only have one use of the initializer call"); + llvm::Function *Fn = llvm::Function::Create( + FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule()); + ModuleInits.push_back(Fn); + } + AllImports.clear(); + + // Add any initializers with specified priority; this uses the same approach + // as EmitCXXGlobalInitFunc(). + if (!PrioritizedCXXGlobalInits.empty()) { + SmallVector LocalCXXGlobalInits; + llvm::array_pod_sort(PrioritizedCXXGlobalInits.begin(), + PrioritizedCXXGlobalInits.end()); + for (SmallVectorImpl::iterator + I = PrioritizedCXXGlobalInits.begin(), + E = PrioritizedCXXGlobalInits.end(); + I != E;) { + SmallVectorImpl::iterator PrioE = + std::upper_bound(I + 1, E, *I, GlobalInitPriorityCmp()); + + for (; I < PrioE; ++I) + ModuleInits.push_back(I->second); + } + PrioritizedCXXGlobalInits.clear(); + } + + // Now append the ones without specified priority. + for (auto F : CXXGlobalInits) + ModuleInits.push_back(F); + CXXGlobalInits.clear(); + + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); + + // We now build the initializer for this module, which has a mangled name + // as per the Itanium ABI . The action of the initializer is guarded so that + // each init is run just once (even though a module might be imported + // multiple times via nested use). + llvm::Function *Fn; + llvm::GlobalVariable *Guard = nullptr; + { + SmallString<256> InitFnName; + llvm::raw_svector_ostream Out(InitFnName); + cast(getCXXABI().getMangleContext()) + .mangleModuleInitializer(Primary, Out); + Fn = CreateGlobalInitOrCleanUpFunction( + FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false, + llvm::GlobalVariable::ExternalLinkage); + + Guard = new llvm::GlobalVariable(getModule(), Int8Ty, /*isConstant=*/false, + llvm::GlobalVariable::InternalLinkage, + llvm::ConstantInt::get(Int8Ty, 0), + InitFnName.str() + "__in_chrg"); + } + CharUnits GuardAlign = CharUnits::One(); + Guard->setAlignment(GuardAlign.getAsAlign()); + + CodeGenFunction(*this).GenerateCXXGlobalInitFunc( + Fn, ModuleInits, ConstantAddress(Guard, Int8Ty, GuardAlign)); + // We allow for the case that a module object is added to a linked binary + // without a specific call to the the initializer. This also ensure that + // implementation partition initializers are called when the partition + // is not imported as an interface. + AddGlobalCtor(Fn); + + // See the comment in EmitCXXGlobalInitFunc about OpenCL global init + // functions. + if (getLangOpts().OpenCL) { + GenKernelArgMetadata(Fn); + Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL); + } + + assert(!getLangOpts().CUDA || !getLangOpts().CUDAIsDevice || + getLangOpts().GPUAllowDeviceInit); + if (getLangOpts().HIP && getLangOpts().CUDAIsDevice) { + Fn->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL); + Fn->addFnAttr("device-init"); + } + + ModuleInits.clear(); +} + static SmallString<128> getTransformedFileName(llvm::Module &M) { SmallString<128> FileName = llvm::sys::path::filename(M.getName()); @@ -650,7 +774,29 @@ while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) CXXGlobalInits.pop_back(); - if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty()) + // When we import C++20 modules, we must run their initializers first. + SmallVector ModuleInits; + if (CXX20ModuleInits) + for (Module *M : ImportedModules) { + // No Itanium initializer in module map modules. + if (M->isModuleMapModule()) + continue; + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + cast(getCXXABI().getMangleContext()) + .mangleModuleInitializer(M, Out); + } + assert(!GetGlobalValue(FnName.str()) && + "We should only have one use of the initializer call"); + llvm::Function *Fn = llvm::Function::Create( + FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule()); + ModuleInits.push_back(Fn); + } + + if (ModuleInits.empty() && CXXGlobalInits.empty() && + PrioritizedCXXGlobalInits.empty()) return; llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); @@ -676,6 +822,13 @@ llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( FTy, "_GLOBAL__I_" + getPrioritySuffix(Priority), FI); + // Prepend the module inits to the highest priority set. + if (!ModuleInits.empty()) { + for (auto F : ModuleInits) + LocalCXXGlobalInits.push_back(F); + ModuleInits.clear(); + } + for (; I < PrioE; ++I) LocalCXXGlobalInits.push_back(I->second); @@ -685,17 +838,33 @@ PrioritizedCXXGlobalInits.clear(); } - if (getCXXABI().useSinitAndSterm() && CXXGlobalInits.empty()) + if (getCXXABI().useSinitAndSterm() && ModuleInits.empty() && + CXXGlobalInits.empty()) return; + for (auto F : CXXGlobalInits) + ModuleInits.push_back(F); + CXXGlobalInits.clear(); + // Include the filename in the symbol name. Including "sub_" matches gcc // and makes sure these symbols appear lexicographically behind the symbols // with priority emitted above. - llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( - FTy, llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())), - FI); - - CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); + llvm::Function *Fn; + if (CXX20ModuleInits && getContext().getModuleForCodeGen()) { + SmallString<256> InitFnName; + llvm::raw_svector_ostream Out(InitFnName); + cast(getCXXABI().getMangleContext()) + .mangleModuleInitializer(getContext().getModuleForCodeGen(), Out); + Fn = CreateGlobalInitOrCleanUpFunction( + FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false, + llvm::GlobalVariable::ExternalLinkage); + } else + Fn = CreateGlobalInitOrCleanUpFunction( + FTy, + llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())), + FI); + + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits); AddGlobalCtor(Fn); // In OpenCL global init functions must be converted to kernels in order to @@ -718,7 +887,7 @@ Fn->addFnAttr("device-init"); } - CXXGlobalInits.clear(); + ModuleInits.clear(); } void CodeGenModule::EmitCXXGlobalCleanUpFunc() { diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -303,7 +303,7 @@ std::unique_ptr ABI; llvm::LLVMContext &VMContext; std::string ModuleNameHash; - + bool CXX20ModuleInits = false; std::unique_ptr TBAA; mutable std::unique_ptr TheTargetCodeGenInfo; @@ -1599,6 +1599,9 @@ /// Emit the function that initializes C++ thread_local variables. void EmitCXXThreadLocalInitFunc(); + /// Emit the function that initializes global variables for a C++ Module. + void EmitCXXModuleInitFunc(clang::Module *Primary); + /// Emit the function that initializes C++ globals. void EmitCXXGlobalInitFunc(); @@ -1666,6 +1669,9 @@ /// Emit the llvm.used and llvm.compiler.used metadata. void emitLLVMUsed(); + /// For C++20 Itanium ABI, emit the initializers for the module. + void EmitModuleInitializers(clang::Module *Primary); + /// Emit the link options introduced by imported modules. void EmitModuleLinkOptions(); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -137,6 +137,13 @@ GlobalsInt8PtrTy = Int8Ty->getPointerTo(DL.getDefaultGlobalsAddressSpace()); ASTAllocaAddressSpace = getTargetCodeGenInfo().getASTAllocaAddressSpace(); + // Build C++20 Module initializers. + // TODO: Add Microsoft here once we know the mangling required for the + // initializers. + CXX20ModuleInits = + LangOpts.CPlusPlusModules && getCXXABI().getMangleContext().getKind() == + ItaniumMangleContext::MK_Itanium; + RuntimeCC = getTargetCodeGenInfo().getABIInfo().getRuntimeCC(); if (LangOpts.ObjC) @@ -511,6 +518,9 @@ } void CodeGenModule::Release() { + Module *Primary = getContext().getModuleForCodeGen(); + if (CXX20ModuleInits && Primary && !Primary->isModuleMapModule()) + EmitModuleInitializers(Primary); EmitDeferred(); DeferredDecls.insert(EmittedDeferredDecls.begin(), EmittedDeferredDecls.end()); @@ -519,7 +529,10 @@ applyGlobalValReplacements(); applyReplacements(); emitMultiVersionFunctions(); - EmitCXXGlobalInitFunc(); + if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition()) + EmitCXXModuleInitFunc(Primary); + else + EmitCXXGlobalInitFunc(); EmitCXXGlobalCleanUpFunc(); registerGlobalDtorsWithAtExit(); EmitCXXThreadLocalInitFunc(); @@ -2502,6 +2515,31 @@ } } +void CodeGenModule::EmitModuleInitializers(clang::Module *Primary) { + // Emit the initializers in the order that sub-modules appear in the + // source, first Global Module Fragments, if present. + if (auto GMF = Primary->getGlobalModuleFragment()) { + for (Decl *D : getContext().getModuleInitializers(GMF)) { + assert(D->getKind() == Decl::Var && "GMF initializer decl is not a var?"); + EmitTopLevelDecl(D); + } + } + // Second any associated with the module, itself. + for (Decl *D : getContext().getModuleInitializers(Primary)) { + // Skip import decls, the inits for those are called explicitly. + if (D->getKind() == Decl::Import) + continue; + EmitTopLevelDecl(D); + } + // Third any associated with the Privat eMOdule Fragment, if present. + if (auto PMF = Primary->getPrivateModuleFragment()) { + for (Decl *D : getContext().getModuleInitializers(PMF)) { + assert(D->getKind() == Decl::Var && "PMF initializer decl is not a var?"); + EmitTopLevelDecl(D); + } + } +} + void CodeGenModule::EmitModuleLinkOptions() { // Collect the set of all of the modules we want to visit to emit link // options, which is essentially the imported modules and all of their @@ -2928,12 +2966,20 @@ // explicitly instantiated, so they should not be emitted eagerly. return false; } - if (const auto *VD = dyn_cast(Global)) + if (const auto *VD = dyn_cast(Global)) { if (Context.getInlineVariableDefinitionKind(VD) == ASTContext::InlineVariableDefinitionKind::WeakUnknown) // A definition of an inline constexpr static data member may change // linkage later if it's redeclared outside the class. return false; + if (CXX20ModuleInits && VD->getOwningModule() && + !VD->getOwningModule()->isModuleMapModule()) { + // For CXX20, module-owned initializers need to be deferred, since it is + // not known at this point if they will be run for the current module or + // as part of the initializer for an imported one. + return false; + } + } // If OpenMP is enabled and threadprivates must be generated like TLS, delay // codegen for global variables, because they may be marked as threadprivate. if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS && @@ -6233,6 +6279,16 @@ DI->EmitImportDecl(*Import); } + // For C++ standard modules we are done - we will call the module + // initializer for imported modules, and that will likewise call those for + // any imports it has. + if (CXX20ModuleInits && Import->getImportedOwningModule() && + !Import->getImportedOwningModule()->isModuleMapModule()) + break; + + // For clang C++ module map modules the initializers for sub-modules are + // emitted here. + // Find all of the submodules and emit the module initializers. llvm::SmallPtrSet Visited; SmallVector Stack; diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp --- a/clang/lib/Parse/ParseAST.cpp +++ b/clang/lib/Parse/ParseAST.cpp @@ -172,6 +172,29 @@ for (Decl *D : S.WeakTopLevelDecls()) Consumer->HandleTopLevelDecl(DeclGroupRef(D)); + // For C++20 modules, the codegen for module initializers needs to be altered + // and to be able to use a name based on the module name. + + // At this point, we should know if we are building a non-header C++20 module. + if (S.getLangOpts().CPlusPlusModules && !S.getLangOpts().IsHeaderFile && + !S.getLangOpts().CurrentModule.empty()) { + // If we are building the module from source, then the top level module + // will be here. + Module *CodegenModule = S.getCurrentModule(); + bool Interface = true; + if (CodegenModule) + // We only use module initializers for interfaces (including partition + // implementation units). + Interface = S.currentModuleIsInterface(); + else + // If we are building the module from a PCM file, then the module can be + // found here. + CodegenModule = S.getPreprocessor().getCurrentModule(); + // If neither. then .... + assert(CodegenModule && "codegen for a module, but don't know which?"); + if (Interface) + S.getASTContext().setModuleForCodeGen(CodegenModule); + } Consumer->HandleTranslationUnit(S.getASTContext()); // Finalize the template instantiation observer chain. diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -344,6 +344,16 @@ // statements, so imports are allowed. ImportState = ModuleImportState::ImportAllowed; + // For an implementation, We already made an implicit import (its interface). + // Make and return the import decl to be added to the current TU. + if (MDK == ModuleDeclKind::Implementation) { + // Make the import decl for the interface. + ImportDecl *Import = + ImportDecl::Create(Context, CurContext, ModuleLoc, Mod, Path[0].second); + // and return it to be added. + return ConvertDeclToDeclGroup(Import); + } + // FIXME: Create a ModuleDecl. return nullptr; } diff --git a/clang/test/CodeGen/module-intializer-pmf.cpp b/clang/test/CodeGen/module-intializer-pmf.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/module-intializer-pmf.cpp @@ -0,0 +1,41 @@ + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %s \ +// RUN: -emit-module-interface -o HasPMF.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 HasPMF.pcm \ +// RUN: -S -emit-llvm -o - | FileCheck %s + +module; + +struct Glob { + Glob(){}; +}; + +Glob G; + +export module HasPMF; + +export struct InMod { + InMod(){}; +}; + +export InMod IM; + +module :private; + +struct InPMF { + InPMF(){}; +}; + +InPMF P; + +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call {{.*}} @_ZN4GlobC1Ev +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call {{.*}} @_ZNW6HasPMF5InPMFC1Ev +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call {{.*}} @_ZNW6HasPMF5InModC1Ev +// CHECK: define void @_ZGIW6HasPMF +// CHECK: store i8 1, ptr @_ZGIW6HasPMF__in_chrg +// CHECK: call void @__cxx_global_var_init +// CHECK: call void @__cxx_global_var_init +// CHECK: call void @__cxx_global_var_init diff --git a/clang/test/CodeGen/module-intializer.cpp b/clang/test/CodeGen/module-intializer.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/module-intializer.cpp @@ -0,0 +1,186 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 N.cpp \ +// RUN: -emit-module-interface -o N.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 N.pcm -S -emit-llvm \ +// RUN: -o - | FileCheck %s --check-prefix=CHECK-N + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 O.cpp \ +// RUN: -emit-module-interface -o O.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 O.pcm -S -emit-llvm \ +// RUN: -o - | FileCheck %s --check-prefix=CHECK-O + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-part.cpp \ +// RUN: -emit-module-interface -o M-part.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-part.pcm -S \ +// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-P + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M.cpp \ +// RUN: -fmodule-file=N.pcm -fmodule-file=O.pcm -fmodule-file=M-part.pcm \ +// RUN: -emit-module-interface -o M.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M.pcm -S -emit-llvm \ +// RUN: -o - | FileCheck %s --check-prefix=CHECK-M + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 useM.cpp \ +// RUN: -fmodule-file=M.pcm -S -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK-USE + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-impl.cpp \ +// RUN: -fmodule-file=M.pcm -S -emit-llvm -o - \ +// RUN: | FileCheck %s --check-prefix=CHECK-IMPL + +//--- N-h.h + +struct Oink { + Oink(){}; +}; + +Oink Hog; + +//--- N.cpp + +module; +#include "N-h.h" + +export module N; + +export struct Quack { + Quack(){}; +}; + +export Quack Duck; + +// CHECK-N: define internal void @__cxx_global_var_init +// CHECK-N: call {{.*}} @_ZN4OinkC1Ev +// CHECK-N: define internal void @__cxx_global_var_init +// CHECK-N: call {{.*}} @_ZNW1N5QuackC1Ev +// CHECK-N: define void @_ZGIW1N +// CHECK-N: store i8 1, ptr @_ZGIW1N__in_chrg +// CHECK-N: call void @__cxx_global_var_init +// CHECK-N: call void @__cxx_global_var_init + +//--- O-h.h + +struct Meow { + Meow(){}; +}; + +Meow Cat; + +//--- O.cpp + +module; +#include "O-h.h" + +export module O; + +export struct Bark { + Bark(){}; +}; + +export Bark Dog; + +// CHECK-O: define internal void @__cxx_global_var_init +// CHECK-O: call {{.*}} @_ZN4MeowC2Ev +// CHECK-O: define internal void @__cxx_global_var_init +// CHECK-O: call {{.*}} @_ZNW1O4BarkC1Ev +// CHECK-O: define void @_ZGIW1O +// CHECK-O: store i8 1, ptr @_ZGIW1O__in_chrg +// CHECK-O: call void @__cxx_global_var_init +// CHECK-O: call void @__cxx_global_var_init + +//--- P-h.h + +struct Croak { + Croak(){}; +}; + +Croak Frog; + +//--- M-part.cpp + +module; +#include "P-h.h" + +module M:Part; + +struct Squawk { + Squawk(){}; +}; + +Squawk parrot; + +// CHECK-P: define internal void @__cxx_global_var_init +// CHECK-P: call {{.*}} @_ZN5CroakC1Ev +// CHECK-P: define internal void @__cxx_global_var_init +// CHECK-P: call {{.*}} @_ZNW1M6SquawkC1Ev +// CHECK-P: define void @_ZGIW1MWP4Part +// CHECK-P: store i8 1, ptr @_ZGIW1MWP4Part__in_chrg +// CHECK-P: call void @__cxx_global_var_init +// CHECK-P: call void @__cxx_global_var_init + +//--- M-h.h + +struct Moo { + Moo(){}; +}; + +Moo Cow; + +//--- M.cpp + +module; +#include "M-h.h" + +export module M; +import N; +export import O; +import :Part; + +export struct Baa { + int x; + Baa(){}; + Baa(int x) : x(x) {} + int getX() { return x; } +}; + +export Baa Sheep(10); + +// CHECK-M: define internal void @__cxx_global_var_init +// CHECK-M: call {{.*}} @_ZN3MooC1Ev +// CHECK-M: define internal void @__cxx_global_var_init +// CHECK-M: call {{.*}} @_ZNW1M3BaaC1Ei +// CHECK-M: declare void @_ZGIW1O() +// CHECK-M: declare void @_ZGIW1N() +// CHECK-M: declare void @_ZGIW1MWP4Part() +// CHECK-M: define void @_ZGIW1M +// CHECK-M: store i8 1, ptr @_ZGIW1M__in_chrg +// CHECK-M: call void @_ZGIW1O() +// CHECK-M: call void @_ZGIW1N() +// CHECK-M: call void @_ZGIW1MWP4Part() +// CHECK-M: call void @__cxx_global_var_init +// CHECK-M: call void @__cxx_global_var_init + +//--- useM.cpp + +import M; + +int main() { + return Sheep.getX(); +} + +// CHECK-USE: declare void @_ZGIW1M +// CHECK-USE: define internal void @_GLOBAL__sub_I_useM.cpp +// CHECK-USE: call void @_ZGIW1M() + +//--- M-impl.cpp + +module M; + +int foo(int i) { return i + 1; } + +// CHECK-IMPL: declare void @_ZGIW1M +// CHECK-IMPL: define internal void @_GLOBAL__sub_I_M_impl.cpp +// CHECK-IMPL: call void @_ZGIW1M()