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 @@ -472,6 +472,9 @@ }; llvm::DenseMap ModuleInitializers; + /// For module code-gen cases, this is the top-level module we are building. + mutable Module *PrimaryModule = nullptr; + static constexpr unsigned ConstantArrayTypesLog2InitSize = 8; static constexpr unsigned GeneralTypesLog2InitSize = 9; static constexpr unsigned FunctionProtoTypesLog2InitSize = 12; @@ -1075,6 +1078,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) { PrimaryModule = M; } + + /// Get module under construction, nullptr if this is not a C++20 module. + Module *getModuleForCodeGen() { return PrimaryModule; } + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl->getMostRecentDecl(); } 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 @@ -2245,11 +2245,6 @@ /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; - /// Get the module unit whose scope we are currently within. - Module *getCurrentModule() const { - return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; - } - /// Helper function to judge if we are in module purview. /// Return false if we are not in a module. bool isCurrentModulePurview() const { @@ -2269,6 +2264,11 @@ bool isUsableModule(const Module *M); public: + /// Get the module whose scope we are currently within. + Module *getCurrentModule() const { + return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; + } + /// 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,112 @@ CXXThreadLocals.clear(); } +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 (there is no way for an importer + // to know if the function could be omitted at this time). + + // Module initialisers 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) { + 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 initialiser 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 initialiser 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(getContext().getModuleForCodeGen(), 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)); + AddGlobalCtor(Fn); + + // See the comment in EmitCXXGlobalInitFunc about OpenCL global init + // functions. + if (getLangOpts().OpenCL) { + GenOpenCLArgMetadata(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 +756,26 @@ while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) CXXGlobalInits.pop_back(); - if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty()) + // Module initialiser trump all other priorities. + SmallVector ModuleInits; + if (CXX20ModuleInits) + for (Module *M : ImportedModules) { + 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 initialiser 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 +801,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 +817,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 +866,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; @@ -1527,6 +1527,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(Module *Primary); + /// Emit the function that initializes C++ globals. void EmitCXXGlobalInitFunc(); @@ -1594,6 +1597,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) @@ -510,12 +517,18 @@ } void CodeGenModule::Release() { + Module *Primary = getContext().getModuleForCodeGen(); + if (CXX20ModuleInits && Primary) + EmitModuleInitializers(Primary); EmitDeferred(); EmitVTablesOpportunistically(); applyGlobalValReplacements(); applyReplacements(); emitMultiVersionFunctions(); - EmitCXXGlobalInitFunc(); + if (CXX20ModuleInits && Primary) + EmitCXXModuleInitFunc(Primary); + else + EmitCXXGlobalInitFunc(); EmitCXXGlobalCleanUpFunc(); registerGlobalDtorsWithAtExit(); EmitCXXThreadLocalInitFunc(); @@ -2470,6 +2483,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->findSubmodule("")) { + 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->findSubmodule("")) { + 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 @@ -2882,12 +2920,21 @@ // 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()) { + // For CXX20, module-owned initialisers 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. + // llvm::dbgs() << "deferring: "; + // VD->dump(); + 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 && @@ -3156,10 +3203,13 @@ // Defer code generation to first use when possible, e.g. if this is an inline // function. If the global must always be emitted, do it eagerly if possible // to benefit from cache locality. - if (MustBeEmitted(Global) && MayBeEmittedEagerly(Global)) { - // Emit the definition if it can't be deferred. - EmitGlobalDefinition(GD); - return; + bool MBEE = MayBeEmittedEagerly(Global); + if (MustBeEmitted(Global)) { + if (MBEE) { + // Emit the definition if it can't be deferred. + EmitGlobalDefinition(GD); + return; + } } // If we're deferring emission of a C++ variable with an @@ -3176,7 +3226,7 @@ addDeferredDeclToEmit(GD); } else if (MustBeEmitted(Global)) { // The value must be emitted, but cannot be emitted eagerly. - assert(!MayBeEmittedEagerly(Global)); + assert(!MBEE); addDeferredDeclToEmit(GD); } else { // Otherwise, remember that we saw a deferred decl with this name. The @@ -6182,6 +6232,12 @@ DI->EmitImportDecl(*Import); } + // For CXX20 we are done - we will call the module initialiser for the + // imported module, and that will likewise call those for any imports it + // has. + if (CXX20ModuleInits) + break; + // 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,23 @@ 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(); + if (!CodegenModule) + // 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?"); + S.getASTContext().setModuleForCodeGen(CodegenModule); + } Consumer->HandleTranslationUnit(S.getASTContext()); // Finalize the template instantiation observer chain. 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,172 @@ +// 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 + +//--- 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 void @_ZN4OinkC1Ev +// CHECK-N: define internal void @__cxx_global_var_init +// CHECK-N: call void @_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 void @_ZN4MeowC2Ev +// CHECK-O: define internal void @__cxx_global_var_init +// CHECK-O: call void @_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 void @_ZN5CroakC1Ev +// CHECK-P: define internal void @__cxx_global_var_init +// CHECK-P: call void @_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 void @_ZN3MooC1Ev +// CHECK-M: define internal void @__cxx_global_var_init +// CHECK-M: call void @_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()