From 47cbc4e0ee6098b7be3c60108000a979f1809949 Mon Sep 17 00:00:00 2001
From: Stepan Dyatkovskiy
Date: Sat, 23 Jun 2012 10:58:58 +0000
Subject: Optimized usage of new SwitchInst case values (IntegersSubset type)
in Local.cpp, Execution.cpp and BitcodeWriter.cpp. I got about 1% of
compile-time improvement on my machines (Ubuntu 11.10 i386 and Ubuntu 12.04
x64).
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159076 91177308-0d34-0410-b5e6-96231b3b80d8
---
lib/Bitcode/Writer/BitcodeWriter.cpp | 43 ++++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 12 deletions(-)
(limited to 'lib/Bitcode/Writer/BitcodeWriter.cpp')
diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp
index 6526b012d8..333d82ad4a 100644
--- a/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1157,19 +1157,38 @@ static void WriteInstruction(const Instruction &I, unsigned InstID,
Vals64.push_back(SI.getNumCases());
for (SwitchInst::CaseIt i = SI.case_begin(), e = SI.case_end();
i != e; ++i) {
- IntegersSubset CaseRanges = i.getCaseValueEx();
- Vals64.push_back(CaseRanges.getNumItems());
- for (unsigned ri = 0, rn = CaseRanges.getNumItems(); ri != rn; ++ri) {
- IntegersSubset::Range r = CaseRanges.getItem(ri);
- bool IsSingleNumber = r.isSingleNumber();
-
- Vals64.push_back(IsSingleNumber);
-
- unsigned Code, Abbrev; // will unused.
+ IntegersSubset& CaseRanges = i.getCaseValueEx();
+ unsigned Code, Abbrev; // will unused.
+
+ if (CaseRanges.isSingleNumber()) {
+ Vals64.push_back(1/*NumItems = 1*/);
+ Vals64.push_back(true/*IsSingleNumber = true*/);
+ EmitAPInt(Vals64, Code, Abbrev, CaseRanges.getSingleNumber(0), true);
+ } else {
+
+ Vals64.push_back(CaseRanges.getNumItems());
- EmitAPInt(Vals64, Code, Abbrev, r.getLow(), true);
- if (!IsSingleNumber)
- EmitAPInt(Vals64, Code, Abbrev, r.getHigh(), true);
+ if (CaseRanges.isSingleNumbersOnly()) {
+ for (unsigned ri = 0, rn = CaseRanges.getNumItems();
+ ri != rn; ++ri) {
+
+ Vals64.push_back(true/*IsSingleNumber = true*/);
+
+ EmitAPInt(Vals64, Code, Abbrev,
+ CaseRanges.getSingleNumber(ri), true);
+ }
+ } else
+ for (unsigned ri = 0, rn = CaseRanges.getNumItems();
+ ri != rn; ++ri) {
+ IntegersSubset::Range r = CaseRanges.getItem(ri);
+ bool IsSingleNumber = CaseRanges.isSingleNumber(ri);
+
+ Vals64.push_back(IsSingleNumber);
+
+ EmitAPInt(Vals64, Code, Abbrev, r.getLow(), true);
+ if (!IsSingleNumber)
+ EmitAPInt(Vals64, Code, Abbrev, r.getHigh(), true);
+ }
}
Vals64.push_back(VE.getValueID(i.getCaseSuccessor()));
}
--
cgit v1.2.3-70-g09d2
From ce718ff9f42c7da092eaa01dd0242e8d5ba84713 Mon Sep 17 00:00:00 2001
From: Hans Wennborg
Date: Sat, 23 Jun 2012 11:37:03 +0000
Subject: Extend the IL for selecting TLS models (PR9788)
This allows the user/front-end to specify a model that is better
than what LLVM would choose by default. For example, a variable
might be declared as
@x = thread_local(initialexec) global i32 42
if it will not be used in a shared library that is dlopen'ed.
If the specified model isn't supported by the target, or if LLVM can
make a better choice, a different model may be used.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159077 91177308-0d34-0410-b5e6-96231b3b80d8
---
docs/BitCodeFormat.html | 14 +-
docs/LangRef.html | 34 ++++-
include/llvm/GlobalVariable.h | 41 ++++-
lib/AsmParser/LLLexer.cpp | 3 +
lib/AsmParser/LLParser.cpp | 50 ++++++-
lib/AsmParser/LLParser.h | 3 +
lib/AsmParser/LLToken.h | 3 +-
lib/Bitcode/Reader/BitcodeReader.cpp | 19 ++-
lib/Bitcode/Writer/BitcodeWriter.cpp | 13 +-
lib/Linker/LinkModules.cpp | 4 +-
lib/Target/CppBackend/CPPBackend.cpp | 25 +++-
lib/Target/TargetMachine.cpp | 40 ++++-
lib/Transforms/IPO/GlobalOpt.cpp | 14 +-
.../Instrumentation/AddressSanitizer.cpp | 2 +-
lib/Transforms/Instrumentation/GCOVProfiling.cpp | 2 +-
lib/Transforms/Utils/CloneModule.cpp | 2 +-
lib/VMCore/AsmWriter.cpp | 22 ++-
lib/VMCore/Core.cpp | 2 +-
lib/VMCore/Globals.cpp | 51 ++++++-
lib/VMCore/IRBuilder.cpp | 2 +-
test/Assembler/tls-models.ll | 11 ++
test/CodeGen/ARM/tls-models.ll | 117 +++++++++++++++
test/CodeGen/Mips/tls-models.ll | 113 ++++++++++++++
test/CodeGen/X86/tls-models.ll | 166 +++++++++++++++++++++
24 files changed, 706 insertions(+), 47 deletions(-)
create mode 100644 test/Assembler/tls-models.ll
create mode 100644 test/CodeGen/ARM/tls-models.ll
create mode 100644 test/CodeGen/Mips/tls-models.ll
create mode 100644 test/CodeGen/X86/tls-models.ll
(limited to 'lib/Bitcode/Writer/BitcodeWriter.cpp')
diff --git a/docs/BitCodeFormat.html b/docs/BitCodeFormat.html
index 30145de581..6a670f56b9 100644
--- a/docs/BitCodeFormat.html
+++ b/docs/BitCodeFormat.html
@@ -864,7 +864,7 @@ library name referenced.
-
[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal]
+
[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr]
The GLOBALVAR record (code 7) marks the declaration or
definition of a global variable. The operand fields are:
@@ -915,8 +915,16 @@ encoding of the visibility of this variable:
-
threadlocal: If present and non-zero, indicates that the variable
-is thread_local
+
threadlocal: If present, an encoding of the thread local storage
+mode of the variable:
+
+ - not thread local: code 0
+ - thread local; default TLS model: code 1
+ - localdynamic: code 2
+ - initialexec: code 3
+ - localexec: code 4
+
+
unnamed_addr: If present and non-zero, indicates that the variable
has unnamed_addr
diff --git a/docs/LangRef.html b/docs/LangRef.html
index 9cb7e6392a..ba653dbd49 100644
--- a/docs/LangRef.html
+++ b/docs/LangRef.html
@@ -838,9 +838,32 @@ define i32 @main() {
; i32()*
Global variables define regions of memory allocated at compilation time
instead of run-time. Global variables may optionally be initialized, may
have an explicit section to be placed in, and may have an optional explicit
- alignment specified. A variable may be defined as "thread_local", which
+ alignment specified.
+
+
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). A variable may be defined as a global
+ 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 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 section of an executable, etc).
@@ -893,6 +916,13 @@ define i32 @main() { ; i32()*
@G = addrspace(5) constant float 1.0, section "foo", align 4
+
The following example defines a thread-local global with
+ the initialexec TLS model:
+
+
+@G = thread_local(initialexec) global i32 0, align 4
+
+
diff --git a/include/llvm/GlobalVariable.h b/include/llvm/GlobalVariable.h
index 034ade1fb0..fbb708b26b 100644
--- a/include/llvm/GlobalVariable.h
+++ b/include/llvm/GlobalVariable.h
@@ -41,24 +41,47 @@ class GlobalVariable : public GlobalValue, public ilist_node {
void setParent(Module *parent);
bool isConstantGlobal : 1; // Is this a global constant?
- bool isThreadLocalSymbol : 1; // Is this symbol "Thread Local"?
+ unsigned threadLocalMode : 3; // Is this symbol "Thread Local",
+ // if so, what is the desired model?
public:
// allocate space for exactly one operand
void *operator new(size_t s) {
return User::operator new(s, 1);
}
- /// GlobalVariable ctor - If a parent module is specified, the global is
- /// automatically inserted into the end of the specified modules global list.
+
+ enum ThreadLocalMode {
+ NotThreadLocal = 0,
+ GeneralDynamicTLSModel,
+ LocalDynamicTLSModel,
+ InitialExecTLSModel,
+ LocalExecTLSModel
+ };
+
+ // TODO: Remove these once Clang is updated.
GlobalVariable(Type *Ty, bool isConstant, LinkageTypes Linkage,
Constant *Initializer = 0, const Twine &Name = "",
bool ThreadLocal = false, unsigned AddressSpace = 0);
+ GlobalVariable(Module &M, Type *Ty, bool isConstant,
+ LinkageTypes Linkage, Constant *Initializer,
+ const Twine &Name = "",
+ GlobalVariable *InsertBefore = 0, bool ThreadLocal = false,
+ unsigned AddressSpace = 0);
+
+ /// GlobalVariable ctor - If a parent module is specified, the global is
+ /// automatically inserted into the end of the specified modules global list.
+ // TODO: Put default param values back when ctors above are removed.
+ GlobalVariable(Type *Ty, bool isConstant, LinkageTypes Linkage,
+ Constant *Initializer, const Twine &Name,
+ ThreadLocalMode, unsigned AddressSpace = 0);
/// GlobalVariable ctor - This creates a global and inserts it before the
/// specified other global.
+ // TODO: Put default param values back when ctors above are removed.
GlobalVariable(Module &M, Type *Ty, bool isConstant,
LinkageTypes Linkage, Constant *Initializer,
const Twine &Name,
- GlobalVariable *InsertBefore = 0, bool ThreadLocal = false,
+ GlobalVariable *InsertBefore,
+ ThreadLocalMode,
unsigned AddressSpace = 0);
~GlobalVariable() {
@@ -135,8 +158,14 @@ public:
void setConstant(bool Val) { isConstantGlobal = Val; }
/// If the value is "Thread Local", its value isn't shared by the threads.
- bool isThreadLocal() const { return isThreadLocalSymbol; }
- void setThreadLocal(bool Val) { isThreadLocalSymbol = Val; }
+ 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);
+ }
/// copyAttributesFrom - copy all additional attributes (those not needed to
/// create a GlobalVariable) from the GlobalVariable Src to this one.
diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp
index dabcbaa9e8..670c1bbe98 100644
--- a/lib/AsmParser/LLLexer.cpp
+++ b/lib/AsmParser/LLLexer.cpp
@@ -474,6 +474,9 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(extern_weak);
KEYWORD(external);
KEYWORD(thread_local);
+ KEYWORD(localdynamic);
+ KEYWORD(initialexec);
+ KEYWORD(localexec);
KEYWORD(zeroinitializer);
KEYWORD(undef);
KEYWORD(null);
diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp
index fe415615d8..095b7c5f67 100644
--- a/lib/AsmParser/LLParser.cpp
+++ b/lib/AsmParser/LLParser.cpp
@@ -645,12 +645,13 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
unsigned Linkage, bool HasLinkage,
unsigned Visibility) {
unsigned AddrSpace;
- bool ThreadLocal, IsConstant, UnnamedAddr;
+ bool IsConstant, UnnamedAddr;
+ GlobalVariable::ThreadLocalMode TLM;
LocTy UnnamedAddrLoc;
LocTy TyLoc;
Type *Ty = 0;
- if (ParseOptionalToken(lltok::kw_thread_local, ThreadLocal) ||
+ if (ParseOptionalThreadLocal(TLM) ||
ParseOptionalAddrSpace(AddrSpace) ||
ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr,
&UnnamedAddrLoc) ||
@@ -691,7 +692,8 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
if (GV == 0) {
GV = new GlobalVariable(*M, Ty, false, GlobalValue::ExternalLinkage, 0,
- Name, 0, false, AddrSpace);
+ Name, 0, GlobalVariable::NotThreadLocal,
+ AddrSpace);
} else {
if (GV->getType()->getElementType() != Ty)
return Error(TyLoc,
@@ -710,7 +712,7 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
GV->setConstant(IsConstant);
GV->setLinkage((GlobalValue::LinkageTypes)Linkage);
GV->setVisibility((GlobalValue::VisibilityTypes)Visibility);
- GV->setThreadLocal(ThreadLocal);
+ GV->setThreadLocalMode(TLM);
GV->setUnnamedAddr(UnnamedAddr);
// Parse attributes on the global.
@@ -858,6 +860,46 @@ bool LLParser::ParseUInt32(unsigned &Val) {
return false;
}
+/// ParseTLSModel
+/// := 'localdynamic'
+/// := 'initialexec'
+/// := 'localexec'
+bool LLParser::ParseTLSModel(GlobalVariable::ThreadLocalMode &TLM) {
+ switch (Lex.getKind()) {
+ default:
+ return TokError("expected localdynamic, initialexec or localexec");
+ case lltok::kw_localdynamic:
+ TLM = GlobalVariable::LocalDynamicTLSModel;
+ break;
+ case lltok::kw_initialexec:
+ TLM = GlobalVariable::InitialExecTLSModel;
+ break;
+ case lltok::kw_localexec:
+ TLM = GlobalVariable::LocalExecTLSModel;
+ break;
+ }
+
+ Lex.Lex();
+ return false;
+}
+
+/// ParseOptionalThreadLocal
+/// := /*empty*/
+/// := 'thread_local'
+/// := 'thread_local' '(' tlsmodel ')'
+bool LLParser::ParseOptionalThreadLocal(GlobalVariable::ThreadLocalMode &TLM) {
+ TLM = GlobalVariable::NotThreadLocal;
+ if (!EatIfPresent(lltok::kw_thread_local))
+ return false;
+
+ TLM = GlobalVariable::GeneralDynamicTLSModel;
+ if (Lex.getKind() == lltok::lparen) {
+ Lex.Lex();
+ return ParseTLSModel(TLM) ||
+ ParseToken(lltok::rparen, "expected ')' after thread local model");
+ }
+ return false;
+}
/// ParseOptionalAddrSpace
/// := /*empty*/
diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h
index dda8808381..257c726229 100644
--- a/lib/AsmParser/LLParser.h
+++ b/lib/AsmParser/LLParser.h
@@ -171,6 +171,9 @@ namespace llvm {
Loc = Lex.getLoc();
return ParseUInt32(Val);
}
+
+ bool ParseTLSModel(GlobalVariable::ThreadLocalMode &TLM);
+ bool ParseOptionalThreadLocal(GlobalVariable::ThreadLocalMode &TLM);
bool ParseOptionalAddrSpace(unsigned &AddrSpace);
bool ParseOptionalAttrs(Attributes &Attrs, unsigned AttrKind);
bool ParseOptionalLinkage(unsigned &Linkage, bool &HasLinkage);
diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h
index adf5d4f4d0..0461e7b63a 100644
--- a/lib/AsmParser/LLToken.h
+++ b/lib/AsmParser/LLToken.h
@@ -44,13 +44,14 @@ namespace lltok {
kw_unnamed_addr,
kw_extern_weak,
kw_external, kw_thread_local,
+ kw_localdynamic, kw_initialexec, kw_localexec,
kw_zeroinitializer,
kw_undef, kw_null,
kw_to,
kw_tail,
kw_target,
kw_triple,
- kw_unwind,
+ kw_unwind,
kw_deplibs,
kw_datalayout,
kw_volatile,
diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp
index 83756daa7a..4a20b1c9d6 100644
--- a/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -102,6 +102,17 @@ static GlobalValue::VisibilityTypes GetDecodedVisibility(unsigned Val) {
}
}
+static GlobalVariable::ThreadLocalMode GetDecodedThreadLocalMode(unsigned Val) {
+ switch (Val) {
+ case 0: return GlobalVariable::NotThreadLocal;
+ default: // Map unknown non-zero value to general dynamic.
+ case 1: return GlobalVariable::GeneralDynamicTLSModel;
+ case 2: return GlobalVariable::LocalDynamicTLSModel;
+ case 3: return GlobalVariable::InitialExecTLSModel;
+ case 4: return GlobalVariable::LocalExecTLSModel;
+ }
+}
+
static int GetDecodedCastOpcode(unsigned Val) {
switch (Val) {
default: return -1;
@@ -1544,9 +1555,10 @@ bool BitcodeReader::ParseModule(bool Resume) {
GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility;
if (Record.size() > 6)
Visibility = GetDecodedVisibility(Record[6]);
- bool isThreadLocal = false;
+
+ GlobalVariable::ThreadLocalMode TLM = GlobalVariable::NotThreadLocal;
if (Record.size() > 7)
- isThreadLocal = Record[7];
+ TLM = GetDecodedThreadLocalMode(Record[7]);
bool UnnamedAddr = false;
if (Record.size() > 8)
@@ -1554,12 +1566,11 @@ bool BitcodeReader::ParseModule(bool Resume) {
GlobalVariable *NewGV =
new GlobalVariable(*TheModule, Ty, isConstant, Linkage, 0, "", 0,
- isThreadLocal, AddressSpace);
+ TLM, AddressSpace);
NewGV->setAlignment(Alignment);
if (!Section.empty())
NewGV->setSection(Section);
NewGV->setVisibility(Visibility);
- NewGV->setThreadLocal(isThreadLocal);
NewGV->setUnnamedAddr(UnnamedAddr);
ValueList.push_back(NewGV);
diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp
index 333d82ad4a..5b1725f550 100644
--- a/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -379,6 +379,17 @@ static unsigned getEncodedVisibility(const GlobalValue *GV) {
llvm_unreachable("Invalid visibility");
}
+static unsigned getEncodedThreadLocalMode(const GlobalVariable *GV) {
+ switch (GV->getThreadLocalMode()) {
+ case GlobalVariable::NotThreadLocal: return 0;
+ case GlobalVariable::GeneralDynamicTLSModel: return 1;
+ case GlobalVariable::LocalDynamicTLSModel: return 2;
+ case GlobalVariable::InitialExecTLSModel: return 3;
+ case GlobalVariable::LocalExecTLSModel: return 4;
+ }
+ llvm_unreachable("Invalid TLS model");
+}
+
// Emit top-level description of module, including target triple, inline asm,
// descriptors for global variables, and function prototype info.
static void WriteModuleInfo(const Module *M, const ValueEnumerator &VE,
@@ -487,7 +498,7 @@ static void WriteModuleInfo(const Module *M, const ValueEnumerator &VE,
GV->getVisibility() != GlobalValue::DefaultVisibility ||
GV->hasUnnamedAddr()) {
Vals.push_back(getEncodedVisibility(GV));
- Vals.push_back(GV->isThreadLocal());
+ Vals.push_back(getEncodedThreadLocalMode(GV));
Vals.push_back(GV->hasUnnamedAddr());
} else {
AbbrevToUse = SimpleGVarAbbrev;
diff --git a/lib/Linker/LinkModules.cpp b/lib/Linker/LinkModules.cpp
index 7293f3d0e8..afba2e8917 100644
--- a/lib/Linker/LinkModules.cpp
+++ b/lib/Linker/LinkModules.cpp
@@ -684,7 +684,7 @@ bool ModuleLinker::linkAppendingVarProto(GlobalVariable *DstGV,
GlobalVariable *NG =
new GlobalVariable(*DstGV->getParent(), NewType, SrcGV->isConstant(),
DstGV->getLinkage(), /*init*/0, /*name*/"", DstGV,
- DstGV->isThreadLocal(),
+ DstGV->getThreadLocalMode(),
DstGV->getType()->getAddressSpace());
// Propagate alignment, visibility and section info.
@@ -759,7 +759,7 @@ bool ModuleLinker::linkGlobalProto(GlobalVariable *SGV) {
new GlobalVariable(*DstM, TypeMap.get(SGV->getType()->getElementType()),
SGV->isConstant(), SGV->getLinkage(), /*init*/0,
SGV->getName(), /*insertbefore*/0,
- SGV->isThreadLocal(),
+ SGV->getThreadLocalMode(),
SGV->getType()->getAddressSpace());
// Propagate alignment, visibility and section info.
copyGVAttributes(NewDGV, SGV);
diff --git a/lib/Target/CppBackend/CPPBackend.cpp b/lib/Target/CppBackend/CPPBackend.cpp
index cd2ebcb508..0ea2a299dd 100644
--- a/lib/Target/CppBackend/CPPBackend.cpp
+++ b/lib/Target/CppBackend/CPPBackend.cpp
@@ -130,6 +130,7 @@ namespace {
private:
void printLinkageType(GlobalValue::LinkageTypes LT);
void printVisibilityType(GlobalValue::VisibilityTypes VisTypes);
+ void printThreadLocalMode(GlobalVariable::ThreadLocalMode TLM);
void printCallingConv(CallingConv::ID cc);
void printEscapedString(const std::string& str);
void printCFP(const ConstantFP* CFP);
@@ -325,6 +326,26 @@ void CppWriter::printVisibilityType(GlobalValue::VisibilityTypes VisType) {
}
}
+void CppWriter::printThreadLocalMode(GlobalVariable::ThreadLocalMode TLM) {
+ switch (TLM) {
+ case GlobalVariable::NotThreadLocal:
+ Out << "GlobalVariable::NotThreadLocal";
+ break;
+ case GlobalVariable::GeneralDynamicTLSModel:
+ Out << "GlobalVariable::GeneralDynamicTLSModel";
+ break;
+ case GlobalVariable::LocalDynamicTLSModel:
+ Out << "GlobalVariable::LocalDynamicTLSModel";
+ break;
+ case GlobalVariable::InitialExecTLSModel:
+ Out << "GlobalVariable::InitialExecTLSModel";
+ break;
+ case GlobalVariable::LocalExecTLSModel:
+ Out << "GlobalVariable::LocalExecTLSModel";
+ break;
+ }
+}
+
// printEscapedString - Print each character of the specified string, escaping
// it if it is not printable or if it is an escape char.
void CppWriter::printEscapedString(const std::string &Str) {
@@ -996,7 +1017,9 @@ void CppWriter::printVariableHead(const GlobalVariable *GV) {
}
if (GV->isThreadLocal()) {
printCppName(GV);
- Out << "->setThreadLocal(true);";
+ Out << "->setThreadLocalMode(";
+ printThreadLocalMode(GV->getThreadLocalMode());
+ Out << ");";
nl(Out);
}
if (is_inline) {
diff --git a/lib/Target/TargetMachine.cpp b/lib/Target/TargetMachine.cpp
index 6cdab5a266..382571982b 100644
--- a/lib/Target/TargetMachine.cpp
+++ b/lib/Target/TargetMachine.cpp
@@ -77,6 +77,24 @@ CodeModel::Model TargetMachine::getCodeModel() const {
return CodeGenInfo->getCodeModel();
}
+/// Get the IR-specified TLS model for Var.
+static TLSModel::Model getSelectedTLSModel(const GlobalVariable *Var) {
+ switch (Var->getThreadLocalMode()) {
+ case GlobalVariable::NotThreadLocal:
+ llvm_unreachable("getSelectedTLSModel for non-TLS variable");
+ break;
+ case GlobalVariable::GeneralDynamicTLSModel:
+ return TLSModel::GeneralDynamic;
+ case GlobalVariable::LocalDynamicTLSModel:
+ return TLSModel::LocalDynamic;
+ case GlobalVariable::InitialExecTLSModel:
+ return TLSModel::InitialExec;
+ case GlobalVariable::LocalExecTLSModel:
+ return TLSModel::LocalExec;
+ }
+ llvm_unreachable("invalid TLS model");
+}
+
TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
// If GV is an alias then use the aliasee for determining
// thread-localness.
@@ -86,22 +104,31 @@ TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
bool isLocal = Var->hasLocalLinkage();
bool isDeclaration = Var->isDeclaration();
+ bool isPIC = getRelocationModel() == Reloc::PIC_;
+ bool isPIE = Options.PositionIndependentExecutable;
// FIXME: what should we do for protected and internal visibility?
// For variables, is internal different from hidden?
bool isHidden = Var->hasHiddenVisibility();
- if (getRelocationModel() == Reloc::PIC_ &&
- !Options.PositionIndependentExecutable) {
+ TLSModel::Model Model;
+ if (isPIC && !isPIE) {
if (isLocal || isHidden)
- return TLSModel::LocalDynamic;
+ Model = TLSModel::LocalDynamic;
else
- return TLSModel::GeneralDynamic;
+ Model = TLSModel::GeneralDynamic;
} else {
if (!isDeclaration || isHidden)
- return TLSModel::LocalExec;
+ Model = TLSModel::LocalExec;
else
- return TLSModel::InitialExec;
+ Model = TLSModel::InitialExec;
}
+
+ // If the user specified a more specific model, use that.
+ TLSModel::Model SelectedModel = getSelectedTLSModel(Var);
+ if (SelectedModel > Model)
+ return SelectedModel;
+
+ return Model;
}
/// getOptLevel - Returns the optimization level: None, Less,
@@ -135,4 +162,3 @@ void TargetMachine::setFunctionSections(bool V) {
void TargetMachine::setDataSections(bool V) {
DataSections = V;
}
-
diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp
index 1668d6bc16..b192d17134 100644
--- a/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/lib/Transforms/IPO/GlobalOpt.cpp
@@ -517,7 +517,7 @@ static GlobalVariable *SRAGlobal(GlobalVariable *GV, const TargetData &TD) {
GlobalVariable *NGV = new GlobalVariable(STy->getElementType(i), false,
GlobalVariable::InternalLinkage,
In, GV->getName()+"."+Twine(i),
- GV->isThreadLocal(),
+ GV->getThreadLocalMode(),
GV->getType()->getAddressSpace());
Globals.insert(GV, NGV);
NewGlobals.push_back(NGV);
@@ -550,7 +550,7 @@ static GlobalVariable *SRAGlobal(GlobalVariable *GV, const TargetData &TD) {
GlobalVariable *NGV = new GlobalVariable(STy->getElementType(), false,
GlobalVariable::InternalLinkage,
In, GV->getName()+"."+Twine(i),
- GV->isThreadLocal(),
+ GV->getThreadLocalMode(),
GV->getType()->getAddressSpace());
Globals.insert(GV, NGV);
NewGlobals.push_back(NGV);
@@ -866,7 +866,7 @@ static GlobalVariable *OptimizeGlobalAddressOfMalloc(GlobalVariable *GV,
UndefValue::get(GlobalType),
GV->getName()+".body",
GV,
- GV->isThreadLocal());
+ GV->getThreadLocalMode());
// If there are bitcast users of the malloc (which is typical, usually we have
// a malloc + bitcast) then replace them with uses of the new global. Update
@@ -899,7 +899,7 @@ static GlobalVariable *OptimizeGlobalAddressOfMalloc(GlobalVariable *GV,
new GlobalVariable(Type::getInt1Ty(GV->getContext()), false,
GlobalValue::InternalLinkage,
ConstantInt::getFalse(GV->getContext()),
- GV->getName()+".init", GV->isThreadLocal());
+ GV->getName()+".init", GV->getThreadLocalMode());
bool InitBoolUsed = false;
// Loop over all uses of GV, processing them in turn.
@@ -1321,7 +1321,7 @@ static GlobalVariable *PerformHeapAllocSRoA(GlobalVariable *GV, CallInst *CI,
PFieldTy, false, GlobalValue::InternalLinkage,
Constant::getNullValue(PFieldTy),
GV->getName() + ".f" + Twine(FieldNo), GV,
- GV->isThreadLocal());
+ GV->getThreadLocalMode());
FieldGlobals.push_back(NGV);
unsigned TypeSize = TD->getTypeAllocSize(FieldTy);
@@ -1647,7 +1647,7 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
GlobalValue::InternalLinkage,
ConstantInt::getFalse(GV->getContext()),
GV->getName()+".b",
- GV->isThreadLocal());
+ GV->getThreadLocalMode());
GV->getParent()->getGlobalList().insert(GV, NewGV);
Constant *InitVal = GV->getInitializer();
@@ -2054,7 +2054,7 @@ static GlobalVariable *InstallGlobalCtors(GlobalVariable *GCL,
// Create the new global and insert it next to the existing list.
GlobalVariable *NGV = new GlobalVariable(CA->getType(), GCL->isConstant(),
GCL->getLinkage(), CA, "",
- GCL->isThreadLocal());
+ GCL->getThreadLocalMode());
GCL->getParent()->getGlobalList().insert(GCL, NGV);
NGV->takeName(GCL);
diff --git a/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/lib/Transforms/Instrumentation/AddressSanitizer.cpp
index a9d08db9c6..375a2ae7b0 100644
--- a/lib/Transforms/Instrumentation/AddressSanitizer.cpp
+++ b/lib/Transforms/Instrumentation/AddressSanitizer.cpp
@@ -511,7 +511,7 @@ bool AddressSanitizer::insertGlobalRedzones(Module &M) {
// Create a new global variable with enough space for a redzone.
GlobalVariable *NewGlobal = new GlobalVariable(
M, NewTy, G->isConstant(), G->getLinkage(),
- NewInitializer, "", G, G->isThreadLocal());
+ NewInitializer, "", G, G->getThreadLocalMode());
NewGlobal->copyAttributesFrom(G);
NewGlobal->setAlignment(RedzoneSize);
diff --git a/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index 6c42137b3d..60804794f2 100644
--- a/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -448,7 +448,7 @@ bool GCOVProfiler::emitProfileArcs() {
new GlobalVariable(*M, CounterTy, false,
GlobalValue::InternalLinkage,
Constant::getNullValue(CounterTy),
- "__llvm_gcov_ctr", 0, false, 0);
+ "__llvm_gcov_ctr");
CountersBySP.push_back(std::make_pair(Counters, (MDNode*)SP));
UniqueVector ComplexEdgePreds;
diff --git a/lib/Transforms/Utils/CloneModule.cpp b/lib/Transforms/Utils/CloneModule.cpp
index a0e027b5f1..1dac6b5b8b 100644
--- a/lib/Transforms/Utils/CloneModule.cpp
+++ b/lib/Transforms/Utils/CloneModule.cpp
@@ -53,7 +53,7 @@ Module *llvm::CloneModule(const Module *M, ValueToValueMapTy &VMap) {
I->isConstant(), I->getLinkage(),
(Constant*) 0, I->getName(),
(GlobalVariable*) 0,
- I->isThreadLocal(),
+ I->getThreadLocalMode(),
I->getType()->getAddressSpace());
GV->copyAttributesFrom(I);
VMap[I] = GV;
diff --git a/lib/VMCore/AsmWriter.cpp b/lib/VMCore/AsmWriter.cpp
index 4a7fde1905..9147b63512 100644
--- a/lib/VMCore/AsmWriter.cpp
+++ b/lib/VMCore/AsmWriter.cpp
@@ -1376,6 +1376,26 @@ static void PrintVisibility(GlobalValue::VisibilityTypes Vis,
}
}
+static void PrintThreadLocalModel(GlobalVariable::ThreadLocalMode TLM,
+ formatted_raw_ostream &Out) {
+ switch (TLM) {
+ case GlobalVariable::NotThreadLocal:
+ break;
+ case GlobalVariable::GeneralDynamicTLSModel:
+ Out << "thread_local ";
+ break;
+ case GlobalVariable::LocalDynamicTLSModel:
+ Out << "thread_local(localdynamic) ";
+ break;
+ case GlobalVariable::InitialExecTLSModel:
+ Out << "thread_local(initialexec) ";
+ break;
+ case GlobalVariable::LocalExecTLSModel:
+ Out << "thread_local(localexec) ";
+ break;
+ }
+}
+
void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
if (GV->isMaterializable())
Out << "; Materializable\n";
@@ -1388,8 +1408,8 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
PrintLinkage(GV->getLinkage(), Out);
PrintVisibility(GV->getVisibility(), Out);
+ PrintThreadLocalModel(GV->getThreadLocalMode(), Out);
- if (GV->isThreadLocal()) Out << "thread_local ";
if (unsigned AddressSpace = GV->getType()->getAddressSpace())
Out << "addrspace(" << AddressSpace << ") ";
if (GV->hasUnnamedAddr()) Out << "unnamed_addr ";
diff --git a/lib/VMCore/Core.cpp b/lib/VMCore/Core.cpp
index 30d8a9b12f..972db3cb86 100644
--- a/lib/VMCore/Core.cpp
+++ b/lib/VMCore/Core.cpp
@@ -1210,7 +1210,7 @@ LLVMValueRef LLVMAddGlobalInAddressSpace(LLVMModuleRef M, LLVMTypeRef Ty,
unsigned AddressSpace) {
return wrap(new GlobalVariable(*unwrap(M), unwrap(Ty), false,
GlobalValue::ExternalLinkage, 0, Name, 0,
- false, AddressSpace));
+ GlobalVariable::NotThreadLocal, AddressSpace));
}
LLVMValueRef LLVMGetNamedGlobal(LLVMModuleRef M, const char *Name) {
diff --git a/lib/VMCore/Globals.cpp b/lib/VMCore/Globals.cpp
index 4254fb29e8..a34ea7040e 100644
--- a/lib/VMCore/Globals.cpp
+++ b/lib/VMCore/Globals.cpp
@@ -80,14 +80,16 @@ bool GlobalValue::isDeclaration() const {
// GlobalVariable Implementation
//===----------------------------------------------------------------------===//
+// TODO: Remove once clang is updated.
GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
Constant *InitVal, const Twine &Name,
bool ThreadLocal, unsigned AddressSpace)
- : GlobalValue(PointerType::get(Ty, AddressSpace),
+ : GlobalValue(PointerType::get(Ty, AddressSpace),
Value::GlobalVariableVal,
OperandTraits::op_begin(this),
InitVal != 0, Link, Name),
- isConstantGlobal(constant), isThreadLocalSymbol(ThreadLocal) {
+ isConstantGlobal(constant),
+ threadLocalMode(ThreadLocal ? GeneralDynamicTLSModel : NotThreadLocal) {
if (InitVal) {
assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!");
@@ -97,16 +99,59 @@ GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
LeakDetector::addGarbageObject(this);
}
+// TODO: Remove once clang is updated.
GlobalVariable::GlobalVariable(Module &M, Type *Ty, bool constant,
LinkageTypes Link, Constant *InitVal,
const Twine &Name,
GlobalVariable *Before, bool ThreadLocal,
unsigned AddressSpace)
+ : GlobalValue(PointerType::get(Ty, AddressSpace),
+ Value::GlobalVariableVal,
+ OperandTraits::op_begin(this),
+ InitVal != 0, Link, Name),
+ isConstantGlobal(constant),
+ threadLocalMode(ThreadLocal ? GeneralDynamicTLSModel : NotThreadLocal) {
+ if (InitVal) {
+ assert(InitVal->getType() == Ty &&
+ "Initializer should be the same type as the GlobalVariable!");
+ Op<0>() = InitVal;
+ }
+
+ LeakDetector::addGarbageObject(this);
+
+ if (Before)
+ Before->getParent()->getGlobalList().insert(Before, this);
+ else
+ M.getGlobalList().push_back(this);
+}
+
+GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
+ Constant *InitVal, const Twine &Name,
+ ThreadLocalMode TLMode, unsigned AddressSpace)
+ : GlobalValue(PointerType::get(Ty, AddressSpace),
+ Value::GlobalVariableVal,
+ OperandTraits::op_begin(this),
+ InitVal != 0, Link, Name),
+ isConstantGlobal(constant), threadLocalMode(TLMode) {
+ if (InitVal) {
+ assert(InitVal->getType() == Ty &&
+ "Initializer should be the same type as the GlobalVariable!");
+ Op<0>() = InitVal;
+ }
+
+ LeakDetector::addGarbageObject(this);
+}
+
+GlobalVariable::GlobalVariable(Module &M, Type *Ty, bool constant,
+ LinkageTypes Link, Constant *InitVal,
+ const Twine &Name,
+ GlobalVariable *Before, ThreadLocalMode TLMode,
+ unsigned AddressSpace)
: GlobalValue(PointerType::get(Ty, AddressSpace),
Value::GlobalVariableVal,
OperandTraits::op_begin(this),
InitVal != 0, Link, Name),
- isConstantGlobal(constant), isThreadLocalSymbol(ThreadLocal) {
+ isConstantGlobal(constant), threadLocalMode(TLMode) {
if (InitVal) {
assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!");
diff --git a/lib/VMCore/IRBuilder.cpp b/lib/VMCore/IRBuilder.cpp
index b45923489a..4fc33ff070 100644
--- a/lib/VMCore/IRBuilder.cpp
+++ b/lib/VMCore/IRBuilder.cpp
@@ -28,7 +28,7 @@ Value *IRBuilderBase::CreateGlobalString(StringRef Str, const Twine &Name) {
Module &M = *BB->getParent()->getParent();
GlobalVariable *GV = new GlobalVariable(M, StrConstant->getType(),
true, GlobalValue::PrivateLinkage,
- StrConstant, "", 0, false);
+ StrConstant);
GV->setName(Name);
GV->setUnnamedAddr(true);
return GV;
diff --git a/test/Assembler/tls-models.ll b/test/Assembler/tls-models.ll
new file mode 100644
index 0000000000..42f24962ae
--- /dev/null
+++ b/test/Assembler/tls-models.ll
@@ -0,0 +1,11 @@
+; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
+
+; CHECK: @a = thread_local global i32 0
+; CHECK: @b = thread_local(localdynamic) global i32 0
+; CHECK: @c = thread_local(initialexec) global i32 0
+; CHECK: @d = thread_local(localexec) global i32 0
+
+@a = thread_local global i32 0
+@b = thread_local(localdynamic) global i32 0
+@c = thread_local(initialexec) global i32 0
+@d = thread_local(localexec) global i32 0
diff --git a/test/CodeGen/ARM/tls-models.ll b/test/CodeGen/ARM/tls-models.ll
new file mode 100644
index 0000000000..a5f3c9005a
--- /dev/null
+++ b/test/CodeGen/ARM/tls-models.ll
@@ -0,0 +1,117 @@
+; RUN: llc -march=arm -mtriple=arm-linux-gnueabi < %s | FileCheck -check-prefix=CHECK-NONPIC %s
+; RUN: llc -march=arm -mtriple=arm-linux-gnueabi -relocation-model=pic < %s | FileCheck -check-prefix=CHECK-PIC %s
+
+
+@external_gd = external thread_local global i32
+@internal_gd = internal thread_local global i32 42
+
+@external_ld = external thread_local(localdynamic) global i32
+@internal_ld = internal thread_local(localdynamic) global i32 42
+
+@external_ie = external thread_local(initialexec) global i32
+@internal_ie = internal thread_local(initialexec) global i32 42
+
+@external_le = external thread_local(localexec) global i32
+@internal_le = internal thread_local(localexec) global i32 42
+
+; ----- no model specified -----
+
+define i32* @f1() {
+entry:
+ ret i32* @external_gd
+
+ ; Non-PIC code can use initial-exec, PIC code has to use general dynamic.
+ ; CHECK-NONPIC: f1:
+ ; CHECK-NONPIC: external_gd(gottpoff)
+ ; CHECK-PIC: f1:
+ ; CHECK-PIC: external_gd(tlsgd)
+}
+
+define i32* @f2() {
+entry:
+ ret i32* @internal_gd
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic,
+ ; but that is not implemented, so falls back to general dynamic.
+ ; CHECK-NONPIC: f2:
+ ; CHECK-NONPIC: internal_gd(tpoff)
+ ; CHECK-PIC: f2:
+ ; CHECK-PIC: internal_gd(tlsgd)
+}
+
+
+; ----- localdynamic specified -----
+
+define i32* @f3() {
+entry:
+ ret i32* @external_ld
+
+ ; Non-PIC code can use initial exec, PIC should use local dynamic,
+ ; but that is not implemented, so falls back to general dynamic.
+ ; CHECK-NONPIC: f3:
+ ; CHECK-NONPIC: external_ld(gottpoff)
+ ; CHECK-PIC: f3:
+ ; CHECK-PIC: external_ld(tlsgd)
+}
+
+define i32* @f4() {
+entry:
+ ret i32* @internal_ld
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic,
+ ; but that is not implemented, so it falls back to general dynamic.
+ ; CHECK-NONPIC: f4:
+ ; CHECK-NONPIC: internal_ld(tpoff)
+ ; CHECK-PIC: f4:
+ ; CHECK-PIC: internal_ld(tlsgd)
+}
+
+
+; ----- initialexec specified -----
+
+define i32* @f5() {
+entry:
+ ret i32* @external_ie
+
+ ; Non-PIC and PIC code will use initial exec as specified.
+ ; CHECK-NONPIC: f5:
+ ; CHECK-NONPIC: external_ie(gottpoff)
+ ; CHECK-PIC: f5:
+ ; CHECK-PIC: external_ie(gottpoff)
+}
+
+define i32* @f6() {
+entry:
+ ret i32* @internal_ie
+
+ ; Non-PIC code can use local exec, PIC code use initial exec as specified.
+ ; CHECK-NONPIC: f6:
+ ; CHECK-NONPIC: internal_ie(tpoff)
+ ; CHECK-PIC: f6:
+ ; CHECK-PIC: internal_ie(gottpoff)
+}
+
+
+; ----- localexec specified -----
+
+define i32* @f7() {
+entry:
+ ret i32* @external_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; CHECK-NONPIC: f7:
+ ; CHECK-NONPIC: external_le(tpoff)
+ ; CHECK-PIC: f7:
+ ; CHECK-PIC: external_le(tpoff)
+}
+
+define i32* @f8() {
+entry:
+ ret i32* @internal_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; CHECK-NONPIC: f8:
+ ; CHECK-NONPIC: internal_le(tpoff)
+ ; CHECK-PIC: f8:
+ ; CHECK-PIC: internal_le(tpoff)
+}
diff --git a/test/CodeGen/Mips/tls-models.ll b/test/CodeGen/Mips/tls-models.ll
new file mode 100644
index 0000000000..8f5789ec79
--- /dev/null
+++ b/test/CodeGen/Mips/tls-models.ll
@@ -0,0 +1,113 @@
+; RUN: llc -march=mipsel < %s | FileCheck -check-prefix=CHECK-PIC %s
+; RUN: llc -march=mipsel -relocation-model=static < %s | FileCheck -check-prefix=CHECK-NONPIC %s
+
+@external_gd = external thread_local global i32
+@internal_gd = internal thread_local global i32 42
+
+@external_ld = external thread_local(localdynamic) global i32
+@internal_ld = internal thread_local(localdynamic) global i32 42
+
+@external_ie = external thread_local(initialexec) global i32
+@internal_ie = internal thread_local(initialexec) global i32 42
+
+@external_le = external thread_local(localexec) global i32
+@internal_le = internal thread_local(localexec) global i32 42
+
+; ----- no model specified -----
+
+define i32* @f1() {
+entry:
+ ret i32* @external_gd
+
+ ; Non-PIC code can use initial-exec, PIC code has to use general dynamic.
+ ; CHECK-NONPIC: f1:
+ ; CHECK-NONPIC: %gottprel
+ ; CHECK-PIC: f1:
+ ; CHECK-PIC: %tlsgd
+}
+
+define i32* @f2() {
+entry:
+ ret i32* @internal_gd
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic.
+ ; CHECK-NONPIC: f2:
+ ; CHECK-NONPIC: %tprel_hi
+ ; CHECK-PIC: f2:
+ ; CHECK-PIC: %tlsldm
+}
+
+
+; ----- localdynamic specified -----
+
+define i32* @f3() {
+entry:
+ ret i32* @external_ld
+
+ ; Non-PIC code can use initial exec, PIC should use local dynamic.
+ ; CHECK-NONPIC: f3:
+ ; CHECK-NONPIC: %gottprel
+ ; CHECK-PIC: f3:
+ ; CHECK-PIC: %tlsldm
+}
+
+define i32* @f4() {
+entry:
+ ret i32* @internal_ld
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic.
+ ; CHECK-NONPIC: f4:
+ ; CHECK-NONPIC: %tprel_hi
+ ; CHECK-PIC: f4:
+ ; CHECK-PIC: %tlsldm
+}
+
+
+; ----- initialexec specified -----
+
+define i32* @f5() {
+entry:
+ ret i32* @external_ie
+
+ ; Non-PIC and PIC code will use initial exec as specified.
+ ; CHECK-NONPIC: f5:
+ ; CHECK-NONPIC: %gottprel
+ ; CHECK-PIC: f5:
+ ; CHECK-PIC: %gottprel
+}
+
+define i32* @f6() {
+entry:
+ ret i32* @internal_ie
+
+ ; Non-PIC code can use local exec, PIC code use initial exec as specified.
+ ; CHECK-NONPIC: f6:
+ ; CHECK-NONPIC: %tprel_hi
+ ; CHECK-PIC: f6:
+ ; CHECK-PIC: %gottprel
+}
+
+
+; ----- localexec specified -----
+
+define i32* @f7() {
+entry:
+ ret i32* @external_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; CHECK-NONPIC: f7:
+ ; CHECK-NONPIC: %tprel_hi
+ ; CHECK-PIC: f7:
+ ; CHECK-PIC: %tprel_hi
+}
+
+define i32* @f8() {
+entry:
+ ret i32* @internal_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; CHECK-NONPIC: f8:
+ ; CHECK-NONPIC: %tprel_hi
+ ; CHECK-PIC: f8:
+ ; CHECK-PIC: %tprel_hi
+}
diff --git a/test/CodeGen/X86/tls-models.ll b/test/CodeGen/X86/tls-models.ll
new file mode 100644
index 0000000000..7c527e210a
--- /dev/null
+++ b/test/CodeGen/X86/tls-models.ll
@@ -0,0 +1,166 @@
+; RUN: llc < %s -march=x86-64 -mtriple=x86_64-linux-gnu | FileCheck -check-prefix=X64 %s
+; RUN: llc < %s -march=x86-64 -mtriple=x86_64-linux-gnu -relocation-model=pic | FileCheck -check-prefix=X64_PIC %s
+; RUN: llc < %s -march=x86 -mtriple=i386-linux-gnu | FileCheck -check-prefix=X32 %s
+; RUN: llc < %s -march=x86 -mtriple=i386-linux-gnu -relocation-model=pic | FileCheck -check-prefix=X32_PIC %s
+
+; Darwin always uses the same model.
+; RUN: llc < %s -march=x86-64 -mtriple=x86_64-apple-darwin | FileCheck -check-prefix=DARWIN %s
+
+@external_gd = external thread_local global i32
+@internal_gd = internal thread_local global i32 42
+
+@external_ld = external thread_local(localdynamic) global i32
+@internal_ld = internal thread_local(localdynamic) global i32 42
+
+@external_ie = external thread_local(initialexec) global i32
+@internal_ie = internal thread_local(initialexec) global i32 42
+
+@external_le = external thread_local(localexec) global i32
+@internal_le = internal thread_local(localexec) global i32 42
+
+; ----- no model specified -----
+
+define i32* @f1() {
+entry:
+ ret i32* @external_gd
+
+ ; Non-PIC code can use initial-exec, PIC code has to use general dynamic.
+ ; X64: f1:
+ ; X64: external_gd@GOTTPOFF
+ ; X32: f1:
+ ; X32: external_gd@INDNTPOFF
+ ; X64_PIC: f1:
+ ; X64_PIC: external_gd@TLSGD
+ ; X32_PIC: f1:
+ ; X32_PIC: external_gd@TLSGD
+ ; DARWIN: f1:
+ ; DARWIN: _external_gd@TLVP
+}
+
+define i32* @f2() {
+entry:
+ ret i32* @internal_gd
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic.
+ ; X64: f2:
+ ; X64: internal_gd@TPOFF
+ ; X32: f2:
+ ; X32: internal_gd@NTPOFF
+ ; X64_PIC: f2:
+ ; X64_PIC: internal_gd@TLSLD
+ ; X32_PIC: f2:
+ ; X32_PIC: internal_gd@TLSLDM
+ ; DARWIN: f2:
+ ; DARWIN: _internal_gd@TLVP
+}
+
+
+; ----- localdynamic specified -----
+
+define i32* @f3() {
+entry:
+ ret i32* @external_ld
+
+ ; Non-PIC code can use initial exec, PIC code use local dynamic as specified.
+ ; X64: f3:
+ ; X64: external_ld@GOTTPOFF
+ ; X32: f3:
+ ; X32: external_ld@INDNTPOFF
+ ; X64_PIC: f3:
+ ; X64_PIC: external_ld@TLSLD
+ ; X32_PIC: f3:
+ ; X32_PIC: external_ld@TLSLDM
+ ; DARWIN: f3:
+ ; DARWIN: _external_ld@TLVP
+}
+
+define i32* @f4() {
+entry:
+ ret i32* @internal_ld
+
+ ; Non-PIC code can use local exec, PIC code can use local dynamic.
+ ; X64: f4:
+ ; X64: internal_ld@TPOFF
+ ; X32: f4:
+ ; X32: internal_ld@NTPOFF
+ ; X64_PIC: f4:
+ ; X64_PIC: internal_ld@TLSLD
+ ; X32_PIC: f4:
+ ; X32_PIC: internal_ld@TLSLDM
+ ; DARWIN: f4:
+ ; DARWIN: _internal_ld@TLVP
+}
+
+
+; ----- initialexec specified -----
+
+define i32* @f5() {
+entry:
+ ret i32* @external_ie
+
+ ; Non-PIC and PIC code will use initial exec as specified.
+ ; X64: f5:
+ ; X64: external_ie@GOTTPOFF
+ ; X32: f5:
+ ; X32: external_ie@INDNTPOFF
+ ; X64_PIC: f5:
+ ; X64_PIC: external_ie@GOTTPOFF
+ ; X32_PIC: f5:
+ ; X32_PIC: external_ie@GOTNTPOFF
+ ; DARWIN: f5:
+ ; DARWIN: _external_ie@TLVP
+}
+
+define i32* @f6() {
+entry:
+ ret i32* @internal_ie
+
+ ; Non-PIC code can use local exec, PIC code use initial exec as specified.
+ ; X64: f6:
+ ; X64: internal_ie@TPOFF
+ ; X32: f6:
+ ; X32: internal_ie@NTPOFF
+ ; X64_PIC: f6:
+ ; X64_PIC: internal_ie@GOTTPOFF
+ ; X32_PIC: f6:
+ ; X32_PIC: internal_ie@GOTNTPOFF
+ ; DARWIN: f6:
+ ; DARWIN: _internal_ie@TLVP
+}
+
+
+; ----- localexec specified -----
+
+define i32* @f7() {
+entry:
+ ret i32* @external_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; X64: f7:
+ ; X64: external_le@TPOFF
+ ; X32: f7:
+ ; X32: external_le@NTPOFF
+ ; X64_PIC: f7:
+ ; X64_PIC: external_le@TPOFF
+ ; X32_PIC: f7:
+ ; X32_PIC: external_le@NTPOFF
+ ; DARWIN: f7:
+ ; DARWIN: _external_le@TLVP
+}
+
+define i32* @f8() {
+entry:
+ ret i32* @internal_le
+
+ ; Non-PIC and PIC code will use local exec as specified.
+ ; X64: f8:
+ ; X64: internal_le@TPOFF
+ ; X32: f8:
+ ; X32: internal_le@NTPOFF
+ ; X64_PIC: f8:
+ ; X64_PIC: internal_le@TPOFF
+ ; X32_PIC: f8:
+ ; X32_PIC: internal_le@NTPOFF
+ ; DARWIN: f8:
+ ; DARWIN: _internal_le@TLVP
+}
--
cgit v1.2.3-70-g09d2