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