1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2017-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 #include "Compiler/Optimizer/OpenCLPasses/GenericAddressResolution/GenericAddressDynamicResolution.hpp"
10 #include "AdaptorCommon/ImplicitArgs.hpp"
11 #include "Compiler/CodeGenContextWrapper.hpp"
12 #include "Compiler/CodeGenPublicEnums.h"
13 #include "Compiler/IGCPassSupport.h"
14 #include "Compiler/MetaDataUtilsWrapper.h"
15 #include "common/LLVMWarningsPush.hpp"
16 #include "llvmWrapper/Support/Alignment.h"
17 #include <llvm/IR/Module.h>
18 #include <llvm/IR/Instructions.h>
19 #include <llvm/IR/DataLayout.h>
20 #include <llvm/IR/DebugInfoMetadata.h>
21 #include "common/LLVMWarningsPop.hpp"
22 #include "Probe/Assertion.h"
23
24 using namespace llvm;
25 using namespace IGC;
26 using IGCLLVM::getAlign;
27
28 namespace {
29 class GenericAddressAnalysis : public FunctionPass {
30 public:
31 static char ID;
32
GenericAddressAnalysis()33 GenericAddressAnalysis()
34 : FunctionPass(ID)
35 {
36 }
37 ~GenericAddressAnalysis() = default;
38
getAnalysisUsage(AnalysisUsage & AU) const39 virtual void getAnalysisUsage(AnalysisUsage& AU) const override
40 {
41 AU.setPreservesCFG();
42 AU.addRequired<MetaDataUtilsWrapper>();
43 }
44
getPassName() const45 virtual StringRef getPassName() const override
46 {
47 return "GenericAddressAnalysis";
48 }
49
50 virtual bool runOnFunction(Function& F) override;
51 };
52 } // namespace
53
54 // Register pass
55 #define PASS_FLAG2 "igc-generic-address-analysis"
56 #define PASS_DESCRIPTION2 "Finds when generic pointers are used"
57 #define PASS_CFG_ONLY2 false
58 #define PASS_ANALYSIS2 false
59 IGC_INITIALIZE_PASS_BEGIN(GenericAddressAnalysis, PASS_FLAG2, PASS_DESCRIPTION2, PASS_CFG_ONLY2, PASS_ANALYSIS2)
60 IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
61 IGC_INITIALIZE_PASS_END(GenericAddressAnalysis, PASS_FLAG2, PASS_DESCRIPTION2, PASS_CFG_ONLY2, PASS_ANALYSIS2)
62
63 char GenericAddressAnalysis::ID = 0;
64
runOnFunction(Function & F)65 bool GenericAddressAnalysis::runOnFunction(Function& F)
66 {
67 for (auto& BB : F.getBasicBlockList()) {
68 for (auto& Inst : BB.getInstList()) {
69 Type* Ty = Inst.getType();
70 if (auto AI = dyn_cast<AllocaInst>(&Inst))
71 Ty = AI->getAllocatedType();
72 else if (auto LI = dyn_cast<LoadInst>(&Inst))
73 Ty = LI->getPointerOperand()->getType();
74 else if (auto SI = dyn_cast<StoreInst>(&Inst))
75 Ty = SI->getPointerOperand()->getType();
76 else if (auto GEP = dyn_cast<GetElementPtrInst>(&Inst))
77 Ty = GEP->getPointerOperandType();
78 auto PT = dyn_cast<PointerType>(Ty);
79 if (PT && PT->getAddressSpace() == ADDRESS_SPACE_GENERIC) {
80 return true;
81 }
82 }
83 }
84
85 return false;
86 }
87
88 namespace {
89 class GenericAddressDynamicResolution : public FunctionPass {
90 public:
91 static char ID;
92 Module* m_module = nullptr;
93 CodeGenContext* m_ctx = nullptr;
94
GenericAddressDynamicResolution()95 GenericAddressDynamicResolution()
96 : FunctionPass(ID)
97 {
98 }
99 ~GenericAddressDynamicResolution() = default;
100
getPassName() const101 virtual StringRef getPassName() const override
102 {
103 return "GenericAddressDynamicResolution";
104 }
105
getAnalysisUsage(AnalysisUsage & AU) const106 virtual void getAnalysisUsage(AnalysisUsage& AU) const override
107 {
108 AU.addRequired<MetaDataUtilsWrapper>();
109 AU.addRequired<CodeGenContextWrapper>();
110 }
111
112 virtual bool runOnFunction(Function& F) override;
113
114 bool visitLoadStoreInst(Instruction& I);
115 bool visitIntrinsicCall(CallInst& I);
getModule()116 Module* getModule() { return m_module; }
117
118 private:
119 Type* getPointerAsIntType(LLVMContext& Ctx, unsigned AS);
120 void resolveGAS(Instruction& I, Value* pointerOperand);
121 void resolveGASWithoutBranches(Instruction& I, Value* pointerOperand);
122 };
123 } // namespace
124
125
126 // Register pass to igc-opt
127 #define PASS_FLAG "igc-generic-address-dynamic-resolution"
128 #define PASS_DESCRIPTION "Resolve generic address space loads/stores"
129 #define PASS_CFG_ONLY false
130 #define PASS_ANALYSIS false
131 IGC_INITIALIZE_PASS_BEGIN(GenericAddressDynamicResolution, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
132 IGC_INITIALIZE_PASS_DEPENDENCY(MetaDataUtilsWrapper)
133 IGC_INITIALIZE_PASS_END(GenericAddressDynamicResolution, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
134
135 char GenericAddressDynamicResolution::ID = 0;
136
runOnFunction(Function & F)137 bool GenericAddressDynamicResolution::runOnFunction(Function& F)
138 {
139 m_module = F.getParent();
140 m_ctx = getAnalysis<CodeGenContextWrapper>().getCodeGenContext();
141 bool modified = false;
142 bool changed = false;
143
144 // iterate for all the intrinisics used by to_local, to_global, and to_private
145 do {
146 changed = false;
147
148 for (inst_iterator i = inst_begin(F); i != inst_end(F); ++i) {
149 Instruction& instruction = (*i);
150
151 if (CallInst * intrinsic = dyn_cast<CallInst>(&instruction)) {
152 changed = visitIntrinsicCall(*intrinsic);
153 }
154
155 if (changed) {
156 modified = true;
157 break;
158 }
159 }
160 } while (changed);
161
162 // iterate over all loads/stores with generic address space pointers
163 do {
164 changed = false;
165
166 for (inst_iterator i = inst_begin(F); i != inst_end(F); ++i) {
167 Instruction& instruction = (*i);
168
169 if (isa<LoadInst>(instruction) || isa<StoreInst>(instruction)) {
170 changed = visitLoadStoreInst(instruction);
171 }
172
173 if (changed) {
174 modified = true;
175 break;
176 }
177 }
178 } while (changed);
179
180 return modified;
181 }
182
getPointerAsIntType(LLVMContext & ctx,const unsigned AS)183 Type* GenericAddressDynamicResolution::getPointerAsIntType(LLVMContext& ctx, const unsigned AS)
184 {
185 Module* pModule = getModule();
186 DataLayout dataLayout = pModule->getDataLayout();
187 unsigned ptrBits(dataLayout.getPointerSizeInBits(AS));
188 return IntegerType::get(ctx, ptrBits);
189 }
190
visitLoadStoreInst(Instruction & I)191 bool GenericAddressDynamicResolution::visitLoadStoreInst(Instruction& I)
192 {
193 bool changed = false;
194
195 Value* pointerOperand = nullptr;
196 unsigned int pointerAddressSpace = ADDRESS_SPACE_NUM_ADDRESSES;
197
198 if (LoadInst * load = dyn_cast<LoadInst>(&I)) {
199 pointerOperand = load->getPointerOperand();
200 pointerAddressSpace = load->getPointerAddressSpace();
201 }
202 else if (StoreInst * store = dyn_cast<StoreInst>(&I)) {
203 pointerOperand = store->getPointerOperand();
204 pointerAddressSpace = store->getPointerAddressSpace();
205 }
206 else {
207 IGC_ASSERT_EXIT_MESSAGE(0, "Unable to resolve generic address space pointer");
208 }
209
210 if (pointerAddressSpace == ADDRESS_SPACE_GENERIC) {
211 if((m_ctx->allocatePrivateAsGlobalBuffer() || m_ctx->hasNoPrivateToGenericCast()) &&
212 m_ctx->hasNoLocalToGenericCast())
213 {
214 resolveGASWithoutBranches(I, pointerOperand);
215 }
216 else
217 {
218 resolveGAS(I, pointerOperand);
219 }
220 changed = true;
221 }
222
223 return changed;
224 }
225
resolveGAS(Instruction & I,Value * pointerOperand)226 void GenericAddressDynamicResolution::resolveGAS(Instruction& I, Value* pointerOperand)
227 {
228 std::stringstream warningInfo;
229 if (m_ctx->m_instrTypes.hasDebugInfo)
230 {
231 llvm::DILocation* dbInfo = I.getDebugLoc();
232 llvm::Instruction* prevInst = I.getPrevNode();
233
234 while (dbInfo == nullptr)
235 {
236 if (prevInst == nullptr)
237 {
238 break;
239 }
240 dbInfo = prevInst->getDebugLoc();
241 prevInst = prevInst->getPrevNode();
242 }
243 if (dbInfo != nullptr)
244 {
245 warningInfo << "from dir:" << dbInfo->getDirectory().str();
246 warningInfo << " from file:" << dbInfo->getFilename().str();
247 warningInfo << " line:" << dbInfo->getLine();
248 warningInfo << " :";
249 }
250 }
251 warningInfo << "Adding additional control flow due to presence of generic address space operations";
252 getAnalysis<CodeGenContextWrapper>().getCodeGenContext()->EmitWarning(warningInfo.str().c_str());
253
254 // Every time there is a load/store from/to a generic pointer, we have to resolve
255 // its corresponding address space by looking at its tag on bits[61:63].
256 // First, the generic pointer's tag is obtained to then perform the load/store
257 // with the corresponding address space.
258
259 IGCLLVM::IRBuilder<> builder(&I);
260 PointerType* pointerType = dyn_cast<PointerType>(pointerOperand->getType());
261 IGC_ASSERT( pointerType != nullptr );
262 ConstantInt* privateTag = builder.getInt64(1); // tag 001
263 ConstantInt* localTag = builder.getInt64(2); // tag 010
264
265 Type* intPtrTy = getPointerAsIntType(pointerOperand->getContext(), ADDRESS_SPACE_GENERIC);
266 Value* ptrAsInt = PtrToIntInst::Create(Instruction::PtrToInt, pointerOperand, intPtrTy, "", &I);
267 // Get actual tag
268 Value* tag = builder.CreateLShr(ptrAsInt, ConstantInt::get(ptrAsInt->getType(), 61));
269
270 // Three cases for private, local and global pointers
271 BasicBlock* currentBlock = I.getParent();
272 BasicBlock* convergeBlock = currentBlock->splitBasicBlock(&I);
273
274 BasicBlock* privateBlock = nullptr;
275 BasicBlock* localBlock = nullptr;
276 BasicBlock* globalBlock = nullptr;
277
278 Value* localLoad = nullptr;
279 Value* privateLoad = nullptr;
280 Value* globalLoad = nullptr;
281
282 // GAS needs to resolve to private only if
283 // 1) private is NOT allocated in global space; and
284 // 2) there is a cast from private to GAS.
285 bool hasPrivate = !(m_ctx->allocatePrivateAsGlobalBuffer() || m_ctx->hasNoPrivateToGenericCast());
286 bool hasLocal = !m_ctx->hasNoLocalToGenericCast();
287
288 auto createBlock = [&](const Twine& BlockName, const Twine& LoadName, IGC::ADDRESS_SPACE addressSpace, Value*& load)
289 {
290 BasicBlock* BB = BasicBlock::Create(I.getContext(), BlockName, convergeBlock->getParent(), convergeBlock);
291 builder.SetInsertPoint(BB);
292 PointerType* ptrType = pointerType->getElementType()->getPointerTo(addressSpace);
293 Value* ptr = builder.CreateAddrSpaceCast(pointerOperand, ptrType);
294
295 if (LoadInst* LI = dyn_cast<LoadInst>(&I))
296 {
297 load = builder.CreateAlignedLoad(ptr, getAlign(LI->getAlignment()), LI->isVolatile(), LoadName);
298 }
299 else if (StoreInst* SI = dyn_cast<StoreInst>(&I))
300 {
301 builder.CreateAlignedStore(I.getOperand(0), ptr, getAlign(SI->getAlignment()), SI->isVolatile());
302 }
303
304 builder.CreateBr(convergeBlock);
305 return BB;
306 };
307
308 // Private branch
309 if (hasPrivate)
310 {
311 privateBlock = createBlock("PrivateBlock", "privateLoad", ADDRESS_SPACE_PRIVATE, privateLoad);
312 }
313
314 // Local Branch
315 if (hasLocal)
316 {
317 localBlock = createBlock("LocalBlock", "localLoad", ADDRESS_SPACE_LOCAL, localLoad);
318 }
319
320 // Global Branch
321 globalBlock = createBlock("GlobalBlock", "globalLoad", ADDRESS_SPACE_GLOBAL, globalLoad);
322
323 currentBlock->getTerminator()->eraseFromParent();
324 builder.SetInsertPoint(currentBlock);
325
326 const int numPrivateLocal = (hasPrivate && hasLocal) ? 2 : ((hasPrivate || hasLocal) ? 1 : 0);
327 IGC_ASSERT(0 < numPrivateLocal);
328
329 {
330 SwitchInst* switchTag = builder.CreateSwitch(tag, globalBlock, numPrivateLocal);
331 // Based on tag there are two cases 001: private, 010: local, 000/111: global
332 if (hasPrivate)
333 {
334 switchTag->addCase(privateTag, privateBlock);
335 }
336 if (hasLocal)
337 {
338 switchTag->addCase(localTag, localBlock);
339 }
340
341 if (isa<LoadInst>(&I))
342 {
343 IGCLLVM::IRBuilder<> phiBuilder(&(*convergeBlock->begin()));
344 PHINode* phi = phiBuilder.CreatePHI(I.getType(), numPrivateLocal + 1, I.getName());
345 if (privateLoad)
346 {
347 phi->addIncoming(privateLoad, privateBlock);
348 }
349 if (localLoad)
350 {
351 phi->addIncoming(localLoad, localBlock);
352 }
353 phi->addIncoming(globalLoad, globalBlock);
354 I.replaceAllUsesWith(phi);
355 }
356 }
357
358 I.eraseFromParent();
359 }
360
resolveGASWithoutBranches(Instruction & I,Value * pointerOperand)361 void GenericAddressDynamicResolution::resolveGASWithoutBranches(Instruction& I, Value* pointerOperand)
362 {
363 IGCLLVM::IRBuilder<> builder(&I);
364 PointerType* pointerType = dyn_cast<PointerType>(pointerOperand->getType());
365 IGC_ASSERT( pointerType != nullptr );
366
367 Value* nonLocalLoad = nullptr;
368
369 PointerType* ptrType = pointerType->getElementType()->getPointerTo(ADDRESS_SPACE_GLOBAL);
370 Value* globalPtr = builder.CreateAddrSpaceCast(pointerOperand, ptrType);
371
372 if (LoadInst* LI = dyn_cast<LoadInst>(&I))
373 {
374 nonLocalLoad = builder.CreateAlignedLoad(globalPtr, getAlign(LI->getAlignment()), LI->isVolatile(), "globalOrPrivateLoad");
375 }
376 else if (StoreInst* SI = dyn_cast<StoreInst>(&I))
377 {
378 builder.CreateAlignedStore(I.getOperand(0), globalPtr, getAlign(SI->getAlignment()), SI->isVolatile());
379 }
380
381 if (nonLocalLoad != nullptr)
382 {
383 I.replaceAllUsesWith(nonLocalLoad);
384 }
385 I.eraseFromParent();
386 }
387
visitIntrinsicCall(CallInst & I)388 bool GenericAddressDynamicResolution::visitIntrinsicCall(CallInst& I)
389 {
390 bool changed = false;
391 Function* pCalledFunc = I.getCalledFunction();
392 if (pCalledFunc == nullptr)
393 {
394 // Indirect call
395 return false;
396 }
397
398 StringRef funcName = pCalledFunc->getName();
399
400 if ((funcName == "__builtin_IB_to_private") || (funcName == "__builtin_IB_to_local")
401 || (funcName == "__builtin_IB_to_global"))
402 {
403 IGC_ASSERT(I.getNumArgOperands() == 1);
404 Value* arg = I.getArgOperand(0);
405 PointerType* dstType = dyn_cast<PointerType>(I.getType());
406 IGC_ASSERT( dstType != nullptr );
407 const unsigned targetAS = cast<PointerType>(I.getType())->getAddressSpace();
408
409 IGCLLVM::IRBuilder<> builder(&I);
410 PointerType* pointerType = dyn_cast<PointerType>(arg->getType());
411 IGC_ASSERT( pointerType != nullptr );
412 ConstantInt* globalTag = builder.getInt64(0); // tag 000/111
413 ConstantInt* privateTag = builder.getInt64(1); // tag 001
414 ConstantInt* localTag = builder.getInt64(2); // tag 010
415
416 Type* intPtrTy = getPointerAsIntType(arg->getContext(), ADDRESS_SPACE_GENERIC);
417 Value* ptrAsInt = PtrToIntInst::Create(Instruction::PtrToInt, arg, intPtrTy, "", &I);
418 // Get actual tag
419 Value* tag = builder.CreateLShr(ptrAsInt, ConstantInt::get(ptrAsInt->getType(), 61));
420
421 Value* newPtr = nullptr;
422 Value* newPtrNull = nullptr;
423 Value* cmpTag = nullptr;
424
425 // Tag was already obtained from GAS pointer, now we check its address space (AS)
426 // and the target AS for this intrinsic call
427 if (targetAS == ADDRESS_SPACE_PRIVATE)
428 cmpTag = builder.CreateICmpEQ(tag, privateTag, "cmpTag");
429 else if (targetAS == ADDRESS_SPACE_LOCAL)
430 cmpTag = builder.CreateICmpEQ(tag, localTag, "cmpTag");
431 else if (targetAS == ADDRESS_SPACE_GLOBAL)
432 cmpTag = builder.CreateICmpEQ(tag, globalTag, "cmpTag");
433
434 // Two cases:
435 // 1: Generic pointer's AS matches with instrinsic's target AS
436 // So we create the address space cast
437 // 2: Generic pointer's AS does not match with instrinsic's target AS
438 // So the instrinsic call returns NULL
439 BasicBlock* currentBlock = I.getParent();
440 BasicBlock* convergeBlock = currentBlock->splitBasicBlock(&I);
441 BasicBlock* ifBlock = BasicBlock::Create(I.getContext(), "IfBlock",
442 convergeBlock->getParent(), convergeBlock);
443 BasicBlock* elseBlock = BasicBlock::Create(I.getContext(), "ElseBlock",
444 convergeBlock->getParent(), convergeBlock);
445
446 // If Block
447 {
448 IRBuilder<> ifBuilder(ifBlock);
449 PointerType* ptrType = pointerType->getElementType()->getPointerTo(targetAS);
450 newPtr = ifBuilder.CreateAddrSpaceCast(arg, ptrType);
451 ifBuilder.CreateBr(convergeBlock);
452 }
453
454 // Else Block
455 {
456 IRBuilder<> elseBuilder(elseBlock);
457 Value* ptrNull = Constant::getNullValue(I.getType());
458 newPtrNull = elseBuilder.CreatePointerCast(ptrNull, dstType, "");
459 elseBuilder.CreateBr(convergeBlock);
460 }
461
462 currentBlock->getTerminator()->eraseFromParent();
463 builder.SetInsertPoint(currentBlock);
464 builder.CreateCondBr(cmpTag, ifBlock, elseBlock);
465
466 IRBuilder<> phiBuilder(&(*convergeBlock->begin()));
467 PHINode* phi = phiBuilder.CreatePHI(I.getType(), 2, I.getName());
468 phi->addIncoming(newPtr, ifBlock);
469 phi->addIncoming(newPtrNull, elseBlock);
470 I.replaceAllUsesWith(phi);
471 I.eraseFromParent();
472 changed = true;
473 }
474
475 return changed;
476 }
477
478 namespace IGC {
createGenericAddressAnalysisPass()479 FunctionPass* createGenericAddressAnalysisPass()
480 {
481 return new GenericAddressAnalysis;
482 }
createGenericAddressDynamicResolutionPass()483 FunctionPass* createGenericAddressDynamicResolutionPass()
484 {
485 return new GenericAddressDynamicResolution;
486 }
487 } // namespace IGC
488