| 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(Val: 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 | |
| 46 | bool lowerGlobal(GlobalVariable *GV); |
| 47 | |
| 48 | bool runOnModule(Module &M) override; |
| 49 | }; |
| 50 | } |
| 51 | |
| 52 | char XCoreLowerThreadLocal::ID = 0; |
| 53 | |
| 54 | INITIALIZE_PASS(XCoreLowerThreadLocal, "xcore-lower-thread-local" , |
| 55 | "Lower thread local variables" , false, false) |
| 56 | |
| 57 | ModulePass *llvm::createXCoreLowerThreadLocalPass() { |
| 58 | return new XCoreLowerThreadLocal(); |
| 59 | } |
| 60 | |
| 61 | static ArrayType *createLoweredType(Type *OriginalType) { |
| 62 | return ArrayType::get(ElementType: OriginalType, NumElements: MaxThreads); |
| 63 | } |
| 64 | |
| 65 | static Constant * |
| 66 | createLoweredInitializer(ArrayType *NewType, Constant *OriginalInitializer) { |
| 67 | SmallVector<Constant *, 8> Elements(MaxThreads); |
| 68 | for (unsigned i = 0; i != MaxThreads; ++i) { |
| 69 | Elements[i] = OriginalInitializer; |
| 70 | } |
| 71 | return ConstantArray::get(T: NewType, V: Elements); |
| 72 | } |
| 73 | |
| 74 | |
| 75 | static bool replaceConstantExprOp(ConstantExpr *CE, Pass *P) { |
| 76 | do { |
| 77 | SmallVector<WeakTrackingVH, 8> WUsers(CE->users()); |
| 78 | llvm::sort(C&: WUsers); |
| 79 | WUsers.erase(CS: llvm::unique(R&: WUsers), CE: WUsers.end()); |
| 80 | while (!WUsers.empty()) |
| 81 | if (WeakTrackingVH WU = WUsers.pop_back_val()) { |
| 82 | if (PHINode *PN = dyn_cast<PHINode>(Val&: WU)) { |
| 83 | for (int I = 0, E = PN->getNumIncomingValues(); I < E; ++I) |
| 84 | if (PN->getIncomingValue(i: I) == CE) { |
| 85 | BasicBlock *PredBB = PN->getIncomingBlock(i: I); |
| 86 | if (PredBB->getTerminator()->getNumSuccessors() > 1) |
| 87 | PredBB = SplitEdge(From: PredBB, To: PN->getParent()); |
| 88 | BasicBlock::iterator InsertPos = |
| 89 | PredBB->getTerminator()->getIterator(); |
| 90 | Instruction *NewInst = CE->getAsInstruction(); |
| 91 | NewInst->insertBefore(BB&: *PredBB, InsertPos); |
| 92 | PN->setOperand(i_nocapture: I, Val_nocapture: NewInst); |
| 93 | } |
| 94 | } else if (Instruction *Instr = dyn_cast<Instruction>(Val&: WU)) { |
| 95 | Instruction *NewInst = CE->getAsInstruction(); |
| 96 | NewInst->insertBefore(BB&: *Instr->getParent(), InsertPos: Instr->getIterator()); |
| 97 | Instr->replaceUsesOfWith(From: CE, To: NewInst); |
| 98 | } else { |
| 99 | ConstantExpr *CExpr = dyn_cast<ConstantExpr>(Val&: WU); |
| 100 | if (!CExpr || !replaceConstantExprOp(CE: CExpr, P)) |
| 101 | return false; |
| 102 | } |
| 103 | } |
| 104 | } while (CE->hasNUsesOrMore(N: 1)); // We need to check because a recursive |
| 105 | // sibling may have used 'CE' when getAsInstruction was called. |
| 106 | CE->destroyConstant(); |
| 107 | return true; |
| 108 | } |
| 109 | |
| 110 | static bool rewriteNonInstructionUses(GlobalVariable *GV, Pass *P) { |
| 111 | SmallVector<WeakTrackingVH, 8> WUsers; |
| 112 | for (User *U : GV->users()) |
| 113 | if (!isa<Instruction>(Val: U)) |
| 114 | WUsers.push_back(Elt: WeakTrackingVH(U)); |
| 115 | while (!WUsers.empty()) |
| 116 | if (WeakTrackingVH WU = WUsers.pop_back_val()) { |
| 117 | ConstantExpr *CE = dyn_cast<ConstantExpr>(Val&: WU); |
| 118 | if (!CE || !replaceConstantExprOp(CE, P)) |
| 119 | return false; |
| 120 | } |
| 121 | return true; |
| 122 | } |
| 123 | |
| 124 | static bool isZeroLengthArray(Type *Ty) { |
| 125 | ArrayType *AT = dyn_cast<ArrayType>(Val: Ty); |
| 126 | return AT && (AT->getNumElements() == 0); |
| 127 | } |
| 128 | |
| 129 | bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable *GV) { |
| 130 | Module *M = GV->getParent(); |
| 131 | if (!GV->isThreadLocal()) |
| 132 | return false; |
| 133 | |
| 134 | // Skip globals that we can't lower and leave it for the backend to error. |
| 135 | if (!rewriteNonInstructionUses(GV, P: this) || |
| 136 | !GV->getType()->isSized() || isZeroLengthArray(Ty: GV->getType())) |
| 137 | return false; |
| 138 | |
| 139 | // Create replacement global. |
| 140 | ArrayType *NewType = createLoweredType(OriginalType: GV->getValueType()); |
| 141 | Constant *NewInitializer = nullptr; |
| 142 | if (GV->hasInitializer()) |
| 143 | NewInitializer = createLoweredInitializer(NewType, |
| 144 | OriginalInitializer: GV->getInitializer()); |
| 145 | GlobalVariable *NewGV = |
| 146 | new GlobalVariable(*M, NewType, GV->isConstant(), GV->getLinkage(), |
| 147 | NewInitializer, "" , nullptr, |
| 148 | GlobalVariable::NotThreadLocal, |
| 149 | GV->getType()->getAddressSpace(), |
| 150 | GV->isExternallyInitialized()); |
| 151 | |
| 152 | // Update uses. |
| 153 | SmallVector<User *, 16> Users(GV->users()); |
| 154 | for (User *U : Users) { |
| 155 | Instruction *Inst = cast<Instruction>(Val: U); |
| 156 | IRBuilder<> Builder(Inst); |
| 157 | Value *ThreadID = Builder.CreateIntrinsic(ID: Intrinsic::xcore_getid, Args: {}); |
| 158 | Value *Addr = Builder.CreateInBoundsGEP(Ty: NewGV->getValueType(), Ptr: NewGV, |
| 159 | IdxList: {Builder.getInt64(C: 0), ThreadID}); |
| 160 | U->replaceUsesOfWith(From: GV, To: Addr); |
| 161 | } |
| 162 | |
| 163 | // Remove old global. |
| 164 | NewGV->takeName(V: GV); |
| 165 | GV->eraseFromParent(); |
| 166 | return true; |
| 167 | } |
| 168 | |
| 169 | bool XCoreLowerThreadLocal::runOnModule(Module &M) { |
| 170 | // Find thread local globals. |
| 171 | bool MadeChange = false; |
| 172 | SmallVector<GlobalVariable *, 16> ThreadLocalGlobals; |
| 173 | for (GlobalVariable &GV : M.globals()) |
| 174 | if (GV.isThreadLocal()) |
| 175 | ThreadLocalGlobals.push_back(Elt: &GV); |
| 176 | for (GlobalVariable *GV : ThreadLocalGlobals) |
| 177 | MadeChange |= lowerGlobal(GV); |
| 178 | return MadeChange; |
| 179 | } |
| 180 | |