gem5/src/arch/hsail/operand.hh
Tony Gutierrez 38708f369b hsail: fix unsigned offset bug in address calculation
it's possible for the offset provided to an HSAIL mem inst to be a negative
value, however the variable we use to hold the offset is an unsigned type.
this can lead to excessively large offset values when the offset is negative,
which will almost certainly cause the access to go out of bounds.
2016-12-02 11:40:52 -05:00

797 lines
22 KiB
C++

/*
* Copyright (c) 2012-2015 Advanced Micro Devices, Inc.
* All rights reserved.
*
* For use for simulation and test purposes only
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Steve Reinhardt
*/
#ifndef __ARCH_HSAIL_OPERAND_HH__
#define __ARCH_HSAIL_OPERAND_HH__
/**
* @file operand.hh
*
* Defines classes encapsulating HSAIL instruction operands.
*/
#include <limits>
#include <string>
#include "arch/hsail/Brig.h"
#include "base/trace.hh"
#include "base/types.hh"
#include "debug/GPUReg.hh"
#include "enums/RegisterType.hh"
#include "gpu-compute/brig_object.hh"
#include "gpu-compute/compute_unit.hh"
#include "gpu-compute/hsail_code.hh"
#include "gpu-compute/shader.hh"
#include "gpu-compute/vector_register_file.hh"
#include "gpu-compute/wavefront.hh"
class Label;
class StorageElement;
class BaseOperand
{
public:
Enums::RegisterType registerType;
uint32_t regOperandSize;
BaseOperand() { registerType = Enums::RT_NONE; regOperandSize = 0; }
bool isVectorRegister() { return registerType == Enums::RT_VECTOR; }
bool isScalarRegister() { return registerType == Enums::RT_SCALAR; }
bool isCondRegister() { return registerType == Enums::RT_CONDITION; }
unsigned int regIndex() { return 0; }
uint32_t opSize() { return regOperandSize; }
virtual ~BaseOperand() { }
};
class BrigRegOperandInfo
{
public:
Brig::BrigKind16_t kind;
Brig::BrigType type;
Brig::BrigRegisterKind regKind;
BrigRegOperandInfo(Brig::BrigKind16_t _kind,
Brig::BrigRegisterKind _regKind)
: kind(_kind), regKind(_regKind)
{
}
BrigRegOperandInfo(Brig::BrigKind16_t _kind, Brig::BrigType _type)
: kind(_kind), type(_type)
{
}
BrigRegOperandInfo() : kind(Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES),
type(Brig::BRIG_TYPE_NONE)
{
}
};
BrigRegOperandInfo findRegDataType(unsigned opOffset, const BrigObject *obj);
class BaseRegOperand : public BaseOperand
{
public:
unsigned regIdx;
char regFileChar;
bool init(unsigned opOffset, const BrigObject *obj,
unsigned &maxRegIdx, char _regFileChar);
bool init_from_vect(unsigned opOffset, const BrigObject *obj, int at,
unsigned &maxRegIdx, char _regFileChar);
void initWithStrOffset(unsigned strOffset, const BrigObject *obj,
unsigned &maxRegIdx, char _regFileChar);
unsigned int regIndex() { return regIdx; }
};
class SRegOperand : public BaseRegOperand
{
public:
static unsigned maxRegIdx;
bool
init(unsigned opOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint32_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::init(opOffset, obj, maxRegIdx, 's');
}
bool
init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
{
regOperandSize = sizeof(uint32_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
's');
}
void
initWithStrOffset(unsigned strOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint32_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
's');
}
template<typename OperandType>
OperandType
get(Wavefront *w, int lane)
{
assert(sizeof(OperandType) <= sizeof(uint32_t));
assert(regIdx < w->maxSpVgprs);
// if OperandType is smaller than 32-bit, we truncate the value
OperandType ret;
uint32_t vgprIdx;
switch (sizeof(OperandType)) {
case 1: // 1 byte operand
vgprIdx = w->remap(regIdx, 1, 1);
ret = (w->computeUnit->vrf[w->simdId]->
read<uint32_t>(vgprIdx, lane)) & 0xff;
break;
case 2: // 2 byte operand
vgprIdx = w->remap(regIdx, 2, 1);
ret = (w->computeUnit->vrf[w->simdId]->
read<uint32_t>(vgprIdx, lane)) & 0xffff;
break;
case 4: // 4 byte operand
vgprIdx = w->remap(regIdx,sizeof(OperandType), 1);
ret = w->computeUnit->vrf[w->simdId]->
read<OperandType>(vgprIdx, lane);
break;
default:
panic("Bad OperandType\n");
break;
}
return (OperandType)ret;
}
// special get method for compatibility with LabelOperand
uint32_t
getTarget(Wavefront *w, int lane)
{
return get<uint32_t>(w, lane);
}
template<typename OperandType>
void set(Wavefront *w, int lane, OperandType &val);
std::string disassemble();
};
template<typename OperandType>
void
SRegOperand::set(Wavefront *w, int lane, OperandType &val)
{
DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $s%d <- %d\n",
w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx, val);
assert(sizeof(OperandType) == sizeof(uint32_t));
assert(regIdx < w->maxSpVgprs);
uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
w->computeUnit->vrf[w->simdId]->write<OperandType>(vgprIdx,val,lane);
}
template<>
inline void
SRegOperand::set(Wavefront *w, int lane, uint64_t &val)
{
DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $s%d <- %d\n",
w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx, val);
assert(regIdx < w->maxSpVgprs);
uint32_t vgprIdx = w->remap(regIdx, sizeof(uint32_t), 1);
w->computeUnit->vrf[w->simdId]->write<uint32_t>(vgprIdx, val, lane);
}
class DRegOperand : public BaseRegOperand
{
public:
static unsigned maxRegIdx;
bool
init(unsigned opOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint64_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::init(opOffset, obj, maxRegIdx, 'd');
}
bool
init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
{
regOperandSize = sizeof(uint64_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
'd');
}
void
initWithStrOffset(unsigned strOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint64_t);
registerType = Enums::RT_VECTOR;
return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
'd');
}
template<typename OperandType>
OperandType
get(Wavefront *w, int lane)
{
assert(sizeof(OperandType) <= sizeof(uint64_t));
// TODO: this check is valid only for HSAIL
assert(regIdx < w->maxDpVgprs);
uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
return w->computeUnit->vrf[w->simdId]->read<OperandType>(vgprIdx,lane);
}
template<typename OperandType>
void
set(Wavefront *w, int lane, OperandType &val)
{
DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $d%d <- %d\n",
w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx,
val);
assert(sizeof(OperandType) <= sizeof(uint64_t));
// TODO: this check is valid only for HSAIL
assert(regIdx < w->maxDpVgprs);
uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
w->computeUnit->vrf[w->simdId]->write<OperandType>(vgprIdx,val,lane);
}
std::string disassemble();
};
class CRegOperand : public BaseRegOperand
{
public:
static unsigned maxRegIdx;
bool
init(unsigned opOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint8_t);
registerType = Enums::RT_CONDITION;
return BaseRegOperand::init(opOffset, obj, maxRegIdx, 'c');
}
bool
init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
{
regOperandSize = sizeof(uint8_t);
registerType = Enums::RT_CONDITION;
return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
'c');
}
void
initWithStrOffset(unsigned strOffset, const BrigObject *obj)
{
regOperandSize = sizeof(uint8_t);
registerType = Enums::RT_CONDITION;
return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
'c');
}
template<typename OperandType>
OperandType
get(Wavefront *w, int lane)
{
assert(regIdx < w->condRegState->numRegs());
return w->condRegState->read<OperandType>((int)regIdx, lane);
}
template<typename OperandType>
void
set(Wavefront *w, int lane, OperandType &val)
{
DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $c%d <- %d\n",
w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx,
val);
assert(regIdx < w->condRegState->numRegs());
w->condRegState->write<OperandType>(regIdx,lane,val);
}
std::string disassemble();
};
template<typename T>
class ImmOperand : public BaseOperand
{
private:
uint16_t kind;
public:
T bits;
bool init(unsigned opOffset, const BrigObject *obj);
bool init_from_vect(unsigned opOffset, const BrigObject *obj, int at);
std::string disassemble();
template<typename OperandType>
OperandType
get(Wavefront *w)
{
assert(sizeof(OperandType) <= sizeof(T));
panic_if(w == nullptr, "WF pointer needs to be set");
switch (kind) {
// immediate operand is WF size
case Brig::BRIG_KIND_OPERAND_WAVESIZE:
return (OperandType)w->computeUnit->wfSize();
break;
default:
return *(OperandType*)&bits;
break;
}
}
// This version of get() takes a WF* and a lane id for
// compatibility with the register-based get() methods.
template<typename OperandType>
OperandType
get(Wavefront *w, int lane)
{
return get<OperandType>(w);
}
};
template<typename T>
bool
ImmOperand<T>::init(unsigned opOffset, const BrigObject *obj)
{
const Brig::BrigOperand *brigOp = obj->getOperand(opOffset);
switch (brigOp->kind) {
// this is immediate operand
case Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES:
{
DPRINTF(GPUReg, "sizeof(T): %lu, byteCount: %d\n", sizeof(T),
brigOp->byteCount);
auto cbptr = (Brig::BrigOperandConstantBytes*)brigOp;
bits = *((T*)(obj->getData(cbptr->bytes + 4)));
kind = brigOp->kind;
return true;
}
break;
case Brig::BRIG_KIND_OPERAND_WAVESIZE:
kind = brigOp->kind;
bits = std::numeric_limits<unsigned long long>::digits;
return true;
default:
kind = Brig::BRIG_KIND_NONE;
return false;
}
}
template <typename T>
bool
ImmOperand<T>::init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
{
const Brig::BrigOperand *brigOp = obj->getOperand(opOffset);
if (brigOp->kind != Brig::BRIG_KIND_OPERAND_OPERAND_LIST) {
kind = Brig::BRIG_KIND_NONE;
return false;
}
const Brig::BrigOperandOperandList *brigVecOp =
(const Brig::BrigOperandOperandList *)brigOp;
unsigned *data_offset =
(unsigned *)obj->getData(brigVecOp->elements + 4 * (at + 1));
const Brig::BrigOperand *p =
(const Brig::BrigOperand *)obj->getOperand(*data_offset);
if (p->kind != Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES) {
kind = Brig::BRIG_KIND_NONE;
return false;
}
return init(*data_offset, obj);
}
template<typename T>
std::string
ImmOperand<T>::disassemble()
{
return csprintf("0x%08x", bits);
}
template<typename RegOperand, typename T>
class RegOrImmOperand : public BaseOperand
{
private:
bool is_imm;
public:
void setImm(const bool value) { is_imm = value; }
ImmOperand<T> imm_op;
RegOperand reg_op;
RegOrImmOperand() { is_imm = false; }
void init(unsigned opOffset, const BrigObject *obj);
void init_from_vect(unsigned opOffset, const BrigObject *obj, int at);
std::string disassemble();
template<typename OperandType>
OperandType
get(Wavefront *w, int lane)
{
return is_imm ? imm_op.template get<OperandType>(w) :
reg_op.template get<OperandType>(w, lane);
}
uint32_t
opSize()
{
if (!is_imm) {
return reg_op.opSize();
}
return 0;
}
bool
isVectorRegister()
{
if (!is_imm) {
return reg_op.registerType == Enums::RT_VECTOR;
}
return false;
}
bool
isCondRegister()
{
if (!is_imm) {
return reg_op.registerType == Enums::RT_CONDITION;
}
return false;
}
bool
isScalarRegister()
{
if (!is_imm) {
return reg_op.registerType == Enums::RT_SCALAR;
}
return false;
}
unsigned int
regIndex()
{
if (!is_imm) {
return reg_op.regIndex();
}
return 0;
}
};
template<typename RegOperand, typename T>
void
RegOrImmOperand<RegOperand, T>::init(unsigned opOffset, const BrigObject *obj)
{
is_imm = false;
if (reg_op.init(opOffset, obj)) {
return;
}
if (imm_op.init(opOffset, obj)) {
is_imm = true;
return;
}
fatal("RegOrImmOperand::init(): bad operand kind %d\n",
obj->getOperand(opOffset)->kind);
}
template<typename RegOperand, typename T>
void
RegOrImmOperand<RegOperand, T>::init_from_vect(unsigned opOffset,
const BrigObject *obj, int at)
{
if (reg_op.init_from_vect(opOffset, obj, at)) {
is_imm = false;
return;
}
if (imm_op.init_from_vect(opOffset, obj, at)) {
is_imm = true;
return;
}
fatal("RegOrImmOperand::init(): bad operand kind %d\n",
obj->getOperand(opOffset)->kind);
}
template<typename RegOperand, typename T>
std::string
RegOrImmOperand<RegOperand, T>::disassemble()
{
return is_imm ? imm_op.disassemble() : reg_op.disassemble();
}
typedef RegOrImmOperand<SRegOperand, uint32_t> SRegOrImmOperand;
typedef RegOrImmOperand<DRegOperand, uint64_t> DRegOrImmOperand;
typedef RegOrImmOperand<CRegOperand, bool> CRegOrImmOperand;
class AddrOperandBase : public BaseOperand
{
protected:
// helper function for init()
void parseAddr(const Brig::BrigOperandAddress *op, const BrigObject *obj);
// helper function for disassemble()
std::string disassemble(std::string reg_disassembly);
uint64_t calcUniformBase();
public:
virtual void calcVector(Wavefront *w, std::vector<Addr> &addrVec) = 0;
virtual uint64_t calcLane(Wavefront *w, int lane=0) = 0;
int64_t offset;
const char *name = nullptr;
StorageElement *storageElement;
};
template<typename RegOperandType>
class RegAddrOperand : public AddrOperandBase
{
public:
RegOperandType reg;
void init(unsigned opOffset, const BrigObject *obj);
uint64_t calcUniform();
void calcVector(Wavefront *w, std::vector<Addr> &addrVec);
uint64_t calcLane(Wavefront *w, int lane=0);
uint32_t opSize() { return reg.opSize(); }
bool isVectorRegister() { return reg.registerType == Enums::RT_VECTOR; }
bool isCondRegister() { return reg.registerType == Enums::RT_CONDITION; }
bool isScalarRegister() { return reg.registerType == Enums::RT_SCALAR; }
unsigned int regIndex() { return reg.regIndex(); }
std::string disassemble();
};
template<typename RegOperandType>
void
RegAddrOperand<RegOperandType>::init(unsigned opOffset, const BrigObject *obj)
{
using namespace Brig;
const BrigOperand *baseOp = obj->getOperand(opOffset);
switch (baseOp->kind) {
case BRIG_KIND_OPERAND_ADDRESS:
{
const BrigOperandAddress *op = (BrigOperandAddress*)baseOp;
storageElement = nullptr;
reg.init(op->reg, obj);
if (reg.regFileChar == 's') {
// if the address expression is 32b, then the hi
// bits of the offset must be set to 0 in the BRIG
assert(!op->offset.hi);
/**
* the offset field of an HSAIL instruction may be negative
* so here we cast the raw bits we get from the BRIG file to
* a signed type to avoid address calculation errors
*/
offset = (int32_t)(op->offset.lo);
reg.regOperandSize = sizeof(uint32_t);
registerType = Enums::RT_VECTOR;
}
else if (reg.regFileChar == 'd') {
offset = (int64_t)(((uint64_t)(op->offset.hi) << 32)
| (uint64_t)(op->offset.lo));
reg.regOperandSize = sizeof(uint64_t);
registerType = Enums::RT_VECTOR;
}
}
break;
default:
fatal("RegAddrOperand: bad operand kind %d\n", baseOp->kind);
break;
}
}
template<typename RegOperandType>
uint64_t
RegAddrOperand<RegOperandType>::calcUniform()
{
fatal("can't do calcUniform() on register-based address\n");
return 0;
}
template<typename RegOperandType>
void
RegAddrOperand<RegOperandType>::calcVector(Wavefront *w,
std::vector<Addr> &addrVec)
{
Addr address = calcUniformBase();
for (int lane = 0; lane < w->computeUnit->wfSize(); ++lane) {
if (w->execMask(lane)) {
if (reg.regFileChar == 's') {
addrVec[lane] = address + reg.template get<uint32_t>(w, lane);
} else {
addrVec[lane] = address + reg.template get<Addr>(w, lane);
}
}
}
}
template<typename RegOperandType>
uint64_t
RegAddrOperand<RegOperandType>::calcLane(Wavefront *w, int lane)
{
Addr address = calcUniformBase();
return address + reg.template get<Addr>(w, lane);
}
template<typename RegOperandType>
std::string
RegAddrOperand<RegOperandType>::disassemble()
{
return AddrOperandBase::disassemble(reg.disassemble());
}
typedef RegAddrOperand<SRegOperand> SRegAddrOperand;
typedef RegAddrOperand<DRegOperand> DRegAddrOperand;
class NoRegAddrOperand : public AddrOperandBase
{
public:
void init(unsigned opOffset, const BrigObject *obj);
uint64_t calcUniform();
void calcVector(Wavefront *w, std::vector<Addr> &addrVec);
uint64_t calcLane(Wavefront *w, int lane=0);
std::string disassemble();
};
inline uint64_t
NoRegAddrOperand::calcUniform()
{
return AddrOperandBase::calcUniformBase();
}
inline uint64_t
NoRegAddrOperand::calcLane(Wavefront *w, int lane)
{
return calcUniform();
}
inline void
NoRegAddrOperand::calcVector(Wavefront *w, std::vector<Addr> &addrVec)
{
uint64_t address = calcUniformBase();
for (int lane = 0; lane < w->computeUnit->wfSize(); ++lane)
addrVec[lane] = address;
}
class LabelOperand : public BaseOperand
{
public:
Label *label;
void init(unsigned opOffset, const BrigObject *obj);
std::string disassemble();
// special get method for compatibility with SRegOperand
uint32_t getTarget(Wavefront *w, int lane);
};
class ListOperand : public BaseOperand
{
public:
int elementCount;
std::vector<StorageElement*> callArgs;
int
getSrcOperand(int idx)
{
DPRINTF(GPUReg, "getSrcOperand, idx: %d, sz_args: %d\n", idx,
callArgs.size());
return callArgs.at(idx)->offset;
}
void init(unsigned opOffset, const BrigObject *obj);
std::string disassemble();
template<typename OperandType>
OperandType
get(Wavefront *w, int lane, int arg_idx)
{
return w->readCallArgMem<OperandType>(lane, getSrcOperand(arg_idx));
}
template<typename OperandType>
void
set(Wavefront *w, int lane, OperandType val)
{
w->writeCallArgMem<OperandType>(lane, getSrcOperand(0), val);
DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: arg[%d] <- %d\n",
w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane,
getSrcOperand(0), val);
}
};
class FunctionRefOperand : public BaseOperand
{
public:
const char *func_name;
void init(unsigned opOffset, const BrigObject *obj);
std::string disassemble();
};
#endif // __ARCH_HSAIL_OPERAND_HH__