diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 52acef97b..da620b482 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -1059,6 +1059,13 @@ bool FinalizeVop2Instruction(uint32_t pc, std::span code, uint32 inst.src2.kind = OperandKind::VccLo; inst.src_count = 3; break; + case Opcode::V_PK_FMAC_F16: + // VOP2 has no OP_SEL fields, so the high operation implicitly reads high halves. + inst.src0.op_sel_hi = true; + inst.src1.op_sel_hi = true; + inst.dst.op_sel_hi = true; + inst.src_count = 2; + break; default: inst.src_count = 2; break; } ReadLiteralOperands(code, word_index, inst); @@ -1107,12 +1114,17 @@ bool DecodeVop2Dpp(uint32_t pc, std::span code, uint32_t word_in if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) { return false; } - ApplyDefaultVop2F16Destination(inst); ApplyDppModifier(inst.src0, modifier); inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u; inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u; + ApplyDefaultVop2F16Destination(inst); + const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16; + if (packed_fmac) { + inst.src0.negate_hi = inst.src0.negate; + inst.src1.negate_hi = inst.src1.negate; + } - if (!IsVop2FloatSourceOpcode(inst.opcode) && + if (!IsVop2FloatSourceOpcode(inst.opcode) && !packed_fmac && (inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute)) { SetUnsupported(inst, Family::VOP2, opcode, "VOP2 DPP integer source modifiers are not supported"); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 988f336fd..4392a7ff6 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -14250,6 +14250,53 @@ TestCase VectorLaneAndPackedOps() { O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase Vop2PkFmacF16AccumulatesPackedHalvesIndependently() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 0, 0x50004c00u); // low=16.0h, high=32.0h + AppendVMovLiteral(&code, 1, 0x42003c00u); // low=1.0h, high=3.0h + AppendVMovLiteral(&code, 2, 0x48004400u); // low=4.0h, high=8.0h + + code.push_back(EncodeVop2(0x3c, 0, Vgpr(1), 2)); + AppendStoreVgpr(&code, 0, 0); + code.push_back(EncodeVop2(0x3c, 0, Vgpr(1), 2)); + AppendStoreVgpr(&code, 0, 1); + + AppendVMovLiteral(&code, 3, 0x50004c00u); + code.push_back(EncodeVop2(0x3c, 3, 242, 2)); // Inline 1.0h is {1.0h, 0.0h}. + AppendStoreVgpr(&code, 3, 2); + AppendEnd(&code); + + return {"Vop2PkFmacF16AccumulatesPackedHalvesIndependently", + code, + {}, + {0x53004d00u, 0x55004e00u, 0x50004d00u}, + {O::V_MOV_B32, O::V_PK_FMAC_F16, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}}; +} + +TestCase Vop2PkFmacF16DppNegatesBothPackedHalves() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 0, 0x50004c00u); // low=16.0h, high=32.0h + AppendVMovLiteral(&code, 1, 0x42003c00u); // low=1.0h, high=3.0h + AppendVMovLiteral(&code, 2, 0x48004400u); // low=4.0h, high=8.0h + + code.push_back(EncodeVop2(0x3c, 0, 250, 2)); + code.push_back(EncodeVop2Dpp(1, 0, 0xf, 0xf, 1)); + AppendStoreVgpr(&code, 0, 0); + AppendEnd(&code); + + return {"Vop2PkFmacF16DppNegatesBothPackedHalves", + code, + {}, + {0x48004a00u}, + {O::V_MOV_B32, O::V_PK_FMAC_F16, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}}; +} + TestCase Vop3pOpselHiUsesArchitecturalSourceBits() { using O = ShaderOpcode; @@ -20800,6 +20847,8 @@ std::vector MakeCases() { AddCase(VectorVop3BSubCoU32UsesRdna2Opcode310); AddCase(VectorMadU64U32UnsignedCarryOut); AddCase(VectorLaneAndPackedOps); + AddCase(Vop2PkFmacF16AccumulatesPackedHalvesIndependently); + AddCase(Vop2PkFmacF16DppNegatesBothPackedHalves); AddCase(Vop3pOpselHiUsesArchitecturalSourceBits); AddCase(CvtPkU8F32PacksSelectedByte); AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index f9c202d0a..9a7d8f07e 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4203,6 +4203,17 @@ void TestNewShaderDecoderArchitecture() { packed_inst.src2.op_sel_hi == (source == 2u), "VOP3P OPSEL_HI source bit mapping is incorrect"); } + + const uint32_t packed_fmac_dpp[] = {0x780402fau, 0xff500000u}; + Instruction packed_fmac; + Check(DecodeInstruction(packed_fmac_dpp, 0u, packed_fmac, &error), + error.c_str()); + Check(packed_fmac.opcode == Opcode::V_PK_FMAC_F16 && + packed_fmac.src0.dpp && packed_fmac.src0.op_sel_hi && + packed_fmac.src1.op_sel_hi && packed_fmac.dst.op_sel_hi && + packed_fmac.src0.negate && packed_fmac.src0.negate_hi && + packed_fmac.src1.negate && packed_fmac.src1.negate_hi, + "VOP2 DPP V_PK_FMAC_F16 lost its implicit packed modifiers"); } void TestNewShaderRecompilerRejectsDppOn64BitCompares() {