1//===-- GenericToNVVM.cpp - Convert generic module to NVVM module - C++ -*-===//
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// Convert generic global variables into either .global or .const access based
10// on the variable's "constant" qualifier.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MCTargetDesc/NVPTXBaseInfo.h"
15#include "NVPTX.h"
16#include "NVVMProperties.h"
17#include "llvm/CodeGen/ValueTypes.h"
18#include "llvm/IR/Constants.h"
19#include "llvm/IR/DerivedTypes.h"
20#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/Instructions.h"
22#include "llvm/IR/Intrinsics.h"
23#include "llvm/IR/LegacyPassManager.h"
24#include "llvm/IR/Module.h"
25#include "llvm/IR/Operator.h"
26#include "llvm/IR/ValueMap.h"
27#include "llvm/Transforms/Utils/ValueMapper.h"
28
29using namespace llvm;
30
31namespace {
32class GenericToNVVM {
33public:
34 bool runOnModule(Module &M);
35
36private:
37 Value *remapConstant(Module *M, Function *F, Constant *C,
38 IRBuilder<> &Builder);
39 Value *remapConstantVectorOrConstantAggregate(Module *M, Function *F,
40 Constant *C,
41 IRBuilder<> &Builder);
42 Value *remapConstantExpr(Module *M, Function *F, ConstantExpr *C,
43 IRBuilder<> &Builder);
44
45 typedef ValueMap<GlobalVariable *, GlobalVariable *> GVMapTy;
46 typedef ValueMap<Constant *, Value *> ConstantToValueMapTy;
47 GVMapTy GVMap;
48 ConstantToValueMapTy ConstantToValueMap;
49};
50} // end namespace
51
52bool GenericToNVVM::runOnModule(Module &M) {
53 // Create a clone of each global variable that has the default address space.
54 // The clone is created with the global address space specifier, and the pair
55 // of original global variable and its clone is placed in the GVMap for later
56 // use.
57
58 for (GlobalVariable &GV : llvm::make_early_inc_range(Range: M.globals())) {
59 if (GV.getType()->getAddressSpace() == llvm::ADDRESS_SPACE_GENERIC &&
60 llvm::getPTXOpaqueType(GV) == llvm::PTXOpaqueType::None &&
61 !GV.getName().starts_with(Prefix: "llvm.")) {
62 GlobalVariable *NewGV = new GlobalVariable(
63 M, GV.getValueType(), GV.isConstant(), GV.getLinkage(),
64 GV.hasInitializer() ? GV.getInitializer() : nullptr, "", &GV,
65 GV.getThreadLocalMode(), llvm::ADDRESS_SPACE_GLOBAL);
66 NewGV->copyAttributesFrom(Src: &GV);
67 NewGV->copyMetadata(Src: &GV, /*Offset=*/0);
68 GVMap[&GV] = NewGV;
69 }
70 }
71
72 // Return immediately, if every global variable has a specific address space
73 // specifier.
74 if (GVMap.empty()) {
75 return false;
76 }
77
78 // Walk through the instructions in function defitinions, and replace any use
79 // of original global variables in GVMap with a use of the corresponding
80 // copies in GVMap. If necessary, promote constants to instructions.
81 for (Function &F : M) {
82 if (F.isDeclaration()) {
83 continue;
84 }
85 IRBuilder<> Builder(&*F.getEntryBlock().getFirstNonPHIOrDbg());
86 for (BasicBlock &BB : F) {
87 for (Instruction &II : BB) {
88 for (unsigned i = 0, e = II.getNumOperands(); i < e; ++i) {
89 Value *Operand = II.getOperand(i);
90 if (isa<Constant>(Val: Operand)) {
91 II.setOperand(
92 i, Val: remapConstant(M: &M, F: &F, C: cast<Constant>(Val: Operand), Builder));
93 }
94 }
95 }
96 }
97 ConstantToValueMap.clear();
98 }
99
100 // Copy GVMap over to a standard value map.
101 ValueToValueMapTy VM;
102 for (auto I = GVMap.begin(), E = GVMap.end(); I != E; ++I)
103 VM[I->first] = I->second;
104
105 // Walk through the global variable initializers, and replace any use of
106 // original global variables in GVMap with a use of the corresponding copies
107 // in GVMap. The copies need to be bitcast to the original global variable
108 // types, as we cannot use cvta in global variable initializers.
109 // Snapshot the map first: replaceAllUsesWith() and eraseFromParent() below
110 // fire ValueMap callbacks that mutate GVMap, so we must not hold an iterator
111 // into GVMap across them.
112 SmallVector<std::pair<GlobalVariable *, GlobalVariable *>, 0> GVs(
113 GVMap.begin(), GVMap.end());
114 for (auto [GV, NewGV] : GVs) {
115 // Remove GV from the map so that it can be RAUWed.
116 GVMap.erase(Val: GV);
117
118 Constant *BitCastNewGV = ConstantExpr::getPointerCast(C: NewGV, Ty: GV->getType());
119 // At this point, the remaining uses of GV should be found only in global
120 // variable initializers, as other uses have been already been removed
121 // while walking through the instructions in function definitions.
122 GV->replaceAllUsesWith(V: BitCastNewGV);
123 std::string Name = std::string(GV->getName());
124 GV->eraseFromParent();
125 NewGV->setName(Name);
126 }
127 assert(GVMap.empty() && "Expected it to be empty by now");
128
129 return true;
130}
131
132Value *GenericToNVVM::remapConstant(Module *M, Function *F, Constant *C,
133 IRBuilder<> &Builder) {
134 // If the constant C has been converted already in the given function F, just
135 // return the converted value.
136 ConstantToValueMapTy::iterator CTII = ConstantToValueMap.find(Val: C);
137 if (CTII != ConstantToValueMap.end()) {
138 return CTII->second;
139 }
140
141 Value *NewValue = C;
142 if (isa<GlobalVariable>(Val: C)) {
143 // If the constant C is a global variable and is found in GVMap, substitute
144 //
145 // addrspacecast GVMap[C] to addrspace(0)
146 //
147 // for our use of C.
148 GVMapTy::iterator I = GVMap.find(Val: cast<GlobalVariable>(Val: C));
149 if (I != GVMap.end()) {
150 GlobalVariable *GV = I->second;
151 NewValue = Builder.CreateAddrSpaceCast(
152 V: GV, DestTy: PointerType::get(C&: GV->getContext(), AddressSpace: llvm::ADDRESS_SPACE_GENERIC));
153 }
154 } else if (isa<ConstantAggregate>(Val: C)) {
155 // If any element in the constant vector or aggregate C is or uses a global
156 // variable in GVMap, the constant C needs to be reconstructed, using a set
157 // of instructions.
158 NewValue = remapConstantVectorOrConstantAggregate(M, F, C, Builder);
159 } else if (isa<ConstantExpr>(Val: C)) {
160 // If any operand in the constant expression C is or uses a global variable
161 // in GVMap, the constant expression C needs to be reconstructed, using a
162 // set of instructions.
163 NewValue = remapConstantExpr(M, F, C: cast<ConstantExpr>(Val: C), Builder);
164 }
165
166 ConstantToValueMap[C] = NewValue;
167 return NewValue;
168}
169
170Value *GenericToNVVM::remapConstantVectorOrConstantAggregate(
171 Module *M, Function *F, Constant *C, IRBuilder<> &Builder) {
172 bool OperandChanged = false;
173 SmallVector<Value *, 4> NewOperands;
174 unsigned NumOperands = C->getNumOperands();
175
176 // Check if any element is or uses a global variable in GVMap, and thus
177 // converted to another value.
178 for (unsigned i = 0; i < NumOperands; ++i) {
179 Value *Operand = C->getOperand(i);
180 Value *NewOperand = remapConstant(M, F, C: cast<Constant>(Val: Operand), Builder);
181 OperandChanged |= Operand != NewOperand;
182 NewOperands.push_back(Elt: NewOperand);
183 }
184
185 // If none of the elements has been modified, return C as it is.
186 if (!OperandChanged) {
187 return C;
188 }
189
190 // If any of the elements has been modified, construct the equivalent
191 // vector or aggregate value with a set instructions and the converted
192 // elements.
193 Value *NewValue = PoisonValue::get(T: C->getType());
194 if (isa<ConstantVector>(Val: C)) {
195 for (unsigned i = 0; i < NumOperands; ++i) {
196 Value *Idx = ConstantInt::get(Ty: Type::getInt32Ty(C&: M->getContext()), V: i);
197 NewValue = Builder.CreateInsertElement(Vec: NewValue, NewElt: NewOperands[i], Idx);
198 }
199 } else {
200 for (unsigned i = 0; i < NumOperands; ++i) {
201 NewValue =
202 Builder.CreateInsertValue(Agg: NewValue, Val: NewOperands[i], Idxs: ArrayRef(i));
203 }
204 }
205
206 return NewValue;
207}
208
209Value *GenericToNVVM::remapConstantExpr(Module *M, Function *F, ConstantExpr *C,
210 IRBuilder<> &Builder) {
211 bool OperandChanged = false;
212 SmallVector<Value *, 4> NewOperands;
213 unsigned NumOperands = C->getNumOperands();
214
215 // Check if any operand is or uses a global variable in GVMap, and thus
216 // converted to another value.
217 for (unsigned i = 0; i < NumOperands; ++i) {
218 Value *Operand = C->getOperand(i_nocapture: i);
219 Value *NewOperand = remapConstant(M, F, C: cast<Constant>(Val: Operand), Builder);
220 OperandChanged |= Operand != NewOperand;
221 NewOperands.push_back(Elt: NewOperand);
222 }
223
224 // If none of the operands has been modified, return C as it is.
225 if (!OperandChanged) {
226 return C;
227 }
228
229 // If any of the operands has been modified, construct the instruction with
230 // the converted operands.
231 unsigned Opcode = C->getOpcode();
232 switch (Opcode) {
233 case Instruction::ExtractElement:
234 // ExtractElementConstantExpr
235 return Builder.CreateExtractElement(Vec: NewOperands[0], Idx: NewOperands[1]);
236 case Instruction::InsertElement:
237 // InsertElementConstantExpr
238 return Builder.CreateInsertElement(Vec: NewOperands[0], NewElt: NewOperands[1],
239 Idx: NewOperands[2]);
240 case Instruction::ShuffleVector:
241 // ShuffleVector
242 return Builder.CreateShuffleVector(V1: NewOperands[0], V2: NewOperands[1],
243 Mask: NewOperands[2]);
244 case Instruction::GetElementPtr:
245 // GetElementPtrConstantExpr
246 return Builder.CreateGEP(Ty: cast<GEPOperator>(Val: C)->getSourceElementType(),
247 Ptr: NewOperands[0],
248 IdxList: ArrayRef(&NewOperands[1], NumOperands - 1), Name: "",
249 NW: cast<GEPOperator>(Val: C)->isInBounds());
250 case Instruction::Select:
251 // SelectConstantExpr
252 return Builder.CreateSelect(C: NewOperands[0], True: NewOperands[1], False: NewOperands[2]);
253 default:
254 // BinaryConstantExpr
255 if (Instruction::isBinaryOp(Opcode)) {
256 return Builder.CreateBinOp(Opc: Instruction::BinaryOps(C->getOpcode()),
257 LHS: NewOperands[0], RHS: NewOperands[1]);
258 }
259 // UnaryConstantExpr
260 if (Instruction::isCast(Opcode)) {
261 return Builder.CreateCast(Op: Instruction::CastOps(C->getOpcode()),
262 V: NewOperands[0], DestTy: C->getType());
263 }
264 llvm_unreachable("GenericToNVVM encountered an unsupported ConstantExpr");
265 }
266}
267
268namespace {
269class GenericToNVVMLegacyPass : public ModulePass {
270public:
271 static char ID;
272
273 GenericToNVVMLegacyPass() : ModulePass(ID) {}
274
275 bool runOnModule(Module &M) override;
276};
277} // namespace
278
279char GenericToNVVMLegacyPass::ID = 0;
280
281ModulePass *llvm::createGenericToNVVMLegacyPass() {
282 return new GenericToNVVMLegacyPass();
283}
284
285INITIALIZE_PASS(
286 GenericToNVVMLegacyPass, "generic-to-nvvm",
287 "Ensure that the global variables are in the global address space", false,
288 false)
289
290bool GenericToNVVMLegacyPass::runOnModule(Module &M) {
291 return GenericToNVVM().runOnModule(M);
292}
293
294PreservedAnalyses GenericToNVVMPass::run(Module &M, ModuleAnalysisManager &AM) {
295 return GenericToNVVM().runOnModule(M) ? PreservedAnalyses::none()
296 : PreservedAnalyses::all();
297}
298