diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 0b00c90c..e8822614 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -35,7 +35,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = { {0x04u, Opcode::V_SUB_F32, Vop2SdwaProfile::Float32}, {0x05u, Opcode::V_SUBREV_F32}, {0x08u, Opcode::V_MUL_F32, Vop2SdwaProfile::Float32}, - {0x09u, Opcode::V_MUL_I32_I24, Vop2SdwaProfile::IntegerFullDestination}, + {0x09u, Opcode::V_MUL_I32_I24, Vop2SdwaProfile::IntegerPartialDestination}, {0x0bu, Opcode::V_MUL_U32_U24, Vop2SdwaProfile::IntegerFullDestination}, {0x0fu, Opcode::V_MIN_F32}, {0x10u, Opcode::V_MAX_F32}, diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0d70e1b3..c5479c93 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -21531,6 +21531,65 @@ TestCase Vop2SdwaMaxI32CapturedHighWord(u32 wave_size) { return test; } +TestCase Vop2SdwaMulI24Destinations(u32 wave_size) { + using O = ShaderOpcode; + constexpr std::array sources{ + 0xdead1234u, 0xabcdffffu, 0x98768001u, 0x34560000u, 0x56789abcu}; + constexpr std::array captured_expected{ + 0xdead91a0u, 0xdead3400u, 0xabcdfff8u, 0xabcdff00u, + 0x98760008u, 0x98760100u, 0x34560000u, 0x34560000u, + 0x56789abcu, 0x56789abcu}; + constexpr std::array partial_expected{ + 0x00000080u, 0xffffff80u, 0xa1b2c380u, + 0x00008000u, 0xffff8000u, 0xa1b280d4u, + 0x00800000u, 0xff800000u, 0xa180c3d4u, + 0x80000000u, 0x80000000u, 0x80b2c3d4u, + 0x0000a180u, 0xffffa180u, 0xa1b2a180u, + 0xa1800000u, 0xa1800000u, 0xa180c3d4u}; + TestCase test; + test.name = wave_size == 64 ? "Vop2SdwaMulI24DestinationsWave64" + : "Vop2SdwaMulI24DestinationsWave32"; + test.initial.assign(sources.begin(), sources.end()); + test.initial.push_back(0xabcde080u); + test.expected = test.initial; + auto &code = test.code; + for (u32 index = 0; index < sources.size(); ++index) { + AppendVMovU32(&code, 30, index * sizeof(u32)); + AppendBufferLoadDword(&code, 12, 30); + AppendBufferLoadDword(&code, 26, 30); + AppendSMovLiteral(&code, 106, 256); + code.push_back(EncodeSMovB32(126, InlineU32(index + 1u == sources.size() ? 0 : 1))); + // Captured WORD0/PRESERVE encodings alias their source and destination. + code.insert(code.end(), {0x121818f9u, 0x04861488u, + 0x123434f9u, 0x0486146au}); + code.push_back(EncodeSMovB32(126, InlineU32(1))); + AppendStoreVgpr(&code, 12, static_cast(test.expected.size())); + AppendStoreVgpr(&code, 26, static_cast(test.expected.size()) + 1u); + test.expected.push_back(captured_expected[index * 2u]); + test.expected.push_back(captured_expected[index * 2u + 1u]); + } + AppendVMovU32(&code, 30, sources.size() * sizeof(u32)); + AppendBufferLoadDword(&code, 12, 30); + for (u32 selector = 0; selector < 6; ++selector) { + for (u32 unused = 0; unused < 3; ++unused) { + AppendVMovLiteral(&code, 26, 0xa1b2c3d4u); + code.push_back(EncodeVop2(0x09, 26, 249, 12)); + code.push_back(EncodeVop2Sdwa(InlineU32(3), selector, unused, 6, 4, + 0, 0, 0, 0, 0, 0, 1)); + AppendStoreVgpr(&code, 26, static_cast(test.expected.size())); + test.expected.push_back(partial_expected[selector * 3u + unused]); + } + } + AppendEnd(&code); + test.initial.resize(test.expected.size()); + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD, + O::V_MUL_I32_I24, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpIMul", "OpBitFieldUExtract"}; + test.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; +} + TestCase Vop2SdwaLshrrevCapturedByte1Source() { using O = ShaderOpcode; @@ -34595,6 +34654,8 @@ std::vector MakeCases() { AddCase(Vop2SdwaAshrrevCapturedWord0SignExtends); cases.push_back(Vop2SdwaMaxI32CapturedHighWord(32)); cases.push_back(Vop2SdwaMaxI32CapturedHighWord(64)); + cases.push_back(Vop2SdwaMulI24Destinations(32)); + cases.push_back(Vop2SdwaMulI24Destinations(64)); AddCase(Vop2SdwaLshrrevCapturedByte1Source); AddCase(Vop2SdwaSubNcPreservesByteAndWordDestinations); AddCase(Vop3CvtPkI16I32Captured); @@ -40415,6 +40476,13 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorAddcWritesPerLaneCarryOut()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--sdwa-mul-i24-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, Vop2SdwaMulI24Destinations(32)); + RunCase(&vulkan, Vop2SdwaMulI24Destinations(64)); + RunCase(&vulkan, VectorIntegerOps()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--cvt-pk-i16-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, Vop3CvtPkI16I32Captured()); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c38e912d..36f5300f 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -3668,6 +3668,39 @@ void TestNewShaderRecompilerVop1SdwaBfrev() { } } +void TestVop2SdwaMulI24Destination() { + using namespace ShaderRecompiler::Decoder; + const uint32_t captured[][2] = { + {0x121818f9u, 0x04861488u}, {0x123434f9u, 0x0486146au}}; + for (uint32_t index = 0; index < 2; ++index) { + Instruction decoded; + DecodeInstruction(captured[index], 0, decoded); + const uint32_t reg = index == 0 ? 12u : 26u; + Check(decoded.opcode == Opcode::V_MUL_I32_I24 && decoded.word_count == 2 && + decoded.dst.kind == OperandKind::Vgpr && decoded.dst.reg == reg && + decoded.dst.sdwa_sel == 4 && decoded.dst.sdwa_dst_unused == 2 && + decoded.dst.explicit_sdwa_dst && !decoded.dst.clamp && + decoded.dst.omod == 0 && decoded.src0.sdwa_sel == 6 && + decoded.src1.kind == OperandKind::Vgpr && decoded.src1.reg == reg && + decoded.src1.sdwa_sel == 4 && !decoded.src1.sdwa_sext, + "captured V_MUL_I32_I24 SDWA destination/source metadata is incorrect"); + Check(index == 0 ? decoded.src0.kind == OperandKind::IntegerInlineConstant && + decoded.src0.value == 8 + : decoded.src0.kind == OperandKind::VccLo, + "captured V_MUL_I32_I24 SDWA scalar source is incorrect"); + } + for (const uint32_t modifier : { + 0x04861788u, 0x04861c88u, 0x04871488u, 0x07861488u, + 0x04961488u, 0x04a61488u, 0x14861488u, 0x24861488u, + 0x04863488u, 0x04865488u}) { + const uint32_t invalid[] = {captured[0][0], modifier}; + Instruction decoded; + DecodeInstruction(invalid, 0, decoded); + Check(decoded.opcode == Opcode::UNSUPPORTED && decoded.word_count == 2, + "V_MUL_I32_I24 SDWA accepted reserved selectors or float modifiers"); + } +} + void TestNewShaderRecompilerVop1SdwaNotDestination() { auto options = MakeCompileOptions(ShaderType::Pixel); @@ -14784,6 +14817,7 @@ int main() { TestNewShaderRecompilerF64AluEncodings(); TestNewShaderRecompilerVop1SdwaBfrev(); TestNewShaderRecompilerVop1SdwaNotDestination(); + TestVop2SdwaMulI24Destination(); TestNewShaderRecompilerScalarMemoryBindingDomains(); // Opcode semantics and optimized SPIR-V are exercised by // ShaderRecompilerComputeTests; keep the distinct decoder contract checks