From 5b7d3343cf1586e42068f0ce455ab0675ec43ada Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 18 Sep 2026 12:25:13 +0400 Subject: [PATCH] shader: V_CMP_NLT_F16 --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 3 +- .../recompiler/frontend/translate/Vector.cpp | 3 + tests/ShaderRecompilerComputeTests.cpp | 89 +++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 73f269ecc..45a94d623 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -381,6 +381,7 @@ enum class Opcode { V_CMP_GE_F16, V_CMP_NGT_F16, V_CMP_NEQ_F16, + V_CMP_NLT_F16, V_CMPX_LT_F16, V_CMPX_EQ_F16, V_CMPX_LE_F16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 1f6dc9077..7e9998ff1 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -247,7 +247,7 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0xcau, Opcode::V_CMP_EQ_F16}, {0xcbu, Opcode::V_CMP_LE_F16}, {0xccu, Opcode::V_CMP_GT_F16}, {0xcdu, Opcode::V_CMP_LG_F16}, {0xceu, Opcode::V_CMP_GE_F16}, {0xebu, Opcode::V_CMP_NGT_F16}, - {0xedu, Opcode::V_CMP_NEQ_F16}, + {0xedu, Opcode::V_CMP_NEQ_F16}, {0xeeu, Opcode::V_CMP_NLT_F16}, {0xd9u, Opcode::V_CMPX_LT_F16}, {0xdau, Opcode::V_CMPX_EQ_F16}, {0xdbu, Opcode::V_CMPX_LE_F16}, {0xdcu, Opcode::V_CMPX_GT_F16}, {0xdeu, Opcode::V_CMPX_GE_F16}, {0xfbu, Opcode::V_CMPX_NGT_F16}, @@ -830,6 +830,7 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMP_GE_F16: case Opcode::V_CMP_NGT_F16: case Opcode::V_CMP_NEQ_F16: + case Opcode::V_CMP_NLT_F16: 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 3ffb50943..f990bd811 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -276,6 +276,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_NEQ_F16: EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual32, true, true); return true; + case O::V_CMP_NLT_F16: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, false); + return true; case O::V_CMPX_NLT_F16: EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, true); return true; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 050a81e49..c70784e22 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -15734,6 +15734,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::V_CMP_GE_F16: case Opcode::V_CMP_NGT_F16: case Opcode::V_CMP_NEQ_F16: + case Opcode::V_CMP_NLT_F16: case Opcode::V_CMPX_LT_F16: case Opcode::V_CMPX_EQ_F16: case Opcode::V_CMPX_LE_F16: @@ -21475,6 +21476,88 @@ TestCase VectorVopcCmpNgtF16CapturedSdwaAndEdges() { return test; } +TestCase VectorVopcCmpNltF16CapturedSdwaAndEdges() { + using O = ShaderOpcode; + enum Encoding { Captured, Compact, Vop3NegAbs, SdwaNegAbs }; + struct CompareCase { + u32 packed; // Captured instruction compares low half against high half. + u32 expected; + Encoding encoding = Captured; + u32 exec = 1; + }; + const std::array cases{{ + {0x40003c00u, 0}, // +1 < +2 + {0x3c004000u, 1}, // +2 > +1 + {0x3c003c00u, 1}, // Equal + {0x80000000u, 1}, // +0 versus -0 + {0x00008000u, 1}, // -0 versus +0 + {0x3c007e00u, 1}, // Quiet NaN in src0: unordered GE, not ordered GE. + {0x7e003c00u, 1}, // Quiet NaN in src1 + {0x3c007c01u, 1}, // Signaling NaN in src0 + {0x7c013c00u, 1}, // Signaling NaN in src1 + {0xfc007c00u, 1}, // +infinity versus -infinity + {0x7c00fc00u, 0}, // -infinity versus +infinity + {0x00003c00u, 0, Captured, 0}, // Inactive lane contributes no mask bit. + {0x40003c00u, 0, Compact}, + {0x7e003c00u, 1, Compact}, + {0x0000bc00u, 0, Vop3NegAbs}, // -abs(-1) < 0 + {0x0000bc00u, 0, SdwaNegAbs}, + }}; + constexpr u32 sentinel = 0x89abcdefu; + TestCase test; + test.name = "VectorVopcCmpNltF16CapturedSdwaAndEdges"; + for (const auto &entry : cases) { + test.initial.push_back(entry.packed); + } + test.expected = test.initial; + auto &code = test.code; + code.push_back(EncodeSMovB32(22, 2)); // Preserve the input buffer descriptor. + code.push_back(EncodeSMovB32(23, 3)); + for (u32 i = 0; i < cases.size(); ++i) { + const auto &entry = cases[i]; + AppendVMovU32(&code, 30, i * 4u); + AppendBufferLoadDword(&code, 3, 30); // Runtime input prevents constant folding. + AppendVMovU32(&code, 1, entry.packed >> 16u); + for (u32 reg : {3u, 106u, 107u}) { + AppendSMovLiteral(&code, reg, sentinel); + } + code.push_back(EncodeSMovB32(126, InlineU32(entry.exec))); + switch (entry.encoding) { + case Captured: code.insert(code.end(), {0x7ddc06f9u, 0x05068203u}); break; + case Compact: code.push_back(EncodeVopc(0xee, Vgpr(3), 1)); break; + case Vop3NegAbs: + AppendVop3(&code, 0xee, 2, Vgpr(3), Vgpr(1), 0, 1, 0, false, 0, 1); + break; + case SdwaNegAbs: + code.insert(code.end(), {0x7ddc06f9u, 0x05368203u}); + break; + } + code.push_back(EncodeSMovB32(20, entry.encoding == Compact ? 106 : 2)); + code.push_back(EncodeSMovB32(21, 3)); // Wave32 must preserve adjacent SGPR. + code.push_back(EncodeSMovB32(24, 126)); + code.push_back(EncodeSMovB32(25, 127)); + code.push_back(EncodeSMovB32(126, InlineU32(1))); + code.push_back(EncodeSMovB32(2, 22)); + code.push_back(EncodeSMovB32(3, 23)); + const u32 out = static_cast(cases.size()) + i * 6u; + AppendStoreSgprPair(&code, 20, out); + AppendStoreSgprPair(&code, 24, out + 2u); + AppendStoreSgprPair(&code, 106, out + 4u); + test.expected.insert(test.expected.end(), + {entry.expected, sentinel, entry.exec, 0u, + entry.encoding == Compact ? entry.expected : sentinel, + sentinel}); + } + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD, + O::V_CMP_NLT_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"V_CMP_NLT_F16", cases.size()}}; + test.required_spirv = {"OpFUnordGreaterThanEqual"}; + test.compute_info.wave_size = 32; + test.has_compute_info = true; + return test; +} + TestCase VectorVopcCmpxNgtF16CapturedSdwaExecMask() { using O = ShaderOpcode; @@ -27046,6 +27129,7 @@ std::vector MakeCases() { AddCase(VectorVopcCmpxGtU16CapturedSdwaExecMask); AddCase(VectorVopcCmpxLtU16CapturedSdwaExecMask); AddCase(VectorVopcCmpNgtF16CapturedSdwaAndEdges); + AddCase(VectorVopcCmpNltF16CapturedSdwaAndEdges); AddCase(VectorVopcCmpxNgtF16CapturedSdwaExecMask); AddCase(VectorCompareInvertedMaskSelect); AddCase(BranchSelect); @@ -31758,6 +31842,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorVop3FloatCompareNegSourceModifier()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--cmp-nlt-f16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorVopcCmpNltF16CapturedSdwaAndEdges()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--fract-f16-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorFractF16CapturedAndEdges());