From: Hans Wennborg Date: Sat, 23 Jun 2012 11:51:46 +0000 (+0000) Subject: Support the tls_model attribute (PR9788) X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5e2d5dec7736f6f9292d4212dec67295909f1328;p=clang Support the tls_model attribute (PR9788) This adds support for the tls_model attribute. This allows the user to choose a TLS model that is better than what LLVM would select by default. For example, a variable might be declared as: __thread int x __attribute__((tls_model("initial-exec"))); if it will not be used in a shared library that is dlopen'ed. This depends on LLVM r159077. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159078 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 294377318d..7bca3bf218 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -123,6 +123,20 @@ int f(bool b) { flag. +

Support for tls_model attribute

+ +

Clang now supports the tls_model attribute, allowing code that +uses thread-local storage to explicitly select which model to use. The available +models are "global-dynamic", "local-dynamic", +"initial-exec" and "local-exec". See +ELF Handling For Thread-Local + Storage for more information.

+ +

The compiler is free to choose a different model if the specified model is not +supported by the target, or if the compiler determines that a more specific +model can be used. +

+

New Compiler Flags

diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 988271ffbf..468d623eb7 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -160,6 +160,12 @@ def AlwaysInline : InheritableAttr { let Spellings = [GNU<"always_inline">]; } +def TLSModel : InheritableAttr { + let Spellings = [GNU<"tls_model">]; + let Subjects = [Var]; + let Args = [StringArgument<"Model">]; +} + def AnalyzerNoReturn : InheritableAttr { let Spellings = [GNU<"analyzer_noreturn">]; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 3a4c7feefc..32f9ba88ba 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1603,6 +1603,8 @@ def err_objc_literal_comparison : Error< "direct comparison of %select{a string literal|an array literal|" "a dictionary literal|a numeric literal|a boxed expression|}0 is not " "allowed%select{|; use -isEqual: instead}1">; +def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", " + "\"local-dynamic\", \"initial-exec\" or \"local-exec\"">; let CategoryName = "Cocoa API Issue" in { def warn_objc_redundant_literal_use : Warning< @@ -1670,13 +1672,13 @@ def warn_attribute_wrong_decl_type : Warning< "functions, methods and blocks|functions, methods, and parameters|" "classes|variables|methods|variables, functions and labels|" "fields and global variables|structs|" - "variables, functions and tag types}1">; + "variables, functions and tag types|thread-local variables}1">; def err_attribute_wrong_decl_type : Error< "%0 attribute only applies to %select{functions|unions|" "variables and functions|functions and methods|parameters|" "functions, methods and blocks|functions, methods, and parameters|" "classes|variables|methods|variables, functions and labels|" - "fields and global variables|structs}1">; + "fields and global variables|structs|thread-local variables}1">; def warn_function_attribute_wrong_type : Warning< "'%0' only applies to function types; type here is %1">; def warn_pointer_attribute_wrong_type : Warning< diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index ff803c612f..08a938254b 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -183,12 +183,22 @@ 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, - D.isThreadSpecified(), + CGM.EmitNullConstant(D.getType()), Name, 0, TLM, CGM.getContext().getTargetAddressSpace(Ty)); GV->setAlignment(getContext().getDeclAlign(&D).getQuantity()); if (Linkage != llvm::GlobalValue::InternalLinkage) @@ -239,7 +249,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D, OldGV->isConstant(), OldGV->getLinkage(), Init, "", /*InsertBefore*/ OldGV, - D.isThreadSpecified(), + OldGV->getThreadLocalMode(), CGM.getContext().getTargetAddressSpace(D.getType())); GV->setVisibility(OldGV->getVisibility()); @@ -1066,7 +1076,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), constant->getType(), true, llvm::GlobalValue::PrivateLinkage, - constant, Name, 0, false, 0); + constant, Name); GV->setAlignment(alignment.getQuantity()); GV->setUnnamedAddr(true); diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 854810b310..97512ad530 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -932,7 +932,8 @@ public: C = new llvm::GlobalVariable(CGM.getModule(), C->getType(), E->getType().isConstant(CGM.getContext()), llvm::GlobalValue::InternalLinkage, - C, ".compoundliteral", 0, false, + C, ".compoundliteral", 0, + llvm::GlobalVariable::NotThreadLocal, CGM.getContext().getTargetAddressSpace(E->getType())); return C; } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 43d573689c..1d6ddd56de 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -1187,7 +1187,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, new llvm::GlobalVariable(getModule(), Ty->getElementType(), false, llvm::GlobalValue::ExternalLinkage, 0, MangledName, 0, - false, AddrSpace); + llvm::GlobalVariable::NotThreadLocal, AddrSpace); // Handle things which are present even on external declarations. if (D) { @@ -1211,6 +1211,12 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, } 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 (AddrSpace != Ty->getAddressSpace()) diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 8f3bd78ac4..d1ecfec78a 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -496,6 +496,15 @@ 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/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index b443911daf..744d18bfd9 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -43,7 +43,8 @@ enum AttributeDeclKind { ExpectedMethod, ExpectedVariableFunctionOrLabel, ExpectedFieldOrGlobalVar, - ExpectedStruct + ExpectedStruct, + ExpectedTLSVar }; //===----------------------------------------------------------------------===// @@ -1440,6 +1441,42 @@ static void handleAlwaysInlineAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) AlwaysInlineAttr(Attr.getRange(), S.Context)); } +static void handleTLSModelAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + // Check the attribute arguments. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + return; + } + + Expr *Arg = Attr.getArg(0); + Arg = Arg->IgnoreParenCasts(); + StringLiteral *Str = dyn_cast(Arg); + + // Check that it is a string. + if (!Str) { + S.Diag(Attr.getLoc(), diag::err_attribute_not_string) << "tls_model"; + return; + } + + if (!isa(D) || !cast(D)->isThreadSpecified()) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedTLSVar; + return; + } + + // Check that the value. + StringRef Model = Str->getString(); + if (Model != "global-dynamic" && Model != "local-dynamic" + && Model != "initial-exec" && Model != "local-exec") { + S.Diag(Attr.getLoc(), diag::err_attr_tlsmodel_arg); + return; + } + + D->addAttr(::new (S.Context) TLSModelAttr(Attr.getRange(), S.Context, + Model)); +} + static void handleMallocAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. if (Attr.hasParameterOrArguments()) { @@ -3939,6 +3976,7 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, handleAlwaysInlineAttr (S, D, Attr); break; case AttributeList::AT_AnalyzerNoReturn: handleAnalyzerNoReturnAttr (S, D, Attr); break; + case AttributeList::AT_TLSModel: handleTLSModelAttr (S, D, Attr); break; case AttributeList::AT_Annotate: handleAnnotateAttr (S, D, Attr); break; case AttributeList::AT_Availability:handleAvailabilityAttr(S, D, Attr); break; case AttributeList::AT_CarriesDependency: diff --git a/test/CodeGen/thread-specifier.c b/test/CodeGen/thread-specifier.c index a1f3e16800..a2d3e62c8c 100644 --- a/test/CodeGen/thread-specifier.c +++ b/test/CodeGen/thread-specifier.c @@ -3,7 +3,13 @@ // CHECK: @b = external thread_local global // CHECK: @d.e = internal thread_local global // CHECK: @d.f = internal thread_local global +// CHECK: @f.a = internal thread_local(initialexec) global // CHECK: @a = thread_local global +// CHECK: @g = thread_local global +// CHECK: @h = thread_local(localdynamic) global +// CHECK: @i = thread_local(initialexec) global +// CHECK: @j = thread_local(localexec) global + __thread int a; extern __thread int b; int c() { return *&b; } @@ -13,3 +19,12 @@ int d() { return 0; } +__thread int g __attribute__((tls_model("global-dynamic"))); +__thread int h __attribute__((tls_model("local-dynamic"))); +__thread int i __attribute__((tls_model("initial-exec"))); +__thread int j __attribute__((tls_model("local-exec"))); + +int f() { + __thread static int a __attribute__((tls_model("initial-exec"))); + return a++; +} diff --git a/test/Sema/attr-tls_model.c b/test/Sema/attr-tls_model.c new file mode 100644 index 0000000000..46f7d9b0f4 --- /dev/null +++ b/test/Sema/attr-tls_model.c @@ -0,0 +1,14 @@ +// RUN: %clang -Xclang -verify -fsyntax-only %s + +#if !__has_attribute(tls_model) +#error "Should support tls_model attribute" +#endif + +int f() __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}} + +int x __attribute((tls_model("global-dynamic"))); // expected-error {{'tls_model' attribute only applies to thread-local variables}} +static __thread int y __attribute((tls_model("global-dynamic"))); // no-warning + +static __thread int y __attribute((tls_model("local", "dynamic"))); // expected-error {{attribute takes one argument}} +static __thread int y __attribute((tls_model(123))); // expected-error {{argument to tls_model attribute was not a string literal}} +static __thread int y __attribute((tls_model("foobar"))); // expected-error {{tls_model must be "global-dynamic", "local-dynamic", "initial-exec" or "local-exec"}}