forked from OSchip/llvm-project
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. llvm-svn: 159078
This commit is contained in:
parent
cbe34b4cc9
commit
d3b01bc7c6
|
@ -123,6 +123,20 @@ int f(bool b) {
|
|||
flag.</li>
|
||||
</ul>
|
||||
|
||||
<h4 id="tlsmodel">Support for <code>tls_model</code> attribute</h4>
|
||||
|
||||
<p>Clang now supports the <code>tls_model</code> attribute, allowing code that
|
||||
uses thread-local storage to explicitly select which model to use. The available
|
||||
models are <code>"global-dynamic"</code>, <code>"local-dynamic"</code>,
|
||||
<code>"initial-exec"</code> and <code>"local-exec"</code>. See
|
||||
<a href="http://www.akkadia.org/drepper/tls.pdf">ELF Handling For Thread-Local
|
||||
Storage</a> for more information.</p>
|
||||
|
||||
<p>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.
|
||||
</p>
|
||||
|
||||
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
||||
<h3 id="newflags">New Compiler Flags</h3>
|
||||
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
||||
|
|
|
@ -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">];
|
||||
}
|
||||
|
|
|
@ -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<
|
||||
|
|
|
@ -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<TLSModelAttr>()) {
|
||||
assert(D.isThreadSpecified() && "Can't have TLS model on non-tls var.");
|
||||
const TLSModelAttr *Attr = D.getAttr<TLSModelAttr>();
|
||||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<TLSModelAttr>()) {
|
||||
const TLSModelAttr *Attr = D->getAttr<TLSModelAttr>();
|
||||
GV->setThreadLocalMode(GetLLVMTLSModel(Attr->getModel()));
|
||||
}
|
||||
}
|
||||
|
||||
if (AddrSpace != Ty->getAddressSpace())
|
||||
|
|
|
@ -496,6 +496,15 @@ public:
|
|||
llvm_unreachable("unknown visibility!");
|
||||
}
|
||||
|
||||
static llvm::GlobalVariable::ThreadLocalMode GetLLVMTLSModel(StringRef S) {
|
||||
return llvm::StringSwitch<llvm::GlobalVariable::ThreadLocalMode>(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<CXXConstructorDecl>(GD.getDecl()))
|
||||
return GetAddrOfCXXConstructor(cast<CXXConstructorDecl>(GD.getDecl()),
|
||||
|
|
|
@ -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<StringLiteral>(Arg);
|
||||
|
||||
// Check that it is a string.
|
||||
if (!Str) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_not_string) << "tls_model";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isa<VarDecl>(D) || !cast<VarDecl>(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:
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -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"}}
|
Loading…
Reference in New Issue