From ff54608569d1bcfb6974b7a0e84e22f25d7c4d9a Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 25 Sep 2026 14:00:17 +0400 Subject: [PATCH] shader: add V_CMPX_O_F32 opcode --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 3 + .../recompiler/frontend/translate/Compare.cpp | 4 +- .../frontend/translate/Translator.h | 2 +- .../recompiler/frontend/translate/Vector.cpp | 5 +- tests/ShaderRecompilerComputeTests.cpp | 70 +++++++++++++++++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index a37f2d879..9410989c9 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -354,6 +354,7 @@ enum class Opcode { V_CMPX_GT_F32, V_CMPX_LG_F32, V_CMPX_GE_F32, + V_CMPX_O_F32, V_CMPX_NGE_F32, V_CMPX_NLG_F32, V_CMPX_NGT_F32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index ce098fdc0..55f608eca 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -214,6 +214,7 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0x11u, Opcode::V_CMPX_LT_F32}, {0x12u, Opcode::V_CMPX_EQ_F32}, {0x13u, Opcode::V_CMPX_LE_F32}, {0x14u, Opcode::V_CMPX_GT_F32}, {0x15u, Opcode::V_CMPX_LG_F32}, {0x16u, Opcode::V_CMPX_GE_F32}, + {0x17u, Opcode::V_CMPX_O_F32}, {0x19u, Opcode::V_CMPX_NGE_F32}, {0x1au, Opcode::V_CMPX_NLG_F32}, {0x1bu, Opcode::V_CMPX_NGT_F32}, {0x1cu, Opcode::V_CMPX_NLE_F32}, {0x1du, Opcode::V_CMPX_NEQ_F32}, {0x1eu, Opcode::V_CMPX_NLT_F32}, @@ -820,6 +821,7 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMPX_GT_F32: case Opcode::V_CMPX_LG_F32: case Opcode::V_CMPX_GE_F32: + case Opcode::V_CMPX_O_F32: case Opcode::V_CMPX_NGE_F32: case Opcode::V_CMPX_NLG_F32: case Opcode::V_CMPX_NGT_F32: @@ -1432,6 +1434,7 @@ bool IsVopcCompareExec(Opcode opcode) { case Opcode::V_CMPX_GT_F32: case Opcode::V_CMPX_LG_F32: case Opcode::V_CMPX_GE_F32: + case Opcode::V_CMPX_O_F32: case Opcode::V_CMPX_NGE_F32: case Opcode::V_CMPX_NLG_F32: case Opcode::V_CMPX_NGT_F32: diff --git a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp index 4006374d8..bc0ce8f17 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp @@ -47,12 +47,12 @@ void Translator::EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpc EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {lhs, rhs})), false, cmpx); } -void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered) { +void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered, bool cmpx) { const auto lhs = IR::F32(ReadOperand(inst.src0, IR::Type::F32)); const auto rhs = IR::F32(ReadOperand(inst.src1, IR::Type::F32)); const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {lhs})), IR::U1(ir.Emit(IR::ValueOpcode::FPIsNan32, {rhs}))); - EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, false); + EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, cmpx); } void Translator::EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index a1d1d5953..ecd747ec9 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -124,7 +124,7 @@ private: bool signed_value, bool cmpx); void EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool half, bool cmpx); - void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered); + void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered, bool cmpx); void EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx); void V_CVT_F32_UBYTE(const Decoder::Instruction& inst, uint32_t byte_index); void V_CVT_F32_U32(const Decoder::Instruction& inst); diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 90b438d6f..557b146ed 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -285,8 +285,9 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_NLT_F16: EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual32, true, true); return true; - case O::V_CMP_O_F32: EmitFloatOrderedCompare(inst, true); return true; - case O::V_CMP_U_F32: EmitFloatOrderedCompare(inst, false); return true; + case O::V_CMP_O_F32: EmitFloatOrderedCompare(inst, true, false); return true; + case O::V_CMP_U_F32: EmitFloatOrderedCompare(inst, false, false); return true; + case O::V_CMPX_O_F32: EmitFloatOrderedCompare(inst, true, true); return true; case O::V_CMP_CLASS_F32: EmitFloatClassCompare(inst, false); return true; case O::V_CMPX_CLASS_F32: EmitFloatClassCompare(inst, true); return true; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 07c695b7a..d4c9493b6 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -16457,6 +16457,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::V_CMPX_GT_F32: case Opcode::V_CMPX_LG_F32: case Opcode::V_CMPX_GE_F32: + case Opcode::V_CMPX_O_F32: case Opcode::V_CMPX_NGE_F32: case Opcode::V_CMPX_NLG_F32: case Opcode::V_CMPX_NGT_F32: @@ -22344,6 +22345,69 @@ TestCase VectorCompareExecOps() { O::S_ENDPGM}}; } +TestCase VectorVopcCmpxOrderedCapturedExecMask() { + using O = ShaderOpcode; + struct CompareCase { + u32 lhs; + u32 rhs; + u32 initial_exec; + u32 expected_exec; + bool captured; + }; + constexpr std::array cases{{ + {0x3f800000u, 0x3f800000u, 1u, 1u, true}, // Finite value. + {0x7fc00000u, 0x7fc00000u, 1u, 0u, true}, // Quiet NaN. + {0x7f800001u, 0x7f800001u, 1u, 0u, true}, // Signaling NaN. + {0x7f800000u, 0x7f800000u, 1u, 1u, true}, // Infinity is ordered. + {0x80000000u, 0x80000000u, 1u, 1u, true}, // Signed zero is ordered. + {0x3f800000u, 0x3f800000u, 0u, 0u, true}, // Inactive lane. + {0x3f800000u, 0x7fc00000u, 1u, 0u, false}, // NaN in SRC1. + {0x7fc00000u, 0x3f800000u, 1u, 0u, false}, // NaN in SRC0. + }}; + constexpr u32 vcc_lo = 0x13579bdfu; + constexpr u32 vcc_hi = 0x2468ace0u; + + TestCase test; + test.name = "VectorVopcCmpxOrderedCapturedExecMask"; + auto &code = test.code; + for (const auto &entry : cases) { + test.initial.push_back(entry.lhs); + test.initial.push_back(entry.rhs); + } + test.expected = test.initial; + for (u32 i = 0; i < cases.size(); ++i) { + const auto &entry = cases[i]; + AppendVMovU32(&code, 30, i * 8u); + AppendBufferLoadDword(&code, 1, 30); + AppendVMovU32(&code, 30, i * 8u + 4u); + AppendBufferLoadDword(&code, 2, 30); + AppendSMovLiteral(&code, 106, vcc_lo); + AppendSMovLiteral(&code, 107, vcc_hi); + code.push_back(EncodeSMovB32(126, InlineU32(entry.initial_exec))); + code.push_back(entry.captured ? 0x7c2e0301u + : EncodeVopc(0x17u, Vgpr(1), 2)); + code.push_back(EncodeSMovB32(20, 126)); + code.push_back(EncodeSMovB32(21, 127)); + code.push_back(EncodeSMovB32(22, 106)); + code.push_back(EncodeSMovB32(23, 107)); + code.push_back(EncodeSMovB32(126, InlineU32(1))); + const u32 out = static_cast(cases.size()) * 2u + i * 4u; + AppendStoreSgprPair(&code, 20, out); + AppendStoreSgprPair(&code, 22, out + 2u); + test.expected.insert(test.expected.end(), + {entry.expected_exec, 0u, vcc_lo, vcc_hi}); + } + test.initial.resize(test.expected.size()); + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD, + O::V_CMPX_O_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"V_CMPX_O_F32 exec_lo, v1, v1", 6u}, + {"V_CMPX_O_F32 exec_lo, v1, v2", 2u}}; + test.compute_info.wave_size = 32; + test.has_compute_info = true; + return test; +} + TestCase VectorVop3FloatCompareNegSourceModifier() { using O = ShaderOpcode; @@ -28651,6 +28715,7 @@ std::vector MakeCases() { AddCase(Vop3CndmaskUsesSgprMaskLaneBits); AddCase(Vop3CndmaskAllowsDataSourceModifier); AddCase(VectorCompareExecOps); + AddCase(VectorVopcCmpxOrderedCapturedExecMask); AddCase(VectorVop3FloatCompareNegSourceModifier); AddCase(VectorVop3CmpxWritesExecMask); AddCase(VectorVopcSdwaCmpxWritesExecMask); @@ -33424,6 +33489,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorDppBoundsControlZeroPreservesDestination()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--cmpx-o-f32-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorVopcCmpxOrderedCapturedExecMask()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--buffer-snorm-store-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors());