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
30using namespace llvm;
31
32static 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
37namespace {
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
52char XCoreLowerThreadLocal::ID = 0;
53
54INITIALIZE_PASS(XCoreLowerThreadLocal, "xcore-lower-thread-local",
55 "Lower thread local variables", false, false)
56
57ModulePass *llvm::createXCoreLowerThreadLocalPass() {
58 return new XCoreLowerThreadLocal();
59}
60
61static ArrayType *createLoweredType(Type *OriginalType) {
62 return ArrayType::get(ElementType: OriginalType, NumElements: MaxThreads);
63}
64
65static Constant *
66createLoweredInitializer(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
75static 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
110static 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
124static bool isZeroLengthArray(Type *Ty) {
125 ArrayType *AT = dyn_cast<ArrayType>(Val: Ty);
126 return AT && (AT->getNumElements() == 0);
127}
128
129bool 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
169bool 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