[pr19844] Add thread local mode to aliases.

This matches gcc's behavior. It also seems natural given that aliases
contain other properties that govern how it is accessed (linkage,
visibility, dll storage).

Clang still has to be updated to expose this feature to C.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@209759 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Rafael Espindola 2014-05-28 18:15:43 +00:00
parent a5423f2598
commit 665d42accf
18 changed files with 143 additions and 111 deletions

View File

@ -464,6 +464,34 @@ DLL storage class:
exists for defining a dll interface, the compiler, assembler and linker know
it is externally referenced and must refrain from deleting the symbol.
.. _tls_model:
Thread Local Storage Models
---------------------------
A variable may be defined as ``thread_local``, which means that it will
not be shared by threads (each thread will have a separated copy of the
variable). Not all targets support thread-local variables. Optionally, a
TLS model may be specified:
``localdynamic``
For variables that are only used within the current shared library.
``initialexec``
For variables in modules that will not be loaded dynamically.
``localexec``
For variables defined in the executable and only used within it.
If no explicit model is given, the "general dynamic" model is used.
The models correspond to the ELF TLS models; see `ELF Handling For
Thread-Local Storage <http://people.redhat.com/drepper/tls.pdf>`_ for
more information on under which circumstances the different models may
be used. The target may choose a different TLS model if the specified
model is not supported, or if a better choice of model can be made.
A model can also be specified in a alias, but then it only governs how
the alias is accessed. It will not have any effect in the aliasee.
.. _namedtypes:
Structure Types
@ -497,24 +525,6 @@ to be placed in, and may have an optional explicit alignment specified.
Global variables in other translation units can also be declared, in which
case they don't have an initializer.
A variable may be defined as ``thread_local``, which means that it will
not be shared by threads (each thread will have a separated copy of the
variable). Not all targets support thread-local variables. Optionally, a
TLS model may be specified:
``localdynamic``
For variables that are only used within the current shared library.
``initialexec``
For variables in modules that will not be loaded dynamically.
``localexec``
For variables defined in the executable and only used within it.
The models correspond to the ELF TLS models; see `ELF Handling For
Thread-Local Storage <http://people.redhat.com/drepper/tls.pdf>`_ for
more information on under which circumstances the different models may
be used. The target may choose a different TLS model if the specified
model is not supported, or if a better choice of model can be made.
A variable may be defined as a global ``constant``, which indicates that
the contents of the variable will **never** be modified (enabling better
optimization, allowing the global data to be placed in the read-only
@ -572,6 +582,9 @@ iteration.
Globals can also have a :ref:`DLL storage class <dllstorageclass>`.
Variables and aliasaes can have a
:ref:`Thread Local Storage Model <tls_model>`.
Syntax::
[@<GlobalVarName> =] [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal]
@ -674,7 +687,7 @@ Aliases may have an optional :ref:`linkage type <linkage>`, an optional
Syntax::
@<Name> = [Visibility] [DLLStorageClass] alias [Linkage] <AliaseeTy> @<Aliasee>
@<Name> = [Visibility] [DLLStorageClass] [ThreadLocal] alias [Linkage] <AliaseeTy> @<Aliasee>
The linkage must be one of ``private``, ``internal``, ``linkonce``, ``weak``,
``linkonce_odr``, ``weak_odr``, ``external``. Note that some system linkers

View File

@ -63,7 +63,8 @@ protected:
LinkageTypes Linkage, const Twine &Name)
: Constant(Ty, VTy, Ops, NumOps), Linkage(Linkage),
Visibility(DefaultVisibility), UnnamedAddr(0),
DllStorageClass(DefaultStorageClass), Parent(nullptr) {
DllStorageClass(DefaultStorageClass),
ThreadLocal(NotThreadLocal), Parent(nullptr) {
setName(Name);
}
@ -74,21 +75,32 @@ protected:
unsigned UnnamedAddr : 1; // This value's address is not significant
unsigned DllStorageClass : 2; // DLL storage class
unsigned ThreadLocal : 3; // Is this symbol "Thread Local", if so, what is
// the desired model?
private:
// Give subclasses access to what otherwise would be wasted padding.
// (22 + 2 + 1 + 2 + 5) == 32.
unsigned SubClassData : 22;
// (19 + 3 + 2 + 1 + 2 + 5) == 32.
unsigned SubClassData : 19;
protected:
unsigned getGlobalValueSubClassData() const {
return SubClassData;
}
void setGlobalValueSubClassData(unsigned V) {
assert(V < (1 << 22) && "It will not fit");
assert(V < (1 << 19) && "It will not fit");
SubClassData = V;
}
Module *Parent; // The containing module.
public:
enum ThreadLocalMode {
NotThreadLocal = 0,
GeneralDynamicTLSModel,
LocalDynamicTLSModel,
InitialExecTLSModel,
LocalExecTLSModel
};
~GlobalValue() {
removeDeadConstantUsers(); // remove any dead constants using this.
}
@ -110,6 +122,19 @@ public:
Visibility = V;
}
/// If the value is "Thread Local", its value isn't shared by the threads.
bool isThreadLocal() const { return getThreadLocalMode() != NotThreadLocal; }
void setThreadLocal(bool Val) {
setThreadLocalMode(Val ? GeneralDynamicTLSModel : NotThreadLocal);
}
void setThreadLocalMode(ThreadLocalMode Val) {
assert(Val == NotThreadLocal || getValueID() != Value::FunctionVal);
ThreadLocal = Val;
}
ThreadLocalMode getThreadLocalMode() const {
return static_cast<ThreadLocalMode>(ThreadLocal);
}
DLLStorageClassTypes getDLLStorageClass() const {
return DLLStorageClassTypes(DllStorageClass);
}

View File

@ -41,9 +41,6 @@ class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {
void setParent(Module *parent);
bool isConstantGlobal : 1; // Is this a global constant?
unsigned threadLocalMode : 3; // Is this symbol "Thread Local",
// if so, what is the desired
// model?
bool isExternallyInitializedConstant : 1; // Is this a global whose value
// can change from its initial
// value before global
@ -55,14 +52,6 @@ public:
return User::operator new(s, 1);
}
enum ThreadLocalMode {
NotThreadLocal = 0,
GeneralDynamicTLSModel,
LocalDynamicTLSModel,
InitialExecTLSModel,
LocalExecTLSModel
};
/// GlobalVariable ctor - If a parent module is specified, the global is
/// automatically inserted into the end of the specified modules global list.
GlobalVariable(Type *Ty, bool isConstant, LinkageTypes Linkage,
@ -155,16 +144,6 @@ public:
bool isConstant() const { return isConstantGlobal; }
void setConstant(bool Val) { isConstantGlobal = Val; }
/// If the value is "Thread Local", its value isn't shared by the threads.
bool isThreadLocal() const { return threadLocalMode != NotThreadLocal; }
void setThreadLocal(bool Val) {
threadLocalMode = Val ? GeneralDynamicTLSModel : NotThreadLocal;
}
void setThreadLocalMode(ThreadLocalMode Val) { threadLocalMode = Val; }
ThreadLocalMode getThreadLocalMode() const {
return static_cast<ThreadLocalMode>(threadLocalMode);
}
bool isExternallyInitialized() const {
return isExternallyInitializedConstant;
}

View File

@ -259,10 +259,13 @@ bool LLParser::ParseTopLevelEntities() {
case lltok::kw_extern_weak: // OptionalLinkage
case lltok::kw_external: { // OptionalLinkage
unsigned Linkage, Visibility, DLLStorageClass;
GlobalVariable::ThreadLocalMode TLM;
if (ParseOptionalLinkage(Linkage) ||
ParseOptionalVisibility(Visibility) ||
ParseOptionalDLLStorageClass(DLLStorageClass) ||
ParseGlobal("", SMLoc(), Linkage, true, Visibility, DLLStorageClass))
ParseOptionalThreadLocal(TLM) ||
ParseGlobal("", SMLoc(), Linkage, true, Visibility, DLLStorageClass,
TLM))
return true;
break;
}
@ -270,18 +273,28 @@ bool LLParser::ParseTopLevelEntities() {
case lltok::kw_hidden: // OptionalVisibility
case lltok::kw_protected: { // OptionalVisibility
unsigned Visibility, DLLStorageClass;
GlobalVariable::ThreadLocalMode TLM;
if (ParseOptionalVisibility(Visibility) ||
ParseOptionalDLLStorageClass(DLLStorageClass) ||
ParseGlobal("", SMLoc(), 0, false, Visibility, DLLStorageClass))
ParseOptionalThreadLocal(TLM) ||
ParseGlobal("", SMLoc(), 0, false, Visibility, DLLStorageClass, TLM))
return true;
break;
}
case lltok::kw_thread_local: { // OptionalThreadLocal
GlobalVariable::ThreadLocalMode TLM;
if (ParseOptionalThreadLocal(TLM) ||
ParseGlobal("", SMLoc(), 0, false, 0, 0, TLM))
return true;
break;
}
case lltok::kw_thread_local: // OptionalThreadLocal
case lltok::kw_addrspace: // OptionalAddrSpace
case lltok::kw_constant: // GlobalType
case lltok::kw_global: // GlobalType
if (ParseGlobal("", SMLoc(), 0, false, 0, 0)) return true;
if (ParseGlobal("", SMLoc(), 0, false, 0, 0, GlobalValue::NotThreadLocal))
return true;
break;
case lltok::kw_attributes: if (ParseUnnamedAttrGrp()) return true; break;
@ -470,15 +483,17 @@ bool LLParser::ParseUnnamedGlobal() {
bool HasLinkage;
unsigned Linkage, Visibility, DLLStorageClass;
GlobalVariable::ThreadLocalMode TLM;
if (ParseOptionalLinkage(Linkage, HasLinkage) ||
ParseOptionalVisibility(Visibility) ||
ParseOptionalDLLStorageClass(DLLStorageClass))
ParseOptionalDLLStorageClass(DLLStorageClass) ||
ParseOptionalThreadLocal(TLM))
return true;
if (HasLinkage || Lex.getKind() != lltok::kw_alias)
return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility,
DLLStorageClass);
return ParseAlias(Name, NameLoc, Visibility, DLLStorageClass);
DLLStorageClass, TLM);
return ParseAlias(Name, NameLoc, Visibility, DLLStorageClass, TLM);
}
/// ParseNamedGlobal:
@ -493,16 +508,18 @@ bool LLParser::ParseNamedGlobal() {
bool HasLinkage;
unsigned Linkage, Visibility, DLLStorageClass;
GlobalVariable::ThreadLocalMode TLM;
if (ParseToken(lltok::equal, "expected '=' in global variable") ||
ParseOptionalLinkage(Linkage, HasLinkage) ||
ParseOptionalVisibility(Visibility) ||
ParseOptionalDLLStorageClass(DLLStorageClass))
ParseOptionalDLLStorageClass(DLLStorageClass) ||
ParseOptionalThreadLocal(TLM))
return true;
if (HasLinkage || Lex.getKind() != lltok::kw_alias)
return ParseGlobal(Name, NameLoc, Linkage, HasLinkage, Visibility,
DLLStorageClass);
return ParseAlias(Name, NameLoc, Visibility, DLLStorageClass);
DLLStorageClass, TLM);
return ParseAlias(Name, NameLoc, Visibility, DLLStorageClass, TLM);
}
// MDString:
@ -639,7 +656,8 @@ static bool isValidVisibilityForLinkage(unsigned V, unsigned L) {
/// Everything through DLL storage class has already been parsed.
///
bool LLParser::ParseAlias(const std::string &Name, LocTy NameLoc,
unsigned Visibility, unsigned DLLStorageClass) {
unsigned Visibility, unsigned DLLStorageClass,
GlobalVariable::ThreadLocalMode TLM) {
assert(Lex.getKind() == lltok::kw_alias);
Lex.Lex();
LocTy LinkageLoc = Lex.getLoc();
@ -699,6 +717,7 @@ bool LLParser::ParseAlias(const std::string &Name, LocTy NameLoc,
std::unique_ptr<GlobalAlias> GA(
GlobalAlias::create(Ty, AddrSpace, (GlobalValue::LinkageTypes)Linkage,
Name, Aliasee, /*Parent*/ nullptr));
GA->setThreadLocalMode(TLM);
GA->setVisibility((GlobalValue::VisibilityTypes)Visibility);
GA->setDLLStorageClass((GlobalValue::DLLStorageClassTypes)DLLStorageClass);
@ -753,21 +772,20 @@ bool LLParser::ParseAlias(const std::string &Name, LocTy NameLoc,
///
bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
unsigned Linkage, bool HasLinkage,
unsigned Visibility, unsigned DLLStorageClass) {
unsigned Visibility, unsigned DLLStorageClass,
GlobalVariable::ThreadLocalMode TLM) {
if (!isValidVisibilityForLinkage(Visibility, Linkage))
return Error(NameLoc,
"symbol with local linkage must have default visibility");
unsigned AddrSpace;
bool IsConstant, UnnamedAddr, IsExternallyInitialized;
GlobalVariable::ThreadLocalMode TLM;
LocTy UnnamedAddrLoc;
LocTy IsExternallyInitializedLoc;
LocTy TyLoc;
Type *Ty = nullptr;
if (ParseOptionalThreadLocal(TLM) ||
ParseOptionalAddrSpace(AddrSpace) ||
if (ParseOptionalAddrSpace(AddrSpace) ||
ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr,
&UnnamedAddrLoc) ||
ParseOptionalToken(lltok::kw_externally_initialized,

View File

@ -239,9 +239,11 @@ namespace llvm {
bool ParseNamedGlobal();
bool ParseGlobal(const std::string &Name, LocTy Loc, unsigned Linkage,
bool HasLinkage, unsigned Visibility,
unsigned DLLStorageClass);
unsigned DLLStorageClass,
GlobalVariable::ThreadLocalMode TLM);
bool ParseAlias(const std::string &Name, LocTy Loc, unsigned Visibility,
unsigned DLLStorageClass);
unsigned DLLStorageClass,
GlobalVariable::ThreadLocalMode TLM);
bool ParseStandaloneMetadata();
bool ParseNamedMetadata();
bool ParseMDString(MDString *&Result);

View File

@ -2017,6 +2017,8 @@ error_code BitcodeReader::ParseModule(bool Resume) {
NewGA->setDLLStorageClass(GetDecodedDLLStorageClass(Record[4]));
else
UpgradeDLLImportExportLinkage(NewGA, Record[2]);
if (Record.size() > 5)
NewGA->setThreadLocalMode(GetDecodedThreadLocalMode(Record[5]));
ValueList.push_back(NewGA);
AliasInits.push_back(std::make_pair(NewGA, Record[1]));
break;

View File

@ -511,7 +511,7 @@ static unsigned getEncodedDLLStorageClass(const GlobalValue &GV) {
llvm_unreachable("Invalid DLL storage class");
}
static unsigned getEncodedThreadLocalMode(const GlobalVariable &GV) {
static unsigned getEncodedThreadLocalMode(const GlobalValue &GV) {
switch (GV.getThreadLocalMode()) {
case GlobalVariable::NotThreadLocal: return 0;
case GlobalVariable::GeneralDynamicTLSModel: return 1;
@ -668,6 +668,8 @@ static void WriteModuleInfo(const Module *M, const ValueEnumerator &VE,
Vals.push_back(getEncodedLinkage(A));
Vals.push_back(getEncodedVisibility(A));
Vals.push_back(getEncodedDLLStorageClass(A));
if (A.isThreadLocal())
Vals.push_back(getEncodedThreadLocalMode(A));
unsigned AbbrevToUse = 0;
Stream.EmitRecord(bitc::MODULE_CODE_ALIAS, Vals, AbbrevToUse);
Vals.clear();

View File

@ -1190,15 +1190,8 @@ SDValue SelectionDAG::getGlobalAddress(const GlobalValue *GV, SDLoc DL,
if (BitWidth < 64)
Offset = SignExtend64(Offset, BitWidth);
const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV);
if (!GVar) {
// If GV is an alias then use the aliasee for determining thread-localness.
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV))
GVar = dyn_cast_or_null<GlobalVariable>(GA->getAliasee());
}
unsigned Opc;
if (GVar && GVar->isThreadLocal())
if (GV->isThreadLocal())
Opc = isTargetGA ? ISD::TargetGlobalTLSAddress : ISD::GlobalTLSAddress;
else
Opc = isTargetGA ? ISD::TargetGlobalAddress : ISD::GlobalAddress;

View File

@ -1488,6 +1488,7 @@ void AssemblyWriter::printAlias(const GlobalAlias *GA) {
}
PrintVisibility(GA->getVisibility(), Out);
PrintDLLStorageClass(GA->getDLLStorageClass(), Out);
PrintThreadLocalModel(GA->getThreadLocalMode(), Out);
Out << "alias ";

View File

@ -113,8 +113,9 @@ GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
: GlobalObject(PointerType::get(Ty, AddressSpace), Value::GlobalVariableVal,
OperandTraits<GlobalVariable>::op_begin(this),
InitVal != nullptr, Link, Name),
isConstantGlobal(constant), threadLocalMode(TLMode),
isConstantGlobal(constant),
isExternallyInitializedConstant(isExternallyInitialized) {
setThreadLocalMode(TLMode);
if (InitVal) {
assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!");
@ -132,8 +133,9 @@ GlobalVariable::GlobalVariable(Module &M, Type *Ty, bool constant,
: GlobalObject(PointerType::get(Ty, AddressSpace), Value::GlobalVariableVal,
OperandTraits<GlobalVariable>::op_begin(this),
InitVal != nullptr, Link, Name),
isConstantGlobal(constant), threadLocalMode(TLMode),
isConstantGlobal(constant),
isExternallyInitializedConstant(isExternallyInitialized) {
setThreadLocalMode(TLMode);
if (InitVal) {
assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!");

View File

@ -240,21 +240,15 @@ unsigned AArch64FastISel::AArch64MaterializeFP(const ConstantFP *CFP, MVT VT) {
}
unsigned AArch64FastISel::AArch64MaterializeGV(const GlobalValue *GV) {
// We can't handle thread-local variables quickly yet. Unfortunately we have
// to peer through any aliases to find out if that rule applies.
const GlobalValue *TLSGV = GV;
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV))
TLSGV = GA->getAliasee();
// We can't handle thread-local variables quickly yet.
if (GV->isThreadLocal())
return 0;
// MachO still uses GOT for large code-model accesses, but ELF requires
// movz/movk sequences, which FastISel doesn't handle yet.
if (TM.getCodeModel() != CodeModel::Small && !Subtarget->isTargetMachO())
return 0;
if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(TLSGV))
if (GVar->isThreadLocal())
return 0;
unsigned char OpFlags = Subtarget->ClassifyGlobalReference(GV, TM);
EVT DestEVT = TLI.getValueType(GV->getType(), true);

View File

@ -1859,15 +1859,9 @@ unsigned PPCFastISel::PPCMaterializeGV(const GlobalValue *GV, MVT VT) {
// handle switches; if that changes, we need them as well. For now,
// what follows assumes everything's a generic (or TLS) global address.
const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV);
if (!GVar) {
// If GV is an alias, use the aliasee for determining thread-locality.
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV))
GVar = dyn_cast_or_null<GlobalVariable>(GA->getAliasee());
}
// FIXME: We don't yet handle the complexity of TLS.
bool IsTLS = GVar && GVar->isThreadLocal();
if (IsTLS)
if (GV->isThreadLocal())
return 0;
// For small code model, generate a simple TOC load.

View File

@ -88,8 +88,8 @@ CodeModel::Model TargetMachine::getCodeModel() const {
}
/// Get the IR-specified TLS model for Var.
static TLSModel::Model getSelectedTLSModel(const GlobalVariable *Var) {
switch (Var->getThreadLocalMode()) {
static TLSModel::Model getSelectedTLSModel(const GlobalValue *GV) {
switch (GV->getThreadLocalMode()) {
case GlobalVariable::NotThreadLocal:
llvm_unreachable("getSelectedTLSModel for non-TLS variable");
break;
@ -127,13 +127,10 @@ TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
Model = TLSModel::InitialExec;
}
const GlobalVariable *Var = dyn_cast<GlobalVariable>(GV);
if (Var) {
// If the user specified a more specific model, use that.
TLSModel::Model SelectedModel = getSelectedTLSModel(Var);
if (SelectedModel > Model)
return SelectedModel;
}
// If the user specified a more specific model, use that.
TLSModel::Model SelectedModel = getSelectedTLSModel(GV);
if (SelectedModel > Model)
return SelectedModel;
return Model;
}

View File

@ -355,17 +355,8 @@ bool X86FastISel::handleConstantAddresses(const Value *V, X86AddressMode &AM) {
return false;
// Can't handle TLS yet.
if (const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV))
if (GVar->isThreadLocal())
return false;
// Can't handle TLS yet, part 2 (this is slightly crazy, but this is how
// it works...).
if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV))
if (const GlobalVariable *GVar =
dyn_cast_or_null<GlobalVariable>(GA->getAliasee()))
if (GVar->isThreadLocal())
return false;
if (GV->isThreadLocal())
return false;
// RIP-relative addresses can't have additional register operands, so if
// we've already folded stuff into the addressing mode, just force the

View File

@ -1,7 +1,7 @@
; RUN: llc -march=mipsel -relocation-model=pic -disable-mips-delay-filler < %s | FileCheck %s
@foo = thread_local global i32 42
@bar = hidden alias i32* @foo
@bar = hidden thread_local alias i32* @foo
define i32* @zed() {
; CHECK-DAG: __tls_get_addr

View File

@ -8,7 +8,7 @@ target triple = "i386-pc-linux-gnu"
@__resp = thread_local global %struct.__res_state* @_res ; <%struct.__res_state**> [#uses=1]
@_res = global %struct.__res_state zeroinitializer, section ".bss" ; <%struct.__res_state*> [#uses=1]
@__libc_resp = hidden alias %struct.__res_state** @__resp ; <%struct.__res_state**> [#uses=2]
@__libc_resp = hidden thread_local alias %struct.__res_state** @__resp ; <%struct.__res_state**> [#uses=2]
define i32 @foo() {
; CHECK-LABEL: foo:

View File

@ -1,4 +1,20 @@
; RUN: llc < %s -mtriple=i686-pc-linux-gnu -asm-verbose=false | FileCheck %s
; RUN: llc < %s -mtriple=i686-pc-linux-gnu -asm-verbose=false \
; RUN: -relocation-model=pic | FileCheck %s
@thread_var = thread_local global i32 42, align 4
@thread_alias = thread_local(localdynamic) alias i32* @thread_var
; CHECK-LABEL: get_thread_var
define i32* @get_thread_var() {
; CHECK: leal thread_var@TLSGD
ret i32* @thread_var
}
; CHECK-LABEL: get_thread_alias
define i32* @get_thread_alias() {
; CHECK: leal thread_alias@TLSLD
ret i32* @thread_alias
}
@bar = global i32 42

View File

@ -17,3 +17,6 @@
@v6 = alias i16, i32* @v1
; CHECK: @v6 = alias i16, i32* @v1
@v7 = thread_local(localdynamic) alias i32* @v1
; CHECK: @v7 = thread_local(localdynamic) alias i32* @v1