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
This commit is contained in:
Hans Wennborg 2012-06-23 11:37:03 +00:00
parent 47cbc4e0ee
commit ce718ff9f4
24 changed files with 707 additions and 48 deletions

View File

@ -864,7 +864,7 @@ library name referenced.</p>
<h4><a name="MODULE_CODE_GLOBALVAR">MODULE_CODE_GLOBALVAR Record</a></h4> <h4><a name="MODULE_CODE_GLOBALVAR">MODULE_CODE_GLOBALVAR Record</a></h4>
<div> <div>
<p><tt>[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal]</tt></p> <p><tt>[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal, unnamed_addr]</tt></p>
<p>The <tt>GLOBALVAR</tt> record (code 7) marks the declaration or <p>The <tt>GLOBALVAR</tt> record (code 7) marks the declaration or
definition of a global variable. The operand fields are:</p> definition of a global variable. The operand fields are:</p>
@ -915,8 +915,16 @@ encoding of the visibility of this variable:
</ul> </ul>
</li> </li>
<li><i>threadlocal</i>: If present and non-zero, indicates that the variable <li><i>threadlocal</i>: If present, an encoding of the thread local storage
is <tt>thread_local</tt></li> mode of the variable:
<ul>
<li><tt>not thread local</tt>: code 0</li>
<li><tt>thread local; default TLS model</tt>: code 1</li>
<li><tt>localdynamic</tt>: code 2</li>
<li><tt>initialexec</tt>: code 3</li>
<li><tt>localexec</tt>: code 4</li>
</ul>
</li>
<li><i>unnamed_addr</i>: If present and non-zero, indicates that the variable <li><i>unnamed_addr</i>: If present and non-zero, indicates that the variable
has <tt>unnamed_addr</tt></li> has <tt>unnamed_addr</tt></li>

View File

@ -838,9 +838,32 @@ define i32 @main() { <i>; i32()* </i>&nbsp;
<p>Global variables define regions of memory allocated at compilation time <p>Global variables define regions of memory allocated at compilation time
instead of run-time. Global variables may optionally be initialized, may 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 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.</p>
<p>A variable may be defined as <tt>thread_local</tt>, which
means that it will not be shared by threads (each thread will have a 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:</p>
<dl>
<dt><b><tt>localdynamic</tt></b>:</dt>
<dd>For variables that are only used within the current shared library.</dd>
<dt><b><tt>initialexec</tt></b>:</dt>
<dd>For variables in modules that will not be loaded dynamically.</dd>
<dt><b><tt>localexec</tt></b>:</dt>
<dd>For variables defined in the executable and only used within it.</dd>
</dl>
<p>The models correspond to the ELF TLS models; see
<a href="http://people.redhat.com/drepper/tls.pdf">ELF
Handling For Thread-Local Storage</a> 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.</p>
<p>A variable may be defined as a global
"constant," which indicates that the contents of the variable "constant," which indicates that the contents of the variable
will <b>never</b> be modified (enabling better optimization, allowing the will <b>never</b> be modified (enabling better optimization, allowing the
global data to be placed in the read-only section of an executable, etc). global data to be placed in the read-only section of an executable, etc).
@ -893,6 +916,13 @@ define i32 @main() { <i>; i32()* </i>&nbsp;
@G = addrspace(5) constant float 1.0, section "foo", align 4 @G = addrspace(5) constant float 1.0, section "foo", align 4
</pre> </pre>
<p>The following example defines a thread-local global with
the <tt>initialexec</tt> TLS model:</p>
<pre class="doc_code">
@G = thread_local(initialexec) global i32 0, align 4
</pre>
</div> </div>

View File

@ -41,24 +41,47 @@ class GlobalVariable : public GlobalValue, public ilist_node<GlobalVariable> {
void setParent(Module *parent); void setParent(Module *parent);
bool isConstantGlobal : 1; // Is this a global constant? 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: public:
// allocate space for exactly one operand // allocate space for exactly one operand
void *operator new(size_t s) { void *operator new(size_t s) {
return User::operator new(s, 1); 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, GlobalVariable(Type *Ty, bool isConstant, LinkageTypes Linkage,
Constant *Initializer = 0, const Twine &Name = "", Constant *Initializer = 0, const Twine &Name = "",
bool ThreadLocal = false, unsigned AddressSpace = 0); 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 /// GlobalVariable ctor - This creates a global and inserts it before the
/// specified other global. /// specified other global.
// TODO: Put default param values back when ctors above are removed.
GlobalVariable(Module &M, Type *Ty, bool isConstant, GlobalVariable(Module &M, Type *Ty, bool isConstant,
LinkageTypes Linkage, Constant *Initializer, LinkageTypes Linkage, Constant *Initializer,
const Twine &Name, const Twine &Name,
GlobalVariable *InsertBefore = 0, bool ThreadLocal = false, GlobalVariable *InsertBefore,
ThreadLocalMode,
unsigned AddressSpace = 0); unsigned AddressSpace = 0);
~GlobalVariable() { ~GlobalVariable() {
@ -135,8 +158,14 @@ public:
void setConstant(bool Val) { isConstantGlobal = Val; } void setConstant(bool Val) { isConstantGlobal = Val; }
/// If the value is "Thread Local", its value isn't shared by the threads. /// If the value is "Thread Local", its value isn't shared by the threads.
bool isThreadLocal() const { return isThreadLocalSymbol; } bool isThreadLocal() const { return threadLocalMode != NotThreadLocal; }
void setThreadLocal(bool Val) { isThreadLocalSymbol = Val; } void setThreadLocal(bool Val) {
threadLocalMode = Val ? GeneralDynamicTLSModel : NotThreadLocal;
}
void setThreadLocalMode(ThreadLocalMode Val) { threadLocalMode = Val; }
ThreadLocalMode getThreadLocalMode() const {
return static_cast<ThreadLocalMode>(threadLocalMode);
}
/// copyAttributesFrom - copy all additional attributes (those not needed to /// copyAttributesFrom - copy all additional attributes (those not needed to
/// create a GlobalVariable) from the GlobalVariable Src to this one. /// create a GlobalVariable) from the GlobalVariable Src to this one.

View File

@ -474,6 +474,9 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(extern_weak); KEYWORD(extern_weak);
KEYWORD(external); KEYWORD(external);
KEYWORD(thread_local); KEYWORD(thread_local);
KEYWORD(localdynamic);
KEYWORD(initialexec);
KEYWORD(localexec);
KEYWORD(zeroinitializer); KEYWORD(zeroinitializer);
KEYWORD(undef); KEYWORD(undef);
KEYWORD(null); KEYWORD(null);

View File

@ -645,12 +645,13 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
unsigned Linkage, bool HasLinkage, unsigned Linkage, bool HasLinkage,
unsigned Visibility) { unsigned Visibility) {
unsigned AddrSpace; unsigned AddrSpace;
bool ThreadLocal, IsConstant, UnnamedAddr; bool IsConstant, UnnamedAddr;
GlobalVariable::ThreadLocalMode TLM;
LocTy UnnamedAddrLoc; LocTy UnnamedAddrLoc;
LocTy TyLoc; LocTy TyLoc;
Type *Ty = 0; Type *Ty = 0;
if (ParseOptionalToken(lltok::kw_thread_local, ThreadLocal) || if (ParseOptionalThreadLocal(TLM) ||
ParseOptionalAddrSpace(AddrSpace) || ParseOptionalAddrSpace(AddrSpace) ||
ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr, ParseOptionalToken(lltok::kw_unnamed_addr, UnnamedAddr,
&UnnamedAddrLoc) || &UnnamedAddrLoc) ||
@ -691,7 +692,8 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
if (GV == 0) { if (GV == 0) {
GV = new GlobalVariable(*M, Ty, false, GlobalValue::ExternalLinkage, 0, GV = new GlobalVariable(*M, Ty, false, GlobalValue::ExternalLinkage, 0,
Name, 0, false, AddrSpace); Name, 0, GlobalVariable::NotThreadLocal,
AddrSpace);
} else { } else {
if (GV->getType()->getElementType() != Ty) if (GV->getType()->getElementType() != Ty)
return Error(TyLoc, return Error(TyLoc,
@ -710,7 +712,7 @@ bool LLParser::ParseGlobal(const std::string &Name, LocTy NameLoc,
GV->setConstant(IsConstant); GV->setConstant(IsConstant);
GV->setLinkage((GlobalValue::LinkageTypes)Linkage); GV->setLinkage((GlobalValue::LinkageTypes)Linkage);
GV->setVisibility((GlobalValue::VisibilityTypes)Visibility); GV->setVisibility((GlobalValue::VisibilityTypes)Visibility);
GV->setThreadLocal(ThreadLocal); GV->setThreadLocalMode(TLM);
GV->setUnnamedAddr(UnnamedAddr); GV->setUnnamedAddr(UnnamedAddr);
// Parse attributes on the global. // Parse attributes on the global.
@ -858,6 +860,46 @@ bool LLParser::ParseUInt32(unsigned &Val) {
return false; 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 /// ParseOptionalAddrSpace
/// := /*empty*/ /// := /*empty*/

View File

@ -171,6 +171,9 @@ namespace llvm {
Loc = Lex.getLoc(); Loc = Lex.getLoc();
return ParseUInt32(Val); return ParseUInt32(Val);
} }
bool ParseTLSModel(GlobalVariable::ThreadLocalMode &TLM);
bool ParseOptionalThreadLocal(GlobalVariable::ThreadLocalMode &TLM);
bool ParseOptionalAddrSpace(unsigned &AddrSpace); bool ParseOptionalAddrSpace(unsigned &AddrSpace);
bool ParseOptionalAttrs(Attributes &Attrs, unsigned AttrKind); bool ParseOptionalAttrs(Attributes &Attrs, unsigned AttrKind);
bool ParseOptionalLinkage(unsigned &Linkage, bool &HasLinkage); bool ParseOptionalLinkage(unsigned &Linkage, bool &HasLinkage);

View File

@ -44,13 +44,14 @@ namespace lltok {
kw_unnamed_addr, kw_unnamed_addr,
kw_extern_weak, kw_extern_weak,
kw_external, kw_thread_local, kw_external, kw_thread_local,
kw_localdynamic, kw_initialexec, kw_localexec,
kw_zeroinitializer, kw_zeroinitializer,
kw_undef, kw_null, kw_undef, kw_null,
kw_to, kw_to,
kw_tail, kw_tail,
kw_target, kw_target,
kw_triple, kw_triple,
kw_unwind, kw_unwind,
kw_deplibs, kw_deplibs,
kw_datalayout, kw_datalayout,
kw_volatile, kw_volatile,

View File

@ -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) { static int GetDecodedCastOpcode(unsigned Val) {
switch (Val) { switch (Val) {
default: return -1; default: return -1;
@ -1544,9 +1555,10 @@ bool BitcodeReader::ParseModule(bool Resume) {
GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility; GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility;
if (Record.size() > 6) if (Record.size() > 6)
Visibility = GetDecodedVisibility(Record[6]); Visibility = GetDecodedVisibility(Record[6]);
bool isThreadLocal = false;
GlobalVariable::ThreadLocalMode TLM = GlobalVariable::NotThreadLocal;
if (Record.size() > 7) if (Record.size() > 7)
isThreadLocal = Record[7]; TLM = GetDecodedThreadLocalMode(Record[7]);
bool UnnamedAddr = false; bool UnnamedAddr = false;
if (Record.size() > 8) if (Record.size() > 8)
@ -1554,12 +1566,11 @@ bool BitcodeReader::ParseModule(bool Resume) {
GlobalVariable *NewGV = GlobalVariable *NewGV =
new GlobalVariable(*TheModule, Ty, isConstant, Linkage, 0, "", 0, new GlobalVariable(*TheModule, Ty, isConstant, Linkage, 0, "", 0,
isThreadLocal, AddressSpace); TLM, AddressSpace);
NewGV->setAlignment(Alignment); NewGV->setAlignment(Alignment);
if (!Section.empty()) if (!Section.empty())
NewGV->setSection(Section); NewGV->setSection(Section);
NewGV->setVisibility(Visibility); NewGV->setVisibility(Visibility);
NewGV->setThreadLocal(isThreadLocal);
NewGV->setUnnamedAddr(UnnamedAddr); NewGV->setUnnamedAddr(UnnamedAddr);
ValueList.push_back(NewGV); ValueList.push_back(NewGV);

View File

@ -379,6 +379,17 @@ static unsigned getEncodedVisibility(const GlobalValue *GV) {
llvm_unreachable("Invalid visibility"); 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, // Emit top-level description of module, including target triple, inline asm,
// descriptors for global variables, and function prototype info. // descriptors for global variables, and function prototype info.
static void WriteModuleInfo(const Module *M, const ValueEnumerator &VE, 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->getVisibility() != GlobalValue::DefaultVisibility ||
GV->hasUnnamedAddr()) { GV->hasUnnamedAddr()) {
Vals.push_back(getEncodedVisibility(GV)); Vals.push_back(getEncodedVisibility(GV));
Vals.push_back(GV->isThreadLocal()); Vals.push_back(getEncodedThreadLocalMode(GV));
Vals.push_back(GV->hasUnnamedAddr()); Vals.push_back(GV->hasUnnamedAddr());
} else { } else {
AbbrevToUse = SimpleGVarAbbrev; AbbrevToUse = SimpleGVarAbbrev;

View File

@ -684,7 +684,7 @@ bool ModuleLinker::linkAppendingVarProto(GlobalVariable *DstGV,
GlobalVariable *NG = GlobalVariable *NG =
new GlobalVariable(*DstGV->getParent(), NewType, SrcGV->isConstant(), new GlobalVariable(*DstGV->getParent(), NewType, SrcGV->isConstant(),
DstGV->getLinkage(), /*init*/0, /*name*/"", DstGV, DstGV->getLinkage(), /*init*/0, /*name*/"", DstGV,
DstGV->isThreadLocal(), DstGV->getThreadLocalMode(),
DstGV->getType()->getAddressSpace()); DstGV->getType()->getAddressSpace());
// Propagate alignment, visibility and section info. // Propagate alignment, visibility and section info.
@ -759,7 +759,7 @@ bool ModuleLinker::linkGlobalProto(GlobalVariable *SGV) {
new GlobalVariable(*DstM, TypeMap.get(SGV->getType()->getElementType()), new GlobalVariable(*DstM, TypeMap.get(SGV->getType()->getElementType()),
SGV->isConstant(), SGV->getLinkage(), /*init*/0, SGV->isConstant(), SGV->getLinkage(), /*init*/0,
SGV->getName(), /*insertbefore*/0, SGV->getName(), /*insertbefore*/0,
SGV->isThreadLocal(), SGV->getThreadLocalMode(),
SGV->getType()->getAddressSpace()); SGV->getType()->getAddressSpace());
// Propagate alignment, visibility and section info. // Propagate alignment, visibility and section info.
copyGVAttributes(NewDGV, SGV); copyGVAttributes(NewDGV, SGV);

View File

@ -130,6 +130,7 @@ namespace {
private: private:
void printLinkageType(GlobalValue::LinkageTypes LT); void printLinkageType(GlobalValue::LinkageTypes LT);
void printVisibilityType(GlobalValue::VisibilityTypes VisTypes); void printVisibilityType(GlobalValue::VisibilityTypes VisTypes);
void printThreadLocalMode(GlobalVariable::ThreadLocalMode TLM);
void printCallingConv(CallingConv::ID cc); void printCallingConv(CallingConv::ID cc);
void printEscapedString(const std::string& str); void printEscapedString(const std::string& str);
void printCFP(const ConstantFP* CFP); 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 // printEscapedString - Print each character of the specified string, escaping
// it if it is not printable or if it is an escape char. // it if it is not printable or if it is an escape char.
void CppWriter::printEscapedString(const std::string &Str) { void CppWriter::printEscapedString(const std::string &Str) {
@ -996,7 +1017,9 @@ void CppWriter::printVariableHead(const GlobalVariable *GV) {
} }
if (GV->isThreadLocal()) { if (GV->isThreadLocal()) {
printCppName(GV); printCppName(GV);
Out << "->setThreadLocal(true);"; Out << "->setThreadLocalMode(";
printThreadLocalMode(GV->getThreadLocalMode());
Out << ");";
nl(Out); nl(Out);
} }
if (is_inline) { if (is_inline) {

View File

@ -77,6 +77,24 @@ CodeModel::Model TargetMachine::getCodeModel() const {
return CodeGenInfo->getCodeModel(); 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 { TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
// If GV is an alias then use the aliasee for determining // If GV is an alias then use the aliasee for determining
// thread-localness. // thread-localness.
@ -86,22 +104,31 @@ TLSModel::Model TargetMachine::getTLSModel(const GlobalValue *GV) const {
bool isLocal = Var->hasLocalLinkage(); bool isLocal = Var->hasLocalLinkage();
bool isDeclaration = Var->isDeclaration(); bool isDeclaration = Var->isDeclaration();
bool isPIC = getRelocationModel() == Reloc::PIC_;
bool isPIE = Options.PositionIndependentExecutable;
// FIXME: what should we do for protected and internal visibility? // FIXME: what should we do for protected and internal visibility?
// For variables, is internal different from hidden? // For variables, is internal different from hidden?
bool isHidden = Var->hasHiddenVisibility(); bool isHidden = Var->hasHiddenVisibility();
if (getRelocationModel() == Reloc::PIC_ && TLSModel::Model Model;
!Options.PositionIndependentExecutable) { if (isPIC && !isPIE) {
if (isLocal || isHidden) if (isLocal || isHidden)
return TLSModel::LocalDynamic; Model = TLSModel::LocalDynamic;
else else
return TLSModel::GeneralDynamic; Model = TLSModel::GeneralDynamic;
} else { } else {
if (!isDeclaration || isHidden) if (!isDeclaration || isHidden)
return TLSModel::LocalExec; Model = TLSModel::LocalExec;
else 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, /// getOptLevel - Returns the optimization level: None, Less,
@ -135,4 +162,3 @@ void TargetMachine::setFunctionSections(bool V) {
void TargetMachine::setDataSections(bool V) { void TargetMachine::setDataSections(bool V) {
DataSections = V; DataSections = V;
} }

View File

@ -517,7 +517,7 @@ static GlobalVariable *SRAGlobal(GlobalVariable *GV, const TargetData &TD) {
GlobalVariable *NGV = new GlobalVariable(STy->getElementType(i), false, GlobalVariable *NGV = new GlobalVariable(STy->getElementType(i), false,
GlobalVariable::InternalLinkage, GlobalVariable::InternalLinkage,
In, GV->getName()+"."+Twine(i), In, GV->getName()+"."+Twine(i),
GV->isThreadLocal(), GV->getThreadLocalMode(),
GV->getType()->getAddressSpace()); GV->getType()->getAddressSpace());
Globals.insert(GV, NGV); Globals.insert(GV, NGV);
NewGlobals.push_back(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 *NGV = new GlobalVariable(STy->getElementType(), false,
GlobalVariable::InternalLinkage, GlobalVariable::InternalLinkage,
In, GV->getName()+"."+Twine(i), In, GV->getName()+"."+Twine(i),
GV->isThreadLocal(), GV->getThreadLocalMode(),
GV->getType()->getAddressSpace()); GV->getType()->getAddressSpace());
Globals.insert(GV, NGV); Globals.insert(GV, NGV);
NewGlobals.push_back(NGV); NewGlobals.push_back(NGV);
@ -866,7 +866,7 @@ static GlobalVariable *OptimizeGlobalAddressOfMalloc(GlobalVariable *GV,
UndefValue::get(GlobalType), UndefValue::get(GlobalType),
GV->getName()+".body", GV->getName()+".body",
GV, GV,
GV->isThreadLocal()); GV->getThreadLocalMode());
// If there are bitcast users of the malloc (which is typical, usually we have // 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 // 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, new GlobalVariable(Type::getInt1Ty(GV->getContext()), false,
GlobalValue::InternalLinkage, GlobalValue::InternalLinkage,
ConstantInt::getFalse(GV->getContext()), ConstantInt::getFalse(GV->getContext()),
GV->getName()+".init", GV->isThreadLocal()); GV->getName()+".init", GV->getThreadLocalMode());
bool InitBoolUsed = false; bool InitBoolUsed = false;
// Loop over all uses of GV, processing them in turn. // 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, PFieldTy, false, GlobalValue::InternalLinkage,
Constant::getNullValue(PFieldTy), Constant::getNullValue(PFieldTy),
GV->getName() + ".f" + Twine(FieldNo), GV, GV->getName() + ".f" + Twine(FieldNo), GV,
GV->isThreadLocal()); GV->getThreadLocalMode());
FieldGlobals.push_back(NGV); FieldGlobals.push_back(NGV);
unsigned TypeSize = TD->getTypeAllocSize(FieldTy); unsigned TypeSize = TD->getTypeAllocSize(FieldTy);
@ -1647,7 +1647,7 @@ static bool TryToShrinkGlobalToBoolean(GlobalVariable *GV, Constant *OtherVal) {
GlobalValue::InternalLinkage, GlobalValue::InternalLinkage,
ConstantInt::getFalse(GV->getContext()), ConstantInt::getFalse(GV->getContext()),
GV->getName()+".b", GV->getName()+".b",
GV->isThreadLocal()); GV->getThreadLocalMode());
GV->getParent()->getGlobalList().insert(GV, NewGV); GV->getParent()->getGlobalList().insert(GV, NewGV);
Constant *InitVal = GV->getInitializer(); 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. // Create the new global and insert it next to the existing list.
GlobalVariable *NGV = new GlobalVariable(CA->getType(), GCL->isConstant(), GlobalVariable *NGV = new GlobalVariable(CA->getType(), GCL->isConstant(),
GCL->getLinkage(), CA, "", GCL->getLinkage(), CA, "",
GCL->isThreadLocal()); GCL->getThreadLocalMode());
GCL->getParent()->getGlobalList().insert(GCL, NGV); GCL->getParent()->getGlobalList().insert(GCL, NGV);
NGV->takeName(GCL); NGV->takeName(GCL);

View File

@ -511,7 +511,7 @@ bool AddressSanitizer::insertGlobalRedzones(Module &M) {
// Create a new global variable with enough space for a redzone. // Create a new global variable with enough space for a redzone.
GlobalVariable *NewGlobal = new GlobalVariable( GlobalVariable *NewGlobal = new GlobalVariable(
M, NewTy, G->isConstant(), G->getLinkage(), M, NewTy, G->isConstant(), G->getLinkage(),
NewInitializer, "", G, G->isThreadLocal()); NewInitializer, "", G, G->getThreadLocalMode());
NewGlobal->copyAttributesFrom(G); NewGlobal->copyAttributesFrom(G);
NewGlobal->setAlignment(RedzoneSize); NewGlobal->setAlignment(RedzoneSize);

View File

@ -448,7 +448,7 @@ bool GCOVProfiler::emitProfileArcs() {
new GlobalVariable(*M, CounterTy, false, new GlobalVariable(*M, CounterTy, false,
GlobalValue::InternalLinkage, GlobalValue::InternalLinkage,
Constant::getNullValue(CounterTy), Constant::getNullValue(CounterTy),
"__llvm_gcov_ctr", 0, false, 0); "__llvm_gcov_ctr");
CountersBySP.push_back(std::make_pair(Counters, (MDNode*)SP)); CountersBySP.push_back(std::make_pair(Counters, (MDNode*)SP));
UniqueVector<BasicBlock *> ComplexEdgePreds; UniqueVector<BasicBlock *> ComplexEdgePreds;

View File

@ -53,7 +53,7 @@ Module *llvm::CloneModule(const Module *M, ValueToValueMapTy &VMap) {
I->isConstant(), I->getLinkage(), I->isConstant(), I->getLinkage(),
(Constant*) 0, I->getName(), (Constant*) 0, I->getName(),
(GlobalVariable*) 0, (GlobalVariable*) 0,
I->isThreadLocal(), I->getThreadLocalMode(),
I->getType()->getAddressSpace()); I->getType()->getAddressSpace());
GV->copyAttributesFrom(I); GV->copyAttributesFrom(I);
VMap[I] = GV; VMap[I] = GV;

View File

@ -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) { void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
if (GV->isMaterializable()) if (GV->isMaterializable())
Out << "; Materializable\n"; Out << "; Materializable\n";
@ -1388,8 +1408,8 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
PrintLinkage(GV->getLinkage(), Out); PrintLinkage(GV->getLinkage(), Out);
PrintVisibility(GV->getVisibility(), Out); PrintVisibility(GV->getVisibility(), Out);
PrintThreadLocalModel(GV->getThreadLocalMode(), Out);
if (GV->isThreadLocal()) Out << "thread_local ";
if (unsigned AddressSpace = GV->getType()->getAddressSpace()) if (unsigned AddressSpace = GV->getType()->getAddressSpace())
Out << "addrspace(" << AddressSpace << ") "; Out << "addrspace(" << AddressSpace << ") ";
if (GV->hasUnnamedAddr()) Out << "unnamed_addr "; if (GV->hasUnnamedAddr()) Out << "unnamed_addr ";

View File

@ -1210,7 +1210,7 @@ LLVMValueRef LLVMAddGlobalInAddressSpace(LLVMModuleRef M, LLVMTypeRef Ty,
unsigned AddressSpace) { unsigned AddressSpace) {
return wrap(new GlobalVariable(*unwrap(M), unwrap(Ty), false, return wrap(new GlobalVariable(*unwrap(M), unwrap(Ty), false,
GlobalValue::ExternalLinkage, 0, Name, 0, GlobalValue::ExternalLinkage, 0, Name, 0,
false, AddressSpace)); GlobalVariable::NotThreadLocal, AddressSpace));
} }
LLVMValueRef LLVMGetNamedGlobal(LLVMModuleRef M, const char *Name) { LLVMValueRef LLVMGetNamedGlobal(LLVMModuleRef M, const char *Name) {

View File

@ -80,14 +80,59 @@ bool GlobalValue::isDeclaration() const {
// GlobalVariable Implementation // GlobalVariable Implementation
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// TODO: Remove once clang is updated.
GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link, GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
Constant *InitVal, const Twine &Name, Constant *InitVal, const Twine &Name,
bool ThreadLocal, unsigned AddressSpace) bool ThreadLocal, unsigned AddressSpace)
: GlobalValue(PointerType::get(Ty, AddressSpace), : GlobalValue(PointerType::get(Ty, AddressSpace),
Value::GlobalVariableVal, Value::GlobalVariableVal,
OperandTraits<GlobalVariable>::op_begin(this), OperandTraits<GlobalVariable>::op_begin(this),
InitVal != 0, Link, Name), 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!");
Op<0>() = InitVal;
}
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<GlobalVariable>::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<GlobalVariable>::op_begin(this),
InitVal != 0, Link, Name),
isConstantGlobal(constant), threadLocalMode(TLMode) {
if (InitVal) { if (InitVal) {
assert(InitVal->getType() == Ty && assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!"); "Initializer should be the same type as the GlobalVariable!");
@ -100,13 +145,13 @@ GlobalVariable::GlobalVariable(Type *Ty, bool constant, LinkageTypes Link,
GlobalVariable::GlobalVariable(Module &M, Type *Ty, bool constant, GlobalVariable::GlobalVariable(Module &M, Type *Ty, bool constant,
LinkageTypes Link, Constant *InitVal, LinkageTypes Link, Constant *InitVal,
const Twine &Name, const Twine &Name,
GlobalVariable *Before, bool ThreadLocal, GlobalVariable *Before, ThreadLocalMode TLMode,
unsigned AddressSpace) unsigned AddressSpace)
: GlobalValue(PointerType::get(Ty, AddressSpace), : GlobalValue(PointerType::get(Ty, AddressSpace),
Value::GlobalVariableVal, Value::GlobalVariableVal,
OperandTraits<GlobalVariable>::op_begin(this), OperandTraits<GlobalVariable>::op_begin(this),
InitVal != 0, Link, Name), InitVal != 0, Link, Name),
isConstantGlobal(constant), isThreadLocalSymbol(ThreadLocal) { isConstantGlobal(constant), threadLocalMode(TLMode) {
if (InitVal) { if (InitVal) {
assert(InitVal->getType() == Ty && assert(InitVal->getType() == Ty &&
"Initializer should be the same type as the GlobalVariable!"); "Initializer should be the same type as the GlobalVariable!");

View File

@ -28,7 +28,7 @@ Value *IRBuilderBase::CreateGlobalString(StringRef Str, const Twine &Name) {
Module &M = *BB->getParent()->getParent(); Module &M = *BB->getParent()->getParent();
GlobalVariable *GV = new GlobalVariable(M, StrConstant->getType(), GlobalVariable *GV = new GlobalVariable(M, StrConstant->getType(),
true, GlobalValue::PrivateLinkage, true, GlobalValue::PrivateLinkage,
StrConstant, "", 0, false); StrConstant);
GV->setName(Name); GV->setName(Name);
GV->setUnnamedAddr(true); GV->setUnnamedAddr(true);
return GV; return GV;

View File

@ -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

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}