Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -124,6 +124,7 @@ MSVC2017_5 = 1912, MSVC2017_7 = 1914, MSVC2019 = 1920, + MSVC2019_5 = 1925, MSVC2019_8 = 1928, }; Index: clang/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -401,7 +401,8 @@ ArrayRef CXXThreadLocalInitVars) override; bool usesThreadWrapperFunction(const VarDecl *VD) const override { - return false; + return getContext().getLangOpts().isCompatibleWithMSVC( + LangOptions::MSVC2019_5); } LValue EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, const VarDecl *VD, QualType LValType) override; @@ -2397,11 +2398,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) { Index: clang/test/CodeGenCXX/ms-thread_local.cpp =================================================================== --- clang/test/CodeGenCXX/ms-thread_local.cpp +++ 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,10 +18,23 @@ // 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: 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() + // CHECK-LABEL: define internal void @__tls_init() // CHECK: call void @"??__Eb@@YAXXZ" // CHECK-LD-LABEL: define internal void @__tls_init()