diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/Vop3MadI64.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/Vop3MadI64.cpp new file mode 100644 index 000000000..295d0c372 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/Vop3MadI64.cpp @@ -0,0 +1,169 @@ +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 32; +constexpr std::uint32_t Inputs = 4; +constexpr std::uint32_t Results = 16; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array Code{ + 0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0xd577080a, 0x041a0b04, 0xd501000c, + 0x00210280, 0xd576080d, 0x041a0b04, 0xd501000f, 0x00210280, 0xd5776a10, 0x04198304, 0xd5010012, + 0x01a90280, 0xd5770913, 0x03060905, 0xd5010015, 0x00250280, 0xe0701000, 0x80010a03, 0xe0701004, + 0x80010b03, 0xe0701008, 0x80010c03, 0xe070100c, 0x80010d03, 0xe0701010, 0x80010e03, 0xe0701014, + 0x80010f03, 0xe0701018, 0x80011003, 0xe070101c, 0x80011103, 0xe0701020, 0x80011203, 0xe0701024, + 0x80011303, 0xe0701028, 0x80011403, 0xe070102c, 0x80011503, 0xbf810000, +}; + +constexpr std::uint32_t Rows[32][4] = { + {0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0x00000001u, 0x00000001u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u}, + {0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu}, + {0xffffffffu, 0x00000001u, 0x00000000u, 0x00000000u}, + {0xffffffffu, 0xffffffffu, 0x00000000u, 0x00000000u}, + {0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u}, + {0x80000000u, 0x80000000u, 0xffffffffu, 0x3fffffffu}, + {0x80000000u, 0x80000000u, 0x00000000u, 0x40000000u}, + {0x7fffffffu, 0x7fffffffu, 0xffffffffu, 0x7fffffffu}, + {0x80000000u, 0x7fffffffu, 0x00000000u, 0x80000000u}, + {0x80000000u, 0x00000001u, 0x7fffffffu, 0x00000000u}, + {0x80000000u, 0x00000001u, 0x80000000u, 0x00000000u}, + {0xffffffffu, 0x00000001u, 0x00000001u, 0x00000000u}, + {0x00000001u, 0xffffffffu, 0xffffffffu, 0xffffffffu}, + {0x00000002u, 0x80000000u, 0x00000000u, 0x80000000u}, + {0x00010000u, 0x00010000u, 0xffffffffu, 0xffffffffu}, + {0xffff0000u, 0x00010000u, 0x00000000u, 0x00000000u}, + {0x7fffffffu, 0x00000002u, 0xffffffffu, 0x7fffffffu}, + {0xfffffffeu, 0x7fffffffu, 0xffffffffu, 0xffffffffu}, + {0x80000001u, 0x80000001u, 0x00000000u, 0xc0000000u}, + {0x00000003u, 0x00000005u, 0x00000007u, 0x00000000u}, + {0x73cf256du, 0xdda1494cu, 0x8f4d3e27u, 0xdb5b5fabu}, + {0xec99108du, 0xc7fde805u, 0x7734d7c1u, 0x73ab4876u}, + {0x8201e2bdu, 0xdae44550u, 0x965eda32u, 0x309d6b79u}, + {0x2f45e678u, 0xcdcc6929u, 0x830c71c2u, 0x79cb9e86u}, + {0xa13ffe79u, 0x9d2c67edu, 0xcb008853u, 0x2fa91425u}, + {0x18187993u, 0x7253edc6u, 0x4dabb481u, 0x244caf9cu}, + {0x17362f25u, 0x89e7d15fu, 0xcf44dd3fu, 0xe3eff9c0u}, + {0xb1852f27u, 0xa26b7f62u, 0x0ab8ab67u, 0x986e86cbu}, + {0xfb710734u, 0x656abd72u, 0xf6fa5db8u, 0x73f778aau}, + {0xa7677796u, 0xbd299753u, 0x9d95847eu, 0xa66b0d38u} +}; +constexpr std::uint32_t Expected[32][12] = { + {0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu, 0x00000001u}, + {0x00000001u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffffffffu, 0x00000001u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x80000000u, 0x00000001u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000001u, 0xffffffffu, 0xffffffffu, 0x00000001u}, + {0xffffffffu, 0xffffffffu, 0x00000001u, 0xffffffffu, 0xffffffffu, 0x00000000u, 0xffffffffu, 0xffffffffu, 0x00000001u, 0xffffffffu, 0xffffffffu, 0x00000001u}, + {0xffffffffu, 0xffffffffu, 0x00000001u, 0xffffffffu, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u, 0xfffffffeu, 0xffffffffu, 0x00000001u}, + {0x00000001u, 0x00000000u, 0x00000000u, 0x00000001u, 0xfffffffeu, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u}, + {0x00000000u, 0x40000000u, 0x00000000u, 0x00000000u, 0x40000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0xffffffffu, 0x3fffffffu, 0x00000000u}, + {0xffffffffu, 0x7fffffffu, 0x00000000u, 0xffffffffu, 0x7fffffffu, 0x00000000u, 0x7fffffffu, 0x40000000u, 0x00000000u, 0xffffffffu, 0x3fffffffu, 0x00000000u}, + {0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x40000000u, 0x00000000u, 0xffffffffu, 0x3fffffffu, 0x00000000u}, + {0x00000000u, 0xbfffffffu, 0x00000000u, 0x00000000u, 0xbfffffffu, 0x00000000u, 0x80000000u, 0x7fffffffu, 0x00000000u, 0x00000000u, 0x3fffffffu, 0x00000000u}, + {0x80000000u, 0x40000000u, 0x00000001u, 0x80000000u, 0xbfffffffu, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000001u, 0x7fffffffu, 0xc0000000u, 0x00000001u}, + {0xffffffffu, 0xffffffffu, 0x00000001u, 0xffffffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0x00000000u, 0x00000000u, 0x7fffffffu, 0xffffffffu, 0x00000001u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x7fffffffu, 0xffffffffu, 0x00000001u}, + {0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000002u, 0x00000000u, 0x00000000u, 0xfffffffeu, 0xffffffffu, 0x00000001u}, + {0xfffffffeu, 0xffffffffu, 0x00000001u, 0xfffffffeu, 0x00000000u, 0x00000001u, 0xfffffffeu, 0xffffffffu, 0x00000001u, 0xfffffffeu, 0xffffffffu, 0x00000001u}, + {0x00000000u, 0x7fffffffu, 0x00000001u, 0x00000000u, 0x80000001u, 0x00000000u, 0xfffffffeu, 0x7fffffffu, 0x00000001u, 0xffffffffu, 0xfffffffeu, 0x00000001u}, + {0xffffffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0x00000000u, 0x00000001u, 0xfffeffffu, 0xffffffffu, 0x00000001u, 0xffffffffu, 0x00000000u, 0x00000000u}, + {0x00000000u, 0xffffffffu, 0x00000001u, 0x00000000u, 0x0000ffffu, 0x00000000u, 0x00010000u, 0x00000000u, 0x00000000u, 0xffffffffu, 0xfffffffeu, 0x00000001u}, + {0xfffffffdu, 0x80000000u, 0x00000000u, 0xfffffffdu, 0x80000000u, 0x00000000u, 0x80000000u, 0x7fffffffu, 0x00000000u, 0xfffffffdu, 0x00000000u, 0x00000000u}, + {0x00000001u, 0xffffffffu, 0x00000001u, 0x00000001u, 0x7ffffffeu, 0x00000001u, 0x00000001u, 0x00000000u, 0x00000000u, 0x00000001u, 0xffffffffu, 0x00000001u}, + {0x00000001u, 0xffffffffu, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x7fffffffu, 0xc0000000u, 0x00000001u, 0x00000000u, 0x3fffffffu, 0x00000000u}, + {0x00000016u, 0x00000000u, 0x00000000u, 0x00000016u, 0x00000000u, 0x00000000u, 0x00000004u, 0x00000000u, 0x00000000u, 0x0000000eu, 0x00000000u, 0x00000000u}, + {0xa5056f83u, 0xcbcf03fcu, 0x00000001u, 0xa5056f83u, 0x3f9e2969u, 0x00000001u, 0x1b7e18bau, 0xdb5b5fabu, 0x00000001u, 0x15b8315bu, 0xf073a451u, 0x00000001u}, + {0xc38af282u, 0x77e9f576u, 0x00000000u, 0xc38af282u, 0x2c80ee08u, 0x00000001u, 0x8a9bc734u, 0x73ab4876u, 0x00000000u, 0x4c561ac0u, 0x043ead00u, 0x00000000u}, + {0x9b66a642u, 0x42e0cb62u, 0x00000000u, 0x9b66a642u, 0x9fc6f36fu, 0x00000000u, 0x145cf775u, 0x309d6b7au, 0x00000000u, 0x0507cc0fu, 0x12435fe9u, 0x00000000u}, + {0x806592fau, 0x708670bbu, 0x00000000u, 0x806592fau, 0x9fcc5733u, 0x00000000u, 0x53c68b4au, 0x79cb9e86u, 0x00000000u, 0xfd592137u, 0xf6bad234u, 0x00000001u}, + {0xc46dcd58u, 0x543ce545u, 0x00000000u, 0xc46dcd58u, 0x92a94babu, 0x00000000u, 0x29c089dau, 0x2fa91426u, 0x00000000u, 0xf96d4504u, 0x2493d11fu, 0x00000000u}, + {0x79cfd333u, 0x2f0f7c10u, 0x00000000u, 0x79cfd333u, 0x2f0f7c10u, 0x00000000u, 0x35933aeeu, 0x244caf9cu, 0x00000000u, 0x2c241eb1u, 0x0ac2cc74u, 0x00000000u}, + {0x004090fau, 0xd93aceb7u, 0x00000001u, 0x004090fau, 0xf070fddcu, 0x00000000u, 0xb80eae1au, 0xe3eff9c0u, 0x00000001u, 0x30fbb3bau, 0xf54ad4f6u, 0x00000001u}, + {0x75661155u, 0xb51eab18u, 0x00000001u, 0x75661155u, 0x090f59a1u, 0x00000001u, 0x59337c40u, 0x986e86cbu, 0x00000001u, 0x6aad65edu, 0x1cb0244du, 0x00000000u}, + {0xe128f6e0u, 0x722929efu, 0x00000000u, 0xe128f6e0u, 0xd793e761u, 0x00000000u, 0xfb895684u, 0x73f778aau, 0x00000000u, 0xea2e9927u, 0xfe31b144u, 0x00000001u}, + {0xd0b0c420u, 0xbd8c941fu, 0x00000001u, 0xd0b0c420u, 0x221da308u, 0x00000001u, 0xf62e0ce8u, 0xa66b0d38u, 0x00000001u, 0x331b3fa1u, 0x172186e7u, 0x00000000u} +}; +constexpr const char* Names[12] = {"i64 lo", "i64 hi", "i64 carry", "u64 lo", "u64 hi", "u64 carry", "i64 neg1 lo", "i64 neg1 hi", "i64 neg1 carry", "i64 add-1 lo", "i64 add-1 hi", "i64 add-1 carry"}; + +void Fill(std::uint32_t tid, std::uint32_t* words) { + std::copy(std::begin(Rows[tid]), std::end(Rows[tid]), words); +} + +std::array BufferDescriptor(const void* data, std::uint32_t bytes) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu), bytes, 0x01016facu}; +} + +std::string Hex(std::uint32_t value) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x", value); + return text; +} + +void Expect(std::uint32_t tid, std::uint32_t actual, std::uint32_t expected, const char* name) { + Require(actual == expected, std::string("vop3 mad i64: lane ") + std::to_string(tid) + " " + name + " is " + Hex(actual) + ", expected " + Hex(expected)); +} + +void Run(AgcDriver::VulkanDevice& device) { + for (std::uint32_t tid = 0; tid < Threads; ++tid) Fill(tid, &Input[tid * Inputs]); + Output.fill(0xdeadbeefu); + std::vector userData(8, 0u); + const auto input = BufferDescriptor(Input.data(), static_cast(Input.size() * 4u)); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size() * 4u)); + std::copy(input.begin(), input.end(), userData.begin()); + std::copy(output.begin(), output.end(), userData.begin() + 4); + const std::span code(Code); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0u, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + const auto result = ShaderRecompiler::Recompile(request); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); +} + +void Check() { + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + const std::uint32_t* in = &Input[tid * Inputs]; + const std::uint32_t* out = &Output[tid * Results]; + for (std::uint32_t i = 0; i < 12; ++i) Expect(tid, out[i], Expected[tid][i], Names[i]); + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Run(*device); + Check(); + std::puts("vop3 mad i64 tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp index 3856a1cd6..423d77ad5 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp @@ -58,6 +58,7 @@ enum class RdnaOpcode : std::uint16_t { VMulLoU32, VMulHiU32, VMadU64U32, + VMadI64I32, VSadU32, VSadU8, VSadHiU8, diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp index 12ff6eead..a381f4ab4 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp @@ -169,6 +169,7 @@ bool IsVectorAluOpcode(RdnaOpcode opcode) { case RdnaOpcode::VMulLoU32: case RdnaOpcode::VMulHiU32: case RdnaOpcode::VMadU64U32: + case RdnaOpcode::VMadI64I32: case RdnaOpcode::VSadU32: case RdnaOpcode::VSadU8: case RdnaOpcode::VSadHiU8: diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp index 96dc5c982..2fc4d6fb4 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp @@ -437,6 +437,7 @@ constexpr VectorOpcodeInfo vop3Opcodes[] = { {0x142u, RdnaOpcode::VMadI32I24}, {0x143u, RdnaOpcode::VMadU32U24}, {0x176u, RdnaOpcode::VMadU64U32}, + {0x177u, RdnaOpcode::VMadI64I32}, {0x144u, RdnaOpcode::VCubeidF32}, {0x145u, RdnaOpcode::VCubescF32}, {0x146u, RdnaOpcode::VCubetcF32}, @@ -647,7 +648,7 @@ bool isVop3BCarryOutOpcode(RdnaOpcode opcode) { } bool isVop3BMadU64Opcode(RdnaOpcode opcode) { - return opcode == RdnaOpcode::VMadU64U32; + return opcode == RdnaOpcode::VMadU64U32 || opcode == RdnaOpcode::VMadI64I32; } bool isPermlaneOpcode(RdnaOpcode opcode) { diff --git a/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp b/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp index fbc425252..00818af8c 100644 --- a/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp +++ b/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp @@ -191,7 +191,7 @@ private: bool vFfbh32(const RdnaInstruction& inst, bool sign); bool sFlbitI32B64(const RdnaInstruction& inst, bool sign); bool integer24(const RdnaInstruction& inst, bool sign, bool addend, bool high = false); - bool vMadU64U32(const RdnaInstruction& inst); + bool vMad64x32(const RdnaInstruction& inst, bool sign); bool vSadU32(const RdnaInstruction& inst); IrU32 byteSad(const IrU32& lhs, const IrU32& rhs, std::uint32_t fieldBits, bool masked); bool subwordSad(const RdnaInstruction& inst, std::uint32_t fieldBits, std::uint32_t sumShift, bool masked); diff --git a/core/shader/recompiler/Translation/src/EmbeddedVertexFetch.cpp b/core/shader/recompiler/Translation/src/EmbeddedVertexFetch.cpp index b7ea1f13b..5185feede 100644 --- a/core/shader/recompiler/Translation/src/EmbeddedVertexFetch.cpp +++ b/core/shader/recompiler/Translation/src/EmbeddedVertexFetch.cpp @@ -160,6 +160,7 @@ std::uint32_t decodedDstSize(const RdnaInstruction& inst) { std::uint32_t embeddedFetchDstSize(const RdnaInstruction& inst) { switch (inst.op) { case RdnaOpcode::VMadU64U32: + case RdnaOpcode::VMadI64I32: case RdnaOpcode::VQsadPkU16U8: case RdnaOpcode::VMqsadPkU16U8: return 2u; diff --git a/core/shader/recompiler/Translation/src/IntegerInstructions.cpp b/core/shader/recompiler/Translation/src/IntegerInstructions.cpp index 55bfc63f3..7e895e5f3 100644 --- a/core/shader/recompiler/Translation/src/IntegerInstructions.cpp +++ b/core/shader/recompiler/Translation/src/IntegerInstructions.cpp @@ -312,12 +312,12 @@ bool TranslationContext::integer24(const RdnaInstruction& inst, bool sign, bool return true; } -bool TranslationContext::vMadU64U32(const RdnaInstruction& inst) { +bool TranslationContext::vMad64x32(const RdnaInstruction& inst, bool sign) { const IrU32 lhs = readU32(sourceAt(inst, 0u)); const IrU32 rhs = readU32(sourceAt(inst, 1u)); const std::array add = extractU64(readU64(sourceAt(inst, 2u))); const IrU32 mulLow(ir.IMul(lhs.Value(), rhs.Value())); - const IrU32 mulHigh(ir.Emit(IrOpcode::UMulHi, IrType::U32, {&lhs.Value(), &rhs.Value()})); + const IrU32 mulHigh(ir.Emit(sign ? IrOpcode::SMulHi : IrOpcode::UMulHi, IrType::U32, {&lhs.Value(), &rhs.Value()})); const IrU32 low(ir.IAdd(mulLow.Value(), add[0].Value())); const IrU1 carryLow(ir.ULessThan(low.Value(), mulLow.Value())); const IrU32 high0(ir.IAdd(mulHigh.Value(), add[1].Value())); @@ -328,7 +328,14 @@ bool TranslationContext::vMadU64U32(const RdnaInstruction& inst) { const IrU64 result(ir.ConstructU64(low.Value(), high.Value())); writeOperand(inst.destination, &result.Value()); if (inst.destination2.kind != RdnaOperandKind::Null && inst.destination2.kind != RdnaOperandKind::Unknown) { - writeMask(inst.destination2, IrU1(ir.LogicalOr(carry0.Value(), carry1.Value()))); + const IrU1 carry(ir.LogicalOr(carry0.Value(), carry1.Value())); + if (!sign) { + writeMask(inst.destination2, carry); + return true; + } + const IrU32 signsDiffer(ir.ShiftRightLogical(ir.BitwiseXor(mulHigh.Value(), add[1].Value()), ir.Constant(31u))); + const IrU32 carryU32(ir.Select(carry.Value(), ir.Constant(1u), ir.Constant(0u))); + writeMask(inst.destination2, IrU1(ir.INotEqual(signsDiffer.Value(), carryU32.Value()))); } return true; } diff --git a/core/shader/recompiler/Translation/src/VectorInstructions.cpp b/core/shader/recompiler/Translation/src/VectorInstructions.cpp index 99f0129d6..f9b27dff0 100644 --- a/core/shader/recompiler/Translation/src/VectorInstructions.cpp +++ b/core/shader/recompiler/Translation/src/VectorInstructions.cpp @@ -1028,7 +1028,9 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) { case RdnaOpcode::VMadU32U24: return integer24(inst, false, true); case RdnaOpcode::VMadU64U32: - return vMadU64U32(inst); + return vMad64x32(inst, false); + case RdnaOpcode::VMadI64I32: + return vMad64x32(inst, true); case RdnaOpcode::VSadU32: return vSadU32(inst); case RdnaOpcode::VSadU8: