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 @@ -687,6 +687,12 @@ } AllImports.clear(); + // If we have an empty initializer then we do not want to create a guard var. + // 'Empty' needs only to apply to init functions that we call directly, calls + // to imported module initializers need not be counted since those will + // contain local guards as needed (and might themselves be empty). + bool ElideGuard = PrioritizedCXXGlobalInits.empty() && CXXGlobalInits.empty(); + // Add any initializers with specified priority; this uses the same approach // as EmitCXXGlobalInitFunc(). if (!PrioritizedCXXGlobalInits.empty()) { @@ -719,7 +725,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 +734,23 @@ 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 (ElideGuard) { + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, ModuleInits); + } else { + // Create the initializer with a guard var to ensure that the contained + // inits are only run once. + 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()); + CodeGenFunction(*this).GenerateCXXGlobalInitFunc( + Fn, ModuleInits, ConstantAddress(Guard, Int8Ty, GuardAlign)); + } } - 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. 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,64 @@ +// 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-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-NOT: @_ZGIW1P__in_chrg +// CHECK-P: call void @_ZGIW1O() + +// 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 +