[clang] bd7f4c5 - [C++20][Modules] Elide unused guard variables in Itanium ABI module initializers.

Iain Sandoe via cfe-commits cfe-commits at lists.llvm.org
Sun Dec 18 01:17:04 PST 2022


Author: Iain Sandoe
Date: 2022-12-18T09:16:27Z
New Revision: bd7f4c561f5e2385ebe5500a044efe0de30cccaa

URL: https://github.com/llvm/llvm-project/commit/bd7f4c561f5e2385ebe5500a044efe0de30cccaa
DIFF: https://github.com/llvm/llvm-project/commit/bd7f4c561f5e2385ebe5500a044efe0de30cccaa.diff

LOG: [C++20][Modules] Elide unused guard variables in Itanium ABI module initializers.

For the Itanium ABI, we emit an initializer for each module.  This is responsible
for handling initialization of global vars.  Relates to P1874R1.

The initializer has a known mangling and is automatically called from any TU that
imports a module. Since, at present, the importer has no way to determine that an
imported module does not require an initializer, we generate the initializer for
all cases (even when it is empty).

Initializers must be run once, with the ordering guaranteed by the import graph
and this is ensured in the current code by addition of a guard variable.

In the case that a module has no requirement for global initializers, and also does
not import any other modules, we can elide the guard variable.

Differential Revision: https://reviews.llvm.org/D134589

Added: 
    clang/test/CodeGenCXX/module-initializer-guard-elision.cpp

Modified: 
    clang/lib/CodeGen/CGDeclCXX.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index 009749b38509b..dcd811ea257b3 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -640,7 +640,12 @@ void CodeGenModule::EmitCXXThreadLocalInitFunc() {
 
 /* 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 
diff erent
+   implementation).
 
    First we call any initializers for imported modules.
    We then call initializers for the Global Module Fragment (if present)
@@ -652,13 +657,10 @@ 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).
-
+  // 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<Module *> AllImports;
   // Ones that we export
   for (auto I : Primary->Exports)
@@ -685,7 +687,6 @@ void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
         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 @@ void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
       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 @@ void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
   // 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 @@ void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
         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 @@ void CodeGenModule::EmitCXXModuleInitFunc(Module *Primary) {
     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
index 0000000000000..2b01a670fb727
--- /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
+


        


More information about the cfe-commits mailing list