/*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Author: Benjamin Segovia
*/
/**
* \file function.hpp
* \author Benjamin Segovia
*/
#ifndef __GBE_IR_FUNCTION_HPP__
#define __GBE_IR_FUNCTION_HPP__
#include "ir/immediate.hpp"
#include "ir/register.hpp"
#include "ir/instruction.hpp"
#include "ir/profile.hpp"
#include "ir/sampler.hpp"
#include "ir/printf.hpp"
#include "ir/image.hpp"
#include "sys/vector.hpp"
#include "sys/set.hpp"
#include "sys/map.hpp"
#include "sys/alloc.hpp"
#include
namespace gbe {
namespace ir {
/*! Commonly used in the CFG */
typedef set BlockSet;
class Unit; // Function belongs to a unit
/*! Function basic blocks really belong to a function since:
* 1 - registers used in the basic blocks belongs to the function register
* file
* 2 - branches point to basic blocks of the same function
*/
class BasicBlock : public NonCopyable, public intrusive_list
{
public:
/*! Empty basic block */
BasicBlock(Function &fn);
/*! Releases all the instructions */
~BasicBlock(void);
/*! Append a new instruction at the end of the stream */
void append(Instruction &insn);
void insertAt(iterator pos, Instruction &insn);
/*! Get the parent function */
Function &getParent(void) { return fn; }
const Function &getParent(void) const { return fn; }
/*! Get the next and previous allocated block */
BasicBlock *getNextBlock(void) const { return this->nextBlock; }
BasicBlock *getPrevBlock(void) const { return this->prevBlock; }
/*! Get / set the first and last instructions */
Instruction *getFirstInstruction(void) const;
Instruction *getLastInstruction(void) const;
/*! Get successors and predecessors */
const BlockSet &getSuccessorSet(void) const { return successors; }
const BlockSet &getPredecessorSet(void) const { return predecessors; }
/*! Get the label index of this block */
LabelIndex getLabelIndex(void) const;
/*! Apply the given functor on all instructions */
template
INLINE void foreach(const T &functor) {
auto it = this->begin();
while (it != this->end()) {
auto curr = it++;
functor(*curr);
}
}
set undefPhiRegs;
set definedPhiRegs;
/* these three are used by structure transforming */
public:
/* if needEndif is true, it means that this bb is the exit of an
* outermost structure, so this block needs another endif to match
* the if inserted at the entry of this structure, otherwise this
* block is in the middle of a structure, there's no need to insert
* extra endif. */
bool needEndif;
/* if needIf is true, it means that this bb is the entry of an
* outermost structure, so this block needs an if instruction just
* like other unstructured bbs. otherwise this block is in the
* middle of a structure, there's no need to insert an if. */
bool needIf;
/* since we need to insert an if and endif at the entry and exit
* bb of an outermost structure respectively, so the endif is not
* in the same bb with if, in order to get the endif's position,
* we need to store the endif label in the entry bb. */
LabelIndex endifLabel;
/* the identified if-then and if-else structure contains more than
* one bbs, in order to insert if, else and endif properly, we give
* all the IF ELSE and ENDIF a label for convenience. matchingEndifLabel
* is used when inserts instruction if and else, and matchingElseLabel
* is used when inserts instruction if. */
LabelIndex matchingEndifLabel;
LabelIndex matchingElseLabel;
/* IR ELSE's target is the matching ENDIF's LabelIndex, thisElseLabel
* is used to store the virtual label of the instruction just below
* ELSE. */
LabelIndex thisElseLabel;
/* betongToStructure is used as a mark of wether this bb belongs to an
* identified structure. */
bool belongToStructure;
/* isStructureExit and matchingStructureEntry is used for buildJIPs at
* backend, isStructureExit is true means the bb is an identified structure's
* exit bb, while matchingStructureEntry means the entry bb of the same
* identified structure. so if isStructureExit is false then matchingStructureEntry
* is meaningless. */
bool isStructureExit;
/* This block is an exit point of a loop block. It may not be exit point of
the large structure block. */
bool isLoopExit;
/* This block has an extra branch in the end of the block. */
bool hasExtraBra;
BasicBlock *matchingStructureEntry;
/* variable liveout is for if-else structure liveness analysis. eg. we have an sequence of
* bbs of 0, 1, 2, 3, 4 and the CFG is as below:
* 0
* |\
* 1 \
* | 2
* 4 |
* \ /
* 3
* we would identify 1 and 4 an sequence structure and 0 1 4 2 an if-else structure.
* since we will insert an else instruction at the top of bb 2, we have to add an
* unconditional jump at the bottom of bb 4 to bb 2 for executing the inserted else. this
* would cause a change of CFG. at origin, bb 2 always executes before bb 4, but after
* this insertion, bb 2 may executes after bb 4 which leads to bb 2's livein(i.e. part of
* bb 0's liveout) may be destroyed by bb 4. so we inserted the livein of the entry of
* else node into all the basic blocks belong to 'then' part while the liveout is
* calculated in structural_analysis.cpp:calculateNecessaryLiveout(); */
std::set liveout;
/* selfLoop's label.
* */
LabelIndex whileLabel;
private:
friend class Function; //!< Owns the basic blocks
BlockSet predecessors; //!< Incoming blocks
BlockSet successors; //!< Outgoing blocks
BasicBlock *nextBlock; //!< Block allocated just after this one
BasicBlock *prevBlock; //!< Block allocated just before this one
Function &fn; //!< Function the block belongs to
GBE_CLASS(BasicBlock);
};
/*! In fine, function input arguments can be pushed from the constant
* buffer if they are structures. Other arguments can be images (textures)
* and will also require special treatment.
*/
struct FunctionArgument {
enum Type {
GLOBAL_POINTER = 0, // __global
CONSTANT_POINTER = 1, // __constant
LOCAL_POINTER = 2, // __local
VALUE = 3, // int, float
STRUCTURE = 4, // struct foo
IMAGE = 5, // image*d_t
SAMPLER = 6
};
struct InfoFromLLVM { // All the info about passed by llvm, using -cl-kernel-arg-info
uint32_t addrSpace;
std::string typeName;
std::string accessQual;
std::string typeQual;
std::string argName; // My different from arg->getName()
bool isImage1dT() const {
return typeName.compare("image1d_t") == 0;
}
bool isImage1dArrayT() const {
return typeName.compare("image1d_array_t") == 0;
}
bool isImage1dBufferT() const {
return typeName.compare("image1d_buffer_t") == 0;
}
bool isImage2dT() const {
return typeName.compare("image2d_t") == 0;
}
bool isImage2dArrayT() const {
return typeName.compare("image2d_array_t") == 0;
}
bool isImage3dT() const {
return typeName.compare("image3d_t") == 0;
}
bool isImageType() const {
return isImage1dT() || isImage1dArrayT() || isImage1dBufferT() ||
isImage2dT() || isImage2dArrayT() || isImage3dT();
}
bool isSamplerType() const {
return typeName.compare("sampler_t") == 0;
}
};
/*! Create a function input argument */
INLINE FunctionArgument(Type type, Register reg, uint32_t size, const std::string &name, uint32_t align, InfoFromLLVM& info, uint8_t bti) :
type(type), reg(reg), size(size), align(align), name(name), info(info), bti(bti) { }
Type type; //!< Gives the type of argument we have
Register reg; //!< Holds the argument
uint32_t size; //!< == sizeof(void*) for ptr, sizeof(elem) for the rest
uint32_t align; //!< address alignment for the argument
const std::string name; //!< Holds the function name for IR output
InfoFromLLVM info; //!< Holds the llvm passed info
uint8_t bti; //!< binding table index
GBE_STRUCT(FunctionArgument); // Use custom allocator
};
/*! Maps the pushed register to the function argument */
struct PushLocation {
INLINE PushLocation(const Function &fn, uint32_t argID, uint32_t offset) :
fn(fn), argID(argID), offset(offset) {}
/*! Get the pushed virtual register */
Register getRegister(void) const;
const Function &fn; //!< Function it belongs to
uint32_t argID; //!< Function argument
uint32_t offset; //!< Offset in the function argument
GBE_STRUCT(PushLocation); // Use custom allocator
};
/*! For maps and sets */
INLINE bool operator< (const PushLocation &arg0, const PushLocation &arg1) {
if (arg0.argID != arg1.argID) return arg0.argID < arg1.argID;
return arg0.offset < arg1.offset;
}
/*! CFG loops */
struct Loop : public NonCopyable
{
public:
Loop(const vector &in, const vector> &exit) :
bbs(in), exits(exit) {}
vector bbs;
vector> exits;
GBE_STRUCT(Loop);
};
/*! A function is :
* - a register file
* - a set of basic block layout into a CGF
* - input arguments
*/
class Function : public NonCopyable
{
public:
/*! Map of all pushed registers */
typedef map PushMap;
/*! Map of all pushed location (i.e. part of function argument) */
typedef map LocationMap;
/*! Create an empty function */
Function(const std::string &name, const Unit &unit, Profile profile = PROFILE_OCL);
/*! Release everything *including* the basic block pointers */
~Function(void);
/*! Get the function profile */
INLINE Profile getProfile(void) const { return profile; }
/*! Get a new valid register */
INLINE Register newRegister(RegisterFamily family, bool uniform = false) {
return this->file.append(family, uniform);
}
/*! Get the function name */
const std::string &getName(void) const { return name; }
/*! When set, we do not have choice any more in the back end for it */
INLINE void setSimdWidth(uint32_t width) { simdWidth = width; }
/*! Get the SIMD width (0 if not forced) */
uint32_t getSimdWidth(void) const { return simdWidth; }
/*! Extract the register from the register file */
INLINE RegisterData getRegisterData(Register reg) const { return file.get(reg); }
/*! set a register to uniform or nonuniform type. */
INLINE void setRegisterUniform(Register reg, bool uniform) { file.setUniform(reg, uniform); }
/*! return true if the specified regsiter is uniform type */
INLINE bool isUniformRegister(Register reg) { return file.isUniform(reg); }
/*! Get the register family from the register itself */
INLINE RegisterFamily getRegisterFamily(Register reg) const {
return this->getRegisterData(reg).family;
}
/*! Get the register from the tuple vector */
INLINE Register getRegister(Tuple ID, uint32_t which) const {
return file.get(ID, which);
}
/*! Set the register from the tuple vector */
INLINE void setRegister(Tuple ID, uint32_t which, Register reg) {
file.set(ID, which, reg);
}
/*! Get the register file */
INLINE const RegisterFile &getRegisterFile(void) const { return file; }
/*! Get the given value ie immediate from the function */
INLINE const Immediate &getImmediate(ImmediateIndex ID) const {
return immediates[ID];
}
/*! Create a new immediate and returns its index */
INLINE ImmediateIndex newImmediate(const Immediate &imm) {
const ImmediateIndex index(this->immediateNum());
this->immediates.push_back(imm);
return index;
}
/*! Fast allocation / deallocation of instructions */
DECL_POOL(Instruction, insnPool);
/*! Get input argument */
INLINE const FunctionArgument &getArg(uint32_t ID) const {
GBE_ASSERT(args[ID] != NULL);
return *args[ID];
}
INLINE FunctionArgument &getArg(uint32_t ID) {
GBE_ASSERT(args[ID] != NULL);
return *args[ID];
}
/*! Get arg ID. */
INLINE int32_t getArgID(FunctionArgument *requestArg) {
for (uint32_t ID = 0; ID < args.size(); ID++)
{
if ( args[ID] == requestArg )
return ID;
}
GBE_ASSERTM(0, "Failed to get a valid argument ID.");
return -1;
}
/*! Get the number of pushed registers */
INLINE uint32_t pushedNum(void) const { return pushMap.size(); }
/*! Get the pushed data location for the given register */
INLINE const PushLocation *getPushLocation(Register reg) const {
auto it = pushMap.find(reg);
if (it == pushMap.end())
return NULL;
else
return &it->second;
}
/*! Get the map of pushed registers */
const PushMap &getPushMap(void) const { return this->pushMap; }
/*! Get the map of pushed registers */
const LocationMap &getLocationMap(void) const { return this->locationMap; }
/*! Get input argument from the register (linear research). Return NULL if
* this is not an input argument
*/
INLINE const FunctionArgument *getArg(const Register ®) const {
for (size_t i = 0; i < args.size(); ++i) {
const FunctionArgument* arg = args[i];
if (arg->reg == reg)
return arg;
}
return NULL;
}
INLINE FunctionArgument *getArg(const Register ®) {
for (size_t i = 0; i < args.size(); ++i) {
FunctionArgument* arg = args[i];
if (arg->reg == reg)
return arg;
}
return NULL;
}
/*! Get output register */
INLINE Register getOutput(uint32_t ID) const { return outputs[ID]; }
/*! Get the argument location for the pushed register */
INLINE const PushLocation &getPushLocation(Register reg) {
GBE_ASSERT(pushMap.contains(reg) == true);
return pushMap.find(reg)->second;
}
/*! Says if this is the top basic block (entry point) */
bool isEntryBlock(const BasicBlock &bb) const;
/*! Get function the entry point block */
BasicBlock &getTopBlock(void) const;
/*! Get the last block */
const BasicBlock &getBottomBlock(void) const;
/*! Get the last block */
BasicBlock &getBottomBlock(void);
/*! Get block from its label */
BasicBlock &getBlock(LabelIndex label) const;
/*! Get the label instruction from its label index */
const LabelInstruction *getLabelInstruction(LabelIndex index) const;
/*! Return the number of instructions of the largest basic block */
uint32_t getLargestBlockSize(void) const;
/*! Get the first index of the special registers and number of them */
uint32_t getFirstSpecialReg(void) const;
uint32_t getSpecialRegNum(void) const;
/*! Indicate if the given register is a special one (like localID in OCL) */
bool isSpecialReg(const Register ®) const;
/*! Create a new label (still not bound to a basic block) */
LabelIndex newLabel(void);
/*! Create the control flow graph */
void computeCFG(void);
/*! Sort labels in increasing orders (top block has the smallest label) */
void sortLabels(void);
/*! check empty Label. */
void checkEmptyLabels(void);
/*! Get the pointer family */
RegisterFamily getPointerFamily(void) const;
/*! Number of registers in the register file */
INLINE uint32_t regNum(void) const { return file.regNum(); }
/*! Number of register tuples in the register file */
INLINE uint32_t tupleNum(void) const { return file.tupleNum(); }
/*! Number of labels in the function */
INLINE uint32_t labelNum(void) const { return labels.size(); }
/*! Number of immediate values in the function */
INLINE uint32_t immediateNum(void) const { return immediates.size(); }
/*! Get the number of argument register */
INLINE uint32_t argNum(void) const { return args.size(); }
/*! Get the number of output register */
INLINE uint32_t outputNum(void) const { return outputs.size(); }
/*! Number of blocks in the function */
INLINE uint32_t blockNum(void) const { return blocks.size(); }
/*! Output an immediate value in a stream */
void outImmediate(std::ostream &out, ImmediateIndex index) const;
/*! Apply the given functor on all basic blocks */
template
INLINE void foreachBlock(const T &functor) const {
for (size_t i = 0; i < blocks.size(); ++i) {
BasicBlock* block = blocks[i];
functor(*block);
}
}
/*! Apply the given functor on all instructions */
template
INLINE void foreachInstruction(const T &functor) const {
for (size_t i = 0; i < blocks.size(); ++i) {
BasicBlock* block = blocks[i];
block->foreach(functor);
}
}
/*! Does it use SLM */
INLINE bool getUseSLM(void) const { return this->useSLM; }
/*! Change the SLM config for the function */
INLINE bool setUseSLM(bool useSLM) { return this->useSLM = useSLM; }
/*! get SLM size needed for local variable inside kernel function */
INLINE uint32_t getSLMSize(void) const { return this->slmSize; }
/*! set slm size needed for local variable inside kernel function */
INLINE void setSLMSize(uint32_t size) { this->slmSize = size; }
/*! Get sampler set in this function */
SamplerSet* getSamplerSet(void) const {return samplerSet; }
/*! Get image set in this function */
ImageSet* getImageSet(void) const {return imageSet; }
/*! Get printf set in this function */
PrintfSet* getPrintfSet(void) const {return printfSet; }
/*! Set required work group size. */
void setCompileWorkGroupSize(size_t x, size_t y, size_t z) { compileWgSize[0] = x; compileWgSize[1] = y; compileWgSize[2] = z; }
/*! Get required work group size. */
const size_t *getCompileWorkGroupSize(void) const {return compileWgSize;}
/*! Set function attributes string. */
void setFunctionAttributes(const std::string& functionAttributes) { this->functionAttributes= functionAttributes; }
/*! Get function attributes string. */
const std::string& getFunctionAttributes(void) const {return this->functionAttributes;}
/*! Get stack size. */
INLINE uint32_t getStackSize(void) const { return this->stackSize; }
/*! Push stack size. */
INLINE void pushStackSize(uint32_t step) { this->stackSize += step; }
/*! add the loop info for later liveness analysis */
void addLoop(const vector &bbs, const vector> &exits);
INLINE const vector &getLoops() { return loops; }
vector &getBlocks() { return blocks; }
/*! Get surface starting address register from bti */
Register getSurfaceBaseReg(uint8_t bti) const;
void appendSurface(uint8_t bti, Register reg);
/*! Output the control flow graph to .dot file */
void outputCFG();
private:
friend class Context; //!< Can freely modify a function
std::string name; //!< Function name
const Unit &unit; //!< Function belongs to this unit
vector args; //!< Input registers of the function
vector outputs; //!< Output registers of the function
vector labels; //!< Each label points to a basic block
vector immediates; //!< All immediate values in the function
vector blocks; //!< All chained basic blocks
vector loops; //!< Loops info of the function
map btiRegMap;//!< map bti to surface base address
RegisterFile file; //!< RegisterDatas used by the instructions
Profile profile; //!< Current function profile
PushMap pushMap; //!< Pushed function arguments (reg->loc)
LocationMap locationMap; //!< Pushed function arguments (loc->reg)
uint32_t simdWidth; //!< 8 or 16 if forced, 0 otherwise
bool useSLM; //!< Is SLM required?
uint32_t slmSize; //!< local variable size inside kernel function
uint32_t stackSize; //!< stack size for private memory.
SamplerSet *samplerSet; //!< samplers used in this function.
ImageSet* imageSet; //!< Image set in this function's arguments..
PrintfSet *printfSet; //!< printfSet store the printf info.
size_t compileWgSize[3]; //!< required work group size specified by
// __attribute__((reqd_work_group_size(X, Y, Z))).
std::string functionAttributes; //!< function attribute qualifiers combined.
GBE_CLASS(Function); //!< Use custom allocator
};
/*! Output the function string in the given stream */
std::ostream &operator<< (std::ostream &out, const Function &fn);
} /* namespace ir */
} /* namespace gbe */
#endif /* __GBE_IR_FUNCTION_HPP__ */