diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h index 287d219b..6bd749e6 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h @@ -146,6 +146,15 @@ inline constexpr auto EmitFPOrdEqual32 = EmitFloatCompare32; EMIT_NATIVE(FPOrdEqual64, OpFOrdEqual, U1, uint32_t, uint32_t) EMIT_NATIVE(FPOrdLessThanEqual64, OpFOrdLessThanEqual, U1, uint32_t, uint32_t) EMIT_NATIVE(FPOrdGreaterThanEqual64, OpFOrdGreaterThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdNotEqual64, OpFOrdNotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdLessThan64, OpFOrdLessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdGreaterThan64, OpFOrdGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordEqual64, OpFUnordEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordNotEqual64, OpFUnordNotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordLessThan64, OpFUnordLessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordGreaterThan64, OpFUnordGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordLessThanEqual64, OpFUnordLessThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordGreaterThanEqual64, OpFUnordGreaterThanEqual, U1, uint32_t, uint32_t) inline constexpr auto EmitFPUnordEqual32 = EmitFloatCompare32; inline constexpr auto EmitFPOrdNotEqual32 = EmitFloatCompare32; inline constexpr auto EmitFPUnordNotEqual32 = EmitFloatCompare32; @@ -158,6 +167,7 @@ inline constexpr auto EmitFPUnordLessThanEqual32 = EmitFloatCompare32; inline constexpr auto EmitFPUnordGreaterThanEqual32 = EmitFloatCompare32; EMIT_NATIVE(FPIsNan32, OpIsNan, U1, uint32_t) +EMIT_NATIVE(FPIsNan64, OpIsNan, U1, uint32_t) inline constexpr auto EmitFPCmpClass32 = EmitClassMaskF32; inline constexpr auto EmitFPCmpClass16 = EmitClassMaskF16; EMIT_NATIVE(FPAdd32, OpFAdd, F32, uint32_t, uint32_t) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 1d7d1c65..251b5be5 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -374,10 +374,38 @@ enum class Opcode { V_CMP_NEQ_F32, V_CMP_NLT_F32, V_CMP_TRU_F32, + V_CMP_F_F64, + V_CMP_LT_F64, V_CMP_EQ_F64, V_CMP_LE_F64, + V_CMP_GT_F64, + V_CMP_LG_F64, + V_CMP_GE_F64, + V_CMP_O_F64, + V_CMP_U_F64, + V_CMP_NGE_F64, + V_CMP_NLG_F64, + V_CMP_NGT_F64, + V_CMP_NLE_F64, + V_CMP_NEQ_F64, + V_CMP_NLT_F64, + V_CMP_TRU_F64, + V_CMPX_F_F64, + V_CMPX_LT_F64, + V_CMPX_EQ_F64, V_CMPX_LE_F64, + V_CMPX_GT_F64, + V_CMPX_LG_F64, V_CMPX_GE_F64, + V_CMPX_O_F64, + V_CMPX_U_F64, + V_CMPX_NGE_F64, + V_CMPX_NLG_F64, + V_CMPX_NGT_F64, + V_CMPX_NLE_F64, + V_CMPX_NEQ_F64, + V_CMPX_NLT_F64, + V_CMPX_TRU_F64, V_CMPX_LT_F32, V_CMPX_EQ_F32, V_CMPX_LE_F32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 7c36ca0b..094b8dce 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -236,8 +236,23 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {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}, - {0x22u, Opcode::V_CMP_EQ_F64, false}, {0x23u, Opcode::V_CMP_LE_F64, false}, - {0x33u, Opcode::V_CMPX_LE_F64, false}, {0x36u, Opcode::V_CMPX_GE_F64, false}, + {0x20u, Opcode::V_CMP_F_F64, false}, {0x21u, Opcode::V_CMP_LT_F64, false}, + {0x22u, Opcode::V_CMP_EQ_F64, false}, {0x23u, Opcode::V_CMP_LE_F64, false}, + {0x24u, Opcode::V_CMP_GT_F64, false}, {0x25u, Opcode::V_CMP_LG_F64, false}, + {0x26u, Opcode::V_CMP_GE_F64, false}, {0x27u, Opcode::V_CMP_O_F64, false}, + {0x28u, Opcode::V_CMP_U_F64, false}, {0x29u, Opcode::V_CMP_NGE_F64, false}, + {0x2au, Opcode::V_CMP_NLG_F64, false}, {0x2bu, Opcode::V_CMP_NGT_F64, false}, + {0x2cu, Opcode::V_CMP_NLE_F64, false}, {0x2du, Opcode::V_CMP_NEQ_F64, false}, + {0x2eu, Opcode::V_CMP_NLT_F64, false}, {0x2fu, Opcode::V_CMP_TRU_F64, false}, + {0x30u, Opcode::V_CMPX_F_F64, false}, + {0x31u, Opcode::V_CMPX_LT_F64, false}, {0x32u, Opcode::V_CMPX_EQ_F64, false}, + {0x33u, Opcode::V_CMPX_LE_F64, false}, {0x34u, Opcode::V_CMPX_GT_F64, false}, + {0x35u, Opcode::V_CMPX_LG_F64, false}, {0x36u, Opcode::V_CMPX_GE_F64, false}, + {0x37u, Opcode::V_CMPX_O_F64, false}, {0x38u, Opcode::V_CMPX_U_F64, false}, + {0x39u, Opcode::V_CMPX_NGE_F64, false}, {0x3au, Opcode::V_CMPX_NLG_F64, false}, + {0x3bu, Opcode::V_CMPX_NGT_F64, false}, {0x3cu, Opcode::V_CMPX_NLE_F64, false}, + {0x3du, Opcode::V_CMPX_NEQ_F64, false}, {0x3eu, Opcode::V_CMPX_NLT_F64, false}, + {0x3fu, Opcode::V_CMPX_TRU_F64, false}, {0x80u, Opcode::V_CMP_F_I32}, {0x81u, Opcode::V_CMP_LT_I32}, {0x82u, Opcode::V_CMP_EQ_I32}, {0x83u, Opcode::V_CMP_LE_I32}, {0x84u, Opcode::V_CMP_GT_I32}, {0x85u, Opcode::V_CMP_NE_I32}, @@ -879,10 +894,38 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) { case Opcode::V_CMP_F_F32: case Opcode::V_CMP_LT_F32: case Opcode::V_CMP_EQ_F32: + case Opcode::V_CMP_F_F64: + case Opcode::V_CMP_LT_F64: case Opcode::V_CMP_EQ_F64: case Opcode::V_CMP_LE_F64: + case Opcode::V_CMP_GT_F64: + case Opcode::V_CMP_LG_F64: + case Opcode::V_CMP_GE_F64: + case Opcode::V_CMP_O_F64: + case Opcode::V_CMP_U_F64: + case Opcode::V_CMP_NGE_F64: + case Opcode::V_CMP_NLG_F64: + case Opcode::V_CMP_NGT_F64: + case Opcode::V_CMP_NLE_F64: + case Opcode::V_CMP_NEQ_F64: + case Opcode::V_CMP_NLT_F64: + case Opcode::V_CMP_TRU_F64: + case Opcode::V_CMPX_F_F64: + case Opcode::V_CMPX_LT_F64: + case Opcode::V_CMPX_EQ_F64: case Opcode::V_CMPX_LE_F64: + case Opcode::V_CMPX_GT_F64: + case Opcode::V_CMPX_LG_F64: case Opcode::V_CMPX_GE_F64: + case Opcode::V_CMPX_O_F64: + case Opcode::V_CMPX_U_F64: + case Opcode::V_CMPX_NGE_F64: + case Opcode::V_CMPX_NLG_F64: + case Opcode::V_CMPX_NGT_F64: + case Opcode::V_CMPX_NLE_F64: + case Opcode::V_CMPX_NEQ_F64: + case Opcode::V_CMPX_NLT_F64: + case Opcode::V_CMPX_TRU_F64: case Opcode::V_CMP_LE_F32: case Opcode::V_CMP_GT_F32: case Opcode::V_CMP_LG_F32: @@ -1202,10 +1245,9 @@ VopcSdwaFields DecodeVopcSdwaFields(uint32_t modifier) { } bool SupportsVopcSdwa(Opcode opcode) { + // F64 SDWA operand handling is not implemented. return opcode != Opcode::UNSUPPORTED && opcode != Opcode::V_CMP_NE_I64 && - opcode != Opcode::V_CMP_EQ_F64 && - opcode != Opcode::V_CMP_LE_F64 && opcode != Opcode::V_CMPX_LE_F64 && - opcode != Opcode::V_CMPX_GE_F64; + (opcode < Opcode::V_CMP_F_F64 || opcode > Opcode::V_CMPX_TRU_F64); } void DecodeVopcSdwa(uint32_t pc, std::span code, uint32_t word_index, @@ -1538,8 +1580,22 @@ void ApplyNativeVop3SourceModifiers(Instruction& inst, uint32_t abs, uint32_t ne bool IsVopcCompareExec(Opcode opcode) { switch (opcode) { + case Opcode::V_CMPX_F_F64: + case Opcode::V_CMPX_LT_F64: + case Opcode::V_CMPX_EQ_F64: case Opcode::V_CMPX_LE_F64: + case Opcode::V_CMPX_GT_F64: + case Opcode::V_CMPX_LG_F64: case Opcode::V_CMPX_GE_F64: + case Opcode::V_CMPX_O_F64: + case Opcode::V_CMPX_U_F64: + case Opcode::V_CMPX_NGE_F64: + case Opcode::V_CMPX_NLG_F64: + case Opcode::V_CMPX_NGT_F64: + case Opcode::V_CMPX_NLE_F64: + case Opcode::V_CMPX_NEQ_F64: + case Opcode::V_CMPX_NLT_F64: + case Opcode::V_CMPX_TRU_F64: case Opcode::V_CMPX_LT_F32: case Opcode::V_CMPX_EQ_F32: case Opcode::V_CMPX_LE_F32: diff --git a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp index a2be7021..aa7d2adc 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Compare.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Compare.cpp @@ -65,11 +65,14 @@ void Translator::EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpc EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {lhs, rhs}, flags)), false, cmpx); } -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}))); +void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered, bool cmpx, + bool is64) { + const auto type = is64 ? IR::Type::F64 : IR::Type::F32; + const auto is_nan_op = is64 ? IR::ValueOpcode::FPIsNan64 : IR::ValueOpcode::FPIsNan32; + const auto lhs = ReadOperand(inst.src0, type); + const auto rhs = ReadOperand(inst.src1, type); + const auto unordered = ir.LogicalOr(IR::U1(ir.Emit(is_nan_op, {lhs})), + IR::U1(ir.Emit(is_nan_op, {rhs}))); EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, cmpx); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 73d426f9..b867cd94 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -875,10 +875,38 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_ case Decoder::Opcode::V_FLOOR_F64: case Decoder::Opcode::V_FRACT_F64: include_vector(inst.dst, 2u); [[fallthrough]]; case Decoder::Opcode::V_CVT_F32_F64: include_vector(inst.src0, 2u); break; + case Decoder::Opcode::V_CMP_F_F64: + case Decoder::Opcode::V_CMP_LT_F64: case Decoder::Opcode::V_CMP_EQ_F64: case Decoder::Opcode::V_CMP_LE_F64: + case Decoder::Opcode::V_CMP_GT_F64: + case Decoder::Opcode::V_CMP_LG_F64: + case Decoder::Opcode::V_CMP_GE_F64: + case Decoder::Opcode::V_CMP_O_F64: + case Decoder::Opcode::V_CMP_U_F64: + case Decoder::Opcode::V_CMP_NGE_F64: + case Decoder::Opcode::V_CMP_NLG_F64: + case Decoder::Opcode::V_CMP_NGT_F64: + case Decoder::Opcode::V_CMP_NLE_F64: + case Decoder::Opcode::V_CMP_NEQ_F64: + case Decoder::Opcode::V_CMP_NLT_F64: + case Decoder::Opcode::V_CMP_TRU_F64: + case Decoder::Opcode::V_CMPX_F_F64: + case Decoder::Opcode::V_CMPX_LT_F64: + case Decoder::Opcode::V_CMPX_EQ_F64: case Decoder::Opcode::V_CMPX_LE_F64: + case Decoder::Opcode::V_CMPX_GT_F64: + case Decoder::Opcode::V_CMPX_LG_F64: case Decoder::Opcode::V_CMPX_GE_F64: + case Decoder::Opcode::V_CMPX_O_F64: + case Decoder::Opcode::V_CMPX_U_F64: + case Decoder::Opcode::V_CMPX_NGE_F64: + case Decoder::Opcode::V_CMPX_NLG_F64: + case Decoder::Opcode::V_CMPX_NGT_F64: + case Decoder::Opcode::V_CMPX_NLE_F64: + case Decoder::Opcode::V_CMPX_NEQ_F64: + case Decoder::Opcode::V_CMPX_NLT_F64: + case Decoder::Opcode::V_CMPX_TRU_F64: case Decoder::Opcode::V_CMP_EQ_I64: case Decoder::Opcode::V_CMP_LT_I64: case Decoder::Opcode::V_CMP_LE_I64: diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index e9b9486d..c0b98814 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -127,7 +127,8 @@ 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, bool cmpx); + void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered, bool cmpx, + bool is64 = false); void EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx, bool half = false); 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 059b9471..44872bf7 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -242,6 +242,74 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_CMPX_GE_F64: EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual64, false, true); return; + case O::V_CMP_F_F64: EmitCompareConstant(inst, false, false, false); return; + case O::V_CMP_TRU_F64: EmitCompareConstant(inst, true, false, false); return; + case O::V_CMPX_F_F64: EmitCompareConstant(inst, false, false, true); return; + case O::V_CMPX_TRU_F64: EmitCompareConstant(inst, true, false, true); return; + case O::V_CMP_LT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan64, false, false); + return; + case O::V_CMPX_LT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdLessThan64, false, true); + return; + case O::V_CMPX_EQ_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual64, false, true); + return; + case O::V_CMP_GT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan64, false, false); + return; + case O::V_CMPX_GT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThan64, false, true); + return; + case O::V_CMP_LG_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdNotEqual64, false, false); + return; + case O::V_CMPX_LG_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdNotEqual64, false, true); + return; + case O::V_CMP_GE_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPOrdGreaterThanEqual64, false, false); + return; + case O::V_CMP_NLG_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordEqual64, false, false); + return; + case O::V_CMPX_NLG_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordEqual64, false, true); + return; + case O::V_CMP_NEQ_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual64, false, false); + return; + case O::V_CMPX_NEQ_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordNotEqual64, false, true); + return; + case O::V_CMP_NLE_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThan64, false, false); + return; + case O::V_CMPX_NLE_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThan64, false, true); + return; + case O::V_CMP_NLT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual64, false, false); + return; + case O::V_CMPX_NLT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordGreaterThanEqual64, false, true); + return; + case O::V_CMP_NGE_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThan64, false, false); + return; + case O::V_CMPX_NGE_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThan64, false, true); + return; + case O::V_CMP_NGT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual64, false, false); + return; + case O::V_CMPX_NGT_F64: + EmitFloatCompare(inst, IR::ValueOpcode::FPUnordLessThanEqual64, false, true); + return; + case O::V_CMP_O_F64: EmitFloatOrderedCompare(inst, true, false, true); return; + case O::V_CMP_U_F64: EmitFloatOrderedCompare(inst, false, false, true); return; + case O::V_CMPX_O_F64: EmitFloatOrderedCompare(inst, true, true, true); return; + case O::V_CMPX_U_F64: EmitFloatOrderedCompare(inst, false, true, true); return; case O::V_CMPX_EQ_F32: EmitFloatCompare(inst, IR::ValueOpcode::FPOrdEqual32, false, true); return; diff --git a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc index b95d2917..ed2a0dc0 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc @@ -162,6 +162,15 @@ VALUE_OPCODE(FPOrdEqual32, U1, F32, F32) VALUE_OPCODE(FPOrdEqual64, U1, F64, F64) VALUE_OPCODE(FPOrdLessThanEqual64, U1, F64, F64) VALUE_OPCODE(FPOrdGreaterThanEqual64, U1, F64, F64) +VALUE_OPCODE(FPOrdNotEqual64, U1, F64, F64) +VALUE_OPCODE(FPOrdLessThan64, U1, F64, F64) +VALUE_OPCODE(FPOrdGreaterThan64, U1, F64, F64) +VALUE_OPCODE(FPUnordEqual64, U1, F64, F64) +VALUE_OPCODE(FPUnordNotEqual64, U1, F64, F64) +VALUE_OPCODE(FPUnordLessThan64, U1, F64, F64) +VALUE_OPCODE(FPUnordGreaterThan64, U1, F64, F64) +VALUE_OPCODE(FPUnordLessThanEqual64, U1, F64, F64) +VALUE_OPCODE(FPUnordGreaterThanEqual64, U1, F64, F64) VALUE_OPCODE(FPUnordEqual32, U1, F32, F32) VALUE_OPCODE(FPOrdNotEqual32, U1, F32, F32) VALUE_OPCODE(FPUnordNotEqual32, U1, F32, F32) @@ -174,6 +183,7 @@ VALUE_OPCODE(FPUnordLessThanEqual32, U1, F32, F32) VALUE_OPCODE(FPOrdGreaterThanEqual32, U1, F32, F32) VALUE_OPCODE(FPUnordGreaterThanEqual32, U1, F32, F32) VALUE_OPCODE(FPIsNan32, U1, F32) +VALUE_OPCODE(FPIsNan64, U1, F64) VALUE_OPCODE(FPCmpClass32, U1, F32, U32) VALUE_OPCODE(FPCmpClass16, U1, U32, U32) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 2b770989..6bbb4115 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -25626,7 +25626,7 @@ TestCase VectorCompareInteger64Edges() { TestCase VectorCompareExecWaveMasks(u32 wave_size) { using O = ShaderOpcode; // Rows exercise signed 16-bit truncation, full-width integer ordering, and - // ordered FP64 comparisons (including NaN, signed zero, and subnormals). + // ordered/unordered FP64 comparisons (including NaN, signed zero, and subnormals). constexpr std::array, 8>, 3> inputs{{ {{{0x1111ffff, 0x22220000}, {0x33338000, 0x44447fff}, {0x55550000, 0x66660000}, {0x77777fff, 0x88888000}, @@ -25652,7 +25652,14 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { 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}}; + Compare{0x30, O::V_CMPX_F_F64, 2}, Compare{0x3f, O::V_CMPX_TRU_F64, 2}, + Compare{0x33, O::V_CMPX_LE_F64, 2}, Compare{0x36, O::V_CMPX_GE_F64, 2}, + Compare{0x31, O::V_CMPX_LT_F64, 2}, Compare{0x32, O::V_CMPX_EQ_F64, 2}, + Compare{0x34, O::V_CMPX_GT_F64, 2}, Compare{0x35, O::V_CMPX_LG_F64, 2}, + Compare{0x37, O::V_CMPX_O_F64, 2}, Compare{0x38, O::V_CMPX_U_F64, 2}, + Compare{0x39, O::V_CMPX_NGE_F64, 2}, Compare{0x3a, O::V_CMPX_NLG_F64, 2}, + Compare{0x3b, O::V_CMPX_NGT_F64, 2}, Compare{0x3c, O::V_CMPX_NLE_F64, 2}, + Compare{0x3d, O::V_CMPX_NEQ_F64, 2}, Compare{0x3e, O::V_CMPX_NLT_F64, 2}}; constexpr u32 low_exec = 0xeeeeeeeeu, high_exec = 0x77777777u; constexpr u32 vcc_lo = 0x12345678u, vcc_hi = 0x89abcdefu; TestCase test; @@ -25668,6 +25675,8 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { code.push_back(EncodeSop1(0x04, 10, 126)); AppendSMovLiteral(&code, 106, vcc_lo); AppendSMovLiteral(&code, 107, vcc_hi); + AppendSMovLiteral(&code, 22, vcc_lo); + AppendSMovLiteral(&code, 23, vcc_hi); for (const auto compare : comparisons) { for (u32 word = 0; word < 4; ++word) { code.push_back(EncodeVop2(0x1a, 30, InlineU32(2), 0)); @@ -25680,6 +25689,8 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { const auto pair = inputs[compare.group][lane % 8]; const auto a = std::bit_cast(uint16_t(pair[0])); const auto b = std::bit_cast(uint16_t(pair[1])); + const auto lhs = std::bit_cast(pair[0]); + const auto rhs = std::bit_cast(pair[1]); bool result = false; switch (compare.encoding) { case 0x99: result = a < b; break; @@ -25688,17 +25699,31 @@ 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 0x30: 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 0x3f: 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; + case 0x33: result = lhs <= rhs; break; + case 0x36: result = lhs >= rhs; break; + case 0x31: result = lhs < rhs; break; + case 0x32: result = lhs == rhs; break; + case 0x34: result = lhs > rhs; break; + case 0x35: result = lhs < rhs || lhs > rhs; break; + case 0x37: result = !std::isnan(lhs) && !std::isnan(rhs); break; + case 0x38: result = std::isnan(lhs) || std::isnan(rhs); break; + // The N* predicates are the logical negation of their ordered counterpart, + // which is true for any comparison involving a NaN (IEEE unordered compares). + case 0x39: result = !(lhs >= rhs); break; + case 0x3a: result = !(lhs < rhs || lhs > rhs); break; + case 0x3b: result = !(lhs > rhs); break; + case 0x3c: result = !(lhs <= rhs); break; + case 0x3d: result = !(lhs == rhs); break; + case 0x3e: result = !(lhs < rhs); break; } if (result) expected_exec[lane / 32] |= 1u << (lane % 32); } @@ -25707,14 +25732,14 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { for (const bool vop3 : {false, true}) { AppendSMovLiteral(&code, 126, low_exec); if (wave_size == 64) AppendSMovLiteral(&code, 127, high_exec); - if (vop3) AppendVop3(&code, compare.encoding, 20, Vgpr(1), Vgpr(3)); + if (vop3) AppendVop3(&code, compare.encoding, 22, Vgpr(1), Vgpr(3)); else code.push_back(EncodeVopc(compare.encoding, Vgpr(1), 3)); code.push_back(EncodeSMovB32(20, 126)); code.push_back(EncodeSMovB32(21, 127)); code.push_back(EncodeSop1(0x04, 126, 10)); - for (const auto [reg, value] : std::array, 4>{{ + for (const auto [reg, value] : std::array, 6>{{ {20, expected_exec[0]}, {21, expected_exec[1]}, - {106, vcc_lo}, {107, vcc_hi}}}) { + {106, vcc_lo}, {107, vcc_hi}, {22, vcc_lo}, {23, vcc_hi}}}) { AppendStoreSgprAtLaneDwordOffset(&code, reg, 0, u32(test.expected.size())); test.expected.insert(test.expected.end(), wave_size, value); } @@ -25731,13 +25756,16 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) { test.opcodes.insert(test.opcodes.end(), {O::V_MOV_B32, O::S_MOV_B32, O::S_MOV_B64, O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::BUFFER_LOAD_DWORD, O::BUFFER_STORE_DWORD, O::S_ENDPGM}); - test.required_spirv = {"OpFOrdLessThanEqual", "OpFOrdGreaterThanEqual"}; + test.required_spirv = {"OpFOrdLessThanEqual", "OpFOrdGreaterThanEqual", "OpFOrdLessThan", + "OpFOrdGreaterThan", "OpFOrdEqual", "OpFOrdNotEqual", + "OpFUnordEqual", "OpFUnordNotEqual", "OpFUnordLessThan", + "OpFUnordGreaterThan", "OpFUnordLessThanEqual", "OpFUnordGreaterThanEqual"}; return test; } TestCase VectorCompareF64Edges() { using O = ShaderOpcode; - constexpr std::array, 15> pairs{{ + constexpr std::array, 16> pairs{{ {0xc000000000000000ull, 0xbff0000000000000ull}, {0x8000000000000001ull, 0}, {0x3fc45f306dc9c882ull, 0x3fc45f306dc9c882ull}, {0, 0x8000000000000000ull}, {1, 0}, {1, 1}, {1, 2}, @@ -25746,7 +25774,8 @@ TestCase VectorCompareF64Edges() { {0x7ff0000000000000ull, 0x7ff0000000000000ull}, {0x7ff0000000000000ull, 0xfff0000000000000ull}, {0x7ff8000000000001ull, 0x7ff8000000000001ull}, - {0x7ff0000000000001ull, 0}, {0x7fefffffffffffffull, 0x7fefffffffffffffull}}}; + {0x7ff0000000000001ull, 0}, {0, 0x7ff8000000000001ull}, + {0x7fefffffffffffffull, 0x7fefffffffffffffull}}}; TestCase test; test.name = "VectorCompareF64Edges"; for (const auto& pair : pairs) @@ -25764,19 +25793,28 @@ TestCase VectorCompareF64Edges() { } const auto lhs = std::bit_cast(pairs[i][0]); const auto rhs = std::bit_cast(pairs[i][1]); - test.code.push_back(EncodeVopc(0x22, Vgpr(1), 3)); - store_mask(106, lhs == rhs); - test.code.push_back(EncodeVopc(0x23, Vgpr(1), 3)); - store_mask(106, lhs <= rhs); - AppendVop3(&test.code, 0x23, 20, Vgpr(1), Vgpr(3), 0, 1, 0, false, 0, 1); - store_mask(20, -std::abs(lhs) <= rhs); + for (const bool vop3 : {false, true}) { + const auto a = vop3 ? -std::abs(lhs) : lhs; + const auto b = vop3 ? -std::abs(rhs) : rhs; + const bool ordered = !std::isnan(a) && !std::isnan(b); + // Predicates 0x20..0x2f; N* is the negation of its ordered counterpart. + const std::array results{false, a < b, a == b, a <= b, a > b, + a < b || a > b, a >= b, ordered, !ordered, !(a >= b), + !(a < b || a > b), !(a > b), !(a <= b), !(a == b), !(a < b), true}; + for (u32 predicate = 0; predicate < results.size(); ++predicate) { + if (vop3) + AppendVop3(&test.code, 0x20 + predicate, 20, Vgpr(1), Vgpr(3), + 0, 3, 0, false, 0, 3); + else + test.code.push_back(EncodeVopc(0x20 + predicate, Vgpr(1), 3)); + store_mask(vop3 ? 20 : 106, results[predicate]); + } + } test.code.push_back(EncodeVopc(0x23, 255, 3)); test.code.push_back(0x3ff00000u); // FP64 literals supply the high word. store_mask(106, 1.0 <= rhs); AppendVop3(&test.code, 0x23, 20, 248, Vgpr(3)); store_mask(20, std::bit_cast(uint64_t{0x3fc45f306dc9c882ull}) <= rhs); - AppendVop3(&test.code, 0x22, 20, Vgpr(1), Vgpr(3), 0, 1, 0, false, 0, 1); - store_mask(20, -std::abs(lhs) == rhs); test.code.push_back(EncodeVop1(0x02, 100, Vgpr(3))); test.code.push_back(EncodeVop1(0x02, 101, Vgpr(4))); // Captured e6d76d24f59f8015 PC 0xe4: compare zero with s100:s101. @@ -25788,9 +25826,16 @@ TestCase VectorCompareF64Edges() { 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_EQ_F64, O::V_CMP_LE_F64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; - test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64", "OpFOrdEqual", - "OpFOrdLessThanEqual"}; + O::V_CMP_F_F64, O::V_CMP_LT_F64, O::V_CMP_EQ_F64, O::V_CMP_LE_F64, + O::V_CMP_GT_F64, O::V_CMP_LG_F64, O::V_CMP_GE_F64, O::V_CMP_O_F64, + O::V_CMP_U_F64, O::V_CMP_NGE_F64, O::V_CMP_NLG_F64, O::V_CMP_NGT_F64, + O::V_CMP_NLE_F64, O::V_CMP_NEQ_F64, O::V_CMP_NLT_F64, O::V_CMP_TRU_F64, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64", "OpIsNan", + "OpFOrdEqual", "OpFOrdNotEqual", "OpFOrdLessThan", + "OpFOrdLessThanEqual", "OpFOrdGreaterThan", "OpFOrdGreaterThanEqual", + "OpFUnordEqual", "OpFUnordNotEqual", "OpFUnordLessThan", + "OpFUnordLessThanEqual", "OpFUnordGreaterThan", "OpFUnordGreaterThanEqual"}; return test; }