/* * 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 #include #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 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(vgprIdx, lane)) & 0xff; break; case 2: // 2 byte operand vgprIdx = w->remap(regIdx, 2, 1); ret = (w->computeUnit->vrf[w->simdId]-> read(vgprIdx, lane)) & 0xffff; break; case 4: // 4 byte operand vgprIdx = w->remap(regIdx,sizeof(OperandType), 1); ret = w->computeUnit->vrf[w->simdId]-> read(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(w, lane); } template void set(Wavefront *w, int lane, OperandType &val); std::string disassemble(); }; template 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(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(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 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(vgprIdx,lane); } template 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(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 OperandType get(Wavefront *w, int lane) { assert(regIdx < w->condRegState->numRegs()); return w->condRegState->read((int)regIdx, lane); } template 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(regIdx,lane,val); } std::string disassemble(); }; template 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 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 OperandType get(Wavefront *w, int lane) { return get(w); } }; template bool ImmOperand::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::digits; return true; default: kind = Brig::BRIG_KIND_NONE; return false; } } template bool ImmOperand::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 std::string ImmOperand::disassemble() { return csprintf("0x%08x", bits); } template class RegOrImmOperand : public BaseOperand { private: bool is_imm; public: void setImm(const bool value) { is_imm = value; } ImmOperand 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 OperandType get(Wavefront *w, int lane) { return is_imm ? imm_op.template get(w) : reg_op.template get(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 void RegOrImmOperand::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 void RegOrImmOperand::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 std::string RegOrImmOperand::disassemble() { return is_imm ? imm_op.disassemble() : reg_op.disassemble(); } typedef RegOrImmOperand SRegOrImmOperand; typedef RegOrImmOperand DRegOrImmOperand; typedef RegOrImmOperand 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 &addrVec) = 0; virtual uint64_t calcLane(Wavefront *w, int lane=0) = 0; int64_t offset; const char *name = nullptr; StorageElement *storageElement; }; template class RegAddrOperand : public AddrOperandBase { public: RegOperandType reg; void init(unsigned opOffset, const BrigObject *obj); uint64_t calcUniform(); void calcVector(Wavefront *w, std::vector &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 void RegAddrOperand::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 uint64_t RegAddrOperand::calcUniform() { fatal("can't do calcUniform() on register-based address\n"); return 0; } template void RegAddrOperand::calcVector(Wavefront *w, std::vector &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(w, lane); } else { addrVec[lane] = address + reg.template get(w, lane); } } } } template uint64_t RegAddrOperand::calcLane(Wavefront *w, int lane) { Addr address = calcUniformBase(); return address + reg.template get(w, lane); } template std::string RegAddrOperand::disassemble() { return AddrOperandBase::disassemble(reg.disassemble()); } typedef RegAddrOperand SRegAddrOperand; typedef RegAddrOperand DRegAddrOperand; class NoRegAddrOperand : public AddrOperandBase { public: void init(unsigned opOffset, const BrigObject *obj); uint64_t calcUniform(); void calcVector(Wavefront *w, std::vector &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 &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 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 OperandType get(Wavefront *w, int lane, int arg_idx) { return w->readCallArgMem(lane, getSrcOperand(arg_idx)); } template void set(Wavefront *w, int lane, OperandType val) { w->writeCallArgMem(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__