jyu2 created this revision.
jyu2 added reviewers: rnk, majnemer, erichkeane, cfe-commits.
Herald added a project: clang.

MS only run time problem with inline static data member.
A inline static data member’s init function gets called multiple time.

To fix this, using template static data members initialization method instead.  
So that inline static data member initialize function can be put into COMDAT 
group with global being initialized.  And also put static data member in the 
linker directive.  So that the function can be called before main, even the 
that variable is not been referenced.

The bug is report in:
https://bugs.llvm.org/show_bug.cgi?id=37903


Repository:
  rC Clang

https://reviews.llvm.org/D60912

Files:
  lib/CodeGen/CGDeclCXX.cpp
  test/CodeGenCXX/microsoft-abi-template-static-init.cpp

Index: test/CodeGenCXX/microsoft-abi-template-static-init.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/microsoft-abi-template-static-init.cpp
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 %s -triple=i686-pc-win32 -fms-extensions -std=c++17 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple=x86_64-pc-win32 -fms-extensions -std=c++17 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple=i686-pc-windows-msvc -fms-extensions -std=c++17 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple=x86_64-pc-windows-msvc -std=c++17 -fms-extensions -emit-llvm -o - | FileCheck %s
+
+struct S {
+  S();
+  ~S();
+};
+
+template <typename T> struct __declspec(dllexport) ExportedTemplate {
+  static S s;
+};
+template <typename T> S ExportedTemplate<T>::s;
+void useExportedTemplate(ExportedTemplate<int> x) {
+  (void)x.s;
+}
+int f();
+namespace selectany_init {
+// MS don't put selectany static var in the linker directive, init routine
+// f() is not getting called if x is not referenced.
+int __declspec(selectany) x = f();
+}
+
+namespace explicit_template_instantiation {
+template <typename T> struct A { static  int x; };
+template <typename T> int A<T>::x = f();
+template struct A<int>;
+}
+
+namespace implicit_template_instantiation {
+template <typename T> struct A { static  int x; };
+template <typename T>  int A<T>::x = f();
+int g() { return A<int>::x; }
+}
+
+
+template <class T>
+struct X_ {
+  static T ioo;
+  static T init();
+};
+template <class T> T X_<T>::ioo = X_<T>::init();
+template struct X_<int>;
+
+template <class T>
+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 {
+public:
+  a();
+};
+// For the static var inside unnamed namespace, the object is local to TU.
+// No need to put static var in the linker directive.
+// The static init routine is called before main.
+namespace {
+template <int> class aj {
+public:
+  static a al;
+};
+template <int am> a aj<am>::al;
+class b : aj<3> {
+  void c();
+};
+void b::c() { al; }
+}
+
+// C++17, inline static data member also need to use
+struct A
+{
+  A();
+  ~A();
+};
+
+struct S1
+{
+  inline static A aoo; // C++17 inline variable, thus also a definition
+};
+
+// CHECK: @llvm.used = appending global [5 x i8*] [i8* bitcast (i32* @"?x@?$A@H@explicit_template_instantiation@@2HA" to i8*), i8* bitcast (i32* @"?ioo@?$X_@H@@2HA" to i8*), i8* getelementptr inbounds (%struct.A, %struct.A* @"?aoo@S1@@2UA@@A", i32 0, i32 0), i8* getelementptr inbounds (%struct.S, %struct.S* @"?s@?$ExportedTemplate@H@@2US@@A", i32 0, i32 0), i8* bitcast (i32* @"?x@?$A@H@implicit_template_instantiation@@2HA" to i8*)], section "llvm.metadata"
Index: lib/CodeGen/CGDeclCXX.cpp
===================================================================
--- lib/CodeGen/CGDeclCXX.cpp
+++ lib/CodeGen/CGDeclCXX.cpp
@@ -467,7 +467,10 @@
   } else if (auto *IPA = D->getAttr<InitPriorityAttr>()) {
     OrderGlobalInits Key(IPA->getPriority(), PrioritizedCXXGlobalInits.size());
     PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn));
-  } else if (isTemplateInstantiation(D->getTemplateSpecializationKind())) {
+  } else if (isTemplateInstantiation(D->getTemplateSpecializationKind()) ||
+             (getTarget().getCXXABI().isMicrosoft() &&
+              D->isInlineSpecified() && D->isStaticDataMember() &&
+              getLangOpts().CPlusPlus17)) {
     // C++ [basic.start.init]p2:
     //   Definitions of explicitly specialized class template static data
     //   members have ordered initialization. Other class template static data
@@ -481,6 +484,11 @@
     // minor startup time optimization.  In the MS C++ ABI, there are no guard
     // variables, so this COMDAT key is required for correctness.
     AddGlobalCtor(Fn, 65535, COMDATKey);
+    if (getTarget().getCXXABI().isMicrosoft() && COMDATKey) {
+      // In The MS C++, MS add template static data member in the linker
+      // drective.
+      addUsedGlobal(COMDATKey);
+    }
   } else if (D->hasAttr<SelectAnyAttr>()) {
     // SelectAny globals will be comdat-folded. Put the initializer into a
     // COMDAT group associated with the global, so the initializers get folded
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to