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/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"
29 #define DEBUG_TYPE "xcore-lower-thread-local"
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(8));
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
{
45 XCoreLowerThreadLocal() : ModulePass(ID
) {
46 initializeXCoreLowerThreadLocalPass(*PassRegistry::getPassRegistry());
49 bool lowerGlobal(GlobalVariable
*GV
);
51 bool runOnModule(Module
&M
) override
;
55 char XCoreLowerThreadLocal::ID
= 0;
57 INITIALIZE_PASS(XCoreLowerThreadLocal
, "xcore-lower-thread-local",
58 "Lower thread local variables", false, false)
60 ModulePass
*llvm::createXCoreLowerThreadLocalPass() {
61 return new XCoreLowerThreadLocal();
64 static ArrayType
*createLoweredType(Type
*OriginalType
) {
65 return ArrayType::get(OriginalType
, MaxThreads
);
69 createLoweredInitializer(ArrayType
*NewType
, Constant
*OriginalInitializer
) {
70 SmallVector
<Constant
*, 8> Elements(MaxThreads
);
71 for (unsigned i
= 0; i
!= MaxThreads
; ++i
) {
72 Elements
[i
] = OriginalInitializer
;
74 return ConstantArray::get(NewType
, Elements
);
78 static bool replaceConstantExprOp(ConstantExpr
*CE
, Pass
*P
) {
80 SmallVector
<WeakTrackingVH
, 8> WUsers(CE
->users());
82 WUsers
.erase(std::unique(WUsers
.begin(), WUsers
.end()), WUsers
.end());
83 while (!WUsers
.empty())
84 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
85 if (PHINode
*PN
= dyn_cast
<PHINode
>(WU
)) {
86 for (int I
= 0, E
= PN
->getNumIncomingValues(); I
< E
; ++I
)
87 if (PN
->getIncomingValue(I
) == CE
) {
88 BasicBlock
*PredBB
= PN
->getIncomingBlock(I
);
89 if (PredBB
->getTerminator()->getNumSuccessors() > 1)
90 PredBB
= SplitEdge(PredBB
, PN
->getParent());
91 Instruction
*InsertPos
= PredBB
->getTerminator();
92 Instruction
*NewInst
= CE
->getAsInstruction(InsertPos
);
93 PN
->setOperand(I
, NewInst
);
95 } else if (Instruction
*Instr
= dyn_cast
<Instruction
>(WU
)) {
96 Instruction
*NewInst
= CE
->getAsInstruction(Instr
);
97 Instr
->replaceUsesOfWith(CE
, NewInst
);
99 ConstantExpr
*CExpr
= dyn_cast
<ConstantExpr
>(WU
);
100 if (!CExpr
|| !replaceConstantExprOp(CExpr
, P
))
104 } while (CE
->hasNUsesOrMore(1)); // We need to check because a recursive
105 // sibling may have used 'CE' when getAsInstruction was called.
106 CE
->destroyConstant();
110 static bool rewriteNonInstructionUses(GlobalVariable
*GV
, Pass
*P
) {
111 SmallVector
<WeakTrackingVH
, 8> WUsers
;
112 for (User
*U
: GV
->users())
113 if (!isa
<Instruction
>(U
))
114 WUsers
.push_back(WeakTrackingVH(U
));
115 while (!WUsers
.empty())
116 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
117 ConstantExpr
*CE
= dyn_cast
<ConstantExpr
>(WU
);
118 if (!CE
|| !replaceConstantExprOp(CE
, P
))
124 static bool isZeroLengthArray(Type
*Ty
) {
125 ArrayType
*AT
= dyn_cast
<ArrayType
>(Ty
);
126 return AT
&& (AT
->getNumElements() == 0);
129 bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable
*GV
) {
130 Module
*M
= GV
->getParent();
131 if (!GV
->isThreadLocal())
134 // Skip globals that we can't lower and leave it for the backend to error.
135 if (!rewriteNonInstructionUses(GV
, this) ||
136 !GV
->getType()->isSized() || isZeroLengthArray(GV
->getType()))
139 // Create replacement global.
140 ArrayType
*NewType
= createLoweredType(GV
->getValueType());
141 Constant
*NewInitializer
= nullptr;
142 if (GV
->hasInitializer())
143 NewInitializer
= createLoweredInitializer(NewType
,
144 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());
153 SmallVector
<User
*, 16> Users(GV
->users());
154 for (unsigned I
= 0, E
= Users
.size(); I
!= E
; ++I
) {
156 Instruction
*Inst
= cast
<Instruction
>(U
);
157 IRBuilder
<> Builder(Inst
);
158 Function
*GetID
= Intrinsic::getDeclaration(GV
->getParent(),
159 Intrinsic::xcore_getid
);
160 Value
*ThreadID
= Builder
.CreateCall(GetID
, {});
161 Value
*Addr
= Builder
.CreateInBoundsGEP(NewGV
->getValueType(), NewGV
,
162 {Builder
.getInt64(0), ThreadID
});
163 U
->replaceUsesOfWith(GV
, Addr
);
166 // Remove old global.
168 GV
->eraseFromParent();
172 bool XCoreLowerThreadLocal::runOnModule(Module
&M
) {
173 // Find thread local globals.
174 bool MadeChange
= false;
175 SmallVector
<GlobalVariable
*, 16> ThreadLocalGlobals
;
176 for (GlobalVariable
&GV
: M
.globals())
177 if (GV
.isThreadLocal())
178 ThreadLocalGlobals
.push_back(&GV
);
179 for (unsigned I
= 0, E
= ThreadLocalGlobals
.size(); I
!= E
; ++I
) {
180 MadeChange
|= lowerGlobal(ThreadLocalGlobals
[I
]);