Skip to content

Commit cd4d6d7

Browse files
committed
PR48030: Fix COMDAT-related linking problem with C++ thread_local static data members.
Previously when emitting a C++ guarded initializer, we tried to work out what the enclosing function would be used for and added it to the COMDAT containing the variable if we thought that doing so would be correct. But this was done from a context in which we didn't -- and realistically couldn't -- correctly infer how the enclosing function would be used. Instead, add the initialization function to a COMDAT from the code that creates it, in the case where it makes sense to do so: when we know that the one and only reference to the initialization function is in @llvm.global.ctors and that reference is in the same COMDAT. Reviewed By: rjmccall Differential Revision: https://reviews.llvm.org/D108680
1 parent 977eeb0 commit cd4d6d7

File tree

6 files changed

+45
-20
lines changed

6 files changed

+45
-20
lines changed

clang/lib/CodeGen/CGDeclCXX.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,16 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,
581581
// llvm.used to prevent linker GC.
582582
addUsedGlobal(COMDATKey);
583583
}
584+
585+
// If we used a COMDAT key for the global ctor, the init function can be
586+
// discarded if the global ctor entry is discarded.
587+
// FIXME: Do we need to restrict this to ELF and Wasm?
588+
llvm::Comdat *C = Addr->getComdat();
589+
if (COMDATKey && C &&
590+
(getTarget().getTriple().isOSBinFormatELF() ||
591+
getTarget().getTriple().isOSBinFormatWasm())) {
592+
Fn->setComdat(C);
593+
}
584594
} else {
585595
I = DelayedCXXInitPosition.find(D); // Re-do lookup in case of re-hash.
586596
if (I == DelayedCXXInitPosition.end()) {

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,11 +2445,6 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
24452445
(CGM.getTarget().getTriple().isOSBinFormatELF() ||
24462446
CGM.getTarget().getTriple().isOSBinFormatWasm())) {
24472447
guard->setComdat(C);
2448-
// An inline variable's guard function is run from the per-TU
2449-
// initialization function, not via a dedicated global ctor function, so
2450-
// we can't put it in a comdat.
2451-
if (!NonTemplateInline)
2452-
CGF.CurFn->setComdat(C);
24532448
} else if (CGM.supportsCOMDAT() && guard->isWeakForLinker()) {
24542449
guard->setComdat(CGM.getModule().getOrInsertComdat(guard->getName()));
24552450
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
2+
3+
// PR48030
4+
5+
template<typename T> struct TLS { static thread_local T *mData; };
6+
inline decltype(nullptr) non_constant_initializer() { return nullptr; }
7+
template<typename T> thread_local T *TLS<T>::mData = non_constant_initializer();
8+
struct S {};
9+
S *current() { return TLS<S>::mData; };
10+
11+
// CHECK-DAG: @_ZN3TLSI1SE5mDataE = linkonce_odr thread_local global {{.*}}, comdat,
12+
// CHECK-DAG: @_ZGVN3TLSI1SE5mDataE = linkonce_odr thread_local global {{.*}}, comdat($_ZN3TLSI1SE5mDataE),
13+
// CHECK-DAG: @_ZTHN3TLSI1SE5mDataE = linkonce_odr alias {{.*}} @__cxx_global_var_init
14+
15+
// CHECK-LABEL: define {{.*}} @_Z7currentv()
16+
// CHECK: call {{.*}} @_ZTWN3TLSI1SE5mDataE()
17+
18+
// CHECK-LABEL: define weak_odr hidden {{.*}} @_ZTWN3TLSI1SE5mDataE() {{.*}} comdat {
19+
// CHECK: call void @_ZTHN3TLSI1SE5mDataE()
20+
// CHECK: ret {{.*}} @_ZN3TLSI1SE5mDataE
21+
22+
// Unlike for a global, the global initialization function must not be in a
23+
// COMDAT with the variable, because it is referenced from the _ZTH function
24+
// which is outside that COMDAT.
25+
//
26+
// CHECK-NOT: define {{.*}} @__cxx_global_var_init{{.*}}comdat

clang/test/CodeGenCXX/cxx11-thread-local.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,7 @@ int f() {
179179

180180
// LINUX_AIX: define internal void @[[VF_M_INIT]]()
181181
// DARWIN: define internal cxx_fast_tlscc void @[[VF_M_INIT]]()
182-
// LINUX-SAME: comdat($_ZN1VIfE1mE)
183-
// DARWIN-NOT: comdat
182+
// CHECK-NOT: comdat
184183
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1VIfE1mE to i8*)
185184
// CHECK: %[[VF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
186185
// CHECK: br i1 %[[VF_M_INITIALIZED]],
@@ -192,8 +191,7 @@ int f() {
192191

193192
// LINUX_AIX: define internal void @[[XF_M_INIT]]()
194193
// DARWIN: define internal cxx_fast_tlscc void @[[XF_M_INIT]]()
195-
// LINUX-SAME: comdat($_ZN1XIfE1mE)
196-
// DARWIN-NOT: comdat
194+
// CHECK-NOT: comdat
197195
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1XIfE1mE to i8*)
198196
// CHECK: %[[XF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
199197
// CHECK: br i1 %[[XF_M_INITIALIZED]],
@@ -313,8 +311,7 @@ void set_anon_i() {
313311

314312
// LINUX_AIX: define internal void @[[V_M_INIT]]()
315313
// DARWIN: define internal cxx_fast_tlscc void @[[V_M_INIT]]()
316-
// LINUX-SAME: comdat($_ZN1VIiE1mE)
317-
// DARWIN-NOT: comdat
314+
// CHECK-NOT: comdat
318315
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*)
319316
// CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
320317
// CHECK: br i1 %[[V_M_INITIALIZED]],
@@ -326,8 +323,7 @@ void set_anon_i() {
326323

327324
// LINUX_AIX: define internal void @[[X_M_INIT]]()
328325
// DARWIN: define internal cxx_fast_tlscc void @[[X_M_INIT]]()
329-
// LINUX-SAME: comdat($_ZN1XIiE1mE)
330-
// DARWIN-NOT: comdat
326+
// CHECK-NOT: comdat
331327
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1XIiE1mE to i8*)
332328
// CHECK: %[[X_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
333329
// CHECK: br i1 %[[X_M_INITIALIZED]],

clang/test/CodeGenCXX/cxx1z-inline-variables.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ const int &yib = Y<int>::b;
9090
// CHECK-LABEL: define {{.*}}global_var_init
9191
// CHECK: call i32 @_Z1fv
9292

93-
// CHECK-LABEL: define {{.*}}global_var_init
94-
// CHECK-NOT: comdat
95-
// CHECK-SAME: {{$}}
93+
// CHECK-LABEL: define {{.*}}global_var_init{{.*}} comdat($b)
9694
// CHECK: load atomic {{.*}} acquire, align
9795
// CHECK: br
9896
// CHECK: __cxa_guard_acquire(i64* @_ZGV1b)

clang/test/OpenMP/threadprivate_codegen.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3847,7 +3847,7 @@ int foobar() {
38473847
//
38483848
//
38493849
// CHECK-TLS1-LABEL: define {{[^@]+}}@__cxx_global_var_init.3
3850-
// CHECK-TLS1-SAME: () #[[ATTR0]] comdat($_ZN2STI2S4E2stE) {
3850+
// CHECK-TLS1-SAME: () #[[ATTR0]] {
38513851
// CHECK-TLS1-NEXT: entry:
38523852
// CHECK-TLS1-NEXT: [[TMP0:%.*]] = load i8, i8* bitcast (i64* @_ZGVN2STI2S4E2stE to i8*), align 8
38533853
// CHECK-TLS1-NEXT: [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0
@@ -4373,7 +4373,7 @@ int foobar() {
43734373
//
43744374
//
43754375
// CHECK-TLS2-LABEL: define {{[^@]+}}@__cxx_global_var_init.3
4376-
// CHECK-TLS2-SAME: () #[[ATTR6]] comdat($_ZN2STI2S4E2stE) {
4376+
// CHECK-TLS2-SAME: () #[[ATTR6]] {
43774377
// CHECK-TLS2-NEXT: entry:
43784378
// CHECK-TLS2-NEXT: [[TMP0:%.*]] = load i8, i8* bitcast (i64* @_ZGVN2STI2S4E2stE to i8*), align 8
43794379
// CHECK-TLS2-NEXT: [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0
@@ -4906,7 +4906,7 @@ int foobar() {
49064906
//
49074907
//
49084908
// CHECK-TLS3-LABEL: define {{[^@]+}}@__cxx_global_var_init.3
4909-
// CHECK-TLS3-SAME: () #[[ATTR0]] comdat($_ZN2STI2S4E2stE) !dbg [[DBG289:![0-9]+]] {
4909+
// CHECK-TLS3-SAME: () #[[ATTR0]] !dbg [[DBG289:![0-9]+]] {
49104910
// CHECK-TLS3-NEXT: entry:
49114911
// CHECK-TLS3-NEXT: [[TMP0:%.*]] = load i8, i8* bitcast (i64* @_ZGVN2STI2S4E2stE to i8*), align 8, !dbg [[DBG290:![0-9]+]]
49124912
// CHECK-TLS3-NEXT: [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0, !dbg [[DBG290]]
@@ -5459,7 +5459,7 @@ int foobar() {
54595459
//
54605460
//
54615461
// CHECK-TLS4-LABEL: define {{[^@]+}}@__cxx_global_var_init.3
5462-
// CHECK-TLS4-SAME: () #[[ATTR7]] comdat($_ZN2STI2S4E2stE) !dbg [[DBG289:![0-9]+]] {
5462+
// CHECK-TLS4-SAME: () #[[ATTR7]] !dbg [[DBG289:![0-9]+]] {
54635463
// CHECK-TLS4-NEXT: entry:
54645464
// CHECK-TLS4-NEXT: [[TMP0:%.*]] = load i8, i8* bitcast (i64* @_ZGVN2STI2S4E2stE to i8*), align 8, !dbg [[DBG290:![0-9]+]]
54655465
// CHECK-TLS4-NEXT: [[GUARD_UNINITIALIZED:%.*]] = icmp eq i8 [[TMP0]], 0, !dbg [[DBG290]]

0 commit comments

Comments
 (0)