[clang] [MS] Put dllexported inline global initializers in a comdat (PR #107154)

Reid Kleckner via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 3 16:39:04 PDT 2024


https://github.com/rnk updated https://github.com/llvm/llvm-project/pull/107154

>From cfb2cea5a4d4e0c1712e038692c4c5acee6b1f27 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rnk at google.com>
Date: Tue, 3 Sep 2024 21:16:40 +0000
Subject: [PATCH 1/2] [MS] Put dllexported inline global initializers in a
 comdat

Follow-up to c19f4f8069722f6804086d4438a0254104242c46 to handle corner
case of exported inline variables.

Should fix #56485
---
 clang/lib/CodeGen/CGDeclCXX.cpp                              | 2 +-
 clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index 2f56355cff90ec..c9e0fb0d8afaba 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -586,7 +586,7 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
                                           PrioritizedCXXGlobalInits.size());
     PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn));
   } else if (isTemplateInstantiation(D->getTemplateSpecializationKind()) ||
-             getContext().GetGVALinkageForVariable(D) == GVA_DiscardableODR ||
+             !isUniqueGVALinkage(getContext().GetGVALinkageForVariable(D)) ||
              D->hasAttr<SelectAnyAttr>()) {
     // C++ [basic.start.init]p2:
     //   Definitions of explicitly specialized class template static data
diff --git a/clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp b/clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp
index 60b48abca2f89a..871551240debfd 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-template-static-init.cpp
@@ -49,8 +49,6 @@ struct X {
   static T ioo;
   static T init();
 };
-// template specialized static data don't need in llvm.used,
-// the static init routine get call from _GLOBAL__sub_I_ routines.
 template <> int X<int>::ioo = X<int>::init();
 template struct X<int>;
 class a {
@@ -87,5 +85,6 @@ struct S1
 int foo();
 inline int zoo = foo();
 inline static int boo = foo();
+inline __declspec(dllexport) A exported_inline{};
 
-// CHECK: @llvm.used = appending global [8 x ptr] [ptr @"?x at selectany_init@@3HA", ptr @"?x1 at selectany_init@@3HA", ptr @"?x@?$A at H@explicit_template_instantiation@@2HA", ptr @"?ioo@?$X_ at H@@2HA", ptr @"?aoo at S1@@2UA@@A", ptr @"?zoo@@3HA", ptr @"?s@?$ExportedTemplate at H@@2US@@A", ptr @"?x@?$A at H@implicit_template_instantiation@@2HA"], section "llvm.metadata"
+// CHECK: @llvm.used = appending global [10 x ptr] [ptr @"?x at selectany_init@@3HA", ptr @"?x1 at selectany_init@@3HA", ptr @"?x@?$A at H@explicit_template_instantiation@@2HA", ptr @"?ioo@?$X_ at H@@2HA", ptr @"?ioo@?$X at H@@2HA", ptr @"?aoo at S1@@2UA@@A", ptr @"?zoo@@3HA", ptr @"?exported_inline@@3UA@@A", ptr @"?s@?$ExportedTemplate at H@@2US@@A", ptr @"?x@?$A at H@implicit_template_instantiation@@2HA"], section "llvm.metadata"

>From 3cb368a8c01f99a7b12ece55e5b2145650d4f89d Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rnk at google.com>
Date: Tue, 3 Sep 2024 23:38:51 +0000
Subject: [PATCH 2/2] Update comment block on why vague linkage global
 initialization is different

---
 clang/lib/CodeGen/CGDeclCXX.cpp | 41 ++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 11 deletions(-)

diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index c9e0fb0d8afaba..1ae8a2674df20d 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -588,29 +588,48 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
   } else if (isTemplateInstantiation(D->getTemplateSpecializationKind()) ||
              !isUniqueGVALinkage(getContext().GetGVALinkageForVariable(D)) ||
              D->hasAttr<SelectAnyAttr>()) {
+    // For vague linkage globals, put the initializer into its own global_ctors
+    // entry with the global as a comdat key. This ensures at most one
+    // initializer per DSO runs during DSO dynamic initialization.
+    //
+    // For ELF platforms, this is an important code size and startup time
+    // optimization. For dynamic, non-hidden symbols, the weak guard variable
+    // remains to ensure that other DSOs do not re-initialize the global.
+    //
+    // For PE-COFF platforms, there is no guard variable, and COMDAT associativity
+    // is the only way to ensure vauge linkage globals are initialized exactly
+    // once.
+    //
+    // MachO is the only remaining platform with no comdats that doesn't
+    // benefit from this optimization. The rest are mainly modeled on ELF
+    // behavior.
+    //
+    // C++ requires that inline global variables are initialized in source
+    // order, but this requirement does not exist for templated entities.
+    // llvm.global_ctors does not guarantee initialization order, so in
+    // general, Clang does not fully conform to the ordering requirement.
+    // However, in practice, LLVM emits global_ctors in the provided order, and
+    // users typically don't rely on ordering between inline globals in
+    // different headers which are then transitively included in varying order.
+    // Clang's current behavior is a practical tradeoff, since dropping the
+    // comdat would lead to unacceptable impact on code size and startup time.
+    //
+    // FIXME: Find a solution to guarantee source-order initialization of
+    // inline variables.
+    //
     // C++ [basic.start.init]p2:
     //   Definitions of explicitly specialized class template static data
     //   members have ordered initialization. Other class template static data
     //   members (i.e., implicitly or explicitly instantiated specializations)
     //   have unordered initialization.
     //
-    // As a consequence, we can put them into their own llvm.global_ctors entry.
-    //
-    // If the global is externally visible, put the initializer into a COMDAT
-    // group with the global being initialized.  On most platforms, this is a
-    // minor startup time optimization.  In the MS C++ ABI, there are no guard
-    // variables, so this COMDAT key is required for correctness.
-    //
-    // SelectAny globals will be comdat-folded. Put the initializer into a
-    // COMDAT group associated with the global, so the initializers get folded
-    // too.
-    I = DelayedCXXInitPosition.find(D);
     // CXXGlobalInits.size() is the lex order number for the next deferred
     // VarDecl. Use it when the current VarDecl is non-deferred. Although this
     // lex order number is shared between current VarDecl and some following
     // VarDecls, their order of insertion into `llvm.global_ctors` is the same
     // as the lexing order and the following stable sort would preserve such
     // order.
+    I = DelayedCXXInitPosition.find(D);
     unsigned LexOrder =
         I == DelayedCXXInitPosition.end() ? CXXGlobalInits.size() : I->second;
     AddGlobalCtor(Fn, 65535, LexOrder, COMDATKey);



More information about the cfe-commits mailing list