Skip to content

Commit ac50710

Browse files
committed
[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 #51873 ). - We then proceed to emit the deferred inits and continue to emit the CXX init function. Differential Revision: https://reviews.llvm.org/D126189
1 parent 54f57d3 commit ac50710

File tree

10 files changed

+517
-11
lines changed

10 files changed

+517
-11
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
472472
};
473473
llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers;
474474

475+
/// For module code-gen cases, this is the top-level module we are building.
476+
Module *TopLevelModule = nullptr;
477+
475478
static constexpr unsigned ConstantArrayTypesLog2InitSize = 8;
476479
static constexpr unsigned GeneralTypesLog2InitSize = 9;
477480
static constexpr unsigned FunctionProtoTypesLog2InitSize = 12;
@@ -1075,6 +1078,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
10751078
/// Get the initializations to perform when importing a module, if any.
10761079
ArrayRef<Decl*> getModuleInitializers(Module *M);
10771080

1081+
/// Set the (C++20) module we are building.
1082+
void setModuleForCodeGen(Module *M) { TopLevelModule = M; }
1083+
1084+
/// Get module under construction, nullptr if this is not a C++20 module.
1085+
Module *getModuleForCodeGen() const { return TopLevelModule; }
1086+
10781087
TranslationUnitDecl *getTranslationUnitDecl() const {
10791088
return TUDecl->getMostRecentDecl();
10801089
}

clang/include/clang/Basic/Module.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,18 @@ class Module {
665665
Module *findSubmodule(StringRef Name) const;
666666
Module *findOrInferSubmodule(StringRef Name);
667667

668+
/// Get the Global Module Fragment (sub-module) for this module, it there is
669+
/// one.
670+
///
671+
/// \returns The GMF sub-module if found, or NULL otherwise.
672+
Module *getGlobalModuleFragment() { return findSubmodule("<global>"); }
673+
674+
/// Get the Private Module Fragment (sub-module) for this module, it there is
675+
/// one.
676+
///
677+
/// \returns The PMF sub-module if found, or NULL otherwise.
678+
Module *getPrivateModuleFragment() { return findSubmodule("<private>"); }
679+
668680
/// Determine whether the specified module would be visible to
669681
/// a lookup at the end of this module.
670682
///

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,6 +2281,11 @@ class Sema final {
22812281
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
22822282
}
22832283

2284+
/// Is the module scope we are an interface?
2285+
bool currentModuleIsInterface() const {
2286+
return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface;
2287+
}
2288+
22842289
/// Get the module owning an entity.
22852290
Module *getOwningModule(const Decl *Entity) {
22862291
return Entity->getOwningModule();

clang/lib/CodeGen/CGDeclCXX.cpp

Lines changed: 171 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,127 @@ void CodeGenModule::EmitCXXThreadLocalInitFunc() {
618618
CXXThreadLocals.clear();
619619
}
620620

621+
/* Build the initializer for a C++20 module:
622+
This is arranged to be run only once regardless of how many times the module
623+
might be included transitively. This arranged by using a control variable.
624+
625+
First we call any initializers for imported modules.
626+
We then call initializers for the Global Module Fragment (if present)
627+
We then call initializers for the current module.
628+
We then call initializers for the Private Module Fragment (if present)
629+
*/
630+
631+
void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
632+
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
633+
CXXGlobalInits.pop_back();
634+
635+
// We create the function, even if it is empty, since an importer of this
636+
// module will refer to it unconditionally (for the current implementation
637+
// there is no way for the importer to know that an importee does not need
638+
// an initializer to be run).
639+
640+
// Module initializers for imported modules are emitted first.
641+
// Collect the modules that we import
642+
SmallVector<Module *> AllImports;
643+
// Ones that we export
644+
for (auto I : Primary->Exports)
645+
AllImports.push_back(I.getPointer());
646+
// Ones that we only import.
647+
for (Module *M : Primary->Imports)
648+
AllImports.push_back(M);
649+
650+
SmallVector<llvm::Function *, 8> ModuleInits;
651+
for (Module *M : AllImports) {
652+
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
653+
SmallString<256> FnName;
654+
{
655+
llvm::raw_svector_ostream Out(FnName);
656+
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
657+
.mangleModuleInitializer(M, Out);
658+
}
659+
assert(!GetGlobalValue(FnName.str()) &&
660+
"We should only have one use of the initializer call");
661+
llvm::Function *Fn = llvm::Function::Create(
662+
FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule());
663+
ModuleInits.push_back(Fn);
664+
}
665+
AllImports.clear();
666+
667+
// Add any initializers with specified priority; this uses the same approach
668+
// as EmitCXXGlobalInitFunc().
669+
if (!PrioritizedCXXGlobalInits.empty()) {
670+
SmallVector<llvm::Function *, 8> LocalCXXGlobalInits;
671+
llvm::array_pod_sort(PrioritizedCXXGlobalInits.begin(),
672+
PrioritizedCXXGlobalInits.end());
673+
for (SmallVectorImpl<GlobalInitData>::iterator
674+
I = PrioritizedCXXGlobalInits.begin(),
675+
E = PrioritizedCXXGlobalInits.end();
676+
I != E;) {
677+
SmallVectorImpl<GlobalInitData>::iterator PrioE =
678+
std::upper_bound(I + 1, E, *I, GlobalInitPriorityCmp());
679+
680+
for (; I < PrioE; ++I)
681+
ModuleInits.push_back(I->second);
682+
}
683+
PrioritizedCXXGlobalInits.clear();
684+
}
685+
686+
// Now append the ones without specified priority.
687+
for (auto F : CXXGlobalInits)
688+
ModuleInits.push_back(F);
689+
CXXGlobalInits.clear();
690+
691+
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
692+
const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction();
693+
694+
// We now build the initializer for this module, which has a mangled name
695+
// as per the Itanium ABI . The action of the initializer is guarded so that
696+
// each init is run just once (even though a module might be imported
697+
// multiple times via nested use).
698+
llvm::Function *Fn;
699+
llvm::GlobalVariable *Guard = nullptr;
700+
{
701+
SmallString<256> InitFnName;
702+
llvm::raw_svector_ostream Out(InitFnName);
703+
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
704+
.mangleModuleInitializer(Primary, Out);
705+
Fn = CreateGlobalInitOrCleanUpFunction(
706+
FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false,
707+
llvm::GlobalVariable::ExternalLinkage);
708+
709+
Guard = new llvm::GlobalVariable(getModule(), Int8Ty, /*isConstant=*/false,
710+
llvm::GlobalVariable::InternalLinkage,
711+
llvm::ConstantInt::get(Int8Ty, 0),
712+
InitFnName.str() + "__in_chrg");
713+
}
714+
CharUnits GuardAlign = CharUnits::One();
715+
Guard->setAlignment(GuardAlign.getAsAlign());
716+
717+
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(
718+
Fn, ModuleInits, ConstantAddress(Guard, Int8Ty, GuardAlign));
719+
// We allow for the case that a module object is added to a linked binary
720+
// without a specific call to the the initializer. This also ensure that
721+
// implementation partition initializers are called when the partition
722+
// is not imported as an interface.
723+
AddGlobalCtor(Fn);
724+
725+
// See the comment in EmitCXXGlobalInitFunc about OpenCL global init
726+
// functions.
727+
if (getLangOpts().OpenCL) {
728+
GenKernelArgMetadata(Fn);
729+
Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL);
730+
}
731+
732+
assert(!getLangOpts().CUDA || !getLangOpts().CUDAIsDevice ||
733+
getLangOpts().GPUAllowDeviceInit);
734+
if (getLangOpts().HIP && getLangOpts().CUDAIsDevice) {
735+
Fn->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL);
736+
Fn->addFnAttr("device-init");
737+
}
738+
739+
ModuleInits.clear();
740+
}
741+
621742
static SmallString<128> getTransformedFileName(llvm::Module &M) {
622743
SmallString<128> FileName = llvm::sys::path::filename(M.getName());
623744

@@ -650,7 +771,26 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
650771
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
651772
CXXGlobalInits.pop_back();
652773

653-
if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty())
774+
// When we import C++20 modules, we must run their initializers first.
775+
SmallVector<llvm::Function *, 8> ModuleInits;
776+
if (CXX20ModuleInits)
777+
for (Module *M : ImportedModules) {
778+
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
779+
SmallString<256> FnName;
780+
{
781+
llvm::raw_svector_ostream Out(FnName);
782+
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
783+
.mangleModuleInitializer(M, Out);
784+
}
785+
assert(!GetGlobalValue(FnName.str()) &&
786+
"We should only have one use of the initializer call");
787+
llvm::Function *Fn = llvm::Function::Create(
788+
FTy, llvm::Function::ExternalLinkage, FnName.str(), &getModule());
789+
ModuleInits.push_back(Fn);
790+
}
791+
792+
if (ModuleInits.empty() && CXXGlobalInits.empty() &&
793+
PrioritizedCXXGlobalInits.empty())
654794
return;
655795

656796
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
@@ -676,6 +816,13 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
676816
llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(
677817
FTy, "_GLOBAL__I_" + getPrioritySuffix(Priority), FI);
678818

819+
// Prepend the module inits to the highest priority set.
820+
if (!ModuleInits.empty()) {
821+
for (auto F : ModuleInits)
822+
LocalCXXGlobalInits.push_back(F);
823+
ModuleInits.clear();
824+
}
825+
679826
for (; I < PrioE; ++I)
680827
LocalCXXGlobalInits.push_back(I->second);
681828

@@ -685,17 +832,33 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
685832
PrioritizedCXXGlobalInits.clear();
686833
}
687834

688-
if (getCXXABI().useSinitAndSterm() && CXXGlobalInits.empty())
835+
if (getCXXABI().useSinitAndSterm() && ModuleInits.empty() &&
836+
CXXGlobalInits.empty())
689837
return;
690838

839+
for (auto F : CXXGlobalInits)
840+
ModuleInits.push_back(F);
841+
CXXGlobalInits.clear();
842+
691843
// Include the filename in the symbol name. Including "sub_" matches gcc
692844
// and makes sure these symbols appear lexicographically behind the symbols
693845
// with priority emitted above.
694-
llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(
695-
FTy, llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())),
696-
FI);
697-
698-
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits);
846+
llvm::Function *Fn;
847+
if (CXX20ModuleInits && getContext().getModuleForCodeGen()) {
848+
SmallString<256> InitFnName;
849+
llvm::raw_svector_ostream Out(InitFnName);
850+
cast<ItaniumMangleContext>(getCXXABI().getMangleContext())
851+
.mangleModuleInitializer(getContext().getModuleForCodeGen(), Out);
852+
Fn = CreateGlobalInitOrCleanUpFunction(
853+
FTy, llvm::Twine(InitFnName), FI, SourceLocation(), false,
854+
llvm::GlobalVariable::ExternalLinkage);
855+
} else
856+
Fn = CreateGlobalInitOrCleanUpFunction(
857+
FTy,
858+
llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())),
859+
FI);
860+
861+
CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits);
699862
AddGlobalCtor(Fn);
700863

701864
// In OpenCL global init functions must be converted to kernels in order to
@@ -718,7 +881,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
718881
Fn->addFnAttr("device-init");
719882
}
720883

721-
CXXGlobalInits.clear();
884+
ModuleInits.clear();
722885
}
723886

724887
void CodeGenModule::EmitCXXGlobalCleanUpFunc() {

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
136136
GlobalsInt8PtrTy = Int8Ty->getPointerTo(DL.getDefaultGlobalsAddressSpace());
137137
ASTAllocaAddressSpace = getTargetCodeGenInfo().getASTAllocaAddressSpace();
138138

139+
// Build C++20 Module initializers.
140+
// TODO: Add Microsoft here once we know the mangling required for the
141+
// initializers.
142+
CXX20ModuleInits =
143+
LangOpts.CPlusPlusModules && getCXXABI().getMangleContext().getKind() ==
144+
ItaniumMangleContext::MK_Itanium;
145+
139146
RuntimeCC = getTargetCodeGenInfo().getABIInfo().getRuntimeCC();
140147

141148
if (LangOpts.ObjC)
@@ -509,12 +516,18 @@ static void setVisibilityFromDLLStorageClass(const clang::LangOptions &LO,
509516
}
510517

511518
void CodeGenModule::Release() {
519+
Module *Primary = getContext().getModuleForCodeGen();
520+
if (CXX20ModuleInits && Primary)
521+
EmitModuleInitializers(Primary);
512522
EmitDeferred();
513523
EmitVTablesOpportunistically();
514524
applyGlobalValReplacements();
515525
applyReplacements();
516526
emitMultiVersionFunctions();
517-
EmitCXXGlobalInitFunc();
527+
if (CXX20ModuleInits && Primary && Primary->isInterfaceOrPartition())
528+
EmitCXXModuleInitFunc(Primary);
529+
else
530+
EmitCXXGlobalInitFunc();
518531
EmitCXXGlobalCleanUpFunc();
519532
registerGlobalDtorsWithAtExit();
520533
EmitCXXThreadLocalInitFunc();
@@ -2491,6 +2504,31 @@ static void addLinkOptionsPostorder(CodeGenModule &CGM, Module *Mod,
24912504
}
24922505
}
24932506

2507+
void CodeGenModule::EmitModuleInitializers(clang::Module *Primary) {
2508+
// Emit the initializers in the order that sub-modules appear in the
2509+
// source, first Global Module Fragments, if present.
2510+
if (auto GMF = Primary->getGlobalModuleFragment()) {
2511+
for (Decl *D : getContext().getModuleInitializers(GMF)) {
2512+
assert(D->getKind() == Decl::Var && "GMF initializer decl is not a var?");
2513+
EmitTopLevelDecl(D);
2514+
}
2515+
}
2516+
// Second any associated with the module, itself.
2517+
for (Decl *D : getContext().getModuleInitializers(Primary)) {
2518+
// Skip import decls, the inits for those are called explicitly.
2519+
if (D->getKind() == Decl::Import)
2520+
continue;
2521+
EmitTopLevelDecl(D);
2522+
}
2523+
// Third any associated with the Privat eMOdule Fragment, if present.
2524+
if (auto PMF = Primary->getPrivateModuleFragment()) {
2525+
for (Decl *D : getContext().getModuleInitializers(PMF)) {
2526+
assert(D->getKind() == Decl::Var && "PMF initializer decl is not a var?");
2527+
EmitTopLevelDecl(D);
2528+
}
2529+
}
2530+
}
2531+
24942532
void CodeGenModule::EmitModuleLinkOptions() {
24952533
// Collect the set of all of the modules we want to visit to emit link
24962534
// options, which is essentially the imported modules and all of their
@@ -2896,12 +2934,19 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
28962934
// explicitly instantiated, so they should not be emitted eagerly.
28972935
return false;
28982936
}
2899-
if (const auto *VD = dyn_cast<VarDecl>(Global))
2937+
if (const auto *VD = dyn_cast<VarDecl>(Global)) {
29002938
if (Context.getInlineVariableDefinitionKind(VD) ==
29012939
ASTContext::InlineVariableDefinitionKind::WeakUnknown)
29022940
// A definition of an inline constexpr static data member may change
29032941
// linkage later if it's redeclared outside the class.
29042942
return false;
2943+
if (CXX20ModuleInits && VD->getOwningModule()) {
2944+
// For CXX20, module-owned initializers need to be deferred, since it is
2945+
// not known at this point if they will be run for the current module or
2946+
// as part of the initializer for an imported one.
2947+
return false;
2948+
}
2949+
}
29052950
// If OpenMP is enabled and threadprivates must be generated like TLS, delay
29062951
// codegen for global variables, because they may be marked as threadprivate.
29072952
if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS &&
@@ -6198,6 +6243,12 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
61986243
DI->EmitImportDecl(*Import);
61996244
}
62006245

6246+
// For CXX20 we are done - we will call the module initializer for the
6247+
// imported module, and that will likewise call those for any imports it
6248+
// has.
6249+
if (CXX20ModuleInits)
6250+
break;
6251+
62016252
// Find all of the submodules and emit the module initializers.
62026253
llvm::SmallPtrSet<clang::Module *, 16> Visited;
62036254
SmallVector<clang::Module *, 16> Stack;

0 commit comments

Comments
 (0)