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 | |