From f60f6af9e84fe7752edb6fa42464778ac8a53e99 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Thu, 28 Jun 2012 08:01:44 +0000 Subject: [PATCH] Add -ftls-model command-line flag. This allows for setting the default TLS model. (PR9788) llvm-svn: 159336 --- clang/docs/tools/clang.pod | 8 ++++ clang/include/clang/Driver/Options.td | 1 + clang/include/clang/Frontend/CodeGenOptions.h | 11 +++++ clang/lib/CodeGen/CGDecl.cpp | 18 +++---- clang/lib/CodeGen/CodeGenModule.cpp | 48 ++++++++++++++++--- clang/lib/CodeGen/CodeGenModule.h | 13 ++--- clang/lib/Driver/Tools.cpp | 2 + clang/lib/Frontend/CompilerInvocation.cpp | 32 ++++++++++++- clang/test/CodeGen/tls-model.c | 28 +++++++++++ 9 files changed, 132 insertions(+), 29 deletions(-) create mode 100644 clang/test/CodeGen/tls-model.c diff --git a/clang/docs/tools/clang.pod b/clang/docs/tools/clang.pod index 8f61568e0761..425a91ef037d 100644 --- a/clang/docs/tools/clang.pod +++ b/clang/docs/tools/clang.pod @@ -303,6 +303,14 @@ This flag sets the default visibility level. This flag specifies that variables without initializers get common linkage. It can be disabled with B<-fno-common>. +=item B<-ftls-model> + +Set the default thread-local storage (TLS) model to use for thread-local +variables. Valid values are: "global-dynamic", "local-dynamic", "initial-exec" +and "local-exec". The default is "global-dynamic". The default model can be +overridden with the tls_model attribute. The compiler will try to choose a more +efficient model if possible. + =item B<-flto> B<-emit-llvm> Generate output files in LLVM formats, suitable for link time optimization. When diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 0161e2d48b49..6a99b5a64499 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -648,6 +648,7 @@ def Wframe_larger_than_EQ : Joined<"-Wframe-larger-than=">, Alias, Alias; def fthreadsafe_statics : Flag<"-fthreadsafe-statics">, Group; def ftime_report : Flag<"-ftime-report">, Group, Flags<[CC1Option]>; +def ftlsmodel_EQ : Joined<"-ftls-model=">, Group, Flags<[CC1Option]>; def ftrapv : Flag<"-ftrapv">, Group, Flags<[CC1Option]>, HelpText<"Trap on integer overflow">; def ftrapv_handler_EQ : Joined<"-ftrapv-handler=">, Group, diff --git a/clang/include/clang/Frontend/CodeGenOptions.h b/clang/include/clang/Frontend/CodeGenOptions.h index c3ac10c982b5..b36f5f275127 100644 --- a/clang/include/clang/Frontend/CodeGenOptions.h +++ b/clang/include/clang/Frontend/CodeGenOptions.h @@ -44,6 +44,13 @@ public: FullDebugInfo // Generate complete debug info. }; + enum TLSModel { + GeneralDynamicTLSModel, + LocalDynamicTLSModel, + InitialExecTLSModel, + LocalExecTLSModel + }; + unsigned AsmVerbose : 1; ///< -dA, -fverbose-asm. unsigned ObjCAutoRefCountExceptions : 1; ///< Whether ARC should be EH-safe. unsigned CUDAIsDevice : 1; ///< Set when compiling for CUDA device. @@ -175,6 +182,9 @@ public: /// The run-time penalty for bounds checking, or 0 to disable. unsigned char BoundsChecking; + /// The default TLS model to use. + TLSModel DefaultTLSModel; + public: CodeGenOptions() { AsmVerbose = 0; @@ -231,6 +241,7 @@ public: DebugInfo = NoDebugInfo; Inlining = NoInlining; RelocationModel = "pic"; + DefaultTLSModel = GeneralDynamicTLSModel; } ObjCDispatchMethodKind getObjCDispatchMethod() const { diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 08a938254ba1..be6638e0e57b 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -183,26 +183,20 @@ CodeGenFunction::CreateStaticVarDecl(const VarDecl &D, else Name = GetStaticDeclName(*this, D, Separator); - llvm::GlobalVariable::ThreadLocalMode TLM; - TLM = D.isThreadSpecified() ? llvm::GlobalVariable::GeneralDynamicTLSModel - : llvm::GlobalVariable::NotThreadLocal; - - // Set the TLS mode if it it's explicitly specified. - if (D.hasAttr()) { - assert(D.isThreadSpecified() && "Can't have TLS model on non-tls var."); - const TLSModelAttr *Attr = D.getAttr(); - TLM = CodeGenModule::GetLLVMTLSModel(Attr->getModel()); - } - llvm::Type *LTy = CGM.getTypes().ConvertTypeForMem(Ty); llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), LTy, Ty.isConstant(getContext()), Linkage, - CGM.EmitNullConstant(D.getType()), Name, 0, TLM, + CGM.EmitNullConstant(D.getType()), Name, 0, + llvm::GlobalVariable::NotThreadLocal, CGM.getContext().getTargetAddressSpace(Ty)); GV->setAlignment(getContext().getDeclAlign(&D).getQuantity()); if (Linkage != llvm::GlobalValue::InternalLinkage) GV->setVisibility(CurFn->getVisibility()); + + if (D.isThreadSpecified()) + CGM.setTLSMode(GV, D); + return GV; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 8f769d90204f..25053b91a4cd 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -258,6 +258,45 @@ void CodeGenModule::setGlobalVisibility(llvm::GlobalValue *GV, GV->setVisibility(GetLLVMVisibility(LV.visibility())); } +static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(StringRef S) { + return llvm::StringSwitch(S) + .Case("global-dynamic", llvm::GlobalVariable::GeneralDynamicTLSModel) + .Case("local-dynamic", llvm::GlobalVariable::LocalDynamicTLSModel) + .Case("initial-exec", llvm::GlobalVariable::InitialExecTLSModel) + .Case("local-exec", llvm::GlobalVariable::LocalExecTLSModel); +} + +static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel( + CodeGenOptions::TLSModel M) { + switch (M) { + case CodeGenOptions::GeneralDynamicTLSModel: + return llvm::GlobalVariable::GeneralDynamicTLSModel; + case CodeGenOptions::LocalDynamicTLSModel: + return llvm::GlobalVariable::LocalDynamicTLSModel; + case CodeGenOptions::InitialExecTLSModel: + return llvm::GlobalVariable::InitialExecTLSModel; + case CodeGenOptions::LocalExecTLSModel: + return llvm::GlobalVariable::LocalExecTLSModel; + } + llvm_unreachable("Invalid TLS model!"); +} + +void CodeGenModule::setTLSMode(llvm::GlobalVariable *GV, + const VarDecl &D) const { + assert(D.isThreadSpecified() && "setting TLS mode on non-TLS var!"); + + llvm::GlobalVariable::ThreadLocalMode TLM; + TLM = GetLLVMTLSModel(CodeGenOpts.DefaultTLSModel); + + // Override the TLS model if it is explicitly specified. + if (D.hasAttr()) { + const TLSModelAttr *Attr = D.getAttr(); + TLM = GetLLVMTLSModel(Attr->getModel()); + } + + GV->setThreadLocalMode(TLM); +} + /// Set the symbol visibility of type information (vtable and RTTI) /// associated with the given type. void CodeGenModule::setTypeVisibility(llvm::GlobalValue *GV, @@ -1212,13 +1251,8 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, GV->setVisibility(GetLLVMVisibility(LV.visibility())); } - GV->setThreadLocal(D->isThreadSpecified()); - - // Set the TLS model if it it's explicitly specified. - if (D->hasAttr()) { - const TLSModelAttr *Attr = D->getAttr(); - GV->setThreadLocalMode(GetLLVMTLSModel(Attr->getModel())); - } + if (D->isThreadSpecified()) + setTLSMode(GV, *D); } if (AddrSpace != Ty->getAddressSpace()) diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index a742d846c19f..d6ff50d5ad79 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -474,6 +474,10 @@ public: /// GlobalValue. void setGlobalVisibility(llvm::GlobalValue *GV, const NamedDecl *D) const; + /// setTLSMode - Set the TLS mode for the given LLVM GlobalVariable + /// for the thread-local variable declaration D. + void setTLSMode(llvm::GlobalVariable *GV, const VarDecl &D) const; + /// TypeVisibilityKind - The kind of global variable that is passed to /// setTypeVisibility enum TypeVisibilityKind { @@ -498,15 +502,6 @@ public: llvm_unreachable("unknown visibility!"); } - static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(StringRef S) { - return llvm::StringSwitch(S) - .Case("global-dynamic", llvm::GlobalVariable::GeneralDynamicTLSModel) - .Case("local-dynamic", llvm::GlobalVariable::LocalDynamicTLSModel) - .Case("initial-exec", llvm::GlobalVariable::InitialExecTLSModel) - .Case("local-exec", llvm::GlobalVariable::LocalExecTLSModel) - .Default(llvm::GlobalVariable::NotThreadLocal); - } - llvm::Constant *GetAddrOfGlobal(GlobalDecl GD) { if (isa(GD.getDecl())) return GetAddrOfCXXConstructor(cast(GD.getDecl()), diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index c3ea22f888a3..c5a837f3d1e5 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -2209,6 +2209,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_fvisibility_inlines_hidden); + Args.AddLastArg(CmdArgs, options::OPT_ftlsmodel_EQ); + // -fhosted is default. if (Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false) || KernelOrKext) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 40328d420099..4100deacbd1a 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -307,6 +307,20 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, ToArgsList &Res) { Res.push_back("-disable-llvm-verifier"); for (unsigned i = 0, e = Opts.BackendOptions.size(); i != e; ++i) Res.push_back("-backend-option", Opts.BackendOptions[i]); + + switch (Opts.DefaultTLSModel) { + case CodeGenOptions::GeneralDynamicTLSModel: + break; + case CodeGenOptions::LocalDynamicTLSModel: + Res.push_back("-ftls-model=local-dynamic"); + break; + case CodeGenOptions::InitialExecTLSModel: + Res.push_back("-ftls-model=initial-exec"); + break; + case CodeGenOptions::LocalExecTLSModel: + Res.push_back("-ftls-model=local-exec"); + break; + } } static void DependencyOutputOptsToArgs(const DependencyOutputOptions &Opts, @@ -788,7 +802,7 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) { if (Opts.AppleKext) Res.push_back("-fapple-kext"); - + if (Opts.getVisibilityMode() != DefaultVisibility) { Res.push_back("-fvisibility"); if (Opts.getVisibilityMode() == HiddenVisibility) { @@ -1256,6 +1270,22 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, } } + if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) { + StringRef Name = A->getValue(Args); + unsigned Model = llvm::StringSwitch(Name) + .Case("global-dynamic", CodeGenOptions::GeneralDynamicTLSModel) + .Case("local-dynamic", CodeGenOptions::LocalDynamicTLSModel) + .Case("initial-exec", CodeGenOptions::InitialExecTLSModel) + .Case("local-exec", CodeGenOptions::LocalExecTLSModel) + .Default(~0U); + if (Model == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.DefaultTLSModel = static_cast(Model); + } + } + return Success; } diff --git a/clang/test/CodeGen/tls-model.c b/clang/test/CodeGen/tls-model.c new file mode 100644 index 000000000000..b5bae77e97f3 --- /dev/null +++ b/clang/test/CodeGen/tls-model.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD +// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=global-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-GD +// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-dynamic -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LD +// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=initial-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-IE +// RUN: %clang_cc1 %s -triple x86_64-pc-linux-gnu -ftls-model=local-exec -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-LE + +int __thread x; +int f() { + static int __thread y; + return y++; +} +int __thread __attribute__((tls_model("initial-exec"))) z; + +// CHECK-GD: @f.y = internal thread_local global i32 0 +// CHECK-GD: @x = thread_local global i32 0 +// CHECK-GD: @z = thread_local(initialexec) global i32 0 + +// CHECK-LD: @f.y = internal thread_local(localdynamic) global i32 0 +// CHECK-LD: @x = thread_local(localdynamic) global i32 0 +// CHECK-LD: @z = thread_local(initialexec) global i32 0 + +// CHECK-IE: @f.y = internal thread_local(initialexec) global i32 0 +// CHECK-IE: @x = thread_local(initialexec) global i32 0 +// CHECK-IE: @z = thread_local(initialexec) global i32 0 + +// CHECK-LE: @f.y = internal thread_local(localexec) global i32 0 +// CHECK-LE: @x = thread_local(localexec) global i32 0 +// CHECK-LE: @z = thread_local(initialexec) global i32 0