| //===-- R600KernelParameters.cpp - Lower kernel function arguments --------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This pass lowers kernel function arguments to loads from the vertex buffer. |
| // |
| // Kernel arguemnts are stored in the vertex buffer at an offset of 9 dwords, |
| // so arg0 needs to be loaded from VTX_BUFFER[9] and arg1 is loaded from |
| // VTX_BUFFER[10], etc. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "AMDGPU.h" |
| #include "AMDIL.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/Constants.h" |
| #include "llvm/Function.h" |
| #include "llvm/Intrinsics.h" |
| #include "llvm/Metadata.h" |
| #include "llvm/Module.h" |
| #include "llvm/Target/TargetData.h" |
| #include "llvm/Support/IRBuilder.h" |
| #include "llvm/Support/TypeBuilder.h" |
| |
| #include <map> |
| #include <set> |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| #define CONSTANT_CACHE_SIZE_DW 127 |
| |
| class R600KernelParameters : public FunctionPass { |
| const TargetData *TD; |
| LLVMContext* Context; |
| Module *Mod; |
| |
| struct Param { |
| Param() : Val(NULL), PtrVal(NULL), OffsetInDW(0), SizeInDW(0), |
| IsIndirect(true), SpecialID(0) {} |
| |
| Value* Val; |
| Value* PtrVal; |
| int OffsetInDW; |
| int SizeInDW; |
| |
| bool IsIndirect; |
| |
| std::string SpecialType; |
| int SpecialID; |
| |
| int End() { return OffsetInDW + SizeInDW; } |
| // The first 9 dwords are reserved for the grid sizes. |
| int getRatOffset() { return 9 + OffsetInDW; } |
| }; |
| |
| std::vector<Param> Params; |
| |
| bool IsOpenCLKernel(const Function *Fun); |
| int getLastSpecialID(const std::string& TypeName); |
| |
| int getListSize(); |
| void AddParam(Argument *Arg); |
| int CalculateArgumentSize(Argument *Arg); |
| void RunAna(Function *Fun); |
| void Replace(Function *Fun); |
| bool IsIndirect(Value *Val, std::set<Value*> &Visited); |
| void Propagate(Function* Fun); |
| void Propagate(Value *V, const Twine &Name, bool IsIndirect = true); |
| Value* ConstantRead(Function *Fun, Param &P); |
| Value* handleSpecial(Function *Fun, Param &P); |
| bool IsSpecialType(Type *T); |
| std::string getSpecialTypeName(Type *T); |
| public: |
| static char ID; |
| R600KernelParameters() : FunctionPass(ID) {}; |
| R600KernelParameters(const TargetData* TD) : FunctionPass(ID), TD(TD) {} |
| bool runOnFunction (Function &F); |
| void getAnalysisUsage(AnalysisUsage &AU) const; |
| const char *getPassName() const; |
| bool doInitialization(Module &M); |
| bool doFinalization(Module &M); |
| }; |
| |
| char R600KernelParameters::ID = 0; |
| |
| static RegisterPass<R600KernelParameters> X("kerparam", |
| "OpenCL Kernel Parameter conversion", false, false); |
| |
| bool R600KernelParameters::IsOpenCLKernel(const Function* Fun) { |
| Module *Mod = const_cast<Function*>(Fun)->getParent(); |
| NamedMDNode * MD = Mod->getOrInsertNamedMetadata("opencl.kernels"); |
| |
| if (!MD || !MD->getNumOperands()) { |
| return false; |
| } |
| |
| for (int i = 0; i < int(MD->getNumOperands()); i++) { |
| if (!MD->getOperand(i) || !MD->getOperand(i)->getOperand(0)) { |
| continue; |
| } |
| |
| assert(MD->getOperand(i)->getNumOperands() == 1); |
| |
| if (MD->getOperand(i)->getOperand(0)->getName() == Fun->getName()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| int R600KernelParameters::getLastSpecialID(const std::string &TypeName) { |
| int LastID = -1; |
| |
| for (std::vector<Param>::iterator i = Params.begin(); i != Params.end(); i++) { |
| if (i->SpecialType == TypeName) { |
| LastID = i->SpecialID; |
| } |
| } |
| |
| return LastID; |
| } |
| |
| int R600KernelParameters::getListSize() { |
| if (Params.size() == 0) { |
| return 0; |
| } |
| |
| return Params.back().End(); |
| } |
| |
| bool R600KernelParameters::IsIndirect(Value *Val, std::set<Value*> &Visited) { |
| //XXX Direct parameters are not supported yet, so return true here. |
| return true; |
| #if 0 |
| if (isa<LoadInst>(Val)) { |
| return false; |
| } |
| |
| if (isa<IntegerType>(Val->getType())) { |
| assert(0 && "Internal error"); |
| return false; |
| } |
| |
| if (Visited.count(Val)) { |
| return false; |
| } |
| |
| Visited.insert(Val); |
| |
| if (isa<getElementPtrInst>(Val)) { |
| getElementPtrInst* GEP = dyn_cast<getElementPtrInst>(Val); |
| getElementPtrInst::op_iterator I = GEP->op_begin(); |
| |
| for (++I; I != GEP->op_end(); ++I) { |
| if (!isa<Constant>(*I)) { |
| return true; |
| } |
| } |
| } |
| |
| for (Value::use_iterator I = Val->use_begin(); i != Val->use_end(); ++I) { |
| Value* V2 = dyn_cast<Value>(*I); |
| |
| if (V2) { |
| if (IsIndirect(V2, Visited)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| #endif |
| } |
| |
| void R600KernelParameters::AddParam(Argument *Arg) { |
| Param P; |
| |
| P.Val = dyn_cast<Value>(Arg); |
| P.OffsetInDW = getListSize(); |
| P.SizeInDW = CalculateArgumentSize(Arg); |
| |
| if (isa<PointerType>(Arg->getType()) && Arg->hasByValAttr()) { |
| std::set<Value*> Visited; |
| P.IsIndirect = IsIndirect(P.Val, Visited); |
| } |
| |
| Params.push_back(P); |
| } |
| |
| int R600KernelParameters::CalculateArgumentSize(Argument *Arg) { |
| Type* T = Arg->getType(); |
| |
| if (Arg->hasByValAttr() && dyn_cast<PointerType>(T)) { |
| T = dyn_cast<PointerType>(T)->getElementType(); |
| } |
| |
| int StoreSizeInDW = (TD->getTypeStoreSize(T) + 3)/4; |
| |
| assert(StoreSizeInDW); |
| |
| return StoreSizeInDW; |
| } |
| |
| |
| void R600KernelParameters::RunAna(Function* Fun) { |
| assert(IsOpenCLKernel(Fun)); |
| |
| for (Function::arg_iterator I = Fun->arg_begin(); I != Fun->arg_end(); ++I) { |
| AddParam(I); |
| } |
| |
| } |
| |
| void R600KernelParameters::Replace(Function* Fun) { |
| for (std::vector<Param>::iterator I = Params.begin(); I != Params.end(); ++I) { |
| Value *NewVal; |
| |
| if (IsSpecialType(I->Val->getType())) { |
| NewVal = handleSpecial(Fun, *I); |
| } else { |
| NewVal = ConstantRead(Fun, *I); |
| } |
| if (NewVal) { |
| I->Val->replaceAllUsesWith(NewVal); |
| } |
| } |
| } |
| |
| void R600KernelParameters::Propagate(Function* Fun) { |
| for (std::vector<Param>::iterator I = Params.begin(); I != Params.end(); ++I) { |
| if (I->PtrVal) { |
| Propagate(I->PtrVal, I->Val->getName(), I->IsIndirect); |
| } |
| } |
| } |
| |
| void R600KernelParameters::Propagate(Value* V, const Twine& Name, bool IsIndirect) { |
| LoadInst* Load = dyn_cast<LoadInst>(V); |
| GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V); |
| |
| unsigned Addrspace; |
| |
| if (IsIndirect) { |
| Addrspace = AMDGPUAS::PARAM_I_ADDRESS; |
| } else { |
| Addrspace = AMDGPUAS::PARAM_D_ADDRESS; |
| } |
| |
| if (GEP && GEP->getType()->getAddressSpace() != Addrspace) { |
| Value *Op = GEP->getPointerOperand(); |
| |
| if (dyn_cast<PointerType>(Op->getType())->getAddressSpace() != Addrspace) { |
| Op = new BitCastInst(Op, PointerType::get(dyn_cast<PointerType>( |
| Op->getType())->getElementType(), Addrspace), |
| Name, dyn_cast<Instruction>(V)); |
| } |
| |
| std::vector<Value*> Params(GEP->idx_begin(), GEP->idx_end()); |
| |
| GetElementPtrInst* GEP2 = GetElementPtrInst::Create(Op, Params, Name, |
| dyn_cast<Instruction>(V)); |
| GEP2->setIsInBounds(GEP->isInBounds()); |
| V = dyn_cast<Value>(GEP2); |
| GEP->replaceAllUsesWith(GEP2); |
| GEP->eraseFromParent(); |
| Load = NULL; |
| } |
| |
| if (Load) { |
| ///normally at this point we have the right address space |
| if (Load->getPointerAddressSpace() != Addrspace) { |
| Value *OrigPtr = Load->getPointerOperand(); |
| PointerType *OrigPtrType = dyn_cast<PointerType>(OrigPtr->getType()); |
| |
| Type* NewPtrType = PointerType::get(OrigPtrType->getElementType(), |
| Addrspace); |
| |
| Value* NewPtr = OrigPtr; |
| |
| if (OrigPtr->getType() != NewPtrType) { |
| NewPtr = new BitCastInst(OrigPtr, NewPtrType, "prop_cast", Load); |
| } |
| |
| Value* new_Load = new LoadInst(NewPtr, Name, Load); |
| Load->replaceAllUsesWith(new_Load); |
| Load->eraseFromParent(); |
| } |
| |
| return; |
| } |
| |
| std::vector<User*> Users(V->use_begin(), V->use_end()); |
| |
| for (int i = 0; i < int(Users.size()); i++) { |
| Value* V2 = dyn_cast<Value>(Users[i]); |
| |
| if (V2) { |
| Propagate(V2, Name, IsIndirect); |
| } |
| } |
| } |
| |
| Value* R600KernelParameters::ConstantRead(Function *Fun, Param &P) { |
| assert(Fun->front().begin() != Fun->front().end()); |
| |
| Instruction *FirstInst = Fun->front().begin(); |
| IRBuilder <> Builder (FirstInst); |
| /* First 3 dwords are reserved for the dimmension info */ |
| |
| if (!P.Val->hasNUsesOrMore(1)) { |
| return NULL; |
| } |
| unsigned Addrspace; |
| |
| if (P.IsIndirect) { |
| Addrspace = AMDGPUAS::PARAM_I_ADDRESS; |
| } else { |
| Addrspace = AMDGPUAS::PARAM_D_ADDRESS; |
| } |
| |
| Argument *Arg = dyn_cast<Argument>(P.Val); |
| Type * ArgType = P.Val->getType(); |
| PointerType * ArgPtrType = dyn_cast<PointerType>(P.Val->getType()); |
| |
| if (ArgPtrType && Arg->hasByValAttr()) { |
| Value* ParamAddrSpacePtr = ConstantPointerNull::get( |
| PointerType::get(Type::getInt32Ty(*Context), |
| Addrspace)); |
| Value* ParamPtr = GetElementPtrInst::Create(ParamAddrSpacePtr, |
| ConstantInt::get(Type::getInt32Ty(*Context), |
| P.getRatOffset()), Arg->getName(), |
| FirstInst); |
| ParamPtr = new BitCastInst(ParamPtr, |
| PointerType::get(ArgPtrType->getElementType(), |
| Addrspace), |
| Arg->getName(), FirstInst); |
| P.PtrVal = ParamPtr; |
| return ParamPtr; |
| } else { |
| Value *ParamAddrSpacePtr = ConstantPointerNull::get(PointerType::get( |
| ArgType, Addrspace)); |
| |
| Value *ParamPtr = Builder.CreateGEP(ParamAddrSpacePtr, |
| ConstantInt::get(Type::getInt32Ty(*Context), P.getRatOffset()), |
| Arg->getName()); |
| |
| Value *Param_Value = Builder.CreateLoad(ParamPtr, Arg->getName()); |
| |
| return Param_Value; |
| } |
| } |
| |
| Value* R600KernelParameters::handleSpecial(Function* Fun, Param& P) { |
| std::string Name = getSpecialTypeName(P.Val->getType()); |
| int ID; |
| |
| assert(!Name.empty()); |
| |
| if (Name == "image2d_t" || Name == "image3d_t") { |
| int LastID = std::max(getLastSpecialID("image2d_t"), |
| getLastSpecialID("image3d_t")); |
| |
| if (LastID == -1) { |
| ID = 2; ///ID0 and ID1 are used internally by the driver |
| } else { |
| ID = LastID + 1; |
| } |
| } else if (Name == "sampler_t") { |
| int LastID = getLastSpecialID("sampler_t"); |
| |
| if (LastID == -1) { |
| ID = 0; |
| } else { |
| ID = LastID + 1; |
| } |
| } else { |
| ///TODO: give some error message |
| return NULL; |
| } |
| |
| P.SpecialType = Name; |
| P.SpecialID = ID; |
| |
| Instruction *FirstInst = Fun->front().begin(); |
| |
| return new IntToPtrInst(ConstantInt::get(Type::getInt32Ty(*Context), |
| P.SpecialID), P.Val->getType(), |
| "resourceID", FirstInst); |
| } |
| |
| |
| bool R600KernelParameters::IsSpecialType(Type* T) { |
| return !getSpecialTypeName(T).empty(); |
| } |
| |
| std::string R600KernelParameters::getSpecialTypeName(Type* T) { |
| PointerType *PT = dyn_cast<PointerType>(T); |
| StructType *ST = NULL; |
| |
| if (PT) { |
| ST = dyn_cast<StructType>(PT->getElementType()); |
| } |
| |
| if (ST) { |
| std::string Prefix = "struct.opencl_builtin_type_"; |
| |
| std::string Name = ST->getName().str(); |
| |
| if (Name.substr(0, Prefix.length()) == Prefix) { |
| return Name.substr(Prefix.length(), Name.length()); |
| } |
| } |
| |
| return ""; |
| } |
| |
| |
| bool R600KernelParameters::runOnFunction (Function &F) { |
| if (!IsOpenCLKernel(&F)) { |
| return false; |
| } |
| |
| RunAna(&F); |
| Replace(&F); |
| Propagate(&F); |
| |
| return false; |
| } |
| |
| void R600KernelParameters::getAnalysisUsage(AnalysisUsage &AU) const { |
| FunctionPass::getAnalysisUsage(AU); |
| AU.setPreservesAll(); |
| } |
| |
| const char *R600KernelParameters::getPassName() const { |
| return "OpenCL Kernel parameter conversion to memory"; |
| } |
| |
| bool R600KernelParameters::doInitialization(Module &M) { |
| Context = &M.getContext(); |
| Mod = &M; |
| |
| return false; |
| } |
| |
| bool R600KernelParameters::doFinalization(Module &M) { |
| return false; |
| } |
| |
| } // End anonymous namespace |
| |
| FunctionPass* llvm::createR600KernelParametersPass(const TargetData* TD) { |
| return new R600KernelParameters(TD); |
| } |