From 7881c69a54f8b813152b11d0b24eca999a5dd92c Mon Sep 17 00:00:00 2001 From: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com> Date: Sat, 19 Sep 2026 15:15:40 +0200 Subject: [PATCH] shader: decode v_cmp_class_f16 --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 4 +- .../recompiler/frontend/translate/Vector.cpp | 1 + tests/ShaderRecompilerComputeTests.cpp | 84 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 580719d08..d39091708 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -401,6 +401,7 @@ enum class Opcode { V_CMP_T_I32, V_CMP_CLASS_F32, V_CMPX_CLASS_F32, + V_CMP_CLASS_F16, V_CMPX_CLASS_F16, V_CMP_LT_I16, V_CMP_EQ_I16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 99fa3e165..75cfbd300 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -245,7 +245,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0x88u, Opcode::V_CMP_CLASS_F32}, {0x89u, Opcode::V_CMP_LT_I16}, {0x8au, Opcode::V_CMP_EQ_I16}, {0x8bu, Opcode::V_CMP_LE_I16}, {0x8cu, Opcode::V_CMP_GT_I16}, {0x8du, Opcode::V_CMP_NE_I16}, - {0x8eu, Opcode::V_CMP_GE_I16}, {0x91u, Opcode::V_CMPX_LT_I32}, + {0x8eu, Opcode::V_CMP_GE_I16}, {0x8fu, Opcode::V_CMP_CLASS_F16, false}, + {0x91u, Opcode::V_CMPX_LT_I32}, {0x92u, Opcode::V_CMPX_EQ_I32}, {0x93u, Opcode::V_CMPX_LE_I32}, {0x94u, Opcode::V_CMPX_GT_I32}, {0x95u, Opcode::V_CMPX_NE_I32}, {0x96u, Opcode::V_CMPX_GE_I32}, {0x98u, Opcode::V_CMPX_CLASS_F32}, @@ -929,6 +930,7 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMPX_NEQ_F16: case Opcode::V_CMPX_NLT_F16: case Opcode::V_CMP_CLASS_F32: + case Opcode::V_CMP_CLASS_F16: case Opcode::V_CMPX_CLASS_F16: case Opcode::V_CMPX_CLASS_F32: return true; default: return false; diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 685bc7360..059b9471e 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -374,6 +374,7 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_U_F32: EmitFloatOrderedCompare(inst, false, true); return; case O::V_CMP_CLASS_F32: EmitFloatClassCompare(inst, false); return; case O::V_CMPX_CLASS_F32: EmitFloatClassCompare(inst, true); return; + case O::V_CMP_CLASS_F16: EmitFloatClassCompare(inst, false, true); return; case O::V_CMPX_CLASS_F16: EmitFloatClassCompare(inst, true, true); return; case O::V_CVT_F32_UBYTE0: V_CVT_F32_UBYTE(inst, 0); return; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 495b51703..9cd933028 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -19073,6 +19073,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::V_CMPX_NLT_F32: case Opcode::V_CMP_CLASS_F32: case Opcode::V_CMPX_CLASS_F32: + case Opcode::V_CMP_CLASS_F16: case Opcode::V_CMPX_CLASS_F16: case Opcode::V_CMP_LT_F16: case Opcode::V_CMP_EQ_F16: @@ -26468,6 +26469,79 @@ TestCase VectorVopcSdwaCmpxClassF32CapturedExecMask() { return test; } +TestCase VectorCmpClassF16(u32 wave_size) { + using O = ShaderOpcode; + constexpr std::array values{ + 0x7c01u, 0x7e01u, 0xfc00u, 0xbc00u, 0x8001u, + 0x8000u, 0x0000u, 0x0001u, 0x3c00u, 0x7c00u}; + constexpr u32 guard = 0x87654321u; + TestCase test; + test.name = wave_size == 64 ? "VectorCmpClassF16Wave64" : "VectorCmpClassF16Wave32"; + for (const auto value : values) { + test.initial.insert(test.initial.end(), {0xa55a0000u | value, (value << 16u) | 0x7e01u}); + } + test.expected = test.initial; + auto &code = test.code; + const auto compare = [&](std::initializer_list words, u32 expected, + u32 dst = 106u, u32 exec = 1u) { + AppendSMovLiteral(&code, 106, 0x12345678u); + AppendSMovLiteral(&code, 107, guard); + AppendSMovLiteral(&code, dst + 1u, guard); + code.push_back(EncodeSopc(0x06, InlineU32(expected), InlineU32(0))); + code.insert(code.end(), words); + const u32 sources[] = {dst, dst + 1u, 126, 127, 253}; + for (u32 i = 0; i < std::size(sources); ++i) { + code.push_back(EncodeSMovB32(30u + i, sources[i])); + } + code.push_back(EncodeSMovB32(126, InlineU32(1))); + const u32 results[] = {expected, wave_size == 32 ? guard : 0u, exec, 0u, 1u - expected}; + for (u32 i = 0; i < std::size(results); ++i) { + AppendStoreSgpr(&code, 30u + i, static_cast(test.expected.size())); + test.expected.push_back(results[i]); + } + if (dst != 106u) { + AppendStoreSgpr(&code, 106, static_cast(test.expected.size())); + AppendStoreSgpr(&code, 107, static_cast(test.expected.size()) + 1u); + test.expected.insert(test.expected.end(), {0x12345678u, guard}); + } + }; + for (u32 index = 0; index < values.size(); ++index) { + AppendVMovU32(&code, 30, index * 8u); + AppendBufferLoadDword(&code, 0, 30); + AppendVMovU32(&code, 30, index * 8u + 4u); + AppendBufferLoadDword(&code, 2, 30); + for (u32 bit = 0; bit < 12; ++bit) { + AppendVMovU32(&code, 1, bit == 11 ? 0 : 1u << bit); + const u32 expected = bit == index ? 1u : 0u; + compare({EncodeVopc(0x8f, Vgpr(0), 1)}, expected); + compare({EncodeVop3Word0(0x8f, 20), EncodeVop3Word1(Vgpr(0), Vgpr(1))}, + expected, 20); + } + // SDWA selects the high half, applies -abs, and writes an explicit SGPR pair. + compare({EncodeVopc(0x8f, 249, InlineU32(56)), + EncodeVopcSdwa(2, 20, 1, 5, 6, 0, 0, 1, 1, 0, 0, 0, 1)}, + index >= 3 && index <= 8 ? 1u : 0u, 20); + compare({EncodeVop3Word0(0x8f, 20, 1), + EncodeVop3Word1(Vgpr(0), InlineU32(56), 0, 0, 1)}, + index >= 3 && index <= 8 ? 1u : 0u, 20); + } + AppendVMovU32(&code, 1, 1u << 8u); + compare({EncodeVopc(0x8f, 242, 1)}, 1); // Inline 1.0 becomes half 0x3c00. + compare({EncodeVopc(0x8f, 255, 1), 0xdead3c00u}, 1); // Literal low half. + compare({EncodeVop3Word0(0x8f, 20), EncodeVop3Word1(242, 248)}, 1, 20); + // Inline INV_2PI supplies half bits 0x3118, including the positive-normal class. + code.push_back(EncodeSMovB32(126, InlineU32(0))); + compare({EncodeVopc(0x8f, 242, 1)}, 0, 106, 0); // Inactive lanes clear the result. + AppendEnd(&code); + test.initial.resize(test.expected.size()); + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::S_CMP_EQ_U32, O::BUFFER_LOAD_DWORD, + O::V_CMP_CLASS_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"V_CMP_CLASS_F16", 264u}}; + test.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; +} + TestCase VectorCmpxClassF16(u32 wave_size) { using O = ShaderOpcode; // One raw half value for each architectural class bit, in bit order. @@ -35978,6 +36052,8 @@ std::vector MakeCases() { AddCase(VectorVop3CmpxNeI64CapturedExecMask); AddCase(VectorCompareClassF32); AddCase(VectorVopcSdwaCmpxClassF32CapturedExecMask); + cases.push_back(VectorCmpClassF16(32)); + cases.push_back(VectorCmpClassF16(64)); cases.push_back(VectorCmpxClassF16(32)); cases.push_back(VectorCmpxClassF16(64)); AddCase(VectorCompareF16Ops); @@ -41474,6 +41550,14 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorVopcSdwaCmpxClassF32CapturedExecMask()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--class-f16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorCmpClassF16(32)); + RunCase(&vulkan, VectorCmpClassF16(64)); + RunCase(&vulkan, VectorCmpxClassF16(32)); + RunCase(&vulkan, VectorCmpxClassF16(64)); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--cmpx-class-f16-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorCmpxClassF16(32));