diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -124,6 +124,7 @@ MSVC2017_5 = 1912, MSVC2017_7 = 1914, MSVC2019 = 1920, + MSVC2019_5 = 1925, MSVC2019_8 = 1928, }; diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -79,6 +79,18 @@ ASTContext &getContext() const { return CGM.getContext(); } + bool mayNeedDestruction(const VarDecl *VD) const; + + /// 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. + // For weak definitions, any initializer available in the current translation + // is not necessarily reflective of the initializer used; such initializers + // are ignored unless if InspectInitForWeakDef is true. + bool + isEmittedWithConstantInitializer(const VarDecl *VD, + bool InspectInitForWeakDef = false) const; + virtual bool requiresArrayCookie(const CXXDeleteExpr *E, QualType eltType); virtual bool requiresArrayCookie(const CXXNewExpr *E); diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -154,6 +154,51 @@ CGF.CXXABIThisValue = ThisPtr; } +bool CGCXXABI::mayNeedDestruction(const VarDecl *VD) const { + if (VD->needsDestruction(getContext())) + return true; + + // If the variable has an incomplete class type (or array thereof), it + // might need destruction. + const Type *T = VD->getType()->getBaseElementTypeUnsafe(); + if (T->getAs() && T->isIncompleteType()) + return true; + + return false; +} + +bool CGCXXABI::isEmittedWithConstantInitializer( + const VarDecl *VD, bool InspectInitForWeakDef) 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 (!InspectInitForWeakDef && (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 !mayNeedDestruction(VD) && 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 the + // variable being constant-initialized in every translation unit if it's + // constant-initialized in any translation unit, which isn't actually + // guaranteed by the standard but is necessary for sanity. + return InitDecl->hasConstantInitialization(); +} + void CGCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType) { assert(!CGF.hasAggregateEvaluationKind(ResultType) && diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -334,59 +334,6 @@ ArrayRef CXXThreadLocalInits, ArrayRef CXXThreadLocalInitVars) override; - bool mayNeedDestruction(const VarDecl *VD) const { - if (VD->needsDestruction(getContext())) - return true; - - // If the variable has an incomplete class type (or array thereof), it - // might need destruction. - const Type *T = VD->getType()->getBaseElementTypeUnsafe(); - if (T->getAs() && T->isIncompleteType()) - return true; - - return false; - } - - /// 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. - // For weak definitions, any initializer available in the current translation - // is not necessarily reflective of the initializer used; such initializers - // are ignored unless if InspectInitForWeakDef is true. - bool - isEmittedWithConstantInitializer(const VarDecl *VD, - bool InspectInitForWeakDef = false) 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 (!InspectInitForWeakDef && - (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 !mayNeedDestruction(VD) && 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 the - // variable being constant-initialized in every translation unit if it's - // constant-initialized in any translation unit, which isn't actually - // guaranteed by the standard but is necessary for sanity. - return InitDecl->hasConstantInitialization(); - } - bool usesThreadWrapperFunction(const VarDecl *VD) const override { return !isEmittedWithConstantInitializer(VD) || mayNeedDestruction(VD); diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -401,7 +401,9 @@ ArrayRef CXXThreadLocalInitVars) override; bool usesThreadWrapperFunction(const VarDecl *VD) const override { - return false; + return getContext().getLangOpts().isCompatibleWithMSVC( + LangOptions::MSVC2019_5) && + (!isEmittedWithConstantInitializer(VD) || mayNeedDestruction(VD)); } LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) override; @@ -2397,11 +2399,97 @@ } } +static llvm::GlobalValue *getTlsGuardVar(CodeGenModule &CGM) { + // __tls_guard comes from the MSVC runtime and reflects + // whether TLS has been initialized for a particular thread. + // It is set from within __dyn_tls_init by the runtime. + // Every library and executable has its own variable. + llvm::Type *VTy = llvm::Type::getInt8Ty(CGM.getLLVMContext()); + llvm::Constant *TlsGuardConstant = + CGM.CreateRuntimeVariable(VTy, "__tls_guard"); + llvm::GlobalValue *TlsGuard = cast(TlsGuardConstant); + + TlsGuard->setThreadLocal(true); + + return TlsGuard; +} + +static llvm::FunctionCallee getDynTlsOnDemandInitFn(CodeGenModule &CGM) { + // __dyn_tls_on_demand_init comes from the MSVC runtime and triggers + // dynamic TLS initialization by calling __dyn_tls_init internally. + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), {}, + /*isVarArg=*/false); + return CGM.CreateRuntimeFunction( + FTy, "__dyn_tls_on_demand_init", + llvm::AttributeList::get(CGM.getLLVMContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoUnwind), + /*Local=*/true); +} + +static void emitTlsGuardCheck(CodeGenFunction &CGF, llvm::GlobalValue *TlsGuard, + llvm::BasicBlock *DynInitBB, + llvm::BasicBlock *ContinueBB) { + llvm::LoadInst *TlsGuardValue = + CGF.Builder.CreateLoad(Address(TlsGuard, CharUnits::One())); + llvm::Value *CmpResult = + CGF.Builder.CreateICmpEQ(TlsGuardValue, CGF.Builder.getInt8(0)); + CGF.Builder.CreateCondBr(CmpResult, DynInitBB, ContinueBB); +} + +static void emitDynamicTlsInitializationCall(CodeGenFunction &CGF, + llvm::GlobalValue *TlsGuard, + llvm::BasicBlock *ContinueBB) { + llvm::FunctionCallee Initializer = getDynTlsOnDemandInitFn(CGF.CGM); + llvm::Function *InitializerFunction = + cast(Initializer.getCallee()); + llvm::CallInst *CallVal = CGF.Builder.CreateCall(InitializerFunction); + CallVal->setCallingConv(InitializerFunction->getCallingConv()); + + CGF.Builder.CreateBr(ContinueBB); +} + +static void emitDynamicTlsInitialization(CodeGenFunction &CGF) { + llvm::BasicBlock *DynInitBB = + CGF.createBasicBlock("dyntls.dyn_init", CGF.CurFn); + llvm::BasicBlock *ContinueBB = + CGF.createBasicBlock("dyntls.continue", CGF.CurFn); + + llvm::GlobalValue *TlsGuard = getTlsGuardVar(CGF.CGM); + + emitTlsGuardCheck(CGF, TlsGuard, DynInitBB, ContinueBB); + CGF.Builder.SetInsertPoint(DynInitBB); + emitDynamicTlsInitializationCall(CGF, TlsGuard, ContinueBB); + CGF.Builder.SetInsertPoint(ContinueBB); +} + LValue MicrosoftCXXABI::EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) { - CGF.CGM.ErrorUnsupported(VD, "thread wrappers"); - return LValue(); + // Dynamic TLS initialization works by checking the state of a + // guard variable (__tls_guard) to see whether TLS initialization + // for a thread has happend yet. + // If not, the initialization is triggered on-demand + // by calling __dyn_tls_on_demand_init. + emitDynamicTlsInitialization(CGF); + + // Emit the variable just like any regular global variable. + + llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD); + llvm::Type *RealVarTy = CGF.getTypes().ConvertTypeForMem(VD->getType()); + + unsigned AS = cast(V->getType())->getAddressSpace(); + V = CGF.Builder.CreateBitCast(V, RealVarTy->getPointerTo(AS)); + + CharUnits Alignment = CGF.getContext().getDeclAlign(VD); + Address Addr(V, Alignment); + + LValue LV = VD->getType()->isReferenceType() + ? CGF.EmitLoadOfReferenceLValue(Addr, VD->getType(), + AlignmentSource::Decl) + : CGF.MakeAddrLValue(Addr, LValType, AlignmentSource::Decl); + return LV; } static ConstantAddress getInitThreadEpochPtr(CodeGenModule &CGM) { diff --git a/clang/test/CodeGenCXX/ms-thread_local.cpp b/clang/test/CodeGenCXX/ms-thread_local.cpp --- a/clang/test/CodeGenCXX/ms-thread_local.cpp +++ b/clang/test/CodeGenCXX/ms-thread_local.cpp @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -emit-llvm -o - | FileCheck %s -// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD +// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.25 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.20 -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LEGACY +// RUN: %clang_cc1 %s -std=c++1y -triple=i686-pc-win32 -fms-compatibility-version=19.25 -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD struct A { A(); @@ -17,14 +18,22 @@ // CHECK-DAG: @"?b@@3UA@@A" = dso_local thread_local global %struct.A zeroinitializer, align 1 // CHECK-DAG: @"__tls_init$initializer$" = internal constant void ()* @__tls_init, section ".CRT$XDU" +// CHECK-DAG: @__tls_guard = external dso_local thread_local global i8 // CHECK-LD-DAG: @"?b@@3UA@@A" = dso_local thread_local(localdynamic) global %struct.A zeroinitializer, align 1 // CHECK-LD-DAG: @"__tls_init$initializer$" = internal constant void ()* @__tls_init, section ".CRT$XDU" +// CHECK-LD-DAG: @__tls_guard = external dso_local thread_local global i8 +// CHECK-LEGACY-NOT: @__tls_guard = external dso_local thread_local global i8 thread_local A b; -// CHECK-LABEL: define internal void @__tls_init() -// CHECK: call void @"??__Eb@@YAXXZ" -// CHECK-LD-LABEL: define internal void @__tls_init() -// CHECK-LD: call void @"??__Eb@@YAXXZ" +// CHECK-LABEL: declare dso_local void @__dyn_tls_on_demand_init() +// CHECK-LD-LABEL: declare dso_local void @__dyn_tls_on_demand_init() +// CHECK-LEGACY-NOT: declare dso_local void @__dyn_tls_on_demand_init() + +// CHECK-LABEL: define dso_local void @"?f@@YA?AUA@@XZ"(%struct.A* noalias sret(%struct.A) align 1 %agg.result) +// CHECK: call void @__dyn_tls_on_demand_init() +// CHECK-LD-LABEL: define dso_local void @"?f@@YA?AUA@@XZ"(%struct.A* noalias sret(%struct.A) align 1 %agg.result) +// CHECK-LD: call void @__dyn_tls_on_demand_init() +// CHECK-LEGACY-NOT: call void @__dyn_tls_on_demand_init() thread_local A &c = b; thread_local A &d = c; @@ -35,6 +44,22 @@ return c; } +// CHECK-LABEL: define dso_local i32 @"?g@@YAHXZ"() +// CHECK-NOT: call void @__dyn_tls_on_demand_init() +// CHECK-LD-LABEL: define dso_local i32 @"?g@@YAHXZ"() +// CHECK-LD-NOT: call void @__dyn_tls_on_demand_init() + +thread_local int e = 2; + +int g() { + return e; +} + +// CHECK-LABEL: define internal void @__tls_init() +// CHECK: call void @"??__Eb@@YAXXZ" +// CHECK-LD-LABEL: define internal void @__tls_init() +// CHECK-LD: call void @"??__Eb@@YAXXZ" + // CHECK: !llvm.linker.options = !{![[dyn_tls_init:[0-9]+]]} // CHECK: ![[dyn_tls_init]] = !{!"/include:___dyn_tls_init@12"} // CHECK-LD: !llvm.linker.options = !{![[dyn_tls_init:[0-9]+]]}