From 85b60b65d158c48465eb0925e13f9fdfec9b868f Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Sun, 11 Oct 2026 10:40:37 +0000 Subject: [PATCH] fix(recompiler): fold unordered infinity comparisons --- core/libs/tests/VectorCompareConst.cpp | 35 +++++++++++++++++++ .../Translation/src/CompareInstructions.cpp | 18 ++++++++++ 2 files changed, 53 insertions(+) diff --git a/core/libs/tests/VectorCompareConst.cpp b/core/libs/tests/VectorCompareConst.cpp index 51ee5102f..8891fca64 100644 --- a/core/libs/tests/VectorCompareConst.cpp +++ b/core/libs/tests/VectorCompareConst.cpp @@ -18,6 +18,37 @@ static void Check(std::uint32_t encoding, RdnaOpcode opcode, bool exec) { TranslationContext context(program, block, 256); context.TranslateInstruction(instruction); } + +static void CheckUnorderedInfinity(RdnaOpcode opcode, std::uint32_t constant, bool constantIsLhs) { + RdnaInstruction instruction{}; + instruction.op = opcode; + instruction.family = RdnaInstructionFamily::VOPC; + instruction.sourceCount = 2u; + instruction.destination.kind = RdnaOperandKind::VccLo; + RdnaOperand& variable = constantIsLhs ? instruction.source1 : instruction.source0; + variable.kind = RdnaOperandKind::VectorRegister; + variable.reg = 1u; + RdnaOperand& infinity = constantIsLhs ? instruction.source0 : instruction.source1; + infinity.kind = RdnaOperandKind::FloatInlineConstant; + infinity.value = constant; + IrProgram program; + auto& block = program.CreateBlock(); + program.SetEntryBlock(block); + TranslationContext context(program, block, 256u); + context.TranslateInstruction(instruction); + const IrValue* vccValue = nullptr; + bool comparedFloats = false; + for (const auto* value : block.Instructions()) { + if (value->Opcode() == IrOpcode::SetVcc) vccValue = value->Argument(0u); + comparedFloats = comparedFloats || value->Opcode() == IrOpcode::FPUnordLessThanEqual32 || + value->Opcode() == IrOpcode::FPUnordGreaterThanEqual32; + } + Require(vccValue != nullptr && !comparedFloats); + Require(vccValue->Opcode() == IrOpcode::LogicalAnd); + const IrValue* result = vccValue->Argument(1u)->Resolve(); + Require(result->HasImmediate() && result->ImmediateU32() == 1u); +} + int main() { Check(0xa0u, RdnaOpcode::VCmpFI64, false); Check(0xa7u, RdnaOpcode::VCmpTI64, false); @@ -43,4 +74,8 @@ int main() { Check(0xffu, RdnaOpcode::VCmpxTruF16, true); Check(0xdfu, RdnaOpcode::VCmpxOF16, true); Check(0xf8u, RdnaOpcode::VCmpxUF16, true); + CheckUnorderedInfinity(RdnaOpcode::VCmpNgtF32, 0x7f800000u, false); + CheckUnorderedInfinity(RdnaOpcode::VCmpNltF32, 0xff800000u, false); + CheckUnorderedInfinity(RdnaOpcode::VCmpNltF32, 0x7f800000u, true); + CheckUnorderedInfinity(RdnaOpcode::VCmpNgtF32, 0xff800000u, true); } diff --git a/core/shader/recompiler/Translation/src/CompareInstructions.cpp b/core/shader/recompiler/Translation/src/CompareInstructions.cpp index a96ced998..eac2f2a94 100644 --- a/core/shader/recompiler/Translation/src/CompareInstructions.cpp +++ b/core/shader/recompiler/Translation/src/CompareInstructions.cpp @@ -40,6 +40,16 @@ IrOpcode mirroredFloatCompare(IrOpcode opcode) { } } +bool unorderedInfinityCompareIsTrue(IrOpcode opcode, bool constantIsLhs, std::uint32_t constant) { + if (constant == 0x7f800000u) { + return constantIsLhs ? opcode == IrOpcode::FPUnordGreaterThanEqual32 : opcode == IrOpcode::FPUnordLessThanEqual32; + } + if (constant == 0xff800000u) { + return constantIsLhs ? opcode == IrOpcode::FPUnordLessThanEqual32 : opcode == IrOpcode::FPUnordGreaterThanEqual32; + } + return false; +} + } void TranslateCompareInstruction(IrBuilder& builder, const RdnaInstruction& instruction) { @@ -90,6 +100,14 @@ void TranslationContext::emitFloatCompare(const RdnaInstruction& inst, IrOpcode if (swap) std::swap(lhsBits, rhsBits); const auto lhsConstant = constantBits(*lhsBits); const auto rhsConstant = constantBits(*rhsBits); + if (lhsConstant.has_value() && !rhsConstant.has_value() && unorderedInfinityCompareIsTrue(opcode, true, *lhsConstant)) { + emitCompareConstant(inst, true, false, cmpx); + return; + } + if (!lhsConstant.has_value() && rhsConstant.has_value() && unorderedInfinityCompareIsTrue(opcode, false, *rhsConstant)) { + emitCompareConstant(inst, true, false, cmpx); + return; + } if ((f32DenormalFlush & 1u) != 0u && lhsConstant.has_value() != rhsConstant.has_value()) { const IrOpcode oriented = lhsConstant.has_value() ? mirroredFloatCompare(opcode) : opcode; emitCompareResult(inst, floatCompareWithConstant(oriented, IrU32(lhsConstant.has_value() ? *rhsBits : *lhsBits), lhsConstant.has_value() ? *lhsConstant : *rhsConstant), false, cmpx);