summaryrefslogtreecommitdiff
path: root/clang/lib/CodeGen/CodeGenModule.cpp
diff options
context:
space:
mode:
authorIain Sandoe <iain@sandoe.co.uk>2022-05-15 14:47:54 +0100
committerIain Sandoe <iain@sandoe.co.uk>2022-07-22 08:38:07 +0100
commitafda39a566d9b076bbcbeac1a6a1d4f6aecb3274 (patch)
tree11f30af6ac67f3c656d37c1d0ebeb0ad98fbbfd3 /clang/lib/CodeGen/CodeGenModule.cpp
parent2a88fb2ecb72300bfbbc74c586fb415cc18c9f9d (diff)
re-land [C++20][Modules] Build module static initializers per P1874R1.
The re-land fixes module map module dependencies seen on Greendragon, but not in the clang test suite. --- 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 interface [which includes partition interface and implementation units] (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 initializers 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 module implementation unit, or 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. For all module unit kinds we include a global constructor entry, this allows for the (in most cases unusual) possibility that a module object could be included in a final binary without a specific call to its initializer. 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. - In 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. Differential Revision: https://reviews.llvm.org/D126189
Diffstat (limited to 'clang/lib/CodeGen/CodeGenModule.cpp')
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp60
1 files changed, 58 insertions, 2 deletions
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 2ec71fa29765..7bc79630d67f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -137,6 +137,13 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
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 @@ static void setVisibilityFromDLLStorageClass(const clang::LangOptions &LO,
}
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 @@ void CodeGenModule::Release() {
applyGlobalValReplacements();
applyReplacements();
emitMultiVersionFunctions();
- EmitCXXGlobalInitFunc();
+ if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition())
+ EmitCXXModuleInitFunc(Primary);
+ else
+ EmitCXXGlobalInitFunc();
EmitCXXGlobalCleanUpFunc();
registerGlobalDtorsWithAtExit();
EmitCXXThreadLocalInitFunc();
@@ -2502,6 +2515,31 @@ static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
}
}
+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 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
// 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() &&
+ !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 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
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<clang::Module *, 16> Visited;
SmallVector<clang::Module *, 16> Stack;