blob: 9e8651c95d48ce1495be34db85972d6b4b5ef61d [file] [log] [blame]
David Netoc5fb5242018-07-30 13:28:31 -04001// Copyright 2018 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <climits>
16#include <map>
17#include <set>
18#include <utility>
19#include <vector>
20
21#include "llvm/ADT/DenseMap.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/UniqueVector.h"
24#include "llvm/IR/Constants.h"
25#include "llvm/IR/DerivedTypes.h"
26#include "llvm/IR/Function.h"
27#include "llvm/IR/IRBuilder.h"
28#include "llvm/IR/Instructions.h"
29#include "llvm/IR/Module.h"
30#include "llvm/Pass.h"
alan-bakerf26dac92019-11-20 14:41:51 -050031#include "llvm/Support/CommandLine.h"
David Netoc5fb5242018-07-30 13:28:31 -040032#include "llvm/Support/raw_ostream.h"
33
34#include "clspv/Option.h"
David Netoc5fb5242018-07-30 13:28:31 -040035
36#include "ArgKind.h"
SJW61531372020-06-09 07:31:08 -050037#include "Builtins.h"
alan-bakera71f1932019-04-11 11:04:34 -040038#include "CallGraphOrderedFunctions.h"
Alan Baker202c8c72018-08-13 13:47:44 -040039#include "Constants.h"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040040#include "Passes.h"
David Netoc5fb5242018-07-30 13:28:31 -040041
42using namespace llvm;
43
44#define DEBUG_TYPE "directresourceaccess"
45
46namespace {
47
48cl::opt<bool> ShowDRA("show-dra", cl::init(false), cl::Hidden,
49 cl::desc("Show direct resource access details"));
50
51using SamplerMapType = llvm::ArrayRef<std::pair<unsigned, std::string>>;
52
53class DirectResourceAccessPass final : public ModulePass {
54public:
55 static char ID;
56 DirectResourceAccessPass() : ModulePass(ID) {}
57 bool runOnModule(Module &M) override;
58
59private:
David Netoc5fb5242018-07-30 13:28:31 -040060 // For each kernel argument that will map to a resource variable (descriptor),
61 // try to rewrite the uses of the argument as a direct access of the resource.
62 // We can only do this if all the callees of the function use the same
63 // resource access value for that argument. Returns true if the module
64 // changed.
65 bool RewriteResourceAccesses(Function *fn);
66
67 // Rewrite uses of this resrouce-based arg if all the callers pass in the
68 // same resource access. Returns true if the module changed.
69 bool RewriteAccessesForArg(Function *fn, int arg_index, Argument &arg);
70};
Diego Novilloa4c44fa2019-04-11 10:56:15 -040071
David Netoc5fb5242018-07-30 13:28:31 -040072} // namespace
73
74char DirectResourceAccessPass::ID = 0;
Diego Novilloa4c44fa2019-04-11 10:56:15 -040075INITIALIZE_PASS(DirectResourceAccessPass, "DirectResourceAccessPass",
76 "Direct resource access", false, false)
David Netoc5fb5242018-07-30 13:28:31 -040077
78namespace clspv {
79ModulePass *createDirectResourceAccessPass() {
80 return new DirectResourceAccessPass();
81}
82} // namespace clspv
83
84namespace {
85bool DirectResourceAccessPass::runOnModule(Module &M) {
86 bool Changed = false;
87
88 if (clspv::Option::DirectResourceAccess()) {
alan-bakera71f1932019-04-11 11:04:34 -040089 auto ordered_functions = clspv::CallGraphOrderedFunctions(M);
90 if (ShowDRA) {
91 outs() << "DRA: Ordered functions:\n";
92 for (Function *fun : ordered_functions) {
93 outs() << "DRA: " << fun->getName() << "\n";
94 }
95 }
96
David Netoc5fb5242018-07-30 13:28:31 -040097 for (auto *fn : ordered_functions) {
98 Changed |= RewriteResourceAccesses(fn);
99 }
100 }
101
102 return Changed;
103}
104
David Netoc5fb5242018-07-30 13:28:31 -0400105bool DirectResourceAccessPass::RewriteResourceAccesses(Function *fn) {
106 bool Changed = false;
107 int arg_index = 0;
108 for (Argument &arg : fn->args()) {
alan-bakerc4579bb2020-04-29 14:15:50 -0400109 switch (clspv::GetArgKind(arg)) {
David Netoc5fb5242018-07-30 13:28:31 -0400110 case clspv::ArgKind::Buffer:
Alan Bakerfcda9482018-10-02 17:09:59 -0400111 case clspv::ArgKind::BufferUBO:
alan-bakerf6bc8252020-09-23 14:58:55 -0400112 case clspv::ArgKind::SampledImage:
113 case clspv::ArgKind::StorageImage:
David Netoc5fb5242018-07-30 13:28:31 -0400114 case clspv::ArgKind::Sampler:
Alan Baker202c8c72018-08-13 13:47:44 -0400115 case clspv::ArgKind::Local:
David Netoc5fb5242018-07-30 13:28:31 -0400116 Changed |= RewriteAccessesForArg(fn, arg_index, arg);
117 break;
David Neto3a0df832018-08-03 14:35:42 -0400118 default:
119 // Should not happen
120 break;
David Netoc5fb5242018-07-30 13:28:31 -0400121 }
122 arg_index++;
123 }
124 return Changed;
125}
126
127bool DirectResourceAccessPass::RewriteAccessesForArg(Function *fn,
128 int arg_index,
129 Argument &arg) {
130 bool Changed = false;
131 if (fn->use_empty()) {
132 return false;
133 }
134
135 // We can convert a parameter to a direct resource access if it is
136 // either a direct call to a clspv.resource.var.* or if it a GEP of
137 // such a thing (where the GEP can only have zero indices).
138 struct ParamInfo {
Alan Baker202c8c72018-08-13 13:47:44 -0400139 // The base value. It is either a global variable or a resource-access
140 // builtin function. (@clspv.resource.var.* or @clspv.local.var.*)
141 Value *base;
David Netoc5fb5242018-07-30 13:28:31 -0400142 // The descriptor set.
143 uint32_t set;
144 // The binding.
145 uint32_t binding;
146 // If the parameter is a GEP, then this is the number of zero-indices
147 // the GEP used.
148 unsigned num_gep_zeroes;
149 // An example call fitting
150 CallInst *sample_call;
151 };
152 // The common valid parameter info across all the callers seen soo far.
153
154 bool seen_one = false;
155 ParamInfo common;
156 // Tries to merge the given parameter info into |common|. If it is the first
157 // time we've tried, then save it. Returns true if there is no conflict.
158 auto merge_param_info = [&seen_one, &common](const ParamInfo &pi) {
159 if (!seen_one) {
160 common = pi;
161 seen_one = true;
162 return true;
163 }
Alan Baker202c8c72018-08-13 13:47:44 -0400164 return pi.base == common.base && pi.set == common.set &&
David Netoc5fb5242018-07-30 13:28:31 -0400165 pi.binding == common.binding &&
166 pi.num_gep_zeroes == common.num_gep_zeroes;
167 };
168
169 for (auto &use : fn->uses()) {
170 if (auto *caller = dyn_cast<CallInst>(use.getUser())) {
171 Value *value = caller->getArgOperand(arg_index);
172 // We care about two cases:
173 // - a direct call to clspv.resource.var.*
174 // - a GEP with only zero indices, where the base pointer is
175
176 // Unpack GEPs with zeros, if we can. Rewrite |value| as we go along.
177 unsigned num_gep_zeroes = 0;
David Neto2f450002018-08-01 16:13:03 -0400178 bool first_gep = true;
David Netoc5fb5242018-07-30 13:28:31 -0400179 for (auto *gep = dyn_cast<GetElementPtrInst>(value); gep;
180 gep = dyn_cast<GetElementPtrInst>(value)) {
181 if (!gep->hasAllZeroIndices()) {
182 return false;
183 }
David Neto2f450002018-08-01 16:13:03 -0400184 // If not the first GEP, then ignore the "element" index (which I call
185 // "slide") since that will be combined with the last index of the
186 // previous GEP.
187 num_gep_zeroes += gep->getNumIndices() + (first_gep ? 0 : -1);
David Netoc5fb5242018-07-30 13:28:31 -0400188 value = gep->getPointerOperand();
David Neto2f450002018-08-01 16:13:03 -0400189 first_gep = false;
David Netoc5fb5242018-07-30 13:28:31 -0400190 }
191 if (auto *call = dyn_cast<CallInst>(value)) {
192 // If the call is a call to a @clspv.resource.var.* function, then try
193 // to merge it, assuming the given number of GEP zero-indices so far.
SJW61531372020-06-09 07:31:08 -0500194 auto *callee = call->getCalledFunction();
195 auto &func_info = clspv::Builtins::Lookup(callee);
196 if (func_info.getType() == clspv::Builtins::kClspvResource) {
David Netoc5fb5242018-07-30 13:28:31 -0400197 const auto set = uint32_t(
198 dyn_cast<ConstantInt>(call->getOperand(0))->getZExtValue());
199 const auto binding = uint32_t(
200 dyn_cast<ConstantInt>(call->getOperand(1))->getZExtValue());
SJW61531372020-06-09 07:31:08 -0500201 if (!merge_param_info({callee, set, binding, num_gep_zeroes, call}))
David Netoc5fb5242018-07-30 13:28:31 -0400202 return false;
SJW61531372020-06-09 07:31:08 -0500203 } else if (func_info.getType() == clspv::Builtins::kClspvLocal) {
Alan Baker202c8c72018-08-13 13:47:44 -0400204 const uint32_t spec_id = uint32_t(
205 dyn_cast<ConstantInt>(call->getOperand(0))->getZExtValue());
SJW61531372020-06-09 07:31:08 -0500206 if (!merge_param_info({callee, spec_id, 0, num_gep_zeroes, call}))
Alan Baker202c8c72018-08-13 13:47:44 -0400207 return false;
David Netoc5fb5242018-07-30 13:28:31 -0400208 } else {
209 // A call but not to a resource access builtin function.
210 return false;
211 }
Alan Baker202c8c72018-08-13 13:47:44 -0400212 } else if (isa<GlobalValue>(value)) {
213 if (!merge_param_info({value, 0, 0, num_gep_zeroes, nullptr}))
214 return false;
David Netoc5fb5242018-07-30 13:28:31 -0400215 } else {
216 // Not a call.
217 return false;
218 }
219 } else {
220 // There isn't enough commonality. Bail out without changing anything.
221 return false;
222 }
223 }
224 if (ShowDRA) {
225 if (seen_one) {
226 outs() << "DRA: Rewrite " << fn->getName() << " arg " << arg_index << " "
Alan Baker202c8c72018-08-13 13:47:44 -0400227 << arg.getName() << ": " << common.base->getName() << " ("
David Netoc5fb5242018-07-30 13:28:31 -0400228 << common.set << "," << common.binding
Alan Bakerfc6888e2018-08-20 20:54:33 -0400229 << ") zeroes: " << common.num_gep_zeroes << " sample-call ";
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400230 if (common.sample_call)
231 outs() << *common.sample_call << "\n";
232 else
233 outs() << "nullptr\n";
David Netoc5fb5242018-07-30 13:28:31 -0400234 }
235 }
236
237 // Now rewrite the argument, using the info in |common|.
238
239 Changed = true;
240 IRBuilder<> Builder(fn->getParent()->getContext());
241 auto *zero = Builder.getInt32(0);
242 Builder.SetInsertPoint(fn->getEntryBlock().getFirstNonPHI());
243
Alan Baker202c8c72018-08-13 13:47:44 -0400244 Value *replacement = common.base;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400245 if (Function *function = dyn_cast<Function>(replacement)) {
Alan Baker202c8c72018-08-13 13:47:44 -0400246 // Create the call.
247 SmallVector<Value *, 8> args(common.sample_call->arg_begin(),
248 common.sample_call->arg_end());
249 replacement = Builder.CreateCall(function, args);
250 if (ShowDRA) {
251 outs() << "DRA: Replace: call " << *replacement << "\n";
252 }
David Netoc5fb5242018-07-30 13:28:31 -0400253 }
254 if (common.num_gep_zeroes) {
255 SmallVector<Value *, 3> zeroes;
256 for (unsigned i = 0; i < common.num_gep_zeroes; i++) {
257 zeroes.push_back(zero);
258 }
Alan Baker202c8c72018-08-13 13:47:44 -0400259 // Builder.CreateGEP is not used to avoid creating a GEPConstantExpr in the
260 // case of global variables.
James Price06f91142021-07-15 20:03:29 -0400261 replacement = GetElementPtrInst::Create(
262 replacement->getType()->getPointerElementType(), replacement, zeroes);
Alan Baker202c8c72018-08-13 13:47:44 -0400263 Builder.Insert(cast<Instruction>(replacement));
David Netoc5fb5242018-07-30 13:28:31 -0400264 if (ShowDRA) {
265 outs() << "DRA: Replace: gep " << *replacement << "\n";
266 }
267 }
268 arg.replaceAllUsesWith(replacement);
269
270 return Changed;
271}
272
273} // namespace