jamieschmeiser created this revision.
jamieschmeiser added reviewers: hubert.reinterpretcast, sfertile, 
cebowleratibm, DiggerLin, rsmith.
jamieschmeiser requested review of this revision.
Herald added a project: clang.

Changes to support thread_local storage on AIX.

      

The AIX linker will produce errors on unresolved weak symbols.  Change the
generated code to not check for the initialization function but just call
it and ensure that it always exists.  Also, the AIX atexit routine has a
different name (and signature) so call it correctly.  Update the lit tests
to test on AIX appropriately.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D104420

Files:
  clang/lib/CodeGen/ItaniumCXXABI.cpp
  clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
  clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp
  clang/test/CodeGenCXX/cxx11-thread-local.cpp

Index: clang/test/CodeGenCXX/cxx11-thread-local.cpp
===================================================================
--- clang/test/CodeGenCXX/cxx11-thread-local.cpp
+++ clang/test/CodeGenCXX/cxx11-thread-local.cpp
@@ -9,11 +9,13 @@
 // RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -femulated-tls -emit-llvm %s -o - \
 // RUN:     -triple x86_64-linux-gnu 2>&1 | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s
 // RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefix=CHECK --check-prefix=AIX %s
 
 int f();
 int g();
 
 // LINUX-DAG: @a ={{.*}} thread_local global i32 0
+// AIX-DAG: @a ={{.*}} thread_local global i32 0
 // DARWIN-DAG: @a = internal thread_local global i32 0
 thread_local int a = f();
 extern thread_local int b;
@@ -24,6 +26,7 @@
 
 struct U { static thread_local int m; };
 // LINUX-DAG: @_ZN1U1mE ={{.*}} thread_local global i32 0
+// AIX-DAG: @_ZN1U1mE ={{.*}} thread_local global i32 0
 // DARWIN-DAG: @_ZN1U1mE = internal thread_local global i32 0
 thread_local int U::m = f();
 
@@ -90,8 +93,10 @@
 // CHECK-DAG: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]]
 
 // LINUX-DAG: @_ZTH1a ={{.*}} alias void (), void ()* @__tls_init
+// AIX-DAG: @_ZTH1a ={{.*}} alias void (), void ()* @__tls_init
 // DARWIN-DAG: @_ZTH1a = internal alias void (), void ()* @__tls_init
 // LINUX-DAG: @_ZTHN1U1mE ={{.*}} alias void (), void ()* @__tls_init
+// AIX-DAG: @_ZTHN1U1mE ={{.*}} alias void (), void ()* @__tls_init
 // DARWIN-DAG: @_ZTHN1U1mE = internal alias void (), void ()* @__tls_init
 // CHECK-DAG: @_ZTHN1VIiE1mE = linkonce_odr alias void (), void ()* @[[V_M_INIT:[^, ]*]]
 // CHECK-DAG: @_ZTHN1XIiE1mE = linkonce_odr alias void (), void ()* @[[X_M_INIT:[^, ]*]]
@@ -106,16 +111,16 @@
 // Individual variable initialization functions:
 
 // CHECK: define {{.*}} @[[A_INIT:.*]]()
-// CHECK: call i32 @_Z1fv()
+// CHECK: call{{.*}} i32 @_Z1fv()
 // CHECK-NEXT: store i32 {{.*}}, i32* @a, align 4
 
 // CHECK-LABEL: define{{.*}} i32 @_Z1fv()
 int f() {
   // CHECK: %[[GUARD:.*]] = load i8, i8* @_ZGVZ1fvE1n, align 1
   // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0
-  // CHECK: br i1 %[[NEED_INIT]]
+  // CHECK: br i1 %[[NEED_INIT]]{{.*}}
 
-  // CHECK: %[[CALL:.*]] = call i32 @_Z1gv()
+  // CHECK: %[[CALL:.*]] = call{{.*}} i32 @_Z1gv()
   // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4
   // CHECK: store i8 1, i8* @_ZGVZ1fvE1n
   // CHECK: br label
@@ -127,54 +132,67 @@
 
 // CHECK: define {{.*}} @[[C_INIT:.*]]()
 // LINUX: call i32* @_ZTW1b()
+// AIX: call i32* @_ZTW1b()
 // DARWIN: call cxx_fast_tlscc i32* @_ZTW1b()
 // CHECK-NEXT: load i32, i32* %{{.*}}, align 4
 // CHECK-NEXT: store i32 %{{.*}}, i32* @c, align 4
 
 // LINUX-LABEL: define linkonce_odr hidden i32* @_ZTW1b()
+// AIX-LABEL: define linkonce_odr hidden i32* @_ZTW1b()
 // LINUX: br i1 icmp ne (void ()* @_ZTH1b, void ()* null),
 // not null:
 // LINUX: call void @_ZTH1b()
+// AIX-NOT: br i1 icmp ne (void ()* @_ZTH1b, void ()* null),
+// AIX: call void @_ZTH1b()
 // LINUX: br label
 // finally:
 // LINUX: ret i32* @b
+// AIX: ret i32* @b
 // DARWIN-LABEL: declare cxx_fast_tlscc i32* @_ZTW1b()
 // There is no definition of the thread wrapper on Darwin for external TLV.
 
 // CHECK: define {{.*}} @[[D_INIT:.*]]()
-// CHECK: call i32 @_Z1gv()
+// CHECK: call{{.*}} i32 @_Z1gv()
 // CHECK-NEXT: store i32 %{{.*}}, i32* @_ZL1d, align 4
 
 // CHECK: define {{.*}} @[[U_M_INIT:.*]]()
-// CHECK: call i32 @_Z1fv()
+// CHECK: call{{.*}} i32 @_Z1fv()
 // CHECK-NEXT: store i32 %{{.*}}, i32* @_ZN1U1mE, align 4
 
 // CHECK: define {{.*}} @[[E_INIT:.*]]()
 // LINUX: call i32* @_ZTWN1VIiE1mE()
+// AIX: call i32* @_ZTWN1VIiE1mE()
 // DARWIN: call cxx_fast_tlscc i32* @_ZTWN1VIiE1mE()
 // CHECK-NEXT: load i32, i32* %{{.*}}, align 4
 // LINUX: call {{.*}}* @_ZTWN1XIiE1mE()
+// AIX: call {{.*}}* @_ZTWN1XIiE1mE()
 // DARWIN: call cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE()
 // CHECK: store {{.*}} @e
 
 // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE()
+// AIX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE()
 // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1VIiE1mE()
 // LINUX: call void @_ZTHN1VIiE1mE()
+// AIX: call void @_ZTHN1VIiE1mE()
 // DARWIN: call cxx_fast_tlscc void @_ZTHN1VIiE1mE()
 // CHECK: ret i32* @_ZN1VIiE1mE
 
 // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1WIiE1mE()
+// AIX-LABEL: define weak_odr hidden i32* @_ZTWN1WIiE1mE()
 // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1WIiE1mE()
 // CHECK-NOT: call
 // CHECK: ret i32* @_ZN1WIiE1mE
 
 // LINUX-LABEL: define weak_odr hidden {{.*}}* @_ZTWN1XIiE1mE()
+// AIX-LABEL: define weak_odr hidden {{.*}}* @_ZTWN1XIiE1mE()
 // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE()
 // LINUX: call void @_ZTHN1XIiE1mE()
+// AIX: call void @_ZTHN1XIiE1mE()
 // DARWIN: call cxx_fast_tlscc void @_ZTHN1XIiE1mE()
 // CHECK: ret {{.*}}* @_ZN1XIiE1mE
 
 // LINUX: define internal void @[[VF_M_INIT]]()
+// AIX: define internal void @[[VF_M_INIT]]()
 // DARWIN: define internal cxx_fast_tlscc void @[[VF_M_INIT]]()
 // LINUX-SAME: comdat($_ZN1VIfE1mE)
 // DARWIN-NOT: comdat
@@ -182,12 +200,13 @@
 // CHECK: %[[VF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
 // CHECK: br i1 %[[VF_M_INITIALIZED]],
 // need init:
-// CHECK: call i32 @_Z1gv()
+// CHECK: call{{.*}} i32 @_Z1gv()
 // CHECK: store i32 %{{.*}}, i32* @_ZN1VIfE1mE, align 4
 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1VIfE1mE to i8*)
 // CHECK: br label
 
 // LINUX: define internal void @[[XF_M_INIT]]()
+// AIX: define internal void @[[XF_M_INIT]]()
 // DARWIN: define internal cxx_fast_tlscc void @[[XF_M_INIT]]()
 // LINUX-SAME: comdat($_ZN1XIfE1mE)
 // DARWIN-NOT: comdat
@@ -196,30 +215,43 @@
 // CHECK: br i1 %[[XF_M_INITIALIZED]],
 // need init:
 // LINUX: call {{.*}}__cxa_thread_atexit
+// AIX: call {{.*}}__pt_atexit_np
 // DARWIN: call {{.*}}_tlv_atexit
 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1XIfE1mE to i8*)
 // CHECK: br label
 
 // LINUX: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*)
+// AIX: declare i32 @__pt_atexit_np(i32, i32 (i32, ...)*, ...)
 // DARWIN: declare i32 @_tlv_atexit(void (i8*)*, i8*, i8*)
 
 // DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1VIcE1mE()
 // LINUX: define linkonce_odr hidden i32* @_ZTWN1VIcE1mE() {{#[0-9]+}} comdat {
+// AIX: define linkonce_odr hidden i32* @_ZTWN1VIcE1mE() {{#[0-9]+}} {
 // LINUX: br i1 icmp ne (void ()* @_ZTHN1VIcE1mE,
 // LINUX: call void @_ZTHN1VIcE1mE()
+// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1VIcE1mE
+// AIX: call void @_ZTHN1VIcE1mE()
 // LINUX: ret i32* @_ZN1VIcE1mE
+// AIX: ret i32* @_ZN1VIcE1mE
 
 // DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1WIcE1mE()
 // LINUX: define linkonce_odr hidden i32* @_ZTWN1WIcE1mE() {{#[0-9]+}} comdat {
+// AIX: define linkonce_odr hidden i32* @_ZTWN1WIcE1mE() {{#[0-9]+}} {
 // LINUX: br i1 icmp ne (void ()* @_ZTHN1WIcE1mE,
 // LINUX: call void @_ZTHN1WIcE1mE()
+// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1WIcE1mE,
+// AIX: call void @_ZTHN1WIcE1mE()
 // LINUX: ret i32* @_ZN1WIcE1mE
+// AIX: ret i32* @_ZN1WIcE1mE
 
 // DARWIN: declare cxx_fast_tlscc {{.*}}* @_ZTWN1XIcE1mE()
 // LINUX: define linkonce_odr hidden {{.*}}* @_ZTWN1XIcE1mE() {{#[0-9]+}} comdat {
 // LINUX: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE,
 // LINUX: call void @_ZTHN1XIcE1mE()
+// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE,
+// AIX: call void @_ZTHN1XIcE1mE()
 // LINUX: ret {{.*}}* @_ZN1XIcE1mE
+// AIX: ret {{.*}}* @_ZN1XIcE1mE
 
 struct S { S(); ~S(); };
 struct T { ~T(); };
@@ -229,6 +261,7 @@
   // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1s
   // CHECK: call void @_ZN1SC1Ev(%struct.S* {{[^,]*}} @_ZZ8tls_dtorvE1s)
   // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle
+  // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}@_ZN1SD1Ev {{.*}})
   // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle
   // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s
   static thread_local S s;
@@ -236,6 +269,7 @@
   // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1t
   // CHECK-NOT: _ZN1T
   // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle
+  // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}@_ZN1TD1Ev {{.*}})
   // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle
   // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t
   static thread_local T t;
@@ -243,6 +277,7 @@
   // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1u
   // CHECK: call void @_ZN1SC1Ev(%struct.S* {{[^,]*}} @_ZGRZ8tls_dtorvE1u_)
   // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u_{{.*}} @__dso_handle
+  // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}@_ZN1SD1Ev {{.*}})
   // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u_{{.*}} @__dso_handle
   // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u
   static thread_local const S &u = S();
@@ -262,6 +297,7 @@
 // CHECK: define {{.*}} @_ZN7PR192541fEv(
 int PR19254::f() {
   // LINUX: call void @_ZTHN7PR192541nE(
+  // AIX: call void @_ZTHN7PR192541nE(
   // DARWIN: call cxx_fast_tlscc i32* @_ZTWN7PR192541nE(
   return this->n;
 }
@@ -273,9 +309,11 @@
   anon_i = 2;
 }
 // LINUX-LABEL: define internal i32* @_ZTWN12_GLOBAL__N_16anon_iE()
+// AIX-LABEL: define internal i32* @_ZTWN12_GLOBAL__N_16anon_iE()
 // DARWIN-LABEL: define internal cxx_fast_tlscc i32* @_ZTWN12_GLOBAL__N_16anon_iE()
 
 // LINUX: define internal void @[[V_M_INIT]]()
+// AIX: define internal void @[[V_M_INIT]]()
 // DARWIN: define internal cxx_fast_tlscc void @[[V_M_INIT]]()
 // LINUX-SAME: comdat($_ZN1VIiE1mE)
 // DARWIN-NOT: comdat
@@ -283,12 +321,13 @@
 // CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
 // CHECK: br i1 %[[V_M_INITIALIZED]],
 // need init:
-// CHECK: call i32 @_Z1gv()
+// CHECK: call{{.*}} i32 @_Z1gv()
 // CHECK: store i32 %{{.*}}, i32* @_ZN1VIiE1mE, align 4
 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*)
 // CHECK: br label
 
 // LINUX: define internal void @[[X_M_INIT]]()
+// AIX: define internal void @[[X_M_INIT]]()
 // DARWIN: define internal cxx_fast_tlscc void @[[X_M_INIT]]()
 // LINUX-SAME: comdat($_ZN1XIiE1mE)
 // DARWIN-NOT: comdat
@@ -297,6 +336,7 @@
 // CHECK: br i1 %[[X_M_INITIALIZED]],
 // need init:
 // LINUX: call {{.*}}__cxa_thread_atexit
+// AIX: call {{.*}}__pt_atexit_np
 // DARWIN: call {{.*}}_tlv_atexit
 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1XIiE1mE to i8*)
 // CHECK: br label
@@ -323,8 +363,10 @@
 
 
 // LINUX: define weak_odr hidden i32* @_ZTW1a()
+// AIX: define weak_odr hidden i32* @_ZTW1a()
 // DARWIN: define cxx_fast_tlscc i32* @_ZTW1a()
 // LINUX:   call void @_ZTH1a()
+// AIX:   call void @_ZTH1a()
 // DARWIN: call cxx_fast_tlscc void @_ZTH1a()
 // CHECK:   ret i32* @a
 // CHECK: }
@@ -336,11 +378,23 @@
 // CHECK-NOT: define {{.*}} @_ZTWL1d()
 
 // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE()
+// AIX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE()
 // DARWIN-LABEL: define cxx_fast_tlscc i32* @_ZTWN1U1mE()
 // LINUX: call void @_ZTHN1U1mE()
+// AIX: call void @_ZTHN1U1mE()
 // DARWIN: call cxx_fast_tlscc void @_ZTHN1U1mE()
 // CHECK: ret i32* @_ZN1U1mE
 
 // LINUX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]]
+// AIX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]]
+
+// AIX: define extern_weak void @_ZTHN1WIiE1mE(){{.*}} {
+// AIX-NEXT: ret void
+// AIX-NEXT: }
+// CHECK-NOT: @_ZTHN1WIfE1mE =
+// AIX: define extern_weak void @_ZTHN1WIfE1mE(){{.*}} {
+// AIX-NEXT: ret void
+// AIX-NEXT: }
 
 // LINUX: attributes [[ATTR]] = { {{.+}} }
+// AIX: attributes [[ATTR]] = { {{.+}} }
Index: clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp
===================================================================
--- clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp
+++ clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=LINUX %s
 // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=DARWIN %s
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefix=AIX %s
 
 // Regression test for PR40327
 
@@ -12,6 +13,11 @@
 // DARWIN: @hidden_tls = internal thread_local global i32
 // DARWIN: define cxx_fast_tlscc i32* @_ZTW11default_tls()
 // DARWIN: define hidden cxx_fast_tlscc i32* @_ZTW10hidden_tls()
+//
+// AIX: @default_tls = thread_local global i32
+// AIX: @hidden_tls = thread_local global i32
+// AIX: define weak_odr hidden i32* @_ZTW11default_tls()
+// AIX: define weak_odr hidden i32* @_ZTW10hidden_tls()
 
 __attribute__((visibility("default"))) thread_local int default_tls;
 __attribute__((visibility("hidden"))) thread_local int hidden_tls;
Index: clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
===================================================================
--- clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
+++ clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
@@ -1,13 +1,16 @@
 // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s
 // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefix=CHECK --check-prefix=AIX %s
 
 int &f();
 
 // LINUX: @r ={{.*}} thread_local global i32* null
+// AIX: @r ={{.*}} thread_local global i32* null
 // DARWIN: @r = internal thread_local global i32* null
 thread_local int &r = f();
 
 // LINUX: @_ZTH1r ={{.*}} alias void (), void ()* @__tls_init
+// AIXX: @_ZTH1r ={{.*}} alias void (), void ()* @__tls_init
 // DARWIN: @_ZTH1r = internal alias void (), void ()* @__tls_init
 
 int &g() { return r; }
@@ -18,19 +21,24 @@
 
 // CHECK-LABEL: define{{.*}} nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) i32* @_Z1gv()
 // LINUX: call i32* @_ZTW1r()
+// AIX: call i32* @_ZTW1r()
 // DARWIN: call cxx_fast_tlscc i32* @_ZTW1r()
 // CHECK: ret i32* %{{.*}}
 
 // LINUX: define weak_odr hidden i32* @_ZTW1r() [[ATTR0:#[0-9]+]] comdat {
+// AIX: define weak_odr hidden i32* @_ZTW1r() [[ATTR0:#[0-9]+]] {
 // DARWIN: define cxx_fast_tlscc i32* @_ZTW1r() [[ATTR1:#[0-9]+]] {
 // LINUX: call void @_ZTH1r()
+// AIX: call void @_ZTH1r()
 // DARWIN: call cxx_fast_tlscc void @_ZTH1r()
 // CHECK: load i32*, i32** @r, align 8
 // CHECK: ret i32* %{{.*}}
 
 // LINUX-LABEL: define internal void @__tls_init()
+// AIX-LABEL: define internal void @__tls_init()
 // DARWIN-LABEL: define internal cxx_fast_tlscc void @__tls_init()
 // CHECK: call void @[[R_INIT]]()
 
 // LINUX: attributes [[ATTR0]] = { {{.*}}"target-features"{{.*}} }
+// AIX: attributes [[ATTR0]] = { {{.*}}"target-features"{{.*}} }
 // DARWIN: attributes [[ATTR1]] = { {{.*}}nounwind{{.*}}"target-features"{{.*}}  }
Index: clang/lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -337,14 +337,16 @@
   /// Determine whether we will definitely emit this variable with a constant
   /// initializer, either because the language semantics demand it or because
   /// we know that the initializer is a constant.
-  bool isEmittedWithConstantInitializer(const VarDecl *VD) const {
+  // Ignore all attributes except ConstInit when IgnoreAttrs is true.
+  bool isEmittedWithConstantInitializer(const VarDecl *VD,
+                                        bool IgnoreAttrs = false) const {
     VD = VD->getMostRecentDecl();
     if (VD->hasAttr<ConstInitAttr>())
       return true;
 
     // All later checks examine the initializer specified on the variable. If
     // the variable is weak, such examination would not be correct.
-    if (VD->isWeak() || VD->hasAttr<SelectAnyAttr>())
+    if (!IgnoreAttrs && (VD->isWeak() || VD->hasAttr<SelectAnyAttr>()))
       return false;
 
     const VarDecl *InitDecl = VD->getInitializingDeclaration();
@@ -2551,6 +2553,8 @@
 static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
                                         llvm::FunctionCallee dtor,
                                         llvm::Constant *addr, bool TLS) {
+  assert(!CGF.getTarget().getTriple().isOSAIX() &&
+         "unexpected call to emitGlobalDtorWithCXAAtExit");
   assert((TLS || CGF.getTypes().getCodeGenOpts().CXAAtExit) &&
          "__cxa_atexit is disabled");
   const char *Name = "__cxa_atexit";
@@ -2956,6 +2960,32 @@
     }
 
     llvm::LLVMContext &Context = CGM.getModule().getContext();
+
+    // The linker on AIX is not happy with missing weak symbols.  However,
+    // other TUs will not know whether the initialization routine exists
+    // so create a weak, empty, init function to satisfy the linker.
+    // This is needed whenever a thread wrapper function is not used, and
+    // also when the symbol is weak.
+    if (CGM.getTriple().isOSAIX() &&
+        isEmittedWithConstantInitializer(VD, true) &&
+        !VD->needsDestruction(getContext())) {
+      // Emit a weak global function referring to the initialization function.
+      // This function will not exist if the TU defining the thread_local
+      // variable in question does not need any dynamic initialization for
+      // its thread_local variables.
+      llvm::Function *Func = llvm::Function::Create(
+          InitFnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(),
+          &CGM.getModule());
+      const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction();
+      CGM.SetLLVMFunctionAttributes(GlobalDecl(), FI,
+                                    cast<llvm::Function>(Func),
+                                    /*IsThunk=*/false);
+      // Create a function body that just returns
+      llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Func);
+      CGBuilderTy Builder(CGM, Entry);
+      Builder.CreateRetVoid();
+    }
+
     llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper);
     CGBuilderTy Builder(CGM, Entry);
     if (HasConstantInitialization) {
@@ -2970,7 +3000,14 @@
           Fn->setCallingConv(llvm::CallingConv::CXX_FAST_TLS);
         }
       }
-    } else {
+    } else if (CGM.getTriple().isOSAIX())
+      // On AIX, all thread_local vars will have init routines regardless of
+      // whether they are const-initialized or not.  Since the routine is
+      // guaranteed to exist, we can unconditionally call it without testing
+      // for its existance.  This avoids potentially unresolved weak symbols
+      // which the AIX linker isn't happy with.
+      Builder.CreateCall(InitFnTy, Init);
+    else {
       // Don't know whether we have an init function. Call it if it exists.
       llvm::Value *Have = Builder.CreateIsNotNull(Init);
       llvm::BasicBlock *InitBB = llvm::BasicBlock::Create(Context, "", Wrapper);
@@ -4733,8 +4770,27 @@
 void XLCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
                                   llvm::FunctionCallee dtor,
                                   llvm::Constant *addr) {
-  if (D.getTLSKind() != VarDecl::TLS_None)
-    llvm::report_fatal_error("thread local storage not yet implemented on AIX");
+  if (D.getTLSKind() != VarDecl::TLS_None) {
+    // atexit routine expects "int(*)(int,...)"
+    llvm::Type *FpTy =
+        llvm::FunctionType::get(CGM.IntTy, CGM.IntTy, true)->getPointerTo();
+
+    // extern "C" int __pt_atexit_np(int flags, int(*)(int,...), ...);
+    llvm::FunctionType *AtExitTy =
+        llvm::FunctionType::get(CGM.IntTy, {CGM.IntTy, FpTy}, true);
+
+    // Fetch the actual function.
+    llvm::FunctionCallee AtExit =
+        CGM.CreateRuntimeFunction(AtExitTy, "__pt_atexit_np");
+
+    // First param is flags and must be 0
+    llvm::Value* Arg1 = llvm::Constant::getNullValue(CGM.IntTy);
+    // Second param is function ptr
+    llvm::Value* Arg2 = llvm::ConstantExpr::getBitCast(
+        cast<llvm::Constant>(dtor.getCallee()), FpTy);
+    CGF.EmitNounwindRuntimeCall(AtExit, {Arg1, Arg2});
+    return;
+  }
 
   // Create __dtor function for the var decl.
   llvm::Function *dtorStub = CGF.createAtExitStub(D, dtor, addr);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to