shader: add V_MIN3_U16 opcode

This commit is contained in:
nmzik
2026-10-07 09:48:34 +04:00
parent 26cce9909a
commit 96eb681b90
6 changed files with 79 additions and 13 deletions
@@ -269,6 +269,7 @@ enum class Opcode {
V_MIN3_I32,
V_MIN3_U32,
V_MIN3_F16,
V_MIN3_U16,
V_MAX3_F32,
V_MAX3_I32,
V_MAX3_U32,
@@ -318,6 +318,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
{0x152u, Opcode::V_MIN3_I32},
{0x153u, Opcode::V_MIN3_U32},
{0x351u, Opcode::V_MIN3_F16},
{0x353u, Opcode::V_MIN3_U16},
{0x154u, Opcode::V_MAX3_F32},
{0x155u, Opcode::V_MAX3_I32},
{0x156u, Opcode::V_MAX3_U32},
@@ -468,8 +469,9 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) {
opcode == Opcode::V_MED3_F16 || opcode == Opcode::V_FMA_F16;
}
bool IsNativeVop3I16TernaryOpcode(Opcode opcode) {
return opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MAD_I16;
bool IsNativeVop3B16TernaryOpcode(Opcode opcode) {
return opcode == Opcode::V_MIN3_U16 || opcode == Opcode::V_MED3_I16 ||
opcode == Opcode::V_MAD_I16;
}
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
@@ -1375,7 +1377,7 @@ void ApplyNativeVop3TernaryModifiers(Instruction& inst, uint32_t op_sel, uint32_
inst.dst.sdwa_sel = ((op_sel & 0x8u) != 0) ? 5u : 4u;
}
void ApplyNativeVop3I16TernarySelectors(Instruction& inst, uint32_t op_sel) {
void ApplyNativeVop3B16TernarySelectors(Instruction& inst, uint32_t op_sel) {
Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2};
for (uint32_t i = 0; i < 3u; i++) {
sources[i]->op_sel = ((op_sel >> i) & 1u) != 0;
@@ -1494,7 +1496,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
// Clamp is applied by WriteF16 for every f16 ternary; omod only for V_FMA_F16.
return opcode != Opcode::V_FMA_F16 && omod != 0u;
}
if (IsNativeVop3I16TernaryOpcode(opcode)) {
if (IsNativeVop3B16TernaryOpcode(opcode)) {
return abs != 0u || (clamp != 0u && !clamp_modifier) || omod != 0u || neg != 0u;
}
if (IsNativeVop3B16BinaryOpcode(opcode)) {
@@ -1731,7 +1733,7 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
const bool vop3b_uses_sdst = carry_in_out || vop3b_carry_out || vop3b_mad_u64;
const bool mad_mix = false;
const bool f16_ternary = IsNativeVop3F16TernaryOpcode(inst.opcode);
const bool i16_ternary = IsNativeVop3I16TernaryOpcode(inst.opcode);
const bool b16_ternary = IsNativeVop3B16TernaryOpcode(inst.opcode);
const bool b16_binary = IsNativeVop3B16BinaryOpcode(inst.opcode);
const bool pack_b32_f16 = inst.opcode == Opcode::V_PACK_B32_F16;
const bool permlane = IsPermlaneOpcode(inst.opcode);
@@ -1842,8 +1844,8 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg);
} else if (f16_ternary) {
ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg);
} else if (i16_ternary) {
ApplyNativeVop3I16TernarySelectors(inst, op_sel);
} else if (b16_ternary) {
ApplyNativeVop3B16TernarySelectors(inst, op_sel);
} else if (b16_binary) {
ApplyNativeVop3B16BinaryModifiers(inst, op_sel);
} else if (pack_b32_f16) {
@@ -31,11 +31,12 @@ void Translator::V_MAD_I16(const Decoder::Instruction& inst) {
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
}
void Translator::V_MED3_I16(const Decoder::Instruction& inst) {
void Translator::Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool sign) {
const auto result = IR::U32(ir.Emit(
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
ReadU16AsU32(inst.src2, true)}));
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
opcode, {ReadU16AsU32(inst.src0, sign), ReadU16AsU32(inst.src1, sign),
ReadU16AsU32(inst.src2, sign)}));
Write16Bits(DestinationOperand(inst), result);
}
void Translator::PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
@@ -166,8 +166,8 @@ private:
void FloatCube(const Decoder::Instruction& inst, uint32_t result_kind);
void Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
void Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
void Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
void V_MAD_I16(const Decoder::Instruction& inst);
void V_MED3_I16(const Decoder::Instruction& inst);
void PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool arithmetic);
void PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
@@ -422,7 +422,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
case O::V_MAD_I16: return V_MAD_I16(inst);
case O::V_MED3_I16: return V_MED3_I16(inst);
case O::V_MED3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMedTri32, true);
case O::V_MIN3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMinTri32, false);
case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true);
case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true);
case O::V_MIN_U16: return Integer16Binary(inst, IR::ValueOpcode::UMin32, false);
+61
View File
@@ -22148,6 +22148,65 @@ TestCase Vop3MadI16CapturedSelectorsAndSaturation() {
return test;
}
TestCase Vop3Min3U16CapturedAndSelectors() {
using O = ShaderOpcode;
struct MinCase {
u32 lhs, rhs, third, expected;
u32 selectors = 0;
u32 dst = 17;
bool active = true;
};
constexpr u32 a = 0x0009ea60u, b = 0x0007c350u, c = 0x00059c40u;
const std::array<MinCase, 10> cases{{
{a, b, c, 0x0005beefu, 14}, // Captured: min(60000, 7, 5) into v17.hi.
{a, b, c, 0xa5a59c40u}, // Unsigned values above INT16_MAX.
{a, b, c, 0xa5a50009u, 1},
{a, b, c, 0xa5a50007u, 2},
{a, b, c, 0xa5a50005u, 4},
{a, b, c, 0x0005beefu, 15},
{a, b, c, 0x9c40ea60u, 8, 4}, // Destination aliases source 0.
{0xabcdffffu, 0xffff0000u, 0x98768000u, 0xa5a50000u},
{0x0000ffffu, 0x7fffffffu, 0x8000ffffu, 0xa5a5ffffu},
{a, b, c, 0xa5a5beefu, 14, 17, false},
}};
TestCase test;
test.name = "Vop3Min3U16CapturedAndSelectors";
for (const auto &entry : cases) {
test.initial.insert(test.initial.end(), {entry.lhs, entry.rhs, entry.third});
}
test.expected = test.initial;
auto &code = test.code;
for (u32 i = 0; i < cases.size(); ++i) {
const auto &entry = cases[i];
u32 offset = i * 12u;
for (u32 reg : {4u, 0u, 7u}) {
AppendVMovU32(&code, 30, offset);
AppendBufferLoadDword(&code, reg, 30); // Runtime inputs prevent folding.
offset += 4u;
}
AppendVMovLiteral(&code, 17, 0xa5a5beefu);
if (!entry.active) {
code.push_back(EncodeSop1(0x04, 24, 126)); // Preserve the initial active lanes.
code.push_back(EncodeSop1(0x04, 126, 128));
}
if (i == 0u) {
code.insert(code.end(), {0xd7537011u, 0x041e0104u});
} else {
AppendVop3(&code, 0x353, entry.dst, Vgpr(4), Vgpr(0), Vgpr(7), 0,
entry.selectors);
}
if (!entry.active) code.push_back(EncodeSop1(0x04, 126, 24));
AppendStoreVgpr(&code, entry.dst, static_cast<u32>(test.initial.size()) + i);
test.expected.push_back(entry.expected);
}
AppendEnd(&code);
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MIN3_U16,
O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.decoded_counts = {{"V_MIN3_U16", cases.size()}};
test.required_spirv = {"OpULessThan"};
return test;
}
TestCase Vop3Med3I16Captured() {
using O = ShaderOpcode;
@@ -35459,6 +35518,7 @@ std::vector<TestCase> MakeCases() {
AddCase(Vop3CvtPkI16I32Captured);
AddCase(Vop3MulLoU16CapturedAndSelectors);
AddCase(Vop3MadI16CapturedSelectorsAndSaturation);
AddCase(Vop3Min3U16CapturedAndSelectors);
AddCase(Vop3Med3I16Captured);
AddCase(Vop2SdwaMinU32PreservesWordDestination);
AddCase(VectorShiftCountsMaskLowBits);
@@ -41403,6 +41463,7 @@ int main(int argc, char **argv) {
}
if (argc == 2 && std::strcmp(argv[1], "--med3-i16-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, Vop3Min3U16CapturedAndSelectors());
RunCase(&vulkan, Vop3Med3I16Captured());
return 0;
}