introduce new gcn decoder

This commit is contained in:
Asuka 2022-04-09 20:27:18 +08:00
parent 86f9b04502
commit a8ff46542b
12 changed files with 12512 additions and 22 deletions

View file

@ -25,7 +25,9 @@
<ClInclude Include="Emulator\VirtualCPU.h" />
<ClInclude Include="Graphics\Gcn\GcnCommon.h" />
<ClInclude Include="Graphics\Gcn\GcnContants.h" />
<ClInclude Include="Graphics\Gcn\GcnEnums.h" />
<ClInclude Include="Graphics\Gcn\GcnDecoder.h" />
<ClInclude Include="Graphics\Gcn\GcnEnum.h" />
<ClInclude Include="Graphics\Gcn\GcnInstruction.h" />
<ClInclude Include="Graphics\Gcn\GcnProgramInfo.h" />
<ClInclude Include="Graphics\Gcn\GcnShaderRegField.h" />
<ClInclude Include="Graphics\Gcn\GcnShaderRegister.h" />
@ -259,6 +261,8 @@
<ClCompile Include="Emulator\TLSHandler.cpp" />
<ClCompile Include="Emulator\VirtualCPU.cpp" />
<ClCompile Include="GPCS4Main.cpp" />
<ClCompile Include="Graphics\Gcn\GcnDecoder.cpp" />
<ClCompile Include="Graphics\Gcn\GcnInstruction.cpp" />
<ClCompile Include="Graphics\Gcn\GcnProgramInfo.cpp" />
<ClCompile Include="Graphics\Gnm\GnmCommandBuffer.cpp" />
<ClCompile Include="Graphics\Gnm\GnmCommandBufferDispatch.cpp" />

View file

@ -814,9 +814,6 @@
<ClInclude Include="Graphics\Gcn\GcnContants.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnEnums.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnShaderRegField.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
@ -826,6 +823,15 @@
<ClInclude Include="Graphics\Gcn\GcnProgramInfo.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnDecoder.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnEnum.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
<ClInclude Include="Graphics\Gcn\GcnInstruction.h">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Loader\EbootObject.cpp">
@ -1428,6 +1434,12 @@
<ClCompile Include="Graphics\Gcn\GcnProgramInfo.cpp">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClCompile>
<ClCompile Include="Graphics\Gcn\GcnDecoder.cpp">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClCompile>
<ClCompile Include="Graphics\Gcn\GcnInstruction.cpp">
<Filter>Source Files\Graphics\Gcn</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="Emulator\TLSStub.asm">

View file

@ -0,0 +1,856 @@
#include "GcnDecoder.h"
#include "UtilBit.h"
#include <algorithm>
using namespace util;
LOG_CHANNEL(Graphic.Gcn.GcnDecoder);
namespace sce::gcn
{
uint32_t GcnCodeSlice::at(uint32_t id) const
{
return m_ptr[id];
}
uint32_t GcnCodeSlice::readu32()
{
return *(m_ptr++);
}
uint64_t GcnCodeSlice::readu64()
{
uint64_t value = *(uint64_t*)m_ptr;
m_ptr += 2;
return value;
}
///
GcnDecodeContext::GcnDecodeContext()
{
}
GcnDecodeContext::~GcnDecodeContext()
{
}
void GcnDecodeContext::decodeInstruction(GcnCodeSlice& code)
{
const uint32_t token = code.at(0);
GcnInstEncoding encoding = getInstructionEncoding(token);
LOG_ASSERT(encoding != GcnInstEncoding::ILLEGAL, "illegal encoding %d", encoding);
uint32_t encodingLen = getEncodingLength(encoding);
// Clear the instruction
m_instruction = GcnShaderInstruction();
// Decode
if (encodingLen == sizeof(uint32_t))
{
decodeInstruction32(encoding, code);
}
else
{
decodeInstruction64(encoding, code);
}
// Update instruction meta info.
updateInstructionMeta(encoding);
// Detect literal constant.
// Only 32 bits instructions may have literal constant.
// Note:
// Literal constant decode must be performed after meta info updated.
if (encodingLen == sizeof(uint32_t))
{
decodeLiteralConstant(encoding, code);
}
}
GcnInstEncoding GcnDecodeContext::getInstructionEncoding(uint32_t hexInstruction)
{
GcnInstEncoding instructionEncoding = GcnInstEncoding::ILLEGAL;
/// Try all instructions encoding masks from longest to shortest until
/// legal GcnInstEncoding is found.
/// GcnEncodingMask::Mask9bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_9bit);
switch (instructionEncoding)
{
case GcnInstEncoding::SOP1:
case GcnInstEncoding::SOPP:
case GcnInstEncoding::SOPC:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask7bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_7bit);
switch (instructionEncoding)
{
case GcnInstEncoding::VOP1:
case GcnInstEncoding::VOPC:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask6bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_6bit);
switch (instructionEncoding)
{
case GcnInstEncoding::VOP3:
case GcnInstEncoding::EXP:
case GcnInstEncoding::VINTRP:
case GcnInstEncoding::DS:
case GcnInstEncoding::MUBUF:
case GcnInstEncoding::MTBUF:
case GcnInstEncoding::MIMG:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask5bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_5bit);
switch (instructionEncoding)
{
case GcnInstEncoding::SMRD:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask4bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_4bit);
switch (instructionEncoding)
{
case GcnInstEncoding::SOPK:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask2bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_2bit);
switch (instructionEncoding)
{
case GcnInstEncoding::SOP2:
return instructionEncoding;
default:
break;
}
/// GcnEncodingMask::Mask1bit
instructionEncoding = static_cast<GcnInstEncoding>(hexInstruction & (uint32_t)GcnEncodingMask::MASK_1bit);
switch (instructionEncoding)
{
case GcnInstEncoding::VOP2:
return instructionEncoding;
default:
break;
}
/// If no legal GcnInstEncoding found return GcnInstEncoding::ILLEGAL
return GcnInstEncoding::ILLEGAL;
}
uint32_t GcnDecodeContext::getEncodingLength(GcnInstEncoding encoding)
{
uint32_t instLength = 0;
switch (encoding)
{
case GcnInstEncoding::SOP1:
case GcnInstEncoding::SOPP:
case GcnInstEncoding::SOPC:
case GcnInstEncoding::SOPK:
case GcnInstEncoding::SOP2:
case GcnInstEncoding::VOP1:
case GcnInstEncoding::VOPC:
case GcnInstEncoding::VOP2:
case GcnInstEncoding::SMRD:
case GcnInstEncoding::VINTRP:
instLength = sizeof(uint32_t);
break;
case GcnInstEncoding::VOP3:
case GcnInstEncoding::MUBUF:
case GcnInstEncoding::MTBUF:
case GcnInstEncoding::MIMG:
case GcnInstEncoding::DS:
case GcnInstEncoding::EXP:
instLength = sizeof(uint64_t);
break;
}
return instLength;
}
uint32_t GcnDecodeContext::getOpMapOffset(GcnInstEncoding encoding)
{
uint32_t offset = 0;
switch (encoding)
{
case GcnInstEncoding::SOP1:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SOP1;
break;
case GcnInstEncoding::SOPP:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SOPP;
break;
case GcnInstEncoding::SOPC:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SOPC;
break;
case GcnInstEncoding::VOP1:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_VOP1;
break;
case GcnInstEncoding::VOPC:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_VOPC;
break;
case GcnInstEncoding::VOP3:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_VOP3;
break;
case GcnInstEncoding::EXP:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_EXP;
break;
case GcnInstEncoding::VINTRP:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_VINTRP;
break;
case GcnInstEncoding::DS:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_DS;
break;
case GcnInstEncoding::MUBUF:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_MUBUF;
break;
case GcnInstEncoding::MTBUF:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_MTBUF;
break;
case GcnInstEncoding::MIMG:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_MIMG;
break;
case GcnInstEncoding::SMRD:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SMRD;
break;
case GcnInstEncoding::SOPK:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SOPK;
break;
case GcnInstEncoding::SOP2:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_SOP2;
break;
case GcnInstEncoding::VOP2:
offset = (uint32_t)GcnOpcodeMap::OP_MAP_VOP2;
break;
}
return offset;
}
uint32_t GcnDecodeContext::mapEncodingOp(GcnInstEncoding encoding, GcnOpcode opcode)
{
// Map from uniform opcode to encoding specific opcode.
uint32_t encodingOp = 0;
if (encoding == GcnInstEncoding::VOP3)
{
if (opcode >= GcnOpcode::V_CMP_F_F32 && opcode <= GcnOpcode::V_CMPX_T_U64)
{
uint32_t op = static_cast<uint32_t>(opcode) - static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOPC);
encodingOp = op + static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOPC);
}
else if (opcode >= GcnOpcode::V_CNDMASK_B32 && opcode <= GcnOpcode::V_CVT_PK_I16_I32)
{
uint32_t op = static_cast<uint32_t>(opcode) - static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP2);
encodingOp = op + static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOP2);
}
else if (opcode >= GcnOpcode::V_NOP && opcode <= GcnOpcode::V_MOVRELSD_B32)
{
uint32_t op = static_cast<uint32_t>(opcode) - static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP1);
encodingOp = op + static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOP1);
}
else
{
encodingOp = static_cast<uint32_t>(opcode) - static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP3);
}
}
else
{
uint32_t mapOffset = getOpMapOffset(encoding);
encodingOp = static_cast<uint32_t>(opcode) - mapOffset;
}
return encodingOp;
}
bool GcnDecodeContext::hasAdditionalLiteral(GcnInstEncoding encoding, uint32_t opcode)
{
// Some instructions require an additional literal constant following the binary stream
// even if LiteralConst is not specified in src operand.
bool hasLiteral = false;
switch (encoding)
{
case GcnInstEncoding::SOPK:
{
if (opcode == (uint32_t)GcnOpcodeSOPK::S_SETREG_IMM32_B32)
{
hasLiteral = true;
}
}
break;
case GcnInstEncoding::VOP2:
{
if (opcode == (uint32_t)GcnOpcodeVOP2::V_MADMK_F32 ||
opcode == (uint32_t)GcnOpcodeVOP2::V_MADAK_F32)
{
hasLiteral = true;
}
}
break;
}
return hasLiteral;
}
void GcnDecodeContext::updateInstructionMeta(GcnInstEncoding encoding)
{
uint32_t encodingOp = mapEncodingOp(encoding, m_instruction.opcode);
GcnInstFormat instFormat = gcnInstructionFormat(encoding, encodingOp);
m_instruction.opClass = instFormat.instructionClass;
m_instruction.encoding = encoding;
m_instruction.srcCount = instFormat.srcCount;
m_instruction.length = getEncodingLength(encoding);
// Update src operand numeric type.
// Dst operand's type is set during instruction decoding.
auto setOperandNumType = [&instFormat](GcnInstOperand& src)
{
// Only update uninitialized numeric type.
if (src.numericType == GcnNumericType::Undefined)
{
src.numericType = instFormat.srcType;
}
};
std::for_each_n(std::begin(m_instruction.src), m_instruction.srcCount, setOperandNumType);
}
GcnOperandField GcnDecodeContext::getOperandField(uint32_t code)
{
GcnOperandField field = {};
if (code >= ScalarGPRMin && code <= ScalarGPRMax)
{
field = GcnOperandField::ScalarGPR;
}
else if (code >= SignedConstIntPosMin && code <= SignedConstIntPosMax)
{
field = GcnOperandField::SignedConstIntPos;
}
else if (code >= SignedConstIntNegMin && code <= SignedConstIntNegMax)
{
field = GcnOperandField::SignedConstIntNeg;
}
else if (code >= VectorGPRMin && code <= VectorGPRMax)
{
field = GcnOperandField::VectorGPR;
}
else
{
field = static_cast<GcnOperandField>(code);
}
return field;
}
void GcnDecodeContext::decodeInstruction32(GcnInstEncoding encoding, GcnCodeSlice& code)
{
uint32_t hexInstruction = code.readu32();
switch (encoding)
{
case GcnInstEncoding::SOP1:
decodeInstructionSOP1(hexInstruction);
break;
case GcnInstEncoding::SOPP:
decodeInstructionSOPP(hexInstruction);
break;
case GcnInstEncoding::SOPC:
decodeInstructionSOPC(hexInstruction);
break;
case GcnInstEncoding::SOPK:
decodeInstructionSOPK(hexInstruction);
break;
case GcnInstEncoding::SOP2:
decodeInstructionSOP2(hexInstruction);
break;
case GcnInstEncoding::VOP1:
decodeInstructionVOP1(hexInstruction);
break;
case GcnInstEncoding::VOPC:
decodeInstructionVOPC(hexInstruction);
break;
case GcnInstEncoding::VOP2:
decodeInstructionVOP2(hexInstruction);
break;
case GcnInstEncoding::SMRD:
decodeInstructionSMRD(hexInstruction);
break;
case GcnInstEncoding::VINTRP:
decodeInstructionVINTRP(hexInstruction);
break;
}
}
void GcnDecodeContext::decodeInstruction64(GcnInstEncoding encoding, GcnCodeSlice& code)
{
uint64_t hexInstruction = code.readu64();
switch (encoding)
{
case GcnInstEncoding::VOP3:
decodeInstructionVOP3(hexInstruction);
break;
case GcnInstEncoding::MUBUF:
decodeInstructionMUBUF(hexInstruction);
break;
case GcnInstEncoding::MTBUF:
decodeInstructionMTBUF(hexInstruction);
break;
case GcnInstEncoding::MIMG:
decodeInstructionMIMG(hexInstruction);
break;
case GcnInstEncoding::DS:
decodeInstructionDS(hexInstruction);
break;
case GcnInstEncoding::EXP:
decodeInstructionEXP(hexInstruction);
break;
}
}
void GcnDecodeContext::decodeLiteralConstant(GcnInstEncoding encoding, GcnCodeSlice& code)
{
bool hasLiteral = false;
do
{
// Detect if it's a special instruction.
hasLiteral = hasAdditionalLiteral(
encoding, mapEncodingOp(encoding, m_instruction.opcode));
if (hasLiteral)
{
uint32_t literalCount = code.readu32();
m_instruction.src[m_instruction.srcCount++].literalConst = literalCount;
break;
}
// Find if the instruction contains a literal constant
auto isLiteralSrc = [](GcnInstOperand& src)
{
return src.field == GcnOperandField::LiteralConst;
};
auto iter = std::find_if(
std::begin(m_instruction.src),
std::end(m_instruction.src),
isLiteralSrc);
hasLiteral = (iter != std::end(m_instruction.src));
if (hasLiteral)
{
uint32_t literalCount = code.readu32();
iter->literalConst = literalCount;
break;
}
} while (false);
// Increase instruction length by 4
// if literal constant append.
if (hasLiteral)
{
m_instruction.length += sizeof(uint32_t);
}
}
void GcnDecodeContext::decodeInstructionSOP1(uint32_t hexInstruction)
{
uint32_t ssrc0 = bit::extract(hexInstruction, 7, 0);
uint32_t op = bit::extract(hexInstruction, 15, 8);
uint32_t sdst = bit::extract(hexInstruction, 22, 16);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SOP1));
m_instruction.src[0].field = getOperandField(ssrc0);
m_instruction.src[0].code = ssrc0;
m_instruction.dst[0].field = getOperandField(sdst);
m_instruction.dst[0].code = sdst;
m_instruction.dstCount = 1;
}
void GcnDecodeContext::decodeInstructionSOPP(uint32_t hexInstruction)
{
uint32_t op = bit::extract(hexInstruction, 22, 16);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SOPP));
m_instruction.control.sopp = *reinterpret_cast<GcnInstControlSOPP*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionSOPC(uint32_t hexInstruction)
{
uint32_t ssrc0 = bit::extract(hexInstruction, 7, 0);
uint32_t ssrc1 = bit::extract(hexInstruction, 15, 8);
uint32_t op = bit::extract(hexInstruction, 22, 16);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SOPC));
m_instruction.src[0].field = getOperandField(ssrc0);
m_instruction.src[0].code = ssrc0;
m_instruction.src[1].field = getOperandField(ssrc1);
m_instruction.src[1].code = ssrc1;
}
void GcnDecodeContext::decodeInstructionSOPK(uint32_t hexInstruction)
{
uint32_t sdst = bit::extract(hexInstruction, 22, 16);
uint32_t op = bit::extract(hexInstruction, 27, 23);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SOPK));
m_instruction.dst[0].field = getOperandField(sdst);
m_instruction.dst[0].code = sdst;
m_instruction.dstCount = 1;
m_instruction.control.sopk = *reinterpret_cast<GcnInstControlSOPK*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionSOP2(uint32_t hexInstruction)
{
uint32_t ssrc0 = bit::extract(hexInstruction, 7, 0);
uint32_t ssrc1 = bit::extract(hexInstruction, 15, 8);
uint32_t sdst = bit::extract(hexInstruction, 22, 16);
uint32_t op = bit::extract(hexInstruction, 29, 23);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SOP2));
m_instruction.src[0].field = getOperandField(ssrc0);
m_instruction.src[0].code = ssrc0;
m_instruction.src[1].field = getOperandField(ssrc1);
m_instruction.src[1].code = ssrc1;
m_instruction.dst[0].field = getOperandField(sdst);
m_instruction.dst[0].code = sdst;
m_instruction.dstCount = 1;
}
void GcnDecodeContext::decodeInstructionVOP1(uint32_t hexInstruction)
{
uint32_t src0 = bit::extract(hexInstruction, 8, 0);
uint32_t op = bit::extract(hexInstruction, 16, 9);
uint32_t vdst = bit::extract(hexInstruction, 24, 17);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP1));
m_instruction.src[0].field = getOperandField(src0);
m_instruction.src[0].code = src0;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vdst;
}
void GcnDecodeContext::decodeInstructionVOPC(uint32_t hexInstruction)
{
uint32_t src0 = bit::extract(hexInstruction, 8, 0);
uint32_t vsrc1 = bit::extract(hexInstruction, 16, 9);
uint32_t op = bit::extract(hexInstruction, 24, 17);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOPC));
m_instruction.src[0].field = getOperandField(src0);
m_instruction.src[0].code = src0;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vsrc1;
}
void GcnDecodeContext::decodeInstructionVOP2(uint32_t hexInstruction)
{
uint32_t src0 = bit::extract(hexInstruction, 8, 0);
uint32_t vsrc1 = bit::extract(hexInstruction, 16, 9);
uint32_t vdst = bit::extract(hexInstruction, 24, 17);
uint32_t op = bit::extract(hexInstruction, 30, 25);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP2));
m_instruction.src[0].field = getOperandField(src0);
m_instruction.src[0].code = src0;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vsrc1;
m_instruction.dst[0].field = GcnOperandField::VectorGPR;
m_instruction.dst[0].code = vdst;
m_instruction.dstCount = 1;
}
void GcnDecodeContext::decodeInstructionSMRD(uint32_t hexInstruction)
{
uint32_t sbase = bit::extract(hexInstruction, 14, 9);
uint32_t sdst = bit::extract(hexInstruction, 21, 15);
uint32_t op = bit::extract(hexInstruction, 26, 22);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_SMRD));
m_instruction.src[0].field = GcnOperandField::ScalarGPR;
m_instruction.src[0].code = sbase;
m_instruction.dst[0].field = GcnOperandField::ScalarGPR;
m_instruction.dst[0].code = sdst;
m_instruction.dstCount = 1;
m_instruction.control.smrd = *reinterpret_cast<GcnInstControlSMRD*>(&hexInstruction);
if (op <= static_cast<uint32_t>(GcnOpcodeSMRD::S_LOAD_DWORDX16))
{
m_instruction.control.smrd.count = 1 << op;
}
else if (op >= static_cast<uint32_t>(GcnOpcodeSMRD::S_BUFFER_LOAD_DWORD) &&
op <= static_cast<uint32_t>(GcnOpcodeSMRD::S_BUFFER_LOAD_DWORDX16))
{
m_instruction.control.smrd.count = 1 << (op - 8);
}
}
void GcnDecodeContext::decodeInstructionVINTRP(uint32_t hexInstruction)
{
uint32_t vsrc = bit::extract(hexInstruction, 7, 0);
uint32_t op = bit::extract(hexInstruction, 17, 16);
uint32_t vdst = bit::extract(hexInstruction, 25, 18);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VINTRP));
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = vsrc;
m_instruction.dst[0].field = GcnOperandField::VectorGPR;
m_instruction.dst[0].code = vdst;
m_instruction.dstCount = 1;
m_instruction.control.vintrp = *reinterpret_cast<GcnInstControlVINTRP*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionVOP3(uint64_t hexInstruction)
{
uint32_t vdst = bit::extract(hexInstruction, 7, 0);
uint32_t sdst = bit::extract(hexInstruction, 14, 8); // For VOP3B
uint32_t op = bit::extract(hexInstruction, 25, 17);
uint32_t src0 = bit::extract(hexInstruction, 40, 32);
uint32_t src1 = bit::extract(hexInstruction, 49, 41);
uint32_t src2 = bit::extract(hexInstruction, 58, 50);
if (op >= static_cast<uint32_t>(GcnOpcodeVOP3::V_CMP_F_F32) &&
op <= static_cast<uint32_t>(GcnOpcodeVOP3::V_CMPX_T_U64))
{
// Map from VOP3 to VOPC
uint32_t vopcOp = op - static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOPC);
m_instruction.opcode = static_cast<GcnOpcode>(vopcOp + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOPC));
}
else if (op >= static_cast<uint32_t>(GcnOpcodeVOP3::V_CNDMASK_B32) &&
op <= static_cast<uint32_t>(GcnOpcodeVOP3::V_CVT_PK_I16_I32))
{
// Map from VOP3 to VOP2
uint32_t vop2Op = op - static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOP2);
m_instruction.opcode = static_cast<GcnOpcode>(vop2Op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP2));
}
else if (op >= static_cast<uint32_t>(GcnOpcodeVOP3::V_NOP) &&
op <= static_cast<uint32_t>(GcnOpcodeVOP3::V_MOVRELSD_B32))
{
// Map from VOP3 to VOP1
uint32_t vop1Op = op - static_cast<uint32_t>(GcnOpMapVOP3VOPX::VOP3_TO_VOP1);
m_instruction.opcode = static_cast<GcnOpcode>(vop1Op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP1));
}
else
{
// VOP3 encoding, do not map.
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_VOP3));
}
m_instruction.src[0].field = getOperandField(src0);
m_instruction.src[0].code = src0;
m_instruction.src[1].field = getOperandField(src1);
m_instruction.src[1].code = src1;
m_instruction.src[2].field = getOperandField(src2);
m_instruction.src[2].code = src2;
m_instruction.dst[0].field = GcnOperandField::VectorGPR;
m_instruction.dst[0].code = vdst;
m_instruction.dst[1].field = GcnOperandField::ScalarGPR;
m_instruction.dst[1].code = sdst;
if (op >= static_cast<uint32_t>(GcnOpcodeVOP3::V_ADD_I32) &&
op <= static_cast<uint32_t>(GcnOpcodeVOP3::V_DIV_SCALE_F64))
{
// VOP3B has a sdst operand.
m_instruction.dstCount = 2;
}
else
{
m_instruction.dstCount = 1;
}
m_instruction.control.vop3 = *reinterpret_cast<GcnInstControlVOP3*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionMUBUF(uint64_t hexInstruction)
{
uint32_t op = bit::extract(hexInstruction, 24, 18);
uint32_t vaddr = bit::extract(hexInstruction, 39, 32);
uint32_t vdata = bit::extract(hexInstruction, 47, 40);
uint32_t srsrc = bit::extract(hexInstruction, 52, 48);
uint32_t soffset = bit::extract(hexInstruction, 63, 56);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_MUBUF));
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = vaddr;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vdata;
m_instruction.src[2].field = GcnOperandField::ScalarGPR;
m_instruction.src[2].code = srsrc;
m_instruction.src[3].field = GcnOperandField::ScalarGPR;
m_instruction.src[3].code = soffset;
m_instruction.control.mubuf = *reinterpret_cast<GcnInstControlMUBUF*>(&hexInstruction);
if (op >= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_LOAD_FORMAT_X) &&
op <= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_LOAD_FORMAT_XYZW))
{
m_instruction.control.mubuf.count = op + 1;
}
else if (op >= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_STORE_FORMAT_X) &&
op <= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_STORE_FORMAT_XYZW))
{
m_instruction.control.mubuf.count = op - 3;
}
else if (op >= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_LOAD_DWORD) &&
op <= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_LOAD_DWORDX4))
{
m_instruction.control.mubuf.count = 1 << (op - 12);
}
else if (op >= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_STORE_DWORD) &&
op <= static_cast<uint32_t>(GcnOpcodeMUBUF::BUFFER_STORE_DWORDX4))
{
m_instruction.control.mubuf.count = 1 << (op - 28);
}
}
void GcnDecodeContext::decodeInstructionMTBUF(uint64_t hexInstruction)
{
uint32_t op = bit::extract(hexInstruction, 18, 16);
uint32_t vaddr = bit::extract(hexInstruction, 39, 32);
uint32_t vdata = bit::extract(hexInstruction, 47, 40);
uint32_t srsrc = bit::extract(hexInstruction, 52, 48);
uint32_t soffset = bit::extract(hexInstruction, 63, 56);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_MTBUF));
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = vaddr;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vdata;
m_instruction.src[2].field = GcnOperandField::ScalarGPR;
m_instruction.src[2].code = srsrc;
m_instruction.src[3].field = GcnOperandField::ScalarGPR;
m_instruction.src[3].code = soffset;
m_instruction.control.mtbuf = *reinterpret_cast<GcnInstControlMTBUF*>(&hexInstruction);
if (op >= static_cast<uint32_t>(GcnOpcodeMTBUF::TBUFFER_LOAD_FORMAT_X) &&
op <= static_cast<uint32_t>(GcnOpcodeMTBUF::TBUFFER_LOAD_FORMAT_XYZW))
{
m_instruction.control.mtbuf.count = op + 1;
}
else if (op >= static_cast<uint32_t>(GcnOpcodeMTBUF::TBUFFER_STORE_FORMAT_X) &&
op <= static_cast<uint32_t>(GcnOpcodeMTBUF::TBUFFER_STORE_FORMAT_XYZW))
{
m_instruction.control.mtbuf.count = op - 3;
}
}
void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction)
{
uint32_t op = bit::extract(hexInstruction, 24, 18);
uint32_t vaddr = bit::extract(hexInstruction, 39, 32);
uint32_t vdata = bit::extract(hexInstruction, 47, 40);
uint32_t srsrc = bit::extract(hexInstruction, 52, 48);
uint32_t ssamp = bit::extract(hexInstruction, 57, 53);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_MIMG));
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = vaddr;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vdata;
m_instruction.src[2].field = GcnOperandField::ScalarGPR;
m_instruction.src[2].code = srsrc;
m_instruction.src[3].field = GcnOperandField::ScalarGPR;
m_instruction.src[3].code = ssamp;
m_instruction.control.mimg = *reinterpret_cast<GcnInstControlMIMG*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction)
{
uint32_t op = bit::extract(hexInstruction, 25, 18);
uint32_t addr = bit::extract(hexInstruction, 39, 32);
uint32_t data0 = bit::extract(hexInstruction, 47, 40);
uint32_t data1 = bit::extract(hexInstruction, 55, 48);
uint32_t vdst = bit::extract(hexInstruction, 63, 56);
m_instruction.opcode = static_cast<GcnOpcode>(op + static_cast<uint32_t>(GcnOpcodeMap::OP_MAP_DS));
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = addr;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = data0;
m_instruction.src[2].field = GcnOperandField::VectorGPR;
m_instruction.src[2].code = data1;
m_instruction.dst[0].field = GcnOperandField::VectorGPR;
m_instruction.dst[0].code = vdst;
m_instruction.dstCount = 1;
m_instruction.control.ds = *reinterpret_cast<GcnInstControlDS*>(&hexInstruction);
}
void GcnDecodeContext::decodeInstructionEXP(uint64_t hexInstruction)
{
uint32_t vsrc0 = bit::extract(hexInstruction, 39, 32);
uint32_t vsrc1 = bit::extract(hexInstruction, 47, 40);
uint32_t vsrc2 = bit::extract(hexInstruction, 55, 48);
uint32_t vsrc3 = bit::extract(hexInstruction, 63, 56);
m_instruction.opcode = GcnOpcode::EXP;
m_instruction.src[0].field = GcnOperandField::VectorGPR;
m_instruction.src[0].code = vsrc0;
m_instruction.src[1].field = GcnOperandField::VectorGPR;
m_instruction.src[1].code = vsrc1;
m_instruction.src[2].field = GcnOperandField::VectorGPR;
m_instruction.src[2].code = vsrc2;
m_instruction.src[3].field = GcnOperandField::VectorGPR;
m_instruction.src[3].code = vsrc3;
m_instruction.control.exp = *reinterpret_cast<GcnInstControlEXP*>(&hexInstruction);
}
} // namespace sce::gcn

View file

@ -0,0 +1,97 @@
#pragma once
#include "GcnInstruction.h"
namespace sce::gcn
{
class GcnCodeSlice
{
public:
GcnCodeSlice(
const uint32_t* ptr,
const uint32_t* end) :
m_ptr(ptr),
m_end(end)
{
}
uint32_t at(uint32_t id) const;
uint32_t readu32();
uint64_t readu64();
bool atEnd() const
{
return m_ptr == m_end;
}
private:
const uint32_t* m_ptr = nullptr;
const uint32_t* m_end = nullptr;
};
class GcnDecodeContext
{
enum GcnOperandFieldRange
{
ScalarGPRMin = 0,
ScalarGPRMax = 103,
SignedConstIntPosMin = 129,
SignedConstIntPosMax = 192,
SignedConstIntNegMin = 193,
SignedConstIntNegMax = 208,
VectorGPRMin = 256,
VectorGPRMax = 511
};
public:
GcnDecodeContext();
~GcnDecodeContext();
const GcnShaderInstruction& getInstruction() const
{
return m_instruction;
}
void decodeInstruction(GcnCodeSlice& code);
private:
GcnInstEncoding getInstructionEncoding(uint32_t token);
uint32_t getEncodingLength(GcnInstEncoding encoding);
uint32_t getOpMapOffset(GcnInstEncoding encoding);
uint32_t mapEncodingOp(GcnInstEncoding encoding, GcnOpcode opcode);
bool hasAdditionalLiteral(GcnInstEncoding encoding, uint32_t opcode);
void updateInstructionMeta(GcnInstEncoding encoding);
GcnOperandField getOperandField(uint32_t code);
void decodeInstruction32(GcnInstEncoding encoding, GcnCodeSlice& code);
void decodeInstruction64(GcnInstEncoding encoding, GcnCodeSlice& code);
void decodeLiteralConstant(GcnInstEncoding encoding, GcnCodeSlice& code);
// 32 bits encodings
void decodeInstructionSOP1(uint32_t hexInstruction);
void decodeInstructionSOPP(uint32_t hexInstruction);
void decodeInstructionSOPC(uint32_t hexInstruction);
void decodeInstructionSOPK(uint32_t hexInstruction);
void decodeInstructionSOP2(uint32_t hexInstruction);
void decodeInstructionVOP1(uint32_t hexInstruction);
void decodeInstructionVOPC(uint32_t hexInstruction);
void decodeInstructionVOP2(uint32_t hexInstruction);
void decodeInstructionSMRD(uint32_t hexInstruction);
void decodeInstructionVINTRP(uint32_t hexInstruction);
// 64 bits encodings
void decodeInstructionVOP3(uint64_t hexInstruction);
void decodeInstructionMUBUF(uint64_t hexInstruction);
void decodeInstructionMTBUF(uint64_t hexInstruction);
void decodeInstructionMIMG(uint64_t hexInstruction);
void decodeInstructionDS(uint64_t hexInstruction);
void decodeInstructionEXP(uint64_t hexInstruction);
private:
GcnShaderInstruction m_instruction;
};
} // namespace sce::gcn

2412
GPCS4/Graphics/Gcn/GcnEnum.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,18 +0,0 @@
#pragma once
namespace sce::gcn
{
enum class PsslProgramType
{
PixelShader = 0,
VertexShader = 1,
GeometryShader = 2,
HullShader = 3,
DomainShader = 4,
ComputeShader = 5,
FetchShader = 6,
ShaderTypeCount
};
} // namespace sce::gcn

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,168 @@
#pragma once
#include "GcnEnum.h"
namespace sce::gcn
{
constexpr uint32_t GcnMaxSrcCount = 4;
constexpr uint32_t GcnMaxDstCount = 2;
struct GcnInstFormat
{
// Instruction class
GcnInstClass instructionClass = GcnInstClass::Undefined;
uint32_t srcCount = 0;
uint32_t dstCount = 0;
GcnNumericType srcType = GcnNumericType::Undefined;
GcnNumericType dstType = GcnNumericType::Undefined;
};
struct GcnInstOperand
{
GcnOperandField field = GcnOperandField::Undefined;
GcnNumericType numericType = GcnNumericType::Undefined;
union
{
uint32_t code = 0xFFFFFFFF;
uint32_t literalConst;
};
};
struct GcnInstControlSOPK
{
uint32_t simm : 16;
uint32_t : 16;
};
struct GcnInstControlSOPP
{
uint32_t simm : 16;
uint32_t : 16;
};
struct GcnInstControlVOP3
{
uint64_t : 8;
uint64_t abs : 3;
uint64_t clmp : 1;
uint64_t : 47;
uint64_t omod : 2;
uint64_t neg : 3;
};
struct GcnInstControlSMRD
{
uint32_t offset : 8;
uint32_t imm : 1;
uint32_t count : 5;
uint32_t : 18;
};
struct GcnInstControlMUBUF
{
uint64_t offset : 12;
uint64_t offen : 1;
uint64_t idxen : 1;
uint64_t glc : 1;
uint64_t : 1;
uint64_t lds : 1;
uint64_t : 37;
uint64_t slc : 1;
uint64_t tfe : 1;
uint64_t count : 3;
uint64_t : 5;
};
struct GcnInstControlMTBUF
{
uint64_t offset : 12;
uint64_t offen : 1;
uint64_t idxen : 1;
uint64_t glc : 1;
uint64_t : 4;
uint64_t dfmt : 4;
uint64_t nfmt : 3;
uint64_t : 28;
uint64_t slc : 1;
uint64_t tfe : 1;
uint64_t count : 3;
uint64_t : 5;
};
struct GcnInstControlMIMG
{
uint64_t : 8;
uint64_t dmask : 4;
uint64_t unrm : 1;
uint64_t glc : 1;
uint64_t da : 1;
uint64_t r128 : 1;
uint64_t tfe : 1;
uint64_t lwe : 1;
uint64_t : 7;
uint64_t slc : 1;
uint64_t : 38;
};
struct GcnInstControlDS
{
uint64_t offset0 : 8;
uint64_t offset1 : 8;
uint64_t : 1;
uint64_t gds : 1;
uint64_t : 46;
};
struct GcnInstControlVINTRP
{
uint32_t : 8;
uint32_t chan : 2;
uint32_t attr : 6;
uint32_t : 16;
};
struct GcnInstControlEXP
{
uint64_t en : 4;
uint64_t target : 6;
uint64_t compr : 1;
uint64_t done : 1;
uint64_t vm : 1;
uint64_t reserved : 51;
};
union GcnInstControl
{
GcnInstControlSOPK sopk;
GcnInstControlSOPP sopp;
GcnInstControlVOP3 vop3;
GcnInstControlSMRD smrd;
GcnInstControlMUBUF mubuf;
GcnInstControlMTBUF mtbuf;
GcnInstControlMIMG mimg;
GcnInstControlDS ds;
GcnInstControlVINTRP vintrp;
GcnInstControlEXP exp;
};
struct GcnShaderInstruction
{
GcnOpcode opcode;
uint32_t length; // in bytes
GcnInstEncoding encoding;
GcnInstClass opClass;
GcnInstControl control;
uint32_t srcCount;
uint32_t dstCount;
GcnInstOperand src[GcnMaxSrcCount];
GcnInstOperand dst[GcnMaxDstCount];
};
GcnInstFormat gcnInstructionFormat(GcnInstEncoding encoding, uint32_t opcode);
} // namespace sce::gcn

1302
Tools/GCNInstructionDefs.cpp Normal file

File diff suppressed because it is too large Load diff

3150
Tools/GCNInstructions.cpp Normal file

File diff suppressed because it is too large Load diff

264
Tools/GCNInternals.h Normal file
View file

@ -0,0 +1,264 @@
/*
* CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
* Copyright (C) 2014-2018 Mateusz Szpakowski
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __CLRX_GCNINTERNALS_H__
#define __CLRX_GCNINTERNALS_H__
#include <CLRX/Config.h>
#include <cstdint>
#include <string>
#include <CLRX/utils/Utilities.h>
#include <CLRX/utils/GPUId.h>
namespace CLRX
{
// enums for GCN encodings
enum : cxbyte
{
GCNENC_NONE,
GCNENC_SOPC, /* 0x17e<<23, opcode = (7bit)<<16 */
GCNENC_SOPP, /* 0x17f<<23, opcode = (7bit)<<16 */
GCNENC_SOP1, /* 0x17d<<23, opcode = (8bit)<<8 */
GCNENC_SOP2, /* 0x2<<30, opcode = (7bit)<<23 */
GCNENC_SOPK, /* 0xb<<28, opcode = (5bit)<<23 */
GCNENC_SMRD, /* 0x18<<27, opcode = (6bit)<<22 */
GCNENC_SMEM = GCNENC_SMRD, /* 0x18<<27, opcode = (6bit)<<22 */
GCNENC_VOPC, /* 0x3e<<25, opcode = (8bit)<<17 */
GCNENC_VOP1, /* 0x3f<<25, opcode = (8bit)<<9 */
GCNENC_VOP2, /* 0x0<<31, opcode = (6bit)<<25 */
GCNENC_VOP3A, /* 0x34<<26, opcode = (9bit)<<17 */
GCNENC_VOP3B, /* 0x34<<26, opcode = (9bit)<<17 */
GCNENC_VINTRP, /* 0x32<<26, opcode = (2bit)<<16 */
GCNENC_DS, /* 0x36<<26, opcode = (8bit)<<18 */
GCNENC_MUBUF, /* 0x38<<26, opcode = (7bit)<<18 */
GCNENC_MTBUF, /* 0x3a<<26, opcode = (3bit)<<16 */
GCNENC_MIMG, /* 0x3c<<26, opcode = (7bit)<<18 */
GCNENC_EXP, /* 0x3e<<26, opcode = none */
GCNENC_FLAT, /* 0x37<<26, opcode = (8bit)<<18 (???8bit) */
GCNENC_VOP3P,
GCNENC_MAXVAL = GCNENC_FLAT
};
typedef uint32_t GCNInsnMode;
// modes for GCN instructions
enum : GCNInsnMode
{
GCN_STDMODE = 0, /// standard mode
GCN_REG_ALL_64 = 15, /// all register operand is 64-bit
GCN_REG_DST_64 = 1, /// destination is 64-bit
GCN_REG_SRC0_64 = 2, /// source0 is 64-bit
GCN_REG_SRC1_64 = 4, /// source1 is 64-bit
GCN_REG_SRC2_64 = 8, /// source2 is 64-bit
GCN_REG_DS0_64 = 3, /// destination and source0 is 64-bit
GCN_REG_DS1_64 = 5, /// destination and source1 is 64-bit
GCN_REG_DS2_64 = 9, /// destination and source2 is 64-bit
/* SOP */
GCN_IMM_NONE = 0x10, // used in Scall insns
GCN_ARG_NONE = 0x20, /// no arguments (operands)
GCN_DST_NONE = 0x20, /// omit destination argument
GCN_IMM_REL = 0x30, /// SOPK, immediate as relative address
GCN_IMM_LOCKS = 0x40, /// SOPK, s_waitcnt locks
GCN_IMM_MSGS = 0x50, /// SOPK, s_sendmsg* message function
GCN_IMM_SREG = 0x60, /// SOPK, hwreg function
GCN_SRC_NONE = 0x70, /// SOP1, omit source
GCN_DST_SRC = 0x80, /// SOPK, SDST is first source
GCN_IMM_DST = 0x100, /// immediate is first (destplace), destination is second
GCN_SOPK_CONST = 0x200, /// constant instead destination
GCN_SOPK_SRIMM32 = 0x300, /// GCN_IMM_DST and GCN_SOPK_CONST together
/* SOPC */
GCN_SRC1_IMM = 0x10, /// treat source1 as immediate value
/* VOP */
GCN_SRC2_NONE = 0x10, /// omit source2
GCN_DS2_VCC = 0x20, /// extra sgpr or VCC in extra destination and in source2
GCN_SRC12_NONE = 0x30, /// omit source1 and source2
GCN_ARG1_IMM = 0x40, /// immediate value as second operand
GCN_ARG2_IMM = 0x50, /// immediate value as third operand
GCN_S0EQS12 = 0x60, /// source0 must be equal to source1 or source2
GCN_DST_VCC = 0x70, /// extra sgpr or VCC in extra destination
GCN_SRC2_VCC = 0x80, /// sgpr or VCC in source2
GCN_DST_VCC_VSRC2 = 0x90, /// sgpr or VCC as dest operand and source2
GCN_DS1_SGPR = 0xa0, /// destination and source1 is SGPR
GCN_SRC1_SGPR = 0xb0, /// source1 is SGPR
GCN_DST_SGPR = 0xc0, /// destination is SGPR
GCN_VOP_ARG_NONE = 0xd0, /// no argument for VOP encodings
GCN_NEW_OPCODE = 0xe0, /// unique opcode that doesn't exists in VINTRP
GCN_P0_P10_P20 = 0xf0, /// second operand is param (P0,P10,P20)
GCN_VOPC_NOVCC = 0x80, /// no VCC in VOPC instruction
GCN_VOP3_VOP2 = 0x100, /// VOP2 encoded as VOP3
GCN_VOP3_VOP1 = 0x200, /// VOP1 encoded as VOP3
GCN_VOP3_VINTRP = 0x300, /// VINTRP encoded as VOP3
GCN_VOP3_DS2_128 = 0x7000, /// VOP3 with DST 128 and SRC2 128
GCN_VOP3_VINTRP_NEW = 0x3e0, /// new VINTRP instructions encoded as VOP3
GCN_VOP3_VOP2_DS01 = 0x110, /// VOP2 in VOP3, destination and two sources
GCN_VOP3_VOP1_DS0 = 0x230, /// VOP1 in VOP3, destination and one source
GCN_VOP3_DST_SGPR = 0x400, /// VOPX in VOP3, and dst is SGPR (if mask1 used)
GCN_VOP3_SRC1_SGPR = 0x800, /// VOPX in VOP3, and src1 is SGPR (if mask1 used)
GCN_VOP3_DS1_SGPR = 0xc00, /// VOPX in VOP3, and dst and src1 is SGPR (if mask1 used)
GCN_VOP3_MASK2 = 0x8300, // mask for VOPx in VOP2 encodings
GCN_VINTRP_SRC2 = 0x1000, /// VOP3/VINTRP with source2 (third source)
GCN_VOP3_MASK3 = 0x3000, /// mask for VINTRP in VOP2 encodings
GCN_VOP3_VOP3P = 0x8000, /// VOP3P encoding
GCN_VOP3_NODST = 0x1000000, /// VOP3 - no DST
GCN_VOP_NODPP = 0x10000, /// VOP instruction can not have DPP
GCN_VOP_NOSDWA = 0x20000, /// VOP instruction can not have SDWA
GCN_VOP_NODPPSDWA = 0x30000, /// VOP instruction can not have DPP and SDWA
GCN_VOP_NOSDWAVEGA = 0x40000, /// VOP instruction can not have SDWA for VEGA
GCN_VCC_IMPL_READ = 0x80000, /// VOP instruction with implicit VCC read
GCN_VCC_IMPL_WRITE = 0x100000, /// VOP instruction with implicit VCC write
GCN_VOP_NOWVSZ = 0x200000, // VOP SDST/VCC size doesn't not depend on wavesize
// DS encoding modes
GCN_ADDR_STD = 0x0, /// standard place of address
GCN_ADDR_DST = 0x10, /// address operand in destination place
GCN_ADDR_SRC = 0x20, /// address operand in source
GCN_ADDR_DST64 = 0x1f, /// address operand in destination place and all is 64-bit
GCN_ADDR_SRC64 = 0x2f, /// address operand in source and all is 64-bit
GCN_ADDR_SRC_D64 = 0x21, /// address operand in source and dest is bit (not addr)
GCN_2OFFSETS = 0x100, /// two 8-bit offsets
GCN_VDATA2 = 0x140, /* two datas, two offsets */
GCN_NOSRC = 0x80, /* only address, no source */
GCN_2SRCS = 0x40, // two (source or dest) datas (DATA), DATA1)
GCN_NOSRC_2OFF = 0x180, /* only address */
GCN_SRC_ADDR2 = 0x200, /// for ds_XXX_src2_XXX instructions
GCN_SRC_ADDR2_64 = 0x20f, /// for ds_XXX_src2_XXX instructions, all is 64-bit
GCN_DS_96 = 0x800, /// 96-bit dest and source
GCN_DS_128 = 0x1000, /// 128-bit dest and source
GCN_ONLYDST = 0x400, /* only vdst */
GCN_DSMASK = 0x3f0, // mask for DS encoding
GCN_DSMASK2 = 0x3c0, // mask for DS encoding 2
GCN_SRCS_MASK = 0xc0, /// only srcs mask
GCN_ONLYGDS = 0x2000, /// instruction must have GDS
GCN_DST128 = 0x4000, // dest is 128-bit
GCN_ONLY_SRC = 0x8000,
// others
GCN_SBASE4 = 0x10, /// SBASE requires 4 registers
GCN_FLOATLIT = 0x40000000U, /// float literal
GCN_F16LIT = 0x80000000U, /// half literal
GCN_LITMASK = 0xc0000000U,
GCN_SMRD_ONLYDST = 0x30, // only destination (no other operands)
GCN_SMEM_SDATA_IMM = 0x40, // treat SDATA as immediate
GCN_SMEM_NOSDATA = 0x80, // no destination
GCN_MEMOP_MX1 = 0x0, /// sdst/sdata requires 1 register
GCN_MEMOP_MX2 = 0x100, /// sdst/sdata requires 2 registers
GCN_MEMOP_MX4 = 0x200, /// sdst/sdata requires 4 registers
GCN_MEMOP_MX8 = 0x300, /// sdst/sdata requires 8 registers
GCN_MEMOP_MX16 = 0x400, /// sdst/sdata requires 16 registers
GCN_MUBUF_X = 0x0, /// vdata requires 1 register
GCN_MUBUF_NOVAD = 0x10, // no vaddr and vdata
GCN_MUBUF_XY = 0x100, /// vdata requires 2 registers
GCN_MUBUF_XYZ = 0x200, /// vdata requires 3 registers
GCN_MUBUF_XYZW = 0x300, /// vdata requires 4 registers
GCN_MUBUF_MX1 = 0x0, /// vdata requires 1 register
GCN_MUBUF_MX2 = 0x100, /// vdata requires 2 registers
GCN_MUBUF_MX3 = 0x200, /// vdata requires 3 registers
GCN_MUBUF_MX4 = 0x300, /// vdata requires 4 registers
GCN_MUBUF_D16 = 0x800, /// vdata size depends on arch (GCN 1.4 - half of size)
GCN_MUBUF_X_D16 = 0x800, /// vdata requires 1 register
GCN_MUBUF_XY_D16 = 0x900, /// vdata requires 2 registers (or 1 for GCN 1.4)
GCN_MUBUF_XYZ_D16 = 0xa00, /// vdata requires 3 registers (or 2 for GCN 1.4)
GCN_MUBUF_XYZW_D16 = 0xb00, /// vdata requires 4 registers (or 2 for GCN 1.4)
GCN_MIMG_SAMPLE = 0x100, /// last operand is SSAMP (4 sregisters)
GCN_MIMG_VDATA4 = 0x200, /// gather requires 4 vdata registers
GCN_MIMG_GATHER = 0x300, /// gather requires 4 vdata registers
GCN_MIMG_VAGE1 = 0x0, /// vaddr requires 1 or more registers
GCN_MIMG_VAGE2 = 0x1, /// vaddr requires 2 or more registers
GCN_MIMG_VAGE3 = 0x2, /// vaddr requires 3 or more registers
GCN_MIMG_VAGE4 = 0x3, /// vaddr requires 4 or more registers
GCN_MIMG_VAGE5 = 0x4, /// vaddr requires 5 or more registers
GCN_MIMG_VAGE6 = 0x5, /// vaddr requires 6 or more registers
GCN_MIMG_VADERIV = 0x10, /// vaddr holds user derivatives
GCN_MIMG_VAGE2D = 0x11, /// vaddr requires 2 or more registers and holds user derivs
GCN_MIMG_VAGE3D = 0x12, /// vaddr requires 3 or more registers and holds user derivs
GCN_MIMG_VAGE4D = 0x13, /// vaddr requires 4 or more registers and holds user derivs
GCN_MIMG_VAGE5D = 0x14, /// vaddr requires 5 or more registers and holds user derivs
GCN_MIMG_VAGE6D = 0x15, /// vaddr requires 6 or more registers and holds user derivs
GCN_MIMG_VA_O = 0x20, // vaddr *O*
GCN_MIMG_VA_B = 0x40, // vaddr *B*
GCN_MIMG_VA_C = 0x80, // vaddr *C*
GCN_MIMG_VA_L = 0x400, // vaddr *L*
GCN_MIMG_VA_CL = 0x800, // vaddr *CL*
GCN_MIMG_VA_B_O = GCN_MIMG_VA_B|GCN_MIMG_VA_O, // vaddr *B* and *O*
GCN_MIMG_VA_B_CL = GCN_MIMG_VA_B|GCN_MIMG_VA_CL, // vaddr *B* and *CL*
// vaddr *B* and *CL* and *O*
GCN_MIMG_VA_B_CL_O = GCN_MIMG_VA_B|GCN_MIMG_VA_CL|GCN_MIMG_VA_O,
GCN_MIMG_VA_C_B = GCN_MIMG_VA_C|GCN_MIMG_VA_B, // vaddr *C* and *B*
// vaddr *C* and *B* and *CL*
GCN_MIMG_VA_C_B_CL = GCN_MIMG_VA_C|GCN_MIMG_VA_B|GCN_MIMG_VA_CL,
GCN_MIMG_VA_L_O = GCN_MIMG_VA_L|GCN_MIMG_VA_O, // vaddr *L* anc *O*
GCN_MIMG_VA_C_L = GCN_MIMG_VA_C|GCN_MIMG_VA_L, // vaddr *C* and *L*
GCN_MIMG_VA_C_CL = GCN_MIMG_VA_C|GCN_MIMG_VA_CL, // vaddr *C* and *CLL*
GCN_MIMG_VA_C_O = GCN_MIMG_VA_C|GCN_MIMG_VA_O, // vaddr *C* and *O*
GCN_MIMG_VA_CL_O = GCN_MIMG_VA_CL|GCN_MIMG_VA_O, // vaddr *CL* and *O*
// vaddr *C* and *CL* and *O*
GCN_MIMG_VA_C_CL_O = GCN_MIMG_VA_C|GCN_MIMG_VA_CL|GCN_MIMG_VA_O,
// vaddr *C* and *CL* and *B* and *O*
GCN_MIMG_VA_C_B_CL_O = GCN_MIMG_VA_C|GCN_MIMG_VA_CL|GCN_MIMG_VA_B|GCN_MIMG_VA_O,
// vaddr *C* and *L* and *O*
GCN_MIMG_VA_C_L_O = GCN_MIMG_VA_C|GCN_MIMG_VA_L|GCN_MIMG_VA_O,
// vaddr *C* and *B* and *O*
GCN_MIMG_VA_C_B_O = GCN_MIMG_VA_C|GCN_MIMG_VA_B|GCN_MIMG_VA_O,
GCN_MIMG_VA_MIP = 0x10000, // vaddr _MIP
GCN_MIMG_VA_MASK = 0xf,
GCN_MLOAD = 0x1000, // instruction load data to vgprs
GCN_MATOMIC = 0x2000, // instruction perform atomics and returns data if glc==1
GCN_MHALFWRITE = 0x4000,
GCN_MCMPSWAP = 0x6000,
GCN_FLAT_DDST = 0x00, // destination as first operand
GCN_FLAT_ADST = 0x10, /// first address, second is DST
GCN_FLAT_NODATA = 0x20, /// omit DATA
GCN_FLAT_NODST = 0x40, /// omit DST
GCN_FLAT_STORE = 0x50, /// store instruction
GCN_CMPSWAP = 0x80, /// ???
GCN_ACMPSWAP = 0x6080, /// ???
GCN_FLAT_FLAT = 0,
GCN_FLAT_SCRATCH = 1,
GCN_FLAT_GLOBAL = 2,
GCN_FLAT_MODEMASK = 7,
GCN_MASK1 = 0xf0,
GCN_MASK2 = 0xf00,
GCN_DSIZE_MASK = 0x700, /// dsize mask
GCN_SHIFT2 = 8
};
struct CLRX_INTERNAL GCNInstruction
{
const char* mnemonic;
cxbyte encoding;
GCNInsnMode mode;
uint16_t code;
GPUArchMask archMask; // mask of architectures whose have instruction
};
// version GCNInstruction for assembler (with two code: for VOPX and VOP3)
struct CLRX_INTERNAL GCNAsmInstruction
{
const char* mnemonic;
cxbyte encoding;
GCNInsnMode mode;
uint16_t code1, code2; // code1 - first code, code2 - VOP3 encoding code
GPUArchMask archMask; // mask of architectures whose have instruction
};
CLRX_INTERNAL extern const GCNInstruction gcnInstrsTable[];
};
#endif

344
Tools/ParseOpcode.py Normal file
View file

@ -0,0 +1,344 @@
import re
ArchDic = {
'ARCH_SOUTHERN_ISLANDS': 1,
'ARCH_SEA_ISLANDS': 2,
'ARCH_VOLCANIC_ISLANDS': 4,
'ARCH_HD7X00': 1,
'ARCH_RX2X0': 2,
'ARCH_RX3X0': 4,
'ARCH_RXVEGA': 8,
'ARCH_VEGA20': 16,
'ARCH_NAVI': 32,
'ARCH_NAVI_DL': 64,
'ARCH_GCN_1_0_1': 0x3,
'ARCH_GCN_1_1_2': 0x6,
'ARCH_GCN_1_1_5': 0x62,
'ARCH_GCN_1_1_2_4': 0x1e,
'ARCH_GCN_1_0_1_2_4': 0x1f,
'ARCH_GCN_1_2_4': 0x1c,
'ARCH_GCN_1_4': 0x18,
'ARCH_GCN_1_5': 0x60,
'ARCH_GCN_1_5_1': 0x40,
'ARCH_GCN_1_1_2_4_5': 0x7e,
'ARCH_GCN_1_2_4_5': 0x7c,
'ARCH_GCN_1_4_5': 0x78,
'ARCH_GCN_1_0_1_5': 0x63,
'ARCH_GCN_ALL': 0xfff,
}
TypeDic = {
'TypeNone' : 'Undefined',
'TypeB8' : 'B8',
'TypeB16' : 'B16',
'TypeB32' : 'B32',
'TypeB64' : 'B64',
'TypeB96' : 'B96',
'TypeB128' : 'B128',
'TypeF16' : 'F16',
'TypeF32' : 'F32',
'TypeF64' : 'F64',
'TypeU8' : 'U8',
'TypeU16' : 'U16',
'TypeU24' : 'U24',
'TypeU32' : 'U32',
'TypeU64' : 'U64',
'TypeI4' : 'I4',
'TypeI8' : 'I8',
'TypeI16' : 'I16',
'TypeI24' : 'I24',
'TypeI32' : 'I32',
'TypeI64' : 'I64',
'TypeF16F32' : 'F16',
'TypeF32F16' : 'F32',
'TypeF32F64' : 'F32',
'TypeF32I32' : 'F32',
'TypeF32I4' : 'F32',
'TypeF32U32' : 'F32',
'TypeF64F32' : 'F64',
'TypeF64I32' : 'F64',
'TypeF64U32' : 'F64',
'TypeI16F32' : 'I16',
'TypeI16I32' : 'I16',
'TypeI32B32' : 'I32',
'TypeI32B64' : 'I32',
'TypeI32F32' : 'I32',
'TypeI32F64' : 'I32',
'TypeI32I16' : 'I32',
'TypeI32I24' : 'I32',
'TypeI32I64' : 'I32',
'TypeI32I8' : 'I32',
'TypeU16F32' : 'U16',
'TypeU16U32' : 'U16',
'TypeU32B32' : 'U32',
'TypeU32F32' : 'U32',
'TypeU32F64' : 'U32',
'TypeU32U24' : 'U32',
'TypeU8F32' : 'U8',
}
DefaultSrcCountTable = {
'GCNENC_SOPC' : 2,
'GCNENC_SOPP' : 0,
'GCNENC_SOP1' : 1,
'GCNENC_SOP2' : 2,
'GCNENC_SOPK' : 0,
'GCNENC_SMRD' : 1,
'GCNENC_VOPC' : 2,
'GCNENC_VOP1' : 1,
'GCNENC_VOP2' : 2,
'GCNENC_VOP3' : 3,
'GCNENC_VINTRP' : 1,
'GCNENC_DS' : 3,
'GCNENC_MUBUF' : 4,
'GCNENC_MTBUF' : 4,
'GCNENC_MIMG' : 4,
'GCNENC_EXP' : 4,
}
ARCH_SOUTHERN_ISLANDS = 1
ARCH_SEA_ISLANDS = 2
InstDefDic = {}
def WriteList(encoding, op_list):
dst_name = encoding + '.txt'
with open(dst_name, 'w') as dst:
for opcode, value, mode in op_list:
line = '{}\t{}\t{}\n'.format(opcode, value, mode)
dst.write(line)
def IsOpInList(op_name, op_list):
for opcode, value, mode in op_list:
if opcode == op_name:
return True
return False
def FindUniqueVOP3(op_dic, vop3_list):
unique_vop3 = []
for opcode, value, mode in vop3_list:
if IsOpInList(opcode, op_dic['GCNENC_VOP1']):
continue
if IsOpInList(opcode, op_dic['GCNENC_VOP2']):
continue
if IsOpInList(opcode, op_dic['GCNENC_VOPC']):
continue
unique_vop3.append((opcode, value, mode))
return unique_vop3
def GetOpKey(encoding, op_name):
key_name = op_name
if encoding == 'GCNENC_VOP3':
key_name = 'V3_' + op_name[2:]
return key_name
def GetMaxOpValue(op_list):
count = len(op_list)
last = op_list[count - 1]
return last[1]
def GetMaxOpName(op_list):
count = len(op_list)
last = op_list[count - 1]
return last[0]
def GetArraySize(op_list):
return GetMaxOpValue(op_list) + 1
def ParseOpType(op_name):
src_type = 'Undefined'
dst_type = src_type
dual_type_pat = re.compile('_(\S\d+)_(\S\d+)$')
single_type_pat = re.compile('_(\S\d+)$')
m = dual_type_pat.search(op_name)
if m:
dst_type = m.group(1)
src_type = m.group(2)
# VINTRP special
if 'P' in dst_type:
dst_type = src_type
else:
m = single_type_pat.search(op_name)
if m:
src_type = m.group(1)
if 'X' in src_type:
src_type = 'Undefined'
dst_type = src_type
return dst_type, src_type
def WriteOpFormat(op_dic):
dst = open('OpFormat.cpp', 'w')
for encoding, op_list in op_dic.items():
array_size = GetArraySize(op_list)
dst.write('const std::array<GcnInstFormat, {}> g_instructionFormat{} = {{{{\n'.format(array_size, encoding.replace('GCNENC_', '')))
struct_array = ['\t{ },'] * array_size
for opcode, value, mode in op_list:
op_key = GetOpKey(encoding, opcode)
if not op_key in InstDefDic:
#print('inst not found: {}'.format(op_key))
continue
inst_info = InstDefDic[op_key]
cls = inst_info[0]
src_count = DefaultSrcCountTable[encoding]
dst_type, src_type = ParseOpType(opcode)
if 'GCN_SRC_NONE' in mode:
src_count = 0
if 'GCN_SRC2_NONE' in mode:
src_count -= 1
if 'GCN_VOP3_VOP2_DS01' in mode:
src_count = 2
if 'GCN_VOP3_VOP1_DS0' in mode:
src_count = 1
if 'GCN_VOP_ARG_NONE' in mode:
src_count = 0
# VOPC in VOP3, specify 2 src operands
if encoding == 'GCNENC_VOP3' and (value >= 0 and value <= 247):
src_count = 2
code_line = '\t// {} = {}\n\t{{ GcnInstClass::{}, {}, {},\n\t\tGcnNumericType::{}, GcnNumericType::{} }},'.\
format(value, opcode, cls, src_count, 1, src_type, dst_type)
struct_array[value] = code_line
dst.write('\n'.join(struct_array))
dst.write('\n}};\n\n\n')
dst.close()
def WriteOpEnum(op_dic):
dst = open('OpEnum.cpp', 'w')
for encoding, op_list in op_dic.items():
encoding_short = encoding.replace('GCNENC_', '')
dst.write('enum class GcnOpcode{} : uint32_t\n'.format(encoding_short))
dst.write('{\n')
for opcode, value, mode in op_list:
dst.write('\t{} = {},\n'.format(opcode, value))
max_op = GetMaxOpName(op_list)
dst.write('\n\tOP_RANGE_{} = {} + 1,\n'.format(encoding_short, max_op))
dst.write('};\n\n')
dst.close()
def ProcessOpcodes(op_list):
print('opcode count:{}'.format(len(op_list)))
op_dic = {}
for opcode, encoding, mode, value in op_list:
if not encoding in op_dic:
op_dic[encoding] = []
op_dic[encoding].append((opcode, value, mode))
def takeOpValue(element):
return element[1]
for value in op_dic.values():
value.sort(key=takeOpValue)
WriteOpFormat(op_dic)
WriteOpEnum(op_dic)
for encoding, lst in op_dic.items():
WriteList(encoding, lst)
vop3_list = op_dic['GCNENC_VOP3']
unique_vop3 = FindUniqueVOP3(op_dic, vop3_list)
WriteList('VOP3_UNIQUE', unique_vop3)
def ParseInstDefs(src_name):
src = open(src_name)
pat = re.compile('\{(.*),.*\{(.*),(.*)\}.*\}')
for line in src.readlines():
if not line:
continue
line = line.rstrip('\n')
m = pat.search(line)
if not m:
continue
op = m.group(1).strip().split('::')[1]
cls = m.group(2).strip().split('::')[1]
type = m.group(3).strip().split('::')[1]
type = TypeDic[type]
if op in InstDefDic:
input('already in dic:' + op)
InstDefDic[op] = (cls, type)
src.close()
# use
# ^ (?!\{).*\},
# in sublime to merge lines
def main():
src = open('GCNInstructions.cpp')
op_list = []
pat = re.compile('\{.*\}')
for line in src.readlines():
line = line.rstrip('\n')
if not line:
continue
m = pat.search(line)
if not m:
continue
line = line.replace('{', '').replace('}', '')
parts = line.split(',')
parts = [x.strip() for x in parts if x]
opcode = parts[0].replace('"', '').upper()
encoding = parts[1]
if encoding == 'GCNENC_VOP3A' or encoding == 'GCNENC_VOP3B':
encoding = 'GCNENC_VOP3'
mode = parts[2]
value_str = parts[3]
if 'x' in value_str:
op_value = int(parts[3], 16)
else:
op_value = int(parts[3])
arch = parts[4]
arch_mask = ArchDic[arch]
if not (arch_mask & ARCH_SEA_ISLANDS):
continue
op_list.append((opcode.upper(), encoding, mode, op_value))
src.close()
ParseInstDefs('GCNInstructionDefs.cpp')
ProcessOpcodes(op_list)
if __name__ == '__main__':
main()