Skip to content

Commit

Permalink
Implement target independent TLS compatible with glibc's emutls.c.
Browse files Browse the repository at this point in the history
The 'common' section TLS is not implemented.
Current C/C++ TLS variables are not placed in common section.
DWARF debug info to get the address of TLS variables is not generated yet.

clang and driver changes in http://reviews.llvm.org/D10524

  Added -femulated-tls flag to select the emulated TLS model,
  which will be used for old targets like Android that do not
  support ELF TLS models.

Added TargetLowering::LowerToTLSEmulatedModel as a target-independent
function to convert a SDNode of TLS variable address to a function call
to __emutls_get_address.

Added into lib/Target/*/*ISelLowering.cpp to call LowerToTLSEmulatedModel
for TLSModel::Emulated. Although all targets supporting ELF TLS models are
enhanced, emulated TLS model has been tested only for Android ELF targets.
Modified AsmPrinter.cpp to print the emutls_v.* and emutls_t.* variables for
emulated TLS variables.
Modified DwarfCompileUnit.cpp to skip some DIE for emulated TLS variabls.

TODO: Add proper DIE for emulated TLS variables.
      Added new unit tests with emulated TLS.

Differential Revision: http://reviews.llvm.org/D10522

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@243438 91177308-0d34-0410-b5e6-96231b3b80d8

Signed-off-by: Dmitry Moskalchuk <dm@crystax.net>
  • Loading branch information
chih-hung authored and dmsck committed Feb 9, 2016
1 parent 5b17c39 commit 97c4868
Show file tree
Hide file tree
Showing 32 changed files with 1,895 additions and 62 deletions.
3 changes: 3 additions & 0 deletions docs/LangRef.rst
Expand Up @@ -494,6 +494,9 @@ 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.

For platforms without linker support of ELF TLS model, the -femulated-tls
flag can be used to generate GCC compatible emulated TLS code.

.. _namedtypes:

Structure Types
Expand Down
5 changes: 5 additions & 0 deletions include/llvm/CodeGen/AsmPrinter.h
Expand Up @@ -235,6 +235,11 @@ class AsmPrinter : public MachineFunctionPass {
///
void EmitJumpTableInfo();

/// Emit the control variable for an emulated TLS variable.
virtual void EmitEmulatedTLSControlVariable(const GlobalVariable *GV,
MCSymbol *EmittedSym,
bool AllZeroInitValue);

/// Emit the specified global variable to the .s file.
virtual void EmitGlobalVariable(const GlobalVariable *GV);

Expand Down
5 changes: 5 additions & 0 deletions include/llvm/CodeGen/CommandFlags.h
Expand Up @@ -219,6 +219,10 @@ FunctionSections("function-sections",
cl::desc("Emit functions into separate sections"),
cl::init(false));

cl::opt<bool> EmulatedTLS("emulated-tls",
cl::desc("Use emulated TLS model"),
cl::init(false));

cl::opt<bool> UniqueSectionNames("unique-section-names",
cl::desc("Give unique names to every section"),
cl::init(true));
Expand Down Expand Up @@ -260,6 +264,7 @@ static inline TargetOptions InitTargetOptionsFromCodeGenFlags() {
Options.DataSections = DataSections;
Options.FunctionSections = FunctionSections;
Options.UniqueSectionNames = UniqueSectionNames;
Options.EmulatedTLS = EmulatedTLS;

Options.MCOptions = InitMCTargetOptionsFromFlags();
Options.JTType = JTableType;
Expand Down
1 change: 1 addition & 0 deletions include/llvm/MC/MCObjectFileInfo.h
Expand Up @@ -216,6 +216,7 @@ class MCObjectFileInfo {
MCSection *getTextSection() const { return TextSection; }
MCSection *getDataSection() const { return DataSection; }
MCSection *getBSSSection() const { return BSSSection; }
MCSection *getReadOnlySection() const { return ReadOnlySection; }
MCSection *getLSDASection() const { return LSDASection; }
MCSection *getCompactUnwindSection() const { return CompactUnwindSection; }
MCSection *getDwarfAbbrevSection() const { return DwarfAbbrevSection; }
Expand Down
4 changes: 4 additions & 0 deletions include/llvm/Target/TargetLowering.h
Expand Up @@ -2821,6 +2821,10 @@ class TargetLowering : public TargetLoweringBase {
virtual bool useLoadStackGuardNode() const {
return false;
}

/// Lower TLS global address SDNode for target independent emulated TLS model.
virtual SDValue LowerToTLSEmulatedModel(const GlobalAddressSDNode *GA,
SelectionDAG &DAG) const;
};

/// Given an LLVM IR type and return type attributes, compute the return value
Expand Down
7 changes: 6 additions & 1 deletion include/llvm/Target/TargetOptions.h
Expand Up @@ -72,7 +72,7 @@ namespace llvm {
UseInitArray(false), DisableIntegratedAS(false),
CompressDebugSections(false), FunctionSections(false),
DataSections(false), UniqueSectionNames(true), TrapUnreachable(false),
FloatABIType(FloatABI::Default),
EmulatedTLS(false), FloatABIType(FloatABI::Default),
AllowFPOpFusion(FPOpFusion::Standard), Reciprocals(TargetRecip()),
JTType(JumpTable::Single),
ThreadModel(ThreadModel::POSIX) {}
Expand Down Expand Up @@ -172,6 +172,10 @@ namespace llvm {
/// Emit target-specific trap instruction for 'unreachable' IR instructions.
unsigned TrapUnreachable : 1;

/// EmulatedTLS - This flag enables emulated TLS model, using emutls
/// function in the runtime library..
unsigned EmulatedTLS : 1;

/// FloatABIType - This setting is set by -float-abi=xxx option is specfied
/// on the command line. This setting may either be Default, Soft, or Hard.
/// Default selects the target's default behavior. Soft selects the ABI for
Expand Down Expand Up @@ -231,6 +235,7 @@ inline bool operator==(const TargetOptions &LHS,
ARE_EQUAL(PositionIndependentExecutable) &&
ARE_EQUAL(UseInitArray) &&
ARE_EQUAL(TrapUnreachable) &&
ARE_EQUAL(EmulatedTLS) &&
ARE_EQUAL(FloatABIType) &&
ARE_EQUAL(AllowFPOpFusion) &&
ARE_EQUAL(Reciprocals) &&
Expand Down
100 changes: 91 additions & 9 deletions lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Expand Up @@ -340,8 +340,58 @@ MCSymbol *AsmPrinter::getSymbol(const GlobalValue *GV) const {
return TM.getSymbol(GV, *Mang);
}

static MCSymbol *getOrCreateEmuTLSControlSym(MCSymbol *GVSym, MCContext &C) {
return C.getOrCreateSymbol(Twine("__emutls_v.") + GVSym->getName());
}

static MCSymbol *getOrCreateEmuTLSInitSym(MCSymbol *GVSym, MCContext &C) {
return C.getOrCreateSymbol(Twine("__emutls_t.") + GVSym->getName());
}

/// EmitEmulatedTLSControlVariable - Emit the control variable for an emulated TLS variable.
void AsmPrinter::EmitEmulatedTLSControlVariable(const GlobalVariable *GV,
MCSymbol *EmittedSym,
bool AllZeroInitValue) {
// If there is init value, use .data.rel.local section;
// otherwise use the .data section.
MCSection *TLSVarSection = const_cast<MCSection*>(
(GV->hasInitializer() && !AllZeroInitValue)
? getObjFileLowering().getDataRelLocalSection()
: getObjFileLowering().getDataSection());
OutStreamer->SwitchSection(TLSVarSection);
MCSymbol *GVSym = getSymbol(GV);
EmitLinkage(GV, EmittedSym); // same linkage as GV
const DataLayout &DL = GV->getParent()->getDataLayout();
uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType());
unsigned AlignLog = getGVAlignmentLog2(GV, DL);
unsigned WordSize = DL.getPointerSize();
unsigned Alignment = DL.getPointerABIAlignment();
EmitAlignment(Log2_32(Alignment));
OutStreamer->EmitLabel(EmittedSym);
OutStreamer->EmitIntValue(Size, WordSize);
OutStreamer->EmitIntValue((1 << AlignLog), WordSize);
OutStreamer->EmitIntValue(0, WordSize);
if (GV->hasInitializer() && !AllZeroInitValue) {
OutStreamer->EmitSymbolValue(
getOrCreateEmuTLSInitSym(GVSym, OutContext), WordSize);
} else
OutStreamer->EmitIntValue(0, WordSize);
if (MAI->hasDotTypeDotSizeDirective())
OutStreamer->emitELFSize(cast<MCSymbolELF>(EmittedSym),
MCConstantExpr::create(4 * WordSize, OutContext));
OutStreamer->AddBlankLine(); // End of the __emutls_v.* variable.
}

/// EmitGlobalVariable - Emit the specified global variable to the .s file.
void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
bool IsEmuTLSVar =
GV->getThreadLocalMode() != llvm::GlobalVariable::NotThreadLocal &&
TM.Options.EmulatedTLS;
assert((!IsEmuTLSVar || getObjFileLowering().getDataRelLocalSection()) &&
"Need relocatable local section for emulated TLS variables");
assert(!(IsEmuTLSVar && GV->hasCommonLinkage()) &&
"No emulated TLS variables in the common section");

if (GV->hasInitializer()) {
// Check to see if this is a special global used by LLVM, if so, emit it.
if (EmitSpecialLLVMGlobal(GV))
Expand All @@ -352,15 +402,22 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
if (GlobalGOTEquivs.count(getSymbol(GV)))
return;

if (isVerbose()) {
if (isVerbose() && !IsEmuTLSVar) {
// When printing the control variable __emutls_v.*,
// we don't need to print the original TLS variable name.
GV->printAsOperand(OutStreamer->GetCommentOS(),
/*PrintType=*/false, GV->getParent());
OutStreamer->GetCommentOS() << '\n';
}
}

MCSymbol *GVSym = getSymbol(GV);
EmitVisibility(GVSym, GV->getVisibility(), !GV->isDeclaration());
MCSymbol *EmittedSym = IsEmuTLSVar ?
getOrCreateEmuTLSControlSym(GVSym, OutContext) : GVSym;
// getOrCreateEmuTLSControlSym only creates the symbol with name and default attributes.
// GV's or GVSym's attributes will be used for the EmittedSym.

EmitVisibility(EmittedSym, GV->getVisibility(), !GV->isDeclaration());

if (!GV->hasInitializer()) // External globals require no extra code.
return;
Expand All @@ -371,7 +428,7 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
"' is already defined");

if (MAI->hasDotTypeDotSizeDirective())
OutStreamer->EmitSymbolAttribute(GVSym, MCSA_ELF_TypeObject);
OutStreamer->EmitSymbolAttribute(EmittedSym, MCSA_ELF_TypeObject);

SectionKind GVKind = TargetLoweringObjectFile::getKindForGlobal(GV, TM);

Expand All @@ -383,13 +440,27 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
// sections and expected to be contiguous (e.g. ObjC metadata).
unsigned AlignLog = getGVAlignmentLog2(GV, *DL);

bool AllZeroInitValue = false;
const Constant *InitValue = GV->getInitializer();
if (isa<ConstantAggregateZero>(InitValue))
AllZeroInitValue = true;
else {
const ConstantInt *InitIntValue = dyn_cast<ConstantInt>(InitValue);
if (InitIntValue && InitIntValue->isZero())
AllZeroInitValue = true;
}
if (IsEmuTLSVar)
EmitEmulatedTLSControlVariable(GV, EmittedSym, AllZeroInitValue);

for (const HandlerInfo &HI : Handlers) {
NamedRegionTimer T(HI.TimerName, HI.TimerGroupName, TimePassesIsEnabled);
HI.Handler->setSymbolSize(GVSym, Size);
}

// Handle common and BSS local symbols (.lcomm).
if (GVKind.isCommon() || GVKind.isBSSLocal()) {
assert(!(IsEmuTLSVar && GVKind.isCommon()) &&
"No emulated TLS variables in the common section");
if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it.
unsigned Align = 1 << AlignLog;

Expand Down Expand Up @@ -434,12 +505,21 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
return;
}

MCSection *TheSection =
if (IsEmuTLSVar && AllZeroInitValue)
return; // No need of initialization values.

MCSymbol *EmittedInitSym = IsEmuTLSVar ?
getOrCreateEmuTLSInitSym(GVSym, OutContext) : GVSym;
// getOrCreateEmuTLSInitSym only creates the symbol with name and default attributes.
// GV's or GVSym's attributes will be used for the EmittedInitSym.

MCSection *TheSection = IsEmuTLSVar ?
getObjFileLowering().getReadOnlySection() :
getObjFileLowering().SectionForGlobal(GV, GVKind, *Mang, TM);

// Handle the zerofill directive on darwin, which is a special form of BSS
// emission.
if (GVKind.isBSSExtern() && MAI->hasMachoZeroFillDirective()) {
if (GVKind.isBSSExtern() && MAI->hasMachoZeroFillDirective() && !IsEmuTLSVar) {
if (Size == 0) Size = 1; // zerofill of 0 bytes is undefined.

// .globl _foo
Expand All @@ -459,7 +539,7 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {
// TLOF class. This will also make it more obvious that stuff like
// MCStreamer::EmitTBSSSymbol is macho specific and only called from macho
// specific code.
if (GVKind.isThreadLocal() && MAI->hasMachoTBSSDirective()) {
if (GVKind.isThreadLocal() && MAI->hasMachoTBSSDirective() && !IsEmuTLSVar) {
// Emit the .tbss symbol
MCSymbol *MangSym =
OutContext.getOrCreateSymbol(GVSym->getName() + Twine("$tlv$init"));
Expand Down Expand Up @@ -502,16 +582,18 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) {

OutStreamer->SwitchSection(TheSection);

EmitLinkage(GV, GVSym);
// emutls_t.* symbols are only used in the current compilation unit.
if (!IsEmuTLSVar)
EmitLinkage(GV, EmittedInitSym);
EmitAlignment(AlignLog, GV);

OutStreamer->EmitLabel(GVSym);
OutStreamer->EmitLabel(EmittedInitSym);

EmitGlobalConstant(GV->getInitializer());

if (MAI->hasDotTypeDotSizeDirective())
// .size foo, 42
OutStreamer->emitELFSize(cast<MCSymbolELF>(GVSym),
OutStreamer->emitELFSize(cast<MCSymbolELF>(EmittedInitSym),
MCConstantExpr::create(Size, OutContext));

OutStreamer->AddBlankLine();
Expand Down
44 changes: 24 additions & 20 deletions lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Expand Up @@ -151,28 +151,32 @@ DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE(
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
const MCSymbol *Sym = Asm->getSymbol(Global);
if (Global->isThreadLocal()) {
// FIXME: Make this work with -gsplit-dwarf.
unsigned PointerSize = Asm->getDataLayout().getPointerSize();
assert((PointerSize == 4 || PointerSize == 8) &&
"Add support for other sizes if necessary");
// Based on GCC's support for TLS:
if (!DD->useSplitDwarf()) {
// 1) Start with a constNu of the appropriate pointer size
addUInt(*Loc, dwarf::DW_FORM_data1,
PointerSize == 4 ? dwarf::DW_OP_const4u : dwarf::DW_OP_const8u);
// 2) containing the (relocated) offset of the TLS variable
// within the module's TLS block.
addExpr(*Loc, dwarf::DW_FORM_udata,
Asm->getObjFileLowering().getDebugThreadLocalSymbol(Sym));
if (Asm->TM.Options.EmulatedTLS) {
// TODO: add debug info for emulated thread local mode.
} else {
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_GNU_const_index);
addUInt(*Loc, dwarf::DW_FORM_udata,
DD->getAddressPool().getIndex(Sym, /* TLS */ true));
// FIXME: Make this work with -gsplit-dwarf.
unsigned PointerSize = Asm->getDataLayout().getPointerSize();
assert((PointerSize == 4 || PointerSize == 8) &&
"Add support for other sizes if necessary");
// Based on GCC's support for TLS:
if (!DD->useSplitDwarf()) {
// 1) Start with a constNu of the appropriate pointer size
addUInt(*Loc, dwarf::DW_FORM_data1,
PointerSize == 4 ? dwarf::DW_OP_const4u : dwarf::DW_OP_const8u);
// 2) containing the (relocated) offset of the TLS variable
// within the module's TLS block.
addExpr(*Loc, dwarf::DW_FORM_udata,
Asm->getObjFileLowering().getDebugThreadLocalSymbol(Sym));
} else {
addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_GNU_const_index);
addUInt(*Loc, dwarf::DW_FORM_udata,
DD->getAddressPool().getIndex(Sym, /* TLS */ true));
}
// 3) followed by an OP to make the debugger do a TLS lookup.
addUInt(*Loc, dwarf::DW_FORM_data1,
DD->useGNUTLSOpcode() ? dwarf::DW_OP_GNU_push_tls_address
: dwarf::DW_OP_form_tls_address);
}
// 3) followed by an OP to make the debugger do a TLS lookup.
addUInt(*Loc, dwarf::DW_FORM_data1,
DD->useGNUTLSOpcode() ? dwarf::DW_OP_GNU_push_tls_address
: dwarf::DW_OP_form_tls_address);
} else {
DD->addArangeLabel(SymbolCU(this, Sym));
addOpAddress(*Loc, Sym);
Expand Down
43 changes: 43 additions & 0 deletions lib/CodeGen/SelectionDAG/TargetLowering.cpp
Expand Up @@ -3036,3 +3036,46 @@ bool TargetLowering::expandFP_TO_SINT(SDNode *Node, SDValue &Result,
DAG.getConstant(0, dl, NVT), Ret, ISD::SETLT);
return true;
}

//===----------------------------------------------------------------------===//
// Implementation of Emulated TLS Model
//===----------------------------------------------------------------------===//

SDValue TargetLowering::LowerToTLSEmulatedModel(const GlobalAddressSDNode *GA,
SelectionDAG &DAG) const {
// Access to address of TLS varialbe xyz is lowered to a function call:
// __emutls_get_address( address of global variable named "__emutls_v.xyz" )
EVT PtrVT = getPointerTy(DAG.getDataLayout());
PointerType *VoidPtrType = Type::getInt8PtrTy(*DAG.getContext());
SDLoc dl(GA);

ArgListTy Args;
ArgListEntry Entry;
std::string NameString = ("__emutls_v." + GA->getGlobal()->getName()).str();
Module *VariableModule = const_cast<Module*>(GA->getGlobal()->getParent());
StringRef EmuTlsVarName(NameString);
GlobalVariable *EmuTlsVar = VariableModule->getNamedGlobal(EmuTlsVarName);
if (!EmuTlsVar)
EmuTlsVar = dyn_cast_or_null<GlobalVariable>(
VariableModule->getOrInsertGlobal(EmuTlsVarName, VoidPtrType));
Entry.Node = DAG.getGlobalAddress(EmuTlsVar, dl, PtrVT);
Entry.Ty = VoidPtrType;
Args.push_back(Entry);

SDValue EmuTlsGetAddr = DAG.getExternalSymbol("__emutls_get_address", PtrVT);

TargetLowering::CallLoweringInfo CLI(DAG);
CLI.setDebugLoc(dl).setChain(DAG.getEntryNode());
CLI.setCallee(CallingConv::C, VoidPtrType, EmuTlsGetAddr, std::move(Args), 0);
std::pair<SDValue, SDValue> CallResult = LowerCallTo(CLI);

// TLSADDR will be codegen'ed as call. Inform MFI that function has calls.
// At last for X86 targets, maybe good for other targets too?
MachineFrameInfo *MFI = DAG.getMachineFunction().getFrameInfo();
MFI->setAdjustsStack(true); // Is this only for X86 target?
MFI->setHasCalls(true);

assert((GA->getOffset() == 0) &&
"Emulated TLS must have zero offset in GlobalAddressSDNode");
return CallResult.first;
}
4 changes: 4 additions & 0 deletions lib/Target/AArch64/AArch64ISelLowering.cpp
Expand Up @@ -3160,6 +3160,10 @@ AArch64TargetLowering::LowerELFGlobalTLSAddress(SDValue Op,
const GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);

TLSModel::Model Model = getTargetMachine().getTLSModel(GA->getGlobal());

if (DAG.getTarget().Options.EmulatedTLS)
return LowerToTLSEmulatedModel(GA, DAG);

if (!EnableAArch64ELFLocalDynamicTLSGeneration) {
if (Model == TLSModel::LocalDynamic)
Model = TLSModel::GeneralDynamic;
Expand Down
2 changes: 2 additions & 0 deletions lib/Target/ARM/ARMISelLowering.cpp
Expand Up @@ -2577,6 +2577,8 @@ ARMTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const {
assert(Subtarget->isTargetELF() &&
"TLS not implemented for non-ELF targets");
GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
if (DAG.getTarget().Options.EmulatedTLS)
return LowerToTLSEmulatedModel(GA, DAG);

TLSModel::Model model = getTargetMachine().getTLSModel(GA->getGlobal());

Expand Down
3 changes: 3 additions & 0 deletions lib/Target/Mips/MipsISelLowering.cpp
Expand Up @@ -1719,6 +1719,9 @@ lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const
// Local Exec TLS Model.

GlobalAddressSDNode *GA = cast<GlobalAddressSDNode>(Op);
if (DAG.getTarget().Options.EmulatedTLS)
return LowerToTLSEmulatedModel(GA, DAG);

SDLoc DL(GA);
const GlobalValue *GV = GA->getGlobal();
EVT PtrVT = getPointerTy(DAG.getDataLayout());
Expand Down

0 comments on commit 97c4868

Please sign in to comment.