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 @@ -703,13 +703,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 +717,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,17 +726,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 an empty initializer then we do not want to create a guard + // var. 'Empty' excludes calls to imported module initializers since + // those will contain local guards as needed (and might themselves + // be empty). + ConstantAddress GuardAddr = ConstantAddress::invalid(); + if (!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 + // 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. @@ -759,6 +765,9 @@ Fn->addFnAttr("device-init"); } + // We are done with the inits. + 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,68 @@ +// 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. +//--- P.cpp + +export module P; + +export import O; +export int bar (); + +// CHECK-P: define void @_ZGIW1P +// CHECK-P-LABEL: entry +// CHECK-P-NEXT: call void @_ZGIW1O() +// CHECK-P-NEXT: ret void +// CHECK-P-NOT: @_ZGIW1P__in_chrg + +// 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 +