13ca95b02SDimitry Andric //===- LowerEmuTLS.cpp - Add __emutls_[vt].* variables --------------------===//
23ca95b02SDimitry Andric //
33ca95b02SDimitry Andric //                     The LLVM Compiler Infrastructure
43ca95b02SDimitry Andric //
53ca95b02SDimitry Andric // This file is distributed under the University of Illinois Open Source
63ca95b02SDimitry Andric // License. See LICENSE.TXT for details.
73ca95b02SDimitry Andric //
83ca95b02SDimitry Andric //===----------------------------------------------------------------------===//
93ca95b02SDimitry Andric //
103ca95b02SDimitry Andric // This transformation is required for targets depending on libgcc style
113ca95b02SDimitry Andric // emulated thread local storage variables. For every defined TLS variable xyz,
123ca95b02SDimitry Andric // an __emutls_v.xyz is generated. If there is non-zero initialized value
133ca95b02SDimitry Andric // an __emutls_t.xyz is also generated.
143ca95b02SDimitry Andric //
153ca95b02SDimitry Andric //===----------------------------------------------------------------------===//
163ca95b02SDimitry Andric 
173ca95b02SDimitry Andric #include "llvm/ADT/SmallVector.h"
183ca95b02SDimitry Andric #include "llvm/CodeGen/Passes.h"
192cab237bSDimitry Andric #include "llvm/CodeGen/TargetLowering.h"
20d8866befSDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
213ca95b02SDimitry Andric #include "llvm/IR/LLVMContext.h"
223ca95b02SDimitry Andric #include "llvm/IR/Module.h"
233ca95b02SDimitry Andric #include "llvm/Pass.h"
243ca95b02SDimitry Andric 
253ca95b02SDimitry Andric using namespace llvm;
263ca95b02SDimitry Andric 
273ca95b02SDimitry Andric #define DEBUG_TYPE "loweremutls"
283ca95b02SDimitry Andric 
293ca95b02SDimitry Andric namespace {
303ca95b02SDimitry Andric 
313ca95b02SDimitry Andric class LowerEmuTLS : public ModulePass {
323ca95b02SDimitry Andric public:
333ca95b02SDimitry Andric   static char ID; // Pass identification, replacement for typeid
LowerEmuTLS()34d8866befSDimitry Andric   LowerEmuTLS() : ModulePass(ID) {
353ca95b02SDimitry Andric     initializeLowerEmuTLSPass(*PassRegistry::getPassRegistry());
363ca95b02SDimitry Andric   }
37d8866befSDimitry Andric 
383ca95b02SDimitry Andric   bool runOnModule(Module &M) override;
393ca95b02SDimitry Andric private:
403ca95b02SDimitry Andric   bool addEmuTlsVar(Module &M, const GlobalVariable *GV);
copyLinkageVisibility(Module & M,const GlobalVariable * from,GlobalVariable * to)413ca95b02SDimitry Andric   static void copyLinkageVisibility(Module &M,
423ca95b02SDimitry Andric                                     const GlobalVariable *from,
433ca95b02SDimitry Andric                                     GlobalVariable *to) {
443ca95b02SDimitry Andric     to->setLinkage(from->getLinkage());
453ca95b02SDimitry Andric     to->setVisibility(from->getVisibility());
463ca95b02SDimitry Andric     if (from->hasComdat()) {
473ca95b02SDimitry Andric       to->setComdat(M.getOrInsertComdat(to->getName()));
483ca95b02SDimitry Andric       to->getComdat()->setSelectionKind(from->getComdat()->getSelectionKind());
493ca95b02SDimitry Andric     }
503ca95b02SDimitry Andric   }
513ca95b02SDimitry Andric };
523ca95b02SDimitry Andric }
533ca95b02SDimitry Andric 
543ca95b02SDimitry Andric char LowerEmuTLS::ID = 0;
553ca95b02SDimitry Andric 
56302affcbSDimitry Andric INITIALIZE_PASS(LowerEmuTLS, DEBUG_TYPE,
57d8866befSDimitry Andric                 "Add __emutls_[vt]. variables for emultated TLS model", false,
58d8866befSDimitry Andric                 false)
593ca95b02SDimitry Andric 
createLowerEmuTLSPass()60d8866befSDimitry Andric ModulePass *llvm::createLowerEmuTLSPass() { return new LowerEmuTLS(); }
613ca95b02SDimitry Andric 
runOnModule(Module & M)623ca95b02SDimitry Andric bool LowerEmuTLS::runOnModule(Module &M) {
633ca95b02SDimitry Andric   if (skipModule(M))
643ca95b02SDimitry Andric     return false;
653ca95b02SDimitry Andric 
66d8866befSDimitry Andric   auto *TPC = getAnalysisIfAvailable<TargetPassConfig>();
67d8866befSDimitry Andric   if (!TPC)
68d8866befSDimitry Andric     return false;
69d8866befSDimitry Andric 
70d8866befSDimitry Andric   auto &TM = TPC->getTM<TargetMachine>();
71*4ba319b5SDimitry Andric   if (!TM.useEmulatedTLS())
723ca95b02SDimitry Andric     return false;
733ca95b02SDimitry Andric 
743ca95b02SDimitry Andric   bool Changed = false;
753ca95b02SDimitry Andric   SmallVector<const GlobalVariable*, 8> TlsVars;
763ca95b02SDimitry Andric   for (const auto &G : M.globals()) {
773ca95b02SDimitry Andric     if (G.isThreadLocal())
783ca95b02SDimitry Andric       TlsVars.append({&G});
793ca95b02SDimitry Andric   }
803ca95b02SDimitry Andric   for (const auto G : TlsVars)
813ca95b02SDimitry Andric     Changed |= addEmuTlsVar(M, G);
823ca95b02SDimitry Andric   return Changed;
833ca95b02SDimitry Andric }
843ca95b02SDimitry Andric 
addEmuTlsVar(Module & M,const GlobalVariable * GV)853ca95b02SDimitry Andric bool LowerEmuTLS::addEmuTlsVar(Module &M, const GlobalVariable *GV) {
863ca95b02SDimitry Andric   LLVMContext &C = M.getContext();
873ca95b02SDimitry Andric   PointerType *VoidPtrType = Type::getInt8PtrTy(C);
883ca95b02SDimitry Andric 
893ca95b02SDimitry Andric   std::string EmuTlsVarName = ("__emutls_v." + GV->getName()).str();
903ca95b02SDimitry Andric   GlobalVariable *EmuTlsVar = M.getNamedGlobal(EmuTlsVarName);
913ca95b02SDimitry Andric   if (EmuTlsVar)
923ca95b02SDimitry Andric     return false;  // It has been added before.
933ca95b02SDimitry Andric 
943ca95b02SDimitry Andric   const DataLayout &DL = M.getDataLayout();
953ca95b02SDimitry Andric   Constant *NullPtr = ConstantPointerNull::get(VoidPtrType);
963ca95b02SDimitry Andric 
973ca95b02SDimitry Andric   // Get non-zero initializer from GV's initializer.
983ca95b02SDimitry Andric   const Constant *InitValue = nullptr;
993ca95b02SDimitry Andric   if (GV->hasInitializer()) {
1003ca95b02SDimitry Andric     InitValue = GV->getInitializer();
1013ca95b02SDimitry Andric     const ConstantInt *InitIntValue = dyn_cast<ConstantInt>(InitValue);
1023ca95b02SDimitry Andric     // When GV's init value is all 0, omit the EmuTlsTmplVar and let
1033ca95b02SDimitry Andric     // the emutls library function to reset newly allocated TLS variables.
1043ca95b02SDimitry Andric     if (isa<ConstantAggregateZero>(InitValue) ||
1053ca95b02SDimitry Andric         (InitIntValue && InitIntValue->isZero()))
1063ca95b02SDimitry Andric       InitValue = nullptr;
1073ca95b02SDimitry Andric   }
1083ca95b02SDimitry Andric 
1093ca95b02SDimitry Andric   // Create the __emutls_v. symbol, whose type has 4 fields:
1103ca95b02SDimitry Andric   //     word size;   // size of GV in bytes
1113ca95b02SDimitry Andric   //     word align;  // alignment of GV
1123ca95b02SDimitry Andric   //     void *ptr;   // initialized to 0; set at run time per thread.
1133ca95b02SDimitry Andric   //     void *templ; // 0 or point to __emutls_t.*
1143ca95b02SDimitry Andric   // sizeof(word) should be the same as sizeof(void*) on target.
1153ca95b02SDimitry Andric   IntegerType *WordType = DL.getIntPtrType(C);
1163ca95b02SDimitry Andric   PointerType *InitPtrType = InitValue ?
1173ca95b02SDimitry Andric       PointerType::getUnqual(InitValue->getType()) : VoidPtrType;
1183ca95b02SDimitry Andric   Type *ElementTypes[4] = {WordType, WordType, VoidPtrType, InitPtrType};
1193ca95b02SDimitry Andric   ArrayRef<Type*> ElementTypeArray(ElementTypes, 4);
1203ca95b02SDimitry Andric   StructType *EmuTlsVarType = StructType::create(ElementTypeArray);
1213ca95b02SDimitry Andric   EmuTlsVar = cast<GlobalVariable>(
1223ca95b02SDimitry Andric       M.getOrInsertGlobal(EmuTlsVarName, EmuTlsVarType));
1233ca95b02SDimitry Andric   copyLinkageVisibility(M, GV, EmuTlsVar);
1243ca95b02SDimitry Andric 
1253ca95b02SDimitry Andric   // Define "__emutls_t.*" and "__emutls_v.*" only if GV is defined.
1263ca95b02SDimitry Andric   if (!GV->hasInitializer())
1273ca95b02SDimitry Andric     return true;
1283ca95b02SDimitry Andric 
1293ca95b02SDimitry Andric   Type *GVType = GV->getValueType();
1303ca95b02SDimitry Andric   unsigned GVAlignment = GV->getAlignment();
1313ca95b02SDimitry Andric   if (!GVAlignment) {
1323ca95b02SDimitry Andric     // When LLVM IL declares a variable without alignment, use
1333ca95b02SDimitry Andric     // the ABI default alignment for the type.
1343ca95b02SDimitry Andric     GVAlignment = DL.getABITypeAlignment(GVType);
1353ca95b02SDimitry Andric   }
1363ca95b02SDimitry Andric 
1373ca95b02SDimitry Andric   // Define "__emutls_t.*" if there is InitValue
1383ca95b02SDimitry Andric   GlobalVariable *EmuTlsTmplVar = nullptr;
1393ca95b02SDimitry Andric   if (InitValue) {
1403ca95b02SDimitry Andric     std::string EmuTlsTmplName = ("__emutls_t." + GV->getName()).str();
1413ca95b02SDimitry Andric     EmuTlsTmplVar = dyn_cast_or_null<GlobalVariable>(
1423ca95b02SDimitry Andric         M.getOrInsertGlobal(EmuTlsTmplName, GVType));
1433ca95b02SDimitry Andric     assert(EmuTlsTmplVar && "Failed to create emualted TLS initializer");
1443ca95b02SDimitry Andric     EmuTlsTmplVar->setConstant(true);
1453ca95b02SDimitry Andric     EmuTlsTmplVar->setInitializer(const_cast<Constant*>(InitValue));
1463ca95b02SDimitry Andric     EmuTlsTmplVar->setAlignment(GVAlignment);
1473ca95b02SDimitry Andric     copyLinkageVisibility(M, GV, EmuTlsTmplVar);
1483ca95b02SDimitry Andric   }
1493ca95b02SDimitry Andric 
1503ca95b02SDimitry Andric   // Define "__emutls_v.*" with initializer and alignment.
1513ca95b02SDimitry Andric   Constant *ElementValues[4] = {
1523ca95b02SDimitry Andric       ConstantInt::get(WordType, DL.getTypeStoreSize(GVType)),
1533ca95b02SDimitry Andric       ConstantInt::get(WordType, GVAlignment),
1543ca95b02SDimitry Andric       NullPtr, EmuTlsTmplVar ? EmuTlsTmplVar : NullPtr
1553ca95b02SDimitry Andric   };
1563ca95b02SDimitry Andric   ArrayRef<Constant*> ElementValueArray(ElementValues, 4);
1573ca95b02SDimitry Andric   EmuTlsVar->setInitializer(
1583ca95b02SDimitry Andric       ConstantStruct::get(EmuTlsVarType, ElementValueArray));
1593ca95b02SDimitry Andric   unsigned MaxAlignment = std::max(
1603ca95b02SDimitry Andric       DL.getABITypeAlignment(WordType),
1613ca95b02SDimitry Andric       DL.getABITypeAlignment(VoidPtrType));
1623ca95b02SDimitry Andric   EmuTlsVar->setAlignment(MaxAlignment);
1633ca95b02SDimitry Andric   return true;
1643ca95b02SDimitry Andric }
165