blob: 997b22f2276169ba1c59589da9678c7248f9fbb7 [file] [log] [blame]
/*
* Copyright (c) 2016-2017 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.
*
* Authors: Anthony Gutierrez
*/
#include "arch/gcn3/insts/op_encodings.hh"
#include <iomanip>
namespace Gcn3ISA
{
// --- Inst_SOP2 base class methods ---
Inst_SOP2::Inst_SOP2(InFmt_SOP2 *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
} else {
varSize = 4;
} // if
} // Inst_SOP2
int
Inst_SOP2::instSize() const
{
return varSize;
} // instSize
bool
Inst_SOP2::hasSecondDword(InFmt_SOP2 *iFmt)
{
if (iFmt->SSRC0 == REG_SRC_LITERAL)
return true;
if (iFmt->SSRC1 == REG_SRC_LITERAL)
return true;
return false;
}
void
Inst_SOP2::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << opSelectorToRegSym(instData.SDST) << ", ";
if (instData.SSRC0 == REG_SRC_LITERAL) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< _srcLiteral << ", ";
} else {
dis_stream << opSelectorToRegSym(instData.SSRC0) << ", ";
}
if (instData.SSRC1 == REG_SRC_LITERAL) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< _srcLiteral;
} else {
dis_stream << opSelectorToRegSym(instData.SSRC1);
}
disassembly = dis_stream.str();
}
bool
Inst_SOP2::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return isScalarReg(instData.SSRC0);
case 1:
return isScalarReg(instData.SSRC1);
case 2:
return isScalarReg(instData.SDST);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_SOP2::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOP2 instructions cannot access VGPRs
return false;
}
int
Inst_SOP2::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return opSelectorToRegIdx(instData.SSRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return opSelectorToRegIdx(instData.SSRC1,
gpuDynInst->wavefront()->reservedScalarRegs);
case 2:
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_SOPK base class methods ---
Inst_SOPK::Inst_SOPK(InFmt_SOPK *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
} else {
varSize = 4;
} // if
} // Inst_SOPK
Inst_SOPK::~Inst_SOPK()
{
} // ~Inst_SOPK
int
Inst_SOPK::instSize() const
{
return varSize;
} // instSize
bool
Inst_SOPK::hasSecondDword(InFmt_SOPK *iFmt)
{
/*
SOPK can be a 64-bit instruction, i.e., have a second dword:
S_SETREG_IMM32_B32 writes some or all of the LSBs of a 32-bit
literal constant into a hardware register;
the way to detect such special case is to explicitly check the
opcode (20/0x14)
*/
if (iFmt->OP == 0x14)
return true;
return false;
}
void
Inst_SOPK::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
// S_SETREG_IMM32_B32 is a 64-bit instruction, using a
// 32-bit literal constant
if (instData.OP == 0x14) {
dis_stream << "0x" << std::hex << std::setfill('0')
<< std::setw(8) << extData.imm_u32 << ", ";
} else {
dis_stream << opSelectorToRegSym(instData.SDST) << ", ";
}
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(4)
<< instData.SIMM16;
disassembly = dis_stream.str();
}
bool
Inst_SOPK::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return false;
case 1:
return isScalarReg(instData.SDST);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_SOPK::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOPK instruction cannot access VGPRs
return false;
}
int
Inst_SOPK::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return -1;
case 1:
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_SOP1 base class methods ---
Inst_SOP1::Inst_SOP1(InFmt_SOP1 *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
} else {
varSize = 4;
} // if
} // Inst_SOP1
Inst_SOP1::~Inst_SOP1()
{
} // ~Inst_SOP1
int
Inst_SOP1::instSize() const
{
return varSize;
} // instSize
bool
Inst_SOP1::hasSecondDword(InFmt_SOP1 *iFmt)
{
if (iFmt->SSRC0 == REG_SRC_LITERAL)
return true;
return false;
}
void
Inst_SOP1::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << opSelectorToRegSym(instData.SDST) << ", ";
if (instData.SSRC0 == REG_SRC_LITERAL) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< extData.imm_u32;
} else {
dis_stream << opSelectorToRegSym(instData.SSRC0);
}
disassembly = dis_stream.str();
}
bool
Inst_SOP1::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
if (instData.OP == 0x1C) {
// Special case for s_getpc, which has no source reg.
// Instead, it implicitly reads the PC.
return isScalarReg(instData.SDST);
}
return isScalarReg(instData.SSRC0);
case 1:
return isScalarReg(instData.SDST);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_SOP1::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOP1 instruction cannot access VGPRs
return false;
}
int
Inst_SOP1::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
if (instData.OP == 0x1C) {
// Special case for s_getpc, which has no source reg.
// Instead, it implicitly reads the PC.
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
return opSelectorToRegIdx(instData.SSRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_SOPC base class methods ---
Inst_SOPC::Inst_SOPC(InFmt_SOPC *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
} else {
varSize = 4;
} // if
} // Inst_SOPC
Inst_SOPC::~Inst_SOPC()
{
} // ~Inst_SOPC
int
Inst_SOPC::instSize() const
{
return varSize;
} // instSize
bool
Inst_SOPC::hasSecondDword(InFmt_SOPC *iFmt)
{
if (iFmt->SSRC0 == REG_SRC_LITERAL)
return true;
if (iFmt->SSRC1 == REG_SRC_LITERAL)
return true;
return false;
}
void
Inst_SOPC::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
if (instData.SSRC0 == REG_SRC_LITERAL) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< extData.imm_u32;
} else {
dis_stream << opSelectorToRegSym(instData.SSRC0) << ", ";
}
if (instData.SSRC1 == REG_SRC_LITERAL) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< extData.imm_u32;
} else {
dis_stream << opSelectorToRegSym(instData.SSRC1);
}
disassembly = dis_stream.str();
}
bool
Inst_SOPC::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SSRC0 is always a scalar reg or a constant
return isScalarReg(instData.SSRC0);
case 1:
// SSRC1 is always a scalar reg or a constant
return isScalarReg(instData.SSRC1);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_SOPC::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOPC instructions cannot access VGPRs
return false;
}
int
Inst_SOPC::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return opSelectorToRegIdx(instData.SSRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return opSelectorToRegIdx(instData.SSRC1,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_SOPP base class methods ---
Inst_SOPP::Inst_SOPP(InFmt_SOPP *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
// copy first instruction DWORD
instData = iFmt[0];
} // Inst_SOPP
Inst_SOPP::~Inst_SOPP()
{
} // ~Inst_SOPP
int
Inst_SOPP::instSize() const
{
return 4;
} // instSize
void
Inst_SOPP::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode;
switch (instData.OP) {
case 8:
{
dis_stream << " ";
int dest = 4 * instData.SIMM16 + 4;
dis_stream << "label_" << std::hex << dest;
}
break;
case 12:
{
dis_stream << " ";
int vm_cnt = 0;
int exp_cnt = 0;
int lgkm_cnt = 0;
vm_cnt = bits<uint16_t>(instData.SIMM16, 3, 0);
exp_cnt = bits<uint16_t>(instData.SIMM16, 6, 4);
lgkm_cnt = bits<uint16_t>(instData.SIMM16, 11, 8);
// if the counts are not maxed out, then we
// print out the count value
if (vm_cnt != 0xf) {
dis_stream << "vmcnt(" << vm_cnt << ")";
}
if (lgkm_cnt != 0xf) {
if (vm_cnt != 0xf)
dis_stream << " & ";
dis_stream << "lgkmcnt(" << lgkm_cnt << ")";
}
if (exp_cnt != 0x7) {
if (vm_cnt != 0xf || lgkm_cnt != 0xf)
dis_stream << " & ";
dis_stream << "expcnt(" << exp_cnt << ")";
}
}
break;
default:
break;
}
disassembly = dis_stream.str();
}
bool
Inst_SOPP::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOPP instructions have a maximum of 1 operand,
// and it's always an immediate value
return false;
}
bool
Inst_SOPP::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOPP instructions have a maximum of 1 operand,
// and it's always an immediate value
return false;
}
int
Inst_SOPP::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SOPP instructions have a maximum of 1 operand,
// and it's always an immediate value
return -1;
}
// --- Inst_SMEM base class methods ---
Inst_SMEM::Inst_SMEM(InFmt_SMEM *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Scalar);
setFlag(GlobalSegment);
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_SMEM_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
if (instData.GLC)
setFlag(GloballyCoherent);
} // Inst_SMEM
Inst_SMEM::~Inst_SMEM()
{
} // ~Inst_SMEM
int
Inst_SMEM::instSize() const
{
return 8;
} // instSize
void
Inst_SMEM::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
if (numDstRegOperands()) {
if (getOperandSize(getNumOperands() - 1) > 4) {
dis_stream << "s[" << instData.SDATA << ":"
<< instData.SDATA + getOperandSize(getNumOperands() - 1) /
4 - 1 << "], ";
} else {
dis_stream << "s" << instData.SDATA << ", ";
}
}
// SBASE has an implied LSB of 0, so we need
// to shift by one to get the actual value
dis_stream << "s[" << (instData.SBASE << 1) << ":"
<< ((instData.SBASE << 1) + 1) << "], ";
if (instData.IMM) {
// IMM == 1 implies OFFSET should be
// used as the offset
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(2)
<< extData.OFFSET;
} else {
// IMM == 0 implies OFFSET should be
// used to specify SGRP in which the
// offset is held
dis_stream << "s" << extData.OFFSET;
}
disassembly = dis_stream.str();
}
bool
Inst_SMEM::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SBASE is always a scalar
return true;
case 1:
if (instData.IMM) {
return false;
} else {
return isScalarReg(extData.OFFSET);
}
case 2:
return isScalarReg(instData.SDATA);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_SMEM::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// SMEM instructions cannot access VGPRs
return false;
}
int
Inst_SMEM::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SBASE has an implied LSB of 0, so we need
// to shift by one to get the actual value
return opSelectorToRegIdx(instData.SBASE << 1,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
if (instData.IMM) {
// operand is an immediate value, not a register
return -1;
} else {
return extData.OFFSET;
}
case 2:
return opSelectorToRegIdx(instData.SDATA,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_VOP2 base class methods ---
Inst_VOP2::Inst_VOP2(InFmt_VOP2 *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
if (iFmt->SRC0 == REG_SRC_DPP) {
setFlag(IsDPP);
} else if (iFmt->SRC0 == REG_SRC_SWDA) {
setFlag(IsSDWA);
}
} else {
varSize = 4;
} // if
} // Inst_VOP2
Inst_VOP2::~Inst_VOP2()
{
} // ~Inst_VOP2
int
Inst_VOP2::instSize() const
{
return varSize;
} // instSize
bool
Inst_VOP2::hasSecondDword(InFmt_VOP2 *iFmt)
{
/*
There are a few cases where VOP2 instructions have a second dword:
1. SRC0 is a literal
2. SRC0 is being used to add a data parallel primitive (DPP)
operation to the instruction.
3. SRC0 is being used for sub d-word addressing (SDWA) of the
operands in the instruction.
4. VOP2 instructions also have four special opcodes:',
V_MADMK_{F16, F32} (0x24, 0x17), and V_MADAK_{F16, F32}',
(0x25, 0x18), that are always 64b. the only way to',
detect these special cases is to explicitly check,',
the opcodes',
*/
if (iFmt->SRC0 == REG_SRC_LITERAL || (iFmt->SRC0 == REG_SRC_DPP) ||
(iFmt->SRC0 == REG_SRC_SWDA) || iFmt->OP == 0x17 ||
iFmt->OP == 0x18 || iFmt->OP == 0x24 || iFmt->OP == 0x25)
return true;
return false;
}
void
Inst_VOP2::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << "v" << instData.VDST << ", ";
if (writesVCC())
dis_stream << "vcc, ";
if ((instData.SRC0 == REG_SRC_LITERAL) ||
(instData.SRC0 == REG_SRC_DPP) ||
(instData.SRC0 == REG_SRC_SWDA)) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< _srcLiteral << ", ";
} else {
dis_stream << opSelectorToRegSym(instData.SRC0) << ", ";
}
// VOP2 instructions have four special opcodes:',
// V_MADMK_{F16, F32} (0x24, 0x17), and V_MADAK_{F16, F32}',
// (0x25, 0x18), that are always 64b. the only way to',
// detect these special cases is to explicitly check,',
// the opcodes',
if (instData.OP == 0x17 || instData.OP == 0x18 || instData.OP == 0x24
|| instData.OP == 0x25) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< extData.imm_u32 << ", ";
}
dis_stream << std::resetiosflags(std::ios_base::basefield) << "v"
<< instData.VSRC1;
if (readsVCC())
dis_stream << ", vcc";
disassembly = dis_stream.str();
}
bool
Inst_VOP2::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isScalarReg(instData.SRC0);
case 1:
// instData.VSRC1 is never a scalar reg
return false;
case 2:
if (readsVCC()) {
return true;
} else {
// instData.VDST is never a scalar reg
return false;
}
case 3:
// if a VOP2 instruction has more than 3 ops
// it must read from or write to VCC, and
// VCC is always in an SGPR
assert(readsVCC() || writesVCC());
if (readsVCC()) {
return false;
} else {
return true;
}
case 4:
// if a VOP2 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is always in an SGPR
assert(writesVCC() && readsVCC());
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_VOP2::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isVectorReg(instData.SRC0);
case 1:
// instData.VSRC1 is always a vector reg
return true;
case 2:
if (readsVCC()) {
return false;
} else {
// instData.VDST is always a vector reg
return true;
}
case 3:
// if a VOP2 instruction has more than 3 ops
// it must read from or write to VCC, and
// VCC is always in an SGPR
assert(writesVCC() || readsVCC());
if (readsVCC()) {
return true;
} else {
return false;
}
case 4:
// if a VOP2 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is always in an SGPR
assert(writesVCC() && readsVCC());
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_VOP2::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return opSelectorToRegIdx(instData.SRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return instData.VSRC1;
case 2:
if (readsVCC()) {
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
} else {
return instData.VDST;
}
case 3:
assert(writesVCC() || readsVCC());
if (readsVCC()) {
return instData.VDST;
} else {
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
}
case 4:
assert(writesVCC() && readsVCC());
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_VOP1 base class methods ---
Inst_VOP1::Inst_VOP1(InFmt_VOP1 *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
if (iFmt->SRC0 == REG_SRC_DPP) {
setFlag(IsDPP);
} else if (iFmt->SRC0 == REG_SRC_SWDA) {
setFlag(IsSDWA);
}
} else {
varSize = 4;
} // if
} // Inst_VOP1
Inst_VOP1::~Inst_VOP1()
{
} // ~Inst_VOP1
int
Inst_VOP1::instSize() const
{
return varSize;
} // instSize
bool
Inst_VOP1::hasSecondDword(InFmt_VOP1 *iFmt)
{
/*
There are several cases where VOP1 instructions have a second dword:
1. SRC0 is a literal.
2. SRC0 is being used to add a data parallel primitive (DPP)
operation to the instruction.
3. SRC0 is being used for sub d-word addressing (SDWA) of the
operands in the instruction.
*/
if ((iFmt->SRC0 == REG_SRC_LITERAL) || (iFmt->SRC0 == REG_SRC_DPP) ||
(iFmt->SRC0 == REG_SRC_SWDA))
return true;
return false;
}
void
Inst_VOP1::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << "v" << instData.VDST << ", ";
if ((instData.SRC0 == REG_SRC_LITERAL) ||
(instData.SRC0 == REG_SRC_DPP) ||
(instData.SRC0 == REG_SRC_SWDA)) {
dis_stream << "0x" << std::hex << std::setfill('0') << std::setw(8)
<< _srcLiteral;
} else {
dis_stream << opSelectorToRegSym(instData.SRC0);
}
disassembly = dis_stream.str();
}
bool
Inst_VOP1::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return isScalarReg(instData.SRC0);
case 1:
// VDST is never a scalar reg
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_VOP1::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return isVectorReg(instData.SRC0);
case 1:
// VDST is always a vector reg
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_VOP1::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return opSelectorToRegIdx(instData.SRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return instData.VDST;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_VOPC base class methods ---
Inst_VOPC::Inst_VOPC(InFmt_VOPC *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
if (hasSecondDword(iFmt)) {
// copy second instruction DWORD into union
extData = ((MachInst)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
varSize = 4 + 4;
if (iFmt->SRC0 == REG_SRC_DPP) {
setFlag(IsDPP);
} else if (iFmt->SRC0 == REG_SRC_SWDA) {
setFlag(IsSDWA);
}
} else {
varSize = 4;
} // if
} // Inst_VOPC
Inst_VOPC::~Inst_VOPC()
{
} // ~Inst_VOPC
int
Inst_VOPC::instSize() const
{
return varSize;
} // instSize
bool
Inst_VOPC::hasSecondDword(InFmt_VOPC *iFmt)
{
/*
There are several cases where VOPC instructions have a second dword:
1. SRC0 is a literal.
2. SRC0 is being used to add a data parallel primitive (DPP)
operation to the instruction.
3. SRC0 is being used for sub d-word addressing (SDWA) of the
operands in the instruction.
*/
if ((iFmt->SRC0 == REG_SRC_LITERAL) || (iFmt->SRC0 == REG_SRC_DPP) ||
(iFmt->SRC0 == REG_SRC_SWDA))
return true;
return false;
}
void
Inst_VOPC::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " vcc, ";
dis_stream << opSelectorToRegSym(instData.SRC0) << ", ";
dis_stream << "v" << instData.VSRC1;
disassembly = dis_stream.str();
}
bool
Inst_VOPC::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return isScalarReg(instData.SRC0);
case 1:
// VSRC1 is never a scalar register
return false;
case 2:
// VCC is always a scalar register
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_VOPC::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return isVectorReg(instData.SRC0);
case 1:
// VSRC1 is never a scalar register
return true;
case 2:
// VCC is always a scalar register
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_VOPC::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return opSelectorToRegIdx(instData.SRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
return instData.VSRC1;
case 2:
// VCC
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_VINTRP base class methods ---
Inst_VINTRP::Inst_VINTRP(InFmt_VINTRP *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
} // Inst_VINTRP
Inst_VINTRP::~Inst_VINTRP()
{
} // ~Inst_VINTRP
int
Inst_VINTRP::instSize() const
{
return 4;
} // instSize
// --- Inst_VOP3 base class methods ---
Inst_VOP3::Inst_VOP3(InFmt_VOP3 *iFmt, const std::string &opcode,
bool sgpr_dst)
: GCN3GPUStaticInst(opcode), sgprDst(sgpr_dst)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_VOP3_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
} // Inst_VOP3
Inst_VOP3::~Inst_VOP3()
{
} // ~Inst_VOP3
int
Inst_VOP3::instSize() const
{
return 8;
} // instSize
void
Inst_VOP3::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
int num_regs = 0;
if (getOperandSize(getNumOperands() - 1) > 4) {
num_regs = getOperandSize(getNumOperands() - 1) / 4;
if (sgprDst)
dis_stream << "s[";
else
dis_stream << "v[";
dis_stream << instData.VDST << ":" << instData.VDST +
num_regs - 1 << "], ";
} else {
if (sgprDst)
dis_stream << "s";
else
dis_stream << "v";
dis_stream << instData.VDST << ", ";
}
num_regs = getOperandSize(0) / 4;
if (extData.NEG & 0x1) {
dis_stream << "-" << opSelectorToRegSym(extData.SRC0, num_regs);
} else {
dis_stream << opSelectorToRegSym(extData.SRC0, num_regs);
}
if (numSrcRegOperands() > 1) {
num_regs = getOperandSize(1) / 4;
if (extData.NEG & 0x2) {
dis_stream << ", -"
<< opSelectorToRegSym(extData.SRC1, num_regs);
} else {
dis_stream << ", "
<< opSelectorToRegSym(extData.SRC1, num_regs);
}
}
if (numSrcRegOperands() > 2) {
num_regs = getOperandSize(2) / 4;
if (extData.NEG & 0x4) {
dis_stream << ", -"
<< opSelectorToRegSym(extData.SRC2, num_regs);
} else {
dis_stream << ", "
<< opSelectorToRegSym(extData.SRC2, num_regs);
}
}
disassembly = dis_stream.str();
}
bool
Inst_VOP3::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isScalarReg(extData.SRC0);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1. SRC1 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isScalarReg(extData.SRC1);
} else {
// if we only have 1 source operand, opIdx 1
// will be VDST, and VDST is only a scalar
// for v_cmp instructions
if (sgprDst)
return true;
return false;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2. SRC2 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isScalarReg(extData.SRC2);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST, and VDST is only a scalar
// for v_cmp instructions
if (sgprDst)
return true;
return false;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write,
// and VCC is always stored in an SGPR pair
assert(writesVCC() || readsVCC());
return true;
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands, opIdx 3
// will be VDST, and VDST is only a scalar
// for v_cmp instructions
if (sgprDst)
return true;
return false;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write, and
// and VCC is always stored in an SGPR pair
assert(writesVCC() || readsVCC());
return true;
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is always in an SGPR
assert(writesVCC() || readsVCC());
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_VOP3::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isVectorReg(extData.SRC0);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1. SRC1 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isVectorReg(extData.SRC1);
} else {
// if we only have 1 source operands, opIdx 1
// will be VDST, and VDST is a scalar for v_cmp
// instructions
if (sgprDst)
return false;
return true;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2. SRC2 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isVectorReg(extData.SRC2);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST, and VDST is a scalar for v_cmp
// instructions
if (sgprDst)
return false;
return true;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write,
// and VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands, opIdx 3
// will be VDST, and VDST is a scalar for v_cmp
// instructions
if (sgprDst)
return false;
return true;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write, and
// and VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_VOP3::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0
return opSelectorToRegIdx(extData.SRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1
return opSelectorToRegIdx(extData.SRC1,
gpuDynInst->wavefront()->reservedScalarRegs);
} else {
// if we only have 1 source operand, opIdx 1
// will be VDST
if (sgprDst) {
return opSelectorToRegIdx(instData.VDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
return instData.VDST;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2. SRC2 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return opSelectorToRegIdx(extData.SRC2,
gpuDynInst->wavefront()->reservedScalarRegs);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST, and VDST is always a vector
// reg
if (sgprDst) {
return opSelectorToRegIdx(instData.VDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
return instData.VDST;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write,
// and VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands then op
// idx 3 will correspond to VDST
if (sgprDst) {
return opSelectorToRegIdx(instData.VDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
return instData.VDST;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(REG_VCC_LO,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_VOP3_SDST_ENC base class methods ---
Inst_VOP3_SDST_ENC::Inst_VOP3_SDST_ENC(InFmt_VOP3_SDST_ENC *iFmt,
const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_VOP3_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
} // Inst_VOP3_SDST_ENC
Inst_VOP3_SDST_ENC::~Inst_VOP3_SDST_ENC()
{
} // ~Inst_VOP3_SDST_ENC
int
Inst_VOP3_SDST_ENC::instSize() const
{
return 8;
} // instSize
void
Inst_VOP3_SDST_ENC::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << "v" << instData.VDST << ", ";
if (numDstRegOperands() == 2) {
if (getOperandSize(getNumOperands() - 1) > 4) {
int num_regs = getOperandSize(getNumOperands() - 1) / 4;
dis_stream << opSelectorToRegSym(instData.SDST, num_regs)
<< ", ";
} else {
dis_stream << opSelectorToRegSym(instData.SDST) << ", ";
}
}
if (extData.NEG & 0x1) {
dis_stream << "-" << opSelectorToRegSym(extData.SRC0) << ", ";
} else {
dis_stream << opSelectorToRegSym(extData.SRC0) << ", ";
}
if (extData.NEG & 0x2) {
dis_stream << "-" << opSelectorToRegSym(extData.SRC1);
} else {
dis_stream << opSelectorToRegSym(extData.SRC1);
}
if (numSrcRegOperands() == 3) {
if (extData.NEG & 0x4) {
dis_stream << ", -" << opSelectorToRegSym(extData.SRC2);
} else {
dis_stream << ", " << opSelectorToRegSym(extData.SRC2);
}
}
if (readsVCC())
dis_stream << ", vcc";
disassembly = dis_stream.str();
}
bool
Inst_VOP3_SDST_ENC::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isScalarReg(extData.SRC0);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1. SRC1 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isScalarReg(extData.SRC1);
} else {
// if we only have 1 source operand, opIdx 1
// will be VDST, and VDST is never a scalar
// reg
if (instData.VDST == REG_VCC_LO)
return true;
return false;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2. SRC2 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isScalarReg(extData.SRC2);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST, and VDST is never a scalar
// reg
if (instData.VDST == REG_VCC_LO)
return true;
return false;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write,
// and VCC is always stored in an SGPR pair
assert(writesVCC() || readsVCC());
return true;
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands then op
// idx 3 will correspond to VDST, and VDST
// is never a scalar reg
if (instData.VDST == REG_VCC_LO)
return true;
return false;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write, and
// and VCC is always stored in an SGPR pair
assert(writesVCC() || readsVCC());
return true;
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is always in an SGPR
assert(writesVCC() || readsVCC());
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_VOP3_SDST_ENC::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0 may be a scalar or vector register, an
// inline constant, or a special HW register
return isVectorReg(extData.SRC0);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1. SRC1 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isVectorReg(extData.SRC1);
} else {
// if we only have 1 source operand, opIdx 1
// will be VDST, and VDST is always a vector
// reg
if (instData.VDST == REG_VCC_LO)
return false;
return true;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2. SRC2 may be
// a scalar or vector register, an inline
// constant, or a special HW register
return isVectorReg(extData.SRC2);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST, and VDST is always a vector
// reg
if (instData.VDST == REG_VCC_LO)
return false;
return true;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write,
// and VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands then op
// idx 3 will correspond to VDST, and VDST
// is always a vector reg
if (instData.VDST == REG_VCC_LO)
return false;
return true;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write, and
// and VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC, and
// VCC is never stored in a VGPR
assert(writesVCC() || readsVCC());
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_VOP3_SDST_ENC::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
// SRC0
return opSelectorToRegIdx(extData.SRC0,
gpuDynInst->wavefront()->reservedScalarRegs);
case 1:
if (numSrcRegOperands() > 1) {
// if we have more than 1 source operand then
// op index 1 corresponds to SRC1
return opSelectorToRegIdx(extData.SRC1,
gpuDynInst->wavefront()->reservedScalarRegs);
} else {
// if we only have 1 source operand, opIdx 1
// will be VDST
return instData.VDST;
}
case 2:
if (numSrcRegOperands() > 2) {
// if we have more than 2 source operand then
// op index 2 corresponds to SRC2
return opSelectorToRegIdx(extData.SRC2,
gpuDynInst->wavefront()->reservedScalarRegs);
} else if (numSrcRegOperands() == 2) {
// if we only have 2 source operands, opIdx 2
// will be VDST
return instData.VDST;
} else {
// if this idx doesn't correspond to SRCX or
// VDST then it must be a VCC read or write
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
case 3:
if (numSrcRegOperands() == 3) {
// if we have 3 source operands then op
// idx 3 will correspond to VDST
return instData.VDST;
} else {
// if this idx doesn't correspond to VDST
// then it must be a VCC read or write
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
}
case 4:
// if a VOP3 instruction has more than 4 ops
// it must read from and write to VCC
assert(writesVCC() || readsVCC());
return opSelectorToRegIdx(instData.SDST,
gpuDynInst->wavefront()->reservedScalarRegs);
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_DS base class methods ---
Inst_DS::Inst_DS(InFmt_DS *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(GroupSegment);
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_DS_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
} // Inst_DS
Inst_DS::~Inst_DS()
{
} // ~Inst_DS
int
Inst_DS::instSize() const
{
return 8;
} // instSize
void
Inst_DS::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
if (numDstRegOperands())
dis_stream << "v" << extData.VDST << ", ";
dis_stream << "v" << extData.ADDR;
if (numSrcRegOperands() > 1)
dis_stream << ", v" << extData.DATA0;
if (numSrcRegOperands() > 2)
dis_stream << ", v" << extData.DATA1;
uint16_t offset = 0;
if (instData.OFFSET1) {
offset += instData.OFFSET1;
offset <<= 8;
}
if (instData.OFFSET0)
offset += instData.OFFSET0;
if (offset)
dis_stream << " offset:" << offset;
disassembly = dis_stream.str();
}
bool
Inst_DS::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// DS instructions cannot access SGPRs
return false;
}
bool
Inst_DS::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// DS instructions only access VGPRs
return true;
}
int
Inst_DS::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return extData.ADDR;
case 1:
if (numSrcRegOperands() > 1) {
return extData.DATA0;
} else if (numDstRegOperands()) {
return extData.VDST;
}
case 2:
if (numSrcRegOperands() > 2) {
return extData.DATA1;
} else if (numDstRegOperands()) {
return extData.VDST;
}
case 3:
assert(numDstRegOperands());
return extData.VDST;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_MUBUF base class methods ---
Inst_MUBUF::Inst_MUBUF(InFmt_MUBUF *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_MUBUF_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
if (instData.GLC)
setFlag(GloballyCoherent);
if (instData.SLC)
setFlag(SystemCoherent);
} // Inst_MUBUF
Inst_MUBUF::~Inst_MUBUF()
{
} // ~Inst_MUBUF
int
Inst_MUBUF::instSize() const
{
return 8;
} // instSize
void
Inst_MUBUF::generateDisassembly()
{
// SRSRC is always in units of 4 SGPRs
int srsrc_val = extData.SRSRC * 4;
std::stringstream dis_stream;
dis_stream << _opcode << " ";
dis_stream << "v" << extData.VDATA << ", v" << extData.VADDR << ", ";
dis_stream << "s[" << srsrc_val << ":"
<< srsrc_val + 3 << "], ";
dis_stream << "s" << extData.SOFFSET;
if (instData.OFFSET)
dis_stream << ", offset:" << instData.OFFSET;
disassembly = dis_stream.str();
}
bool
Inst_MUBUF::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return false;
case 1:
return true;
case 2:
return true;
case 3:
return false;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
bool
Inst_MUBUF::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return true;
case 1:
return false;
case 2:
return false;
case 3:
return true;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return false;
}
}
int
Inst_MUBUF::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return extData.VADDR;
case 1:
// SRSRC is always in units of 4 SGPRs
return extData.SRSRC * 4;
case 2:
return extData.SOFFSET;
case 3:
return extData.VDATA;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
// --- Inst_MTBUF base class methods ---
Inst_MTBUF::Inst_MTBUF(InFmt_MTBUF *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_MTBUF_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
if (instData.GLC)
setFlag(GloballyCoherent);
if (extData.SLC)
setFlag(SystemCoherent);
} // Inst_MTBUF
Inst_MTBUF::~Inst_MTBUF()
{
} // ~Inst_MTBUF
int
Inst_MTBUF::instSize() const
{
return 8;
} // instSize
// --- Inst_MIMG base class methods ---
Inst_MIMG::Inst_MIMG(InFmt_MIMG *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_MIMG_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
if (instData.GLC)
setFlag(GloballyCoherent);
if (instData.SLC)
setFlag(SystemCoherent);
} // Inst_MIMG
Inst_MIMG::~Inst_MIMG()
{
} // ~Inst_MIMG
int
Inst_MIMG::instSize() const
{
return 8;
} // instSize
// --- Inst_EXP base class methods ---
Inst_EXP::Inst_EXP(InFmt_EXP *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_EXP_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
} // Inst_EXP
Inst_EXP::~Inst_EXP()
{
} // ~Inst_EXP
int
Inst_EXP::instSize() const
{
return 8;
} // instSize
// --- Inst_FLAT base class methods ---
Inst_FLAT::Inst_FLAT(InFmt_FLAT *iFmt, const std::string &opcode)
: GCN3GPUStaticInst(opcode)
{
setFlag(Flat);
// copy first instruction DWORD
instData = iFmt[0];
// copy second instruction DWORD
extData = ((InFmt_FLAT_1 *)iFmt)[1];
_srcLiteral = *reinterpret_cast<uint32_t*>(&iFmt[1]);
if (instData.GLC)
setFlag(GloballyCoherent);
if (instData.SLC)
setFlag(SystemCoherent);
} // Inst_FLAT
Inst_FLAT::~Inst_FLAT()
{
} // ~Inst_FLAT
int
Inst_FLAT::instSize() const
{
return 8;
} // instSize
void
Inst_FLAT::generateDisassembly()
{
std::stringstream dis_stream;
dis_stream << _opcode << " ";
if (isLoad())
dis_stream << "v" << extData.VDST << ", ";
dis_stream << "v[" << extData.ADDR << ":" << extData.ADDR + 1 << "]";
if (isStore())
dis_stream << ", v" << extData.DATA;
disassembly = dis_stream.str();
}
bool
Inst_FLAT::isScalarRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// if a FLAT instruction has more than two
// operands it must be an atomic
if (opIdx == 2)
assert(isAtomic());
// FLAT instructions cannot access SGPRs
return false;
}
bool
Inst_FLAT::isVectorRegister(int opIdx)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
// if a FLAT instruction has more than two
// operands it must be an atomic
if (opIdx == 2)
assert(isAtomic());
// FLAT instructions only access VGPRs
return true;
}
int
Inst_FLAT::getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst)
{
assert(opIdx >= 0);
assert(opIdx < getNumOperands());
switch (opIdx) {
case 0:
return extData.ADDR;
case 1:
if (isStore()) {
return extData.DATA;
} else if (isLoad()) {
return extData.VDST;
} else if (isAtomic()) {
// For flat_atomic instructions,
// the DATA VGPR gives the source
return extData.DATA;
} else {
fatal("Unsupported flat instr type\n");
}
case 2:
// if a FLAT instruction has more than two
// operands it must be an atomic
assert(isAtomic());
return extData.VDST;
default:
fatal("Operand at idx %i does not exist\n", opIdx);
return -1;
}
}
} // namespace Gcn3ISA