diff options
author | Chih-Hung Hsieh <chh@google.com> | 2016-01-13 23:56:37 +0000 |
---|---|---|
committer | Chih-Hung Hsieh <chh@google.com> | 2016-01-13 23:56:37 +0000 |
commit | 578864007baa81366d2a98a307e016b78bed82af (patch) | |
tree | 1fd31bd59f979f919b502fbc50eb271b8e580c4f /llvm/lib/CodeGen | |
parent | 02fe4b93341845695296883496f50aa5946c9b7e (diff) | |
download | bcm5719-llvm-578864007baa81366d2a98a307e016b78bed82af.tar.gz bcm5719-llvm-578864007baa81366d2a98a307e016b78bed82af.zip |
[TLS] New lower emutls pass, fix linkage bugs.
Previous implementation in http://reviews.llvm.org/D10522
created external references to __emutls_v.* variables.
Such references are inaccurate and cannot be handled by
all linkers, e.g. Android dynamic and gold linkers for aarch64.
Now a new LowerEmuTLS pass to go through all global variables,
and add emutls_v.* and emutls_t.* variables.
These __emutls* variables have the same linkage and
visibility as the associated user defined TLS variable.
Also removed old code that dump __emutls* variables in AsmPrinter.cpp,
and updated TLS unit tests.
Differential Revision: http://reviews.llvm.org/D15300
llvm-svn: 257718
Diffstat (limited to 'llvm/lib/CodeGen')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 84 | ||||
-rw-r--r-- | llvm/lib/CodeGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/lib/CodeGen/LLVMTargetMachine.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/CodeGen/LowerEmuTLS.cpp | 159 | ||||
-rw-r--r-- | llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp | 4 |
5 files changed, 178 insertions, 74 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 5f67d3daa97..8d962555e30 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -347,51 +347,17 @@ 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) { - MCSection *TLSVarSection = 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; + bool IsEmuTLSVar = TM.Options.EmulatedTLS && GV->isThreadLocal(); assert(!(IsEmuTLSVar && GV->hasCommonLinkage()) && "No emulated TLS variables in the common section"); + // Never emit TLS variable xyz in emulated TLS model. + // The initialization value is in __emutls_t.xyz instead of xyz. + if (IsEmuTLSVar) + return; + if (GV->hasInitializer()) { // Check to see if this is a special global used by LLVM, if so, emit it. if (EmitSpecialLLVMGlobal(GV)) @@ -402,7 +368,7 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { if (GlobalGOTEquivs.count(getSymbol(GV))) return; - if (isVerbose() && !IsEmuTLSVar) { + if (isVerbose()) { // When printing the control variable __emutls_v.*, // we don't need to print the original TLS variable name. GV->printAsOperand(OutStreamer->GetCommentOS(), @@ -412,8 +378,7 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { } MCSymbol *GVSym = getSymbol(GV); - MCSymbol *EmittedSym = IsEmuTLSVar ? - getOrCreateEmuTLSControlSym(GVSym, OutContext) : GVSym; + MCSymbol *EmittedSym = GVSym; // getOrCreateEmuTLSControlSym only creates the symbol with name and default attributes. // GV's or GVSym's attributes will be used for the EmittedSym. @@ -440,18 +405,6 @@ 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); @@ -459,8 +412,6 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { // 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; @@ -505,21 +456,14 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { return; } - 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. + MCSymbol *EmittedInitSym = GVSym; - MCSection *TheSection = IsEmuTLSVar ? - getObjFileLowering().getReadOnlySection() : + MCSection *TheSection = 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() && !IsEmuTLSVar) { + if (GVKind.isBSSExtern() && MAI->hasMachoZeroFillDirective()) { if (Size == 0) Size = 1; // zerofill of 0 bytes is undefined. // .globl _foo @@ -539,7 +483,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() && !IsEmuTLSVar) { + if (GVKind.isThreadLocal() && MAI->hasMachoTBSSDirective()) { // Emit the .tbss symbol MCSymbol *MangSym = OutContext.getOrCreateSymbol(GVSym->getName() + Twine("$tlv$init")); @@ -583,9 +527,7 @@ void AsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { OutStreamer->SwitchSection(TheSection); - // emutls_t.* symbols are only used in the current compilation unit. - if (!IsEmuTLSVar) - EmitLinkage(GV, EmittedInitSym); + EmitLinkage(GV, EmittedInitSym); EmitAlignment(AlignLog, GV); OutStreamer->EmitLabel(EmittedInitSym); diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index a078c3c707a..358a49f55f5 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -53,6 +53,7 @@ add_llvm_library(LLVMCodeGen LiveStackAnalysis.cpp LiveVariables.cpp LocalStackSlotAllocation.cpp + LowerEmuTLS.cpp MachineBasicBlock.cpp MachineBlockFrequencyInfo.cpp MachineBlockPlacement.cpp diff --git a/llvm/lib/CodeGen/LLVMTargetMachine.cpp b/llvm/lib/CodeGen/LLVMTargetMachine.cpp index 1c27377feee..16357de83cf 100644 --- a/llvm/lib/CodeGen/LLVMTargetMachine.cpp +++ b/llvm/lib/CodeGen/LLVMTargetMachine.cpp @@ -94,6 +94,10 @@ addPassesToGenerateCode(LLVMTargetMachine *TM, PassManagerBase &PM, AnalysisID StartAfter, AnalysisID StopAfter, MachineFunctionInitializer *MFInitializer = nullptr) { + // When in emulated TLS mode, add the LowerEmuTLS pass. + if (TM->Options.EmulatedTLS) + PM.add(createLowerEmuTLSPass(TM)); + // Add internal analysis passes from the target machine. PM.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); diff --git a/llvm/lib/CodeGen/LowerEmuTLS.cpp b/llvm/lib/CodeGen/LowerEmuTLS.cpp new file mode 100644 index 00000000000..58655dc043d --- /dev/null +++ b/llvm/lib/CodeGen/LowerEmuTLS.cpp @@ -0,0 +1,159 @@ +//===- LowerEmuTLS.cpp - Add __emutls_[vt].* variables --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This transformation is required for targets depending on libgcc style +// emulated thread local storage variables. For every defined TLS variable xyz, +// an __emutls_v.xyz is generated. If there is non-zero initialized value +// an __emutls_t.xyz is also generated. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Scalar.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Target/TargetLowering.h" + +using namespace llvm; + +#define DEBUG_TYPE "loweremutls" + +namespace { + +class LowerEmuTLS : public ModulePass { + const TargetMachine *TM; +public: + static char ID; // Pass identification, replacement for typeid + explicit LowerEmuTLS() : ModulePass(ID), TM(nullptr) { } + explicit LowerEmuTLS(const TargetMachine *TM) + : ModulePass(ID), TM(TM) { + initializeLowerEmuTLSPass(*PassRegistry::getPassRegistry()); + } + bool runOnModule(Module &M) override; +private: + bool addEmuTlsVar(Module &M, const GlobalVariable *GV); + static void copyLinkageVisibility(Module &M, + const GlobalVariable *from, + GlobalVariable *to) { + to->setLinkage(from->getLinkage()); + to->setVisibility(from->getVisibility()); + if (from->hasComdat()) { + to->setComdat(M.getOrInsertComdat(to->getName())); + to->getComdat()->setSelectionKind(from->getComdat()->getSelectionKind()); + } + } +}; +} + +char LowerEmuTLS::ID = 0; + +INITIALIZE_PASS(LowerEmuTLS, "loweremutls", + "Add __emutls_[vt]. variables for emultated TLS model", + false, false) + +ModulePass *llvm::createLowerEmuTLSPass(const TargetMachine *TM) { + return new LowerEmuTLS(TM); +} + +bool LowerEmuTLS::runOnModule(Module &M) { + if (!TM || !TM->Options.EmulatedTLS) + return false; + + bool Changed = false; + SmallVector<const GlobalVariable*, 8> TlsVars; + for (const auto &G : M.globals()) { + if (G.isThreadLocal()) + TlsVars.append({&G}); + } + for (const auto G : TlsVars) + Changed |= addEmuTlsVar(M, G); + return Changed; +} + +bool LowerEmuTLS::addEmuTlsVar(Module &M, const GlobalVariable *GV) { + LLVMContext &C = M.getContext(); + PointerType *VoidPtrType = Type::getInt8PtrTy(C); + + std::string EmuTlsVarName = ("__emutls_v." + GV->getName()).str(); + GlobalVariable *EmuTlsVar = M.getNamedGlobal(EmuTlsVarName); + if (EmuTlsVar) + return false; // It has been added before. + + const DataLayout &DL = M.getDataLayout(); + Constant *NullPtr = ConstantPointerNull::get(VoidPtrType); + + // Get non-zero initializer from GV's initializer. + const Constant *InitValue = nullptr; + if (GV->hasInitializer()) { + InitValue = GV->getInitializer(); + const ConstantInt *InitIntValue = dyn_cast<ConstantInt>(InitValue); + // When GV's init value is all 0, omit the EmuTlsTmplVar and let + // the emutls library function to reset newly allocated TLS variables. + if (isa<ConstantAggregateZero>(InitValue) || + (InitIntValue && InitIntValue->isZero())) + InitValue = nullptr; + } + + // Create the __emutls_v. symbol, whose type has 4 fields: + // word size; // size of GV in bytes + // word align; // alignment of GV + // void *ptr; // initialized to 0; set at run time per thread. + // void *templ; // 0 or point to __emutls_t.* + // sizeof(word) should be the same as sizeof(void*) on target. + IntegerType *WordType = DL.getIntPtrType(C); + PointerType *InitPtrType = InitValue ? + PointerType::getUnqual(InitValue->getType()) : VoidPtrType; + Type *ElementTypes[4] = {WordType, WordType, VoidPtrType, InitPtrType}; + ArrayRef<Type*> ElementTypeArray(ElementTypes, 4); + StructType *EmuTlsVarType = StructType::create(ElementTypeArray); + EmuTlsVar = cast<GlobalVariable>( + M.getOrInsertGlobal(EmuTlsVarName, EmuTlsVarType)); + copyLinkageVisibility(M, GV, EmuTlsVar); + + // Define "__emutls_t.*" and "__emutls_v.*" only if GV is defined. + if (!GV->hasInitializer()) + return true; + + Type *GVType = GV->getValueType(); + unsigned GVAlignment = GV->getAlignment(); + if (!GVAlignment) { + // When LLVM IL declares a variable without alignment, use + // the ABI default alignment for the type. + GVAlignment = DL.getABITypeAlignment(GVType); + } + + // Define "__emutls_t.*" if there is InitValue + GlobalVariable *EmuTlsTmplVar = nullptr; + if (InitValue) { + std::string EmuTlsTmplName = ("__emutls_t." + GV->getName()).str(); + EmuTlsTmplVar = dyn_cast_or_null<GlobalVariable>( + M.getOrInsertGlobal(EmuTlsTmplName, GVType)); + assert(EmuTlsTmplVar && "Failed to create emualted TLS initializer"); + EmuTlsTmplVar->setConstant(true); + EmuTlsTmplVar->setInitializer(const_cast<Constant*>(InitValue)); + EmuTlsTmplVar->setAlignment(GVAlignment); + copyLinkageVisibility(M, GV, EmuTlsTmplVar); + } + + // Define "__emutls_v.*" with initializer and alignment. + Constant *ElementValues[4] = { + ConstantInt::get(WordType, DL.getTypeStoreSize(GVType)), + ConstantInt::get(WordType, GVAlignment), + NullPtr, EmuTlsTmplVar ? EmuTlsTmplVar : NullPtr + }; + ArrayRef<Constant*> ElementValueArray(ElementValues, 4); + EmuTlsVar->setInitializer( + ConstantStruct::get(EmuTlsVarType, ElementValueArray)); + unsigned MaxAlignment = std::max( + DL.getABITypeAlignment(WordType), + DL.getABITypeAlignment(VoidPtrType)); + EmuTlsVar->setAlignment(MaxAlignment); + return true; +} diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index c64d882d69a..df0453394e8 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -3057,9 +3057,7 @@ SDValue TargetLowering::LowerToTLSEmulatedModel(const GlobalAddressSDNode *GA, 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)); + assert(EmuTlsVar && "Cannot find EmuTlsVar "); Entry.Node = DAG.getGlobalAddress(EmuTlsVar, dl, PtrVT); Entry.Ty = VoidPtrType; Args.push_back(Entry); |