diff options
author | Tom Stellard <thomas.stellard@amd.com> | 2012-07-16 14:17:08 +0000 |
---|---|---|
committer | Tom Stellard <thomas.stellard@amd.com> | 2012-07-16 14:17:08 +0000 |
commit | 23dc769a9b592b356accb74a2e8816e6630ebaa8 (patch) | |
tree | 524525cb3a9cd08626bc85148ab7186a479b505b /lib/Target/AMDGPU/R600KernelParameters.cpp | |
parent | 9db5b5ffa9fccd5c7f1f39a3e9aa66cc4a5eedc1 (diff) |
AMDGPU: Add core backend files for R600/SI codegen v6
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@160270 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Target/AMDGPU/R600KernelParameters.cpp')
-rw-r--r-- | lib/Target/AMDGPU/R600KernelParameters.cpp | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/lib/Target/AMDGPU/R600KernelParameters.cpp b/lib/Target/AMDGPU/R600KernelParameters.cpp new file mode 100644 index 0000000000..27e12d4b1f --- /dev/null +++ b/lib/Target/AMDGPU/R600KernelParameters.cpp @@ -0,0 +1,462 @@ +//===-- 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 or !MD->getNumOperands()) { + return false; + } + + for (int i = 0; i < int(MD->getNumOperands()); i++) { + if (!MD->getOperand(i) or !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 and "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()) and 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() and 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 = AMDILAS::PARAM_I_ADDRESS; + } else { + Addrspace = AMDILAS::PARAM_D_ADDRESS; + } + + if (GEP and 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 = AMDILAS::PARAM_I_ADDRESS; + } else { + Addrspace = AMDILAS::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 and 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" or 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); +} |