diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -640,7 +640,12 @@ /* 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. + might be included transitively. This arranged by using a guard variable. + + If there are no initalizers at all (and also no imported modules) we reduce + this to an empty function (since the Itanium ABI requires that this function + be available to a caller, which might be produced by a different + implementation). First we call any initializers for imported modules. We then call initializers for the Global Module Fragment (if present) @@ -652,13 +657,10 @@ 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). - + // As noted above, we create the function, even if it is empty. // Module initializers for imported modules are emitted first. - // Collect the modules that we import + + // Collect all the modules that we import SmallVector AllImports; // Ones that we export for (auto I : Primary->Exports) @@ -685,7 +687,6 @@ 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(). @@ -703,13 +704,11 @@ 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(); @@ -719,7 +718,6 @@ // 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); @@ -729,18 +727,26 @@ 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"); + // If we have a completely empty initializer then we do not want to create + // the guard variable. + ConstantAddress GuardAddr = ConstantAddress::invalid(); + if (!AllImports.empty() || !PrioritizedCXXGlobalInits.empty() || + !CXXGlobalInits.empty()) { + // Create the guard var. + llvm::GlobalVariable *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()); + GuardAddr = ConstantAddress(Guard, Int8Ty, GuardAlign); + } + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits, + GuardAddr); } - 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 initializer. This also ensure that + // 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 ensures that // implementation partition initializers are called when the partition // is not imported as an interface. AddGlobalCtor(Fn); @@ -759,6 +765,10 @@ Fn->addFnAttr("device-init"); } + // We are done with the inits. + AllImports.clear(); + PrioritizedCXXGlobalInits.clear(); + CXXGlobalInits.clear(); ModuleInits.clear(); } diff --git a/clang/test/CodeGenCXX/module-initializer-guard-elision.cpp b/clang/test/CodeGenCXX/module-initializer-guard-elision.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/module-initializer-guard-elision.cpp @@ -0,0 +1,69 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// 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 P.cpp \ +// RUN: -emit-module-interface -fmodule-file=O.pcm -o P.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 P.pcm -S -emit-llvm \ +// RUN: -o - | FileCheck %s --check-prefix=CHECK-P + +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 Q.cpp \ +// RUN: -emit-module-interface -fmodule-file=O.pcm -o Q.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 Q.pcm -S -emit-llvm \ +// RUN: -o - | FileCheck %s --check-prefix=CHECK-Q + +// Testing cases where we can elide the module initializer guard variable. + +// This module has no global inits and does not import any other module +//--- O.cpp + +export module O; + +export int foo (); + +// CHECK-O: define void @_ZGIW1O +// CHECK-O-LABEL: entry +// CHECK-O-NEXT: ret void +// CHECK-O-NOT: @_ZGIW1O__in_chrg + +// This has no global inits but imports a module, and therefore needs a guard +// variable. +//--- P.cpp + +export module P; + +export import O; +export int bar (); + +// CHECK-P: define void @_ZGIW1P +// CHECK-P-LABEL: init +// CHECK-P: store i8 1, ptr @_ZGIW1P__in_chrg +// CHECK-P: call void @_ZGIW1O() +// CHECK-P-NOT: call void @__cxx_global_var_init + +// This imports a module and has global inits, so needs a guard. +//--- Q.cpp + +export module Q; +export import O; + +export struct Quack { + Quack(){}; +}; + +export Quack Duck; + +export int baz (); + +// CHECK-Q: define internal void @__cxx_global_var_init +// CHECK-Q: call {{.*}} @_ZNW1Q5QuackC1Ev +// CHECK-Q: define void @_ZGIW1Q +// CHECK-Q: store i8 1, ptr @_ZGIW1Q__in_chrg +// CHECK-Q: call void @_ZGIW1O() +// CHECK-Q: call void @__cxx_global_var_init +