mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 19:36:55 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9992ab18fc | |||
| 18af08460f | |||
| 39ee9972b8 |
@@ -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,
|
||||
@@ -631,6 +632,7 @@ struct Operand {
|
||||
uint32_t dpp_ctrl = 0;
|
||||
uint32_t dpp_row_mask = 0xf;
|
||||
uint32_t dpp_bank_mask = 0xf;
|
||||
bool explicit_sdwa_dst = false;
|
||||
bool sdwa_sext = false;
|
||||
bool dpp_fetch_inactive = false;
|
||||
bool dpp_bound_ctrl = false;
|
||||
|
||||
@@ -13,6 +13,7 @@ enum class Vop2SdwaProfile {
|
||||
None,
|
||||
Float32,
|
||||
Float16,
|
||||
PackedFloat16,
|
||||
IntegerFullDestination,
|
||||
IntegerPartialDestination,
|
||||
ReverseRightShift,
|
||||
@@ -66,7 +67,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
|
||||
{0x2bu, Opcode::V_MAC_F32},
|
||||
{0x2cu, Opcode::V_MADMK_F32},
|
||||
{0x2du, Opcode::V_MADAK_F32},
|
||||
{0x2fu, Opcode::V_CVT_PKRTZ_F16_F32},
|
||||
{0x2fu, Opcode::V_CVT_PKRTZ_F16_F32, Vop2SdwaProfile::PackedFloat16},
|
||||
{0x32u, Opcode::V_ADD_F16, Vop2SdwaProfile::Float16},
|
||||
{0x33u, Opcode::V_SUB_F16, Vop2SdwaProfile::Float16},
|
||||
{0x34u, Opcode::V_SUBREV_F16, Vop2SdwaProfile::Float16},
|
||||
@@ -227,7 +228,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},
|
||||
@@ -416,14 +418,6 @@ bool IsPermlaneOpcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_PERMLANE16_B32 || opcode == Opcode::V_PERMLANEX16_B32;
|
||||
}
|
||||
|
||||
bool IsVop2LowHalfF16Opcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_ADD_F16 || opcode == Opcode::V_SUB_F16 ||
|
||||
opcode == Opcode::V_SUBREV_F16 || opcode == Opcode::V_MUL_F16 ||
|
||||
opcode == Opcode::V_FMAC_F16 || opcode == Opcode::V_FMAMK_F16 ||
|
||||
opcode == Opcode::V_FMAAK_F16 || opcode == Opcode::V_MAX_F16 ||
|
||||
opcode == Opcode::V_MIN_F16;
|
||||
}
|
||||
|
||||
bool IsNativeVop3F16TernaryOpcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_MIN3_F16 || opcode == Opcode::V_MAX3_F16 ||
|
||||
opcode == Opcode::V_MED3_F16 || opcode == Opcode::V_FMA_F16;
|
||||
@@ -450,12 +444,6 @@ bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyDefaultVop2F16Destination(Instruction& inst) {
|
||||
if (IsVop2LowHalfF16Opcode(inst.opcode)) {
|
||||
inst.dst.sdwa_sel = 4;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVop1FloatResultOpcode(Opcode opcode);
|
||||
bool IsVopcCompareExec(Opcode opcode);
|
||||
|
||||
@@ -470,6 +458,8 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_FREXP_EXP_I32_F32:
|
||||
case Opcode::V_FREXP_MANT_F32:
|
||||
case Opcode::V_CVT_F16_F32:
|
||||
case Opcode::V_CVT_U16_F16:
|
||||
case Opcode::V_CVT_I16_F16:
|
||||
case Opcode::V_RCP_F32:
|
||||
case Opcode::V_RCP_IFLAG_F32:
|
||||
case Opcode::V_FRACT_F32:
|
||||
@@ -481,7 +471,11 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_LOG_F32:
|
||||
case Opcode::V_RSQ_F32:
|
||||
case Opcode::V_SQRT_F32:
|
||||
case Opcode::V_RCP_F16:
|
||||
case Opcode::V_SQRT_F16:
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_FLOOR_F16:
|
||||
case Opcode::V_CEIL_F16:
|
||||
case Opcode::V_TRUNC_F16:
|
||||
@@ -527,33 +521,34 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
|
||||
{Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false},
|
||||
{Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true},
|
||||
{Opcode::V_CVT_F16_F32, SdwaSelFull(), SdwaSelWords(), SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_F16_F32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_CVT_F16_U16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_U16_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_CVT_F16_I16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_I16_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_RCP_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_SQRT_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_RSQ_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_LOG_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_EXP_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_FLOOR_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_CEIL_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_TRUNC_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_RNDNE_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_SIN_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_COS_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
@@ -585,7 +580,7 @@ bool IsValidFullSdwaDestinationUnused(uint32_t dst_u) {
|
||||
|
||||
bool IsVop1SdwaSourceSupported(Opcode opcode, uint32_t src_sel, bool src_neg, bool src_abs) {
|
||||
if (src_sel == 6u) {
|
||||
return true;
|
||||
return IsVop1FloatSourceOpcode(opcode) || (!src_neg && !src_abs);
|
||||
}
|
||||
const auto* rule = FindVop1SdwaRule(opcode);
|
||||
if (rule == nullptr || !HasSdwaSelector(rule->source_selectors, src_sel)) {
|
||||
@@ -596,29 +591,24 @@ bool IsVop1SdwaSourceSupported(Opcode opcode, uint32_t src_sel, bool src_neg, bo
|
||||
|
||||
bool IsVop1SdwaDestinationSupported(Opcode opcode, uint32_t dst_sel, uint32_t dst_u,
|
||||
uint32_t src_sel) {
|
||||
if ((opcode == Opcode::V_CVT_U16_F16 || opcode == Opcode::V_CVT_I16_F16) && dst_sel == 6u) {
|
||||
return false;
|
||||
}
|
||||
if (dst_sel == 6u) {
|
||||
return IsValidFullSdwaDestinationUnused(dst_u);
|
||||
}
|
||||
const auto* rule = FindVop1SdwaRule(opcode);
|
||||
if (rule == nullptr || dst_u != 2u) {
|
||||
if (rule == nullptr || dst_u == 3u) {
|
||||
return false;
|
||||
}
|
||||
return HasSdwaSelector(rule->partial_dst_selectors, dst_sel) &&
|
||||
HasSdwaSelector(rule->partial_dst_source_selectors, src_sel);
|
||||
}
|
||||
|
||||
bool HasUnsupportedVop1SdwaSourceModifiers(Opcode opcode, bool src_neg, bool src_abs) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_CVT_U16_F16:
|
||||
case Opcode::V_CVT_I16_F16:
|
||||
case Opcode::V_SQRT_F16:
|
||||
case Opcode::V_RSQ_F16: return src_neg || src_abs;
|
||||
case Opcode::V_LOG_F16: return src_neg;
|
||||
default: return false;
|
||||
}
|
||||
bool UsesInexactClampControl(Opcode opcode) {
|
||||
return opcode == Opcode::V_CVT_U32_F32 || opcode == Opcode::V_CVT_I32_F32 ||
|
||||
opcode == Opcode::V_CVT_U16_F16 || opcode == Opcode::V_CVT_I16_F16;
|
||||
}
|
||||
|
||||
bool SupportsVop1Clamp(Opcode opcode) {
|
||||
return IsVop1FloatResultOpcode(opcode) || UsesInexactClampControl(opcode);
|
||||
}
|
||||
|
||||
bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) {
|
||||
@@ -634,7 +624,8 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) {
|
||||
SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA selector is invalid");
|
||||
return false;
|
||||
}
|
||||
if ((clamp != 0u || omod != 0u) && !IsVop1FloatResultOpcode(inst.opcode)) {
|
||||
if ((clamp != 0u && !SupportsVop1Clamp(inst.opcode)) ||
|
||||
(omod != 0u && !IsVop1FloatResultOpcode(inst.opcode))) {
|
||||
SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA output modifiers are not supported");
|
||||
return false;
|
||||
}
|
||||
@@ -643,10 +634,6 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) {
|
||||
"VOP1 SDWA destination selector is not supported");
|
||||
return false;
|
||||
}
|
||||
if (HasUnsupportedVop1SdwaSourceModifiers(inst.opcode, src0_neg != 0u, src0_abs != 0u)) {
|
||||
SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA source modifiers are not supported");
|
||||
return false;
|
||||
}
|
||||
if (!IsVop1SdwaSourceSupported(inst.opcode, src0_sel, src0_neg != 0u, src0_abs != 0u)) {
|
||||
SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA source selector is not supported");
|
||||
return false;
|
||||
@@ -684,10 +671,11 @@ bool DecodeVop1Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
|
||||
if (!DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
inst.dst.sdwa_sel = dst_sel;
|
||||
inst.dst.sdwa_dst_unused = dst_u;
|
||||
inst.dst.clamp = clamp != 0u;
|
||||
inst.dst.omod = omod;
|
||||
inst.dst.sdwa_sel = dst_sel;
|
||||
inst.dst.sdwa_dst_unused = dst_u;
|
||||
inst.dst.explicit_sdwa_dst = true;
|
||||
inst.dst.clamp = clamp != 0u;
|
||||
inst.dst.omod = omod;
|
||||
inst.src0.sdwa_sel = src0_sel;
|
||||
inst.src0.sdwa_sext = src0_sext != 0u;
|
||||
inst.src0.negate = src0_neg != 0u;
|
||||
@@ -779,6 +767,7 @@ bool IsVop2FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_MAC_F32:
|
||||
case Opcode::V_MADMK_F32:
|
||||
case Opcode::V_MADAK_F32:
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
case Opcode::V_ADD_F16:
|
||||
case Opcode::V_SUB_F16:
|
||||
case Opcode::V_SUBREV_F16:
|
||||
@@ -803,6 +792,7 @@ bool IsVop2FloatResultOpcode(Opcode opcode) {
|
||||
case Opcode::V_MAC_F32:
|
||||
case Opcode::V_MADMK_F32:
|
||||
case Opcode::V_MADAK_F32:
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
case Opcode::V_ADD_F16:
|
||||
case Opcode::V_SUB_F16:
|
||||
case Opcode::V_SUBREV_F16:
|
||||
@@ -821,6 +811,9 @@ bool IsVop1FloatResultOpcode(Opcode opcode) {
|
||||
case Opcode::V_CVT_F32_I32:
|
||||
case Opcode::V_CVT_F32_U32:
|
||||
case Opcode::V_CVT_F32_F16:
|
||||
case Opcode::V_CVT_F16_F32:
|
||||
case Opcode::V_CVT_F16_U16:
|
||||
case Opcode::V_CVT_F16_I16:
|
||||
case Opcode::V_CVT_OFF_F32_I4:
|
||||
case Opcode::V_CVT_F32_UBYTE0:
|
||||
case Opcode::V_CVT_F32_UBYTE1:
|
||||
@@ -839,9 +832,14 @@ bool IsVop1FloatResultOpcode(Opcode opcode) {
|
||||
case Opcode::V_RSQ_F32:
|
||||
case Opcode::V_SQRT_F32:
|
||||
case Opcode::V_RCP_F16:
|
||||
case Opcode::V_SQRT_F16:
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_FLOOR_F16:
|
||||
case Opcode::V_CEIL_F16:
|
||||
case Opcode::V_TRUNC_F16:
|
||||
case Opcode::V_RNDNE_F16:
|
||||
case Opcode::V_SIN_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_SIN_F32:
|
||||
@@ -895,7 +893,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;
|
||||
}
|
||||
}
|
||||
@@ -955,6 +954,7 @@ constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = {
|
||||
{SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true},
|
||||
{SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(),
|
||||
true, true},
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, true},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false},
|
||||
{SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false},
|
||||
{SdwaSelFull(), SdwaSelAll(), SdwaSelWords() | SdwaSelFull(), false, false},
|
||||
@@ -979,7 +979,7 @@ bool IsVop2SdwaDestinationSupported(const Vop2SdwaRule& rule, const Vop2SdwaFiel
|
||||
if (fields.dst_sel == 6u) {
|
||||
return IsValidFullSdwaDestinationUnused(fields.dst_u);
|
||||
}
|
||||
return fields.dst_u == 2u && rule.partial_dst;
|
||||
return fields.dst_u != 3u && rule.partial_dst;
|
||||
}
|
||||
|
||||
bool IsFullWidthVop2Sdwa(const Vop2SdwaFields& fields) {
|
||||
@@ -1057,6 +1057,13 @@ bool FinalizeVop2Instruction(uint32_t pc, std::span<const uint32_t> code, uint32
|
||||
inst.src2.kind = OperandKind::VccLo;
|
||||
inst.src_count = 3;
|
||||
break;
|
||||
case Opcode::V_PK_FMAC_F16:
|
||||
// VOP2 has no OP_SEL fields, so the high operation implicitly reads high halves.
|
||||
inst.src0.op_sel_hi = true;
|
||||
inst.src1.op_sel_hi = true;
|
||||
inst.dst.op_sel_hi = true;
|
||||
inst.src_count = 2;
|
||||
break;
|
||||
default: inst.src_count = 2; break;
|
||||
}
|
||||
ReadLiteralOperands(code, word_index, inst);
|
||||
@@ -1078,10 +1085,11 @@ bool DecodeVop2Sdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
|
||||
!DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1, error)) {
|
||||
return false;
|
||||
}
|
||||
inst.dst.sdwa_sel = fields.dst_sel;
|
||||
inst.dst.sdwa_dst_unused = fields.dst_u;
|
||||
inst.dst.clamp = fields.clamp != 0u;
|
||||
inst.dst.omod = fields.omod;
|
||||
inst.dst.sdwa_sel = fields.dst_sel;
|
||||
inst.dst.sdwa_dst_unused = fields.dst_u;
|
||||
inst.dst.explicit_sdwa_dst = true;
|
||||
inst.dst.clamp = fields.clamp != 0u;
|
||||
inst.dst.omod = fields.omod;
|
||||
inst.src0.sdwa_sel = fields.src0_sel;
|
||||
inst.src0.sdwa_sext = fields.src0_sext != 0u;
|
||||
inst.src0.negate = fields.src0_neg != 0u;
|
||||
@@ -1105,12 +1113,16 @@ bool DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
||||
if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
ApplyDefaultVop2F16Destination(inst);
|
||||
ApplyDppModifier(inst.src0, modifier);
|
||||
inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u;
|
||||
inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u;
|
||||
const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16;
|
||||
if (packed_fmac) {
|
||||
inst.src0.negate_hi = inst.src0.negate;
|
||||
inst.src1.negate_hi = inst.src1.negate;
|
||||
}
|
||||
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) &&
|
||||
if (!IsVop2FloatSourceOpcode(inst.opcode) && !packed_fmac &&
|
||||
(inst.src0.negate || inst.src0.absolute || inst.src1.negate || inst.src1.absolute)) {
|
||||
SetUnsupported(inst, Family::VOP2, opcode,
|
||||
"VOP2 DPP integer source modifiers are not supported");
|
||||
@@ -1437,6 +1449,7 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
|
||||
case Opcode::V_MAD_F32:
|
||||
case Opcode::V_FMA_F32:
|
||||
case Opcode::V_FMA_F16:
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
case Opcode::V_LDEXP_F32:
|
||||
case Opcode::V_MIN3_F32:
|
||||
case Opcode::V_MAX3_F32:
|
||||
@@ -1445,12 +1458,17 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SupportsNativeVop3Clamp(Opcode opcode) {
|
||||
return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode);
|
||||
}
|
||||
|
||||
bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix,
|
||||
bool carry_in_out, bool scalar_dst, uint32_t abs,
|
||||
uint32_t op_sel, uint32_t clamp, uint32_t omod,
|
||||
uint32_t neg) {
|
||||
const bool source_modifiers = SupportsNativeVop3SourceModifiers(opcode);
|
||||
const bool result_modifiers = SupportsNativeVop3ResultModifiers(opcode);
|
||||
const bool clamp_modifier = SupportsNativeVop3Clamp(opcode);
|
||||
|
||||
if (permlane) {
|
||||
return abs != 0u || (op_sel & ~0x3u) != 0u || clamp != 0u || omod != 0u || neg != 0u;
|
||||
@@ -1482,7 +1500,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
|
||||
}
|
||||
if (source_modifiers) {
|
||||
return op_sel != 0u || (omod != 0u && !result_modifiers) ||
|
||||
(clamp != 0u && !result_modifiers);
|
||||
(clamp != 0u && !clamp_modifier);
|
||||
}
|
||||
if (result_modifiers) {
|
||||
return abs != 0u || op_sel != 0u || neg != 0u;
|
||||
@@ -1518,6 +1536,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:
|
||||
@@ -1574,7 +1593,6 @@ bool DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
if (!DecodeScalarSource(src0, pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
ApplyDefaultVop2F16Destination(inst);
|
||||
return FinalizeVop2Instruction(pc, code, word_index, inst, error);
|
||||
}
|
||||
|
||||
@@ -1698,6 +1716,7 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const bool scalar_modifier_limits = UsesScalarDestination(inst.opcode);
|
||||
const bool native_source_modifiers = SupportsNativeVop3SourceModifiers(inst.opcode);
|
||||
const bool native_result_modifiers = SupportsNativeVop3ResultModifiers(inst.opcode);
|
||||
const bool native_clamp_modifier = SupportsNativeVop3Clamp(inst.opcode);
|
||||
const bool modifiers =
|
||||
HasUnsupportedNativeVop3Modifiers(inst.opcode, permlane, mad_mix, vop3b_uses_sdst,
|
||||
scalar_modifier_limits, abs, op_sel, clamp, omod, neg);
|
||||
@@ -1732,7 +1751,7 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
if (!dst_ok || !DecodeScalarSource(src0, pc, inst.src0, error)) {
|
||||
return false;
|
||||
}
|
||||
inst.dst.clamp = native_result_modifiers && clamp != 0u;
|
||||
inst.dst.clamp = native_clamp_modifier && clamp != 0u;
|
||||
inst.dst.omod = native_result_modifiers ? omod : 0u;
|
||||
if (permlane) {
|
||||
inst.dst.op_sel = (op_sel & 0x1u) != 0u;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -141,8 +141,10 @@ void Translator::V_CVT_OFF_F32_I4(const Decoder::Instruction& inst) {
|
||||
}
|
||||
|
||||
void Translator::V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst) {
|
||||
const auto lhs = ReadOperand(SourceAt(inst, 0), IR::Type::F32);
|
||||
const auto rhs = ReadOperand(SourceAt(inst, 1), IR::Type::F32);
|
||||
const auto lhs = ApplyF32ResultModifiers(
|
||||
inst.dst, IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32)));
|
||||
const auto rhs = ApplyF32ResultModifiers(
|
||||
inst.dst, IR::F32(ReadOperand(SourceAt(inst, 1), IR::Type::F32)));
|
||||
WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {lhs, rhs}));
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t
|
||||
result.dpp_ctrl = 0;
|
||||
result.dpp_row_mask = 0xf;
|
||||
result.dpp_bank_mask = 0xf;
|
||||
result.explicit_sdwa_dst = false;
|
||||
result.dpp_fetch_inactive = false;
|
||||
result.dpp_bound_ctrl = false;
|
||||
result.dpp = false;
|
||||
|
||||
@@ -104,6 +104,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
|
||||
result.dpp_ctrl = 0;
|
||||
result.dpp_row_mask = 0xf;
|
||||
result.dpp_bank_mask = 0xf;
|
||||
result.explicit_sdwa_dst = false;
|
||||
result.dpp_fetch_inactive = false;
|
||||
result.dpp_bound_ctrl = false;
|
||||
result.dpp = false;
|
||||
@@ -392,12 +393,13 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value)
|
||||
}
|
||||
}
|
||||
if (type == IR::Type::U16) {
|
||||
WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
|
||||
Write16Bits(operand,
|
||||
IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)})));
|
||||
return;
|
||||
}
|
||||
if (type == IR::Type::F16) {
|
||||
const auto bits = IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {value}));
|
||||
WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits})));
|
||||
Write16Bits(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits})));
|
||||
return;
|
||||
}
|
||||
if (type == IR::Type::U64) {
|
||||
@@ -417,38 +419,32 @@ IR::U32 Translator::PackHalf2x16(IR::F32 low, IR::F32 high) {
|
||||
return IR::U32(ir.Emit(IR::ValueOpcode::PackHalf2x16, {pair}));
|
||||
}
|
||||
|
||||
void Translator::Write16Bits(const Decoder::Operand& operand, IR::U32 value) {
|
||||
auto destination = operand;
|
||||
// Native GFX10 16-bit results preserve the unselected half. Plain/DPP destinations use the
|
||||
// low half; native VOP3 may already select either half. Explicit SDWA retains encoded DST_U.
|
||||
if (!destination.explicit_sdwa_dst) {
|
||||
if (destination.sdwa_sel == 6u) {
|
||||
destination.sdwa_sel = 4u;
|
||||
}
|
||||
destination.sdwa_dst_unused = 2u;
|
||||
}
|
||||
destination.omod = 0u;
|
||||
destination.op_sel = false;
|
||||
destination.clamp = false;
|
||||
WriteRawU32(destination, ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu))));
|
||||
}
|
||||
|
||||
void Translator::WriteF16(const Decoder::Operand& operand, IR::F32 value) {
|
||||
value = ApplyF32ResultModifiers(operand, value);
|
||||
const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {value}));
|
||||
const auto bits = IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16,
|
||||
{IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {half}))}));
|
||||
auto merged = bits;
|
||||
if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
||||
{ReadRawU32(PlainOperand(operand)), bits,
|
||||
IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)}));
|
||||
}
|
||||
auto raw = operand;
|
||||
raw.sdwa_sel = 6u;
|
||||
raw.omod = 0u;
|
||||
raw.op_sel = false;
|
||||
raw.clamp = false;
|
||||
WriteRawU32(raw, merged);
|
||||
Write16Bits(operand, bits);
|
||||
}
|
||||
|
||||
void Translator::WriteU16(const Decoder::Operand& operand, IR::U32 value) {
|
||||
auto merged = ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
|
||||
if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert,
|
||||
{ReadRawU32(PlainOperand(operand)), merged,
|
||||
IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)}));
|
||||
}
|
||||
auto raw = operand;
|
||||
raw.sdwa_sel = 6u;
|
||||
raw.omod = 0u;
|
||||
raw.op_sel = false;
|
||||
raw.clamp = false;
|
||||
WriteRawU32(raw, merged);
|
||||
Write16Bits(operand, value);
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU32(const Decoder::Operand& operand) {
|
||||
|
||||
@@ -34,6 +34,7 @@ private:
|
||||
IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteOperand(const Decoder::Operand& operand, IR::Value value);
|
||||
IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high);
|
||||
void Write16Bits(const Decoder::Operand& operand, IR::U32 value);
|
||||
void WriteF16(const Decoder::Operand& operand, IR::F32 value);
|
||||
void WriteU16(const Decoder::Operand& operand, IR::U32 value);
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
@@ -127,7 +128,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -553,6 +553,12 @@ constexpr u32 EncodeVop1Sdwa(u32 src0, u32 dst_sel = 6, u32 dst_u = 0,
|
||||
((s0 & 0x1u) << 23u);
|
||||
}
|
||||
|
||||
constexpr u32 EncodeVop1Dpp(u32 src0, u32 dpp_ctrl = 0, u32 row_mask = 0xf,
|
||||
u32 bank_mask = 0xf) {
|
||||
return (src0 & 0xffu) | ((dpp_ctrl & 0x1ffu) << 8u) |
|
||||
((bank_mask & 0xfu) << 24u) | ((row_mask & 0xfu) << 28u);
|
||||
}
|
||||
|
||||
constexpr u32 EncodeVop2(u32 opcode, u32 dst, u32 src0, u32 src1) {
|
||||
return ((opcode & 0x3fu) << 25u) | ((dst & 0xffu) << 17u) |
|
||||
((src1 & 0xffu) << 9u) | (src0 & 0x1ffu);
|
||||
@@ -11931,6 +11937,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:
|
||||
@@ -14249,6 +14256,53 @@ TestCase VectorLaneAndPackedOps() {
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase Vop2PkFmacF16AccumulatesPackedHalvesIndependently() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0x50004c00u); // low=16.0h, high=32.0h
|
||||
AppendVMovLiteral(&code, 1, 0x42003c00u); // low=1.0h, high=3.0h
|
||||
AppendVMovLiteral(&code, 2, 0x48004400u); // low=4.0h, high=8.0h
|
||||
|
||||
code.push_back(EncodeVop2(0x3c, 0, Vgpr(1), 2));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
code.push_back(EncodeVop2(0x3c, 0, Vgpr(1), 2));
|
||||
AppendStoreVgpr(&code, 0, 1);
|
||||
|
||||
AppendVMovLiteral(&code, 3, 0x50004c00u);
|
||||
code.push_back(EncodeVop2(0x3c, 3, 242, 2)); // Inline 1.0h is {1.0h, 0.0h}.
|
||||
AppendStoreVgpr(&code, 3, 2);
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"Vop2PkFmacF16AccumulatesPackedHalvesIndependently",
|
||||
code,
|
||||
{},
|
||||
{0x53004d00u, 0x55004e00u, 0x50004d00u},
|
||||
{O::V_MOV_B32, O::V_PK_FMAC_F16, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase Vop2PkFmacF16DppNegatesBothPackedHalves() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0x50004c00u); // low=16.0h, high=32.0h
|
||||
AppendVMovLiteral(&code, 1, 0x42003c00u); // low=1.0h, high=3.0h
|
||||
AppendVMovLiteral(&code, 2, 0x48004400u); // low=4.0h, high=8.0h
|
||||
|
||||
code.push_back(EncodeVop2(0x3c, 0, 250, 2));
|
||||
code.push_back(EncodeVop2Dpp(1, 0, 0xf, 0xf, 1));
|
||||
AppendStoreVgpr(&code, 0, 0);
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"Vop2PkFmacF16DppNegatesBothPackedHalves",
|
||||
code,
|
||||
{},
|
||||
{0x48004a00u},
|
||||
{O::V_MOV_B32, O::V_PK_FMAC_F16, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase Vop3pOpselHiUsesArchitecturalSourceBits() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -14320,6 +14374,54 @@ TestCase CvtPkrtzF16F32SubnormalRoundsTowardZero() {
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase CvtPkrtzF16F32SdwaAndOutputModifiers() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0x3f000000u); // 0.5f
|
||||
AppendVMovLiteral(&code, 1, 0x40000000u); // 2.0f
|
||||
AppendVMovLiteral(&code, 2, 0x3f802000u); // exactly 0x3c01h
|
||||
AppendVMovLiteral(&code, 3, 0x00000000u);
|
||||
AppendVMovLiteral(&code, 4, 0xbf000000u); // -0.5f
|
||||
for (u32 dst = 10; dst <= 18; dst++) {
|
||||
AppendVMovLiteral(&code, dst, 0xa1b2c3d4u);
|
||||
}
|
||||
|
||||
code.push_back(EncodeVop2(0x2f, 10, 249, 1));
|
||||
code.push_back(EncodeVop2Sdwa(0, 6, 2, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1));
|
||||
code.push_back(EncodeVop2(0x2f, 11, 249, 3));
|
||||
code.push_back(EncodeVop2Sdwa(2, 0, 0));
|
||||
code.push_back(EncodeVop2(0x2f, 12, 249, 1));
|
||||
code.push_back(EncodeVop2Sdwa(0, 6, 0, 3, 5));
|
||||
code.push_back(EncodeVop2(0x2f, 13, 249, 1));
|
||||
code.push_back(EncodeVop2Sdwa(4, 6, 0, 6, 6, 0, 0, 0, 1, 1));
|
||||
|
||||
code.push_back(EncodeVop2(0x2f, 14, 250, 1));
|
||||
code.push_back(EncodeVop2Dpp(0, 0, 0xf, 0xf, 1));
|
||||
AppendVop3(&code, 0x12f, 15, Vgpr(0), Vgpr(1), 0, 0, 0, true);
|
||||
code.push_back(EncodeVop2(0x2f, 16, 249, 1));
|
||||
code.push_back(EncodeVop2Sdwa(0, 4, 2));
|
||||
code.push_back(EncodeVop2(0x2f, 17, 249, 1));
|
||||
code.push_back(EncodeVop2Sdwa(0, 6, 2, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1));
|
||||
AppendVop3(&code, 0x12f, 18, Vgpr(0), Vgpr(1), 0, 0, 0, false, 1);
|
||||
|
||||
for (u32 dst = 10; dst <= 18; dst++) {
|
||||
AppendStoreVgpr(&code, dst, dst - 10);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"CvtPkrtzF16F32SdwaAndOutputModifiers",
|
||||
code,
|
||||
{},
|
||||
{0x3c003800u, 0x00000001u, 0x00000000u, 0xc0003800u,
|
||||
0x4000b800u, 0x3c003800u, 0xa1b23800u, 0x44003c00u,
|
||||
0x44003c00u},
|
||||
{O::V_MOV_B32, O::V_CVT_PKRTZ_F16_F32, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase PackedMinMaxF16NanAndSignedZeroEdges() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -14427,6 +14529,99 @@ TestCase VectorCvtU16F16Sdwa() {
|
||||
{O::V_MOV_B32, O::V_CVT_U16_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase NativeAndSdwa16BitDestinationWrites() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 0, 0x3fc00000u); // 1.5f -> 0x3e00h
|
||||
AppendVMovLiteral(&code, 1, 0x00004200u); // low=3.0h
|
||||
AppendVMovLiteral(&code, 2, 0x0000c000u); // low=-2.0h
|
||||
AppendVMovLiteral(&code, 3, 0x00003c00u); // low=1.0h
|
||||
AppendVMovLiteral(&code, 4, 0x00004000u); // low=2.0h
|
||||
|
||||
const u32 sentinels[] = {
|
||||
0x3c001234u, 0xbbbb2345u, 0xcccc3456u, 0xdddd4567u, 0xeeee5678u,
|
||||
0xffff6789u, 0xf00d789au, 0x11118888u, 0x22229999u, 0x3333aaaau,
|
||||
0xaaaa0000u, 0xbbbb0000u, 0xcccc0000u, 0xabcd1234u, 0xbcde2345u,
|
||||
0xcdef3456u, 0xdef01234u, 0xef012345u, 0xf0123456u, 0x01234567u,
|
||||
0xaaaa1234u, 0xbbbb5678u, 0x24681234u, 0x13572468u, 0x89abcdefu,
|
||||
0xfedcba98u, 0x0bad1234u,
|
||||
};
|
||||
// AppendStoreVgpr uses v31 as its address scratch register.
|
||||
const auto output_vgpr = [](u32 index) { return 10u + index + (index >= 21u ? 1u : 0u); };
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(sentinels)); i++) {
|
||||
AppendVMovLiteral(&code, output_vgpr(i), sentinels[i]);
|
||||
}
|
||||
|
||||
code.push_back(EncodeVop1(0x0a, 10, Vgpr(0)));
|
||||
code.push_back(EncodeVop1(0x0a, 11, 250));
|
||||
code.push_back(EncodeVop1Dpp(0));
|
||||
AppendVop3(&code, 0x18a, 12, Vgpr(0), 0);
|
||||
code.push_back(EncodeVop1(0x52, 13, Vgpr(1)));
|
||||
code.push_back(EncodeVop1(0x53, 14, Vgpr(2)));
|
||||
|
||||
code.push_back(EncodeVop1(0x0a, 15, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0));
|
||||
code.push_back(EncodeVop1(0x54, 16, 249));
|
||||
code.push_back(EncodeVop1Sdwa(10, 4, 2, 5));
|
||||
code.push_back(EncodeVop1(0x52, 17, 249));
|
||||
code.push_back(EncodeVop1Sdwa(1, 6, 0, 4));
|
||||
code.push_back(EncodeVop1(0x52, 18, 249));
|
||||
code.push_back(EncodeVop1Sdwa(1, 4, 0, 4));
|
||||
code.push_back(EncodeVop1(0x53, 19, 249));
|
||||
code.push_back(EncodeVop1Sdwa(2, 4, 1, 4));
|
||||
|
||||
code.push_back(EncodeVop2(0x32, 20, 250, 4));
|
||||
code.push_back(EncodeVop2Dpp(3));
|
||||
AppendVop3(&code, 0x132, 21, Vgpr(3), Vgpr(4));
|
||||
code.push_back(EncodeVop2(0x32, 22, 249, 4));
|
||||
code.push_back(EncodeVop2Sdwa(3, 4, 0, 4, 4));
|
||||
|
||||
code.push_back(EncodeVop1(0x0a, 23, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 4, 2, 3));
|
||||
code.push_back(EncodeVop1(0x0a, 24, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 4, 2, 5));
|
||||
code.push_back(EncodeVop1(0x0a, 25, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 1, 1));
|
||||
|
||||
code.push_back(EncodeVop1(0x0a, 26, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 0, 0, 0, 0, 1));
|
||||
code.push_back(EncodeVop1(0x0a, 27, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 0, 0, 0, 1));
|
||||
AppendVop3(&code, 0x18a, 28, Vgpr(0), 0, 0, 0, 0, false, 1);
|
||||
AppendVop3(&code, 0x18a, 29, Vgpr(0), 0, 0, 0, 0, true);
|
||||
AppendVop3(&code, 0x34b, 30, Vgpr(3), Vgpr(4), Vgpr(3));
|
||||
AppendVop3(&code, 0x34b, 32, Vgpr(3), Vgpr(4), Vgpr(3), 0, 0x8);
|
||||
code.push_back(EncodeVop1(0x52, 33, 249));
|
||||
code.push_back(EncodeVop1Sdwa(1, 4, 2, 4, 0, 1, 1, 0, 1));
|
||||
AppendVop3(&code, 0x1d3, 34, Vgpr(1), 0, 0, 1, 0, true, 0, 1);
|
||||
code.push_back(EncodeVop1(0x07, 35, 249));
|
||||
code.push_back(EncodeVop1Sdwa(0, 6, 0, 6, 0, 1, 1, 0, 1));
|
||||
AppendVop3(&code, 0x188, 36, Vgpr(0), 0, 0, 1, 0, true, 0, 1);
|
||||
code.push_back(EncodeVop1(0x54, 37, 249));
|
||||
code.push_back(EncodeVop1Sdwa(4, 4, 2, 4, 0, 1, 1));
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(std::size(sentinels)); i++) {
|
||||
AppendStoreVgpr(&code, output_vgpr(i), i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
return {"NativeAndSdwa16BitDestinationWrites",
|
||||
code,
|
||||
{},
|
||||
{0x3c003e00u, 0xbbbb3e00u, 0xcccc3e00u, 0xdddd0003u,
|
||||
0xeeeefffeu, 0x00003e00u, 0xf00d3c00u, 0x00000003u,
|
||||
0x00000003u, 0xfffffffeu, 0xaaaa4200u, 0xbbbb4200u,
|
||||
0x00004200u, 0xabcd0000u, 0xbcde0000u, 0xcdefbe00u,
|
||||
0xdef04200u, 0xef013c00u, 0xf0124200u, 0x01233c00u,
|
||||
0xaaaa4200u, 0x42005678u, 0x24680000u, 0x1357fffdu,
|
||||
0x00000000u, 0xffffffffu, 0x0badb800u},
|
||||
{O::V_MOV_B32, O::V_CVT_F16_F32, O::V_CVT_U32_F32,
|
||||
O::V_CVT_I32_F32, O::V_CVT_U16_F16, O::V_CVT_I16_F16,
|
||||
O::V_RCP_F16, O::V_ADD_F16, O::V_FMA_F16, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase VectorMinMaxMed3F16Ops() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -15785,6 +15980,38 @@ TestCase VectorCompareClassF32() {
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase VectorVopcSdwaCmpxClassF32CapturedExecMask() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> 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;
|
||||
|
||||
@@ -20767,12 +20994,16 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorVop3BSubCoU32UsesRdna2Opcode310);
|
||||
AddCase(VectorMadU64U32UnsignedCarryOut);
|
||||
AddCase(VectorLaneAndPackedOps);
|
||||
AddCase(Vop2PkFmacF16AccumulatesPackedHalvesIndependently);
|
||||
AddCase(Vop2PkFmacF16DppNegatesBothPackedHalves);
|
||||
AddCase(Vop3pOpselHiUsesArchitecturalSourceBits);
|
||||
AddCase(CvtPkU8F32PacksSelectedByte);
|
||||
AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero);
|
||||
AddCase(CvtPkrtzF16F32SdwaAndOutputModifiers);
|
||||
AddCase(PackedMinMaxF16NanAndSignedZeroEdges);
|
||||
AddCase(VectorMinMaxF16Ops);
|
||||
AddCase(VectorCvtU16F16Sdwa);
|
||||
AddCase(NativeAndSdwa16BitDestinationWrites);
|
||||
AddCase(VectorMinMaxMed3F16Ops);
|
||||
AddCase(VectorSpecialF16Ops);
|
||||
AddCase(VectorCosF16CapturedSdwaAndEdges);
|
||||
@@ -20812,6 +21043,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorVopcCmpxNeU64CapturedExecMask);
|
||||
AddCase(VectorVop3CmpxNeI64CapturedExecMask);
|
||||
AddCase(VectorCompareClassF32);
|
||||
AddCase(VectorVopcSdwaCmpxClassF32CapturedExecMask);
|
||||
AddCase(VectorCompareF16Ops);
|
||||
AddCase(Vop2SdwaCndmaskSourceModifier);
|
||||
AddCase(Vop2SdwaCndmaskCapturedByte3Source);
|
||||
|
||||
@@ -3169,16 +3169,16 @@ void TestNewShaderRecompilerStagedShaderOps() {
|
||||
"new decoder did not decode RDNA2 S_SUBB_U32");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "s_bitset0_b32 s3, s1"),
|
||||
"new decoder did not decode RDNA2 S_BITSET0_B32");
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_fmac_f16 v70.sdwa(sel=4"),
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_fmac_f16 v70, v5, v6"),
|
||||
"new decoder did not decode RDNA2 V_FMAC_F16");
|
||||
Check(
|
||||
Common::ContainsStr(
|
||||
result.decoded_dump,
|
||||
"v_fmamk_f16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"),
|
||||
"v_fmamk_f16 v71, v7, 0x3c003c00, v8"),
|
||||
"new decoder did not consume V_FMAMK_F16 literal as the multiply source");
|
||||
Check(Common::ContainsStr(
|
||||
result.decoded_dump,
|
||||
"v_fmaak_f16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"),
|
||||
"v_fmaak_f16 v72, v9, v10, 0x40004000"),
|
||||
"new decoder did not consume V_FMAAK_F16 literal as the add source");
|
||||
Check(Common::ContainsStr(result.ir_dump,
|
||||
"ScalarSubBorrowCarryU32 s2, s0, s1, scc"),
|
||||
@@ -3189,15 +3189,15 @@ void TestNewShaderRecompilerStagedShaderOps() {
|
||||
Check(
|
||||
Common::ContainsStr(
|
||||
result.ir_dump,
|
||||
"FmaF16 v70.sdwa(sel=4,sext=0), v5, v6, v70.sdwa(sel=4,sext=0)"),
|
||||
"FmaF16 v70, v5, v6, v70"),
|
||||
"V_FMAC_F16 did not lower using the destination as the FMA accumulator");
|
||||
Check(
|
||||
Common::ContainsStr(result.ir_dump,
|
||||
"FmaF16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"),
|
||||
"FmaF16 v71, v7, 0x3c003c00, v8"),
|
||||
"V_FMAMK_F16 did not lower with the literal in source 1");
|
||||
Check(
|
||||
Common::ContainsStr(result.ir_dump,
|
||||
"FmaF16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"),
|
||||
"FmaF16 v72, v9, v10, 0x40004000"),
|
||||
"V_FMAAK_F16 did not lower with the literal in source 2");
|
||||
Check(SpirvContainsOpcode(result.spirv, 130),
|
||||
"SPIR-V binary does not contain OpISub for S_SUBB_U32");
|
||||
@@ -4203,6 +4203,17 @@ void TestNewShaderDecoderArchitecture() {
|
||||
packed_inst.src2.op_sel_hi == (source == 2u),
|
||||
"VOP3P OPSEL_HI source bit mapping is incorrect");
|
||||
}
|
||||
|
||||
const uint32_t packed_fmac_dpp[] = {0x780402fau, 0xff500000u};
|
||||
Instruction packed_fmac;
|
||||
Check(DecodeInstruction(packed_fmac_dpp, 0u, packed_fmac, &error),
|
||||
error.c_str());
|
||||
Check(packed_fmac.opcode == Opcode::V_PK_FMAC_F16 &&
|
||||
packed_fmac.src0.dpp && packed_fmac.src0.op_sel_hi &&
|
||||
packed_fmac.src1.op_sel_hi && packed_fmac.dst.op_sel_hi &&
|
||||
packed_fmac.src0.negate && packed_fmac.src0.negate_hi &&
|
||||
packed_fmac.src1.negate && packed_fmac.src1.negate_hi,
|
||||
"VOP2 DPP V_PK_FMAC_F16 lost its implicit packed modifiers");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerRejectsDppOn64BitCompares() {
|
||||
@@ -4231,6 +4242,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 +12548,7 @@ int main() {
|
||||
// ShaderRecompilerComputeTests; keep the distinct decoder contract checks
|
||||
// here.
|
||||
TestNewShaderDecoderArchitecture();
|
||||
TestNewShaderRecompilerCapturedVopcSdwaCmpxClass();
|
||||
TestNewShaderRecompilerRejectsDppOn64BitCompares();
|
||||
TestNewShaderRecompilerIrLookupMissFailsExplicitly();
|
||||
TestPsInputCountRegisterDecode();
|
||||
|
||||
Reference in New Issue
Block a user