1 //===-- NVPTXImageOptimizer.cpp - Image optimization pass -----------------===//
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 // This pass implements IR-level optimizations of image access code,
10 // including:
11 //
12 // 1. Eliminate istypep intrinsics when image access qualifier is known
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "NVPTX.h"
17 #include "NVPTXUtilities.h"
18 #include "llvm/Analysis/ConstantFolding.h"
19 #include "llvm/IR/Instructions.h"
20 #include "llvm/IR/Intrinsics.h"
21 #include "llvm/IR/IntrinsicsNVPTX.h"
22 #include "llvm/IR/Module.h"
23 #include "llvm/Pass.h"
24 
25 using namespace llvm;
26 
27 namespace {
28 class NVPTXImageOptimizer : public FunctionPass {
29 private:
30   static char ID;
31   SmallVector<Instruction*, 4> InstrToDelete;
32 
33 public:
34   NVPTXImageOptimizer();
35 
36   bool runOnFunction(Function &F) override;
37 
38 private:
39   bool replaceIsTypePSampler(Instruction &I);
40   bool replaceIsTypePSurface(Instruction &I);
41   bool replaceIsTypePTexture(Instruction &I);
42   Value *cleanupValue(Value *V);
43   void replaceWith(Instruction *From, ConstantInt *To);
44 };
45 }
46 
47 char NVPTXImageOptimizer::ID = 0;
48 
49 NVPTXImageOptimizer::NVPTXImageOptimizer()
50   : FunctionPass(ID) {}
51 
52 bool NVPTXImageOptimizer::runOnFunction(Function &F) {
53   if (skipFunction(F))
54     return false;
55 
56   bool Changed = false;
57   InstrToDelete.clear();
58 
59   // Look for call instructions in the function
60   for (BasicBlock &BB : F) {
61     for (Instruction &Instr : BB) {
62       if (CallInst *CI = dyn_cast<CallInst>(&Instr)) {
63         Function *CalledF = CI->getCalledFunction();
64         if (CalledF && CalledF->isIntrinsic()) {
65           // This is an intrinsic function call, check if its an istypep
66           switch (CalledF->getIntrinsicID()) {
67           default: break;
68           case Intrinsic::nvvm_istypep_sampler:
69             Changed |= replaceIsTypePSampler(Instr);
70             break;
71           case Intrinsic::nvvm_istypep_surface:
72             Changed |= replaceIsTypePSurface(Instr);
73             break;
74           case Intrinsic::nvvm_istypep_texture:
75             Changed |= replaceIsTypePTexture(Instr);
76             break;
77           }
78         }
79       }
80     }
81   }
82 
83   // Delete any istypep instances we replaced in the IR
84   for (Instruction *I : InstrToDelete)
85     I->eraseFromParent();
86 
87   return Changed;
88 }
89 
90 bool NVPTXImageOptimizer::replaceIsTypePSampler(Instruction &I) {
91   Value *TexHandle = cleanupValue(I.getOperand(0));
92   if (isSampler(*TexHandle)) {
93     // This is an OpenCL sampler, so it must be a samplerref
94     replaceWith(&I, ConstantInt::getTrue(I.getContext()));
95     return true;
96   } else if (isImage(*TexHandle)) {
97     // This is an OpenCL image, so it cannot be a samplerref
98     replaceWith(&I, ConstantInt::getFalse(I.getContext()));
99     return true;
100   } else {
101     // The image type is unknown, so we cannot eliminate the intrinsic
102     return false;
103   }
104 }
105 
106 bool NVPTXImageOptimizer::replaceIsTypePSurface(Instruction &I) {
107   Value *TexHandle = cleanupValue(I.getOperand(0));
108   if (isImageReadWrite(*TexHandle) ||
109       isImageWriteOnly(*TexHandle)) {
110     // This is an OpenCL read-only/read-write image, so it must be a surfref
111     replaceWith(&I, ConstantInt::getTrue(I.getContext()));
112     return true;
113   } else if (isImageReadOnly(*TexHandle) ||
114              isSampler(*TexHandle)) {
115     // This is an OpenCL read-only/ imageor sampler, so it cannot be
116     // a surfref
117     replaceWith(&I, ConstantInt::getFalse(I.getContext()));
118     return true;
119   } else {
120     // The image type is unknown, so we cannot eliminate the intrinsic
121     return false;
122   }
123 }
124 
125 bool NVPTXImageOptimizer::replaceIsTypePTexture(Instruction &I) {
126   Value *TexHandle = cleanupValue(I.getOperand(0));
127   if (isImageReadOnly(*TexHandle)) {
128     // This is an OpenCL read-only image, so it must be a texref
129     replaceWith(&I, ConstantInt::getTrue(I.getContext()));
130     return true;
131   } else if (isImageWriteOnly(*TexHandle) ||
132              isImageReadWrite(*TexHandle) ||
133              isSampler(*TexHandle)) {
134     // This is an OpenCL read-write/write-only image or a sampler, so it
135     // cannot be a texref
136     replaceWith(&I, ConstantInt::getFalse(I.getContext()));
137     return true;
138   } else {
139     // The image type is unknown, so we cannot eliminate the intrinsic
140     return false;
141   }
142 }
143 
144 void NVPTXImageOptimizer::replaceWith(Instruction *From, ConstantInt *To) {
145   // We implement "poor man's DCE" here to make sure any code that is no longer
146   // live is actually unreachable and can be trivially eliminated by the
147   // unreachable block elimination pass.
148   for (Use &U : From->uses()) {
149     if (BranchInst *BI = dyn_cast<BranchInst>(U)) {
150       if (BI->isUnconditional()) continue;
151       BasicBlock *Dest;
152       if (To->isZero())
153         // Get false block
154         Dest = BI->getSuccessor(1);
155       else
156         // Get true block
157         Dest = BI->getSuccessor(0);
158       BranchInst::Create(Dest, BI);
159       InstrToDelete.push_back(BI);
160     }
161   }
162   From->replaceAllUsesWith(To);
163   InstrToDelete.push_back(From);
164 }
165 
166 Value *NVPTXImageOptimizer::cleanupValue(Value *V) {
167   if (ExtractValueInst *EVI = dyn_cast<ExtractValueInst>(V)) {
168     return cleanupValue(EVI->getAggregateOperand());
169   }
170   return V;
171 }
172 
173 FunctionPass *llvm::createNVPTXImageOptimizerPass() {
174   return new NVPTXImageOptimizer();
175 }
176