From 96eb681b903c5ced92ac8e6776bd479b49ab3e16 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 7 Oct 2026 09:48:34 +0400 Subject: [PATCH] shader: add V_MIN3_U16 opcode --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 16 ++--- .../recompiler/frontend/translate/Integer.cpp | 9 +-- .../frontend/translate/Translator.h | 2 +- .../recompiler/frontend/translate/Vector.cpp | 3 +- tests/ShaderRecompilerComputeTests.cpp | 61 +++++++++++++++++++ 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index cbfe143e5..51dc84ff4 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -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, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 873d53f76..99fa3e165 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -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 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 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) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 2a54988d9..01e5645de 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -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, diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 2da58b213..e63442814 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -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); diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 4810e2a63..685bc7360 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -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); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 277296ee1..f9217f2b2 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -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 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(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 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; }