R600KernelParameters.cpp revision a75c6163e605f35b14f26930dd9227e4f337ec9e
1//===-- R600KernelParameters.cpp - TODO: Add brief description -------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// TODO: Add full description
11//
12//===----------------------------------------------------------------------===//
13
14#include <llvm-c/Core.h>
15#include "R600KernelParameters.h"
16#include "R600OpenCLUtils.h"
17#include "llvm/Constants.h"
18#include "llvm/Intrinsics.h"
19#include "llvm/Support/IRBuilder.h"
20#include "llvm/Support/TypeBuilder.h"
21// #include "llvm/CodeGen/Function.h"
22
23namespace AMDILAS {
24enum AddressSpaces {
25  PRIVATE_ADDRESS  = 0, // Address space for private memory.
26  GLOBAL_ADDRESS   = 1, // Address space for global memory (RAT0, VTX0).
27  CONSTANT_ADDRESS = 2, // Address space for constant memory.
28  LOCAL_ADDRESS    = 3, // Address space for local memory.
29  REGION_ADDRESS   = 4, // Address space for region memory.
30  ADDRESS_NONE     = 5, // Address space for unknown memory.
31  PARAM_D_ADDRESS  = 6, // Address space for direct addressible parameter memory (CONST0)
32  PARAM_I_ADDRESS  = 7, // Address space for indirect addressible parameter memory (VTX1)
33  LAST_ADDRESS     = 8
34};
35}
36
37
38#include <map>
39#include <set>
40
41using namespace llvm;
42using namespace std;
43
44#define CONSTANT_CACHE_SIZE_DW 127
45
46class R600KernelParameters : public llvm::FunctionPass
47{
48  const llvm::TargetData * TD;
49  LLVMContext* Context;
50  Module *mod;
51
52  struct param
53  {
54    param() : val(NULL), ptr_val(NULL), offset_in_dw(0), size_in_dw(0), indirect(false), specialID(0) {}
55
56    llvm::Value* val;
57    llvm::Value* ptr_val;
58    int offset_in_dw;
59    int size_in_dw;
60
61    bool indirect;
62
63    string specialType;
64    int specialID;
65
66    int end() { return offset_in_dw + size_in_dw; }
67    /* The first 9 dwords are reserved for the grid sizes. */
68    int get_rat_offset() { return 9 + offset_in_dw; }
69  };
70
71  std::vector<param> params;
72
73  int getLastSpecialID(const string& TypeName);
74
75  int getListSize();
76  void AddParam(llvm::Argument* arg);
77  int calculateArgumentSize(llvm::Argument* arg);
78  void RunAna(llvm::Function* fun);
79  void Replace(llvm::Function* fun);
80  bool isIndirect(Value* val, set<Value*>& visited);
81  void Propagate(llvm::Function* fun);
82  void Propagate(llvm::Value* v, const llvm::Twine& name, bool indirect = false);
83  Value* ConstantRead(Function* fun, param& p);
84  Value* handleSpecial(Function* fun, param& p);
85  bool isSpecialType(Type*);
86  string getSpecialTypeName(Type*);
87public:
88  static char ID;
89  R600KernelParameters() : FunctionPass(ID) {};
90  R600KernelParameters(const llvm::TargetData* TD) : FunctionPass(ID), TD(TD) {}
91//   bool runOnFunction (llvm::Function &F);
92  bool runOnFunction (llvm::Function &F);
93  void getAnalysisUsage(AnalysisUsage &AU) const;
94  const char *getPassName() const;
95  bool doInitialization(Module &M);
96  bool doFinalization(Module &M);
97};
98
99char R600KernelParameters::ID = 0;
100
101static RegisterPass<R600KernelParameters> X("kerparam", "OpenCL Kernel Parameter conversion", false, false);
102
103int R600KernelParameters::getLastSpecialID(const string& TypeName)
104{
105  int lastID = -1;
106
107  for (vector<param>::iterator i = params.begin(); i != params.end(); i++)
108  {
109    if (i->specialType == TypeName)
110    {
111      lastID = i->specialID;
112    }
113  }
114
115  return lastID;
116}
117
118int R600KernelParameters::getListSize()
119{
120  if (params.size() == 0)
121  {
122    return 0;
123  }
124
125  return params.back().end();
126}
127
128bool R600KernelParameters::isIndirect(Value* val, set<Value*>& visited)
129{
130  if (isa<LoadInst>(val))
131  {
132    return false;
133  }
134
135  if (isa<IntegerType>(val->getType()))
136  {
137    assert(0 and "Internal error");
138    return false;
139  }
140
141  if (visited.count(val))
142  {
143    return false;
144  }
145
146  visited.insert(val);
147
148  if (isa<GetElementPtrInst>(val))
149  {
150    GetElementPtrInst* GEP = dyn_cast<GetElementPtrInst>(val);
151    GetElementPtrInst::op_iterator i = GEP->op_begin();
152
153    for (i++; i != GEP->op_end(); i++)
154    {
155      if (!isa<Constant>(*i))
156      {
157        return true;
158      }
159    }
160  }
161
162  for (Value::use_iterator i = val->use_begin(); i != val->use_end(); i++)
163  {
164    Value* v2 = dyn_cast<Value>(*i);
165
166    if (v2)
167    {
168      if (isIndirect(v2, visited))
169      {
170        return true;
171      }
172    }
173  }
174
175  return false;
176}
177
178void R600KernelParameters::AddParam(llvm::Argument* arg)
179{
180  param p;
181
182  p.val = dyn_cast<Value>(arg);
183  p.offset_in_dw = getListSize();
184  p.size_in_dw = calculateArgumentSize(arg);
185
186  if (isa<PointerType>(arg->getType()) and arg->hasByValAttr())
187  {
188    set<Value*> visited;
189    p.indirect = isIndirect(p.val, visited);
190  }
191
192  params.push_back(p);
193}
194
195int R600KernelParameters::calculateArgumentSize(llvm::Argument* arg)
196{
197  Type* t = arg->getType();
198
199  if (arg->hasByValAttr() and dyn_cast<PointerType>(t))
200  {
201    t = dyn_cast<PointerType>(t)->getElementType();
202  }
203
204  int store_size_in_dw = (TD->getTypeStoreSize(t) + 3)/4;
205
206  assert(store_size_in_dw);
207
208  return store_size_in_dw;
209}
210
211
212void R600KernelParameters::RunAna(llvm::Function* fun)
213{
214  assert(isOpenCLKernel(fun));
215
216  for (Function::arg_iterator i = fun->arg_begin(); i != fun->arg_end(); i++)
217  {
218    AddParam(i);
219  }
220
221}
222
223void R600KernelParameters::Replace(llvm::Function* fun)
224{
225  for (std::vector<param>::iterator i = params.begin(); i != params.end(); i++)
226  {
227    Value *new_val;
228
229    if (isSpecialType(i->val->getType()))
230    {
231      new_val = handleSpecial(fun, *i);
232    }
233    else
234    {
235      new_val = ConstantRead(fun, *i);
236    }
237    if (new_val)
238    {
239      i->val->replaceAllUsesWith(new_val);
240    }
241  }
242}
243
244void R600KernelParameters::Propagate(llvm::Function* fun)
245{
246  for (std::vector<param>::iterator i = params.begin(); i != params.end(); i++)
247  {
248    if (i->ptr_val)
249    {
250      Propagate(i->ptr_val, i->val->getName(), i->indirect);
251   }
252  }
253}
254
255void R600KernelParameters::Propagate(Value* v, const Twine& name, bool indirect)
256{
257  LoadInst* load = dyn_cast<LoadInst>(v);
258  GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(v);
259
260  unsigned addrspace;
261
262  if (indirect)
263  {
264    addrspace = AMDILAS::PARAM_I_ADDRESS;
265  }
266  else
267  {
268    addrspace = AMDILAS::PARAM_D_ADDRESS;
269  }
270
271  if (GEP and GEP->getType()->getAddressSpace() != addrspace)
272  {
273    Value* op = GEP->getPointerOperand();
274
275    if (dyn_cast<PointerType>(op->getType())->getAddressSpace() != addrspace)
276    {
277      op = new BitCastInst(op, PointerType::get(dyn_cast<PointerType>(op->getType())->getElementType(), addrspace), name, dyn_cast<Instruction>(v));
278    }
279
280    vector<Value*> params(GEP->idx_begin(), GEP->idx_end());
281
282    GetElementPtrInst* GEP2 = GetElementPtrInst::Create(op, params, name, dyn_cast<Instruction>(v));
283    GEP2->setIsInBounds(GEP->isInBounds());
284    v = dyn_cast<Value>(GEP2);
285    GEP->replaceAllUsesWith(GEP2);
286    GEP->eraseFromParent();
287    load = NULL;
288  }
289
290  if (load)
291  {
292    if (load->getPointerAddressSpace() != addrspace) ///normally at this point we have the right address space
293    {
294      Value *orig_ptr = load->getPointerOperand();
295      PointerType *orig_ptr_type = dyn_cast<PointerType>(orig_ptr->getType());
296
297      Type* new_ptr_type = PointerType::get(orig_ptr_type->getElementType(), addrspace);
298
299      Value* new_ptr = orig_ptr;
300
301      if (orig_ptr->getType() != new_ptr_type)
302      {
303        new_ptr = new BitCastInst(orig_ptr, new_ptr_type, "prop_cast", load);
304      }
305
306      Value* new_load = new LoadInst(new_ptr, name, load);
307      load->replaceAllUsesWith(new_load);
308      load->eraseFromParent();
309    }
310
311    return;
312  }
313
314  vector<User*> users(v->use_begin(), v->use_end());
315
316  for (int i = 0; i < int(users.size()); i++)
317  {
318    Value* v2 = dyn_cast<Value>(users[i]);
319
320    if (v2)
321    {
322      Propagate(v2, name, indirect);
323    }
324  }
325}
326
327Value* R600KernelParameters::ConstantRead(Function* fun, param& p)
328{
329  assert(fun->front().begin() != fun->front().end());
330
331  Instruction *first_inst = fun->front().begin();
332  IRBuilder <> builder (first_inst);
333/* First 3 dwords are reserved for the dimmension info */
334
335  if (!p.val->hasNUsesOrMore(1))
336  {
337    return NULL;
338  }
339  unsigned addrspace;
340
341  if (p.indirect)
342  {
343    addrspace = AMDILAS::PARAM_I_ADDRESS;
344  }
345  else
346  {
347    addrspace = AMDILAS::PARAM_D_ADDRESS;
348  }
349
350  Argument *arg = dyn_cast<Argument>(p.val);
351  Type * argType = p.val->getType();
352  PointerType * argPtrType = dyn_cast<PointerType>(p.val->getType());
353
354  if (argPtrType and arg->hasByValAttr())
355  {
356    Value* param_addr_space_ptr = ConstantPointerNull::get(PointerType::get(Type::getInt32Ty(*Context), addrspace));
357    Value* param_ptr = GetElementPtrInst::Create(param_addr_space_ptr, ConstantInt::get(Type::getInt32Ty(*Context), p.get_rat_offset()), arg->getName(), first_inst);
358    param_ptr = new BitCastInst(param_ptr, PointerType::get(argPtrType->getElementType(), addrspace), arg->getName(), first_inst);
359    p.ptr_val = param_ptr;
360    return param_ptr;
361  }
362  else
363  {
364    Value* param_addr_space_ptr = ConstantPointerNull::get(PointerType::get(argType, addrspace));
365
366    Value* param_ptr = builder.CreateGEP(param_addr_space_ptr,
367             ConstantInt::get(Type::getInt32Ty(*Context), p.get_rat_offset()), arg->getName());
368
369    Value* param_value = builder.CreateLoad(param_ptr, arg->getName());
370
371    return param_value;
372  }
373}
374
375Value* R600KernelParameters::handleSpecial(Function* fun, param& p)
376{
377  string name = getSpecialTypeName(p.val->getType());
378  int ID;
379
380  assert(!name.empty());
381
382  if (name == "image2d_t" or name == "image3d_t")
383  {
384    int lastID = max(getLastSpecialID("image2d_t"), getLastSpecialID("image3d_t"));
385
386    if (lastID == -1)
387    {
388      ID = 2; ///ID0 and ID1 are used internally by the driver
389    }
390    else
391    {
392      ID = lastID + 1;
393    }
394  }
395  else if (name == "sampler_t")
396  {
397    int lastID = getLastSpecialID("sampler_t");
398
399    if (lastID == -1)
400    {
401      ID = 0;
402    }
403    else
404    {
405      ID = lastID + 1;
406    }
407  }
408  else
409  {
410    ///TODO: give some error message
411    return NULL;
412  }
413
414  p.specialType = name;
415  p.specialID = ID;
416
417  Instruction *first_inst = fun->front().begin();
418
419  return new IntToPtrInst(ConstantInt::get(Type::getInt32Ty(*Context), p.specialID), p.val->getType(), "resourceID", first_inst);
420}
421
422
423bool R600KernelParameters::isSpecialType(Type* t)
424{
425  return !getSpecialTypeName(t).empty();
426}
427
428string R600KernelParameters::getSpecialTypeName(Type* t)
429{
430  PointerType *pt = dyn_cast<PointerType>(t);
431  StructType *st = NULL;
432
433  if (pt)
434  {
435    st = dyn_cast<StructType>(pt->getElementType());
436  }
437
438  if (st)
439  {
440    string prefix = "struct.opencl_builtin_type_";
441
442    string name = st->getName().str();
443
444    if (name.substr(0, prefix.length()) == prefix)
445    {
446      return name.substr(prefix.length(), name.length());
447    }
448  }
449
450  return "";
451}
452
453
454bool R600KernelParameters::runOnFunction (Function &F)
455{
456  if (!isOpenCLKernel(&F))
457  {
458    return false;
459  }
460
461//  F.dump();
462
463  RunAna(&F);
464  Replace(&F);
465  Propagate(&F);
466
467   mod->dump();
468  return false;
469}
470
471void R600KernelParameters::getAnalysisUsage(AnalysisUsage &AU) const
472{
473//   AU.addRequired<FunctionAnalysis>();
474  FunctionPass::getAnalysisUsage(AU);
475  AU.setPreservesAll();
476}
477
478const char *R600KernelParameters::getPassName() const
479{
480  return "OpenCL Kernel parameter conversion to memory";
481}
482
483bool R600KernelParameters::doInitialization(Module &M)
484{
485  Context = &M.getContext();
486  mod = &M;
487
488  return false;
489}
490
491bool R600KernelParameters::doFinalization(Module &M)
492{
493  return false;
494}
495
496llvm::FunctionPass* createR600KernelParametersPass(const llvm::TargetData* TD)
497{
498  FunctionPass *p = new R600KernelParameters(TD);
499
500  return p;
501}
502
503
504