1 //===-- XCoreLowerThreadLocal - Lower thread local variables --------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file contains a pass that lowers thread local variables on the 11 /// XCore. 12 /// 13 //===----------------------------------------------------------------------===// 14 15 #include "XCore.h" 16 #include "llvm/IR/Constants.h" 17 #include "llvm/IR/DerivedTypes.h" 18 #include "llvm/IR/GlobalVariable.h" 19 #include "llvm/IR/IRBuilder.h" 20 #include "llvm/IR/Intrinsics.h" 21 #include "llvm/IR/IntrinsicsXCore.h" 22 #include "llvm/IR/Module.h" 23 #include "llvm/IR/ValueHandle.h" 24 #include "llvm/Pass.h" 25 #include "llvm/Support/CommandLine.h" 26 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 27 28 #define DEBUG_TYPE "xcore-lower-thread-local" 29 30 using namespace llvm; 31 32 static cl::opt<unsigned> MaxThreads( 33 "xcore-max-threads", cl::Optional, 34 cl::desc("Maximum number of threads (for emulation thread-local storage)"), 35 cl::Hidden, cl::value_desc("number"), cl::init(8)); 36 37 namespace { 38 /// Lowers thread local variables on the XCore. Each thread local variable is 39 /// expanded to an array of n elements indexed by the thread ID where n is the 40 /// fixed number hardware threads supported by the device. 41 struct XCoreLowerThreadLocal : public ModulePass { 42 static char ID; 43 44 XCoreLowerThreadLocal() : ModulePass(ID) { 45 initializeXCoreLowerThreadLocalPass(*PassRegistry::getPassRegistry()); 46 } 47 48 bool lowerGlobal(GlobalVariable *GV); 49 50 bool runOnModule(Module &M) override; 51 }; 52 } 53 54 char XCoreLowerThreadLocal::ID = 0; 55 56 INITIALIZE_PASS(XCoreLowerThreadLocal, "xcore-lower-thread-local", 57 "Lower thread local variables", false, false) 58 59 ModulePass *llvm::createXCoreLowerThreadLocalPass() { 60 return new XCoreLowerThreadLocal(); 61 } 62 63 static ArrayType *createLoweredType(Type *OriginalType) { 64 return ArrayType::get(OriginalType, MaxThreads); 65 } 66 67 static Constant * 68 createLoweredInitializer(ArrayType *NewType, Constant *OriginalInitializer) { 69 SmallVector<Constant *, 8> Elements(MaxThreads); 70 for (unsigned i = 0; i != MaxThreads; ++i) { 71 Elements[i] = OriginalInitializer; 72 } 73 return ConstantArray::get(NewType, Elements); 74 } 75 76 77 static bool replaceConstantExprOp(ConstantExpr *CE, Pass *P) { 78 do { 79 SmallVector<WeakTrackingVH, 8> WUsers(CE->users()); 80 llvm::sort(WUsers); 81 WUsers.erase(llvm::unique(WUsers), WUsers.end()); 82 while (!WUsers.empty()) 83 if (WeakTrackingVH WU = WUsers.pop_back_val()) { 84 if (PHINode *PN = dyn_cast<PHINode>(WU)) { 85 for (int I = 0, E = PN->getNumIncomingValues(); I < E; ++I) 86 if (PN->getIncomingValue(I) == CE) { 87 BasicBlock *PredBB = PN->getIncomingBlock(I); 88 if (PredBB->getTerminator()->getNumSuccessors() > 1) 89 PredBB = SplitEdge(PredBB, PN->getParent()); 90 BasicBlock::iterator InsertPos = 91 PredBB->getTerminator()->getIterator(); 92 Instruction *NewInst = CE->getAsInstruction(); 93 NewInst->insertBefore(*PredBB, InsertPos); 94 PN->setOperand(I, NewInst); 95 } 96 } else if (Instruction *Instr = dyn_cast<Instruction>(WU)) { 97 Instruction *NewInst = CE->getAsInstruction(); 98 NewInst->insertBefore(*Instr->getParent(), Instr->getIterator()); 99 Instr->replaceUsesOfWith(CE, NewInst); 100 } else { 101 ConstantExpr *CExpr = dyn_cast<ConstantExpr>(WU); 102 if (!CExpr || !replaceConstantExprOp(CExpr, P)) 103 return false; 104 } 105 } 106 } while (CE->hasNUsesOrMore(1)); // We need to check because a recursive 107 // sibling may have used 'CE' when getAsInstruction was called. 108 CE->destroyConstant(); 109 return true; 110 } 111 112 static bool rewriteNonInstructionUses(GlobalVariable *GV, Pass *P) { 113 SmallVector<WeakTrackingVH, 8> WUsers; 114 for (User *U : GV->users()) 115 if (!isa<Instruction>(U)) 116 WUsers.push_back(WeakTrackingVH(U)); 117 while (!WUsers.empty()) 118 if (WeakTrackingVH WU = WUsers.pop_back_val()) { 119 ConstantExpr *CE = dyn_cast<ConstantExpr>(WU); 120 if (!CE || !replaceConstantExprOp(CE, P)) 121 return false; 122 } 123 return true; 124 } 125 126 static bool isZeroLengthArray(Type *Ty) { 127 ArrayType *AT = dyn_cast<ArrayType>(Ty); 128 return AT && (AT->getNumElements() == 0); 129 } 130 131 bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable *GV) { 132 Module *M = GV->getParent(); 133 if (!GV->isThreadLocal()) 134 return false; 135 136 // Skip globals that we can't lower and leave it for the backend to error. 137 if (!rewriteNonInstructionUses(GV, this) || 138 !GV->getType()->isSized() || isZeroLengthArray(GV->getType())) 139 return false; 140 141 // Create replacement global. 142 ArrayType *NewType = createLoweredType(GV->getValueType()); 143 Constant *NewInitializer = nullptr; 144 if (GV->hasInitializer()) 145 NewInitializer = createLoweredInitializer(NewType, 146 GV->getInitializer()); 147 GlobalVariable *NewGV = 148 new GlobalVariable(*M, NewType, GV->isConstant(), GV->getLinkage(), 149 NewInitializer, "", nullptr, 150 GlobalVariable::NotThreadLocal, 151 GV->getType()->getAddressSpace(), 152 GV->isExternallyInitialized()); 153 154 // Update uses. 155 SmallVector<User *, 16> Users(GV->users()); 156 for (User *U : Users) { 157 Instruction *Inst = cast<Instruction>(U); 158 IRBuilder<> Builder(Inst); 159 Value *ThreadID = Builder.CreateIntrinsic(Intrinsic::xcore_getid, {}, {}); 160 Value *Addr = Builder.CreateInBoundsGEP(NewGV->getValueType(), NewGV, 161 {Builder.getInt64(0), ThreadID}); 162 U->replaceUsesOfWith(GV, Addr); 163 } 164 165 // Remove old global. 166 NewGV->takeName(GV); 167 GV->eraseFromParent(); 168 return true; 169 } 170 171 bool XCoreLowerThreadLocal::runOnModule(Module &M) { 172 // Find thread local globals. 173 bool MadeChange = false; 174 SmallVector<GlobalVariable *, 16> ThreadLocalGlobals; 175 for (GlobalVariable &GV : M.globals()) 176 if (GV.isThreadLocal()) 177 ThreadLocalGlobals.push_back(&GV); 178 for (GlobalVariable *GV : ThreadLocalGlobals) 179 MadeChange |= lowerGlobal(GV); 180 return MadeChange; 181 } 182