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-09 09:09:09 +0100
commitac507102d258b6fc0cb57eb60c9dfabd57ff562f (patch)
treeaae104b2889b80802a4f5aff524dccb7ef073e9d /clang/lib/CodeGen/CodeGenModule.cpp
parent54f57d3847c00d0233e287ebb5283d04e6083062 (diff)
[C++20][Modules] Build module static initializers per P1874R1.
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.cpp55
1 files changed, 53 insertions, 2 deletions
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 56ed59d1e3f1..e282bc820255 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -136,6 +136,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)
@@ -509,12 +516,18 @@ static void setVisibilityFromDLLStorageClass(const clang::LangOptions &LO,
}
void CodeGenModule::Release() {
+ Module *Primary = getContext().getModuleForCodeGen();
+ if (CXX20ModuleInits && Primary)
+ EmitModuleInitializers(Primary);
EmitDeferred();
EmitVTablesOpportunistically();
applyGlobalValReplacements();
applyReplacements();
emitMultiVersionFunctions();
- EmitCXXGlobalInitFunc();
+ if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition())
+ EmitCXXModuleInitFunc(Primary);
+ else
+ EmitCXXGlobalInitFunc();
EmitCXXGlobalCleanUpFunc();
registerGlobalDtorsWithAtExit();
EmitCXXThreadLocalInitFunc();
@@ -2491,6 +2504,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
@@ -2896,12 +2934,19 @@ 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()) {
+ // 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 &&
@@ -6198,6 +6243,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
DI->EmitImportDecl(*Import);
}
+ // For CXX20 we are done - we will call the module initializer 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;