/*
* 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 instruction.cpp
* \author Benjamin Segovia
*/
#include "ir/instruction.hpp"
#include "ir/function.hpp"
namespace gbe {
namespace ir {
///////////////////////////////////////////////////////////////////////////
// Implements the concrete implementations of the instruction classes. We
// cast an instruction to an internal class to run the given member function
///////////////////////////////////////////////////////////////////////////
namespace internal
{
#define ALIGNED_INSTRUCTION ALIGNED(ALIGNOF(Instruction))
/*! Policy shared by all the internal instructions */
struct BasePolicy {
/*! Create an instruction from its internal representation */
Instruction convert(void) const {
return Instruction(reinterpret_cast(&this->opcode));
}
/*! Output the opcode in the given stream */
INLINE void outOpcode(std::ostream &out) const {
switch (opcode) {
#define DECL_INSN(OPCODE, CLASS) case OP_##OPCODE: out << #OPCODE; break;
#include "instruction.hxx"
#undef DECL_INSN
case OP_INVALID: NOT_SUPPORTED; break;
};
}
/*! Instruction opcode */
Opcode opcode;
};
/*! For regular n source instructions */
template
struct NSrcPolicy {
INLINE uint32_t getSrcNum(void) const { return srcNum; }
INLINE Register getSrc(const Function &fn, uint32_t ID) const {
GBE_ASSERTM((int) ID < (int) srcNum, "Out-of-bound source");
return static_cast(this)->src[ID];
}
INLINE void setSrc(Function &fn, uint32_t ID, Register reg) {
GBE_ASSERTM((int) ID < (int) srcNum, "Out-of-bound source");
static_cast(this)->src[ID] = reg;
}
};
/*! For regular n destinations instructions */
template
struct NDstPolicy {
INLINE uint32_t getDstNum(void) const { return dstNum; }
INLINE Register getDst(const Function &fn, uint32_t ID) const {
GBE_ASSERTM((int) ID < (int) dstNum, "Out-of-bound destination");
return static_cast(this)->dst[ID];
}
INLINE void setDst(Function &fn, uint32_t ID, Register reg) {
GBE_ASSERTM((int) ID < (int) dstNum, "Out-of-bound destination");
static_cast(this)->dst[ID] = reg;
}
};
/*! For instructions that use a tuple for source */
template
struct TupleSrcPolicy {
INLINE uint32_t getSrcNum(void) const {
return static_cast(this)->srcNum;
}
INLINE Register getSrc(const Function &fn, uint32_t ID) const {
GBE_ASSERTM(ID < static_cast(this)->srcNum, "Out-of-bound source register");
return fn.getRegister(static_cast(this)->src, ID);
}
INLINE void setSrc(Function &fn, uint32_t ID, Register reg) {
GBE_ASSERTM(ID < static_cast(this)->srcNum, "Out-of-bound source register");
return fn.setRegister(static_cast(this)->src, ID, reg);
}
};
/*! For instructions that use a tuple for destination */
template
struct TupleDstPolicy {
INLINE uint32_t getDstNum(void) const {
return static_cast(this)->dstNum;
}
INLINE Register getDst(const Function &fn, uint32_t ID) const {
GBE_ASSERTM(ID < static_cast(this)->dstNum, "Out-of-bound source register");
return fn.getRegister(static_cast(this)->dst, ID);
}
INLINE void setDst(Function &fn, uint32_t ID, Register reg) {
GBE_ASSERTM(ID < static_cast(this)->dstNum, "Out-of-bound source register");
return fn.setRegister(static_cast(this)->dst, ID, reg);
}
};
/*! All unary and binary arithmetic instructions */
template // 1 or 2
class ALIGNED_INSTRUCTION NaryInstruction :
public BasePolicy,
public NSrcPolicy, srcNum>,
public NDstPolicy, 1>
{
public:
INLINE Type getType(void) const { return this->type; }
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
Type type; //!< Type of the instruction
Register dst[1]; //!< Index of the register in the register file
Register src[srcNum]; //!< Indices of the sources
};
/*! All 0-source arithmetic instructions */
class ALIGNED_INSTRUCTION NullaryInstruction : public NaryInstruction<0>
{
public:
NullaryInstruction(Opcode opcode, Type type, Register dst) {
this->opcode = opcode;
this->type = type;
this->dst[0] = dst;
}
};
/*! All 1-source arithmetic instructions */
class ALIGNED_INSTRUCTION UnaryInstruction : public NaryInstruction<1>
{
public:
UnaryInstruction(Opcode opcode, Type type, Register dst, Register src) {
this->opcode = opcode;
this->type = type;
this->dst[0] = dst;
this->src[0] = src;
}
};
/*! All 2-source arithmetic instructions */
class ALIGNED_INSTRUCTION BinaryInstruction : public NaryInstruction<2>
{
public:
BinaryInstruction(Opcode opcode,
Type type,
Register dst,
Register src0,
Register src1) {
this->opcode = opcode;
this->type = type;
this->dst[0] = dst;
this->src[0] = src0;
this->src[1] = src1;
}
INLINE bool commutes(void) const {
switch (opcode) {
case OP_ADD:
case OP_ADDSAT:
case OP_XOR:
case OP_OR:
case OP_AND:
case OP_MUL:
return true;
default:
return false;
}
}
};
class ALIGNED_INSTRUCTION TernaryInstruction :
public BasePolicy,
public NDstPolicy,
public TupleSrcPolicy
{
public:
TernaryInstruction(Opcode opcode,
Type type,
Register dst,
Tuple src) {
this->opcode = opcode;
this->type = type;
this->dst[0] = dst;
this->src = src;
}
Type getType(void) const { return type; }
bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
Type type;
Register dst[1];
Tuple src;
static const uint32_t srcNum = 3;
};
/*! Three sources mean we need a tuple to encode it */
class ALIGNED_INSTRUCTION SelectInstruction :
public BasePolicy,
public NDstPolicy,
public TupleSrcPolicy
{
public:
SelectInstruction(Type type, Register dst, Tuple src) {
this->opcode = OP_SEL;
this->type = type;
this->dst[0] = dst;
this->src = src;
}
INLINE Type getType(void) const { return this->type; }
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
Type type; //!< Type of the instruction
Register dst[1]; //!< Dst is the register index
Tuple src; //!< 3 sources do not fit in 8 bytes -> use a tuple
static const uint32_t srcNum = 3;
};
/*! Comparison instructions take two sources of the same type and return a
* boolean value. Since it is pretty similar to binary instruction, we
* steal all the methods from it, except wellFormed (dst register is always
* a boolean value)
*/
class ALIGNED_INSTRUCTION CompareInstruction :
public NaryInstruction<2>
{
public:
CompareInstruction(Opcode opcode,
Type type,
Register dst,
Register src0,
Register src1)
{
this->opcode = opcode;
this->type = type;
this->dst[0] = dst;
this->src[0] = src0;
this->src[1] = src1;
}
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
};
class ALIGNED_INSTRUCTION BitCastInstruction :
public BasePolicy,
public TupleSrcPolicy,
public TupleDstPolicy
{
public:
BitCastInstruction(Type dstType,
Type srcType,
Tuple dst,
Tuple src,
uint8_t dstNum,
uint8_t srcNum)
{
this->opcode = OP_BITCAST;
this->dst = dst;
this->src = src;
this->dstFamily = getFamily(dstType);
this->srcFamily = getFamily(srcType);
GBE_ASSERT(srcNum <= Instruction::MAX_SRC_NUM && dstNum <= Instruction::MAX_DST_NUM);
this->dstNum = dstNum;
this->srcNum = srcNum;
}
INLINE Type getSrcType(void) const { return getType((RegisterFamily)srcFamily); }
INLINE Type getDstType(void) const { return getType((RegisterFamily)dstFamily); }
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
uint8_t dstFamily:4; //!< family to cast to
uint8_t srcFamily:4; //!< family to cast from
Tuple dst;
Tuple src;
uint8_t dstNum; //!,
public NSrcPolicy
{
public:
ConvertInstruction(Opcode opcode,
Type dstType,
Type srcType,
Register dst,
Register src)
{
this->opcode = opcode;
this->dst[0] = dst;
this->src[0] = src;
this->dstType = dstType;
this->srcType = srcType;
}
INLINE Type getSrcType(void) const { return this->srcType; }
INLINE Type getDstType(void) const { return this->dstType; }
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
Register dst[1];
Register src[1];
Type dstType; //!< Type to convert to
Type srcType; //!< Type to convert from
};
class ALIGNED_INSTRUCTION MemInstruction :
public BasePolicy
{
public:
MemInstruction(AddressMode _AM,
AddressSpace _AS,
bool _dwAligned,
Type _type,
Register _offset)
: AM(_AM),
AS(_AS),
dwAligned(_dwAligned),
type(_type),
SurfaceIndex(0),
offset(_offset) {
}
AddressMode getAddressMode() const { return AM; }
AddressSpace getAddressSpace() const { return AS; }
/*! MemInstruction may have one possible btiReg */
Register getBtiReg() const { assert(AM == AM_DynamicBti); return BtiReg; }
unsigned getSurfaceIndex() const { assert(AM != AM_DynamicBti); return SurfaceIndex; }
Register getAddressRegister()const { return offset; }
unsigned getAddressIndex() const { return 0; }
Type getValueType() const { return type; }
INLINE bool isAligned(void) const { return !!dwAligned; }
void setSurfaceIndex (unsigned id) { SurfaceIndex = id; }
void setBtiReg(Register reg) { BtiReg = reg; }
protected:
/*! including address reg + optional bti reg */
int getBaseSrcNum() const { return AM == AM_DynamicBti ? 2 : 1; }
bool hasExtraBtiReg() const { return AM == AM_DynamicBti; }
AddressMode AM;
AddressSpace AS;
uint8_t dwAligned : 1;
Type type;
union {
Register BtiReg;
unsigned SurfaceIndex;
};
Register offset;
};
class ALIGNED_INSTRUCTION AtomicInstruction :
public MemInstruction,
public NDstPolicy
{
public:
AtomicInstruction(AtomicOps atomicOp,
Type type,
Register dst,
AddressSpace addrSpace,
Register address,
Tuple payload,
AddressMode AM)
: MemInstruction(AM, addrSpace, true, type, address)
{
this->opcode = OP_ATOMIC;
this->atomicOp = atomicOp;
this->dst[0] = dst;
this->payload = payload;
int payloadNum = 1;
if((atomicOp == ATOMIC_OP_INC) ||
(atomicOp == ATOMIC_OP_DEC))
payloadNum = 0;
if(atomicOp == ATOMIC_OP_CMPXCHG)
payloadNum = 2;
srcNum = payloadNum + getBaseSrcNum();
}
INLINE Register getSrc(const Function &fn, uint32_t ID) const {
GBE_ASSERTM((int)ID < (int)srcNum, "Out-of-bound source register for atomic");
if (ID == 0) {
return offset;
} else if (hasExtraBtiReg() && (int)ID == (int)srcNum-1) {
return getBtiReg();
} else {
return fn.getRegister(payload, ID - 1);
}
}
INLINE void setSrc(Function &fn, uint32_t ID, Register reg) {
GBE_ASSERTM((int)ID < (int)srcNum, "Out-of-bound source register for atomic");
if (ID == 0) {
offset = reg;
} else if (hasExtraBtiReg() && (int)ID == (int)srcNum - 1) {
setBtiReg(reg);
} else {
fn.setRegister(payload, ID - 1, reg);
}
}
INLINE uint32_t getSrcNum(void) const { return srcNum; }
INLINE AtomicOps getAtomicOpcode(void) const { return this->atomicOp; }
INLINE bool wellFormed(const Function &fn, std::string &whyNot) const;
INLINE void out(std::ostream &out, const Function &fn) const;
Register dst[1];
Tuple payload;
uint8_t srcNum:3; //!