iains created this revision.
Herald added a project: All.
iains added a reviewer: urnathan.
iains added a subscriber: clang-modules.
iains published this revision for review.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

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, we can
elide the guard variable - in this case an optimisation.

For some offload targets, this can also be a matter of correctness - since they
might not support a global variable of this nature (and have empty global 
inits).

In order to handle the second case better, we also eliminate the guard variable
when a current module imports other modules.  This means that we might call the
initializers for the sub-modules more than once - however, if they actually have
active content, that will be guarded locally.  If the entire tree is empty, then
there will be no guard variables - which is appropriate in the off-loading case.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D134589

Files:
  clang/lib/CodeGen/CGDeclCXX.cpp
  clang/test/CodeGenCXX/module-initializer-guard-elision.cpp

Index: clang/test/CodeGenCXX/module-initializer-guard-elision.cpp
===================================================================
--- /dev/null
+++ 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
+
Index: clang/lib/CodeGen/CGDeclCXX.cpp
===================================================================
--- clang/lib/CodeGen/CGDeclCXX.cpp
+++ 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.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to