From df44820b9fb94595f35efc9e0b4a8edc0ddecbce Mon Sep 17 00:00:00 2001 From: Benjamin Segovia Date: Mon, 13 Feb 2012 19:04:16 +0000 Subject: [PATCH] Added more functionalities for the context Started to implement wellFormed function that basically checks that everything is properly set in the instruction --- backend/CMakeLists.txt | 6 +- backend/src/ir/context.cpp | 27 ++++- backend/src/ir/context.hpp | 98 ++++++++++++++- backend/src/ir/function.hpp | 14 ++- backend/src/ir/instruction.cpp | 262 ++++++++++++++++++++++++++--------------- backend/src/ir/instruction.hpp | 89 ++++++++------ backend/src/ir/register.hpp | 13 +- backend/src/ir/type.hpp | 46 ++++++-- backend/src/sys/platform.hpp | 4 - 9 files changed, 394 insertions(+), 165 deletions(-) diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index 81274c4..1ff20a6 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -48,7 +48,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set (VISIBILITY_FLAG "-fvisibility=hidden") if (COMPILER STREQUAL "GCC") - set (CMAKE_CXX_FLAGS "-Wstrict-aliasing=2 -Wno-invalid-offsetof -fno-strict-aliasing -msse2 -ffast-math -fPIC -Wall -fno-rtti -std=c++0x") + set (CMAKE_CXX_FLAGS "-Wstrict-aliasing=2 -Wno-invalid-offsetof -fstrict-aliasing -msse2 -ffast-math -fPIC -Wall -fno-rtti -std=c++0x") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_DEBUG_MEMORY_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_COMPILE_UTESTS_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VISIBILITY_FLAG} -Wl,-E") @@ -64,7 +64,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set (CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") set (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g") set (CMAKE_CXX_COMPILER "clang++") - set (CMAKE_CXX_FLAGS "-fno-strict-aliasing -msse2 -ffast-math -fPIC -Wall -Wno-format-security -Wno-invalid-offsetof -std=c++0x") + set (CMAKE_CXX_FLAGS "-fstrict-aliasing -msse2 -ffast-math -fPIC -Wall -Wno-format-security -Wno-invalid-offsetof -std=c++0x") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_DEBUG_MEMORY_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_COMPILE_UTESTS_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VISIBILITY_FLAG}") @@ -80,7 +80,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux") elseif (COMPILER STREQUAL "ICC") set (CMAKE_CXX_COMPILER "icpc") set (CMAKE_C_COMPILER "icc") - set (CMAKE_CXX_FLAGS "-std=c++0x -wd2928 -Wall -fPIC -fno-strict-aliasing -fp-model fast -xSSE2") + set (CMAKE_CXX_FLAGS "-std=c++0x -wd2928 -Wall -fPIC -fstrict-aliasing -fp-model fast -xSSE2") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_DEBUG_MEMORY_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GBE_COMPILE_UTESTS_FLAG}") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${VISIBILITY_FLAG} -Wl,-E") diff --git a/backend/src/ir/context.cpp b/backend/src/ir/context.cpp index 51adf59..074b7ad 100644 --- a/backend/src/ir/context.cpp +++ b/backend/src/ir/context.cpp @@ -31,9 +31,32 @@ namespace ir { Context::Context(Unit &unit) : unit(unit), fn(NULL), bb(NULL) {} void Context::startFunction(const std::string &name) { - Function *fn = unit.newFunction(name); + if (fn != NULL) fnStack.push_back(fn); + fn = unit.newFunction(name); + } + + void Context::endFunction(void) { + GBE_ASSERT(fn != NULL); + if (fnStack.size() != 0) { + fn = fnStack.back(); + fnStack.pop_back(); + } else + fn = NULL; + } + + RegisterIndex Context::reg(Register::Family family) { GBE_ASSERT(fn != NULL); - fnStack.push_back(fn); + return fn->file.append(family); + } + + void Context::input(RegisterIndex reg) { + GBE_ASSERT(fn != NULL && reg < fn->file.regNum()); + fn->input.push_back(reg); + } + + void Context::output(RegisterIndex reg) { + GBE_ASSERT(fn != NULL && reg < fn->file.regNum()); + fn->output.push_back(reg); } } /* namespace ir */ diff --git a/backend/src/ir/context.hpp b/backend/src/ir/context.hpp index 0927642..07e7b4e 100644 --- a/backend/src/ir/context.hpp +++ b/backend/src/ir/context.hpp @@ -29,6 +29,7 @@ #include "ir/function.hpp" #include "ir/register.hpp" #include "sys/vector.hpp" +#include namespace gbe { namespace ir { @@ -48,26 +49,111 @@ namespace ir { void startFunction(const std::string &name); /*! Close the function */ void endFunction(void); - /*! Create a new register for the given type */ - RegisterIndex reg(Register::Family type); + /*! Create a new register for the given family */ + RegisterIndex reg(Register::Family family); /*! Append a new input register for the function */ void input(RegisterIndex reg); /*! Append a new output register for the function */ void output(RegisterIndex reg); /*! Append a new tuple */ + template INLINE TupleIndex tuple(Args...args); + /*! We just use variadic templates to forward instruction functions */ +#define DECL_INSN(NAME, FAMILY) \ + template INLINE void NAME(Args...args); +#include "ir/instruction.hxx" +#undef DECL_INSN + + /*! MAD with sources directly specified */ + INLINE void MAD(Type type, + RegisterIndex dst, + RegisterIndex src0, + RegisterIndex src1, + RegisterIndex src2) + { + const TupleIndex index = this->tuple(src0, src1, src2); + return this->MAD(type, dst, index); + } + + /*! LOAD with the destinations directly specified */ + template + void LOAD(Type type, RegisterIndex offset, MemorySpace space, Args...values) + { + const TupleIndex index = this->tuple(values...); + const uint16_t valueNum = std::tuple_size>::value; + GBE_ASSERT(valueNum > 0); + this->LOAD(type, index, offset, space, valueNum); + } + + /*! STORE with the sources directly specified */ template - INLINE TupleIndex tuple(Args...args) { - if (UNLIKELY(fn == NULL)) - throw std::exception("Tuple not defined in a function"); - fn->file.append(args...); + void STORE(Type type, RegisterIndex offset, MemorySpace space, Args...values) + { + const TupleIndex index = this->tuple(values...); + const uint16_t valueNum = std::tuple_size>::value; + GBE_ASSERT(valueNum > 0); + this->STORE(type, index, offset, space, valueNum); } + private: + /*! Check out-of-bound arguments */ + template void outOfBoundCheck(const T &t); + /*! Recurse on the arguments to check their correctness */ + template + INLINE void argumentCheck(First first, Rest... rest); + /*! Stop the argument checking */ + INLINE void argumentCheck(void); + /*! A block must be started with a label */ + void startBlock(void); + /*! A block must be ended with a branch */ + void endBlock(void); + /*! Append the instruction in the current basic block */ + void append(const Instruction &insn); Unit &unit; //!< A unit is associated to a contect Function *fn; //!< Current function we are processing Function::BasicBlock *bb; //!< Current basic block we are filling vector fnStack;//!< Stack of functions still to finish }; + template + INLINE TupleIndex Context::tuple(Args...args) { + GBE_ASSERT(fn != NULL); + return fn->file.appendTuple(args...); + } + + // Out-of-bound check for each function argument + template + INLINE void Context::outOfBoundCheck(const T &t) { } + template <> + INLINE void Context::outOfBoundCheck(const RegisterIndex &index) { + GBE_ASSERT(fn != NULL); + GBE_ASSERT(index < fn->regNum()); + } + template <> + INLINE void Context::outOfBoundCheck(const TupleIndex &index) { + GBE_ASSERT(fn != NULL); + GBE_ASSERT(index < fn->tupleNum()); + } + + // Recursively check all the arguments + template + INLINE void Context::argumentCheck(First first, Rest... rest) { + this->outOfBoundCheck(first); + this->argumentCheck(rest...); + } + INLINE void Context::argumentCheck(void) {} + + // Use argument checker to assert argument value correctness +#define DECL_INSN(NAME, FAMILY) \ + template \ + INLINE void Context::NAME(Args...args) { \ + GBE_ASSERT(fn != NULL); \ + this->argumentCheck(args...); \ + const Instruction insn = gbe::ir::NAME(args...); \ + this->append(insn); \ + } +#include "ir/instruction.hxx" +#undef DECL_INSN + } /* namespace ir */ } /* namespace gbe */ diff --git a/backend/src/ir/function.hpp b/backend/src/ir/function.hpp index da1a054..e82f196 100644 --- a/backend/src/ir/function.hpp +++ b/backend/src/ir/function.hpp @@ -68,16 +68,24 @@ namespace ir { } /*! Get the given value ie immediate from the function */ INLINE Value getValue(uint32_t ID) const { - assert(ID < value.size()); - return value[ID]; + GBE_ASSERT(ID < values.size()); + return values[ID]; } + /*! 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 valueNum(void) const { return values.size(); } private: friend class Context; //!< Can freely modify a function vector input; //!< Input registers of the function vector output; //!< Output registers of the function vector labels; //!< Each label points to a basic block - vector value; //!< All immediate values stored in the function + vector values; //!< All immediate values stored in the function vector bb; //!< All the basic blocks one after the others RegisterFile file; //!< All the registers used in the instructions }; diff --git a/backend/src/ir/instruction.cpp b/backend/src/ir/instruction.cpp index 58e8f9f..dbbdda0 100644 --- a/backend/src/ir/instruction.cpp +++ b/backend/src/ir/instruction.cpp @@ -19,7 +19,6 @@ /** * \file instruction.cpp - * * \author Benjamin Segovia */ #include "ir/instruction.hpp" @@ -54,9 +53,20 @@ namespace ir { } }; + /*! Policy shared by all the internal instructions */ + struct ALIGNED_INSTRUCTION BasePolicy { + /*! Create an instruction from its internal representation */ + Instruction convert(void) const { + return Instruction(reinterpret_cast(&this->opcode)); + } + /*! Instruction opcode */ + Opcode opcode; + }; + /*! All unary and binary arithmetic instructions */ template // 1 or 2 - class ALIGNED_INSTRUCTION NaryInstruction { + class NaryInstruction : public BasePolicy + { public: INLINE uint32_t getSrcNum(void) const { return srcNum; } INLINE uint32_t getDstNum(void) const { return 1; } @@ -69,14 +79,14 @@ namespace ir { return src[ID]; } INLINE Type getType(void) const { return this->type; } - Opcode opcode; //!< Instruction opcode Type type; //!< Type of the instruction RegisterIndex dst; //!< Index of the register in the register file RegisterIndex src[srcNum];//!< Indices of the sources }; /*! All 1-source arithmetic instructions */ - class ALIGNED_INSTRUCTION UnaryInstruction : public NaryInstruction<1> { + class UnaryInstruction : public NaryInstruction<1> + { public: UnaryInstruction(Opcode opcode, Type type, @@ -90,7 +100,8 @@ namespace ir { }; /*! All 2-source arithmetic instructions */ - class ALIGNED_INSTRUCTION BinaryInstruction : public NaryInstruction<2> { + class BinaryInstruction : public NaryInstruction<2> + { public: BinaryInstruction(Opcode opcode, Type type, @@ -108,7 +119,8 @@ namespace ir { /*! This is for MADs mostly. Since three sources cannot be encoded in 64 * bytes, we use tuples of registers */ - class ALIGNED_INSTRUCTION TernaryInstruction { + class TernaryInstruction : public BasePolicy + { public: TernaryInstruction(Opcode opcode, Type type, @@ -131,13 +143,13 @@ namespace ir { return fn.getRegisterIndex(src, ID); } INLINE Type getType(void) const { return this->type; } - Opcode opcode; //!< Opcode of the instruction Type type; //!< Type of the instruction RegisterIndex dst; //!< Dst is the register index TupleIndex src; //!< 3 sources do not fit in 8 bytes -> use a tuple }; - class ALIGNED_INSTRUCTION ConvertInstruction { + class ConvertInstruction : public BasePolicy + { public: ConvertInstruction(Type dstType, Type srcType, @@ -162,14 +174,14 @@ namespace ir { assert(ID == 0); return src; } - Opcode opcode; //!< Opcode of the instruction RegisterIndex dst; //!< Converted value RegisterIndex src; //!< To convert Type dstType; //!< Type to convert to Type srcType; //!< Type to convert from }; - class ALIGNED_INSTRUCTION BranchInstruction : public NoDstPolicy { + class BranchInstruction : public BasePolicy, public NoDstPolicy + { public: INLINE BranchInstruction(LabelIndex labelIndex, RegisterIndex predicate) { this->opcode = OP_BRA; @@ -188,13 +200,13 @@ namespace ir { return predicate; } INLINE bool isPredicated(void) const { return hasPredicate; } - Opcode opcode; //!< Opcode of the instruction RegisterIndex predicate; //!< Predication means conditional branch LabelIndex labelIndex; //!< Index of the label the branch targets bool hasPredicate; //!< Is it predicated? }; - class ALIGNED_INSTRUCTION LoadInstruction { + class LoadInstruction : public BasePolicy + { public: LoadInstruction(Type type, TupleIndex dstValues, @@ -222,7 +234,7 @@ namespace ir { INLINE Type getValueType(void) const { return type; } INLINE uint32_t getValueNum(void) const { return valueNum; } INLINE MemorySpace getAddressSpace(void) const { return memSpace; } - Opcode opcode; //!< Opcode of the instruction + INLINE bool wellFormed(const Function &fn, std::string &why) const; Type type; //!< Type to store RegisterIndex offset; //!< First source is the offset where to store TupleIndex values; //!< Values to load @@ -230,7 +242,8 @@ namespace ir { uint16_t valueNum; //!< Number of values to store }; - class ALIGNED_INSTRUCTION StoreInstruction : public NoDstPolicy { + class StoreInstruction : public BasePolicy, public NoDstPolicy + { public: StoreInstruction(Type type, RegisterIndex offset, @@ -253,26 +266,25 @@ namespace ir { return fn.getRegisterIndex(values, ID - 1); } INLINE uint32_t getSrcNum(void) const { return valueNum + 1u; } - INLINE Type getValueType(void) const { return type; } INLINE uint32_t getValueNum(void) const { return valueNum; } + INLINE Type getValueType(void) const { return type; } INLINE MemorySpace getAddressSpace(void) const { return memSpace; } - Opcode opcode; //!< Opcode of the instruction + INLINE bool wellFormed(const Function &fn, std::string &why) const; Type type; //!< Type to store RegisterIndex offset; //!< First source is the offset where to store TupleIndex values; //!< Values to store MemorySpace memSpace; //!< Where to store - uint16_t valueNum; //!< Number of values to store + uint16_t valueNum; //!< Number of values to store }; - class ALIGNED_INSTRUCTION TextureInstruction : - public NoDstPolicy, public NoSrcPolicy // TODO REMOVE THIS + class TextureInstruction : public BasePolicy, public NoDstPolicy, public NoSrcPolicy // TODO REMOVE THIS { public: INLINE TextureInstruction(void) { this->opcode = OP_TEX; } - Opcode opcode; //!< Opcode of the instruction }; - class ALIGNED_INSTRUCTION LoadImmInstruction : public NoSrcPolicy { + class LoadImmInstruction : public BasePolicy, public NoSrcPolicy + { public: INLINE LoadImmInstruction(Type type, RegisterIndex dst, ValueIndex valueIndex) { this->dst = dst; @@ -289,37 +301,101 @@ namespace ir { return dst; } INLINE Type getType(void) const { return this->type; } - Opcode opcode; //!< Opcode of the instruction + bool wellFormed(const Function &fn, std::string &why) const; RegisterIndex dst; //!< Register to store into ValueIndex valueIndex;//!< Index in the vector of immediates Type type; //!< Type of the immediate }; - class ALIGNED_INSTRUCTION FenceInstruction : - public NoSrcPolicy, public NoDstPolicy + class FenceInstruction : public BasePolicy, public NoSrcPolicy, public NoDstPolicy { public: INLINE FenceInstruction(MemorySpace memSpace) { this->opcode = OP_FENCE; this->memSpace = memSpace; } - Opcode opcode; //!< Opcode of the instruction + bool wellFormed(const Function &fn, std::string &why) const; MemorySpace memSpace; //!< The loads and stores to order }; - class ALIGNED_INSTRUCTION LabelInstruction : - public NoDstPolicy, public NoSrcPolicy + class LabelInstruction : public BasePolicy, public NoDstPolicy, public NoSrcPolicy { public: INLINE LabelInstruction(LabelIndex labelIndex) { this->opcode = OP_LABEL; this->labelIndex = labelIndex; } - Opcode opcode; //!< Opcode of the instruction + INLINE bool wellFormed(const Function &fn, std::string &why) const; LabelIndex labelIndex; //!< Index of the label }; #undef ALIGNED_INSTRUCTION + + ///////////////////////////////////////////////////////////////////////// + // Implements all the wellFormed methods + ///////////////////////////////////////////////////////////////////////// + + /*! Loads and stores follow the same restrictions */ + template + INLINE bool wellFormedLoadStore(const T &insn, const Function &fn, std::string &why) { + if (UNLIKELY(insn.offset >= fn.regNum())) { + why = "Out-of-bound offset register index"; + return false; + } else if (UNLIKELY(insn.values + insn.valueNum > fn.tupleNum())) { + why = "Out-of-bound tuple index"; + return false; + } + // Check that all the registers have the same size + const Register::Family family = getFamily(insn.type); + for (uint32_t regID = 0; regID < insn.valueNum; ++regID) { + const RegisterIndex index = fn.getRegisterIndex(insn.values, regID); + if (index > fn.regNum()) { + why = "Out-of-bound register index"; + return false; + } else { + const Register reg = fn.getRegister(regID); + if (reg.family != family) { + why = "Register family does match instruction type"; + return false; + } + } + } + return true; + } + INLINE bool LoadInstruction::wellFormed(const Function &fn, std::string &why) const { + return wellFormedLoadStore(*this, fn, why); + } + INLINE bool StoreInstruction::wellFormed(const Function &fn, std::string &why) const { + return wellFormedLoadStore(*this, fn, why); + } + + INLINE bool LoadImmInstruction::wellFormed(const Function &fn, std::string &why) const { + if (UNLIKELY(dst >= fn.regNum())) { + why = "Out-of-bound register index"; + return false; + } else if (UNLIKELY(valueIndex >= fn.valueNum())) { + why = "Out-of-bound immediate value index"; + return false; + } else if (UNLIKELY(type != fn.getValue(valueIndex).type)) { + why = "Inconsistant type for the immediate value to load"; + return false; + } else + return true; + } + + INLINE bool FenceInstruction::wellFormed(const Function &fn, std::string &why) const { + return true; + } + + + INLINE bool LabelInstruction::wellFormed(const Function &fn, std::string &why) const { + if (UNLIKELY(labelIndex >= fn.labelNum())) { + why = "Out-of-bound label index"; + return false; + } else + return true; + } + } /* namespace internal */ /////////////////////////////////////////////////////////////////////////// @@ -343,18 +419,18 @@ namespace ir { case OP_##OPCODE: \ return HelperIntrospection::value == 1; -#define START_INTROSPECTION(CLASS) \ - STATIC_ASSERT(sizeof(CLASS) == sizeof(Instruction));\ - bool CLASS::isClassOf(const Instruction &insn) { \ - const Opcode op = insn.getOpcode(); \ - typedef CLASS RefClass; \ +#define START_INTROSPECTION(CLASS) \ + static_assert(sizeof(CLASS)==sizeof(Instruction), "Bad instruction size"); \ + bool CLASS::isClassOf(const Instruction &insn) { \ + const Opcode op = insn.getOpcode(); \ + typedef CLASS RefClass; \ switch (op) { -#define END_INTROSPECTION(CLASS) \ - default: return false; \ - }; \ - } \ - STATIC_ASSERT(offsetof(internal::CLASS, opcode) == 0); +#define END_INTROSPECTION(CLASS) \ + default: return false; \ + }; \ + } \ + static_assert(offsetof(internal::CLASS, opcode)==0, "Bad opcode offset"); START_INTROSPECTION(UnaryInstruction) #include "ir/instruction.hxx" @@ -476,100 +552,102 @@ DECL_MEM_FN(BranchInstruction, bool, isPredicated(void), isPredicated()) /////////////////////////////////////////////////////////////////////////// // All unary functions -#define DECL_EMIT_FUNCTION(NAME, OPCODE)\ - Instruction NAME(Type type, RegisterIndex dst, RegisterIndex src) {\ - internal::UnaryInstruction insn(OPCODE, type, dst, src);\ - return *reinterpret_cast(&insn);\ +#define DECL_EMIT_FUNCTION(NAME) \ + Instruction NAME(Type type, RegisterIndex dst, RegisterIndex src) { \ + const internal::UnaryInstruction insn(OP_##NAME, type, dst, src); \ + return insn.convert(); \ } - DECL_EMIT_FUNCTION(mov, OP_MOV) - DECL_EMIT_FUNCTION(cos, OP_COS) - DECL_EMIT_FUNCTION(sin, OP_SIN) - DECL_EMIT_FUNCTION(tan, OP_TAN) - DECL_EMIT_FUNCTION(log, OP_LOG) - DECL_EMIT_FUNCTION(sqr, OP_SQR) - DECL_EMIT_FUNCTION(rsq, OP_RSQ) + DECL_EMIT_FUNCTION(MOV) + DECL_EMIT_FUNCTION(COS) + DECL_EMIT_FUNCTION(SIN) + DECL_EMIT_FUNCTION(TAN) + DECL_EMIT_FUNCTION(LOG) + DECL_EMIT_FUNCTION(SQR) + DECL_EMIT_FUNCTION(RSQ) #undef DECL_EMIT_FUNCTION // All binary functions -#define DECL_EMIT_FUNCTION(NAME, OPCODE)\ - Instruction NAME(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1) {\ - internal::BinaryInstruction insn(OPCODE, type, dst, src0, src1);\ - return *reinterpret_cast(&insn);\ +#define DECL_EMIT_FUNCTION(NAME) \ + Instruction NAME(Type type, RegisterIndex dst, \ + RegisterIndex src0, \ + RegisterIndex src1) { \ + const internal::BinaryInstruction insn(OP_##NAME, type, dst, src0, src1); \ + return insn.convert(); \ } - DECL_EMIT_FUNCTION(mul, OP_MUL) - DECL_EMIT_FUNCTION(add, OP_ADD) - DECL_EMIT_FUNCTION(sub, OP_SUB) - DECL_EMIT_FUNCTION(div, OP_DIV) - DECL_EMIT_FUNCTION(rem, OP_REM) - DECL_EMIT_FUNCTION(shl, OP_SHL) - DECL_EMIT_FUNCTION(shr, OP_SHR) - DECL_EMIT_FUNCTION(asr, OP_ASR) - DECL_EMIT_FUNCTION(bsf, OP_BSF) - DECL_EMIT_FUNCTION(bsb, OP_BSB) - DECL_EMIT_FUNCTION(or$, OP_OR) - DECL_EMIT_FUNCTION(xor$, OP_XOR) - DECL_EMIT_FUNCTION(and$, OP_AND) + DECL_EMIT_FUNCTION(MUL) + DECL_EMIT_FUNCTION(ADD) + DECL_EMIT_FUNCTION(SUB) + DECL_EMIT_FUNCTION(DIV) + DECL_EMIT_FUNCTION(REM) + DECL_EMIT_FUNCTION(SHL) + DECL_EMIT_FUNCTION(SHR) + DECL_EMIT_FUNCTION(ASR) + DECL_EMIT_FUNCTION(BSF) + DECL_EMIT_FUNCTION(BSB) + DECL_EMIT_FUNCTION(OR) + DECL_EMIT_FUNCTION(XOR) + DECL_EMIT_FUNCTION(AND) #undef DECL_EMIT_FUNCTION // MAD - Instruction mad(Type type, RegisterIndex dst, TupleIndex src) { + Instruction MAD(Type type, RegisterIndex dst, TupleIndex src) { internal::TernaryInstruction insn(OP_MAD, type, dst, src); - return *reinterpret_cast(&insn); + return insn.convert(); } // CVT - Instruction cvt(Type dstType, Type srcType, RegisterIndex dst, TupleIndex src) { + Instruction CVT(Type dstType, Type srcType, RegisterIndex dst, TupleIndex src) { internal::ConvertInstruction insn(dstType, srcType, dst, src); - return *reinterpret_cast(&insn); + return insn.convert(); } // BRA - Instruction bra(LabelIndex labelIndex) { + Instruction BRA(LabelIndex labelIndex) { internal::BranchInstruction insn(labelIndex); - return *reinterpret_cast(&insn); + return insn.convert(); } - Instruction bra(LabelIndex labelIndex, RegisterIndex pred) { + Instruction BRA(LabelIndex labelIndex, RegisterIndex pred) { internal::BranchInstruction insn(labelIndex, pred); - return *reinterpret_cast(&insn); + return insn.convert(); } // LOADI - Instruction loadi(Type type, RegisterIndex dst, ValueIndex value) { + Instruction LOADI(Type type, RegisterIndex dst, ValueIndex value) { internal::LoadImmInstruction insn(type, dst, value); - return *reinterpret_cast(&insn); + return insn.convert(); } // LOAD and STORE -#define DECL_EMIT_FUNCTION(NAME, CLASS) \ - Instruction NAME(Type type, \ - TupleIndex tuple, \ - RegisterIndex offset, \ - MemorySpace space, \ - uint16_t valueNum) \ - { \ +#define DECL_EMIT_FUNCTION(NAME, CLASS) \ + Instruction NAME(Type type, \ + TupleIndex tuple, \ + RegisterIndex offset, \ + MemorySpace space, \ + uint16_t valueNum) \ + { \ const internal::CLASS insn(type, tuple, offset, space, valueNum); \ - return *reinterpret_cast(&insn); \ + return insn.convert(); \ } - DECL_EMIT_FUNCTION(load, LoadInstruction) - DECL_EMIT_FUNCTION(store, StoreInstruction) + DECL_EMIT_FUNCTION(LOAD, LoadInstruction) + DECL_EMIT_FUNCTION(STORE, StoreInstruction) #undef DECL_EMIT_FUNCTION // FENCE - Instruction fence(MemorySpace space) { - internal::FenceInstruction insn(space); - return *reinterpret_cast(&insn); + Instruction FENCE(MemorySpace space) { + const internal::FenceInstruction insn(space); + return insn.convert(); } // LABEL - Instruction label(LabelIndex labelIndex) { - internal::LabelInstruction insn(labelIndex); - return *reinterpret_cast(&insn); + Instruction LABEL(LabelIndex labelIndex) { + const internal::LabelInstruction insn(labelIndex); + return insn.convert(); } } /* namespace ir */ diff --git a/backend/src/ir/instruction.hpp b/backend/src/ir/instruction.hpp index 8913212..80b30d3 100644 --- a/backend/src/ir/instruction.hpp +++ b/backend/src/ir/instruction.hpp @@ -25,16 +25,16 @@ #ifndef __GBE_IR_INSTRUCTION_HPP__ #define __GBE_IR_INSTRUCTION_HPP__ -#include "sys/platform.hpp" #include "ir/register.hpp" #include "ir/value.hpp" #include "ir/type.hpp" +#include "sys/platform.hpp" namespace gbe { namespace ir { /*! All opcodes */ - enum Opcode : uint8_t { + enum Opcode : char { #define DECL_INSN(INSN, FAMILY) OP_##INSN, #include "ir/instruction.hxx" #undef DECL_INSN @@ -67,6 +67,14 @@ namespace ir { class ALIGNED(sizeof(uint64_t)) Instruction { public: + /*! Initialize the instruction from a 8 bytes stream */ + INLINE Instruction(const char *stream) { + opcode = Opcode(stream[0]); + for (uint32_t byte = 0; byte < opaqueSize; ++byte) + opaque[byte] = stream[byte+1]; + } + /*! Uninitialize instruction */ + INLINE Instruction(void) {} /*! Get the instruction opcode */ INLINE Opcode getOpcode(void) const { return opcode; } /*! Get the number of sources for this instruction */ @@ -81,11 +89,11 @@ namespace ir { Register getDst(const Function &fn, uint32_t ID = 0u) const; /*! Get the register of the given destination */ Register getSrc(const Function &fn, uint32_t ID = 0u) const; - /*! Check that the instruction is well formed. Return true if well formed. - * j - * Otherwise, fill the string with a help message + /*! Check that the instruction is well formed (type properly match, + * registers not of bound and so on). If not well formed, provide a reason + * in string why */ - bool check(void) const; + bool wellFormed(const Function &fn, std::string &why) const; /*! Indicates if the instruction belongs to instruction type T. Typically, T * can be BinaryInstruction, UnaryInstruction, LoadInstruction and so on */ @@ -93,12 +101,13 @@ namespace ir { return T::isClassOf(*this); } protected: - Opcode opcode; //!< Idendifies the instruction - uint8_t opaque[sizeof(uint64_t)-sizeof(uint8_t)];//!< Remainder of it + enum { opaqueSize = sizeof(uint64_t)-sizeof(uint8_t) }; + Opcode opcode; //!< Idendifies the instruction + char opaque[opaqueSize]; //!< Remainder of it }; // Check that the instruction is properly formed by the compiler - STATIC_ASSERT(sizeof(Instruction) == sizeof(uint64_t)); + static_assert(sizeof(Instruction)==sizeof(uint64_t), "Bad instruction size"); /*! Unary instructions are typed. dst and sources share the same type */ class UnaryInstruction : public Instruction { @@ -259,65 +268,67 @@ namespace ir { /////////////////////////////////////////////////////////////////////////// /*! mov.type dst src */ - Instruction mov(Type type, RegisterIndex dst, RegisterIndex src); + Instruction MOV(Type type, RegisterIndex dst, RegisterIndex src); /*! cos.type dst src */ - Instruction cos(Type type, RegisterIndex dst, RegisterIndex src); + Instruction COS(Type type, RegisterIndex dst, RegisterIndex src); /*! sin.type dst src */ - Instruction sin(Type type, RegisterIndex dst, RegisterIndex src); + Instruction SIN(Type type, RegisterIndex dst, RegisterIndex src); /*! tan.type dst src */ - Instruction tan(Type type, RegisterIndex dst, RegisterIndex src); + Instruction TAN(Type type, RegisterIndex dst, RegisterIndex src); /*! log.type dst src */ - Instruction log(Type type, RegisterIndex dst, RegisterIndex src); + Instruction LOG(Type type, RegisterIndex dst, RegisterIndex src); /*! sqr.type dst src */ - Instruction sqr(Type type, RegisterIndex dst, RegisterIndex src); + Instruction SQR(Type type, RegisterIndex dst, RegisterIndex src); /*! rsq.type dst src */ - Instruction rsq(Type type, RegisterIndex dst, RegisterIndex src); + Instruction RSQ(Type type, RegisterIndex dst, RegisterIndex src); /*! pow.type dst src0 src1 */ - Instruction pow(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction POW(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! mul.type dst src0 src1 */ - Instruction mul(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction MUL(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! add.type dst src0 src1 */ - Instruction add(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction ADD(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! sub.type dst src0 src1 */ - Instruction sub(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction SUB(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! div.type dst src0 src1 */ - Instruction div(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction DIV(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! rem.type dst src0 src1 */ - Instruction rem(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction REM(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! shl.type dst src0 src1 */ - Instruction shl(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction SHL(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! shr.type dst src0 src1 */ - Instruction shr(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction SHR(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! asr.type dst src0 src1 */ - Instruction asr(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction ASR(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! bsf.type dst src0 src1 */ - Instruction bsf(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction BSF(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! bsb.type dst src0 src1 */ - Instruction bsb(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction BSB(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! or.type dst src0 src1 */ - Instruction or$(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction OR(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! xor.type dst src0 src1 */ - Instruction xor$(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction XOR(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! and.type dst src0 src1 */ - Instruction and$(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction AND(Type type, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! mad.type dst {src0, src1, src2} == src */ - Instruction mad(Type type, RegisterIndex dst, TupleIndex src); + Instruction MAD(Type type, RegisterIndex dst, TupleIndex src); /*! cvt.{dstType <- srcType} dst src */ - Instruction cvt(Type dstType, Type srcType, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); + Instruction CVT(Type dstType, Type srcType, RegisterIndex dst, RegisterIndex src0, RegisterIndex src1); /*! bra labelIndex */ - Instruction bra(LabelIndex labelIndex); + Instruction BRA(LabelIndex labelIndex); /*! (pred) bra labelIndex */ - Instruction bra(LabelIndex labelIndex, RegisterIndex pred); + Instruction BRA(LabelIndex labelIndex, RegisterIndex pred); /*! loadi.type dst value */ - Instruction loadi(Type type, RegisterIndex dst, ValueIndex value); + Instruction LOADI(Type type, RegisterIndex dst, ValueIndex value); /*! load.type.space {dst1,...,dst_valueNum} offset value */ - Instruction load(Type type, TupleIndex dst, RegisterIndex offset, MemorySpace space, uint16_t valueNum); + Instruction LOAD(Type type, TupleIndex dst, RegisterIndex offset, MemorySpace space, uint16_t valueNum); /*! store.type.space offset {src1,...,src_valueNum} value */ - Instruction store(Type type, TupleIndex src, RegisterIndex offset, MemorySpace space, uint16_t valueNum); + Instruction STORE(Type type, TupleIndex src, RegisterIndex offset, MemorySpace space, uint16_t valueNum); /*! fence.space */ - Instruction fence(MemorySpace space); + Instruction FENCE(MemorySpace space); /*! label labelIndex */ - Instruction label(LabelIndex labelIndex); + Instruction LABEL(LabelIndex labelIndex); + /*! texture instruction TODO */ + Instruction TEX(void); } /* namespace ir */ } /* namespace gbe */ diff --git a/backend/src/ir/register.hpp b/backend/src/ir/register.hpp index c227fc9..cf97095 100644 --- a/backend/src/ir/register.hpp +++ b/backend/src/ir/register.hpp @@ -73,9 +73,9 @@ namespace ir { public: /*! Return the index of a newly allocated register register */ INLINE RegisterIndex append(Register::Family family) { - const uint32_t index = regs.size(); + const uint32_t index = regNum(); const Register reg(family); - assert(index <= MAX_INDEX); + GBE_ASSERT(index <= MAX_INDEX); regs.push_back(reg); return index; } @@ -83,7 +83,7 @@ namespace ir { template INLINE TupleIndex appendTuple(First first, Rest... rest) { const TupleIndex index = regTuples.size(); - assert(first < regs.size()); + GBE_ASSERT(first < regNum()); regTuples.push_back(first); appendTuple(rest...); return index; @@ -92,18 +92,19 @@ namespace ir { INLINE void appendTuple(void) {} /*! Return a copy of the register at index */ INLINE Register get(RegisterIndex index) const { - assert(index < regs.size() && index <= MAX_INDEX); + GBE_ASSERT(index < regNum()); return regs[index]; } /*! Get the register index from the tuple */ INLINE RegisterIndex get(TupleIndex index, uint32_t which) const { - assert(index + which < regTuples.size()); + GBE_ASSERT(index + which < regTuples.size()); return regTuples[index + which]; } /*! Number of registers in the register file */ INLINE uint32_t regNum(void) const { return regs.size(); } + /*! Number of tuples in the register file */ + INLINE uint32_t tupleNum(void) const { return regTuples.size(); } private: - enum { MAX_INDEX = 0xffff }; //!< We encode indices in 2 bytes vector regs; //!< All the registers together vector regTuples; //!< Tuples are used for many src / dst GBE_CLASS(RegisterFile); diff --git a/backend/src/ir/type.hpp b/backend/src/ir/type.hpp index 3190e72..52dd5c0 100644 --- a/backend/src/ir/type.hpp +++ b/backend/src/ir/type.hpp @@ -26,24 +26,50 @@ #define __GBE_IR_TYPE_HPP__ #include "sys/platform.hpp" +#include "ir/register.hpp" namespace gbe { namespace ir { /*! All types possibly supported by the instruction */ enum Type : uint8_t { - TYPE_S8 = 0, //!< signed 8 bits integer - TYPE_U8, //!< unsigned 8 bits integer - TYPE_S16, //!< signed 16 bits integer - TYPE_U16, //!< unsigned 16 bits integer - TYPE_S32, //!< signed 32 bits integer - TYPE_U32, //!< unsigned 32 bits integer - TYPE_S64, //!< signed 64 bits integer - TYPE_U64, //!< unsigned 64 bits integer - TYPE_FLOAT, //!< 32 bits floating point value - TYPE_DOUBLE //!< 64 bits floating point value + TYPE_BOOL = 0, //!< boolean value + TYPE_S8, //!< signed 8 bits integer + TYPE_U8, //!< unsigned 8 bits integer + TYPE_S16, //!< signed 16 bits integer + TYPE_U16, //!< unsigned 16 bits integer + TYPE_S32, //!< signed 32 bits integer + TYPE_U32, //!< unsigned 32 bits integer + TYPE_S64, //!< signed 64 bits integer + TYPE_U64, //!< unsigned 64 bits integer + TYPE_HALF, //!< 16 bits floating point value + TYPE_FLOAT, //!< 32 bits floating point value + TYPE_DOUBLE //!< 64 bits floating point value }; + /*! Get the register family for each type */ + INLINE Register::Family getFamily(Type type) { + switch (type) { + case TYPE_BOOL: + return Register::BOOL; + case TYPE_S8: + case TYPE_U8: + return Register::BYTE; + case TYPE_S16: + case TYPE_U16: + case TYPE_HALF: + return Register::WORD; + case TYPE_S32: + case TYPE_U32: + case TYPE_FLOAT: + return Register::DWORD; + case TYPE_S64: + case TYPE_U64: + case TYPE_DOUBLE: + return Register::QWORD; + }; + } + } /* namespace ir */ } /* namespace gbe */ diff --git a/backend/src/sys/platform.hpp b/backend/src/sys/platform.hpp index 0fee89e..d4c451e 100644 --- a/backend/src/sys/platform.hpp +++ b/backend/src/sys/platform.hpp @@ -217,10 +217,6 @@ #define GBE_ASSERT(EXPR) do { } while (0) #endif -/*! Compile-time assertion */ -#define STATIC_ASSERT(value) \ - struct JOIN(__,JOIN(__,__LINE__)) { int x[(value) ? 1 : -1]; } - /*! Fatal error macros */ #define NOT_IMPLEMENTED FATAL ("Not implemented") #define FATAL_IF(COND, MSG) \ -- 2.7.4