//===-- 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); }