// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include "common/assert.h" #include "shader_recompiler/frontend/decode.h" namespace Shader::Gcn { namespace bit { template T extract(T value, u32 lst, u32 fst) { return (value >> fst) & ~(~T(0) << (lst - fst + 1)); } } // namespace bit InstEncoding GetInstructionEncoding(u32 token) { auto encoding = static_cast(token & (u32)EncodingMask::MASK_9bit); switch (encoding) { case InstEncoding::SOP1: case InstEncoding::SOPP: case InstEncoding::SOPC: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_7bit); switch (encoding) { case InstEncoding::VOP1: case InstEncoding::VOPC: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_6bit); switch (encoding) { case InstEncoding::VOP3: case InstEncoding::EXP: case InstEncoding::VINTRP: case InstEncoding::DS: case InstEncoding::MUBUF: case InstEncoding::MTBUF: case InstEncoding::MIMG: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_5bit); switch (encoding) { case InstEncoding::SMRD: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_4bit); switch (encoding) { case InstEncoding::SOPK: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_2bit); switch (encoding) { case InstEncoding::SOP2: return encoding; default: break; } encoding = static_cast(token & (u32)EncodingMask::MASK_1bit); switch (encoding) { case InstEncoding::VOP2: return encoding; default: break; } UNREACHABLE(); return InstEncoding::ILLEGAL; } bool HasAdditionalLiteral(InstEncoding encoding, Opcode opcode) { switch (encoding) { case InstEncoding::SOPK: { return opcode == Opcode::S_SETREG_IMM32_B32; } case InstEncoding::VOP2: { return opcode == Opcode::V_MADMK_F32 || opcode == Opcode::V_MADAK_F32; } default: return false; } } bool IsVop3BEncoding(Opcode opcode) { return opcode == Opcode::V_ADD_I32 || opcode == Opcode::V_ADDC_U32 || opcode == Opcode::V_SUB_I32 || opcode == Opcode::V_SUBB_U32 || opcode == Opcode::V_SUBREV_I32 || opcode == Opcode::V_SUBBREV_U32 || opcode == Opcode::V_DIV_SCALE_F32 || opcode == Opcode::V_DIV_SCALE_F64 || opcode == Opcode::V_MAD_U64_U32 || opcode == Opcode::V_MAD_I64_I32; } GcnInst GcnDecodeContext::decodeInstruction(GcnCodeSlice& code) { const uint32_t token = code.at(0); InstEncoding encoding = GetInstructionEncoding(token); ASSERT_MSG(encoding != InstEncoding::ILLEGAL, "illegal encoding"); uint32_t encodingLen = getEncodingLength(encoding); // Clear the instruction m_instruction = GcnInst(); // 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(u32)) { decodeLiteralConstant(encoding, code); } repairOperandType(); return m_instruction; } uint32_t GcnDecodeContext::getEncodingLength(InstEncoding encoding) { uint32_t instLength = 0; switch (encoding) { case InstEncoding::SOP1: case InstEncoding::SOPP: case InstEncoding::SOPC: case InstEncoding::SOPK: case InstEncoding::SOP2: case InstEncoding::VOP1: case InstEncoding::VOPC: case InstEncoding::VOP2: case InstEncoding::SMRD: case InstEncoding::VINTRP: instLength = sizeof(uint32_t); break; case InstEncoding::VOP3: case InstEncoding::MUBUF: case InstEncoding::MTBUF: case InstEncoding::MIMG: case InstEncoding::DS: case InstEncoding::EXP: instLength = sizeof(uint64_t); break; } return instLength; } uint32_t GcnDecodeContext::getOpMapOffset(InstEncoding encoding) { uint32_t offset = 0; switch (encoding) { case InstEncoding::SOP1: offset = (uint32_t)OpcodeMap::OP_MAP_SOP1; break; case InstEncoding::SOPP: offset = (uint32_t)OpcodeMap::OP_MAP_SOPP; break; case InstEncoding::SOPC: offset = (uint32_t)OpcodeMap::OP_MAP_SOPC; break; case InstEncoding::VOP1: offset = (uint32_t)OpcodeMap::OP_MAP_VOP1; break; case InstEncoding::VOPC: offset = (uint32_t)OpcodeMap::OP_MAP_VOPC; break; case InstEncoding::VOP3: offset = (uint32_t)OpcodeMap::OP_MAP_VOP3; break; case InstEncoding::EXP: offset = (uint32_t)OpcodeMap::OP_MAP_EXP; break; case InstEncoding::VINTRP: offset = (uint32_t)OpcodeMap::OP_MAP_VINTRP; break; case InstEncoding::DS: offset = (uint32_t)OpcodeMap::OP_MAP_DS; break; case InstEncoding::MUBUF: offset = (uint32_t)OpcodeMap::OP_MAP_MUBUF; break; case InstEncoding::MTBUF: offset = (uint32_t)OpcodeMap::OP_MAP_MTBUF; break; case InstEncoding::MIMG: offset = (uint32_t)OpcodeMap::OP_MAP_MIMG; break; case InstEncoding::SMRD: offset = (uint32_t)OpcodeMap::OP_MAP_SMRD; break; case InstEncoding::SOPK: offset = (uint32_t)OpcodeMap::OP_MAP_SOPK; break; case InstEncoding::SOP2: offset = (uint32_t)OpcodeMap::OP_MAP_SOP2; break; case InstEncoding::VOP2: offset = (uint32_t)OpcodeMap::OP_MAP_VOP2; break; } return offset; } uint32_t GcnDecodeContext::mapEncodingOp(InstEncoding encoding, Opcode opcode) { // Map from uniform opcode to encoding specific opcode. uint32_t encodingOp = 0; if (encoding == InstEncoding::VOP3) { if (opcode >= Opcode::V_CMP_F_F32 && opcode <= Opcode::V_CMPX_T_U64) { uint32_t op = static_cast(opcode) - static_cast(OpcodeMap::OP_MAP_VOPC); encodingOp = op + static_cast(OpMapVOP3VOPX::VOP3_TO_VOPC); } else if (opcode >= Opcode::V_CNDMASK_B32 && opcode <= Opcode::V_CVT_PK_I16_I32) { uint32_t op = static_cast(opcode) - static_cast(OpcodeMap::OP_MAP_VOP2); encodingOp = op + static_cast(OpMapVOP3VOPX::VOP3_TO_VOP2); } else if (opcode >= Opcode::V_NOP && opcode <= Opcode::V_MOVRELSD_B32) { uint32_t op = static_cast(opcode) - static_cast(OpcodeMap::OP_MAP_VOP1); encodingOp = op + static_cast(OpMapVOP3VOPX::VOP3_TO_VOP1); } else { encodingOp = static_cast(opcode) - static_cast(OpcodeMap::OP_MAP_VOP3); } } else { uint32_t mapOffset = getOpMapOffset(encoding); encodingOp = static_cast(opcode) - mapOffset; } return encodingOp; } void GcnDecodeContext::updateInstructionMeta(InstEncoding encoding) { uint32_t encodingOp = mapEncodingOp(encoding, m_instruction.opcode); InstFormat instFormat = InstructionFormat(encoding, encodingOp); ASSERT_MSG(instFormat.src_type != ScalarType::Undefined && instFormat.dst_type != ScalarType::Undefined, "TODO: Instruction format table not complete, please fix it manually."); m_instruction.inst_class = instFormat.inst_class; m_instruction.category = instFormat.inst_category; m_instruction.encoding = encoding; m_instruction.src_count = instFormat.src_count; m_instruction.length = getEncodingLength(encoding); // Update src operand scalar type. auto setOperandType = [&instFormat](InstOperand& src) { // Only update uninitialized numeric type. if (src.type == ScalarType::Undefined) { src.type = instFormat.src_type; } }; std::for_each_n(m_instruction.src.begin(), m_instruction.src_count, setOperandType); // Update dst operand scalar type. switch (m_instruction.dst_count) { case 2: { if (m_instruction.dst[1].type == ScalarType::Undefined) { // Only VOP3B has an additional sdst operand, // and it must be Uint64 m_instruction.dst[1].type = ScalarType::Uint64; } } [[fallthrough]]; case 1: { if (m_instruction.dst[0].type == ScalarType::Undefined) { m_instruction.dst[0].type = instFormat.dst_type; } } } } void GcnDecodeContext::repairOperandType() { // Some instructions' operand type is not uniform, // it's best to change the instruction table's format and fix them there, // but it's a hard work. // We fix them here. switch (m_instruction.opcode) { case Opcode::V_MAD_U64_U32: m_instruction.src[2].type = ScalarType::Uint64; break; case Opcode::V_MAD_I64_I32: m_instruction.src[2].type = ScalarType::Sint64; break; case Opcode::V_ADDC_U32: m_instruction.src[2].type = ScalarType::Uint64; break; case Opcode::IMAGE_GATHER4_C: m_instruction.src[0].type = ScalarType::Any; break; } } OperandField GcnDecodeContext::getOperandField(uint32_t code) { OperandField field = {}; if (code >= ScalarGPRMin && code <= ScalarGPRMax) { field = OperandField::ScalarGPR; } else if (code >= SignedConstIntPosMin && code <= SignedConstIntPosMax) { field = OperandField::SignedConstIntPos; } else if (code >= SignedConstIntNegMin && code <= SignedConstIntNegMax) { field = OperandField::SignedConstIntNeg; } else if (code >= VectorGPRMin && code <= VectorGPRMax) { field = OperandField::VectorGPR; } else { field = static_cast(code); } return field; } void GcnDecodeContext::decodeInstruction32(InstEncoding encoding, GcnCodeSlice& code) { u32 hexInstruction = code.readu32(); switch (encoding) { case InstEncoding::SOP1: decodeInstructionSOP1(hexInstruction); break; case InstEncoding::SOPP: decodeInstructionSOPP(hexInstruction); break; case InstEncoding::SOPC: decodeInstructionSOPC(hexInstruction); break; case InstEncoding::SOPK: decodeInstructionSOPK(hexInstruction); break; case InstEncoding::SOP2: decodeInstructionSOP2(hexInstruction); break; case InstEncoding::VOP1: decodeInstructionVOP1(hexInstruction); break; case InstEncoding::VOPC: decodeInstructionVOPC(hexInstruction); break; case InstEncoding::VOP2: decodeInstructionVOP2(hexInstruction); break; case InstEncoding::SMRD: decodeInstructionSMRD(hexInstruction); break; case InstEncoding::VINTRP: decodeInstructionVINTRP(hexInstruction); break; } } void GcnDecodeContext::decodeInstruction64(InstEncoding encoding, GcnCodeSlice& code) { uint64_t hexInstruction = code.readu64(); switch (encoding) { case InstEncoding::VOP3: decodeInstructionVOP3(hexInstruction); break; case InstEncoding::MUBUF: decodeInstructionMUBUF(hexInstruction); break; case InstEncoding::MTBUF: decodeInstructionMTBUF(hexInstruction); break; case InstEncoding::MIMG: decodeInstructionMIMG(hexInstruction); break; case InstEncoding::DS: decodeInstructionDS(hexInstruction); break; case InstEncoding::EXP: decodeInstructionEXP(hexInstruction); break; } } void GcnDecodeContext::decodeLiteralConstant(InstEncoding encoding, GcnCodeSlice& code) { if (HasAdditionalLiteral(encoding, m_instruction.opcode)) { u32 encoding_op = mapEncodingOp(encoding, m_instruction.opcode); InstFormat instFormat = InstructionFormat(encoding, encoding_op); m_instruction.src[m_instruction.src_count].field = OperandField::LiteralConst; m_instruction.src[m_instruction.src_count].type = instFormat.src_type; m_instruction.src[m_instruction.src_count].code = code.readu32(); ++m_instruction.src_count; m_instruction.length += sizeof(u32); return; } // Find if the instruction contains a literal constant const auto it = std::ranges::find_if(m_instruction.src, [](InstOperand& src) { return src.field == OperandField::LiteralConst; }); if (it != m_instruction.src.end()) { it->code = code.readu32(); m_instruction.length += sizeof(u32); } } void GcnDecodeContext::decodeInstructionSOP1(u32 hexInstruction) { u32 ssrc0 = bit::extract(hexInstruction, 7, 0); u32 op = bit::extract(hexInstruction, 15, 8); u32 sdst = bit::extract(hexInstruction, 22, 16); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::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.dst_count = 1; } void GcnDecodeContext::decodeInstructionSOPP(u32 hexInstruction) { u32 op = bit::extract(hexInstruction, 22, 16); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_SOPP)); m_instruction.control.sopp = *reinterpret_cast(&hexInstruction); } void GcnDecodeContext::decodeInstructionSOPC(u32 hexInstruction) { u32 ssrc0 = bit::extract(hexInstruction, 7, 0); u32 ssrc1 = bit::extract(hexInstruction, 15, 8); u32 op = bit::extract(hexInstruction, 22, 16); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::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(u32 hexInstruction) { u32 sdst = bit::extract(hexInstruction, 22, 16); u32 op = bit::extract(hexInstruction, 27, 23); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_SOPK)); m_instruction.dst[0].field = getOperandField(sdst); m_instruction.dst[0].code = sdst; m_instruction.dst_count = 1; m_instruction.control.sopk = *reinterpret_cast(&hexInstruction); } void GcnDecodeContext::decodeInstructionSOP2(u32 hexInstruction) { u32 ssrc0 = bit::extract(hexInstruction, 7, 0); u32 ssrc1 = bit::extract(hexInstruction, 15, 8); u32 sdst = bit::extract(hexInstruction, 22, 16); u32 op = bit::extract(hexInstruction, 29, 23); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::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.dst_count = 1; } void GcnDecodeContext::decodeInstructionVOP1(u32 hexInstruction) { u32 src0 = bit::extract(hexInstruction, 8, 0); u32 op = bit::extract(hexInstruction, 16, 9); u32 vdst = bit::extract(hexInstruction, 24, 17); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_VOP1)); m_instruction.src[0].field = getOperandField(src0); m_instruction.src[0].code = m_instruction.src[0].field == OperandField::VectorGPR ? src0 - VectorGPRMin : src0; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdst; m_instruction.dst_count = 1; OpcodeVOP1 vop1Op = static_cast(op); if (vop1Op == OpcodeVOP1::V_READFIRSTLANE_B32) { m_instruction.dst[1].field = getOperandField(vdst); m_instruction.dst[1].type = ScalarType::Uint32; m_instruction.dst[1].code = vdst; } } void GcnDecodeContext::decodeInstructionVOPC(u32 hexInstruction) { u32 src0 = bit::extract(hexInstruction, 8, 0); u32 vsrc1 = bit::extract(hexInstruction, 16, 9); u32 op = bit::extract(hexInstruction, 24, 17); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_VOPC)); m_instruction.src[0].field = getOperandField(src0); m_instruction.src[0].code = m_instruction.src[0].field == OperandField::VectorGPR ? src0 - VectorGPRMin : src0; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = vsrc1; // VOPC dst is forced to VCC. // In order to be unified with VOP3 encoding, // we store it to dst[1] m_instruction.dst[1].field = OperandField::VccLo; m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = static_cast(OperandField::VccLo); } void GcnDecodeContext::decodeInstructionVOP2(u32 hexInstruction) { u32 src0 = bit::extract(hexInstruction, 8, 0); u32 vsrc1 = bit::extract(hexInstruction, 16, 9); u32 vdst = bit::extract(hexInstruction, 24, 17); u32 op = bit::extract(hexInstruction, 30, 25); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_VOP2)); m_instruction.src[0].field = getOperandField(src0); m_instruction.src[0].code = m_instruction.src[0].field == OperandField::VectorGPR ? src0 - VectorGPRMin : src0; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = vsrc1; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdst; m_instruction.dst_count = 1; OpcodeVOP2 vop2Op = static_cast(op); if (vop2Op == OpcodeVOP2::V_READLANE_B32 || vop2Op == OpcodeVOP2::V_WRITELANE_B32) { // vsrc1 is scalar for lane instructions m_instruction.src[1].field = getOperandField(vsrc1); // dst is sgpr m_instruction.dst[1].field = OperandField::ScalarGPR; m_instruction.dst[1].type = ScalarType::Uint32; m_instruction.dst[1].code = vdst; } else if (IsVop3BEncoding(m_instruction.opcode)) { m_instruction.dst[1].field = OperandField::VccLo; m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = static_cast(OperandField::VccLo); } } void GcnDecodeContext::decodeInstructionSMRD(u32 hexInstruction) { u32 sbase = bit::extract(hexInstruction, 14, 9); u32 sdst = bit::extract(hexInstruction, 21, 15); u32 op = bit::extract(hexInstruction, 26, 22); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_SMRD)); m_instruction.src[0].field = OperandField::ScalarGPR; m_instruction.src[0].code = sbase; m_instruction.dst[0].field = OperandField::ScalarGPR; m_instruction.dst[0].code = sdst; m_instruction.dst_count = 1; m_instruction.control.smrd = *reinterpret_cast(&hexInstruction); if (op <= static_cast(OpcodeSMRD::S_LOAD_DWORDX16)) { m_instruction.control.smrd.count = 1 << op; } else if (op >= static_cast(OpcodeSMRD::S_BUFFER_LOAD_DWORD) && op <= static_cast(OpcodeSMRD::S_BUFFER_LOAD_DWORDX16)) { m_instruction.control.smrd.count = 1 << (op - 8); } if (m_instruction.control.smrd.imm == 0) { u32 code = m_instruction.control.smrd.offset; m_instruction.src[1].field = getOperandField(code); m_instruction.src[1].type = ScalarType::Uint32; m_instruction.src[1].code = code; } } void GcnDecodeContext::decodeInstructionVINTRP(u32 hexInstruction) { u32 vsrc = bit::extract(hexInstruction, 7, 0); u32 op = bit::extract(hexInstruction, 17, 16); u32 vdst = bit::extract(hexInstruction, 25, 18); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_VINTRP)); m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = vsrc; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdst; m_instruction.dst_count = 1; m_instruction.control.vintrp = *reinterpret_cast(&hexInstruction); } void GcnDecodeContext::decodeInstructionVOP3(uint64_t hexInstruction) { u32 vdst = bit::extract(hexInstruction, 7, 0); u32 sdst = bit::extract(hexInstruction, 14, 8); // For VOP3B u32 op = bit::extract(hexInstruction, 25, 17); u32 src0 = bit::extract(hexInstruction, 40, 32); u32 src1 = bit::extract(hexInstruction, 49, 41); u32 src2 = bit::extract(hexInstruction, 58, 50); if (op >= static_cast(OpcodeVOP3::V_CMP_F_F32) && op <= static_cast(OpcodeVOP3::V_CMPX_T_U64)) { // Map from VOP3 to VOPC u32 vopcOp = op - static_cast(OpMapVOP3VOPX::VOP3_TO_VOPC); m_instruction.opcode = static_cast(vopcOp + static_cast(OpcodeMap::OP_MAP_VOPC)); } else if (op >= static_cast(OpcodeVOP3::V_CNDMASK_B32) && op <= static_cast(OpcodeVOP3::V_CVT_PK_I16_I32)) { // Map from VOP3 to VOP2 u32 vop2Op = op - static_cast(OpMapVOP3VOPX::VOP3_TO_VOP2); m_instruction.opcode = static_cast(vop2Op + static_cast(OpcodeMap::OP_MAP_VOP2)); } else if (op >= static_cast(OpcodeVOP3::V_NOP) && op <= static_cast(OpcodeVOP3::V_MOVRELSD_B32)) { // Map from VOP3 to VOP1 u32 vop1Op = op - static_cast(OpMapVOP3VOPX::VOP3_TO_VOP1); m_instruction.opcode = static_cast(vop1Op + static_cast(OpcodeMap::OP_MAP_VOP1)); } else { // VOP3 encoding, do not map. m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_VOP3)); } m_instruction.src[0].field = getOperandField(src0); m_instruction.src[0].code = m_instruction.src[0].field == OperandField::VectorGPR ? src0 - VectorGPRMin : src0; m_instruction.src[1].field = getOperandField(src1); m_instruction.src[1].code = m_instruction.src[1].field == OperandField::VectorGPR ? src1 - VectorGPRMin : src1; m_instruction.src[2].field = getOperandField(src2); m_instruction.src[2].code = m_instruction.src[2].field == OperandField::VectorGPR ? src2 - VectorGPRMin : src2; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdst; OpcodeVOP3 vop3Op = static_cast(op); if (IsVop3BEncoding(m_instruction.opcode)) { m_instruction.dst[1].field = OperandField::ScalarGPR; m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = sdst; } else { if (vop3Op >= OpcodeVOP3::V_CMP_F_F32 && vop3Op <= OpcodeVOP3::V_CMPX_T_U64) { m_instruction.dst[1].field = getOperandField(vdst); m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = vdst; } else if (vop3Op >= OpcodeVOP3::V_READLANE_B32 && vop3Op <= OpcodeVOP3::V_WRITELANE_B32) { // vsrc1 is scalar for lane instructions m_instruction.src[1].field = getOperandField(src1); // dst is sgpr for lane instruction m_instruction.dst[1].field = OperandField::ScalarGPR; m_instruction.dst[1].type = ScalarType::Uint32; m_instruction.dst[1].code = vdst; } } if (op >= static_cast(OpcodeVOP3::V_ADD_I32) && op <= static_cast(OpcodeVOP3::V_DIV_SCALE_F64)) { // VOP3B has a sdst operand. m_instruction.dst_count = 2; } else { m_instruction.dst_count = 1; } m_instruction.control.vop3 = *reinterpret_cast(&hexInstruction); // update input modifier auto& control = m_instruction.control.vop3; for (u32 i = 0; i != 3; ++i) { if (control.abs & (1u << i)) { m_instruction.src[i].input_modifier.abs = true; } if (control.neg & (1u << i)) { m_instruction.src[i].input_modifier.neg = true; } } // update output modifier auto& outputMod = m_instruction.dst[0].output_modifier; outputMod.clamp = static_cast(control.clmp); switch (control.omod) { case 0: outputMod.multiplier = std::numeric_limits::quiet_NaN(); break; case 1: outputMod.multiplier = 2.0f; break; case 2: outputMod.multiplier = 4.0f; break; case 3: outputMod.multiplier = 0.5f; break; } } void GcnDecodeContext::decodeInstructionMUBUF(uint64_t hexInstruction) { u32 op = bit::extract(hexInstruction, 24, 18); u32 vaddr = bit::extract(hexInstruction, 39, 32); u32 vdata = bit::extract(hexInstruction, 47, 40); u32 srsrc = bit::extract(hexInstruction, 52, 48); u32 soffset = bit::extract(hexInstruction, 63, 56); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_MUBUF)); m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = vaddr; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = vdata; m_instruction.src[2].field = OperandField::ScalarGPR; m_instruction.src[2].code = srsrc; m_instruction.src[3].field = getOperandField(soffset); m_instruction.src[3].code = soffset; m_instruction.control.mubuf = *reinterpret_cast(&hexInstruction); if (op >= static_cast(OpcodeMUBUF::BUFFER_LOAD_FORMAT_X) && op <= static_cast(OpcodeMUBUF::BUFFER_LOAD_FORMAT_XYZW)) { m_instruction.control.mubuf.count = op + 1; m_instruction.control.mubuf.size = (op + 1) * sizeof(u32); } else if (op >= static_cast(OpcodeMUBUF::BUFFER_STORE_FORMAT_X) && op <= static_cast(OpcodeMUBUF::BUFFER_STORE_FORMAT_XYZW)) { m_instruction.control.mubuf.count = op - 3; m_instruction.control.mubuf.size = (op - 3) * sizeof(u32); } else if (op >= static_cast(OpcodeMUBUF::BUFFER_LOAD_DWORD) && op <= static_cast(OpcodeMUBUF::BUFFER_LOAD_DWORDX3)) { m_instruction.control.mubuf.count = op == static_cast(OpcodeMUBUF::BUFFER_LOAD_DWORDX3) ? 3 : 1 << (op - 12); m_instruction.control.mubuf.size = m_instruction.control.mubuf.count * sizeof(u32); } else if (op >= static_cast(OpcodeMUBUF::BUFFER_STORE_DWORD) && op <= static_cast(OpcodeMUBUF::BUFFER_STORE_DWORDX3)) { m_instruction.control.mubuf.count = op == static_cast(OpcodeMUBUF::BUFFER_STORE_DWORDX3) ? 3 : 1 << (op - 28); m_instruction.control.mubuf.size = m_instruction.control.mubuf.count * sizeof(u32); } else if (op >= static_cast(OpcodeMUBUF::BUFFER_LOAD_UBYTE) && op <= static_cast(OpcodeMUBUF::BUFFER_LOAD_SSHORT)) { m_instruction.control.mubuf.count = 1; if (op >= static_cast(OpcodeMUBUF::BUFFER_LOAD_UBYTE) && op <= static_cast(OpcodeMUBUF::BUFFER_LOAD_SBYTE)) { m_instruction.control.mubuf.size = 1; } else { m_instruction.control.mubuf.size = 2; } } else if (op >= static_cast(OpcodeMUBUF::BUFFER_STORE_BYTE) && op <= static_cast(OpcodeMUBUF::BUFFER_STORE_SHORT)) { m_instruction.control.mubuf.count = 1; if (op == static_cast(OpcodeMUBUF::BUFFER_STORE_BYTE)) { m_instruction.control.mubuf.size = 1; } else { m_instruction.control.mubuf.size = 2; } } else if (op >= static_cast(OpcodeMUBUF::BUFFER_ATOMIC_SWAP) && op <= static_cast(OpcodeMUBUF::BUFFER_ATOMIC_FMAX)) { m_instruction.control.mubuf.count = 1; m_instruction.control.mubuf.size = sizeof(u32); } else if (op >= static_cast(OpcodeMUBUF::BUFFER_ATOMIC_SWAP_X2) && op <= static_cast(OpcodeMUBUF::BUFFER_ATOMIC_FMAX_X2)) { m_instruction.control.mubuf.count = 2; m_instruction.control.mubuf.size = sizeof(u32) * 2; } } void GcnDecodeContext::decodeInstructionMTBUF(uint64_t hexInstruction) { u32 op = bit::extract(hexInstruction, 18, 16); u32 vaddr = bit::extract(hexInstruction, 39, 32); u32 vdata = bit::extract(hexInstruction, 47, 40); u32 srsrc = bit::extract(hexInstruction, 52, 48); u32 soffset = bit::extract(hexInstruction, 63, 56); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_MTBUF)); m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = vaddr; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = vdata; m_instruction.src[2].field = OperandField::ScalarGPR; m_instruction.src[2].code = srsrc; m_instruction.src[3].field = getOperandField(soffset); m_instruction.src[3].code = soffset; m_instruction.control.mtbuf = *reinterpret_cast(&hexInstruction); if (op >= static_cast(OpcodeMTBUF::TBUFFER_LOAD_FORMAT_X) && op <= static_cast(OpcodeMTBUF::TBUFFER_LOAD_FORMAT_XYZW)) { m_instruction.control.mtbuf.count = op + 1; } else if (op >= static_cast(OpcodeMTBUF::TBUFFER_STORE_FORMAT_X) && op <= static_cast(OpcodeMTBUF::TBUFFER_STORE_FORMAT_XYZW)) { m_instruction.control.mtbuf.count = op - 3; } } u32 GcnDecodeContext::getMimgModifier(Opcode opcode) { MimgModifierFlags flags = {}; switch (opcode) { case Opcode::IMAGE_SAMPLE: break; case Opcode::IMAGE_SAMPLE_CL: flags.set(MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_D: flags.set(MimgModifier::Derivative); break; case Opcode::IMAGE_SAMPLE_D_CL: flags.set(MimgModifier::Derivative, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_L: flags.set(MimgModifier::Lod); break; case Opcode::IMAGE_SAMPLE_B: flags.set(MimgModifier::LodBias); break; case Opcode::IMAGE_SAMPLE_B_CL: flags.set(MimgModifier::LodBias, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_LZ: flags.set(MimgModifier::Level0); break; case Opcode::IMAGE_SAMPLE_C: flags.set(MimgModifier::Pcf); break; case Opcode::IMAGE_SAMPLE_C_CL: flags.set(MimgModifier::Pcf, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_C_D: flags.set(MimgModifier::Pcf, MimgModifier::Derivative); break; case Opcode::IMAGE_SAMPLE_C_D_CL: flags.set(MimgModifier::Pcf, MimgModifier::Derivative, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_C_L: flags.set(MimgModifier::Pcf, MimgModifier::Lod); break; case Opcode::IMAGE_SAMPLE_C_B: flags.set(MimgModifier::Pcf, MimgModifier::LodBias); break; case Opcode::IMAGE_SAMPLE_C_B_CL: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_C_LZ: flags.set(MimgModifier::Pcf, MimgModifier::Level0); break; case Opcode::IMAGE_SAMPLE_O: flags.set(MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_CL_O: flags.set(MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_D_O: flags.set(MimgModifier::Derivative, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_D_CL_O: flags.set(MimgModifier::Derivative, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_L_O: flags.set(MimgModifier::Lod, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_B_O: flags.set(MimgModifier::LodBias, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_B_CL_O: flags.set(MimgModifier::LodBias, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_LZ_O: flags.set(MimgModifier::Level0, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_O: flags.set(MimgModifier::Pcf, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_D_O: flags.set(MimgModifier::Pcf, MimgModifier::Derivative, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_D_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::Derivative, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_L_O: flags.set(MimgModifier::Pcf, MimgModifier::Lod, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_B_O: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_B_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_LZ_O: flags.set(MimgModifier::Pcf, MimgModifier::Level0, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4: break; case Opcode::IMAGE_GATHER4_CL: flags.set(MimgModifier::LodClamp); break; case Opcode::IMAGE_GATHER4_L: flags.set(MimgModifier::Lod); break; case Opcode::IMAGE_GATHER4_B: flags.set(MimgModifier::LodBias); break; case Opcode::IMAGE_GATHER4_B_CL: flags.set(MimgModifier::LodBias, MimgModifier::LodClamp); break; case Opcode::IMAGE_GATHER4_LZ: flags.set(MimgModifier::Level0); break; case Opcode::IMAGE_GATHER4_C: flags.set(MimgModifier::Pcf); break; case Opcode::IMAGE_GATHER4_C_CL: flags.set(MimgModifier::Pcf, MimgModifier::LodClamp); break; case Opcode::IMAGE_GATHER4_C_L: flags.set(MimgModifier::Pcf, MimgModifier::Lod); break; case Opcode::IMAGE_GATHER4_C_B: flags.set(MimgModifier::Pcf, MimgModifier::LodBias); break; case Opcode::IMAGE_GATHER4_C_B_CL: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::LodClamp); break; case Opcode::IMAGE_GATHER4_C_LZ: flags.set(MimgModifier::Pcf, MimgModifier::Level0); break; case Opcode::IMAGE_GATHER4_O: flags.set(MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_CL_O: flags.set(MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_L_O: flags.set(MimgModifier::Lod, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_B_O: flags.set(MimgModifier::LodBias, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_B_CL_O: flags.set(MimgModifier::LodBias, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_LZ_O: flags.set(MimgModifier::Level0, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_O: flags.set(MimgModifier::Pcf, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_L_O: flags.set(MimgModifier::Pcf, MimgModifier::Lod, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_B_O: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_B_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::LodBias, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_GATHER4_C_LZ_O: flags.set(MimgModifier::Pcf, MimgModifier::Level0, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_CD: flags.set(MimgModifier::CoarseDerivative); break; case Opcode::IMAGE_SAMPLE_CD_CL: flags.set(MimgModifier::CoarseDerivative, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_C_CD: flags.set(MimgModifier::Pcf, MimgModifier::CoarseDerivative); break; case Opcode::IMAGE_SAMPLE_C_CD_CL: flags.set(MimgModifier::Pcf, MimgModifier::CoarseDerivative, MimgModifier::LodClamp); break; case Opcode::IMAGE_SAMPLE_CD_O: flags.set(MimgModifier::CoarseDerivative, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_CD_CL_O: flags.set(MimgModifier::CoarseDerivative, MimgModifier::LodClamp, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_CD_O: flags.set(MimgModifier::Pcf, MimgModifier::CoarseDerivative, MimgModifier::Offset); break; case Opcode::IMAGE_SAMPLE_C_CD_CL_O: flags.set(MimgModifier::Pcf, MimgModifier::CoarseDerivative, MimgModifier::LodClamp, MimgModifier::Offset); break; } return flags.raw(); } void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) { u32 op = bit::extract(hexInstruction, 24, 18); u32 vaddr = bit::extract(hexInstruction, 39, 32); u32 vdata = bit::extract(hexInstruction, 47, 40); u32 srsrc = bit::extract(hexInstruction, 52, 48); u32 ssamp = bit::extract(hexInstruction, 57, 53); m_instruction.opcode = static_cast(op + static_cast(OpcodeMap::OP_MAP_MIMG)); m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = vaddr; m_instruction.src[2].field = OperandField::ScalarGPR; m_instruction.src[2].code = srsrc; m_instruction.src[3].field = OperandField::ScalarGPR; m_instruction.src[3].code = ssamp; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdata; m_instruction.control.mimg = *reinterpret_cast(&hexInstruction); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); } void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { OpcodeDS op = (OpcodeDS)bit::extract(hexInstruction, 25, 18); u32 addr = bit::extract(hexInstruction, 39, 32); u32 data0 = bit::extract(hexInstruction, 47, 40); u32 data1 = bit::extract(hexInstruction, 55, 48); u32 vdst = bit::extract(hexInstruction, 63, 56); m_instruction.opcode = static_cast(u32(op) + static_cast(OpcodeMap::OP_MAP_DS)); m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = addr; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = data0; m_instruction.src[2].field = OperandField::VectorGPR; m_instruction.src[2].code = data1; m_instruction.dst[0].field = OperandField::VectorGPR; m_instruction.dst[0].code = vdst; m_instruction.dst_count = 1; m_instruction.control.ds = *reinterpret_cast(&hexInstruction); auto instFormat = InstructionFormat(InstEncoding::DS, (u32)op); m_instruction.control.ds.dual = op == OpcodeDS::DS_WRITE2_B32 || op == OpcodeDS::DS_WRXCHG2_RTN_B32 || op == OpcodeDS::DS_READ2_B32 || op == OpcodeDS::DS_WRITE2_B64 || op == OpcodeDS::DS_WRXCHG2_RTN_B64 || op == OpcodeDS::DS_READ2_B64; m_instruction.control.ds.sign = instFormat.src_type == ScalarType::Sint32; m_instruction.control.ds.relative = op >= OpcodeDS::DS_ADD_SRC2_U32 && op <= OpcodeDS::DS_MAX_SRC2_F64; m_instruction.control.ds.stride = op == OpcodeDS::DS_WRITE2ST64_B32 || op == OpcodeDS::DS_WRXCHG2ST64_RTN_B32 || op == OpcodeDS::DS_READ2ST64_B32 || op == OpcodeDS::DS_WRITE2ST64_B64 || op == OpcodeDS::DS_WRXCHG2ST64_RTN_B64 || op == OpcodeDS::DS_READ2ST64_B64; if (op == OpcodeDS::DS_WRITE_B8 || op == OpcodeDS::DS_READ_I8 || op == OpcodeDS::DS_READ_U8) { m_instruction.control.ds.size = 1; } else if (op == OpcodeDS::DS_WRITE_B16 || op == OpcodeDS::DS_READ_I16 || op == OpcodeDS::DS_READ_U16) { m_instruction.control.ds.size = 2; } else { if (instFormat.src_type == ScalarType::Sint32 || instFormat.src_type == ScalarType::Uint32) { m_instruction.control.ds.size = 4; } else if (instFormat.src_type == ScalarType::Sint64 || instFormat.src_type == ScalarType::Uint64) { m_instruction.control.ds.size = 8; } else { m_instruction.control.ds.size = 0; } } } void GcnDecodeContext::decodeInstructionEXP(uint64_t hexInstruction) { u32 vsrc0 = bit::extract(hexInstruction, 39, 32); u32 vsrc1 = bit::extract(hexInstruction, 47, 40); u32 vsrc2 = bit::extract(hexInstruction, 55, 48); u32 vsrc3 = bit::extract(hexInstruction, 63, 56); m_instruction.opcode = Opcode::EXP; m_instruction.src[0].field = OperandField::VectorGPR; m_instruction.src[0].code = vsrc0; m_instruction.src[1].field = OperandField::VectorGPR; m_instruction.src[1].code = vsrc1; m_instruction.src[2].field = OperandField::VectorGPR; m_instruction.src[2].code = vsrc2; m_instruction.src[3].field = OperandField::VectorGPR; m_instruction.src[3].code = vsrc3; m_instruction.control.exp = *reinterpret_cast(&hexInstruction); } } // namespace Shader::Gcn