iains created this revision. Herald added a project: All. iains added reviewers: urnathan, Bigcheese, ChuanqiXu, jansvoboda11. iains added a subscriber: clang-modules. iains edited the summary of this revision. iains edited the summary of this revision. iains edited the summary of this revision. iains edited the summary of this revision. iains edited the summary of this revision. iains updated this revision to Diff 431313. iains added a comment. iains published this revision for review. Herald added a project: clang. Herald added a subscriber: cfe-commits.
just updated description Currently we only implement this for the Itanium ABI since the correct mangling for the initializers in other ABIs is not yet known. Intended result: For a module (instead of the generic CXX initializer) we emit a module init that: - wraps the contained initializations in a control variable to ensure that the inits only happen once, even if a module is imported many times by imports of the main unit. - calls module initialisers for imported modules first. Note that the order of module import is not significant, and therefore neither is the order of imported module initializers. - 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) For a non-module TU that imports at least one module we emit a regular CXX init that: - Calls the initializers for any imported modules first. - Then proceeds as normal with remaining inits. Implementation: - We provide the module pointer in the AST Context so that CodeGen can act on it and its sub-modules. - We need to account for module build lines like this: ` clang -cc1 -std=c++20 Foo.pcm -emit-obj -o Foo.o` or ` clang -cc1 -std=c++20 -xc++-module Foo.cpp -emit-obj -o Foo.o` - in order to do this, we add to ParseAST to set the module pointer in the ASTContext, once we establish that this is a module build and we know the module pointer. To be able to do this, we make the query for current module public in Sema. - iIn CodeGen, we determine if the current build requires a CXX20-style module init and, if so, we defer any module initializers during the "Eagerly Emitted" phase. - We then walk the module initializers at the end of the TU but before emitting deferred inits (which adds any hidden and static ones, fixing https://github.com/llvm/llvm-project/issues/51873 ). - We then proceed to emit the deferred inits and continue to emit the CXX init function. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D126189 Files: clang/include/clang/AST/ASTContext.h clang/include/clang/Sema/Sema.h clang/lib/CodeGen/CGDeclCXX.cpp clang/lib/CodeGen/CodeGenModule.cpp clang/lib/CodeGen/CodeGenModule.h clang/lib/Parse/ParseAST.cpp clang/test/CodeGen/module-intializer.cpp
Index: clang/test/CodeGen/module-intializer.cpp =================================================================== --- /dev/null +++ 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() Index: clang/lib/Parse/ParseAST.cpp =================================================================== --- clang/lib/Parse/ParseAST.cpp +++ 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. Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -303,7 +303,7 @@ std::unique_ptr<CGCXXABI> ABI; llvm::LLVMContext &VMContext; std::string ModuleNameHash; - + bool CXX20ModuleInits = false; std::unique_ptr<CodeGenTBAA> TBAA; mutable std::unique_ptr<TargetCodeGenInfo> 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(); Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ 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("<global>")) { + 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("<private>")) { + 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<VarDecl>(Global)) + if (const auto *VD = dyn_cast<VarDecl>(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<clang::Module *, 16> Visited; SmallVector<clang::Module *, 16> Stack; Index: clang/lib/CodeGen/CGDeclCXX.cpp =================================================================== --- clang/lib/CodeGen/CGDeclCXX.cpp +++ 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<Module *> 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<llvm::Function *, 8> ModuleInits; + for (Module *M : AllImports) { + llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + cast<ItaniumMangleContext>(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<llvm::Function *, 8> LocalCXXGlobalInits; + llvm::array_pod_sort(PrioritizedCXXGlobalInits.begin(), + PrioritizedCXXGlobalInits.end()); + for (SmallVectorImpl<GlobalInitData>::iterator + I = PrioritizedCXXGlobalInits.begin(), + E = PrioritizedCXXGlobalInits.end(); + I != E;) { + SmallVectorImpl<GlobalInitData>::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<ItaniumMangleContext>(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<llvm::Function *, 8> 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<ItaniumMangleContext>(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<ItaniumMangleContext>(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() { Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2245,11 +2245,6 @@ /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet<const NamespaceDecl*, 8> 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(); Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -472,6 +472,9 @@ }; llvm::DenseMap<Module*, PerModuleInitializers*> 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<Decl*> 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(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits