From 394e63890533121ed650e10f5da045efef442e7f Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 18 Sep 2026 11:21:57 +0400 Subject: [PATCH] shader: V_MUL_LO_U16 --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 3 ++ .../recompiler/frontend/translate/Vector.cpp | 1 + tests/ShaderRecompilerComputeTests.cpp | 51 +++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 762eacf11..73f269ecc 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -300,6 +300,7 @@ enum class Opcode { V_SUBREV_NC_U32, V_ADD_NC_U16, V_SUB_NC_U16, + V_MUL_LO_U16, V_MAX_U16, V_MAX_I16, V_MIN_U16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index de16ce1a4..1f6dc9077 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -295,6 +295,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = { {0x300u, Opcode::V_LSHRREV_B64}, {0x303u, Opcode::V_ADD_NC_U16}, {0x304u, Opcode::V_SUB_NC_U16}, + {0x305u, Opcode::V_MUL_LO_U16}, {0x307u, Opcode::V_LSHRREV_B16}, {0x308u, Opcode::V_ASHRREV_I16}, {0x309u, Opcode::V_MAX_U16}, @@ -430,6 +431,7 @@ bool IsNativeVop3B16BinaryOpcode(Opcode opcode) { switch (opcode) { case Opcode::V_ADD_NC_U16: case Opcode::V_SUB_NC_U16: + case Opcode::V_MUL_LO_U16: case Opcode::V_MAX_U16: case Opcode::V_MAX_I16: case Opcode::V_MIN_U16: @@ -1174,6 +1176,7 @@ uint32_t NativeVop3SourceCount(Opcode opcode) { case Opcode::V_MUL_I32_I24: case Opcode::V_ADD_NC_U16: case Opcode::V_SUB_NC_U16: + case Opcode::V_MUL_LO_U16: case Opcode::V_MAX_U16: case Opcode::V_MAX_I16: case Opcode::V_MIN_U16: diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 112ef202b..3ffb50943 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -324,6 +324,7 @@ bool Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_ADD_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::IAdd32, false); case O::V_SUB_NC_U16: 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_MED3_I16: return V_MED3_I16(inst); case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true); case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 47f8bca9d..050a81e49 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -18159,6 +18159,51 @@ TestCase Vop3CvtPkI16I32Captured() { return test; } +TestCase Vop3MulLoU16CapturedAndSelectors() { + using O = ShaderOpcode; + + TestCase test; + test.name = "Vop3MulLoU16CapturedAndSelectors"; + // Distinct runtime halves expose source selection and prevent constant folding. + test.initial = {0x00138001u, 0x0005ffffu, 0xffffffffu}; + test.expected = {0x00138001u, 0x0005ffffu, 0xffffffffu, + 0xa5a57fffu, 0xa5a5ffedu, 0xa5a58005u, 0xa5a5005fu, + 0x7fff1234u, 0xffed1234u, 0x80051234u, 0x005f1234u, + 0xffff0001u, 0x00231234u}; + auto &code = test.code; + AppendBufferLoadDword(&code, 12, 30); + AppendVMovU32(&code, 30, 4); + AppendBufferLoadDword(&code, 24, 30); + u32 output = 3; + for (u32 selectors : {0u, 1u, 2u, 3u, 8u, 9u, 10u, 11u}) { + AppendVMovLiteral(&code, 2, 0xa5a51234u); + if (selectors == 8u) { + code.push_back(0xd7054002u); + code.push_back(0x0002310cu); // Captured v_mul_lo_u16 v2.hi, v12.lo, v24.lo. + } else { + AppendVop3(&code, 0x305, 2, Vgpr(12), Vgpr(24), 0, 0, selectors); + } + AppendStoreVgpr(&code, 2, output++); + } + + // 0xffff * 0xffff wraps to 1; aliasing preserves the original high half. + AppendVMovU32(&code, 30, 8); + AppendBufferLoadDword(&code, 12, 30); + AppendVop3(&code, 0x305, 12, Vgpr(12), Vgpr(12), 0, 0, 1); + AppendStoreVgpr(&code, 12, output++); + + AppendVMovLiteral(&code, 2, 0xa5a51234u); + AppendVop3(&code, 0x305, 2, 255, Vgpr(24), 0, 0, 11); + code.push_back(0x0007deadu); // Select literal high half: 7 * 5 = 35. + AppendStoreVgpr(&code, 2, output); + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MUL_LO_U16, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.decoded_counts = {{"V_MUL_LO_U16", 10}}; + test.required_spirv = {"OpIMul"}; + return test; +} + TestCase Vop3Med3I16Captured() { using O = ShaderOpcode; @@ -26909,6 +26954,7 @@ std::vector MakeCases() { AddCase(Vop2SdwaLshrrevCapturedByte1Source); AddCase(Vop2SdwaSubNcPreservesByteAndWordDestinations); AddCase(Vop3CvtPkI16I32Captured); + AddCase(Vop3MulLoU16CapturedAndSelectors); AddCase(Vop3Med3I16Captured); AddCase(Vop2SdwaMinU32PreservesWordDestination); AddCase(VectorShiftCountsMaskLowBits); @@ -31913,6 +31959,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, Vop3Med3I16Captured()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--mul-lo-u16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, Vop3MulLoU16CapturedAndSelectors()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--waitcnt-depctr-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, ScalarWaitcntDepctrCapturedVmVsrc());