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/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 static bool replaceConstantExprOp(ConstantExpr
*CE
, Pass
*P
) {
79 SmallVector
<WeakTrackingVH
, 8> WUsers(CE
->users());
81 WUsers
.erase(llvm::unique(WUsers
), WUsers
.end());
82 while (!WUsers
.empty())
83 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
84 if (PHINode
*PN
= dyn_cast
<PHINode
>(WU
)) {
85 for (int I
= 0, E
= PN
->getNumIncomingValues(); I
< E
; ++I
)
86 if (PN
->getIncomingValue(I
) == CE
) {
87 BasicBlock
*PredBB
= PN
->getIncomingBlock(I
);
88 if (PredBB
->getTerminator()->getNumSuccessors() > 1)
89 PredBB
= SplitEdge(PredBB
, PN
->getParent());
90 BasicBlock::iterator InsertPos
=
91 PredBB
->getTerminator()->getIterator();
92 Instruction
*NewInst
= CE
->getAsInstruction();
93 NewInst
->insertBefore(*PredBB
, InsertPos
);
94 PN
->setOperand(I
, NewInst
);
96 } else if (Instruction
*Instr
= dyn_cast
<Instruction
>(WU
)) {
97 Instruction
*NewInst
= CE
->getAsInstruction();
98 NewInst
->insertBefore(*Instr
->getParent(), Instr
->getIterator());
99 Instr
->replaceUsesOfWith(CE
, NewInst
);
101 ConstantExpr
*CExpr
= dyn_cast
<ConstantExpr
>(WU
);
102 if (!CExpr
|| !replaceConstantExprOp(CExpr
, P
))
106 } while (CE
->hasNUsesOrMore(1)); // We need to check because a recursive
107 // sibling may have used 'CE' when getAsInstruction was called.
108 CE
->destroyConstant();
112 static bool rewriteNonInstructionUses(GlobalVariable
*GV
, Pass
*P
) {
113 SmallVector
<WeakTrackingVH
, 8> WUsers
;
114 for (User
*U
: GV
->users())
115 if (!isa
<Instruction
>(U
))
116 WUsers
.push_back(WeakTrackingVH(U
));
117 while (!WUsers
.empty())
118 if (WeakTrackingVH WU
= WUsers
.pop_back_val()) {
119 ConstantExpr
*CE
= dyn_cast
<ConstantExpr
>(WU
);
120 if (!CE
|| !replaceConstantExprOp(CE
, P
))
126 static bool isZeroLengthArray(Type
*Ty
) {
127 ArrayType
*AT
= dyn_cast
<ArrayType
>(Ty
);
128 return AT
&& (AT
->getNumElements() == 0);
131 bool XCoreLowerThreadLocal::lowerGlobal(GlobalVariable
*GV
) {
132 Module
*M
= GV
->getParent();
133 if (!GV
->isThreadLocal())
136 // Skip globals that we can't lower and leave it for the backend to error.
137 if (!rewriteNonInstructionUses(GV
, this) ||
138 !GV
->getType()->isSized() || isZeroLengthArray(GV
->getType()))
141 // Create replacement global.
142 ArrayType
*NewType
= createLoweredType(GV
->getValueType());
143 Constant
*NewInitializer
= nullptr;
144 if (GV
->hasInitializer())
145 NewInitializer
= createLoweredInitializer(NewType
,
146 GV
->getInitializer());
147 GlobalVariable
*NewGV
=
148 new GlobalVariable(*M
, NewType
, GV
->isConstant(), GV
->getLinkage(),
149 NewInitializer
, "", nullptr,
150 GlobalVariable::NotThreadLocal
,
151 GV
->getType()->getAddressSpace(),
152 GV
->isExternallyInitialized());
155 SmallVector
<User
*, 16> Users(GV
->users());
156 for (User
*U
: Users
) {
157 Instruction
*Inst
= cast
<Instruction
>(U
);
158 IRBuilder
<> Builder(Inst
);
159 Value
*ThreadID
= Builder
.CreateIntrinsic(Intrinsic::xcore_getid
, {}, {});
160 Value
*Addr
= Builder
.CreateInBoundsGEP(NewGV
->getValueType(), NewGV
,
161 {Builder
.getInt64(0), ThreadID
});
162 U
->replaceUsesOfWith(GV
, Addr
);
165 // Remove old global.
167 GV
->eraseFromParent();
171 bool XCoreLowerThreadLocal::runOnModule(Module
&M
) {
172 // Find thread local globals.
173 bool MadeChange
= false;
174 SmallVector
<GlobalVariable
*, 16> ThreadLocalGlobals
;
175 for (GlobalVariable
&GV
: M
.globals())
176 if (GV
.isThreadLocal())
177 ThreadLocalGlobals
.push_back(&GV
);
178 for (GlobalVariable
*GV
: ThreadLocalGlobals
)
179 MadeChange
|= lowerGlobal(GV
);