Merge pull request #435 from adriadam10/feat/vop3-mad-i64-i32

feat(shader): implement v_mad_i64_i32
This commit is contained in:
Dean 2026-10-04 22:54:49 +13:00 committed by GitHub
commit 6a4bbade0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 188 additions and 6 deletions

View File

@ -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 <algorithm>
#include <array>
#include <bit>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
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<std::uint32_t, Threads * Inputs> Input{};
alignas(256) std::array<std::uint32_t, Threads * Results> Output{};
alignas(256) constexpr std::array<std::uint32_t, 46> 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<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t bytes) {
const auto address = reinterpret_cast<std::uintptr_t>(data);
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((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<std::uint32_t> userData(8, 0u);
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size() * 4u));
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size() * 4u));
std::copy(input.begin(), input.end(), userData.begin());
std::copy(output.begin(), output.end(), userData.begin() + 4);
const std::span<const std::uint32_t> code(Code);
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(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<std::uintptr_t>(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<std::uintptr_t>(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;
}
}

View File

@ -58,6 +58,7 @@ enum class RdnaOpcode : std::uint16_t {
VMulLoU32,
VMulHiU32,
VMadU64U32,
VMadI64I32,
VSadU32,
VSadU8,
VSadHiU8,

View File

@ -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:

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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<IrU32, 2> 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;
}

View File

@ -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: