Index: include/clang/Basic/Linkage.h =================================================================== --- include/clang/Basic/Linkage.h +++ include/clang/Basic/Linkage.h @@ -82,6 +82,12 @@ return L <= GVA_DiscardableODR; } +/// Do we know that this will be the only definition of this symbol (excluding +/// inlining-only definitions)? +inline bool isUniqueGVALinkage(GVALinkage L) { + return L == GVA_Internal || L == GVA_StrongExternal; +} + inline bool isExternallyVisible(Linkage L) { return L >= VisibleNoLinkage; } Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -577,7 +577,7 @@ // Determine if references to thread_local global variables can be made // directly or require access through a thread wrapper function. - virtual bool usesThreadWrapperFunction() const = 0; + virtual bool usesThreadWrapperFunction(const VarDecl *VD) const = 0; /// Emit a reference to a non-local thread_local variable (including /// triggering the initialization of all thread_local variables in its Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -2361,7 +2361,7 @@ // If it's thread_local, emit a call to its wrapper function instead. if (VD->getTLSKind() == VarDecl::TLS_Dynamic && - CGF.CGM.getCXXABI().usesThreadWrapperFunction()) + CGF.CGM.getCXXABI().usesThreadWrapperFunction(VD)) return CGF.CGM.getCXXABI().EmitThreadLocalVarDeclLValue(CGF, VD, T); // Check if the variable is marked as declare target with link clause in // device codegen. Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -43,6 +43,10 @@ /// VTables - All the vtables which have been defined. llvm::DenseMap VTables; + /// All the thread wrapper functions that have been used. + llvm::SmallVector, 8> + ThreadWrappers; + protected: bool UseARMMethodPtrABI; bool UseARMGuardVarABI; @@ -322,7 +326,42 @@ ArrayRef CXXThreadLocalInits, ArrayRef CXXThreadLocalInitVars) override; - bool usesThreadWrapperFunction() const override { return true; } + /// 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 { + VD = VD->getMostRecentDecl(); + if (VD->hasAttr()) + 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()) + return false; + + const VarDecl *InitDecl = VD->getInitializingDeclaration(); + if (!InitDecl) + return false; + + // If there's no initializer to run, this is constant initialization. + if (!InitDecl->hasInit()) + return true; + + // If we have the only definition, we don't need a thread wrapper if we + // will emit the value as a constant. + if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD))) + return !VD->getType().isDestructedType() && InitDecl->evaluateValue(); + + // Otherwise, we need a thread wrapper unless we know that every + // translation unit will emit the value as a constant. We rely on + // ICE-ness not varying between translation units, which isn't actually + // guaranteed by the standard but is necessary for sanity. + return InitDecl->isInitKnownICE() && InitDecl->isInitICE(); + } + + bool usesThreadWrapperFunction(const VarDecl *VD) const override { + return !isEmittedWithConstantInitializer(VD); + } LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) override; @@ -2456,9 +2495,6 @@ CGM.SetLLVMFunctionAttributes(GlobalDecl(), FI, Wrapper); - if (VD->hasDefinition()) - CGM.SetLLVMFunctionAttributesForDefinition(nullptr, Wrapper); - // Always resolve references to the wrapper at link time. if (!Wrapper->hasLocalLinkage()) if (!isThreadWrapperReplaceable(VD, CGM) || @@ -2471,6 +2507,8 @@ Wrapper->setCallingConv(llvm::CallingConv::CXX_FAST_TLS); Wrapper->addFnAttr(llvm::Attribute::NoUnwind); } + + ThreadWrappers.push_back({VD, Wrapper}); return Wrapper; } @@ -2519,20 +2557,40 @@ } } - // Emit thread wrappers. + // Create declarations for thread wrappers for all thread-local variables + // with non-discardable definitions in this translation unit. for (const VarDecl *VD : CXXThreadLocals) { + if (VD->hasDefinition() && + !isDiscardableGVALinkage(getContext().GetGVALinkageForVariable(VD))) { + llvm::GlobalValue *GV = CGM.GetGlobalValue(CGM.getMangledName(VD)); + getOrCreateThreadLocalWrapper(VD, GV); + } + } + + // Emit all referenced thread wrappers. + for (auto VDAndWrapper : ThreadWrappers) { + const VarDecl *VD = VDAndWrapper.first; llvm::GlobalVariable *Var = cast(CGM.GetGlobalValue(CGM.getMangledName(VD))); - llvm::Function *Wrapper = getOrCreateThreadLocalWrapper(VD, Var); + llvm::Function *Wrapper = VDAndWrapper.second; // 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()) { - Wrapper->setLinkage(llvm::Function::ExternalLinkage); - continue; + if (!VD->hasDefinition()) { + if (isThreadWrapperReplaceable(VD, CGM)) { + Wrapper->setLinkage(llvm::Function::ExternalLinkage); + continue; + } + + // If this isn't a TU in which this variable is defined, the thread + // wrapper is discardable. + if (Wrapper->getLinkage() == llvm::Function::WeakODRLinkage) + Wrapper->setLinkage(llvm::Function::LinkOnceODRLinkage); } + CGM.SetLLVMFunctionAttributesForDefinition(nullptr, Wrapper); + // Mangle the name for the thread_local initialization function. SmallString<256> InitFnName; { @@ -2547,7 +2605,10 @@ // produce a declaration of the initialization function. llvm::GlobalValue *Init = nullptr; bool InitIsInitFunc = false; - if (VD->hasDefinition()) { + bool HasConstantInitialization = false; + if (isEmittedWithConstantInitializer(VD)) { + HasConstantInitialization = true; + } else if (VD->hasDefinition()) { InitIsInitFunc = true; llvm::Function *InitFuncToUse = InitFunc; if (isTemplateInstantiation(VD->getTemplateSpecializationKind())) @@ -2576,7 +2637,9 @@ llvm::LLVMContext &Context = CGM.getModule().getContext(); llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper); CGBuilderTy Builder(CGM, Entry); - if (InitIsInitFunc) { + if (HasConstantInitialization) { + // No dynamic initialization to invoke. + } else if (InitIsInitFunc) { if (Init) { llvm::CallInst *CallVal = Builder.CreateCall(InitFnTy, Init); if (isThreadWrapperReplaceable(VD, CGM)) { Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -386,7 +386,9 @@ ArrayRef CXXThreadLocalInits, ArrayRef CXXThreadLocalInitVars) override; - bool usesThreadWrapperFunction() const override { return false; } + bool usesThreadWrapperFunction(const VarDecl *VD) const override { + return false; + } LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) override; Index: test/CodeGenCXX/cxx11-thread-local.cpp =================================================================== --- test/CodeGenCXX/cxx11-thread-local.cpp +++ test/CodeGenCXX/cxx11-thread-local.cpp @@ -91,15 +91,16 @@ // 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:[^, ]*]] +// FIXME: We really want a CHECK-DAG-NOT for these. +// CHECK-NOT: @_ZTHN1WIiE1mE = +// CHECK-NOT: @_ZTHN1WIfE1mE = +// CHECK-NOT: @_ZTHL1d = // Individual variable initialization functions: @@ -130,7 +131,7 @@ // CHECK-NEXT: load i32, i32* %{{.*}}, align 4 // CHECK-NEXT: store i32 %{{.*}}, i32* @c, align 4 -// LINUX-LABEL: define weak_odr hidden i32* @_ZTW1b() +// LINUX-LABEL: define linkonce_odr hidden i32* @_ZTW1b() // LINUX: br i1 icmp ne (void ()* @_ZTH1b, void ()* null), // not null: // LINUX: call void @_ZTH1b() @@ -203,21 +204,21 @@ // DARWIN: declare i32 @_tlv_atexit(void (i8*)*, i8*, i8*) // DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1VIcE1mE() -// LINUX: define weak_odr hidden i32* @_ZTWN1VIcE1mE() +// LINUX: define linkonce_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: define linkonce_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: define linkonce_odr hidden {{.*}}* @_ZTWN1XIcE1mE() // LINUX-NOT: comdat // LINUX: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE, // LINUX: call void @_ZTHN1XIcE1mE() @@ -269,7 +270,7 @@ } namespace { -thread_local int anon_i{1}; +thread_local int anon_i{f()}; } void set_anon_i() { anon_i = 2; @@ -332,14 +333,10 @@ // CHECK: } -// LINUX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]] - - -// LINUX-LABEL: define internal i32* @_ZTWL1d() -// DARWIN-LABEL: define internal cxx_fast_tlscc i32* @_ZTWL1d() -// LINUX: call void @_ZTHL1d() -// DARWIN: call cxx_fast_tlscc void @_ZTHL1d() -// CHECK: ret i32* @_ZL1d +// Should not emit a thread wrapper for internal-linkage unused variable 'd'. +// We separately check that 'd' does in fact get initialized with the other +// thread-local variables in this TU. +// CHECK-NOT: define {{.*}} @_ZTWL1d() // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE() // DARWIN-LABEL: define cxx_fast_tlscc i32* @_ZTWN1U1mE() @@ -347,4 +344,6 @@ // DARWIN: call cxx_fast_tlscc void @_ZTHN1U1mE() // CHECK: ret i32* @_ZN1U1mE +// LINUX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]] + // LINUX: attributes [[ATTR]] = { {{.+}} } Index: test/CodeGenCXX/cxx2a-thread-local-constinit.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cxx2a-thread-local-constinit.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2a %s -emit-llvm -o - | FileCheck %s + +// CHECK-DAG: @a = external thread_local global i32 +extern thread_local int a; + +// CHECK-DAG: @b = external thread_local global i32 +extern thread_local constinit int b; + +// CHECK-LABEL: define i32 @_Z1fv() +// CHECK: call i32* @_ZTW1a() +// CHECK: } +int f() { return a; } + +// CHECK-LABEL: define linkonce_odr {{.*}} @_ZTW1a() +// CHECK: br i1 +// CHECK: call void @_ZTH1a() +// CHECK: } + +// CHECK-LABEL: define i32 @_Z1gv() +// CHECK-NOT: call +// CHECK: load i32, i32* @b +// CHECK-NOT: call +// CHECK: } +int g() { return b; } + +// CHECK-NOT: define {{.*}} @_ZTW1b() + +extern thread_local int c; + +// CHECK-LABEL: define i32 @_Z1hv() +// CHECK: call i32* @_ZTW1c() +// CHECK: load i32, i32* % +// CHECK: } +int h() { return c; } + +thread_local int c = 0; + +int d_init(); +thread_local int d = d_init(); + +// Note: use of 'c' does not trigger initialization of 'd', because 'c' has a +// constant initializer. +// CHECK-LABEL: define weak_odr {{.*}} @_ZTW1c() +// CHECK-NOT: br i1 +// CHECK-NOT: call +// CHECK: ret i32* @c +// CHECK: } Index: test/CodeGenCXX/tls-init-funcs.cpp =================================================================== --- test/CodeGenCXX/tls-init-funcs.cpp +++ test/CodeGenCXX/tls-init-funcs.cpp @@ -6,9 +6,9 @@ // CHECK: @_tlv_atexit({{.*}}@_ZN1AD1Ev // CHECK: call cxx_fast_tlscc i32* @_ZTW3ext() // CHECK: declare cxx_fast_tlscc i32* @_ZTW3ext() -// CHECK: define weak_odr hidden cxx_fast_tlscc i32* @_ZTW2vtIiE() -// CHECK: define weak_odr hidden cxx_fast_tlscc i32* @_ZTW2vtIvE() -// CHECK: define {{.*}} @_ZTW1a +// CHECK-DAG: define weak_odr hidden cxx_fast_tlscc i32* @_ZTW2vtIiE() +// CHECK-DAG: define weak_odr hidden cxx_fast_tlscc i32* @_ZTW2vtIvE() +// CHECK-DAG: define {{.*}} @_ZTW1a struct A { ~A(); Index: test/CodeGenCXX/windows-on-arm-itanium-thread-local.cpp =================================================================== --- test/CodeGenCXX/windows-on-arm-itanium-thread-local.cpp +++ test/CodeGenCXX/windows-on-arm-itanium-thread-local.cpp @@ -1,11 +1,12 @@ -// RUN: %clang_cc1 -triple thumbv7--windows-itanium -fdeclspec -fms-compatibility -fms-compatibility-version=19.0 -S -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple thumbv7--windows-itanium -fdeclspec -fms-compatibility -fms-compatibility-version=19.0 -emit-llvm -o - %s | FileCheck %s -__declspec(thread) static void *c; +void *g(); +thread_local static void *c = g(); void f(void *p) { c = p; } -// CHECK-LABEL: @f(i8* %p) +// CHECK-LABEL: @_Z1fPv(i8* %p) // CHECK-NOT: call i8** @_ZTWL1c() // CHECK: call arm_aapcs_vfpcc i8** @_ZTWL1c() Index: test/OpenMP/parallel_copyin_codegen.cpp =================================================================== --- test/OpenMP/parallel_copyin_codegen.cpp +++ test/OpenMP/parallel_copyin_codegen.cpp @@ -101,8 +101,7 @@ // LAMBDA: define{{.*}} internal{{.*}} void [[OUTER_LAMBDA]]( // LAMBDA: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 0, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}) - // TLS-LAMBDA: [[G_CPY_VAL:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR:@.+]]() - // TLS-LAMBDA: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i32* [[G_CPY_VAL]]) + // TLS-LAMBDA: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i32* @g) #pragma omp parallel copyin(g) { @@ -120,14 +119,12 @@ // LAMBDA: [[DONE]] // TLS-LAMBDA-DAG: [[G_CAPTURE_SRC:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** % - // TLS-LAMBDA-DAG: [[G_CAPTURE_DST:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR]]() // TLS-LAMBDA-DAG: [[G_CAPTURE_SRCC:%.+]] = ptrtoint i{{[0-9]+}}* [[G_CAPTURE_SRC]] to i{{[0-9]+}} - // TLS-LAMBDA-DAG: [[G_CAPTURE_DSTC:%.+]] = ptrtoint i{{[0-9]+}}* [[G_CAPTURE_DST]] to i{{[0-9]+}} - // TLS-LAMBDA: icmp ne i{{[0-9]+}} {{%.+}}, {{%.+}} + // TLS-LAMBDA: icmp ne i{{[0-9]+}} {{%.+}}, ptrtoint (i{{[0-9]+}}* @g to i{{[0-9]+}}) // TLS-LAMBDA: br i1 %{{.+}}, label %[[NOT_MASTER:.+]], label %[[DONE:.+]] // TLS-LAMBDA: [[NOT_MASTER]] // TLS-LAMBDA: load i{{[0-9]+}}, i{{[0-9]+}}* [[G_CAPTURE_SRC]], - // TLS-LAMBDA: store volatile i{{[0-9]+}} %{{.+}}, i{{[0-9]+}}* [[G_CAPTURE_DST]], align 128 + // TLS-LAMBDA: store volatile i{{[0-9]+}} %{{.+}}, i{{[0-9]+}}* @g, align 128 // TLS-LAMBDA: [[DONE]] // LAMBDA: call {{.*}}void @__kmpc_barrier( @@ -136,18 +133,13 @@ // LAMBDA: call{{.*}} void [[INNER_LAMBDA:@.+]](%{{.+}}* // TLS-LAMBDA: call{{.*}} void [[INNER_LAMBDA:@.+]](%{{.+}}* - // TLS-LAMBDA: define {{.*}}i{{[0-9]+}}* [[G_CTOR]]() - // TLS-LAMBDA: ret i{{[0-9]+}}* [[G]] - // TLS-LAMBDA: } - [&]() { // LAMBDA: define {{.+}} void [[INNER_LAMBDA]](%{{.+}}* [[ARG_PTR:%.+]]) // LAMBDA: store %{{.+}}* [[ARG_PTR]], %{{.+}}** [[ARG_PTR_REF:%.+]], g = 2; // LAMBDA: [[ARG_PTR:%.+]] = load %{{.+}}*, %{{.+}}** [[ARG_PTR_REF]] - // TLS-LAMBDA: [[G_CAPTURE_DST:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR]]() - // TLS-LAMBDA: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* [[G_CAPTURE_DST]], align 128 + // TLS-LAMBDA: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* @g, align 128 }(); } }(); @@ -164,8 +156,7 @@ // BLOCKS: define{{.*}} internal{{.*}} void {{.+}}(i8* // BLOCKS: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 0, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}) - // TLS-BLOCKS: [[G_CPY_VAL:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR:@.+]]() - // TLS-BLOCKS: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i32* [[G_CPY_VAL]]) + // TLS-BLOCKS: call {{.*}}void {{.+}} @__kmpc_fork_call({{.+}}, i32 1, {{.+}}* [[OMP_REGION:@.+]] to {{.+}}, i32* @g) #pragma omp parallel copyin(g) { @@ -183,14 +174,12 @@ // BLOCKS: [[DONE]] // TLS-BLOCKS-DAG: [[G_CAPTURE_SRC:%.+]] = load i{{[0-9]+}}*, i{{[0-9]+}}** % - // TLS-BLOCKS-DAG: [[G_CAPTURE_DST:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR]]() // TLS-BLOCKS-DAG: [[G_CAPTURE_SRCC:%.+]] = ptrtoint i{{[0-9]+}}* [[G_CAPTURE_SRC]] to i{{[0-9]+}} - // TLS-BLOCKS-DAG: [[G_CAPTURE_DSTC:%.+]] = ptrtoint i{{[0-9]+}}* [[G_CAPTURE_DST]] to i{{[0-9]+}} - // TLS-BLOCKS: icmp ne i{{[0-9]+}} {{%.+}}, {{%.+}} + // TLS-BLOCKS: icmp ne i{{[0-9]+}} {{%.+}}, ptrtoint (i{{[0-9]+}}* @g to i{{[0-9]+}}) // TLS-BLOCKS: br i1 %{{.+}}, label %[[NOT_MASTER:.+]], label %[[DONE:.+]] // TLS-BLOCKS: [[NOT_MASTER]] // TLS-BLOCKS: load i{{[0-9]+}}, i{{[0-9]+}}* [[G_CAPTURE_SRC]], - // TLS-BLOCKS: store volatile i{{[0-9]+}} %{{.+}}, i{{[0-9]+}}* [[G_CAPTURE_DST]], align 128 + // TLS-BLOCKS: store volatile i{{[0-9]+}} %{{.+}}, i{{[0-9]+}}* @g, align 128 // TLS-BLOCKS: [[DONE]] // BLOCKS: call {{.*}}void @__kmpc_barrier( @@ -200,14 +189,10 @@ // BLOCKS-NOT: [[G]]{{[[^:word:]]}} // BLOCKS: call {{.*}}void {{%.+}}(i8 - // TLS-BLOCKS: [[G_CAPTURE_DST:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR]]() - // TLS-BLOCKS: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* [[G_CAPTURE_DST]] + // TLS-BLOCKS: store volatile i{{[0-9]+}} 1, i{{[0-9]+}}* @g // TLS-BLOCKS-NOT: [[G]]{{[[^:word:]]}} // TLS-BLOCKS: call {{.*}}void {{%.+}}(i8 - // TLS-BLOCKS: define {{.*}}i{{[0-9]+}}* [[G_CTOR]]() - // TLS-BLOCKS: ret i{{[0-9]+}}* [[G]] - // TLS-BLOCKS: } ^{ // BLOCKS: define {{.+}} void {{@.+}}(i8* // TLS-BLOCKS: define {{.+}} void {{@.+}}(i8* @@ -219,8 +204,7 @@ // BLOCKS: ret // TLS-BLOCKS-NOT: [[G]]{{[[^:word:]]}} - // TLS-BLOCKS: [[G_CAPTURE_DST:%.+]] = call{{( cxx_fast_tlscc)?}} i{{[0-9]+}}* [[G_CTOR]]() - // TLS-BLOCKS: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* [[G_CAPTURE_DST]] + // TLS-BLOCKS: store volatile i{{[0-9]+}} 2, i{{[0-9]+}}* @g // TLS-BLOCKS-NOT: [[G]]{{[[^:word:]]}} // TLS-BLOCKS: ret }(); @@ -524,7 +508,8 @@ ; } #elif defined(NESTED) -int t; +int t_init(); +int t = t_init(); #pragma omp threadprivate(t) // NESTED: foo void foo() {