shader: add missing F64 VOPC compares

Co-authored-by: Hector Mödinger S. <hectormodinger@gmail.com>
This commit is contained in:
nmzik 2026-10-09 21:01:51 +02:00
parent c1926f915d
commit 59183dbfa8
9 changed files with 283 additions and 34 deletions

View File

@ -146,6 +146,15 @@ inline constexpr auto EmitFPOrdEqual32 = EmitFloatCompare32<spv::OpFOrdEqual>;
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<spv::OpFUnordEqual>;
inline constexpr auto EmitFPOrdNotEqual32 = EmitFloatCompare32<spv::OpFOrdNotEqual>;
inline constexpr auto EmitFPUnordNotEqual32 = EmitFloatCompare32<spv::OpFUnordNotEqual>;
@ -158,6 +167,7 @@ inline constexpr auto EmitFPUnordLessThanEqual32 = EmitFloatCompare32<spv::OpFUn
inline constexpr auto EmitFPOrdGreaterThanEqual32 = EmitFloatCompare32<spv::OpFOrdGreaterThanEqual>;
inline constexpr auto EmitFPUnordGreaterThanEqual32 = EmitFloatCompare32<spv::OpFUnordGreaterThanEqual>;
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)

View File

@ -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,

View File

@ -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<const uint32_t> 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:

View File

@ -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);
}

View File

@ -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:

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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<std::array<std::array<uint64_t, 2>, 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<int16_t>(uint16_t(pair[0]));
const auto b = std::bit_cast<int16_t>(uint16_t(pair[1]));
const auto lhs = std::bit_cast<double>(pair[0]);
const auto rhs = std::bit_cast<double>(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<int64_t>(pair[0]) < std::bit_cast<int64_t>(pair[1]); break;
case 0xb2: case 0xf2: result = pair[0] == pair[1]; break;
case 0xb3: result = std::bit_cast<int64_t>(pair[0]) <= std::bit_cast<int64_t>(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<double>(pair[0]) <= std::bit_cast<double>(pair[1]); break;
case 0x36: result = std::bit_cast<double>(pair[0]) >= std::bit_cast<double>(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<std::pair<u32, u32>, 4>{{
for (const auto [reg, value] : std::array<std::pair<u32, u32>, 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<std::array<uint64_t, 2>, 15> pairs{{
constexpr std::array<std::array<uint64_t, 2>, 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<double>(pairs[i][0]);
const auto rhs = std::bit_cast<double>(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<double>(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;
}