From: Anders Carlsson Date: Sat, 6 Feb 2010 23:23:06 +0000 (+0000) Subject: Add support for threadsafe statics, and make them the default (matching gcc). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a508b7de6c5246ab04ed69d0ab4e9977ec1fb4d4;p=clang Add support for threadsafe statics, and make them the default (matching gcc). Daniel, I'd appreciate a review of the driver/cc1 parts. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95508 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 2b6092dea3..2abf9ef7a0 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -136,8 +136,7 @@ public: SymbolVisibility = (unsigned) Default; - // FIXME: The default should be 1. - ThreadsafeStatics = 0; + ThreadsafeStatics = 1; POSIXThreads = 0; Blocks = 0; BlockIntrospection = 0; diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index aefb6fca19..23275b63ff 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -115,6 +115,8 @@ def no_implicit_float : Flag<"-no-implicit-float">, HelpText<"Don't generate implicit floating point instructions (x86-only)">; def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, HelpText<"Disallow merging of constants.">; +def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, + HelpText<"Do not emit code to make initialization of local statics thread safe">; def masm_verbose : Flag<"-masm-verbose">, HelpText<"Generate verbose assembly output">; def mcode_model : Separate<"-mcode-model">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index dbdddf4eb3..767123b14b 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -311,6 +311,7 @@ def fno_show_column : Flag<"-fno-show-column">, Group; def fno_show_source_location : Flag<"-fno-show-source-location">, Group; def fno_stack_protector : Flag<"-fno-stack-protector">, Group; def fno_strict_aliasing : Flag<"-fno-strict-aliasing">, Group; +def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, Group; def fno_unit_at_a_time : Flag<"-fno-unit-at-a-time">, Group; def fno_unwind_tables : Flag<"-fno-unwind-tables">, Group; def fno_working_directory : Flag<"-fno-working-directory">, Group; @@ -351,6 +352,7 @@ def fsyntax_only : Flag<"-fsyntax-only">, Flags<[DriverOption]>; def ftabstop_EQ : Joined<"-ftabstop=">, Group; def ftemplate_depth_ : Joined<"-ftemplate-depth-">, Group; def fterminated_vtables : Flag<"-fterminated-vtables">, Group; +def fthreadsafe_statics : Flag<"-fthreadsafe-statics">, Group; def ftime_report : Flag<"-ftime-report">, Group; def ftrapv : Flag<"-ftrapv">, Group; def funit_at_a_time : Flag<"-funit-at-a-time">, Group; diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 3d5a4e8051..0de3b0be4b 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -184,42 +184,100 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, FinishFunction(); } +static llvm::Constant *getGuardAcquireFn(CodeGenFunction &CGF) { + // int __cxa_guard_acquire(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(CGF.ConvertType(CGF.getContext().IntTy), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_acquire"); +} + +static llvm::Constant *getGuardReleaseFn(CodeGenFunction &CGF) { + // void __cxa_guard_release(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_release"); +} + +static llvm::Constant *getGuardAbortFn(CodeGenFunction &CGF) { + // void __cxa_guard_abort(__int64_t *guard_object); + + const llvm::Type *Int64PtrTy = + llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); + + std::vector Args(1, Int64PtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_abort"); +} + void CodeGenFunction::EmitStaticCXXBlockVarDeclInit(const VarDecl &D, llvm::GlobalVariable *GV) { - // FIXME: This should use __cxa_guard_{acquire,release}? - - assert(!getContext().getLangOptions().ThreadsafeStatics && - "thread safe statics are currently not supported!"); - + bool ThreadsafeStatics = getContext().getLangOptions().ThreadsafeStatics; + llvm::SmallString<256> GuardVName; CGM.getMangleContext().mangleGuardVariable(&D, GuardVName); // Create the guard variable. - llvm::GlobalValue *GuardV = - new llvm::GlobalVariable(CGM.getModule(), llvm::Type::getInt64Ty(VMContext), + const llvm::Type *Int64Ty = llvm::Type::getInt64Ty(VMContext); + llvm::GlobalValue *GuardVariable = + new llvm::GlobalVariable(CGM.getModule(), Int64Ty, false, GV->getLinkage(), - llvm::Constant::getNullValue(llvm::Type::getInt64Ty(VMContext)), + llvm::Constant::getNullValue(Int64Ty), GuardVName.str()); // Load the first byte of the guard variable. const llvm::Type *PtrTy = llvm::PointerType::get(llvm::Type::getInt8Ty(VMContext), 0); - llvm::Value *V = Builder.CreateLoad(Builder.CreateBitCast(GuardV, PtrTy), - "tmp"); + llvm::Value *V = + Builder.CreateLoad(Builder.CreateBitCast(GuardVariable, PtrTy), "tmp"); - // Compare it against 0. - llvm::Value *nullValue - = llvm::Constant::getNullValue(llvm::Type::getInt8Ty(VMContext)); - llvm::Value *ICmp = Builder.CreateICmpEQ(V, nullValue , "tobool"); - - llvm::BasicBlock *InitBlock = createBasicBlock("init"); + llvm::BasicBlock *InitCheckBlock = createBasicBlock("init.check"); llvm::BasicBlock *EndBlock = createBasicBlock("init.end"); - // If the guard variable is 0, jump to the initializer code. - Builder.CreateCondBr(ICmp, InitBlock, EndBlock); + // Check if the first byte of the guard variable is zero. + Builder.CreateCondBr(Builder.CreateIsNull(V, "tobool"), + InitCheckBlock, EndBlock); - EmitBlock(InitBlock); + EmitBlock(InitCheckBlock); + + if (ThreadsafeStatics) { + // Call __cxa_guard_acquire. + V = Builder.CreateCall(getGuardAcquireFn(*this), GuardVariable); + + llvm::BasicBlock *InitBlock = createBasicBlock("init"); + + Builder.CreateCondBr(Builder.CreateIsNotNull(V, "tobool"), + InitBlock, EndBlock); + + EmitBlock(InitBlock); + + if (Exceptions) { + EHCleanupBlock Cleanup(*this); + + // Call __cxa_guard_abort. + Builder.CreateCall(getGuardAbortFn(*this), GuardVariable); + } + } if (D.getType()->isReferenceType()) { QualType T = D.getType(); @@ -232,9 +290,14 @@ CodeGenFunction::EmitStaticCXXBlockVarDeclInit(const VarDecl &D, } else EmitDeclInit(*this, D, GV); - Builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt8Ty(VMContext), - 1), - Builder.CreateBitCast(GuardV, PtrTy)); + if (ThreadsafeStatics) { + // Call __cxa_guard_release. + Builder.CreateCall(getGuardReleaseFn(*this), GuardVariable); + } else { + llvm::Value *One = + llvm::ConstantInt::get(llvm::Type::getInt8Ty(VMContext), 1); + Builder.CreateStore(One, Builder.CreateBitCast(GuardVariable, PtrTy)); + } EmitBlock(EndBlock); } diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 6c0616d370..2aa71163be 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -1013,6 +1013,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, isSignedCharDefault(getToolChain().getTriple()))) CmdArgs.push_back("-fno-signed-char"); + // -fthreadsafe-static is default. + if (!Args.hasFlag(options::OPT_fthreadsafe_statics, + options::OPT_fno_threadsafe_statics)) + CmdArgs.push_back("-fno-threadsafe-statics"); + // -fms-extensions=0 is default. if (Args.hasFlag(options::OPT_fms_extensions, options::OPT_fno_ms_extensions, getToolChain().getTriple().getOS() == llvm::Triple::Win32)) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 464c9938c8..5b64c7e639 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -480,8 +480,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fno-builtin"); if (!Opts.AssumeSaneOperatorNew) Res.push_back("-fno-assume-sane-operator-new"); - if (Opts.ThreadsafeStatics) - llvm::llvm_report_error("FIXME: Not yet implemented!"); + if (!Opts.ThreadsafeStatics) + Res.push_back("-fno-threadsafe-statics"); if (Opts.POSIXThreads) Res.push_back("-pthread"); if (Opts.Blocks) @@ -1155,7 +1155,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.Microsoft = Args.hasArg(OPT_fms_extensions); Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); if (Args.hasArg(OPT_fno_lax_vector_conversions)) - Opts.LaxVectorConversions = 0; + Opts.LaxVectorConversions = 0; + if (Args.hasArg(OPT_fno_threadsafe_statics)) + Opts.ThreadsafeStatics = 0; Opts.Exceptions = Args.hasArg(OPT_fexceptions); Opts.RTTI = !Args.hasArg(OPT_fno_rtti); Opts.Blocks = Args.hasArg(OPT_fblocks); diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 45cd930ba0..1cdfde3488 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -3005,7 +3005,6 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity, bool IsCopy) { switch (Entity.getKind()) { case InitializedEntity::EK_Result: - case InitializedEntity::EK_Exception: case InitializedEntity::EK_ArrayElement: case InitializedEntity::EK_Member: return !IsCopy; @@ -3014,6 +3013,7 @@ static bool shouldBindAsTemporary(const InitializedEntity &Entity, case InitializedEntity::EK_Variable: case InitializedEntity::EK_Base: case InitializedEntity::EK_VectorElement: + case InitializedEntity::EK_Exception: return false; case InitializedEntity::EK_Parameter: diff --git a/test/CodeGenCXX/threadsafe-statics.cpp b/test/CodeGenCXX/threadsafe-statics.cpp new file mode 100644 index 0000000000..65ebc43c5d --- /dev/null +++ b/test/CodeGenCXX/threadsafe-statics.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck -check-prefix=WITH-TSS %s +// RUN: %clang_cc1 -emit-llvm -o - %s -fno-threadsafe-statics | FileCheck -check-prefix=NO-TSS %s + +int f(); + +// WITH-TSS: define void @_Z1gv() nounwind +// WITH-TSS: call i32 @__cxa_guard_acquire +// WITH-TSS: call void @__cxa_guard_release +// WITH-TSS: ret void +void g() { + static int a = f(); +} + +// NO-TSS: define void @_Z1gv() nounwind +// NO-TSS-NOT: call i32 @__cxa_guard_acquire +// NO-TSS-NOT: call void @__cxa_guard_release +// NO-TSS: ret void