1 //===-- XCoreLowerThreadLocal - Lower thread local variables --------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file contains a pass that lowers thread local variables on the
13 //===----------------------------------------------------------------------===//
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/Module.h"
22 #include "llvm/IR/NoFolder.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"
28 #define DEBUG_TYPE "xcore-lower-thread-local"
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(8));
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
{
44 XCoreLowerThreadLocal() : ModulePass(ID
) {
45 initializeXCoreLowerThreadLocalPass(*PassRegistry::getPassRegistry());
48 bool lowerGlobal(GlobalVariable
*GV
);
50 bool runOnModule(Module
&M
) override
;
54 char XCoreLowerThreadLocal::ID
= 0;
56 INITIALIZE_PASS(XCoreLowerThreadLocal
, "xcore-lower-thread-local",
57 "Lower thread local variables", false, false)
59 ModulePass
*llvm::createXCoreLowerThreadLocalPass() {
60 return new XCoreLowerThreadLocal();
63 static ArrayType
*createLoweredType(Type
*OriginalType
) {
64 return ArrayType::get(OriginalType
, MaxThreads
);
68 createLoweredInitializer(ArrayType
*NewType
, Constant
*OriginalInitializer
) {
69 SmallVector
<Constant
*, 8> Elements(MaxThreads
);
70 for (unsigned i
= 0; i
!= MaxThreads
; ++i
) {
71 Elements
[i
] = OriginalInitializer
;
73 return ConstantArray::get(NewType
, Elements
);
77 createReplacementInstr(ConstantExpr
*CE
, Instruction
*Instr
) {
78 IRBuilder
<NoFolder
> Builder(Instr
);
79 unsigned OpCode
= CE
->getOpcode();
81 case Instruction::GetElementPtr
: {
82 SmallVector
<Value
*,4> CEOpVec(CE
->op_begin(), CE
->op_end());
83 ArrayRef
<Value
*> CEOps(CEOpVec
);
84 return dyn_cast
<Instruction
>(Builder
.CreateInBoundsGEP(
85 cast
<GEPOperator
>(CE
)->getSourceElementType(), CEOps
[0],
88 case Instruction::Add
:
89 case Instruction::Sub
:
90 case Instruction::Mul
:
91 case Instruction::UDiv
:
92 case Instruction::SDiv
:
93 case Instruction::FDiv
:
94 case Instruction::URem
:
95 case Instruction::SRem
:
96 case Instruction::FRem
:
97 case Instruction::Shl
:
98 case Instruction::LShr
:
99 case Instruction::AShr
:
100 case Instruction::And
:
101 case Instruction::Or
:
102 case Instruction::Xor
:
103 return dyn_cast
<Instruction
>(
104 Builder
.CreateBinOp((Instruction::BinaryOps
)OpCode
,
105 CE
->getOperand(0), CE
->getOperand(1),
107 case Instruction::Trunc
:
108 case Instruction::ZExt
:
109 case Instruction::SExt
:
110 case Instruction::FPToUI
:
111 case Instruction::FPToSI
:
112 case Instruction::UIToFP
:
113 case Instruction::SIToFP
:
114 case Instruction::FPTrunc
:
115 case Instruction::FPExt
:
116 case Instruction::PtrToInt
:
117 case Instruction::IntToPtr
:
118 case Instruction::BitCast
:
119 return dyn_cast
<Instruction
>(
120 Builder
.CreateCast((Instruction::CastOps
)OpCode
,
121 CE
->getOperand(0), CE
->getType(),
124 llvm_unreachable("Unhandled constant expression!\n");
128 static bool replaceConstantExprOp(ConstantExpr
*CE
, Pass
*P
) {
130 SmallVector
<WeakTrackingVH
, 8> WUsers(CE
->user_begin(), CE
->user_end());
132 WUsers
.erase(std::unique(WUsers
.begin(), WUsers
.end()), WUsers
.end());
133 while (!WUsers
.empty())
134 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
135 if (PHINode
*PN
= dyn_cast
<PHINode
>(WU
)) {
136 for (int I
= 0, E
= PN
->getNumIncomingValues(); I
< E
; ++I
)
137 if (PN
->getIncomingValue(I
) == CE
) {
138 BasicBlock
*PredBB
= PN
->getIncomingBlock(I
);
139 if (PredBB
->getTerminator()->getNumSuccessors() > 1)
140 PredBB
= SplitEdge(PredBB
, PN
->getParent());
141 Instruction
*InsertPos
= PredBB
->getTerminator();
142 Instruction
*NewInst
= createReplacementInstr(CE
, InsertPos
);
143 PN
->setOperand(I
, NewInst
);
145 } else if (Instruction
*Instr
= dyn_cast
<Instruction
>(WU
)) {
146 Instruction
*NewInst
= createReplacementInstr(CE
, Instr
);
147 Instr
->replaceUsesOfWith(CE
, NewInst
);
149 ConstantExpr
*CExpr
= dyn_cast
<ConstantExpr
>(WU
);
150 if (!CExpr
|| !replaceConstantExprOp(CExpr
, P
))
154 } while (CE
->hasNUsesOrMore(1)); // We need to check because a recursive
155 // sibling may have used 'CE' when createReplacementInstr was called.
156 CE
->destroyConstant();
160 static bool rewriteNonInstructionUses(GlobalVariable
*GV
, Pass
*P
) {
161 SmallVector
<WeakTrackingVH
, 8> WUsers
;
162 for (User
*U
: GV
->users())
163 if (!isa
<Instruction
>(U
))
164 WUsers
.push_back(WeakTrackingVH(U
));
165 while (!WUsers
.empty())
166 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
167 ConstantExpr
*CE
= dyn_cast
<ConstantExpr
>(WU
);
168 if (!CE
|| !replaceConstantExprOp(CE
, P
))
174 static bool isZeroLengthArray(Type
*Ty
) {
175 ArrayType
*AT
= dyn_cast
<ArrayType
>(Ty
);
176 return AT
&& (AT
->getNumElements() == 0);
179 bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable
*GV
) {
180 Module
*M
= GV
->getParent();
181 if (!GV
->isThreadLocal())
184 // Skip globals that we can't lower and leave it for the backend to error.
185 if (!rewriteNonInstructionUses(GV
, this) ||
186 !GV
->getType()->isSized() || isZeroLengthArray(GV
->getType()))
189 // Create replacement global.
190 ArrayType
*NewType
= createLoweredType(GV
->getValueType());
191 Constant
*NewInitializer
= nullptr;
192 if (GV
->hasInitializer())
193 NewInitializer
= createLoweredInitializer(NewType
,
194 GV
->getInitializer());
195 GlobalVariable
*NewGV
=
196 new GlobalVariable(*M
, NewType
, GV
->isConstant(), GV
->getLinkage(),
197 NewInitializer
, "", nullptr,
198 GlobalVariable::NotThreadLocal
,
199 GV
->getType()->getAddressSpace(),
200 GV
->isExternallyInitialized());
203 SmallVector
<User
*, 16> Users(GV
->user_begin(), GV
->user_end());
204 for (unsigned I
= 0, E
= Users
.size(); I
!= E
; ++I
) {
206 Instruction
*Inst
= cast
<Instruction
>(U
);
207 IRBuilder
<> Builder(Inst
);
208 Function
*GetID
= Intrinsic::getDeclaration(GV
->getParent(),
209 Intrinsic::xcore_getid
);
210 Value
*ThreadID
= Builder
.CreateCall(GetID
, {});
211 Value
*Addr
= Builder
.CreateInBoundsGEP(NewGV
->getValueType(), NewGV
,
212 {Builder
.getInt64(0), ThreadID
});
213 U
->replaceUsesOfWith(GV
, Addr
);
216 // Remove old global.
218 GV
->eraseFromParent();
222 bool XCoreLowerThreadLocal::runOnModule(Module
&M
) {
223 // Find thread local globals.
224 bool MadeChange
= false;
225 SmallVector
<GlobalVariable
*, 16> ThreadLocalGlobals
;
226 for (GlobalVariable
&GV
: M
.globals())
227 if (GV
.isThreadLocal())
228 ThreadLocalGlobals
.push_back(&GV
);
229 for (unsigned I
= 0, E
= ThreadLocalGlobals
.size(); I
!= E
; ++I
) {
230 MadeChange
|= lowerGlobal(ThreadLocalGlobals
[I
]);