<div dir="ltr"><div><div>Hi Richard,<br><br></div>Does this mean that Clang now has "real" support for thread_local variables ?<br><br></div><div>Since you seem to be fully implementing this, may we suppose it'll be available (completely) in the 3.3 Release ?<br>
</div><div><br></div>-- Matthieu<br></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Mon, Apr 15, 2013 at 1:01 AM, Richard Smith <span dir="ltr"><<a href="mailto:richard-llvm@metafoo.co.uk" target="_blank">richard-llvm@metafoo.co.uk</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: rsmith<br>
Date: Sun Apr 14 18:01:42 2013<br>
New Revision: 179496<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=179496&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=179496&view=rev</a><br>
Log:<br>
CodeGen support for function-local static thread_local variables with<br>
non-constant constructors or non-trivial destructors. Plus bugfixes for<br>
thread_local references bound to temporaries (the temporaries themselves are<br>
lifetime-extended to become thread_local), and the corresponding case for<br>
std::initializer_list.<br>
<br>
Added:<br>
cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp<br>
Modified:<br>
cfe/trunk/lib/CodeGen/CGCXXABI.cpp<br>
cfe/trunk/lib/CodeGen/CGCXXABI.h<br>
cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
cfe/trunk/lib/CodeGen/CGExpr.cpp<br>
cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp<br>
cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGCXXABI.cpp Sun Apr 14 18:01:42 2013<br>
@@ -220,8 +220,12 @@ void CGCXXABI::EmitGuardedInit(CodeGenFu<br>
}<br>
<br>
void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF,<br>
+ const VarDecl &D,<br>
llvm::Constant *dtor,<br>
llvm::Constant *addr) {<br>
+ if (D.getTLSKind())<br>
+ CGM.ErrorUnsupported(&D, "non-trivial TLS destruction");<br>
+<br>
// The default behavior is to use atexit.<br>
CGF.registerGlobalDtorWithAtExit(dtor, addr);<br>
}<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGCXXABI.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.h?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.h?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGCXXABI.h (original)<br>
+++ cfe/trunk/lib/CodeGen/CGCXXABI.h Sun Apr 14 18:01:42 2013<br>
@@ -330,8 +330,8 @@ public:<br>
///<br>
/// \param dtor - a function taking a single pointer argument<br>
/// \param addr - a pointer to pass to the destructor function.<br>
- virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,<br>
- llvm::Constant *addr);<br>
+ virtual void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,<br>
+ llvm::Constant *dtor, llvm::Constant *addr);<br>
};<br>
<br>
// Create an instance of a C++ ABI class:<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Sun Apr 14 18:01:42 2013<br>
@@ -80,6 +80,7 @@ static void EmitDeclDestroy(CodeGenFunct<br>
case QualType::DK_objc_strong_lifetime:<br>
case QualType::DK_objc_weak_lifetime:<br>
// We don't care about releasing objects during process teardown.<br>
+ assert(!D.getTLSKind() && "should have rejected this");<br>
return;<br>
}<br>
<br>
@@ -105,7 +106,7 @@ static void EmitDeclDestroy(CodeGenFunct<br>
argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);<br>
}<br>
<br>
- CGM.getCXXABI().registerGlobalDtor(CGF, function, argument);<br>
+ CGM.getCXXABI().registerGlobalDtor(CGF, D, function, argument);<br>
}<br>
<br>
/// Emit code to cause the variable at the given address to be considered as<br>
@@ -218,9 +219,6 @@ void CodeGenFunction::EmitCXXGuardedInit<br>
"this initialization requires a guard variable, which "<br>
"the kernel does not support");<br>
<br>
- if (D.getTLSKind())<br>
- CGM.ErrorUnsupported(D.getInit(), "dynamic TLS initialization");<br>
-<br>
CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit);<br>
}<br>
<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Sun Apr 14 18:01:42 2013<br>
@@ -184,12 +184,16 @@ CreateReferenceTemporary(CodeGenFunction<br>
llvm::Type *RefTempTy = CGF.ConvertTypeForMem(Type);<br>
<br>
// Create the reference temporary.<br>
- llvm::GlobalValue *RefTemp =<br>
+ llvm::GlobalVariable *RefTemp =<br>
new llvm::GlobalVariable(CGF.CGM.getModule(),<br>
RefTempTy, /*isConstant=*/false,<br>
llvm::GlobalValue::InternalLinkage,<br>
llvm::Constant::getNullValue(RefTempTy),<br>
Name.str());<br>
+ // If we're binding to a thread_local variable, the temporary is also<br>
+ // thread local.<br>
+ if (VD->getTLSKind())<br>
+ CGF.CGM.setTLSMode(RefTemp, *VD);<br>
return RefTemp;<br>
}<br>
}<br>
@@ -434,12 +438,15 @@ CodeGenFunction::EmitReferenceBindingToE<br>
CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);<br>
CleanupArg = cast<llvm::Constant>(ReferenceTemporary);<br>
}<br>
- CGM.getCXXABI().registerGlobalDtor(*this, CleanupFn, CleanupArg);<br>
+ CGM.getCXXABI().registerGlobalDtor(*this, *VD, CleanupFn, CleanupArg);<br>
} else if (ReferenceInitializerList) {<br>
+ // FIXME: This is wrong. We need to register a global destructor to clean<br>
+ // up the initializer_list object, rather than adding it as a local<br>
+ // cleanup.<br>
EmitStdInitializerListCleanup(ReferenceTemporary,<br>
ReferenceInitializerList);<br>
} else {<br>
- assert(!ObjCARCReferenceLifetimeType.isNull());<br>
+ assert(!ObjCARCReferenceLifetimeType.isNull() && !VD->getTLSKind());<br>
// Note: We intentionally do not register a global "destructor" to<br>
// release the object.<br>
}<br>
<br>
Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sun Apr 14 18:01:42 2013<br>
@@ -1627,6 +1627,7 @@ CodeGenModule::MaybeEmitGlobalStdInitial<br>
D->getLocStart(), D->getLocation(),<br>
name, arrayType, sourceInfo,<br>
SC_Static);<br>
+ backingArray->setTLSKind(D->getTLSKind());<br>
<br>
// Now clone the InitListExpr to initialize the array instead.<br>
// Incredible hack: we want to use the existing InitListExpr here, so we need<br>
<br>
Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Sun Apr 14 18:01:42 2013<br>
@@ -130,8 +130,8 @@ public:<br>
<br>
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,<br>
llvm::GlobalVariable *DeclPtr, bool PerformInit);<br>
- void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,<br>
- llvm::Constant *addr);<br>
+ void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,<br>
+ llvm::Constant *dtor, llvm::Constant *addr);<br>
};<br>
<br>
class ARMCXXABI : public ItaniumCXXABI {<br>
@@ -1042,10 +1042,10 @@ void ItaniumCXXABI::EmitGuardedInit(Code<br>
bool shouldPerformInit) {<br>
CGBuilderTy &Builder = CGF.Builder;<br>
<br>
- // We only need to use thread-safe statics for local variables;<br>
+ // We only need to use thread-safe statics for local non-TLS variables;<br>
// global initialization is always single-threaded.<br>
- bool threadsafe =<br>
- (getContext().getLangOpts().ThreadsafeStatics && D.isLocalVarDecl());<br>
+ bool threadsafe = getContext().getLangOpts().ThreadsafeStatics &&<br>
+ D.isLocalVarDecl() && !D.getTLSKind();<br>
<br>
// If we have a global variable with internal linkage and thread-safe statics<br>
// are disabled, we can just let the guard variable be of type i8.<br>
@@ -1080,6 +1080,8 @@ void ItaniumCXXABI::EmitGuardedInit(Code<br>
llvm::ConstantInt::get(guardTy, 0),<br>
guardName.str());<br>
guard->setVisibility(var->getVisibility());<br>
+ // If the variable is thread-local, so is its guard variable.<br>
+ guard->setThreadLocalMode(var->getThreadLocalMode());<br>
<br>
CGM.setStaticLocalDeclGuardAddress(&D, guard);<br>
}<br>
@@ -1180,7 +1182,10 @@ void ItaniumCXXABI::EmitGuardedInit(Code<br>
/// Register a global destructor using __cxa_atexit.<br>
static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,<br>
llvm::Constant *dtor,<br>
- llvm::Constant *addr) {<br>
+ llvm::Constant *addr,<br>
+ bool TLS) {<br>
+ const char *Name = TLS ? "__cxa_thread_atexit" : "__cxa_atexit";<br>
+<br>
// We're assuming that the destructor function is something we can<br>
// reasonably call with the default CC. Go ahead and cast it to the<br>
// right prototype.<br>
@@ -1193,8 +1198,7 @@ static void emitGlobalDtorWithCXAAtExit(<br>
llvm::FunctionType::get(CGF.IntTy, paramTys, false);<br>
<br>
// Fetch the actual function.<br>
- llvm::Constant *atexit =<br>
- CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");<br>
+ llvm::Constant *atexit = CGF.CGM.CreateRuntimeFunction(atexitTy, Name);<br>
if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))<br>
fn->setDoesNotThrow();<br>
<br>
@@ -1212,12 +1216,15 @@ static void emitGlobalDtorWithCXAAtExit(<br>
<br>
/// Register a global destructor as best as we know how.<br>
void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF,<br>
+ const VarDecl &D,<br>
llvm::Constant *dtor,<br>
llvm::Constant *addr) {<br>
// Use __cxa_atexit if available.<br>
- if (CGM.getCodeGenOpts().CXAAtExit) {<br>
- return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr);<br>
- }<br>
+ if (CGM.getCodeGenOpts().CXAAtExit)<br>
+ return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr, D.getTLSKind());<br>
+<br>
+ if (D.getTLSKind())<br>
+ CGM.ErrorUnsupported(&D, "non-trivial TLS destruction");<br>
<br>
// In Apple kexts, we want to add a global destructor entry.<br>
// FIXME: shouldn't this be guarded by some variable?<br>
<br>
Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)<br>
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Sun Apr 14 18:01:42 2013<br>
@@ -392,6 +392,9 @@ void MicrosoftCXXABI::EmitGuardedInit(Co<br>
// Not sure whether we want thread-safe static local variables as VS<br>
// doesn't make them thread-safe.<br>
<br>
+ if (D.getTLSKind())<br>
+ CGM.ErrorUnsupported(&D, "dynamic TLS initialization");<br>
+<br>
// Emit the initializer and add a global destructor if appropriate.<br>
CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit);<br>
}<br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp?rev=179496&r1=179495&r2=179496&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp?rev=179496&r1=179495&r2=179496&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp Sun Apr 14 18:01:42 2013<br>
@@ -51,6 +51,12 @@ struct wantslist1 {<br>
// CHECK: @globalInitList1 = global %{{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, i32 0, i32 0), i{{32|64}} 3 }<br>
std::initializer_list<int> globalInitList1 = {1, 2, 3};<br>
<br>
+namespace thread_local_global_array {<br>
+ // CHECK: @_ZN25thread_local_global_arrayL11x__initlistE = internal thread_local global [4 x i32] [i32 1, i32 2, i32 3, i32 4]<br>
+ // CHECK: @_ZN25thread_local_global_array1xE = thread_local global {{.*}} @_ZN25thread_local_global_arrayL11x__initlistE, {{.*}} i64 4<br>
+ std::initializer_list<int> thread_local x = { 1, 2, 3, 4 };<br>
+}<br>
+<br>
// CHECK: @_ZL25globalInitList2__initlist = internal global [2 x %{{[^ ]*}}] zeroinitializer<br>
// CHECK: @globalInitList2 = global %{{[^ ]+}} { %[[WITHARG:[^ *]+]]* getelementptr inbounds ([2 x<br>
// CHECK: appending global<br>
<br>
Added: cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp?rev=179496&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp?rev=179496&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp (added)<br>
+++ cfe/trunk/test/CodeGenCXX/cxx11-thread-local.cpp Sun Apr 14 18:01:42 2013<br>
@@ -0,0 +1,56 @@<br>
+// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s<br>
+<br>
+int g();<br>
+<br>
+// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0<br>
+// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0<br>
+<br>
+// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global<br>
+// CHECK: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0<br>
+<br>
+// CHECK: @_ZZ8tls_dtorvE1t = internal thread_local global<br>
+// CHECK: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0<br>
+<br>
+// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global<br>
+// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0<br>
+// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global<br>
+<br>
+// CHECK: define i32 @_Z1fv()<br>
+int f() {<br>
+ // CHECK: %[[GUARD:.*]] = load i8* @_ZGVZ1fvE1n, align 1<br>
+ // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0<br>
+ // CHECK: br i1 %[[NEED_INIT]]<br>
+<br>
+ // CHECK: %[[CALL:.*]] = call i32 @_Z1gv()<br>
+ // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4<br>
+ // CHECK: store i8 1, i8* @_ZGVZ1fvE1n<br>
+ // CHECK: br label<br>
+ static thread_local int n = g();<br>
+<br>
+ // CHECK: load i32* @_ZZ1fvE1n, align 4<br>
+ return n;<br>
+}<br>
+<br>
+struct S { S(); ~S(); };<br>
+struct T { ~T(); };<br>
+<br>
+// CHECK: define void @_Z8tls_dtorv()<br>
+void tls_dtor() {<br>
+ // CHECK: load i8* @_ZGVZ8tls_dtorvE1s<br>
+ // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZZ8tls_dtorvE1s)<br>
+ // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle<br>
+ // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s<br>
+ static thread_local S s;<br>
+<br>
+ // CHECK: load i8* @_ZGVZ8tls_dtorvE1t<br>
+ // CHECK-NOT: _ZN1T<br>
+ // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle<br>
+ // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t<br>
+ static thread_local T t;<br>
+<br>
+ // CHECK: load i8* @_ZGVZ8tls_dtorvE1u<br>
+ // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZGRZ8tls_dtorvE1u)<br>
+ // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u{{.*}} @__dso_handle<br>
+ // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u<br>
+ static thread_local const S &u = S();<br>
+}<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>