From: Richard Smith Date: Sun, 14 Apr 2013 23:01:42 +0000 (+0000) Subject: CodeGen support for function-local static thread_local variables with X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=dbf74baee576ba200009bcead0117cc641d39071;p=platform%2Fupstream%2Fllvm.git CodeGen support for function-local static thread_local variables with non-constant constructors or non-trivial destructors. Plus bugfixes for thread_local references bound to temporaries (the temporaries themselves are lifetime-extended to become thread_local), and the corresponding case for std::initializer_list. llvm-svn: 179496 --- diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp index 0c0a76f..3bf6480 100644 --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -220,8 +220,12 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF, } void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + const VarDecl &D, llvm::Constant *dtor, llvm::Constant *addr) { + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "non-trivial TLS destruction"); + // The default behavior is to use atexit. CGF.registerGlobalDtorWithAtExit(dtor, addr); } diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index 702e59b..1f46f12 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -330,8 +330,8 @@ public: /// /// \param dtor - a function taking a single pointer argument /// \param addr - a pointer to pass to the destructor function. - virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, - llvm::Constant *addr); + virtual void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, + llvm::Constant *dtor, llvm::Constant *addr); }; // Create an instance of a C++ ABI class: diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 609f6aa..fd766bf 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -80,6 +80,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, case QualType::DK_objc_strong_lifetime: case QualType::DK_objc_weak_lifetime: // We don't care about releasing objects during process teardown. + assert(!D.getTLSKind() && "should have rejected this"); return; } @@ -105,7 +106,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, argument = llvm::Constant::getNullValue(CGF.Int8PtrTy); } - CGM.getCXXABI().registerGlobalDtor(CGF, function, argument); + CGM.getCXXABI().registerGlobalDtor(CGF, D, function, argument); } /// Emit code to cause the variable at the given address to be considered as @@ -218,9 +219,6 @@ void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, "this initialization requires a guard variable, which " "the kernel does not support"); - if (D.getTLSKind()) - CGM.ErrorUnsupported(D.getInit(), "dynamic TLS initialization"); - CGM.getCXXABI().EmitGuardedInit(*this, D, DeclPtr, PerformInit); } diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index c515508..1c06b7d 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -184,12 +184,16 @@ CreateReferenceTemporary(CodeGenFunction &CGF, QualType Type, llvm::Type *RefTempTy = CGF.ConvertTypeForMem(Type); // Create the reference temporary. - llvm::GlobalValue *RefTemp = + llvm::GlobalVariable *RefTemp = new llvm::GlobalVariable(CGF.CGM.getModule(), RefTempTy, /*isConstant=*/false, llvm::GlobalValue::InternalLinkage, llvm::Constant::getNullValue(RefTempTy), Name.str()); + // If we're binding to a thread_local variable, the temporary is also + // thread local. + if (VD->getTLSKind()) + CGF.CGM.setTLSMode(RefTemp, *VD); return RefTemp; } } @@ -434,12 +438,15 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); CleanupArg = cast(ReferenceTemporary); } - CGM.getCXXABI().registerGlobalDtor(*this, CleanupFn, CleanupArg); + CGM.getCXXABI().registerGlobalDtor(*this, *VD, CleanupFn, CleanupArg); } else if (ReferenceInitializerList) { + // FIXME: This is wrong. We need to register a global destructor to clean + // up the initializer_list object, rather than adding it as a local + // cleanup. EmitStdInitializerListCleanup(ReferenceTemporary, ReferenceInitializerList); } else { - assert(!ObjCARCReferenceLifetimeType.isNull()); + assert(!ObjCARCReferenceLifetimeType.isNull() && !VD->getTLSKind()); // Note: We intentionally do not register a global "destructor" to // release the object. } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 20532d6..f8627b2 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1627,6 +1627,7 @@ CodeGenModule::MaybeEmitGlobalStdInitializerListInitializer(const VarDecl *D, D->getLocStart(), D->getLocation(), name, arrayType, sourceInfo, SC_Static); + backingArray->setTLSKind(D->getTLSKind()); // Now clone the InitListExpr to initialize the array instead. // Incredible hack: we want to use the existing InitListExpr here, so we need diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index e25d422..25cd5f5 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -130,8 +130,8 @@ public: void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); - void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, - llvm::Constant *addr); + void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, + llvm::Constant *dtor, llvm::Constant *addr); }; class ARMCXXABI : public ItaniumCXXABI { @@ -1042,10 +1042,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, bool shouldPerformInit) { CGBuilderTy &Builder = CGF.Builder; - // We only need to use thread-safe statics for local variables; + // We only need to use thread-safe statics for local non-TLS variables; // global initialization is always single-threaded. - bool threadsafe = - (getContext().getLangOpts().ThreadsafeStatics && D.isLocalVarDecl()); + bool threadsafe = getContext().getLangOpts().ThreadsafeStatics && + D.isLocalVarDecl() && !D.getTLSKind(); // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. @@ -1080,6 +1080,8 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, llvm::ConstantInt::get(guardTy, 0), guardName.str()); guard->setVisibility(var->getVisibility()); + // If the variable is thread-local, so is its guard variable. + guard->setThreadLocalMode(var->getThreadLocalMode()); CGM.setStaticLocalDeclGuardAddress(&D, guard); } @@ -1180,7 +1182,10 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, /// Register a global destructor using __cxa_atexit. static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, llvm::Constant *dtor, - llvm::Constant *addr) { + llvm::Constant *addr, + bool TLS) { + const char *Name = TLS ? "__cxa_thread_atexit" : "__cxa_atexit"; + // We're assuming that the destructor function is something we can // reasonably call with the default CC. Go ahead and cast it to the // right prototype. @@ -1193,8 +1198,7 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, llvm::FunctionType::get(CGF.IntTy, paramTys, false); // Fetch the actual function. - llvm::Constant *atexit = - CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit"); + llvm::Constant *atexit = CGF.CGM.CreateRuntimeFunction(atexitTy, Name); if (llvm::Function *fn = dyn_cast(atexit)) fn->setDoesNotThrow(); @@ -1212,12 +1216,15 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, /// Register a global destructor as best as we know how. void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, + const VarDecl &D, llvm::Constant *dtor, llvm::Constant *addr) { // Use __cxa_atexit if available. - if (CGM.getCodeGenOpts().CXAAtExit) { - return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr); - } + if (CGM.getCodeGenOpts().CXAAtExit) + return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr, D.getTLSKind()); + + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "non-trivial TLS destruction"); // In Apple kexts, we want to add a global destructor entry. // FIXME: shouldn't this be guarded by some variable? diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index 2c7f89a..a0e4f4d 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -392,6 +392,9 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, // Not sure whether we want thread-safe static local variables as VS // doesn't make them thread-safe. + if (D.getTLSKind()) + CGM.ErrorUnsupported(&D, "dynamic TLS initialization"); + // Emit the initializer and add a global destructor if appropriate. CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit); } diff --git a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp index fc90880..d683493 100644 --- a/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -51,6 +51,12 @@ struct wantslist1 { // CHECK: @globalInitList1 = global %{{[^ ]+}} { i32* getelementptr inbounds ([3 x i32]* @_ZL25globalInitList1__initlist, i32 0, i32 0), i{{32|64}} 3 } std::initializer_list globalInitList1 = {1, 2, 3}; +namespace thread_local_global_array { + // CHECK: @_ZN25thread_local_global_arrayL11x__initlistE = internal thread_local global [4 x i32] [i32 1, i32 2, i32 3, i32 4] + // CHECK: @_ZN25thread_local_global_array1xE = thread_local global {{.*}} @_ZN25thread_local_global_arrayL11x__initlistE, {{.*}} i64 4 + std::initializer_list thread_local x = { 1, 2, 3, 4 }; +} + // CHECK: @_ZL25globalInitList2__initlist = internal global [2 x %{{[^ ]*}}] zeroinitializer // CHECK: @globalInitList2 = global %{{[^ ]+}} { %[[WITHARG:[^ *]+]]* getelementptr inbounds ([2 x // CHECK: appending global diff --git a/clang/test/CodeGenCXX/cxx11-thread-local.cpp b/clang/test/CodeGenCXX/cxx11-thread-local.cpp new file mode 100644 index 0000000..5ed582d --- /dev/null +++ b/clang/test/CodeGenCXX/cxx11-thread-local.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s + +int g(); + +// CHECK: @_ZZ1fvE1n = internal thread_local global i32 0 +// CHECK: @_ZGVZ1fvE1n = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1s = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1s = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1t = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1t = internal thread_local global i8 0 + +// CHECK: @_ZZ8tls_dtorvE1u = internal thread_local global +// CHECK: @_ZGVZ8tls_dtorvE1u = internal thread_local global i8 0 +// CHECK: @_ZGRZ8tls_dtorvE1u = internal thread_local global + +// CHECK: define i32 @_Z1fv() +int f() { + // CHECK: %[[GUARD:.*]] = load i8* @_ZGVZ1fvE1n, align 1 + // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0 + // CHECK: br i1 %[[NEED_INIT]] + + // CHECK: %[[CALL:.*]] = call i32 @_Z1gv() + // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4 + // CHECK: store i8 1, i8* @_ZGVZ1fvE1n + // CHECK: br label + static thread_local int n = g(); + + // CHECK: load i32* @_ZZ1fvE1n, align 4 + return n; +} + +struct S { S(); ~S(); }; +struct T { ~T(); }; + +// CHECK: define void @_Z8tls_dtorv() +void tls_dtor() { + // CHECK: load i8* @_ZGVZ8tls_dtorvE1s + // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZZ8tls_dtorvE1s) + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s + static thread_local S s; + + // CHECK: load i8* @_ZGVZ8tls_dtorvE1t + // CHECK-NOT: _ZN1T + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t + static thread_local T t; + + // CHECK: load i8* @_ZGVZ8tls_dtorvE1u + // CHECK: call void @_ZN1SC1Ev(%struct.S* @_ZGRZ8tls_dtorvE1u) + // CHECK: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u{{.*}} @__dso_handle + // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u + static thread_local const S &u = S(); +}