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