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