diff --git a/docs/MSVCCompatibility.rst b/docs/MSVCCompatibility.rst index 8f5d70e85eebab474d59b9e84b7b2136e148faf5..2228a9aed00338eab8c36f4640dcd6fc53a21722 100644 --- a/docs/MSVCCompatibility.rst +++ b/docs/MSVCCompatibility.rst @@ -95,10 +95,11 @@ The status of major ABI-impacting C++ features: .. _consistent with Visual C++: https://msdn.microsoft.com/en-us/library/wfa0edys.aspx -* Thread-safe initialization of local statics: :none:`Unstarted`. We are ABI - compatible with MSVC 2013, which does not support thread-safe local statics. - MSVC "14" changed the ABI to make initialization of local statics thread safe, - and we have not yet implemented this. +* Thread-safe initialization of local statics: :none:`Complete`. MSVC 2015 + added support for thread-safe initialization of such variables by taking an + ABI break. + We are ABI compatible with both the MSVC 2013 and 2015 ABI for static local + variables. * Lambdas: :good:`Mostly complete`. Clang is compatible with Microsoft's implementation of lambdas except for providing overloads for conversion to diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index 1050e65ff02babe834bfa73205484533b8d75812..c5a7ea16a7ecc17dea9df79276b6955da7d925df 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -197,6 +197,10 @@ public: ArrayRef<const CXXRecordDecl *> BasePath, raw_ostream &Out) = 0; + virtual void mangleThreadSafeStaticGuardVariable(const VarDecl *VD, + unsigned GuardNum, + raw_ostream &Out) = 0; + virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD, raw_ostream &) = 0; diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 7fc4c2623fd78ec181d3ce893315c760a6af470c..0bac4159e51dd9c7f8130ae5ee672e4bbd07c599 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -147,6 +147,8 @@ public: void mangleReferenceTemporary(const VarDecl *, unsigned ManglingNumber, raw_ostream &) override; void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &Out) override; + void mangleThreadSafeStaticGuardVariable(const VarDecl *D, unsigned GuardNum, + raw_ostream &Out) override; void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicAtExitDestructor(const VarDecl *D, raw_ostream &Out) override; @@ -2521,17 +2523,16 @@ void MicrosoftMangleContextImpl::mangleReferenceTemporary(const VarDecl *VD, getDiags().Report(VD->getLocation(), DiagID); } +void MicrosoftMangleContextImpl::mangleThreadSafeStaticGuardVariable( + const VarDecl *VD, unsigned GuardNum, raw_ostream &Out) { + MicrosoftCXXNameMangler Mangler(*this, Out); + + Mangler.getStream() << "\01?$TSS" << GuardNum << '@'; + Mangler.mangleNestedName(VD); +} + void MicrosoftMangleContextImpl::mangleStaticGuardVariable(const VarDecl *VD, raw_ostream &Out) { - // TODO: This is not correct, especially with respect to VS "14". VS "14" - // utilizes thread local variables to implement thread safe, re-entrant - // initialization for statics. They no longer differentiate between an - // externally visible and non-externally visible static with respect to - // mangling, they all get $TSS <number>. - // - // N.B. This means that they can get more than 32 static variable guards in a - // scope. It also means that they broke compatibility with their own ABI. - // <guard-name> ::= ?_B <postfix> @5 <scope-depth> // ::= ?$S <guard-num> @ <postfix> @4IA @@ -2544,8 +2545,14 @@ void MicrosoftMangleContextImpl::mangleStaticGuardVariable(const VarDecl *VD, MicrosoftCXXNameMangler Mangler(*this, Out); bool Visible = VD->isExternallyVisible(); - // <operator-name> ::= ?_B # local static guard - Mangler.getStream() << (Visible ? "\01??_B" : "\01?$S1@"); + if (Visible) { + Mangler.getStream() << (getASTContext().getLangOpts().isCompatibleWithMSVC( + 19) + ? "\01??__J" + : "\01??_B"); + } else { + Mangler.getStream() << "\01?$S1@"; + } unsigned ScopeDepth = 0; if (Visible && !getNextDiscriminator(VD, ScopeDepth)) // If we do not have a discriminator and are emitting a guard variable for diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 9c89e4ab8e25d097691aed9aa8daf12097cfb774..431633e808eaa3d939b6fae715d6e07f8316fac8 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -687,6 +687,7 @@ private: /// Map from DeclContext to the current guard variable. We assume that the /// AST is visited in source code order. llvm::DenseMap<const DeclContext *, GuardInfo> GuardVariableMap; + llvm::DenseMap<const DeclContext *, unsigned> ThreadSafeGuardNumMap; llvm::DenseMap<size_t, llvm::StructType *> TypeDescriptorTypeMap; llvm::StructType *BaseClassDescriptorType; @@ -2013,6 +2014,81 @@ LValue MicrosoftCXXABI::EmitThreadLocalVarDeclLValue(CodeGenFunction &CGF, return LValue(); } +static llvm::GlobalVariable *getInitThreadEpochPtr(CodeGenModule &CGM) { + StringRef VarName("_Init_thread_epoch"); + if (auto *GV = CGM.getModule().getNamedGlobal(VarName)) + return GV; + auto *GV = new llvm::GlobalVariable( + CGM.getModule(), CGM.IntTy, + /*Constant=*/false, llvm::GlobalVariable::ExternalLinkage, + /*Initializer=*/nullptr, VarName, + /*InsertBefore=*/nullptr, llvm::GlobalVariable::GeneralDynamicTLSModel); + GV->setAlignment(CGM.getTarget().getIntAlign() / 8); + return GV; +} + +static llvm::Constant *getInitThreadHeaderFn(CodeGenModule &CGM) { + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), + CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + return CGM.CreateRuntimeFunction( + FTy, "_Init_thread_header", + llvm::AttributeSet::get(CGM.getLLVMContext(), + llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoUnwind)); +} + +static llvm::Constant *getInitThreadFooterFn(CodeGenModule &CGM) { + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), + CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + return CGM.CreateRuntimeFunction( + FTy, "_Init_thread_footer", + llvm::AttributeSet::get(CGM.getLLVMContext(), + llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoUnwind)); +} + +static llvm::Constant *getInitThreadAbortFn(CodeGenModule &CGM) { + llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), + CGM.IntTy->getPointerTo(), /*isVarArg=*/false); + return CGM.CreateRuntimeFunction( + FTy, "_Init_thread_abort", + llvm::AttributeSet::get(CGM.getLLVMContext(), + llvm::AttributeSet::FunctionIndex, + llvm::Attribute::NoUnwind)); +} + +namespace { +struct ResetGuardBit : EHScopeStack::Cleanup { + llvm::GlobalVariable *Guard; + unsigned GuardNum; + ResetGuardBit(llvm::GlobalVariable *Guard, unsigned GuardNum) + : Guard(Guard), GuardNum(GuardNum) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + // Reset the bit in the mask so that the static variable may be + // reinitialized. + CGBuilderTy &Builder = CGF.Builder; + llvm::LoadInst *LI = Builder.CreateLoad(Guard); + llvm::ConstantInt *Mask = + llvm::ConstantInt::get(CGF.IntTy, ~(1U << GuardNum)); + Builder.CreateStore(Builder.CreateAnd(LI, Mask), Guard); + } +}; + +struct CallInitThreadAbort : EHScopeStack::Cleanup { + llvm::GlobalVariable *Guard; + CallInitThreadAbort(llvm::GlobalVariable *Guard) : Guard(Guard) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + // Calling _Init_thread_abort will reset the guard's state. + CGF.EmitNounwindRuntimeCall(getInitThreadAbortFn(CGF.CGM), Guard); + } +}; +} + void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *GV, bool PerformInit) { @@ -2027,13 +2103,8 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, return; } - // MSVC always uses an i32 bitfield to guard initialization, which is *not* - // threadsafe. Since the user may be linking in inline functions compiled by - // cl.exe, there's no reason to provide a false sense of security by using - // critical sections here. - - if (D.getTLSKind()) - CGM.ErrorUnsupported(&D, "dynamic TLS initialization"); + bool HasPerVariableGuard = + getContext().getLangOpts().ThreadsafeStatics && !D.getTLSKind(); CGBuilderTy &Builder = CGF.Builder; llvm::IntegerType *GuardTy = CGF.Int32Ty; @@ -2041,75 +2112,139 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, // Get the guard variable for this function if we have one already. GuardInfo *GI = &GuardVariableMap[D.getDeclContext()]; - - unsigned BitIndex; + llvm::GlobalVariable *GuardVar = GI->Guard; + unsigned GuardNum; if (D.isStaticLocal() && D.isExternallyVisible()) { // Externally visible variables have to be numbered in Sema to properly // handle unreachable VarDecls. - BitIndex = getContext().getStaticLocalNumber(&D); - assert(BitIndex > 0); - BitIndex--; + GuardNum = getContext().getStaticLocalNumber(&D); + assert(GuardNum > 0); + GuardNum--; + } else if (HasPerVariableGuard) { + GuardNum = ThreadSafeGuardNumMap[D.getDeclContext()]++; } else { // Non-externally visible variables are numbered here in CodeGen. - BitIndex = GI->BitIndex++; + GuardNum = GI->BitIndex++; } - if (BitIndex >= 32) { + if (HasPerVariableGuard) + GuardVar = nullptr; + + if (!HasPerVariableGuard && GuardNum >= 32) { if (D.isExternallyVisible()) ErrorUnsupportedABI(CGF, "more than 32 guarded initializations"); - BitIndex %= 32; - GI->Guard = nullptr; + GuardNum %= 32; + GuardVar = nullptr; } - // Lazily create the i32 bitfield for this function. - if (!GI->Guard) { + if (!GuardVar) { // Mangle the name for the guard. SmallString<256> GuardName; { llvm::raw_svector_ostream Out(GuardName); - getMangleContext().mangleStaticGuardVariable(&D, Out); + if (HasPerVariableGuard) + getMangleContext().mangleThreadSafeStaticGuardVariable(&D, GuardNum, + Out); + else + getMangleContext().mangleStaticGuardVariable(&D, Out); Out.flush(); } // Create the guard variable with a zero-initializer. Just absorb linkage, // visibility and dll storage class from the guarded variable. - GI->Guard = - new llvm::GlobalVariable(CGM.getModule(), GuardTy, false, + GuardVar = + new llvm::GlobalVariable(CGM.getModule(), GuardTy, /*isConstant=*/false, GV->getLinkage(), Zero, GuardName.str()); - GI->Guard->setVisibility(GV->getVisibility()); - GI->Guard->setDLLStorageClass(GV->getDLLStorageClass()); - if (GI->Guard->isWeakForLinker()) - GI->Guard->setComdat( - CGM.getModule().getOrInsertComdat(GI->Guard->getName())); + GuardVar->setVisibility(GV->getVisibility()); + GuardVar->setDLLStorageClass(GV->getDLLStorageClass()); + if (GuardVar->isWeakForLinker()) + GuardVar->setComdat( + CGM.getModule().getOrInsertComdat(GuardVar->getName())); + if (D.getTLSKind()) + GuardVar->setThreadLocal(true); + if (GI && !HasPerVariableGuard) + GI->Guard = GuardVar; + } + + assert(GuardVar->getLinkage() == GV->getLinkage() && + "static local from the same function had different linkage"); + + if (!HasPerVariableGuard) { + // Pseudo code for the test: + // if (!(GuardVar & MyGuardBit)) { + // GuardVar |= MyGuardBit; + // ... initialize the object ...; + // } + + // Test our bit from the guard variable. + llvm::ConstantInt *Bit = llvm::ConstantInt::get(GuardTy, 1U << GuardNum); + llvm::LoadInst *LI = Builder.CreateLoad(GuardVar); + llvm::Value *IsInitialized = + Builder.CreateICmpNE(Builder.CreateAnd(LI, Bit), Zero); + llvm::BasicBlock *InitBlock = CGF.createBasicBlock("init"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end"); + Builder.CreateCondBr(IsInitialized, EndBlock, InitBlock); + + // Set our bit in the guard variable and emit the initializer and add a global + // destructor if appropriate. + CGF.EmitBlock(InitBlock); + Builder.CreateStore(Builder.CreateOr(LI, Bit), GuardVar); + CGF.EHStack.pushCleanup<ResetGuardBit>(EHCleanup, GuardVar, GuardNum); + CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit); + CGF.PopCleanupBlock(); + Builder.CreateBr(EndBlock); + + // Continue. + CGF.EmitBlock(EndBlock); } else { - assert(GI->Guard->getLinkage() == GV->getLinkage() && - "static local from the same function had different linkage"); - } - - // Pseudo code for the test: - // if (!(GuardVar & MyGuardBit)) { - // GuardVar |= MyGuardBit; - // ... initialize the object ...; - // } - - // Test our bit from the guard variable. - llvm::ConstantInt *Bit = llvm::ConstantInt::get(GuardTy, 1U << BitIndex); - llvm::LoadInst *LI = Builder.CreateLoad(GI->Guard); - llvm::Value *IsInitialized = - Builder.CreateICmpNE(Builder.CreateAnd(LI, Bit), Zero); - llvm::BasicBlock *InitBlock = CGF.createBasicBlock("init"); - llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end"); - Builder.CreateCondBr(IsInitialized, EndBlock, InitBlock); - - // Set our bit in the guard variable and emit the initializer and add a global - // destructor if appropriate. - CGF.EmitBlock(InitBlock); - Builder.CreateStore(Builder.CreateOr(LI, Bit), GI->Guard); - CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit); - Builder.CreateBr(EndBlock); - - // Continue. - CGF.EmitBlock(EndBlock); + // Pseudo code for the test: + // if (TSS > _Init_thread_epoch) { + // _Init_thread_header(&TSS); + // if (TSS == -1) { + // ... initialize the object ...; + // _Init_thread_footer(&TSS); + // } + // } + // + // The algorithm is almost identical to what can be found in the appendix + // found in N2325. + + unsigned IntAlign = CGM.getTarget().getIntAlign() / 8; + + // This BasicBLock determines whether or not we have any work to do. + llvm::LoadInst *FirstGuardLoad = + Builder.CreateAlignedLoad(GuardVar, IntAlign); + FirstGuardLoad->setOrdering(llvm::AtomicOrdering::Unordered); + llvm::LoadInst *InitThreadEpoch = + Builder.CreateLoad(getInitThreadEpochPtr(CGM)); + llvm::Value *IsUninitialized = + Builder.CreateICmpSGT(FirstGuardLoad, InitThreadEpoch); + llvm::BasicBlock *AttemptInitBlock = CGF.createBasicBlock("init.attempt"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end"); + Builder.CreateCondBr(IsUninitialized, AttemptInitBlock, EndBlock); + + // This BasicBlock attempts to determine whether or not this thread is + // responsible for doing the initialization. + CGF.EmitBlock(AttemptInitBlock); + CGF.EmitNounwindRuntimeCall(getInitThreadHeaderFn(CGM), GuardVar); + llvm::LoadInst *SecondGuardLoad = + Builder.CreateAlignedLoad(GuardVar, IntAlign); + SecondGuardLoad->setOrdering(llvm::AtomicOrdering::Unordered); + llvm::Value *ShouldDoInit = + Builder.CreateICmpEQ(SecondGuardLoad, getAllOnesInt()); + llvm::BasicBlock *InitBlock = CGF.createBasicBlock("init"); + Builder.CreateCondBr(ShouldDoInit, InitBlock, EndBlock); + + // Ok, we ended up getting selected as the initializing thread. + CGF.EmitBlock(InitBlock); + CGF.EHStack.pushCleanup<CallInitThreadAbort>(EHCleanup, GuardVar); + CGF.EmitCXXGlobalVarDeclInit(D, GV, PerformInit); + CGF.PopCleanupBlock(); + CGF.EmitNounwindRuntimeCall(getInitThreadFooterFn(CGM), GuardVar); + Builder.CreateBr(EndBlock); + + CGF.EmitBlock(EndBlock); + } } bool MicrosoftCXXABI::isZeroInitializable(const MemberPointerType *MPT) { diff --git a/test/CodeGenCXX/dllexport.cpp b/test/CodeGenCXX/dllexport.cpp index cc66a668770a6d136178998bef03f2184a7ca05d..3e398d21a032046f01adccf218991d14e0771232 100644 --- a/test/CodeGenCXX/dllexport.cpp +++ b/test/CodeGenCXX/dllexport.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -std=c++1y -O1 -mconstructor-aliases -disable-llvm-optzns -o - %s -w | FileCheck --check-prefix=MSC --check-prefix=M32 %s -// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=MSC --check-prefix=M64 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G32 %s -// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G64 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -std=c++1y -fno-threadsafe-statics -O1 -mconstructor-aliases -disable-llvm-optzns -o - %s -w | FileCheck --check-prefix=MSC --check-prefix=M32 %s +// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -std=c++1y -fno-threadsafe-statics -O0 -o - %s -w | FileCheck --check-prefix=MSC --check-prefix=M64 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -std=c++1y -fno-threadsafe-statics -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G32 %s +// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -std=c++1y -fno-threadsafe-statics -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G64 %s // Helper structs to make templates more expressive. struct ImplicitInst_Exported {}; diff --git a/test/CodeGenCXX/dllimport.cpp b/test/CodeGenCXX/dllimport.cpp index d2c6765d0c8563a8b16ea1eaab4a756f5b3c061a..2fca59cf18fa7c4374f5e3a307e1d70c1e146c56 100644 --- a/test/CodeGenCXX/dllimport.cpp +++ b/test/CodeGenCXX/dllimport.cpp @@ -1,13 +1,13 @@ -// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC --check-prefix=M32 %s -// RUN: %clang_cc1 -triple x86_64-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC --check-prefix=M64 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G32 %s -// RUN: %clang_cc1 -triple x86_64-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G64 %s -// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O1 -o - %s -DMSABI -w | FileCheck --check-prefix=MO1 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O1 -o - %s -w | FileCheck --check-prefix=GO1 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC --check-prefix=M32 %s +// RUN: %clang_cc1 -triple x86_64-windows-msvc -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC --check-prefix=M64 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G32 %s +// RUN: %clang_cc1 -triple x86_64-windows-gnu -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU --check-prefix=G64 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O1 -o - %s -DMSABI -w | FileCheck --check-prefix=MO1 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O1 -o - %s -w | FileCheck --check-prefix=GO1 %s // CHECK-NOT doesn't play nice with CHECK-DAG, so use separate run lines. -// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC2 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU2 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -DMSABI -w | FileCheck --check-prefix=MSC2 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -fno-threadsafe-statics -emit-llvm -std=c++1y -O0 -o - %s -w | FileCheck --check-prefix=GNU2 %s // Helper structs to make templates more expressive. struct ImplicitInst_Imported {}; diff --git a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp index 97d4b5bfc7e3533a1ea76c664eea4c6b43efba48..5094623d7d0cf3f9679026c81055bef1a800207b 100644 --- a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp +++ b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fms-extensions -emit-llvm %s -o - -mconstructor-aliases -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fms-extensions -fno-threadsafe-statics -emit-llvm %s -o - -mconstructor-aliases -triple=i386-pc-win32 | FileCheck %s // CHECK: @llvm.global_ctors = appending global [5 x { i32, void ()*, i8* }] [ // CHECK: { i32, void ()*, i8* } { i32 65535, void ()* @"\01??__Eselectany1@@YAXXZ", i8* getelementptr inbounds (%struct.S, %struct.S* @"\01?selectany1@@3US@@A", i32 0, i32 0) }, diff --git a/test/CodeGenCXX/microsoft-abi-thread-safe-statics.cpp b/test/CodeGenCXX/microsoft-abi-thread-safe-statics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97a2095b3b47af723be6cc955a717da0e3355bcf --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-thread-safe-statics.cpp @@ -0,0 +1,81 @@ +// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fms-extensions -fms-compatibility -fms-compatibility-version=19 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s +struct S { + S(); + ~S(); +}; + +// CHECK-DAG: @"\01?s@?1??f@@YAAAUS@@XZ@4U2@A" = linkonce_odr thread_local global %struct.S zeroinitializer +// CHECK-DAG: @"\01??__J?1??f@@YAAAUS@@XZ@51" = linkonce_odr thread_local global i32 0 +// CHECK-DAG: @"\01?s@?1??g@@YAAAUS@@XZ@4U2@A" = linkonce_odr global %struct.S zeroinitializer +// CHECK-DAG: @"\01?$TSS0@?1??g@@YAAAUS@@XZ" = linkonce_odr global i32 0 +// CHECK-DAG: @_Init_thread_epoch = external thread_local global i32, align 4 + +// CHECK-LABEL: define {{.*}} @"\01?f@@YAAAUS@@XZ"() +extern inline S &f() { + static thread_local S s; +// CHECK: %[[guard:.*]] = load i32, i32* @"\01??__J?1??f@@YAAAUS@@XZ@51" +// CHECK-NEXT: %[[mask:.*]] = and i32 %[[guard]], 1 +// CHECK-NEXT: %[[cmp:.*]] = icmp ne i32 %[[mask]], 0 +// CHECK-NEXT: br i1 %[[cmp]], label %[[init_end:.*]], label %[[init:.*]] +// +// CHECK: [[init]]: +// CHECK-NEXT: %[[or:.*]] = or i32 %[[guard]], 1 +// CHECK-NEXT: store i32 %[[or]], i32* @"\01??__J?1??f@@YAAAUS@@XZ@51" +// CHECK-NEXT: invoke {{.*}} @"\01??0S@@QAE@XZ"(%struct.S* @"\01?s@?1??f@@YAAAUS@@XZ@4U2@A") +// CHECK-NEXT: to label %[[invoke_cont:.*]] unwind label %[[lpad:.*]] +// +// CHECK: [[invoke_cont]]: +// CHECK-NEXT: call i32 @__tlregdtor(void ()* @"\01??__Fs@?1??f@@YAAAUS@@XZ@YAXXZ") +// CHECK-NEXT: br label %[[init_end:.*]] + +// CHECK: [[init_end]]: +// CHECK-NEXT: ret %struct.S* @"\01?s@?1??f@@YAAAUS@@XZ@4U2@A" + +// CHECK: [[lpad:.*]]: +// CHECK-NEXT: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) +// CHECK-NEXT: cleanup +// CHECK: %[[guard:.*]] = load i32, i32* @"\01??__J?1??f@@YAAAUS@@XZ@51" +// CHECK-NEXT: %[[mask:.*]] = and i32 %[[guard]], -2 +// CHECK-NEXT: store i32 %[[mask]], i32* @"\01??__J?1??f@@YAAAUS@@XZ@51" +// CHECK-NEXT: br label %[[eh_resume:.*]] +// +// CHECK: [[eh_resume]]: +// CHECK: resume { i8*, i32 } + return s; +} + + +// CHECK-LABEL: define {{.*}} @"\01?g@@YAAAUS@@XZ"() +extern inline S &g() { + static S s; +// CHECK: %[[guard:.*]] = load atomic i32, i32* @"\01?$TSS0@?1??g@@YAAAUS@@XZ" unordered, align 4 +// CHECK-NEXT: %[[epoch:.*]] = load i32, i32* @_Init_thread_epoch +// CHECK-NEXT: %[[cmp:.*]] = icmp sgt i32 %[[guard]], %[[epoch]] +// CHECK-NEXT: br i1 %[[cmp]], label %[[init_attempt:.*]], label %[[init_end:.*]] +// +// CHECK: [[init_attempt]]: +// CHECK-NEXT: call void @_Init_thread_header(i32* @"\01?$TSS0@?1??g@@YAAAUS@@XZ") +// CHECK-NEXT: %[[guard2:.*]] = load atomic i32, i32* @"\01?$TSS0@?1??g@@YAAAUS@@XZ" unordered, align 4 +// CHECK-NEXT: %[[cmp2:.*]] = icmp eq i32 %[[guard2]], -1 +// CHECK-NEXT: br i1 %[[cmp2]], label %[[init:.*]], label %[[init_end:.*]] +// +// CHECK: [[init]]: +// CHECK-NEXT: invoke {{.*}} @"\01??0S@@QAE@XZ"(%struct.S* @"\01?s@?1??g@@YAAAUS@@XZ@4U2@A") +// CHECK-NEXT: to label %[[invoke_cont:.*]] unwind label %[[lpad:.*]] +// +// CHECK: [[invoke_cont]]: +// CHECK-NEXT: call i32 @atexit(void ()* @"\01??__Fs@?1??g@@YAAAUS@@XZ@YAXXZ") +// CHECK-NEXT: call void @_Init_thread_footer(i32* @"\01?$TSS0@?1??g@@YAAAUS@@XZ") +// CHECK-NEXT: br label %init.end +// +// CHECK: [[init_end]]: +// CHECK-NEXT: ret %struct.S* @"\01?s@?1??g@@YAAAUS@@XZ@4U2@A" +// +// CHECK: [[lpad]]: +// CHECK: call void @_Init_thread_abort(i32* @"\01?$TSS0@?1??g@@YAAAUS@@XZ") +// CHECK-NEXT: br label %[[eh_resume:.*]] +// +// CHECK: [[eh_resume]]: +// CHECK: resume { i8*, i32 } + return s; +}