[clang] afda39a - re-land [C++20][Modules] Build module static initializers per P1874R1.
Iain Sandoe via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 22 00:41:04 PDT 2022
Author: Iain Sandoe
Date: 2022-07-22T08:38:07+01:00
New Revision: afda39a566d9b076bbcbeac1a6a1d4f6aecb3274
URL: https://github.com/llvm/llvm-project/commit/afda39a566d9b076bbcbeac1a6a1d4f6aecb3274
DIFF: https://github.com/llvm/llvm-project/commit/afda39a566d9b076bbcbeac1a6a1d4f6aecb3274.diff
LOG: 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
Added:
clang/test/CodeGen/module-intializer-pmf.cpp
clang/test/CodeGen/module-intializer.cpp
Modified:
clang/include/clang/AST/ASTContext.h
clang/include/clang/Basic/Module.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/lib/Sema/SemaModule.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 85eba45e4de6e..9536b3faa02da 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -473,6 +473,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
};
llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers;
+ /// For module code-gen cases, this is the top-level module we are building.
+ Module *TopLevelModule = nullptr;
+
static constexpr unsigned ConstantArrayTypesLog2InitSize = 8;
static constexpr unsigned GeneralTypesLog2InitSize = 9;
static constexpr unsigned FunctionProtoTypesLog2InitSize = 12;
@@ -1076,6 +1079,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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) { TopLevelModule = M; }
+
+ /// Get module under construction, nullptr if this is not a C++20 module.
+ Module *getModuleForCodeGen() const { return TopLevelModule; }
+
TranslationUnitDecl *getTranslationUnitDecl() const {
return TUDecl->getMostRecentDecl();
}
diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index a1778baa04530..47d736a3b4557 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -665,6 +665,18 @@ class Module {
Module *findSubmodule(StringRef Name) const;
Module *findOrInferSubmodule(StringRef Name);
+ /// Get the Global Module Fragment (sub-module) for this module, it there is
+ /// one.
+ ///
+ /// \returns The GMF sub-module if found, or NULL otherwise.
+ Module *getGlobalModuleFragment() { return findSubmodule("<global>"); }
+
+ /// Get the Private Module Fragment (sub-module) for this module, it there is
+ /// one.
+ ///
+ /// \returns The PMF sub-module if found, or NULL otherwise.
+ Module *getPrivateModuleFragment() { return findSubmodule("<private>"); }
+
/// Determine whether the specified module would be visible to
/// a lookup at the end of this module.
///
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e51b9daef7d3e..a33d85cc954d0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2281,6 +2281,11 @@ class Sema final {
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
}
+ /// Is the module scope we are an interface?
+ bool currentModuleIsInterface() const {
+ return ModuleScopes.empty() ? false : ModuleScopes.back().ModuleInterface;
+ }
+
/// 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
index de5cb913220a0..949112c63cc9e 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -618,6 +618,130 @@ void CodeGenModule::EmitCXXThreadLocalInitFunc() {
CXXThreadLocals.clear();
}
+/* Build the initializer for a C++20 module:
+ This is arranged to be run only once regardless of how many times the module
+ might be included transitively. This arranged by using a control variable.
+
+ First we call any initializers for imported modules.
+ 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)
+*/
+
+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 (for the current implementation
+ // there is no way for the importer to know that an importee does not need
+ // an initializer to be run).
+
+ // Module initializers 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) {
+ // No Itanium initializer in module map modules.
+ if (M->isModuleMapModule())
+ continue; // TODO: warn of mixed use of module map modules and C++20?
+ 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 initializer 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 initializer 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(Primary, 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));
+ // We allow for the case that a module object is added to a linked binary
+ // without a specific call to the the initializer. This also ensure that
+ // implementation partition initializers are called when the partition
+ // is not imported as an interface.
+ AddGlobalCtor(Fn);
+
+ // See the comment in EmitCXXGlobalInitFunc about OpenCL global init
+ // functions.
+ if (getLangOpts().OpenCL) {
+ GenKernelArgMetadata(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 +774,29 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
while (!CXXGlobalInits.empty() && !CXXGlobalInits.back())
CXXGlobalInits.pop_back();
- if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty())
+ // When we import C++20 modules, we must run their initializers first.
+ SmallVector<llvm::Function *, 8> ModuleInits;
+ if (CXX20ModuleInits)
+ for (Module *M : ImportedModules) {
+ // No Itanium initializer in module map modules.
+ if (M->isModuleMapModule())
+ continue;
+ 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 initializer 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 +822,13 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
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 +838,33 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
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 +887,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() {
Fn->addFnAttr("device-init");
}
- CXXGlobalInits.clear();
+ ModuleInits.clear();
}
void CodeGenModule::EmitCXXGlobalCleanUpFunc() {
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 2ec71fa297659..7bc79630d67f0 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;
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 1123a7cfce15d..aa57dc5a28d28 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -303,7 +303,7 @@ class CodeGenModule : public CodeGenTypeCache {
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;
@@ -1599,6 +1599,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// Emit the function that initializes C++ thread_local variables.
void EmitCXXThreadLocalInitFunc();
+ /// Emit the function that initializes global variables for a C++ Module.
+ void EmitCXXModuleInitFunc(clang::Module *Primary);
+
/// Emit the function that initializes C++ globals.
void EmitCXXGlobalInitFunc();
@@ -1666,6 +1669,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// 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/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp
index 04b3f0460bf3e..5fca029a42668 100644
--- a/clang/lib/Parse/ParseAST.cpp
+++ b/clang/lib/Parse/ParseAST.cpp
@@ -172,6 +172,29 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
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();
+ bool Interface = true;
+ if (CodegenModule)
+ // We only use module initializers for interfaces (including partition
+ // implementation units).
+ Interface = S.currentModuleIsInterface();
+ else
+ // 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?");
+ if (Interface)
+ S.getASTContext().setModuleForCodeGen(CodegenModule);
+ }
Consumer->HandleTranslationUnit(S.getASTContext());
// Finalize the template instantiation observer chain.
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index e9a1ac17ce86e..f5c24bd10daa5 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -344,6 +344,16 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
// statements, so imports are allowed.
ImportState = ModuleImportState::ImportAllowed;
+ // For an implementation, We already made an implicit import (its interface).
+ // Make and return the import decl to be added to the current TU.
+ if (MDK == ModuleDeclKind::Implementation) {
+ // Make the import decl for the interface.
+ ImportDecl *Import =
+ ImportDecl::Create(Context, CurContext, ModuleLoc, Mod, Path[0].second);
+ // and return it to be added.
+ return ConvertDeclToDeclGroup(Import);
+ }
+
// FIXME: Create a ModuleDecl.
return nullptr;
}
diff --git a/clang/test/CodeGen/module-intializer-pmf.cpp b/clang/test/CodeGen/module-intializer-pmf.cpp
new file mode 100644
index 0000000000000..e513b280b0a75
--- /dev/null
+++ b/clang/test/CodeGen/module-intializer-pmf.cpp
@@ -0,0 +1,41 @@
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %s \
+// RUN: -emit-module-interface -o HasPMF.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 HasPMF.pcm \
+// RUN: -S -emit-llvm -o - | FileCheck %s
+
+module;
+
+struct Glob {
+ Glob(){};
+};
+
+Glob G;
+
+export module HasPMF;
+
+export struct InMod {
+ InMod(){};
+};
+
+export InMod IM;
+
+module :private;
+
+struct InPMF {
+ InPMF(){};
+};
+
+InPMF P;
+
+// CHECK: define internal void @__cxx_global_var_init
+// CHECK: call {{.*}} @_ZN4GlobC1Ev
+// CHECK: define internal void @__cxx_global_var_init
+// CHECK: call {{.*}} @_ZNW6HasPMF5InPMFC1Ev
+// CHECK: define internal void @__cxx_global_var_init
+// CHECK: call {{.*}} @_ZNW6HasPMF5InModC1Ev
+// CHECK: define void @_ZGIW6HasPMF
+// CHECK: store i8 1, ptr @_ZGIW6HasPMF__in_chrg
+// CHECK: call void @__cxx_global_var_init
+// CHECK: call void @__cxx_global_var_init
+// CHECK: call void @__cxx_global_var_init
diff --git a/clang/test/CodeGen/module-intializer.cpp b/clang/test/CodeGen/module-intializer.cpp
new file mode 100644
index 0000000000000..0b48e87f9ea7c
--- /dev/null
+++ b/clang/test/CodeGen/module-intializer.cpp
@@ -0,0 +1,186 @@
+// 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
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 M-impl.cpp \
+// RUN: -fmodule-file=M.pcm -S -emit-llvm -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK-IMPL
+
+//--- 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 {{.*}} @_ZN4OinkC1Ev
+// CHECK-N: define internal void @__cxx_global_var_init
+// CHECK-N: call {{.*}} @_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 {{.*}} @_ZN4MeowC2Ev
+// CHECK-O: define internal void @__cxx_global_var_init
+// CHECK-O: call {{.*}} @_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 {{.*}} @_ZN5CroakC1Ev
+// CHECK-P: define internal void @__cxx_global_var_init
+// CHECK-P: call {{.*}} @_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 {{.*}} @_ZN3MooC1Ev
+// CHECK-M: define internal void @__cxx_global_var_init
+// CHECK-M: call {{.*}} @_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()
+
+//--- M-impl.cpp
+
+module M;
+
+int foo(int i) { return i + 1; }
+
+// CHECK-IMPL: declare void @_ZGIW1M
+// CHECK-IMPL: define internal void @_GLOBAL__sub_I_M_impl.cpp
+// CHECK-IMPL: call void @_ZGIW1M()
More information about the cfe-commits
mailing list