diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 79b31d92c..bbf0ee8ff 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -378,6 +378,7 @@ enum class Opcode { V_CMP_GT_F16, V_CMP_LG_F16, V_CMP_GE_F16, + V_CMP_NGT_F16, V_CMP_NEQ_F16, V_CMPX_LT_F16, V_CMPX_EQ_F16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 2cb425289..004040cc8 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -250,7 +250,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0xf5u, Opcode::V_CMPX_NE_U64, false}, {0xc9u, Opcode::V_CMP_LT_F16}, {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}, {0xedu, Opcode::V_CMP_NEQ_F16}, + {0xceu, Opcode::V_CMP_GE_F16}, {0xebu, Opcode::V_CMP_NGT_F16}, + {0xedu, Opcode::V_CMP_NEQ_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}, @@ -824,6 +825,7 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMP_GT_F16: case Opcode::V_CMP_LG_F16: case Opcode::V_CMP_GE_F16: + case Opcode::V_CMP_NGT_F16: case Opcode::V_CMP_NEQ_F16: case Opcode::V_CMPX_LT_F16: case Opcode::V_CMPX_EQ_F16: diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 0fe254314..112ef202b 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -264,6 +264,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_LE_F16: EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThanEqual32, true, true); return true; + case O::V_CMP_NGT_F16: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, false); + return true; case O::V_CMPX_NGT_F16: EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual32, true, true); return true; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 5d8b92724..ffe85b57b 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -15740,6 +15740,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::V_CMP_GT_F16: case Opcode::V_CMP_LG_F16: case Opcode::V_CMP_GE_F16: + case Opcode::V_CMP_NGT_F16: case Opcode::V_CMP_NEQ_F16: case Opcode::V_CMPX_LT_F16: case Opcode::V_CMPX_EQ_F16: @@ -21264,6 +21265,92 @@ TestCase VectorVopcCmpxLtU16CapturedSdwaExecMask() { return test; } +TestCase VectorVopcCmpNgtF16CapturedSdwaAndEdges() { + using O = ShaderOpcode; + enum Encoding { Captured, Compact, Vop3NegAbs, HighWord, ScalarDst, SdwaNegAbs }; + struct CompareCase { + u32 lhs; + u32 rhs; + u32 expected; + Encoding encoding = Captured; + u32 exec = 1; + }; + const std::array cases{{ + {0x7e003c00u, 0, 0}, // +1; ignore NaN in the upper half. + {0x3c00bc00u, 0, 1}, // -1 + {0x3c000000u, 0, 1}, // +0 + {0x3c008000u, 0, 1}, // -0 + {0x3c007e00u, 0, 1}, // Quiet NaN: NGT differs from ordered LE. + {0x3c007c01u, 0, 1}, // Signaling NaN + {0x00007c00u, 0, 0}, // +infinity + {0x0000fc00u, 0, 1}, // -infinity + {0x0000bc00u, 0, 0, Captured, 0}, // Inactive lane contributes no VCC bit. + {0x00003c00u, 0x00007e00u, 1, Compact}, // NaN in src1. + {0x00004000u, 0x00003c00u, 0, Compact}, // +2 > +1 + {0x0000bc00u, 0, 1, Vop3NegAbs}, // -abs(-1) + {0x00003c00u, 0, 1, Vop3NegAbs}, // -abs(+1) + {0xbc004000u, 0x3c00c000u, 1, HighWord}, // Select both upper halves. + {0x00004000u, 0x00003c00u, 0, ScalarDst}, + {0x00003c00u, 0x00004000u, 1, ScalarDst}, + {0x0000bc00u, 0, 1, SdwaNegAbs}, + }}; + constexpr u32 vcc_sentinel = 0x89abcdefu; + TestCase test; + test.name = "VectorVopcCmpNgtF16CapturedSdwaAndEdges"; + for (const auto &entry : cases) { + test.initial.insert(test.initial.end(), {entry.lhs, entry.rhs}); + } + test.expected = test.initial; + auto &code = test.code; + for (u32 i = 0; i < cases.size(); ++i) { + const auto &entry = cases[i]; + AppendVMovU32(&code, 30, i * 8u); + AppendBufferLoadDword(&code, 21, 30); // Runtime inputs prevent constant folding. + AppendVMovU32(&code, 30, i * 8u + 4u); + AppendBufferLoadDword(&code, 1, 30); + AppendSMovLiteral(&code, 106, vcc_sentinel); + AppendSMovLiteral(&code, 107, vcc_sentinel); + code.push_back(EncodeSMovB32(126, InlineU32(entry.exec))); + switch (entry.encoding) { + case Captured: code.insert(code.end(), {0x7dd700f9u, 0x86060015u}); break; + case Compact: code.push_back(EncodeVopc(0xeb, Vgpr(21), 1)); break; + case Vop3NegAbs: + AppendVop3(&code, 0xeb, 106, Vgpr(21), Vgpr(1), 0, 1, 0, false, 0, 1); + break; + case HighWord: + code.push_back(EncodeVopc(0xeb, 249, 1)); + code.push_back(EncodeVopcSdwa(21, 0, 0, 5, 5)); + break; + case ScalarDst: + code.push_back(EncodeVopc(0xeb, 249, 1)); + code.push_back(EncodeVopcSdwa(21, 22, 1)); + break; + case SdwaNegAbs: + code.push_back(EncodeVopc(0xeb, 249, 1)); + code.push_back(EncodeVopcSdwa(21, 0, 0, 6, 6, 0, 0, 1, 1)); + break; + } + code.push_back(EncodeSMovB32(20, entry.encoding == ScalarDst ? 22 : 106)); + code.push_back(EncodeSMovB32(21, 126)); // CMP must preserve EXEC even if false. + code.push_back(EncodeSMovB32(126, InlineU32(1))); + const u32 out = static_cast(test.initial.size()) + i * 4u; + AppendStoreSgprPair(&code, 20, out); + AppendStoreSgprPair(&code, 106, out + 2u); + test.expected.insert(test.expected.end(), + {entry.expected, entry.exec, + entry.encoding == ScalarDst ? vcc_sentinel : entry.expected, + vcc_sentinel}); + } + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD, + O::V_CMP_NGT_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"V_CMP_NGT_F16", cases.size()}}; + test.required_spirv = {"OpFUnordLessThanEqual"}; + test.compute_info.wave_size = 32; + test.has_compute_info = true; + return test; +} + TestCase VectorVopcCmpxNgtF16CapturedSdwaExecMask() { using O = ShaderOpcode; @@ -26773,6 +26860,7 @@ std::vector MakeCases() { AddCase(VectorVopcSdwaCmpxWritesExecMask); AddCase(VectorVopcCmpxGtU16CapturedSdwaExecMask); AddCase(VectorVopcCmpxLtU16CapturedSdwaExecMask); + AddCase(VectorVopcCmpNgtF16CapturedSdwaAndEdges); AddCase(VectorVopcCmpxNgtF16CapturedSdwaExecMask); AddCase(VectorCompareInvertedMaskSelect); AddCase(BranchSelect); @@ -31447,6 +31535,15 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorVopcSdwaCmpxClassF32CapturedExecMask()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--cmp-ngt-f16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorVopcCmpNgtF16CapturedSdwaAndEdges()); + RunCase(&vulkan, VectorVopcCmpxNgtF16CapturedSdwaExecMask()); + RunCase(&vulkan, VectorCompareF16Ops()); + RunCase(&vulkan, Wave32VccMasksPreserveOtherHalf()); + RunCase(&vulkan, VectorVop3FloatCompareNegSourceModifier()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--fract-f16-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorFractF16CapturedAndEdges());