From bb98f26433fa9e711c219aac1711fe8ecba5bf3a Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 7 Oct 2026 04:56:31 +0400 Subject: [PATCH] shader: add missing I64/U64 compare variants --- .../frontend/decode/ShaderDecoder.h | 15 ++++++ .../frontend/decode/VectorAluOps.cpp | 19 ++++++++ .../recompiler/frontend/translate/Compare.cpp | 1 + .../frontend/translate/Translate.cpp | 7 +++ .../recompiler/frontend/translate/Vector.cpp | 27 +++++++++++ tests/ShaderRecompilerComputeTests.cpp | 47 +++++++++++++++---- 6 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index d3ad3b669..c91292c4a 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -455,19 +455,34 @@ enum class Opcode { V_CMP_NE_U32, V_CMP_GE_U32, V_CMP_T_U32, + V_CMP_F_I64, V_CMP_EQ_I64, V_CMP_LT_I64, V_CMP_LE_I64, V_CMP_NE_I64, + V_CMP_T_I64, + V_CMP_F_U64, V_CMP_LT_U64, V_CMP_EQ_U64, V_CMP_LE_U64, V_CMP_GT_U64, V_CMP_NE_U64, V_CMP_GE_U64, + V_CMP_T_U64, + V_CMPX_F_I64, + V_CMPX_LT_I64, + V_CMPX_EQ_I64, + V_CMPX_LE_I64, V_CMPX_NE_I64, + V_CMPX_T_I64, + V_CMPX_F_U64, + V_CMPX_LT_U64, + V_CMPX_EQ_U64, V_CMPX_LE_U64, + V_CMPX_GT_U64, V_CMPX_NE_U64, + V_CMPX_GE_U64, + V_CMPX_T_U64, V_CMPX_LT_U32, V_CMPX_EQ_U32, V_CMPX_LE_U32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 13b1222e2..873d53f76 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -265,10 +265,18 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0xc7u, Opcode::V_CMP_T_U32}, {0xa2u, Opcode::V_CMP_EQ_I64, false}, {0xa1u, Opcode::V_CMP_LT_I64, false}, {0xa3u, Opcode::V_CMP_LE_I64, false}, {0xa5u, Opcode::V_CMP_NE_I64, false}, + {0xa0u, Opcode::V_CMP_F_I64, false}, {0xa7u, Opcode::V_CMP_T_I64, false}, + {0xb0u, Opcode::V_CMPX_F_I64, false}, {0xb1u, Opcode::V_CMPX_LT_I64, false}, + {0xb2u, Opcode::V_CMPX_EQ_I64, false}, {0xb3u, Opcode::V_CMPX_LE_I64, false}, + {0xb7u, Opcode::V_CMPX_T_I64, false}, {0xb5u, Opcode::V_CMPX_NE_I64, false}, {0xd1u, Opcode::V_CMPX_LT_U32}, {0xd2u, Opcode::V_CMPX_EQ_U32}, {0xd3u, Opcode::V_CMPX_LE_U32}, {0xd4u, Opcode::V_CMPX_GT_U32}, {0xd5u, Opcode::V_CMPX_NE_U32}, {0xd6u, Opcode::V_CMPX_GE_U32}, {0xe1u, Opcode::V_CMP_LT_U64, false}, + {0xe0u, Opcode::V_CMP_F_U64, false}, {0xe7u, Opcode::V_CMP_T_U64, false}, + {0xf0u, Opcode::V_CMPX_F_U64, false}, {0xf1u, Opcode::V_CMPX_LT_U64, false}, + {0xf2u, Opcode::V_CMPX_EQ_U64, false}, {0xf4u, Opcode::V_CMPX_GT_U64, false}, + {0xf6u, Opcode::V_CMPX_GE_U64, false}, {0xf7u, Opcode::V_CMPX_T_U64, false}, {0xe2u, Opcode::V_CMP_EQ_U64, false}, {0xe3u, Opcode::V_CMP_LE_U64, false}, {0xe4u, Opcode::V_CMP_GT_U64, false}, {0xe5u, Opcode::V_CMP_NE_U64, false}, {0xe6u, Opcode::V_CMP_GE_U64, false}, {0xf3u, Opcode::V_CMPX_LE_U64, false}, @@ -1555,9 +1563,20 @@ bool IsVopcCompareExec(Opcode opcode) { case Opcode::V_CMPX_GT_U32: case Opcode::V_CMPX_NE_U32: case Opcode::V_CMPX_GE_U32: + case Opcode::V_CMPX_F_I64: + case Opcode::V_CMPX_LT_I64: + case Opcode::V_CMPX_EQ_I64: + case Opcode::V_CMPX_LE_I64: case Opcode::V_CMPX_NE_I64: + case Opcode::V_CMPX_T_I64: + case Opcode::V_CMPX_F_U64: + case Opcode::V_CMPX_LT_U64: + case Opcode::V_CMPX_EQ_U64: case Opcode::V_CMPX_LE_U64: + case Opcode::V_CMPX_GT_U64: case Opcode::V_CMPX_NE_U64: + case Opcode::V_CMPX_GE_U64: + case Opcode::V_CMPX_T_U64: case Opcode::V_CMPX_LT_I16: case Opcode::V_CMPX_EQ_I16: case Opcode::V_CMPX_LE_I16: diff --git a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp index 3b4bee2c9..a2be70217 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp @@ -31,6 +31,7 @@ void Translator::EmitIntegerCompare(const Decoder::Instruction& inst, IR::ValueO const bool signed_64 = opcode == IR::ValueOpcode::SLessThan64 || opcode == IR::ValueOpcode::SLessThanEqual64 || inst.opcode == Decoder::Opcode::V_CMP_EQ_I64 || + inst.opcode == Decoder::Opcode::V_CMPX_EQ_I64 || inst.opcode == Decoder::Opcode::V_CMP_NE_I64 || inst.opcode == Decoder::Opcode::V_CMPX_NE_I64; const auto read = [&](const Decoder::Operand& operand) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 27fb25bf1..e4ab93128 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -889,9 +889,16 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_ case Decoder::Opcode::V_CMP_GT_U64: case Decoder::Opcode::V_CMP_NE_U64: case Decoder::Opcode::V_CMP_GE_U64: + case Decoder::Opcode::V_CMPX_LT_I64: + case Decoder::Opcode::V_CMPX_EQ_I64: + case Decoder::Opcode::V_CMPX_LE_I64: case Decoder::Opcode::V_CMPX_NE_I64: + case Decoder::Opcode::V_CMPX_LT_U64: + case Decoder::Opcode::V_CMPX_EQ_U64: case Decoder::Opcode::V_CMPX_LE_U64: + case Decoder::Opcode::V_CMPX_GT_U64: case Decoder::Opcode::V_CMPX_NE_U64: + case Decoder::Opcode::V_CMPX_GE_U64: include_vector(inst.src0, 2u); include_vector(inst.src1, 2u); break; diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 166767b15..4810e2a63 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -96,16 +96,34 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_LE_I32: EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, false, true); return; + case O::V_CMP_F_I64: + case O::V_CMP_F_U64: EmitCompareConstant(inst, false, false, false); return; + case O::V_CMP_T_I64: + case O::V_CMP_T_U64: EmitCompareConstant(inst, true, false, false); return; + case O::V_CMPX_F_I64: + case O::V_CMPX_F_U64: EmitCompareConstant(inst, false, false, true); return; + case O::V_CMPX_T_I64: + case O::V_CMPX_T_U64: EmitCompareConstant(inst, true, false, true); return; case O::V_CMP_EQ_I64: case O::V_CMP_EQ_U64: EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, false); return; + case O::V_CMPX_EQ_I64: + case O::V_CMPX_EQ_U64: + EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, true); + return; case O::V_CMP_LT_U64: EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan64, IR::Type::U64, false, false); return; + case O::V_CMPX_LT_U64: + EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan64, IR::Type::U64, false, true); + return; case O::V_CMP_GT_U64: EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, false); return; + case O::V_CMPX_GT_U64: + EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, true); + return; case O::V_CMP_NE_I64: case O::V_CMP_NE_U64: EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, false, false); @@ -118,9 +136,15 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMP_LT_I64: EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan64, IR::Type::U64, false, false); return; + case O::V_CMPX_LT_I64: + EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan64, IR::Type::U64, false, true); + return; case O::V_CMP_LE_I64: EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual64, IR::Type::U64, false, false); return; + case O::V_CMPX_LE_I64: + EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual64, IR::Type::U64, false, true); + return; case O::V_CMP_LE_U64: EmitIntegerCompare(inst, IR::ValueOpcode::ULessThanEqual64, IR::Type::U64, false, false); return; @@ -130,6 +154,9 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMP_GE_U64: EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual64, IR::Type::U64, false, false); return; + case O::V_CMPX_GE_U64: + EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual64, IR::Type::U64, false, true); + return; case O::V_CMP_EQ_U16: EmitInteger16Compare(inst, IR::ValueOpcode::IEqual32, false, false); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index a65c62ce2..bfff3808e 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -24729,10 +24729,10 @@ TestCase VectorCompareInteger64Edges() { const auto lhs = pairs[i][0], rhs = pairs[i][1]; const auto signed_lhs = std::bit_cast(lhs); const auto signed_rhs = std::bit_cast(rhs); - for (const auto [opcode, value] : std::array, 5>{{ - {0xa1, signed_lhs < signed_rhs}, {0xa3, signed_lhs <= signed_rhs}, - {0xa5, lhs != rhs}, - {0xe3, lhs <= rhs}, {0xe6, lhs >= rhs}}}) { + for (const auto [opcode, value] : std::array, 9>{{ + {0xa0, false}, {0xa1, signed_lhs < signed_rhs}, + {0xa3, signed_lhs <= signed_rhs}, {0xa5, lhs != rhs}, {0xa7, true}, + {0xe0, false}, {0xe3, lhs <= rhs}, {0xe6, lhs >= rhs}, {0xe7, true}}}) { test.code.push_back(EncodeVopc(opcode, Vgpr(1), 3)); store_mask(106, value); AppendVop3(&test.code, opcode, 20, Vgpr(1), 8); @@ -24750,6 +24750,12 @@ TestCase VectorCompareInteger64Edges() { AppendVop3(&test.code, 0xa3, 20, Vgpr(1), 255); test.code.push_back(0xffffffffu); store_mask(20, signed_lhs <= -1); + test.code.push_back(EncodeSop1(0x04, 10, 126)); + AppendVop3(&test.code, 0xb2, 126, 255, Vgpr(3)); // CMPX_EQ_I64 sign-extends -1. + test.code.push_back(0xffffffffu); + test.code.push_back(EncodeSop1(0x04, 20, 126)); + test.code.push_back(EncodeSop1(0x04, 126, 10)); + store_mask(20, -1 == signed_rhs); // Captured 8395e43f382309df PC 0x20cc: s[2:3] = (s[0:1] != 0) & EXEC. // Save the test buffer descriptor while its SGPRs hold the captured operands. test.code.push_back(EncodeSop1(0x04, 32, 0)); @@ -24765,8 +24771,9 @@ TestCase VectorCompareInteger64Edges() { AppendEnd(&test.code); test.initial.resize(test.expected.size()); test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32, - O::V_CMP_LT_I64, O::V_CMP_LE_I64, O::V_CMP_EQ_I64, O::V_CMP_NE_I64, - O::V_CMP_LE_U64, O::V_CMP_GE_U64, + O::V_CMP_F_I64, O::V_CMP_LT_I64, O::V_CMP_LE_I64, O::V_CMP_EQ_I64, + O::V_CMP_NE_I64, O::V_CMP_T_I64, O::V_CMPX_EQ_I64, + O::V_CMP_F_U64, O::V_CMP_LE_U64, O::V_CMP_GE_U64, O::V_CMP_T_U64, O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.required_spirv = {"OpSLessThan", "OpULessThanEqual", "OpUGreaterThanEqual"}; return test; @@ -24774,7 +24781,7 @@ TestCase VectorCompareInteger64Edges() { TestCase VectorCompareExecWaveMasks(u32 wave_size) { using O = ShaderOpcode; - // Rows exercise signed 16-bit truncation, full-width unsigned ordering, and + // Rows exercise signed 16-bit truncation, full-width integer ordering, and // ordered FP64 comparisons (including NaN, signed zero, and subnormals). constexpr std::array, 8>, 3> inputs{{ {{{0x1111ffff, 0x22220000}, {0x33338000, 0x44447fff}, @@ -24795,8 +24802,13 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { Compare{0x99, O::V_CMPX_LT_I16, 0}, Compare{0x9a, O::V_CMPX_EQ_I16, 0}, Compare{0x9b, O::V_CMPX_LE_I16, 0}, Compare{0x9c, O::V_CMPX_GT_I16, 0}, Compare{0x9d, O::V_CMPX_NE_I16, 0}, Compare{0x9e, O::V_CMPX_GE_I16, 0}, - Compare{0xf3, O::V_CMPX_LE_U64, 1}, Compare{0x33, O::V_CMPX_LE_F64, 2}, - Compare{0x36, O::V_CMPX_GE_F64, 2}}; + Compare{0xb0, O::V_CMPX_F_I64, 1}, Compare{0xb1, O::V_CMPX_LT_I64, 1}, + Compare{0xb2, O::V_CMPX_EQ_I64, 1}, Compare{0xb3, O::V_CMPX_LE_I64, 1}, + Compare{0xb7, O::V_CMPX_T_I64, 1}, Compare{0xf0, O::V_CMPX_F_U64, 1}, + Compare{0xf1, O::V_CMPX_LT_U64, 1}, Compare{0xf2, O::V_CMPX_EQ_U64, 1}, + Compare{0xf3, O::V_CMPX_LE_U64, 1}, Compare{0xf4, O::V_CMPX_GT_U64, 1}, + Compare{0xf6, O::V_CMPX_GE_U64, 1}, Compare{0xf7, O::V_CMPX_T_U64, 1}, + Compare{0x33, O::V_CMPX_LE_F64, 2}, Compare{0x36, O::V_CMPX_GE_F64, 2}}; constexpr u32 low_exec = 0xeeeeeeeeu, high_exec = 0x77777777u; constexpr u32 vcc_lo = 0x12345678u, vcc_hi = 0x89abcdefu; TestCase test; @@ -24832,7 +24844,15 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { case 0x9c: result = a > b; break; case 0x9d: result = a != b; break; case 0x9e: result = a >= b; break; + case 0xb0: case 0xf0: result = false; break; + case 0xb1: result = std::bit_cast(pair[0]) < std::bit_cast(pair[1]); break; + case 0xb2: case 0xf2: result = pair[0] == pair[1]; break; + case 0xb3: result = std::bit_cast(pair[0]) <= std::bit_cast(pair[1]); break; + case 0xb7: case 0xf7: result = true; break; + case 0xf1: result = pair[0] < pair[1]; break; case 0xf3: result = pair[0] <= pair[1]; break; + case 0xf4: result = pair[0] > pair[1]; break; + case 0xf6: result = pair[0] >= pair[1]; break; case 0x33: result = std::bit_cast(pair[0]) <= std::bit_cast(pair[1]); break; case 0x36: result = std::bit_cast(pair[0]) >= std::bit_cast(pair[1]); break; } @@ -40715,6 +40735,15 @@ int main(int argc, char **argv) { vulkan.CheckNativeIndirectDispatch(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--integer64-compare-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorCompareInteger64Edges()); + RunCase(&vulkan, VectorCompareExecWaveMasks(32)); + RunCase(&vulkan, VectorCompareExecWaveMasks(64)); + RunCase(&vulkan, VectorVopcCmpxNeU64CapturedExecMask()); + RunCase(&vulkan, VectorVop3CmpxNeI64CapturedExecMask()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--new-opcodes-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorCompareInteger64Edges());