1//===-- R600KernelParameters.cpp - Lower kernel function arguments --------===//
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// This pass lowers kernel function arguments to loads from the vertex buffer.
11//
12// Kernel arguemnts are stored in the vertex buffer at an offset of 9 dwords,
13// so arg0 needs to be loaded from VTX_BUFFER[9] and arg1 is loaded from
14// VTX_BUFFER[10], etc.
15//
16//===----------------------------------------------------------------------===//
17
18#include "AMDGPU.h"
19#include "AMDIL.h"
20#include "llvm/CodeGen/MachineFunctionPass.h"
21#include "llvm/Constants.h"
22#include "llvm/Function.h"
23#include "llvm/Intrinsics.h"
24#include "llvm/Metadata.h"
25#include "llvm/Module.h"
26#include "llvm/Target/TargetData.h"
27#include "llvm/Support/IRBuilder.h"
28#include "llvm/Support/TypeBuilder.h"
29
30#include <map>
31#include <set>
32
33using namespace llvm;
34
35namespace {
36
37#define CONSTANT_CACHE_SIZE_DW 127
38
39class R600KernelParameters : public FunctionPass {
40  const TargetData *TD;
41  LLVMContext* Context;
42  Module *Mod;
43
44  struct Param {
45    Param() : Val(NULL), PtrVal(NULL), OffsetInDW(0), SizeInDW(0),
46              IsIndirect(true), SpecialID(0) {}
47
48    Value* Val;
49    Value* PtrVal;
50    int OffsetInDW;
51    int SizeInDW;
52
53    bool IsIndirect;
54
55    std::string SpecialType;
56    int SpecialID;
57
58    int End() { return OffsetInDW + SizeInDW; }
59    // The first 9 dwords are reserved for the grid sizes.
60    int getRatOffset() { return 9 + OffsetInDW; }
61  };
62
63  std::vector<Param> Params;
64
65  bool IsOpenCLKernel(const Function *Fun);
66  int getLastSpecialID(const std::string& TypeName);
67
68  int getListSize();
69  void AddParam(Argument *Arg);
70  int CalculateArgumentSize(Argument *Arg);
71  void RunAna(Function *Fun);
72  void Replace(Function *Fun);
73  bool IsIndirect(Value *Val, std::set<Value*> &Visited);
74  void Propagate(Function* Fun);
75  void Propagate(Value *V, const Twine &Name, bool IsIndirect = true);
76  Value* ConstantRead(Function *Fun, Param &P);
77  Value* handleSpecial(Function *Fun, Param &P);
78  bool IsSpecialType(Type *T);
79  std::string getSpecialTypeName(Type *T);
80public:
81  static char ID;
82  R600KernelParameters() : FunctionPass(ID) {};
83  R600KernelParameters(const TargetData* TD) : FunctionPass(ID), TD(TD) {}
84  bool runOnFunction (Function &F);
85  void getAnalysisUsage(AnalysisUsage &AU) const;
86  const char *getPassName() const;
87  bool doInitialization(Module &M);
88  bool doFinalization(Module &M);
89};
90
91char R600KernelParameters::ID = 0;
92
93static RegisterPass<R600KernelParameters> X("kerparam",
94                            "OpenCL Kernel Parameter conversion", false, false);
95
96bool R600KernelParameters::IsOpenCLKernel(const Function* Fun) {
97  Module *Mod = const_cast<Function*>(Fun)->getParent();
98  NamedMDNode * MD = Mod->getOrInsertNamedMetadata("opencl.kernels");
99
100  if (!MD || !MD->getNumOperands()) {
101    return false;
102  }
103
104  for (int i = 0; i < int(MD->getNumOperands()); i++) {
105    if (!MD->getOperand(i) || !MD->getOperand(i)->getOperand(0)) {
106      continue;
107    }
108
109    assert(MD->getOperand(i)->getNumOperands() == 1);
110
111    if (MD->getOperand(i)->getOperand(0)->getName() == Fun->getName()) {
112      return true;
113    }
114  }
115
116  return false;
117}
118
119int R600KernelParameters::getLastSpecialID(const std::string &TypeName) {
120  int LastID = -1;
121
122  for (std::vector<Param>::iterator i = Params.begin(); i != Params.end(); i++) {
123    if (i->SpecialType == TypeName) {
124      LastID = i->SpecialID;
125    }
126  }
127
128  return LastID;
129}
130
131int R600KernelParameters::getListSize() {
132  if (Params.size() == 0) {
133    return 0;
134  }
135
136  return Params.back().End();
137}
138
139bool R600KernelParameters::IsIndirect(Value *Val, std::set<Value*> &Visited) {
140  //XXX Direct parameters are not supported yet, so return true here.
141  return true;
142#if 0
143  if (isa<LoadInst>(Val)) {
144    return false;
145  }
146
147  if (isa<IntegerType>(Val->getType())) {
148    assert(0 && "Internal error");
149    return false;
150  }
151
152  if (Visited.count(Val)) {
153    return false;
154  }
155
156  Visited.insert(Val);
157
158  if (isa<getElementPtrInst>(Val)) {
159    getElementPtrInst* GEP = dyn_cast<getElementPtrInst>(Val);
160    getElementPtrInst::op_iterator I = GEP->op_begin();
161
162    for (++I; I != GEP->op_end(); ++I) {
163      if (!isa<Constant>(*I)) {
164        return true;
165      }
166    }
167  }
168
169  for (Value::use_iterator I = Val->use_begin(); i != Val->use_end(); ++I) {
170    Value* V2 = dyn_cast<Value>(*I);
171
172    if (V2) {
173      if (IsIndirect(V2, Visited)) {
174        return true;
175      }
176    }
177  }
178
179  return false;
180#endif
181}
182
183void R600KernelParameters::AddParam(Argument *Arg) {
184  Param P;
185
186  P.Val = dyn_cast<Value>(Arg);
187  P.OffsetInDW = getListSize();
188  P.SizeInDW = CalculateArgumentSize(Arg);
189
190  if (isa<PointerType>(Arg->getType()) && Arg->hasByValAttr()) {
191    std::set<Value*> Visited;
192    P.IsIndirect = IsIndirect(P.Val, Visited);
193  }
194
195  Params.push_back(P);
196}
197
198int R600KernelParameters::CalculateArgumentSize(Argument *Arg) {
199  Type* T = Arg->getType();
200
201  if (Arg->hasByValAttr() && dyn_cast<PointerType>(T)) {
202    T = dyn_cast<PointerType>(T)->getElementType();
203  }
204
205  int StoreSizeInDW = (TD->getTypeStoreSize(T) + 3)/4;
206
207  assert(StoreSizeInDW);
208
209  return StoreSizeInDW;
210}
211
212
213void R600KernelParameters::RunAna(Function* Fun) {
214  assert(IsOpenCLKernel(Fun));
215
216  for (Function::arg_iterator I = Fun->arg_begin(); I != Fun->arg_end(); ++I) {
217    AddParam(I);
218  }
219
220}
221
222void R600KernelParameters::Replace(Function* Fun) {
223  for (std::vector<Param>::iterator I = Params.begin(); I != Params.end(); ++I) {
224    Value *NewVal;
225
226    if (IsSpecialType(I->Val->getType())) {
227      NewVal = handleSpecial(Fun, *I);
228    } else {
229      NewVal = ConstantRead(Fun, *I);
230    }
231    if (NewVal) {
232      I->Val->replaceAllUsesWith(NewVal);
233    }
234  }
235}
236
237void R600KernelParameters::Propagate(Function* Fun) {
238  for (std::vector<Param>::iterator I = Params.begin(); I != Params.end(); ++I) {
239    if (I->PtrVal) {
240      Propagate(I->PtrVal, I->Val->getName(), I->IsIndirect);
241    }
242  }
243}
244
245void R600KernelParameters::Propagate(Value* V, const Twine& Name, bool IsIndirect) {
246  LoadInst* Load = dyn_cast<LoadInst>(V);
247  GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V);
248
249  unsigned Addrspace;
250
251  if (IsIndirect) {
252    Addrspace = AMDGPUAS::PARAM_I_ADDRESS;
253  }  else {
254    Addrspace = AMDGPUAS::PARAM_D_ADDRESS;
255  }
256
257  if (GEP && GEP->getType()->getAddressSpace() != Addrspace) {
258    Value *Op = GEP->getPointerOperand();
259
260    if (dyn_cast<PointerType>(Op->getType())->getAddressSpace() != Addrspace) {
261      Op = new BitCastInst(Op, PointerType::get(dyn_cast<PointerType>(
262                           Op->getType())->getElementType(), Addrspace),
263                           Name, dyn_cast<Instruction>(V));
264    }
265
266    std::vector<Value*> Params(GEP->idx_begin(), GEP->idx_end());
267
268    GetElementPtrInst* GEP2 = GetElementPtrInst::Create(Op, Params, Name,
269                                                      dyn_cast<Instruction>(V));
270    GEP2->setIsInBounds(GEP->isInBounds());
271    V = dyn_cast<Value>(GEP2);
272    GEP->replaceAllUsesWith(GEP2);
273    GEP->eraseFromParent();
274    Load = NULL;
275  }
276
277  if (Load) {
278    ///normally at this point we have the right address space
279    if (Load->getPointerAddressSpace() != Addrspace) {
280      Value *OrigPtr = Load->getPointerOperand();
281      PointerType *OrigPtrType = dyn_cast<PointerType>(OrigPtr->getType());
282
283      Type* NewPtrType = PointerType::get(OrigPtrType->getElementType(),
284                                            Addrspace);
285
286      Value* NewPtr = OrigPtr;
287
288      if (OrigPtr->getType() != NewPtrType) {
289        NewPtr = new BitCastInst(OrigPtr, NewPtrType, "prop_cast", Load);
290      }
291
292      Value* new_Load = new LoadInst(NewPtr, Name, Load);
293      Load->replaceAllUsesWith(new_Load);
294      Load->eraseFromParent();
295    }
296
297    return;
298  }
299
300  std::vector<User*> Users(V->use_begin(), V->use_end());
301
302  for (int i = 0; i < int(Users.size()); i++) {
303    Value* V2 = dyn_cast<Value>(Users[i]);
304
305    if (V2) {
306      Propagate(V2, Name, IsIndirect);
307    }
308  }
309}
310
311Value* R600KernelParameters::ConstantRead(Function *Fun, Param &P) {
312  assert(Fun->front().begin() != Fun->front().end());
313
314  Instruction *FirstInst = Fun->front().begin();
315  IRBuilder <> Builder (FirstInst);
316/* First 3 dwords are reserved for the dimmension info */
317
318  if (!P.Val->hasNUsesOrMore(1)) {
319    return NULL;
320  }
321  unsigned Addrspace;
322
323  if (P.IsIndirect) {
324    Addrspace = AMDGPUAS::PARAM_I_ADDRESS;
325  } else {
326    Addrspace = AMDGPUAS::PARAM_D_ADDRESS;
327  }
328
329  Argument *Arg = dyn_cast<Argument>(P.Val);
330  Type * ArgType = P.Val->getType();
331  PointerType * ArgPtrType = dyn_cast<PointerType>(P.Val->getType());
332
333  if (ArgPtrType && Arg->hasByValAttr()) {
334    Value* ParamAddrSpacePtr = ConstantPointerNull::get(
335                                    PointerType::get(Type::getInt32Ty(*Context),
336                                    Addrspace));
337    Value* ParamPtr = GetElementPtrInst::Create(ParamAddrSpacePtr,
338                                    ConstantInt::get(Type::getInt32Ty(*Context),
339                                    P.getRatOffset()), Arg->getName(),
340                                    FirstInst);
341    ParamPtr = new BitCastInst(ParamPtr,
342                                PointerType::get(ArgPtrType->getElementType(),
343                                                 Addrspace),
344                                Arg->getName(), FirstInst);
345    P.PtrVal = ParamPtr;
346    return ParamPtr;
347  } else {
348    Value *ParamAddrSpacePtr = ConstantPointerNull::get(PointerType::get(
349                                                        ArgType, Addrspace));
350
351    Value *ParamPtr = Builder.CreateGEP(ParamAddrSpacePtr,
352             ConstantInt::get(Type::getInt32Ty(*Context), P.getRatOffset()),
353                              Arg->getName());
354
355    Value *Param_Value = Builder.CreateLoad(ParamPtr, Arg->getName());
356
357    return Param_Value;
358  }
359}
360
361Value* R600KernelParameters::handleSpecial(Function* Fun, Param& P) {
362  std::string Name = getSpecialTypeName(P.Val->getType());
363  int ID;
364
365  assert(!Name.empty());
366
367  if (Name == "image2d_t" || Name == "image3d_t") {
368    int LastID = std::max(getLastSpecialID("image2d_t"),
369                     getLastSpecialID("image3d_t"));
370
371    if (LastID == -1) {
372      ID = 2; ///ID0 and ID1 are used internally by the driver
373    } else {
374      ID = LastID + 1;
375    }
376  } else if (Name == "sampler_t") {
377    int LastID = getLastSpecialID("sampler_t");
378
379    if (LastID == -1) {
380      ID = 0;
381    } else {
382      ID = LastID + 1;
383    }
384  } else {
385    ///TODO: give some error message
386    return NULL;
387  }
388
389  P.SpecialType = Name;
390  P.SpecialID = ID;
391
392  Instruction *FirstInst = Fun->front().begin();
393
394  return new IntToPtrInst(ConstantInt::get(Type::getInt32Ty(*Context),
395                                           P.SpecialID), P.Val->getType(),
396                                           "resourceID", FirstInst);
397}
398
399
400bool R600KernelParameters::IsSpecialType(Type* T) {
401  return !getSpecialTypeName(T).empty();
402}
403
404std::string R600KernelParameters::getSpecialTypeName(Type* T) {
405  PointerType *PT = dyn_cast<PointerType>(T);
406  StructType *ST = NULL;
407
408  if (PT) {
409    ST = dyn_cast<StructType>(PT->getElementType());
410  }
411
412  if (ST) {
413    std::string Prefix = "struct.opencl_builtin_type_";
414
415    std::string Name = ST->getName().str();
416
417    if (Name.substr(0, Prefix.length()) == Prefix) {
418      return Name.substr(Prefix.length(), Name.length());
419    }
420  }
421
422  return "";
423}
424
425
426bool R600KernelParameters::runOnFunction (Function &F) {
427  if (!IsOpenCLKernel(&F)) {
428    return false;
429  }
430
431  RunAna(&F);
432  Replace(&F);
433  Propagate(&F);
434
435  return false;
436}
437
438void R600KernelParameters::getAnalysisUsage(AnalysisUsage &AU) const {
439  FunctionPass::getAnalysisUsage(AU);
440  AU.setPreservesAll();
441}
442
443const char *R600KernelParameters::getPassName() const {
444  return "OpenCL Kernel parameter conversion to memory";
445}
446
447bool R600KernelParameters::doInitialization(Module &M) {
448  Context = &M.getContext();
449  Mod = &M;
450
451  return false;
452}
453
454bool R600KernelParameters::doFinalization(Module &M) {
455  return false;
456}
457
458} // End anonymous namespace
459
460FunctionPass* llvm::createR600KernelParametersPass(const TargetData* TD) {
461  return new R600KernelParameters(TD);
462}
463