/*
* 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 .
*
*/
#include "immediate.hpp"
using namespace gbe;
using namespace ir;
#define SCALAR_SAME_TYPE_ASSERT() \
GBE_ASSERT(this->getType() == right.getType() && \
this->getElemNum() == right.getElemNum() && \
this->getElemNum() == 1)
#define DECLAR_BINARY_ALL_TYPE_OP(OP) \
Immediate Immediate::operator OP (const Immediate &right) const { \
/*SCALAR_SAME_TYPE_ASSERT();*/ \
switch (this->getType()) { \
default: \
GBE_ASSERT(0); \
case TYPE_BOOL: return Immediate(*this->data.b OP *right.data.b); \
case TYPE_S8: return Immediate(*this->data.s8 OP *right.data.s8); \
case TYPE_U8: return Immediate(*this->data.u8 OP *right.data.u8); \
case TYPE_S16: return Immediate(*this->data.s16 OP *right.data.s16); \
case TYPE_U16: return Immediate(*this->data.u16 OP *right.data.u16); \
case TYPE_S32: return Immediate(*this->data.s32 OP *right.data.s32); \
case TYPE_U32: return Immediate(*this->data.u32 OP *right.data.u32); \
case TYPE_S64: return Immediate(*this->data.s64 OP *right.data.s64); \
case TYPE_U64: return Immediate(*this->data.u64 OP *right.data.u64); \
case TYPE_FLOAT: return Immediate(*this->data.f32 OP *right.data.f32); \
case TYPE_DOUBLE: return Immediate(*this->data.f64 OP *right.data.f64); \
}\
return *this;\
}
DECLAR_BINARY_ALL_TYPE_OP(+)
DECLAR_BINARY_ALL_TYPE_OP(-)
DECLAR_BINARY_ALL_TYPE_OP(*)
DECLAR_BINARY_ALL_TYPE_OP(/)
DECLAR_BINARY_ALL_TYPE_OP(>)
//DECLAR_BINARY_ALL_TYPE_OP(<)
DECLAR_BINARY_ALL_TYPE_OP(==)
DECLAR_BINARY_ALL_TYPE_OP(!=)
DECLAR_BINARY_ALL_TYPE_OP(>=)
DECLAR_BINARY_ALL_TYPE_OP(<=)
DECLAR_BINARY_ALL_TYPE_OP(&&)
#undef DECLAR_BINARY_ALL_TYPE_OP
#define DECLAR_BINARY_INT_TYPE_OP(OP) \
Immediate Immediate::operator OP (const Immediate &right) const { \
/*SCALAR_SAME_TYPE_ASSERT();*/ \
switch (this->getType()) { \
default: \
GBE_ASSERT(0); \
case TYPE_BOOL: return Immediate(*this->data.b OP *right.data.b); \
case TYPE_S8: return Immediate(*this->data.s8 OP *right.data.s8); \
case TYPE_U8: return Immediate(*this->data.u8 OP *right.data.u8); \
case TYPE_S16: return Immediate(*this->data.s16 OP *right.data.s16); \
case TYPE_U16: return Immediate(*this->data.u16 OP *right.data.u16); \
case TYPE_S32: return Immediate(*this->data.s32 OP *right.data.s32); \
case TYPE_U32: return Immediate(*this->data.u32 OP *right.data.u32); \
case TYPE_S64: return Immediate(*this->data.s64 OP *right.data.s64); \
case TYPE_U64: return Immediate(*this->data.u64 OP *right.data.u64); \
}\
return *this;\
}
DECLAR_BINARY_INT_TYPE_OP(%)
DECLAR_BINARY_INT_TYPE_OP(&)
DECLAR_BINARY_INT_TYPE_OP(|)
DECLAR_BINARY_INT_TYPE_OP(^)
#undef DECLAR_BINARY_INT_TYPE_OP
#define DECLAR_BINARY_ASHIFT_OP(OP) \
Immediate Immediate::operator OP (const Immediate &right) const { \
GBE_ASSERT(this->getType() > TYPE_BOOL && this->getType() <= TYPE_U64); \
int32_t shift = right.getIntegerValue(); \
if (shift == 0) \
return *this; \
else \
switch (this->getType()) { \
default: \
GBE_ASSERT(0); \
case TYPE_S8: return Immediate((*this->data.s8 OP shift)); \
case TYPE_U8: return Immediate((*this->data.u8 OP shift)); \
case TYPE_S16: return Immediate((*this->data.s16 OP shift)); \
case TYPE_U16: return Immediate((*this->data.u16 OP shift)); \
case TYPE_S32: return Immediate((*this->data.s32 OP shift)); \
case TYPE_U32: return Immediate((*this->data.u32 OP shift)); \
case TYPE_S64: return Immediate((*this->data.s64 OP shift)); \
case TYPE_U64: return Immediate((*this->data.u64 OP shift)); \
} \
}
DECLAR_BINARY_ASHIFT_OP(>>)
DECLAR_BINARY_ASHIFT_OP(<<)
#undef DECLAR_BINARY_ASHIFT_OP
Immediate Immediate::lshr (const Immediate &left, const Immediate &right) {
GBE_ASSERT(left.getType() > TYPE_BOOL && left.getType() <= TYPE_U64);
int32_t shift = right.getIntegerValue();
if (shift == 0)
return left;
else
switch (left.getType()) {
default:
GBE_ASSERT(0);
case TYPE_S8:
case TYPE_U8: return Immediate((*left.data.u8 >> shift));
case TYPE_S16:
case TYPE_U16: return Immediate((*left.data.u16 >> shift));
case TYPE_S32:
case TYPE_U32: return Immediate((*left.data.u32 >> shift));
case TYPE_S64:
case TYPE_U64: return Immediate((*left.data.u64 >> shift));
}
}
Immediate Immediate::less (const Immediate &left, const Immediate &right) {
GBE_ASSERT(left.getType() > TYPE_BOOL && left.getType() <= TYPE_DOUBLE);
switch (left.getType()) {
default:
GBE_ASSERT(0);
case TYPE_S8: return Immediate(*left.data.s8 < *right.data.s8);
case TYPE_U8: return Immediate(*left.data.u8 < *right.data.u8);
case TYPE_S16: return Immediate(*left.data.s16 < *right.data.s16);
case TYPE_U16: return Immediate(*left.data.u16 < *right.data.u16);
case TYPE_S32: return Immediate(*left.data.s32 < *right.data.s32);
case TYPE_U32: return Immediate(*left.data.u32 < *right.data.u32);
case TYPE_S64: return Immediate(*left.data.s64 < *right.data.s64);
case TYPE_U64: return Immediate(*left.data.u64 < *right.data.u64);
case TYPE_FLOAT: return Immediate(*left.data.f32 < *right.data.f32);
case TYPE_DOUBLE: return Immediate(*left.data.f64 < *right.data.f64);
}
}
Immediate Immediate::extract (const Immediate &left, const Immediate &right, Type dstType) {
GBE_ASSERT(left.getType() > TYPE_BOOL && left.getType() <= TYPE_DOUBLE);
GBE_ASSERT(dstType == left.getType());
uint32_t index = right.getIntegerValue();
GBE_ASSERT(index >= 0 && index < left.getElemNum());
if (left.type != IMM_TYPE_COMP) {
switch (left.getType()) {
default:
GBE_ASSERT(0);
case TYPE_BOOL: return Immediate(left.data.b[index]);
case TYPE_S8: return Immediate(left.data.s8[index]);
case TYPE_U8: return Immediate(left.data.u8[index]);
case TYPE_S16: return Immediate(left.data.s16[index]);
case TYPE_U16: return Immediate(left.data.u16[index]);
case TYPE_S32: return Immediate(left.data.s32[index]);
case TYPE_U32: return Immediate(left.data.u32[index]);
case TYPE_S64: return Immediate(left.data.s64[index]);
case TYPE_U64: return Immediate(left.data.u64[index]);
case TYPE_FLOAT: return Immediate(left.data.f32[index]);
case TYPE_DOUBLE: return Immediate(left.data.f64[index]);
}
} else
return *left.data.immVec[index];
}
Immediate::Immediate(ImmOpCode op, const Immediate &left, const Immediate &right, Type dstType) {
switch (op) {
default:
GBE_ASSERT(0 && "unsupported imm op\n");
case IMM_ADD: *this = left + right; break;
case IMM_SUB: *this = left - right; break;
case IMM_MUL: *this = left * right; break;
case IMM_DIV: *this = left / right; break;
case IMM_AND: *this = left & right; break;
case IMM_OR: *this = left | right; break;
case IMM_XOR: *this = left ^ right; break;
case IMM_REM:
{
if (left.getType() > TYPE_BOOL && left.getType() <= TYPE_U64)
*this = left % right;
else if (left.getType() == TYPE_FLOAT && right.getType() == TYPE_FLOAT) {
*this = Immediate(left);
*this->data.f32 = fmodf(left.getFloatValue(), right.getFloatValue());
}
else if (left.getType() == TYPE_DOUBLE && right.getType() == TYPE_DOUBLE) {
*this = Immediate(left);
*this->data.f64 += fmod(left.getDoubleValue(), right.getDoubleValue());
}
else
GBE_ASSERT(0);
break;
}
case IMM_LSHR:
{
if (left.getElemNum() == 1)
*this = lshr(left, right);
else {
GBE_ASSERT(right.getIntegerValue() <= (left.getElemNum() * left.getTypeSize() * 8));
GBE_ASSERT(right.getIntegerValue() % (left.getTypeSize() * 8) == 0);
copy(left, right.getIntegerValue() / (left.getTypeSize() * 8), left.getElemNum());
}
break;
}
case IMM_ASHR:
{
if (left.getElemNum() == 1)
*this = left >> right;
else {
GBE_ASSERT(0 && "Doesn't support ashr on array constant.");
copy(left, right.getIntegerValue() / (left.getTypeSize() * 8), left.getElemNum());
}
break;
}
case IMM_SHL:
{
if (left.getElemNum() == 1)
*this = left << right;
else {
GBE_ASSERT(right.getIntegerValue() <= (left.getElemNum() * left.getTypeSize() * 8));
GBE_ASSERT(right.getIntegerValue() % (left.getTypeSize() * 8) == 0);
copy(left, -right.getIntegerValue() / (left.getTypeSize() * 8), left.getElemNum());
}
break;
}
case IMM_OEQ: *this = left == right; break;
case IMM_ONE: *this = left != right; break;
case IMM_OLE: *this = left <= right; break;
case IMM_OGE: *this = left >= right; break;
case IMM_OLT: *this = less(left, right); break;
case IMM_OGT: *this = left > right; break;
case IMM_ORD: *this = (left == left) && (right == right); break;
case IMM_EXTRACT: *this = extract(left, right, dstType); break;
}
// If the dst type is large int, we will not change the imm type to large int.
GBE_ASSERT(type == (ImmType)dstType || dstType == TYPE_LARGE_INT || dstType == TYPE_BOOL);
}
Immediate::Immediate(const vector immVec, Type dstType) {
if (immVec.size() == 1) {
*this = *immVec[0];
} else if (!(immVec[0]->isCompType()) && immVec[0]->elemNum == 1) {
this->type = (ImmType)dstType;
this->elemNum = immVec.size();
if (immVec[0]->getTypeSize() * immVec.size() < 8)
this->data.p = &this->defaultData;
else
this->data.p = malloc(immVec[0]->getTypeSize() * immVec.size());
uint8_t *p = (uint8_t*)this->data.p;
for(uint32_t i = 0; i < immVec.size(); i++) {
GBE_ASSERT(immVec[i]->type == immVec[0]->type && immVec[i]->elemNum == 1);
memcpy(p, immVec[i]->data.p, immVec[i]->getTypeSize());
p += immVec[i]->getTypeSize();
}
} else {
GBE_ASSERT(0);
this->type = IMM_TYPE_COMP;
if (immVec.size() * sizeof(Immediate*) < 8)
this->data.p = &this->defaultData;
else
this->data.p = malloc(immVec.size() * sizeof(Immediate*));
this->elemNum = immVec.size();
for(uint32_t i = 0; i < immVec.size(); i++)
this->data.immVec[i] = immVec[i];
}
}
// operator = and copy() are only called from constructor functions
// which this never hold a memory pointer, we don't need to bother
// to check the data.p before assignment.
Immediate & Immediate::operator= (const Immediate & other) {
if (this != &other) {
type = other.type;
elemNum = other.elemNum;
if (other.data.p != &other.defaultData) {
data.p = malloc(other.elemNum * other.getTypeSize());
memcpy(data.p, other.data.p, other.elemNum * other.getTypeSize());
}
else {
defaultData = other.defaultData;
data.p = &defaultData;
}
}
return *this;
}
void Immediate::copy(const Immediate &other, int32_t offset, uint32_t num) {
if (this != &other) {
if (other.type == IMM_TYPE_COMP && num == 1) {
GBE_ASSERT(offset >= 0 && offset <= (int32_t)other.elemNum);
*this = *other.data.immVec[offset];
return;
}
type = other.type;
elemNum = num;
if (num * other.getTypeSize() < 8)
data.p = &defaultData;
else
data.p = malloc(num * other.getTypeSize());
uint8_t* datap = (uint8_t*)data.p;
memset(datap, 0, num * other.getTypeSize());
if (offset < 0) {
datap += (-offset) * other.getTypeSize();
num -= num < (uint32_t)(-offset) ? num : (-offset);
offset = 0;
} else if (offset > 0 && num > 1) {
GBE_ASSERT((int32_t)num > offset);
num -= offset;
}
memcpy(datap, (uint8_t*)other.data.p + offset * other.getTypeSize(),
num * other.getTypeSize());
}
}