From 1d5527246747016e257c25d89b58439e20779506 Mon Sep 17 00:00:00 2001 From: fxpw Date: Mon, 5 Oct 2026 09:22:31 +0400 Subject: [PATCH] shader: support V_CMPX_NE_U16 --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 4 +- .../recompiler/frontend/translate/Vector.cpp | 3 + tests/ShaderRecompilerComputeTests.cpp | 65 ++++++++++++++----- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 11e6c4ff6..873eee42b 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -441,6 +441,7 @@ enum class Opcode { V_CMPX_LT_U16, V_CMPX_EQ_U16, V_CMPX_GT_U16, + V_CMPX_NE_U16, V_CMP_NE_U16, V_CMP_GE_U16, V_CMP_F_U32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 24feeecde..0b00c90c8 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -256,7 +256,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0xabu, Opcode::V_CMP_LE_U16}, {0xacu, Opcode::V_CMP_GT_U16}, {0xadu, Opcode::V_CMP_NE_U16}, {0xaeu, Opcode::V_CMP_GE_U16}, {0xb9u, Opcode::V_CMPX_LT_U16, false}, {0xbau, Opcode::V_CMPX_EQ_U16, false}, - {0xbcu, Opcode::V_CMPX_GT_U16}, {0xc0u, Opcode::V_CMP_F_U32}, + {0xbcu, Opcode::V_CMPX_GT_U16}, {0xbdu, Opcode::V_CMPX_NE_U16, false}, + {0xc0u, Opcode::V_CMP_F_U32}, {0xc1u, Opcode::V_CMP_LT_U32}, {0xc2u, Opcode::V_CMP_EQ_U32}, {0xc3u, Opcode::V_CMP_LE_U32}, {0xc4u, Opcode::V_CMP_GT_U32}, {0xc5u, Opcode::V_CMP_NE_U32}, {0xc6u, Opcode::V_CMP_GE_U32}, @@ -1557,6 +1558,7 @@ bool IsVopcCompareExec(Opcode opcode) { case Opcode::V_CMPX_LT_U16: case Opcode::V_CMPX_EQ_U16: case Opcode::V_CMPX_GT_U16: + case Opcode::V_CMPX_NE_U16: case Opcode::V_CMPX_LT_F16: case Opcode::V_CMPX_EQ_F16: case Opcode::V_CMPX_LE_F16: diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 5e6568839..fa85c7b48 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -143,6 +143,9 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMP_NE_U16: EmitInteger16Compare(inst, IR::ValueOpcode::INotEqual32, false, false); return; + case O::V_CMPX_NE_U16: + EmitInteger16Compare(inst, IR::ValueOpcode::INotEqual32, false, true); + return; case O::V_CMP_NE_I16: EmitInteger16Compare(inst, IR::ValueOpcode::INotEqual32, true, false); return; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 937c0c80b..0d70e1b3a 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -26247,16 +26247,16 @@ TestCase VectorVopcCmpxLtI16WaveMasks(u32 wave_size) { return test; } -TestCase VectorVopcCmpxEqU16SdwaCompactVop3ExecMask() { +TestCase VectorVopcCmpxU16SdwaCompactVop3ExecMask(bool not_equal) { using O = ShaderOpcode; struct CompareCase { u32 lhs; u32 rhs; u32 incoming_exec; u32 expected_exec; - u32 encoding = 0; // SDWA, compact, VOP3, sign-extended words/bytes. + u32 encoding = 0; // SDWA, compact, VOP3, sign-extended words/bytes, literal. }; - const std::array cases{{ + const std::array cases{{ {0x12340000u, 0x56780000u, 1, 1}, // Equal low half; ignore high half. {0xffff0001u, 0xabcd0001u, 1, 1}, {0x00000000u, 0x12340000u, 1, 1}, @@ -26273,10 +26273,17 @@ TestCase VectorVopcCmpxEqU16SdwaCompactVop3ExecMask() { {0x123456ffu, 0x0000ffffu, 1, 1, 4}, {0x0000ff80u, 0x12348000u, 1, 1, 5}, // SEXT BYTE_1 on src1. {0x1234ffffu, 0x5678ffffu, 1, 1, 6}, // SEXT WORD_0 versus DWORD low half. + {0xabcd0001u, 1u, 1, 1, 7}, // Literal source, equal low halves. + {0xabcd0000u, 1u, 1, 0, 7}, + {0x00000000u, 1u, 0, 0}, // Neither EQ nor NE may reactivate an inactive lane. + {0x00010000u, 0xffff0001u, 1, 1, 8}, // Select the high word of src0. + {0xabcd0002u, 0x0002ffffu, 1, 1, 9}, // Select the high word of src1. }}; + const u32 opcode = not_equal ? 0xbdu : 0xbau; constexpr u32 vcc_hi = 0x89abcdefu; TestCase test; - test.name = "VectorVopcCmpxEqU16SdwaCompactVop3ExecMask"; + test.name = not_equal ? "VectorVopcCmpxNeU16SdwaCompactVop3ExecMask" + : "VectorVopcCmpxEqU16SdwaCompactVop3ExecMask"; for (const auto &entry : cases) { test.initial.push_back(entry.lhs); } @@ -26291,26 +26298,36 @@ TestCase VectorVopcCmpxEqU16SdwaCompactVop3ExecMask() { AppendSMovLiteral(&code, 107, vcc_hi); code.push_back(EncodeSMovB32(126, InlineU32(entry.incoming_exec))); switch (entry.encoding) { - case 1: code.push_back(0x7d740300u); break; // compact v0, v1 - case 2: code.insert(code.end(), {0xd4ba007eu, 0x00020300u}); break; + case 1: code.push_back(EncodeVopc(opcode, Vgpr(0), 1u)); break; + case 2: AppendVop3(&code, opcode, 126u, Vgpr(0), Vgpr(1)); break; case 3: - code.push_back(EncodeVopc(0xba, 249u, 1u)); + code.push_back(EncodeVopc(opcode, 249u, 1u)); code.push_back(EncodeVopcSdwa(0u, 0u, 0u, 6u, 6u, 1u, 1u)); break; case 4: - code.push_back(EncodeVopc(0xba, 249u, 1u)); + code.push_back(EncodeVopc(opcode, 249u, 1u)); code.push_back(EncodeVopcSdwa(0u, 0u, 0u, 0u, 6u, 1u, 0u)); break; case 5: - code.push_back(EncodeVopc(0xba, 249u, 1u)); + code.push_back(EncodeVopc(opcode, 249u, 1u)); code.push_back(EncodeVopcSdwa(0u, 0u, 0u, 6u, 1u, 0u, 1u)); break; case 6: - code.push_back(EncodeVopc(0xba, 249u, 1u)); + code.push_back(EncodeVopc(opcode, 249u, 1u)); code.push_back(EncodeVopcSdwa(0u, 0u, 0u, 4u, 6u, 1u, 0u)); break; + case 7: + code.push_back(EncodeVopc(opcode, 255u, 0u)); + code.push_back(entry.rhs); + break; + case 8: + case 9: + code.push_back(EncodeVopc(opcode, 249u, 1u)); + code.push_back(EncodeVopcSdwa(0u, 0u, 0u, entry.encoding == 8u ? 5u : 6u, + entry.encoding == 9u ? 5u : 6u)); + break; default: - code.push_back(EncodeVopc(0xba, 249u, 1u)); + code.push_back(EncodeVopc(opcode, 249u, 1u)); code.push_back(EncodeVopcSdwa(0u)); break; } @@ -26320,17 +26337,27 @@ TestCase VectorVopcCmpxEqU16SdwaCompactVop3ExecMask() { const u32 out = static_cast(cases.size()) + i * 4u; AppendStoreSgprPair(&code, 20, out); AppendStoreSgprPair(&code, 106, out + 2u); // CMPX must not overwrite either VCC half. - test.expected.insert(test.expected.end(), - {entry.expected_exec, 0u, entry.rhs, vcc_hi}); + const u32 expected_exec = not_equal ? entry.incoming_exec & (entry.expected_exec ^ 1u) + : entry.expected_exec; + test.expected.insert(test.expected.end(), {expected_exec, 0u, entry.rhs, vcc_hi}); } AppendEnd(&code); test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD, - O::V_CMPX_EQ_U16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; - test.decoded_counts = {{"V_CMPX_EQ_U16", cases.size()}}; - test.required_spirv = {"OpIEqual"}; + not_equal ? O::V_CMPX_NE_U16 : O::V_CMPX_EQ_U16, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{not_equal ? "V_CMPX_NE_U16" : "V_CMPX_EQ_U16", cases.size()}}; + test.required_spirv = {not_equal ? "OpINotEqual" : "OpIEqual"}; return test; } +TestCase VectorVopcCmpxEqU16SdwaCompactVop3ExecMask() { + return VectorVopcCmpxU16SdwaCompactVop3ExecMask(false); +} + +TestCase VectorVopcCmpxNeU16SdwaCompactVop3ExecMask() { + return VectorVopcCmpxU16SdwaCompactVop3ExecMask(true); +} + TestCase VectorVopcCmpNgtF16CapturedSdwaAndEdges() { using O = ShaderOpcode; enum Encoding { Captured, Compact, Vop3NegAbs, HighWord, ScalarDst, SdwaNegAbs }; @@ -34693,6 +34720,7 @@ std::vector MakeCases() { cases.push_back(VectorVopcCmpxLtI16WaveMasks(wave_size)); } AddCase(VectorVopcCmpxEqU16SdwaCompactVop3ExecMask); + AddCase(VectorVopcCmpxNeU16SdwaCompactVop3ExecMask); AddCase(VectorVopcCmpNgtF16CapturedSdwaAndEdges); AddCase(VectorVopcCmpNltF16CapturedSdwaAndEdges); AddCase(VectorVopcCmpxNgtF16CapturedSdwaExecMask); @@ -39995,6 +40023,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorVopcCmpxEqU16SdwaCompactVop3ExecMask()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--cmpx-ne-u16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorVopcCmpxNeU16SdwaCompactVop3ExecMask()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--cmpx-lt-i16-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorVopcCmpxLtI16CapturedSdwaExecMask());