ir/unit.hpp
ir/constant.cpp
ir/constant.hpp
+ ir/constant_push.cpp
+ ir/constant_push.hpp
ir/instruction.cpp
ir/instruction.hpp
ir/liveness.cpp
--- /dev/null
+/*
+ * 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 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Benjamin Segovia <benjamin.segovia@intel.com>
+ */
+
+/**
+ * \file constant_push.cpp
+ * \author Benjamin Segovia <benjamin.segovia@intel.com>
+ */
+
+#include "ir/constant_push.hpp"
+
+namespace gbe {
+namespace ir {
+ ConstantPush::ConstantPush(void) {}
+ ConstantPush::~ConstantPush(void) {}
+} /* namespace ir */
+} /* namespace gbe */
+
--- /dev/null
+/*
+ * 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 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Benjamin Segovia <benjamin.segovia@intel.com>
+ */
+
+/**
+ * \file constant_push.hpp
+ * \author Benjamin Segovia <benjamin.segovia@intel.com>
+ */
+#ifndef __GBE_IR_CONSTANT_PUSH_HPP__
+#define __GBE_IR_CONSTANT_PUSH_HPP__
+
+#include "ir/register.hpp"
+#include "sys/map.hpp"
+
+namespace gbe {
+namespace ir {
+
+ /*! Maps the register to the function argument */
+ struct ArgLocation {
+ INLINE ArgLocation(void) {}
+ INLINE ArgLocation(uint32_t argID, uint32_t offset) :
+ argID(argID), offset(offset) {}
+ uint32_t argID; //!< Function argument
+ uint32_t offset; //!< Offset in the function argument
+ };
+
+ /*! Structure arguments can be directly pushed to the register file. We store
+ * here the mapping the function argument and the registers that contains the
+ * pushed data
+ */
+ class ConstantPush
+ {
+ public:
+ /*! Set the contanst pushing description */
+ ConstantPush(void);
+ /*! Set the contanst pushing description */
+ ~ConstantPush(void);
+ /*! Maps each register with the function argument */
+ map<Register, ArgLocation> constantMap;
+ GBE_CLASS(ConstantPush); // Uses GBE allocators
+ };
+
+} /* namespace ir */
+} /* namespace gbe */
+
+#endif /* __GBE_IR_CONSTANT_PUSH_HPP__ */
GBE_SAFE_DELETE(usedLabels);
}
+ Function &Context::getFunction(void) {
+ GBE_ASSERTM(fn != NULL, "No function currently defined");
+ return *fn;
+ }
+
void Context::startFunction(const std::string &name) {
fnStack.push_back(StackElem(fn,bb,usedLabels));
fn = unit.newFunction(name);
GBE_ASSERT(fnStack.size() != 0);
GBE_ASSERT(usedLabels != NULL);
+ // Empty function -> append a return
+ if (fn->blockNum() == 0) this->RET();
+
// Check first that all branch instructions point to valid labels
for (auto it = usedLabels->begin(); it != usedLabels->end(); ++it)
GBE_ASSERTM(*it != LABEL_IS_POINTED, "A label is used and not defined");
usedLabels = elem.usedLabels;
}
- Function &Context::getFunction(void) {
- GBE_ASSERTM(fn != NULL, "No function currently defined");
- return *fn;
- }
-
Register Context::reg(RegisterFamily family) {
GBE_ASSERTM(fn != NULL, "No function currently defined");
return fn->newRegister(family);
namespace gbe {
namespace ir {
- // We compile a unit
- class Unit;
-
/*! A context allows an easy creation of the functions (instruction stream and
* the set of immediates and registers needed for it) and constant arrays
*/
/*! Create a new context for this unit */
Context(Unit &unit);
/*! Free resources needed by context */
- ~Context(void);
+ virtual ~Context(void);
/*! Create a new function "name" */
void startFunction(const std::string &name);
/*! Close the function */
void endFunction(void);
+ /*! Get the current processed unit */
+ INLINE Unit &getUnit(void) { return unit; }
+ /*! Get the current processed function */
+ Function &getFunction(void);
/*! Create a new register with the given family for the current function */
Register reg(RegisterFamily family);
/*! Create a new immediate value */
INLINE Immediate getImmediate(ImmediateIndex index) const {
return fn->getImmediate(index);
}
- /*! Get the current processed function */
- Function &getFunction(void);
- /*! Get the current processed unit */
- INLINE Unit &getUnit(void) { return unit; }
/*! Append a new tuple */
template <typename... Args> INLINE Tuple tuple(Args...args) {
GBE_ASSERTM(fn != NULL, "No function currently defined");
* \author Benjamin Segovia <benjamin.segovia@intel.com>
*/
#include "ir/function.hpp"
+#include "ir/constant_push.hpp"
#include "sys/string.hpp"
#include "sys/map.hpp"
namespace ir {
Function::Function(const std::string &name, Profile profile) :
- name(name), profile(profile)
+ name(name), profile(profile), pushedConstant(NULL)
{ initProfile(*this); }
Function::~Function(void) {
for (auto it = blocks.begin(); it != blocks.end(); ++it) GBE_DELETE(*it);
for (auto it = args.begin(); it != args.end(); ++it) GBE_DELETE(*it);
+ GBE_SAFE_DELETE(pushedConstant);
}
void Function::sortLabels(void) {
uint32_t size; /*! == sizeof(void*) for pointer, sizeof(elem) for the rest */
};
+ /*! Constant values can be pushed into registers by the hardware. This class
+ * maintains the mapping between virtual registers and pushed values
+ */
+ class ConstantPush;
+
/*! A function is no more that a set of declared registers and a set of
* basic blocks
*/
vector<BasicBlock*> blocks; //!< All chained basic blocks
RegisterFile file; //!< RegisterDatas used by the instructions
Profile profile; //!< Current function profile
+ ConstantPush *pushedConstant; //!< All constants pushed before function is called
GBE_CLASS(Function); //!< Use gbe allocators
};
}
void lowerReturn(Unit &unit, const std::string &functionName) {
- ContextReturn *ctx = GBE_NEW(ContextReturn, unit);
- ctx->lower(functionName);
- GBE_DELETE(ctx);
+ ContextReturn ctx(unit);
+ ctx.lower(functionName);
}
/*! Characterizes how the argument is used (directly read, indirectly read,
Instruction *add; //!< Can be NULL if we only have load(arg)
Instruction *loadImm; //!< Can also be NULL
uint64_t offset; //!< Offset where to load in the structure
+ uint32_t argID; //!< Associated function argument
};
/*! List of direct loads */
/*! Perform all function arguments substitution if needed */
void lower(const std::string &name);
/*! Lower the given function argument accesses */
- void lower(FunctionArgument &arg);
+ void lower(uint32_t argID);
+ /*! Build the constant push for the function */
+ void buildConstantPush(void);
/*! Inspect the given function argument to see how it is used. If this is
* direct loads only, we also output the list of instructions used for each
* load
*/
- ArgUse getArgUse(FunctionArgument &arg);
+ ArgUse getArgUse(uint32_t argID);
/*! Recursively look if there is a store in the given use */
bool useStore(const ValueDef &def, set<const Instruction*> &visited);
/*! Look if the pointer use only load with immediate offsets */
- bool matchLoadAddImm(const FunctionArgument &arg);
+ bool matchLoadAddImm(uint32_t argID);
Liveness *liveness; //!< To compute the function graph
FunctionDAG *dag; //!< Contains complete dependency information
Unit &unit; //!< The unit we process
LoadAddImmSeq seq; //!< All the direct loads
};
+ INLINE uint64_t getOffsetFromImm(const Immediate &imm) {
+ switch (imm.type) {
+ case TYPE_DOUBLE:
+ case TYPE_FLOAT:
+ case TYPE_S64:
+ case TYPE_U64:
+ case TYPE_U32:
+ case TYPE_U16:
+ case TYPE_U8: return imm.data.u64;
+ case TYPE_S32: return int64_t(imm.data.s32);
+ case TYPE_S16: return int64_t(imm.data.s16);
+ case TYPE_S8: return int64_t(imm.data.s8);
+ case TYPE_BOOL:
+ case TYPE_HALF: NOT_SUPPORTED; return 0;
+ }
+ return 0;
+ }
+
+ bool matchLoad(Instruction *insn,
+ Instruction *add,
+ Instruction *loadImm,
+ uint64_t offset,
+ uint32_t argID,
+ LoadAddImm &loadAddImm)
+ {
+ const Opcode opcode = insn->getOpcode();
+
+ if (opcode == OP_LOAD) {
+ LoadInstruction *load = cast<LoadInstruction>(insn);
+ if (load->getAddressSpace() != MEM_PRIVATE)
+ return false;
+ loadAddImm.load = insn;
+ loadAddImm.add = add;
+ loadAddImm.loadImm = loadImm;
+ loadAddImm.offset = offset;
+ loadAddImm.argID = argID;
+ return true;
+ } else
+ return false;
+ }
+
+
FunctionArgumentLowerer::FunctionArgumentLowerer(Unit &unit) :
liveness(NULL), dag(NULL), unit(unit) {}
FunctionArgumentLowerer::~FunctionArgumentLowerer(void) {
GBE_SAFE_DELETE(liveness);
this->liveness = GBE_NEW(ir::Liveness, *fn);
this->dag = GBE_NEW(ir::FunctionDAG, *this->liveness);
+
+ // Process all structure arguments and find all the direct loads we can
+ // replace
const uint32_t argNum = fn->argNum();
for (uint32_t argID = 0; argID < argNum; ++argID) {
FunctionArgument &arg = fn->getInput(argID);
if (arg.type != FunctionArgument::STRUCTURE) continue;
- this->lower(arg);
+ this->lower(argID);
+ }
+ }
+
+ INLINE bool operator< (const ArgLocation &arg0, const ArgLocation &arg1) {
+ if (arg0.argID != arg1.argID) return arg0.argID < arg1.argID;
+ return arg0.offset < arg1.offset;
+ }
+
+ void FunctionArgumentLowerer::buildConstantPush(void)
+ {
+ // The argument location we already pushed (since the same argument location
+ // can be used several times)
+ set<ArgLocation> inserted;
+ for (const auto &loadAddImm : seq) {
+ LoadInstruction *load = cast<LoadInstruction>(loadAddImm.load);
+ const uint32_t valueNum = load->getValueNum();
+ for (uint32_t valueID = 0; valueID < valueNum; ++valueID) {
+ const Type type = load->getValueType();
+ const RegisterFamily family = getFamily(type);
+ const uint32_t size = getFamilySize(family);
+ const uint32_t offset = loadAddImm.offset + valueID * size;
+ const ArgLocation argLocation(loadAddImm.argID, offset);
+ if (inserted.contains(argLocation))
+ continue;
+ const Register reg = load->getValue(valueID);
+ const Register pushed = fn->newRegister(family);
+ const Instruction mov = MOV(type, reg, pushed);
+ mov.replace(load);
+ }
}
}
return false;
}
- INLINE uint64_t getOffsetFromImm(const Immediate &imm) {
- switch (imm.type) {
- case TYPE_DOUBLE:
- case TYPE_FLOAT:
- case TYPE_S64:
- case TYPE_U64:
- case TYPE_U32:
- case TYPE_U16:
- case TYPE_U8: return imm.data.u64;
- case TYPE_S32: return int64_t(imm.data.s32);
- case TYPE_S16: return int64_t(imm.data.s16);
- case TYPE_S8: return int64_t(imm.data.s8);
- case TYPE_BOOL:
- case TYPE_HALF: NOT_SUPPORTED; return 0;
- }
- return 0;
- }
-
- bool matchLoad(Instruction *insn,
- Instruction *add,
- Instruction *loadImm,
- uint64_t offset,
- LoadAddImm &loadAddImm)
+ bool FunctionArgumentLowerer::matchLoadAddImm(uint32_t argID)
{
- const Opcode opcode = insn->getOpcode();
-
- if (opcode == OP_LOAD) {
- LoadInstruction *load = cast<LoadInstruction>(insn);
- if (load->getAddressSpace() != MEM_PRIVATE)
- return false;
- loadAddImm.load = insn;
- loadAddImm.add = add;
- loadAddImm.loadImm = loadImm;
- loadAddImm.offset = offset;
- return true;
- } else
- return false;
- }
-
- bool FunctionArgumentLowerer::matchLoadAddImm(const FunctionArgument &arg) {
+ const FunctionArgument &arg = fn->getInput(argID);
LoadAddImmSeq tmpSeq;
// Inspect all uses of the function argument pointer
// load dst arg
LoadAddImm loadAddImm;
- if (matchLoad(insn, NULL, NULL, 0, loadAddImm)) {
+ if (matchLoad(insn, NULL, NULL, 0, argID, loadAddImm)) {
tmpSeq.push_back(loadAddImm);
continue;
}
// We finally find something like load dst arg+imm
LoadAddImm loadAddImm;
- if (matchLoad(insn, add, loadImm, offset, loadAddImm)) {
+ if (matchLoad(insn, add, loadImm, offset, argID, loadAddImm)) {
tmpSeq.push_back(loadAddImm);
continue;
}
return true;
}
- ArgUse FunctionArgumentLowerer::getArgUse(FunctionArgument &arg)
+ ArgUse FunctionArgumentLowerer::getArgUse(uint32_t argID)
{
+ FunctionArgument &arg = fn->getInput(argID);
+
// case 1 - we may store something to the structure argument
set<const Instruction*> visited;
if (this->useStore(ValueDef(&arg), visited))
return ARG_WRITTEN;
// case 2 - we look for the patterns: LOAD(ptr) or LOAD(ptr+imm)
- if (this->matchLoadAddImm(arg))
+ if (this->matchLoadAddImm(argID))
return ARG_DIRECT_READ;
// case 3 - LOAD(ptr+runtime_value)
return ARG_INDIRECT_READ;
}
- void FunctionArgumentLowerer::lower(FunctionArgument &arg) {
- const ArgUse argUse = this->getArgUse(arg);
+ void FunctionArgumentLowerer::lower(uint32_t argID) {
+ const ArgUse argUse = this->getArgUse(argID);
GBE_ASSERTM(argUse != ARG_WRITTEN,
"TODO A store to a structure argument "
"(i.e. not a char/short/int/float argument) has been found. "
FAMILY_QWORD = 4
};
+ INLINE uint32_t getFamilySize(RegisterFamily family) {
+ switch (family) {
+ case FAMILY_BYTE: return 1;
+ case FAMILY_WORD: return 2;
+ case FAMILY_DWORD: return 4;
+ case FAMILY_QWORD: return 8;
+ default: NOT_SUPPORTED;
+ };
+ return 0;
+ }
+
/*! A register can be either a byte, a word, a dword or a qword. We store this
* value into a register data (which makes the register file)
*/
INLINE bool operator< (const Register &r0, const Register &r1) {
return r0.value() < r1.value();
}
- /*! Useful to encode anything special */
- static const Register invalidRegister(0xffff);
/*! Tuple is the position of the first register in the tuple vector. We
* enforce type safety with this class
public:
/*! Return the index of a newly allocated register */
INLINE Register append(RegisterFamily family) {
- GBE_ASSERTM(regNum() <= MAX_INDEX,
- "Too many defined registers (only 65536 are supported)");
+ GBE_ASSERTM(regNum() < MAX_INDEX,
+ "Too many defined registers (only 65535 are supported)");
const uint16_t index = regNum();
const RegisterData reg(family);
regs.push_back(reg);
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(); }
+ /*! register and tuple indices are short */
+ enum { MAX_INDEX = 0xffff };
private:
vector<RegisterData> regs; //!< All the registers together
vector<Register> regTuples; //!< Tuples are used for many src / dst
- enum { MAX_INDEX = 0xffff }; //!< register and tuple indices are short
GBE_CLASS(RegisterFile);
};
+ /*! Useful to encode anything special */
+ static const Register invalidRegister(RegisterFile::MAX_INDEX);
+
/*! Output the register file string in the given stream */
std::ostream &operator<< (std::ostream &out, const RegisterFile &file);
return FAMILY_QWORD;
}
private:
+ friend class ContextInterface; //!< Can free modify the unit
hash_map<std::string, Function*> functions; //!< All the defined functions
ConstantSet constantSet; //!< All the constants defined in the unit
PointerSize pointerSize; //!< Size shared by all pointers
namespace gbe {
namespace ir {
- /*! To build the chains (i.e. basically the DAG of values), we are going to
+ /*! To build the chains (i.e. basically the graph of values), we are going to
* iterate on liveout definitions: for each block and for each variable
* (ir::Register) alive at the end of the block (in Block::LiveOut), we are
* computing the set of all possible value definitions. Using these value
const UseSet &FunctionDAG::getUse(const Instruction *insn, uint32_t dstID) const {
return this->getUse(ValueDef(insn, dstID));
}
- const UseSet &FunctionDAG::getUse(const FunctionArgument *input) const {
- return this->getUse(ValueDef(input));
+ const UseSet &FunctionDAG::getUse(const FunctionArgument *arg) const {
+ return this->getUse(ValueDef(arg));
}
const UseSet &FunctionDAG::getUse(const Register ®) const {
return this->getUse(ValueDef(reg));
/*! Declare a class with custom allocators */
#define GBE_CLASS(TYPE) \
- GBE_STRUCT(TYPE) \
+ GBE_STRUCT(TYPE) \
private:
/*! Declare a structure with custom allocators */
-#define GBE_STRUCT(TYPE) \
-public: \
- void* operator new(size_t size) { \
- if (AlignOf<TYPE>::value > sizeof(uintptr_t)) \
- return gbe::alignedMalloc(size, AlignOf<TYPE>::value); \
- else \
- return gbe::memAlloc(size); \
- } \
- void* operator new[](size_t size) { \
- if (AlignOf<TYPE>::value > sizeof(uintptr_t)) \
- return gbe::alignedMalloc(size, AlignOf<TYPE>::value); \
- else \
- return gbe::memAlloc(size); \
- } \
- void* operator new(size_t size, void *p) { return p; } \
- void* operator new[](size_t size, void *p) { return p; } \
- void operator delete(void* ptr) { \
- if (AlignOf<TYPE>::value > sizeof(uintptr_t)) \
- return gbe::alignedFree(ptr); \
- else \
- return gbe::memFree(ptr); \
- } \
- void operator delete[](void* ptr) { \
- if (AlignOf<TYPE>::value > sizeof(uintptr_t)) \
- return gbe::alignedFree(ptr); \
- else \
- return gbe::memFree(ptr); \
- } \
+#define GBE_STRUCT(TYPE) \
+public: \
+ void* operator new(size_t size) { \
+ return gbe::alignedMalloc(size, GBE_DEFAULT_ALIGNMENT); \
+ } \
+ void* operator new[](size_t size) { \
+ return gbe::alignedMalloc(size, GBE_DEFAULT_ALIGNMENT); \
+ } \
+ void* operator new(size_t size, void *p) { return p; } \
+ void* operator new[](size_t size, void *p) { return p; } \
+ void operator delete(void* ptr) { return gbe::alignedFree(ptr); } \
+ void operator delete[](void* ptr) { return gbe::alignedFree(ptr); }
/*! Macros to handle allocation position */
-#define GBE_NEW(T,...) \
+#define GBE_NEW(T,...) \
gbe::_MemDebuggerInsertAlloc(new T(__VA_ARGS__), __FILE__, __FUNCTION__, __LINE__)
-#define GBE_NEW_ARRAY(T,N,...) \
+#define GBE_NEW_ARRAY(T,N,...) \
gbe::_MemDebuggerInsertAlloc(new T[N](__VA_ARGS__), __FILE__, __FUNCTION__, __LINE__)
-#define GBE_NEW_P(T,X,...) \
+#define GBE_NEW_P(T,X,...) \
gbe::_MemDebuggerInsertAlloc(new (X) T(__VA_ARGS__), __FILE__, __FUNCTION__, __LINE__)
-#define GBE_DELETE(X) \
+#define GBE_DELETE(X) \
do { gbe::MemDebuggerRemoveAlloc(X); delete X; } while (0)
-#define GBE_DELETE_ARRAY(X) \
+#define GBE_DELETE_ARRAY(X) \
do { gbe::MemDebuggerRemoveAlloc(X); delete[] X; } while (0)
-#define GBE_MALLOC(SZ) \
+#define GBE_MALLOC(SZ) \
gbe::MemDebuggerInsertAlloc(gbe::memAlloc(SZ),__FILE__, __FUNCTION__, __LINE__)
-#define GBE_FREE(X) \
+#define GBE_FREE(X) \
do { gbe::MemDebuggerRemoveAlloc(X); gbe::memFree(X); } while (0)
-#define GBE_ALIGNED_FREE(X) \
+#define GBE_ALIGNED_FREE(X) \
do { gbe::MemDebuggerRemoveAlloc(X); gbe::alignedFree(X); } while (0)
#define GBE_ALIGNED_MALLOC(SZ,ALIGN) \
};
/*! Helper macros to build and destroy objects with a pool */
-#define DECL_POOL(TYPE, POOL) \
- GrowingPool<TYPE> POOL; \
- template <typename... Args> \
- TYPE *new##TYPE(Args... args) { \
- return new (POOL.allocate()) TYPE(args...); \
- } \
- void delete##TYPE(TYPE *ptr) { \
- ptr->~TYPE(); \
- POOL.deallocate(ptr); \
+#define DECL_POOL(TYPE, POOL) \
+ GrowingPool<TYPE> POOL; \
+ template <typename... Args> \
+ TYPE *new##TYPE(Args... args) { \
+ return new (POOL.allocate()) TYPE(args...); \
+ } \
+ void delete##TYPE(TYPE *ptr) { \
+ ptr->~TYPE(); \
+ POOL.deallocate(ptr); \
}
} /* namespace gbe */
/*! Run-time assertion */
#if GBE_DEBUG
-#define GBE_ASSERT(EXPR) do { \
- if (UNLIKELY(!(EXPR))) \
- gbe::onFailedAssertion(#EXPR, __FILE__, __FUNCTION__, __LINE__);\
+#define GBE_ASSERT(EXPR) do { \
+ if (UNLIKELY(!(EXPR))) \
+ gbe::onFailedAssertion(#EXPR, __FILE__, __FUNCTION__, __LINE__); \
} while (0)
-#define GBE_ASSERTM(EXPR, MSG) do { \
- if (UNLIKELY(!(EXPR))) \
- gbe::onFailedAssertion(MSG, __FILE__, __FUNCTION__, __LINE__); \
+#define GBE_ASSERTM(EXPR, MSG) do { \
+ if (UNLIKELY(!(EXPR))) \
+ gbe::onFailedAssertion(MSG, __FILE__, __FUNCTION__, __LINE__); \
} while (0)
#else
#define GBE_ASSERT(EXPR) do { } while (0)
#define NOT_SUPPORTED GBE_ASSERTM (false, "Not supported")
/*! Fatal error macros */
-#define FATAL_IF(COND, MSG) \
-do { \
- if(UNLIKELY(COND)) FATAL(MSG); \
+#define FATAL_IF(COND, MSG) \
+do { \
+ if(UNLIKELY(COND)) FATAL(MSG); \
} while (0)
/* Safe deletion macros */
#define HERE (STRING(__LINE__) "@" __FILE__)
/*! Typesafe encapusalation of a type (mostly for integers) */
-#define TYPE_SAFE(SAFE, UNSAFE) \
-class SAFE \
-{ \
-public: \
- INLINE SAFE(void) {} \
- explicit INLINE SAFE(uint16_t unsafe) : unsafe(unsafe) {} \
- INLINE operator UNSAFE (void) const { return unsafe; } \
- UNSAFE value(void) const { return unsafe; } \
-private: \
- UNSAFE unsafe; \
+#define TYPE_SAFE(SAFE, UNSAFE) \
+class SAFE \
+{ \
+public: \
+ INLINE SAFE(void) {} \
+ explicit INLINE SAFE(uint16_t unsafe) : unsafe(unsafe) {} \
+ INLINE operator UNSAFE (void) const { return unsafe; } \
+ UNSAFE value(void) const { return unsafe; } \
+private: \
+ UNSAFE unsafe; \
};
+/*! Default alignment for the platform */
+#define GBE_DEFAULT_ALIGNMENT 16
+
/*! Portable AlignOf */
template <typename T>
-struct AlignOf
-{
+struct AlignOf {
struct Helper { char x; T t; };
enum { value = offsetof(Helper, t) };
};