18bbea9cdSRichard Osborne //===-- XCoreLowerThreadLocal - Lower thread local variables --------------===//
28bbea9cdSRichard Osborne //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68bbea9cdSRichard Osborne //
78bbea9cdSRichard Osborne //===----------------------------------------------------------------------===//
88bbea9cdSRichard Osborne ///
98bbea9cdSRichard Osborne /// \file
105f8f34e4SAdrian Prantl /// This file contains a pass that lowers thread local variables on the
118bbea9cdSRichard Osborne ///        XCore.
128bbea9cdSRichard Osborne ///
138bbea9cdSRichard Osborne //===----------------------------------------------------------------------===//
148bbea9cdSRichard Osborne 
158bbea9cdSRichard Osborne #include "XCore.h"
168bbea9cdSRichard Osborne #include "llvm/IR/Constants.h"
178bbea9cdSRichard Osborne #include "llvm/IR/DerivedTypes.h"
188bbea9cdSRichard Osborne #include "llvm/IR/GlobalVariable.h"
198bbea9cdSRichard Osborne #include "llvm/IR/IRBuilder.h"
208a8cd2baSChandler Carruth #include "llvm/IR/Intrinsics.h"
215d986953SReid Kleckner #include "llvm/IR/IntrinsicsXCore.h"
228bbea9cdSRichard Osborne #include "llvm/IR/Module.h"
2364396b06SChandler Carruth #include "llvm/IR/NoFolder.h"
244220e9c1SChandler Carruth #include "llvm/IR/ValueHandle.h"
258bbea9cdSRichard Osborne #include "llvm/Pass.h"
268bbea9cdSRichard Osborne #include "llvm/Support/CommandLine.h"
279715375cSRobert Lytton #include "llvm/Transforms/Utils/BasicBlockUtils.h"
288bbea9cdSRichard Osborne 
298bbea9cdSRichard Osborne #define DEBUG_TYPE "xcore-lower-thread-local"
308bbea9cdSRichard Osborne 
318bbea9cdSRichard Osborne using namespace llvm;
328bbea9cdSRichard Osborne 
338bbea9cdSRichard Osborne static cl::opt<unsigned> MaxThreads(
348bbea9cdSRichard Osborne   "xcore-max-threads", cl::Optional,
358bbea9cdSRichard Osborne   cl::desc("Maximum number of threads (for emulation thread-local storage)"),
368bbea9cdSRichard Osborne   cl::Hidden, cl::value_desc("number"), cl::init(8));
378bbea9cdSRichard Osborne 
388bbea9cdSRichard Osborne namespace {
398bbea9cdSRichard Osborne   /// Lowers thread local variables on the XCore. Each thread local variable is
408bbea9cdSRichard Osborne   /// expanded to an array of n elements indexed by the thread ID where n is the
418bbea9cdSRichard Osborne   /// fixed number hardware threads supported by the device.
428bbea9cdSRichard Osborne   struct XCoreLowerThreadLocal : public ModulePass {
438bbea9cdSRichard Osborne     static char ID;
448bbea9cdSRichard Osborne 
XCoreLowerThreadLocal__anon9633f3530111::XCoreLowerThreadLocal458bbea9cdSRichard Osborne     XCoreLowerThreadLocal() : ModulePass(ID) {
468bbea9cdSRichard Osborne       initializeXCoreLowerThreadLocalPass(*PassRegistry::getPassRegistry());
478bbea9cdSRichard Osborne     }
488bbea9cdSRichard Osborne 
498bbea9cdSRichard Osborne     bool lowerGlobal(GlobalVariable *GV);
508bbea9cdSRichard Osborne 
5160879a3cSCraig Topper     bool runOnModule(Module &M) override;
528bbea9cdSRichard Osborne   };
53f00654e3SAlexander Kornienko }
548bbea9cdSRichard Osborne 
558bbea9cdSRichard Osborne char XCoreLowerThreadLocal::ID = 0;
568bbea9cdSRichard Osborne 
578bbea9cdSRichard Osborne INITIALIZE_PASS(XCoreLowerThreadLocal, "xcore-lower-thread-local",
588bbea9cdSRichard Osborne                 "Lower thread local variables", false, false)
598bbea9cdSRichard Osborne 
createXCoreLowerThreadLocalPass()608bbea9cdSRichard Osborne ModulePass *llvm::createXCoreLowerThreadLocalPass() {
618bbea9cdSRichard Osborne   return new XCoreLowerThreadLocal();
628bbea9cdSRichard Osborne }
638bbea9cdSRichard Osborne 
createLoweredType(Type * OriginalType)648bbea9cdSRichard Osborne static ArrayType *createLoweredType(Type *OriginalType) {
658bbea9cdSRichard Osborne   return ArrayType::get(OriginalType, MaxThreads);
668bbea9cdSRichard Osborne }
678bbea9cdSRichard Osborne 
688bbea9cdSRichard Osborne static Constant *
createLoweredInitializer(ArrayType * NewType,Constant * OriginalInitializer)698bbea9cdSRichard Osborne createLoweredInitializer(ArrayType *NewType, Constant *OriginalInitializer) {
708bbea9cdSRichard Osborne   SmallVector<Constant *, 8> Elements(MaxThreads);
718bbea9cdSRichard Osborne   for (unsigned i = 0; i != MaxThreads; ++i) {
728bbea9cdSRichard Osborne     Elements[i] = OriginalInitializer;
738bbea9cdSRichard Osborne   }
748bbea9cdSRichard Osborne   return ConstantArray::get(NewType, Elements);
758bbea9cdSRichard Osborne }
768bbea9cdSRichard Osborne 
778bbea9cdSRichard Osborne 
replaceConstantExprOp(ConstantExpr * CE,Pass * P)789715375cSRobert Lytton static bool replaceConstantExprOp(ConstantExpr *CE, Pass *P) {
793d3194bfSRobert Lytton   do {
800e219b64SKazu Hirata     SmallVector<WeakTrackingVH, 8> WUsers(CE->users());
810cac726aSFangrui Song     llvm::sort(WUsers);
829715375cSRobert Lytton     WUsers.erase(std::unique(WUsers.begin(), WUsers.end()), WUsers.end());
833d3194bfSRobert Lytton     while (!WUsers.empty())
84e6bca0eeSSanjoy Das       if (WeakTrackingVH WU = WUsers.pop_back_val()) {
859715375cSRobert Lytton         if (PHINode *PN = dyn_cast<PHINode>(WU)) {
869715375cSRobert Lytton           for (int I = 0, E = PN->getNumIncomingValues(); I < E; ++I)
879715375cSRobert Lytton             if (PN->getIncomingValue(I) == CE) {
889715375cSRobert Lytton               BasicBlock *PredBB = PN->getIncomingBlock(I);
899715375cSRobert Lytton               if (PredBB->getTerminator()->getNumSuccessors() > 1)
90d450056cSChandler Carruth                 PredBB = SplitEdge(PredBB, PN->getParent());
919715375cSRobert Lytton               Instruction *InsertPos = PredBB->getTerminator();
92*1b758925SJay Foad               Instruction *NewInst = CE->getAsInstruction(InsertPos);
939715375cSRobert Lytton               PN->setOperand(I, NewInst);
949715375cSRobert Lytton             }
959715375cSRobert Lytton         } else if (Instruction *Instr = dyn_cast<Instruction>(WU)) {
96*1b758925SJay Foad           Instruction *NewInst = CE->getAsInstruction(Instr);
973d3194bfSRobert Lytton           Instr->replaceUsesOfWith(CE, NewInst);
983d3194bfSRobert Lytton         } else {
993d3194bfSRobert Lytton           ConstantExpr *CExpr = dyn_cast<ConstantExpr>(WU);
1009715375cSRobert Lytton           if (!CExpr || !replaceConstantExprOp(CExpr, P))
1018bbea9cdSRichard Osborne             return false;
1028bbea9cdSRichard Osborne         }
1033d3194bfSRobert Lytton       }
104cb402911SAlp Toker   } while (CE->hasNUsesOrMore(1)); // We need to check because a recursive
105*1b758925SJay Foad   // sibling may have used 'CE' when getAsInstruction was called.
1063d3194bfSRobert Lytton   CE->destroyConstant();
1073d3194bfSRobert Lytton   return true;
1083d3194bfSRobert Lytton }
1093d3194bfSRobert Lytton 
rewriteNonInstructionUses(GlobalVariable * GV,Pass * P)1109715375cSRobert Lytton static bool rewriteNonInstructionUses(GlobalVariable *GV, Pass *P) {
111e6bca0eeSSanjoy Das   SmallVector<WeakTrackingVH, 8> WUsers;
112cdf47884SChandler Carruth   for (User *U : GV->users())
113cdf47884SChandler Carruth     if (!isa<Instruction>(U))
114e6bca0eeSSanjoy Das       WUsers.push_back(WeakTrackingVH(U));
1153d3194bfSRobert Lytton   while (!WUsers.empty())
116e6bca0eeSSanjoy Das     if (WeakTrackingVH WU = WUsers.pop_back_val()) {
1173d3194bfSRobert Lytton       ConstantExpr *CE = dyn_cast<ConstantExpr>(WU);
1189715375cSRobert Lytton       if (!CE || !replaceConstantExprOp(CE, P))
1193d3194bfSRobert Lytton         return false;
1203d3194bfSRobert Lytton     }
1213d3194bfSRobert Lytton   return true;
1223d3194bfSRobert Lytton }
1238bbea9cdSRichard Osborne 
isZeroLengthArray(Type * Ty)1248bbea9cdSRichard Osborne static bool isZeroLengthArray(Type *Ty) {
1258bbea9cdSRichard Osborne   ArrayType *AT = dyn_cast<ArrayType>(Ty);
1268bbea9cdSRichard Osborne   return AT && (AT->getNumElements() == 0);
1278bbea9cdSRichard Osborne }
1288bbea9cdSRichard Osborne 
lowerGlobal(GlobalVariable * GV)1298bbea9cdSRichard Osborne bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable *GV) {
1308bbea9cdSRichard Osborne   Module *M = GV->getParent();
1318bbea9cdSRichard Osborne   if (!GV->isThreadLocal())
1328bbea9cdSRichard Osborne     return false;
1338bbea9cdSRichard Osborne 
1348bbea9cdSRichard Osborne   // Skip globals that we can't lower and leave it for the backend to error.
1359715375cSRobert Lytton   if (!rewriteNonInstructionUses(GV, this) ||
1368bbea9cdSRichard Osborne       !GV->getType()->isSized() || isZeroLengthArray(GV->getType()))
1378bbea9cdSRichard Osborne     return false;
1388bbea9cdSRichard Osborne 
1398bbea9cdSRichard Osborne   // Create replacement global.
1405f6eaac6SManuel Jacob   ArrayType *NewType = createLoweredType(GV->getValueType());
141062a2baeSCraig Topper   Constant *NewInitializer = nullptr;
1423d3194bfSRobert Lytton   if (GV->hasInitializer())
1433d3194bfSRobert Lytton     NewInitializer = createLoweredInitializer(NewType,
1448bbea9cdSRichard Osborne                                               GV->getInitializer());
1458bbea9cdSRichard Osborne   GlobalVariable *NewGV =
1468bbea9cdSRichard Osborne     new GlobalVariable(*M, NewType, GV->isConstant(), GV->getLinkage(),
147062a2baeSCraig Topper                        NewInitializer, "", nullptr,
148062a2baeSCraig Topper                        GlobalVariable::NotThreadLocal,
1498bbea9cdSRichard Osborne                        GV->getType()->getAddressSpace(),
1508bbea9cdSRichard Osborne                        GV->isExternallyInitialized());
1518bbea9cdSRichard Osborne 
1528bbea9cdSRichard Osborne   // Update uses.
1530e219b64SKazu Hirata   SmallVector<User *, 16> Users(GV->users());
1548bbea9cdSRichard Osborne   for (unsigned I = 0, E = Users.size(); I != E; ++I) {
1558bbea9cdSRichard Osborne     User *U = Users[I];
1568bbea9cdSRichard Osborne     Instruction *Inst = cast<Instruction>(U);
1578bbea9cdSRichard Osborne     IRBuilder<> Builder(Inst);
1588bbea9cdSRichard Osborne     Function *GetID = Intrinsic::getDeclaration(GV->getParent(),
1598bbea9cdSRichard Osborne                                                 Intrinsic::xcore_getid);
160ff6409d0SDavid Blaikie     Value *ThreadID = Builder.CreateCall(GetID, {});
1613bc1edf9SBenjamin Kramer     Value *Addr = Builder.CreateInBoundsGEP(NewGV->getValueType(), NewGV,
1623bc1edf9SBenjamin Kramer                                             {Builder.getInt64(0), ThreadID});
1638bbea9cdSRichard Osborne     U->replaceUsesOfWith(GV, Addr);
1648bbea9cdSRichard Osborne   }
1658bbea9cdSRichard Osborne 
1668bbea9cdSRichard Osborne   // Remove old global.
1678bbea9cdSRichard Osborne   NewGV->takeName(GV);
1688bbea9cdSRichard Osborne   GV->eraseFromParent();
1698bbea9cdSRichard Osborne   return true;
1708bbea9cdSRichard Osborne }
1718bbea9cdSRichard Osborne 
runOnModule(Module & M)1728bbea9cdSRichard Osborne bool XCoreLowerThreadLocal::runOnModule(Module &M) {
1738bbea9cdSRichard Osborne   // Find thread local globals.
1748bbea9cdSRichard Osborne   bool MadeChange = false;
1758bbea9cdSRichard Osborne   SmallVector<GlobalVariable *, 16> ThreadLocalGlobals;
1760ce253d3SDuncan P. N. Exon Smith   for (GlobalVariable &GV : M.globals())
1770ce253d3SDuncan P. N. Exon Smith     if (GV.isThreadLocal())
1780ce253d3SDuncan P. N. Exon Smith       ThreadLocalGlobals.push_back(&GV);
1798bbea9cdSRichard Osborne   for (unsigned I = 0, E = ThreadLocalGlobals.size(); I != E; ++I) {
1808bbea9cdSRichard Osborne     MadeChange |= lowerGlobal(ThreadLocalGlobals[I]);
1818bbea9cdSRichard Osborne   }
1828bbea9cdSRichard Osborne   return MadeChange;
1838bbea9cdSRichard Osborne }
184