diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 0b1926afc..16549f109 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -351,6 +351,7 @@ enum class Opcode { V_CMP_GE_I32, V_CMP_T_I32, V_CMP_CLASS_F32, + V_CMPX_CLASS_F32, V_CMP_LT_I16, V_CMP_EQ_I16, V_CMP_LE_I16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 26f25e0e2..52acef97b 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -227,7 +227,8 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0x8eu, Opcode::V_CMP_GE_I16}, {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}, {0xa9u, Opcode::V_CMP_LT_U16}, + {0x96u, Opcode::V_CMPX_GE_I32}, {0x98u, Opcode::V_CMPX_CLASS_F32}, + {0xa9u, Opcode::V_CMP_LT_U16}, {0xaau, Opcode::V_CMP_EQ_U16}, {0xabu, Opcode::V_CMP_LE_U16}, {0xacu, Opcode::V_CMP_GT_U16}, {0xadu, Opcode::V_CMP_NE_U16}, {0xaeu, Opcode::V_CMP_GE_U16}, {0xbcu, Opcode::V_CMPX_GT_U16}, @@ -895,7 +896,8 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMPX_NGT_F16: case Opcode::V_CMPX_NEQ_F16: case Opcode::V_CMPX_NLT_F16: - case Opcode::V_CMP_CLASS_F32: return true; + case Opcode::V_CMP_CLASS_F32: + case Opcode::V_CMPX_CLASS_F32: return true; default: return false; } } @@ -1518,6 +1520,7 @@ bool IsVopcCompareExec(Opcode opcode) { case Opcode::V_CMPX_GT_I32: case Opcode::V_CMPX_NE_I32: case Opcode::V_CMPX_GE_I32: + case Opcode::V_CMPX_CLASS_F32: case Opcode::V_CMPX_LT_U32: case Opcode::V_CMPX_EQ_U32: case Opcode::V_CMPX_LE_U32: diff --git a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp index 8d6f8707d..4006374d8 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp @@ -55,11 +55,11 @@ void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, false); } -void Translator::V_CMP_CLASS_F32(const Decoder::Instruction& inst) { +void Translator::EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx) { const auto value = ReadOperand(inst.src0, IR::Type::F32); const auto mask = ReadOperand(inst.src1, IR::Type::U32); EmitCompareResult(inst, IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})), false, - false); + cmpx); } } // namespace Libs::Graphics::ShaderRecompiler::Frontend diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 8ccf1d3cc..4cf389536 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -127,7 +127,7 @@ private: void EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool half, bool cmpx); void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered); - void V_CMP_CLASS_F32(const Decoder::Instruction& inst); + 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); void V_CVT_F32_I32(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 e46fbfd4d..943854275 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -271,7 +271,8 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error 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_CLASS_F32: V_CMP_CLASS_F32(inst); return true; + case O::V_CMP_CLASS_F32: EmitFloatClassCompare(inst, false); return true; + case O::V_CMPX_CLASS_F32: EmitFloatClassCompare(inst, true); return true; case O::V_CVT_F32_UBYTE0: V_CVT_F32_UBYTE(inst, 0); return true; case O::V_CVT_F32_UBYTE1: V_CVT_F32_UBYTE(inst, 1); return true; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index f12c7205a..988f336fd 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11931,6 +11931,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::V_CMPX_NEQ_F32: case Opcode::V_CMPX_NLT_F32: case Opcode::V_CMP_CLASS_F32: + case Opcode::V_CMPX_CLASS_F32: case Opcode::V_CMP_LT_F16: case Opcode::V_CMP_EQ_F16: case Opcode::V_CMP_LE_F16: @@ -15785,6 +15786,38 @@ TestCase VectorCompareClassF32() { O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase VectorVopcSdwaCmpxClassF32CapturedExecMask() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 13, 0x7fc00000u); // Quiet NaN. + AppendVMovU32(&code, 3, 7); + AppendVMovU32(&code, 31, 0); + code.push_back(EncodeSMovB32(106, InlineU32(2))); // qNaN class mask. + code.insert(code.end(), {0x7d30d4f9u, 0x8606000du}); + AppendBufferStoreDword(&code, 3, 31); + + code.push_back(EncodeSMovB32(126, InlineU32(1))); // Restore lane-zero EXEC. + AppendVMovLiteral(&code, 13, 0x3f800000u); // Positive normal. + AppendVMovU32(&code, 3, 9); + AppendVMovU32(&code, 31, 4); + code.push_back(EncodeSMovB32(106, InlineU32(2))); // qNaN class mask. + code.insert(code.end(), {0x7d30d4f9u, 0x8606000du}); + AppendBufferStoreDword(&code, 3, 31); + AppendEnd(&code); + + TestCase test{ + "VectorVopcSdwaCmpxClassF32CapturedExecMask", + code, + {0x11111111u, 0x22222222u}, + {7u, 0x22222222u}, + {O::S_MOV_B32, O::V_MOV_B32, O::V_CMPX_CLASS_F32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; + test.decoded_counts = { + {"V_CMPX_CLASS_F32 exec_lo, v13, vcc_lo", 2}}; + return test; +} + TestCase VectorCompareF16Ops() { using O = ShaderOpcode; @@ -20812,6 +20845,7 @@ std::vector MakeCases() { AddCase(VectorVopcCmpxNeU64CapturedExecMask); AddCase(VectorVop3CmpxNeI64CapturedExecMask); AddCase(VectorCompareClassF32); + AddCase(VectorVopcSdwaCmpxClassF32CapturedExecMask); AddCase(VectorCompareF16Ops); AddCase(Vop2SdwaCndmaskSourceModifier); AddCase(Vop2SdwaCndmaskCapturedByte3Source); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 8fa264552..f9c202d0a 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4231,6 +4231,67 @@ void TestNewShaderRecompilerRejectsDppOn64BitCompares() { } } +void TestNewShaderRecompilerCapturedVopcSdwaCmpxClass() { + using namespace ShaderRecompiler; + + const uint32_t shader[] = { + 0x7d30d4f9u, 0x8606000du, // v_cmpx_class_f32 exec, v13, vcc_lo (SDWA) + EncodeSopp(0x01), + }; + + Decoder::Instruction decoded; + std::string error; + Check(Decoder::DecodeInstruction(shader, 0u, decoded, &error), + error.c_str()); + Check(decoded.family == Decoder::Family::VOPC && + decoded.opcode == Decoder::Opcode::V_CMPX_CLASS_F32 && + decoded.opcode_id == 0x98u && decoded.word_count == 2u && + decoded.raw_count == 2u && + decoded.dst.kind == Decoder::OperandKind::ExecLo && + decoded.src_count == 2u && + decoded.src0.kind == Decoder::OperandKind::Vgpr && + decoded.src0.reg == 13u && decoded.src0.sdwa_sel == 6u && + decoded.src1.kind == Decoder::OperandKind::VccLo && + decoded.src1.sdwa_sel == 6u, + "decoder rejected captured SDWA V_CMPX_CLASS_F32 fields"); + + Decoder::Program program; + CFG::Graph graph; + IR::Program ir; + ShaderPixelInputInfo pixel{}; + Frontend::TranslateOptions translate_options{}; + translate_options.stage = ShaderType::Pixel; + translate_options.wave_size = 64u; + translate_options.pixel = &pixel; + Check(Decoder::DecodeProgram(shader, program, &error) && + CFG::BuildGraph(program, graph, &error) && + Frontend::TranslateProgram(program, graph, translate_options, ir, + &error), + error.c_str()); + uint32_t class_compares = 0u; + uint32_t dynamic_exec_writes = 0u; + for (const auto *block : ir.blocks) { + for (const auto &inst : *block) { + class_compares += + inst.GetOpcode() == IR::ValueOpcode::FPCmpClass32 ? 1u : 0u; + if (inst.GetOpcode() == IR::ValueOpcode::SetExec && + !inst.Arg(0).Resolve().IsImmediate()) { + dynamic_exec_writes++; + } + } + } + Check(class_compares == 1u && dynamic_exec_writes == 1u, + "captured V_CMPX_CLASS_F32 did not lower to class compare plus EXEC update"); + + auto options = MakeCompileOptions(ShaderType::Pixel); + CompileResult result; + Check(TryRecompile(shader, options, result, &error), error.c_str()); + Check(Common::ContainsStr(result.decoded_dump, + "V_CMPX_CLASS_F32 exec_lo, v13, vcc_lo"), + "captured SDWA V_CMPX_CLASS_F32 was not present in the decoded dump"); + CheckSpirvBinaryValidates(result.spirv); +} + void TestNewShaderRecompilerIrLookupMissFailsExplicitly() { ShaderRecompiler::Decoder::Program decoded; ShaderRecompiler::Decoder::Instruction missing; @@ -12476,6 +12537,7 @@ int main() { // ShaderRecompilerComputeTests; keep the distinct decoder contract checks // here. TestNewShaderDecoderArchitecture(); + TestNewShaderRecompilerCapturedVopcSdwaCmpxClass(); TestNewShaderRecompilerRejectsDppOn64BitCompares(); TestNewShaderRecompilerIrLookupMissFailsExplicitly(); TestPsInputCountRegisterDecode();