Skip to content

Commit

Permalink
Improve handling of instantiated thread_local variables in Itanium C+…
Browse files Browse the repository at this point in the history
…+ ABI.

 * Do not initialize these variables when initializing the rest of the
   thread_locals in the TU; they have unordered initialization so they can be
   initialized by themselves.

   This fixes a rejects-valid bug: we would make the per-variable initializer
   function internal, but put it in a comdat keyed off the variable, resulting
   in link errors when the comdat is selected from a different TU (as the per
   TU TLS init function tries to call an init function that does not exist).

 * On Darwin, when we decide that we're not going to emit a thread wrapper
   function at all, demote its linkage to External. Fixes a verifier failure
   on explicit instantiation of a thread_local variable on Darwin.

llvm-svn: 291865
  • Loading branch information
zygoloid committed Jan 13, 2017
1 parent 7150343 commit fbe2369
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 54 deletions.
3 changes: 0 additions & 3 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Expand Up @@ -353,9 +353,6 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D,

if (D->getTLSKind()) {
// FIXME: Should we support init_priority for thread_local?
// FIXME: Ideally, initialization of instantiated thread_local static data
// members of class templates should not trigger initialization of other
// entities in the TU.
// FIXME: We only need to register one __cxa_thread_atexit function for the
// entire TU.
CXXThreadLocalInits.push_back(Fn);
Expand Down
42 changes: 31 additions & 11 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Expand Up @@ -2272,7 +2272,21 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs(
ArrayRef<llvm::Function *> CXXThreadLocalInits,
ArrayRef<const VarDecl *> CXXThreadLocalInitVars) {
llvm::Function *InitFunc = nullptr;
if (!CXXThreadLocalInits.empty()) {

// Separate initializers into those with ordered (or partially-ordered)
// initialization and those with unordered initialization.
llvm::SmallVector<llvm::Function *, 8> OrderedInits;
llvm::SmallDenseMap<const VarDecl *, llvm::Function *> UnorderedInits;
for (unsigned I = 0; I != CXXThreadLocalInits.size(); ++I) {
if (isTemplateInstantiation(
CXXThreadLocalInitVars[I]->getTemplateSpecializationKind()))
UnorderedInits[CXXThreadLocalInitVars[I]->getCanonicalDecl()] =
CXXThreadLocalInits[I];
else
OrderedInits.push_back(CXXThreadLocalInits[I]);
}

if (!OrderedInits.empty()) {
// Generate a guarded initialization function.
llvm::FunctionType *FTy =
llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false);
Expand All @@ -2289,24 +2303,28 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs(
CharUnits GuardAlign = CharUnits::One();
Guard->setAlignment(GuardAlign.getQuantity());

CodeGenFunction(CGM)
.GenerateCXXGlobalInitFunc(InitFunc, CXXThreadLocalInits,
Address(Guard, GuardAlign));
CodeGenFunction(CGM).GenerateCXXGlobalInitFunc(InitFunc, OrderedInits,
Address(Guard, GuardAlign));
// On Darwin platforms, use CXX_FAST_TLS calling convention.
if (CGM.getTarget().getTriple().isOSDarwin()) {
InitFunc->setCallingConv(llvm::CallingConv::CXX_FAST_TLS);
InitFunc->addFnAttr(llvm::Attribute::NoUnwind);
}
}

// Emit thread wrappers.
for (const VarDecl *VD : CXXThreadLocals) {
llvm::GlobalVariable *Var =
cast<llvm::GlobalVariable>(CGM.GetGlobalValue(CGM.getMangledName(VD)));
llvm::Function *Wrapper = getOrCreateThreadLocalWrapper(VD, Var);

// Some targets require that all access to thread local variables go through
// the thread wrapper. This means that we cannot attempt to create a thread
// wrapper or a thread helper.
if (isThreadWrapperReplaceable(VD, CGM) && !VD->hasDefinition())
if (isThreadWrapperReplaceable(VD, CGM) && !VD->hasDefinition()) {
Wrapper->setLinkage(llvm::Function::ExternalLinkage);
continue;
}

// Mangle the name for the thread_local initialization function.
SmallString<256> InitFnName;
Expand All @@ -2322,26 +2340,28 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs(
bool InitIsInitFunc = false;
if (VD->hasDefinition()) {
InitIsInitFunc = true;
if (InitFunc)
llvm::Function *InitFuncToUse = InitFunc;
if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
InitFuncToUse = UnorderedInits.lookup(VD->getCanonicalDecl());
if (InitFuncToUse)
Init = llvm::GlobalAlias::create(Var->getLinkage(), InitFnName.str(),
InitFunc);
InitFuncToUse);
} else {
// 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::FunctionType *FnTy = llvm::FunctionType::get(CGM.VoidTy, false);
Init = llvm::Function::Create(
FnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(),
&CGM.getModule());
Init = llvm::Function::Create(FnTy,
llvm::GlobalVariable::ExternalWeakLinkage,
InitFnName.str(), &CGM.getModule());
const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction();
CGM.SetLLVMFunctionAttributes(nullptr, FI, cast<llvm::Function>(Init));
}

if (Init)
Init->setVisibility(Var->getVisibility());

llvm::Function *Wrapper = getOrCreateThreadLocalWrapper(VD, Var);
llvm::LLVMContext &Context = CGM.getModule().getContext();
llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper);
CGBuilderTy Builder(CGM, Entry);
Expand Down
167 changes: 134 additions & 33 deletions clang/test/CodeGenCXX/cxx11-thread-local.cpp
Expand Up @@ -6,18 +6,18 @@
int f();
int g();

// LINUX: @a = thread_local global i32 0
// DARWIN: @a = internal thread_local global i32 0
// LINUX-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;
// CHECK: @c = global i32 0
// CHECK-DAG: @c = global i32 0
int c = b;
// CHECK: @_ZL1d = internal thread_local global i32 0
// CHECK-DAG: @_ZL1d = internal thread_local global i32 0
static thread_local int d = g();

struct U { static thread_local int m; };
// LINUX: @_ZN1U1mE = thread_local global i32 0
// DARWIN: @_ZN1U1mE = internal thread_local global i32 0
// LINUX-DAG: @_ZN1U1mE = thread_local global i32 0
// DARWIN-DAG: @_ZN1U1mE = internal thread_local global i32 0
thread_local int U::m = f();

namespace MismatchedInitType {
Expand All @@ -35,37 +35,64 @@ namespace MismatchedInitType {
template<typename T> struct V { static thread_local int m; };
template<typename T> thread_local int V<T>::m = g();

// CHECK: @e = global i32 0
int e = V<int>::m;
template<typename T> struct W { static thread_local int m; };
template<typename T> thread_local int W<T>::m = 123;

// CHECK: @_ZN1VIiE1mE = linkonce_odr thread_local global i32 0
struct Dtor { ~Dtor(); };
template<typename T> struct X { static thread_local Dtor m; };
template<typename T> thread_local Dtor X<T>::m;

// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0
// CHECK-DAG: @e = global
void *e = V<int>::m + W<int>::m + &X<int>::m;

// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0
template thread_local int V<float>::m;
template thread_local int W<float>::m;
template thread_local Dtor X<float>::m;

// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global
// CHECK: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0
extern template thread_local int V<char>::m;
extern template thread_local int W<char>::m;
extern template thread_local Dtor X<char>::m;

// CHECK: @_ZZ8tls_dtorvE1t = internal thread_local global
// CHECK: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0
void *e2 = V<char>::m + W<char>::m + &X<char>::m;

// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global
// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0
// CHECK: @_ZGRZ8tls_dtorvE1u_ = internal thread_local global
// CHECK-DAG: @_ZN1VIiE1mE = linkonce_odr thread_local global i32 0
// CHECK-DAG: @_ZN1WIiE1mE = linkonce_odr thread_local global i32 123
// CHECK-DAG: @_ZN1XIiE1mE = linkonce_odr thread_local global {{.*}}
// CHECK-DAG: @_ZN1VIfE1mE = weak_odr thread_local global i32 0
// CHECK-DAG: @_ZN1WIfE1mE = weak_odr thread_local global i32 123
// CHECK-DAG: @_ZN1XIfE1mE = weak_odr thread_local global {{.*}}

// CHECK: @_ZGVN1VIiE1mE = linkonce_odr thread_local global i64 0
// CHECK-DAG: @_ZZ1fvE1n = internal thread_local global i32 0

// CHECK: @__tls_guard = internal thread_local global i8 0
// CHECK-DAG: @_ZGVZ1fvE1n = internal thread_local global i8 0

// CHECK: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]]
// CHECK-DAG: @_ZZ8tls_dtorvE1s = internal thread_local global
// CHECK-DAG: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0

// LINUX: @_ZTH1a = alias void (), void ()* @__tls_init
// DARWIN: @_ZTH1a = internal alias void (), void ()* @__tls_init
// CHECK: @_ZTHL1d = internal alias void (), void ()* @__tls_init
// LINUX: @_ZTHN1U1mE = alias void (), void ()* @__tls_init
// DARWIN: @_ZTHN1U1mE = internal alias void (), void ()* @__tls_init
// CHECK: @_ZTHN1VIiE1mE = linkonce_odr alias void (), void ()* @__tls_init
// CHECK-DAG: @_ZZ8tls_dtorvE1t = internal thread_local global
// CHECK-DAG: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0

// CHECK-DAG: @_ZZ8tls_dtorvE1u = internal thread_local global
// CHECK-DAG: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0
// CHECK-DAG: @_ZGRZ8tls_dtorvE1u_ = internal thread_local global

// CHECK-DAG: @_ZGVN1VIiE1mE = linkonce_odr thread_local global i64 0

// CHECK-DAG: @__tls_guard = internal thread_local global i8 0

// CHECK-DAG: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]]

// LINUX-DAG: @_ZTH1a = alias void (), void ()* @__tls_init
// DARWIN-DAG: @_ZTH1a = internal alias void (), void ()* @__tls_init
// CHECK-DAG: @_ZTHL1d = internal alias void (), void ()* @__tls_init
// LINUX-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-NOT: @_ZTHN1WIiE1mE =
// CHECK-DAG: @_ZTHN1XIiE1mE = linkonce_odr alias void (), void ()* @[[X_M_INIT:[^, ]*]]
// CHECK-DAG: @_ZTHN1VIfE1mE = weak_odr alias void (), void ()* @[[VF_M_INIT:[^, ]*]]
// CHECK-NOT: @_ZTHN1WIfE1mE =
// CHECK-DAG: @_ZTHN1XIfE1mE = weak_odr alias void (), void ()* @[[XF_M_INIT:[^, ]*]]


// Individual variable initialization functions:
Expand Down Expand Up @@ -118,14 +145,74 @@ int f() {
// LINUX: call i32* @_ZTWN1VIiE1mE()
// DARWIN: call cxx_fast_tlscc i32* @_ZTWN1VIiE1mE()
// CHECK-NEXT: load i32, i32* %{{.*}}, align 4
// CHECK-NEXT: store i32 %{{.*}}, i32* @e, align 4
// LINUX: call {{.*}}* @_ZTWN1XIiE1mE()
// DARWIN: call cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE()
// CHECK: store {{.*}} @e

// LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE()
// DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1VIiE1mE()
// LINUX: call void @_ZTHN1VIiE1mE()
// DARWIN: call cxx_fast_tlscc void @_ZTHN1VIiE1mE()
// CHECK: ret i32* @_ZN1VIiE1mE

// LINUX-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()
// DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE()
// LINUX: call void @_ZTHN1XIiE1mE()
// DARWIN: call cxx_fast_tlscc void @_ZTHN1XIiE1mE()
// CHECK: ret {{.*}}* @_ZN1XIiE1mE

// CHECK: define internal {{.*}} @[[VF_M_INIT]]()
// LINUX-SAME: comdat($_ZN1VIfE1mE)
// DARWIN-NOT: comdat
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1VIfE1mE to i8*)
// CHECK: %[[VF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
// CHECK: br i1 %[[VF_M_INITIALIZED]],
// need init:
// CHECK: call i32 @_Z1gv()
// CHECK: store i32 %{{.*}}, i32* @_ZN1VIfE1mE, align 4
// CHECK: store i64 1, i64* @_ZGVN1VIfE1mE
// CHECK: br label

// CHECK: define internal {{.*}} @[[XF_M_INIT]]()
// LINUX-SAME: comdat($_ZN1XIfE1mE)
// DARWIN-NOT: comdat
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1XIfE1mE to i8*)
// CHECK: %[[XF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
// CHECK: br i1 %[[XF_M_INITIALIZED]],
// need init:
// LINUX: call {{.*}}__cxa_thread_atexit
// DARWIN: call {{.*}}_tlv_atexit
// CHECK: store i64 1, i64* @_ZGVN1XIfE1mE
// CHECK: br label

// LINUX: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*)
// DARWIN: declare i32 @_tlv_atexit(void (i8*)*, i8*, i8*)

// DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1VIcE1mE()
// LINUX: define weak_odr hidden i32* @_ZTWN1VIcE1mE()
// LINUX-NOT: comdat
// LINUX: br i1 icmp ne (void ()* @_ZTHN1VIcE1mE,
// LINUX: call void @_ZTHN1VIcE1mE()
// LINUX: ret i32* @_ZN1VIcE1mE

// DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1WIcE1mE()
// LINUX: define weak_odr hidden i32* @_ZTWN1WIcE1mE()
// LINUX-NOT: comdat
// LINUX: br i1 icmp ne (void ()* @_ZTHN1WIcE1mE,
// LINUX: call void @_ZTHN1WIcE1mE()
// LINUX: ret i32* @_ZN1WIcE1mE

// DARWIN: declare cxx_fast_tlscc {{.*}}* @_ZTWN1XIcE1mE()
// LINUX: define weak_odr hidden {{.*}}* @_ZTWN1XIcE1mE()
// LINUX-NOT: comdat
// LINUX: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE,
// LINUX: call void @_ZTHN1XIcE1mE()
// LINUX: ret {{.*}}* @_ZN1XIcE1mE

struct S { S(); ~S(); };
struct T { ~T(); };
Expand Down Expand Up @@ -154,9 +241,6 @@ void tls_dtor() {
static thread_local const S &u = S();
}

// LINUX: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*)
// DARWIN: declare i32 @_tlv_atexit(void (i8*)*, i8*, i8*)

// CHECK: define {{.*}} @_Z7PR15991v(
int PR15991() {
thread_local int n;
Expand Down Expand Up @@ -184,7 +268,9 @@ void set_anon_i() {
// LINUX-LABEL: define internal i32* @_ZTWN12_GLOBAL__N_16anon_iE()
// DARWIN-LABEL: define internal cxx_fast_tlscc i32* @_ZTWN12_GLOBAL__N_16anon_iE()

// CHECK: define {{.*}} @[[V_M_INIT:.*]]()
// CHECK: define internal {{.*}} @[[V_M_INIT]]()
// LINUX-SAME: comdat($_ZN1VIiE1mE)
// DARWIN-NOT: comdat
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*)
// CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
// CHECK: br i1 %[[V_M_INITIALIZED]],
Expand All @@ -194,6 +280,18 @@ void set_anon_i() {
// CHECK: store i64 1, i64* @_ZGVN1VIiE1mE
// CHECK: br label

// CHECK: define internal {{.*}} @[[X_M_INIT]]()
// LINUX-SAME: comdat($_ZN1XIiE1mE)
// DARWIN-NOT: comdat
// CHECK: load i8, i8* bitcast (i64* @_ZGVN1XIiE1mE to i8*)
// CHECK: %[[X_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0
// CHECK: br i1 %[[X_M_INITIALIZED]],
// need init:
// LINUX: call {{.*}}__cxa_thread_atexit
// DARWIN: call {{.*}}_tlv_atexit
// CHECK: store i64 1, i64* @_ZGVN1XIiE1mE
// CHECK: br label

// CHECK: define {{.*}}@[[GLOBAL_INIT:.*]]()
// CHECK: call void @[[C_INIT]]()
// CHECK: call void @[[E_INIT]]()
Expand All @@ -205,10 +303,13 @@ void set_anon_i() {
// CHECK: br i1 %[[NEED_TLS_INIT]],
// init:
// CHECK: store i8 1, i8* @__tls_guard
// CHECK-NOT: call void @[[V_M_INIT]]()
// CHECK: call void @[[A_INIT]]()
// CHECK-NOT: call void @[[V_M_INIT]]()
// CHECK: call void @[[D_INIT]]()
// CHECK-NOT: call void @[[V_M_INIT]]()
// CHECK: call void @[[U_M_INIT]]()
// CHECK: call void @[[V_M_INIT]]()
// CHECK-NOT: call void @[[V_M_INIT]]()


// LIUNX: define weak_odr hidden i32* @_ZTW1a() {
Expand Down
14 changes: 7 additions & 7 deletions clang/test/OpenMP/threadprivate_codegen.cpp
Expand Up @@ -179,9 +179,9 @@ struct S5 {
// CHECK-TLS-DAG: @__dso_handle = external global i8
// CHECK-TLS-DAG: [[GS1_TLS_INIT:@_ZTHL3gs1]] = internal alias void (), void ()* @__tls_init
// CHECK-TLS-DAG: [[ARR_X_TLS_INIT:@_ZTH5arr_x]] = alias void (), void ()* @__tls_init
// CHECK-TLS-DAG: [[ST_INT_ST_TLS_INIT:@_ZTHN2STIiE2stE]] = linkonce_odr alias void (), void ()* @__tls_init
// CHECK-TLS-DAG: [[ST_FLOAT_ST_TLS_INIT:@_ZTHN2STIfE2stE]] = linkonce_odr alias void (), void ()* @__tls_init
// CHECK-TLS-DAG: [[ST_S4_ST_TLS_INIT:@_ZTHN2STI2S4E2stE]] = linkonce_odr alias void (), void ()* @__tls_init


// CHECK-TLS-DAG: [[ST_S4_ST_TLS_INIT:@_ZTHN2STI2S4E2stE]] = linkonce_odr alias void (), void ()* [[ST_S4_ST_CXX_INIT:@[^, ]*]]

struct Static {
static S3 s;
Expand Down Expand Up @@ -640,11 +640,11 @@ int main() {
// CHECK-TLS: ret [2 x [3 x [[S1]]]]* [[ARR_X]]
// CHECK-TLS: }
// CHECK-TLS: define {{.*}} i32* [[ST_INT_ST_TLS_INITD]] {{#[0-9]+}} {
// CHECK-TLS: call void [[ST_INT_ST_TLS_INIT]]
// CHECK-TLS-NOT: call
// CHECK-TLS: ret i32* [[ST_INT_ST]]
// CHECK-TLS: }
// CHECK-TLS: define {{.*}} float* [[ST_FLOAT_ST_TLS_INITD]] {{#[0-9]+}} {
// CHECK-TLS: call void [[ST_FLOAT_ST_TLS_INIT]]
// CHECK-TLS-NOT: call
// CHECK-TLS: ret float* [[ST_FLOAT_ST]]
// CHECK-TLS: }
// CHECK-TLS: define {{.*}} [[S4]]* [[ST_S4_ST_TLS_INITD]] {{#[0-9]+}} {
Expand Down Expand Up @@ -923,7 +923,7 @@ int foobar() {
// CHECK-TLS: define {{.*}}void [[SM_CTOR2]]([[SMAIN]]* {{.*}}, i32 {{.*}})
// CHECK-TLS: define {{.*}}void [[SM_DTOR2]]([[SMAIN]]* {{.*}})

// CHECK-TLS: define internal void [[ST_S4_ST_CXX_INIT:@.*]]()
// CHECK-TLS: define internal void [[ST_S4_ST_CXX_INIT]]()
// CHECK-TLS: call void [[ST_S4_ST_CTOR1:@.*]]([[S4]]* [[ST_S4_ST]], i32 23)
// CHECK-TLS: call i32 @__cxa_thread_atexit(void (i8*)* bitcast (void ([[S4]]*)* [[ST_S4_ST_DTOR1:.*]] to void (i8*)*), i8* bitcast ([[S4]]* [[ST_S4_ST]] to i8*)
// CHECK-TLS: }
Expand All @@ -945,7 +945,7 @@ int foobar() {
// CHECK-TLS: call void [[GS1_CXX_INIT]]
// CHECK-TLS-NOT: call void [[GS2_CXX_INIT]]
// CHECK-TLS: call void [[ARR_X_CXX_INIT]]
// CHECK-TLS: call void [[ST_S4_ST_CXX_INIT]]
// CHECK-TLS-NOT: call void [[ST_S4_ST_CXX_INIT]]
// CHECK-TLS: [[DONE_LABEL]]

// CHECK-TLS-DAG: declare {{.*}} void [[GS3_TLS_INIT]]
Expand Down

0 comments on commit fbe2369

Please sign in to comment.