Merge pull request #507 from adriadam10/feat/vop3-integer-clamp

fix(shader): saturate VOP3 integer mad/sad results on clamp
This commit is contained in:
Dean 2026-10-06 07:27:34 +13:00 committed by GitHub
commit fdd6519eac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 442 additions and 29 deletions

View File

@ -0,0 +1,188 @@
#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, 48> Code{
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0xd543800a, 0x041a0b04, 0xd542800b,
0x041a0b04, 0xd740800c, 0x041a0b04, 0xd75e800d, 0x041a0b04, 0xd773800e, 0x041a0b04, 0xd775800f,
0x041a0b04, 0xd55a8010, 0x041a0b04, 0xd55b8011, 0x041a0b04, 0xd55c8012, 0x041a0b04, 0xd55d8013,
0x041a0b04, 0xd5718014, 0x041a0b04, 0x7e2a0307, 0xd740d815, 0x041a0b04, 0xd75eb016, 0x041a0b04,
0xd7758817, 0x041a0b04, 0xd7739018, 0x041a0b04, 0x7e320307, 0xd75ef819, 0x041a0b04, 0xe0781000,
0x80010a03, 0xe0781010, 0x80010e03, 0xe0781020, 0x80011203, 0xe0781030, 0x80011603, 0xbf810000,
};
constexpr std::uint32_t Rows[32][4] = {
{0x00000000u, 0x00000000u, 0x00000001u, 0x0000ffffu},
{0x00000000u, 0x00007fffu, 0xffffffffu, 0x0000fff0u},
{0x01010101u, 0x01010101u, 0xffffffffu, 0xff00ff00u},
{0x00000001u, 0xff00ff00u, 0x80000000u, 0x7fffffffu},
{0x00000000u, 0x00000000u, 0x506377feu, 0x09418c53u},
{0x00000000u, 0x00000000u, 0xad078efau, 0x00000002u},
{0x00000000u, 0x00000001u, 0x00000002u, 0x293b6032u},
{0x00000000u, 0x00000001u, 0x0000fff0u, 0x00ff00ffu},
{0x00000000u, 0x00000001u, 0x00ff00ffu, 0xdbd16e44u},
{0x00000000u, 0x00000001u, 0x671b91d7u, 0xb1836b5au},
{0x00000000u, 0x00000001u, 0xb51b0523u, 0x54b074b8u},
{0x00000000u, 0x00000001u, 0xff0ec345u, 0x00007fffu},
{0x00000000u, 0x00000002u, 0x00000000u, 0xfefefefeu},
{0x00000000u, 0x0000007fu, 0x00000002u, 0xcee69ed0u},
{0x00000000u, 0x0000007fu, 0x12345678u, 0x40778188u},
{0x00000000u, 0x0000007fu, 0x1bf35776u, 0x12345678u},
{0x00000000u, 0x0000007fu, 0x29c0ba3bu, 0x48e98a0au},
{0x00000000u, 0x0000007fu, 0x9d299c7bu, 0x39cdd549u},
{0x00000000u, 0x0000007fu, 0x9ff9949eu, 0x00ffffffu},
{0x00000000u, 0x00000080u, 0x000000ffu, 0x12345678u},
{0x00000000u, 0x00000080u, 0x00008000u, 0x80000001u},
{0x00000000u, 0x00000080u, 0x0000fff0u, 0xae15989du},
{0x00000000u, 0x00000080u, 0x0000ffffu, 0x00000080u},
{0x00000000u, 0x00000080u, 0x80000001u, 0x0000007fu},
{0x00000000u, 0x00000080u, 0xaf4a9e38u, 0xd42344f4u},
{0x00000000u, 0x00000080u, 0xea4d8c45u, 0x00625618u},
{0x00000000u, 0x00000080u, 0xfd4fb563u, 0xff00ff00u},
{0x00000000u, 0x000000ffu, 0x00000001u, 0x310733d9u},
{0x00000000u, 0x000000ffu, 0x2f3bacc0u, 0xffff0000u},
{0x00000000u, 0x000000ffu, 0x85f55b11u, 0xffff0000u},
{0x00000000u, 0x000000ffu, 0xff00ff00u, 0x9fc408e0u},
{0x00000000u, 0x000000ffu, 0xffff0000u, 0xffff0000u}
};
constexpr std::uint32_t Expected[32][16] = {
{0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x0001ffffu, 0x00000000u, 0x00000001u, 0x00000001u, 0x0000ffffu},
{0xffffffffu, 0xffffffffu, 0x0000ffffu, 0x0000ffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xfffffff0u, 0x0000ffffu, 0xffffffffu, 0xffffffffu, 0xfffffff0u},
{0xffffffffu, 0x7fffffffu, 0x0000ffffu, 0x00007fffu, 0xffffffffu, 0x00010200u, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffff00u, 0x00007fffu, 0x00010200u, 0xffffffffu, 0x7fffff00u},
{0x8000ff00u, 0x8000ff00u, 0x0000ff00u, 0x0000ff00u, 0x8000ff00u, 0x80000000u, 0x800001ffu, 0x81ff0000u, 0x8001fdffu, 0xffffffffu, 0x800001feu, 0x0000ffffu, 0x00008000u, 0x80000000u, 0x8000ff00u, 0x8000ffffu},
{0x506377feu, 0x506377feu, 0x000077feu, 0x000077feu, 0x506377feu, 0x506377feu, 0x506377feu, 0x506377feu, 0x506377feu, 0x506377feu, 0x506377feu, 0x77fe8c53u, 0x00005063u, 0x506377feu, 0x506377feu, 0x50638c53u},
{0xad078efau, 0xad078efau, 0x00008efau, 0x00008efau, 0xad078efau, 0xad078efau, 0xad078efau, 0xad078efau, 0xad078efau, 0xad078efau, 0xad078efau, 0x8efa0002u, 0x0000ad07u, 0xad078efau, 0xad078efau, 0xad070002u},
{0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000003u, 0x00010002u, 0x00000003u, 0x00000003u, 0x00000003u, 0x00026032u, 0x00000000u, 0x00000002u, 0x00000002u, 0x00006032u},
{0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff1u, 0x0001fff0u, 0x0000fff1u, 0x0000fff1u, 0x0000fff1u, 0xfff000ffu, 0x00000000u, 0x0000fff0u, 0x0000fff0u, 0x000000ffu},
{0x00ff00ffu, 0x00ff00ffu, 0x000000ffu, 0x000000ffu, 0x00ff00ffu, 0x00ff00ffu, 0x00ff0100u, 0x010000ffu, 0x00ff0100u, 0x00ff0100u, 0x00ff0100u, 0x00ff6e44u, 0x000000ffu, 0x00ff00ffu, 0x00ff00ffu, 0x00ff6e44u},
{0x671b91d7u, 0x671b91d7u, 0x000091d7u, 0x000091d7u, 0x671b91d7u, 0x671b91d7u, 0x671b91d8u, 0x671c91d7u, 0x671b91d8u, 0x671b91d8u, 0x671b91d8u, 0x91d76b5au, 0x0000671bu, 0x671b91d7u, 0x671b91d7u, 0x671b6b5au},
{0xb51b0523u, 0xb51b0523u, 0x00000523u, 0x00000523u, 0xb51b0523u, 0xb51b0523u, 0xb51b0524u, 0xb51c0523u, 0xb51b0524u, 0xb51b0524u, 0xb51b0524u, 0x052374b8u, 0x0000b51bu, 0xb51b0523u, 0xb51b0523u, 0xb51b74b8u},
{0xff0ec345u, 0xff0ec345u, 0x0000c345u, 0x0000c345u, 0xff0ec345u, 0xff0ec345u, 0xff0ec346u, 0xff0fc345u, 0xff0ec346u, 0xff0ec346u, 0xff0ec346u, 0xc3457fffu, 0x0000ff0eu, 0xff0ec345u, 0xff0ec345u, 0xff0e7fffu},
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000002u, 0x00020000u, 0x00000002u, 0x00000002u, 0x00000002u, 0x0000fefeu, 0x00000000u, 0x00000000u, 0x00000000u, 0x0000fefeu},
{0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000002u, 0x00000081u, 0x007f0002u, 0x00000081u, 0x00000081u, 0x00000081u, 0x00029ed0u, 0x00000000u, 0x00000002u, 0x00000002u, 0x00009ed0u},
{0x12345678u, 0x12345678u, 0x00005678u, 0x00005678u, 0x12345678u, 0x12345678u, 0x123456f7u, 0x12b35678u, 0x123456f7u, 0x123456f7u, 0x123456f7u, 0x56788188u, 0x00001234u, 0x12345678u, 0x12345678u, 0x12348188u},
{0x1bf35776u, 0x1bf35776u, 0x00005776u, 0x00005776u, 0x1bf35776u, 0x1bf35776u, 0x1bf357f5u, 0x1c725776u, 0x1bf357f5u, 0x1bf357f5u, 0x1bf357f5u, 0x57765678u, 0x00001bf3u, 0x1bf35776u, 0x1bf35776u, 0x1bf35678u},
{0x29c0ba3bu, 0x29c0ba3bu, 0x0000ba3bu, 0x0000ba3bu, 0x29c0ba3bu, 0x29c0ba3bu, 0x29c0babau, 0x2a3fba3bu, 0x29c0babau, 0x29c0babau, 0x29c0babau, 0xba3b8a0au, 0x000029c0u, 0x29c0ba3bu, 0x29c0ba3bu, 0x29c08a0au},
{0x9d299c7bu, 0x9d299c7bu, 0x00009c7bu, 0x00009c7bu, 0x9d299c7bu, 0x9d299c7bu, 0x9d299cfau, 0x9da89c7bu, 0x9d299cfau, 0x9d299cfau, 0x9d299cfau, 0x9c7bd549u, 0x00009d29u, 0x9d299c7bu, 0x9d299c7bu, 0x9d29d549u},
{0x9ff9949eu, 0x9ff9949eu, 0x0000949eu, 0x0000949eu, 0x9ff9949eu, 0x9ff9949eu, 0x9ff9951du, 0xa078949eu, 0x9ff9951du, 0x9ff9951du, 0x9ff9951du, 0x949effffu, 0x00009ff9u, 0x9ff9949eu, 0x9ff9949eu, 0x9ff9ffffu},
{0x000000ffu, 0x000000ffu, 0x000000ffu, 0x000000ffu, 0x000000ffu, 0x000000ffu, 0x0000017fu, 0x008000ffu, 0x0000017fu, 0x0000017fu, 0x0000017fu, 0x00ff5678u, 0x00000000u, 0x000000ffu, 0x000000ffu, 0x00005678u},
{0x00008000u, 0x00008000u, 0x00008000u, 0x00008000u, 0x00008000u, 0x00008000u, 0x00008080u, 0x00808000u, 0x00008080u, 0x00008080u, 0x00008080u, 0x80000001u, 0x00000000u, 0x00008000u, 0x00008000u, 0x00000001u},
{0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x0000fff0u, 0x00010070u, 0x0080fff0u, 0x00010070u, 0x00010070u, 0x00010070u, 0xfff0989du, 0x00000000u, 0x0000fff0u, 0x0000fff0u, 0x0000989du},
{0x0000ffffu, 0x0000ffffu, 0x0000ffffu, 0x0000ffffu, 0x0000ffffu, 0x0000ffffu, 0x0001007fu, 0x0080ffffu, 0x0001007fu, 0x0001007fu, 0x0001007fu, 0xffff0080u, 0x00000000u, 0x0000ffffu, 0x0000ffffu, 0x00000080u},
{0x80000001u, 0x80000001u, 0x00000001u, 0x00000001u, 0x80000001u, 0x80000001u, 0x80000081u, 0x80800001u, 0x80000081u, 0x80000081u, 0x80000081u, 0x0001007fu, 0x00008000u, 0x80000001u, 0x80000001u, 0x8000007fu},
{0xaf4a9e38u, 0xaf4a9e38u, 0x00009e38u, 0x00009e38u, 0xaf4a9e38u, 0xaf4a9e38u, 0xaf4a9eb8u, 0xafca9e38u, 0xaf4a9eb8u, 0xaf4a9eb8u, 0xaf4a9eb8u, 0x9e3844f4u, 0x0000af4au, 0xaf4a9e38u, 0xaf4a9e38u, 0xaf4a44f4u},
{0xea4d8c45u, 0xea4d8c45u, 0x00008c45u, 0x00008c45u, 0xea4d8c45u, 0xea4d8c45u, 0xea4d8cc5u, 0xeacd8c45u, 0xea4d8cc5u, 0xea4d8cc5u, 0xea4d8cc5u, 0x8c455618u, 0x0000ea4du, 0xea4d8c45u, 0xea4d8c45u, 0xea4d5618u},
{0xfd4fb563u, 0xfd4fb563u, 0x0000b563u, 0x0000b563u, 0xfd4fb563u, 0xfd4fb563u, 0xfd4fb5e3u, 0xfdcfb563u, 0xfd4fb5e3u, 0xfd4fb5e3u, 0xfd4fb5e3u, 0xb563ff00u, 0x0000fd4fu, 0xfd4fb563u, 0xfd4fb563u, 0xfd4fff00u},
{0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000001u, 0x00000100u, 0x00ff0001u, 0x00000100u, 0x00000100u, 0x00000100u, 0x000133d9u, 0x00000000u, 0x00000001u, 0x00000001u, 0x000033d9u},
{0x2f3bacc0u, 0x2f3bacc0u, 0x0000acc0u, 0x0000acc0u, 0x2f3bacc0u, 0x2f3bacc0u, 0x2f3badbfu, 0x303aacc0u, 0x2f3badbfu, 0x2f3badbfu, 0x2f3badbfu, 0xacc00000u, 0x00002f3bu, 0x2f3bacc0u, 0x2f3bacc0u, 0x2f3b0000u},
{0x85f55b11u, 0x85f55b11u, 0x00005b11u, 0x00005b11u, 0x85f55b11u, 0x85f55b11u, 0x85f55c10u, 0x86f45b11u, 0x85f55c10u, 0x85f55c10u, 0x85f55c10u, 0x5b110000u, 0x000085f5u, 0x85f55b11u, 0x85f55b11u, 0x85f50000u},
{0xff00ff00u, 0xff00ff00u, 0x0000ff00u, 0x0000ff00u, 0xff00ff00u, 0xff00ff00u, 0xff00ffffu, 0xffffff00u, 0xff00ffffu, 0xff00ffffu, 0xff00ffffu, 0xff0008e0u, 0x0000ff00u, 0xff00ff00u, 0xff00ff00u, 0xff0008e0u},
{0xffff0000u, 0xffff0000u, 0x00000000u, 0x00000000u, 0xffff0000u, 0xffff0000u, 0xffff00ffu, 0xffffffffu, 0xffff00ffu, 0xffff00ffu, 0xffff00ffu, 0x00000000u, 0x0000ffffu, 0xffff0000u, 0xffff0000u, 0xffff0000u}
};
constexpr const char* Names[16] = {
"mad_u32_u24",
"mad_i32_i24",
"mad_u16",
"mad_i16",
"mad_u32_u16",
"mad_i32_i16",
"sad_u8",
"sad_hi_u8",
"sad_u16",
"sad_u32",
"msad_u8",
"mad_u16 op_sel",
"mad_i16 op_sel",
"mad_i32_i16 op_sel",
"mad_u32_u16 op_sel",
"mad_i16 op_sel",
};
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 integer clamp: 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 < 16; ++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 integer clamp tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}

View File

@ -0,0 +1,187 @@
#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, 36> Code{
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0xd572800a, 0x041a0d04, 0xd573800c,
0x041a0d04, 0xd575800e, 0x04120d04, 0xd7558012, 0x041a0b04, 0xd7568013, 0x041a0b04, 0xd7528014,
0x041a0b04, 0xd7538015, 0x041a0b04, 0xd7588016, 0x041a0b04, 0xd7598017, 0x041a0b04, 0xd54e8018,
0x041a0b04, 0xd54f8019, 0x041a0b04, 0xe0781000, 0x80010a03, 0xe0781010, 0x80010e03, 0xe0781020,
0x80011203, 0xe0781030, 0x80011603, 0xbf810000,
};
constexpr std::uint32_t Rows[32][4] = {
{0x00000000u, 0x00000000u, 0x00800000u, 0x0000ffffu},
{0x0000007fu, 0xe3718038u, 0xfffffffeu, 0xfd7c84feu},
{0x0000007fu, 0xfffffffeu, 0x0000007fu, 0xfffffffeu},
{0xfffffffeu, 0x00000002u, 0xb93201bfu, 0x00000000u},
{0x00000000u, 0x00000000u, 0x3ff51543u, 0x8ca197b1u},
{0x00000000u, 0x00000001u, 0x007fffffu, 0x29813b74u},
{0x00000000u, 0x00000001u, 0x00ff00ffu, 0x00007fffu},
{0x00000000u, 0x00000001u, 0x01010101u, 0xa48d7f36u},
{0x00000000u, 0x00000001u, 0x1ef5acc3u, 0x5d780492u},
{0x00000000u, 0x00000001u, 0x41f8bfb7u, 0x3f57a9d3u},
{0x00000000u, 0x00000001u, 0xfefefefeu, 0x0000007fu},
{0x00000000u, 0x00000002u, 0x00ffffffu, 0x80000000u},
{0x00000000u, 0x00000002u, 0x7351a059u, 0x00007fffu},
{0x00000000u, 0x00000002u, 0xcb50b50fu, 0x7fffffffu},
{0x00000000u, 0x0000007fu, 0x0000ffffu, 0x01010101u},
{0x00000000u, 0x0000007fu, 0x0ac13011u, 0xffff0000u},
{0x00000000u, 0x0000007fu, 0x4b88f735u, 0x000000ffu},
{0x00000000u, 0x0000007fu, 0xffffffffu, 0x0000fff0u},
{0x00000000u, 0x00000080u, 0x00000000u, 0x00ffffffu},
{0x00000000u, 0x00000080u, 0xffffffffu, 0xde945808u},
{0x00000000u, 0x000000ffu, 0x000000ffu, 0x00000080u},
{0x00000000u, 0x000000ffu, 0x7fffffffu, 0xca9c52c9u},
{0x00000000u, 0x000000ffu, 0x80000001u, 0x0000007fu},
{0x00000000u, 0x000000ffu, 0xffff0000u, 0x00007fffu},
{0x00000000u, 0x000000ffu, 0xffffffffu, 0xffffffffu},
{0x00000000u, 0x00007fffu, 0x01010101u, 0xcff6ab04u},
{0x00000000u, 0x00007fffu, 0x44e9da39u, 0x63548e6fu},
{0x00000000u, 0x00007fffu, 0x4944fa2au, 0xf5524d50u},
{0x00000000u, 0x00007fffu, 0xa1dfdb1eu, 0xa9e05a10u},
{0x00000000u, 0x00007fffu, 0xffff0000u, 0x01010101u},
{0x00000000u, 0x00007fffu, 0xffffffffu, 0x0000007fu},
{0x00000000u, 0x0000fff0u, 0x00000080u, 0x01010101u}
};
constexpr std::uint32_t Expected[32][16] = {
{0x01000080u, 0x0080ffffu, 0x01000080u, 0x0080ffffu, 0x00000080u, 0x00000080u, 0x00800080u, 0x0001007fu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
{0xffffffffu, 0xffff8841u, 0xffffffffu, 0xffff8841u, 0x000003fbu, 0xe37183fbu, 0xffffffffu, 0xfd7c87d0u, 0x0000007fu, 0x0000fffeu, 0x00008038u, 0x0000007fu, 0x0000fffeu, 0x00008038u, 0x000001ffu, 0x007fe371u},
{0x017d007fu, 0xffffffffu, 0x007f007fu, 0xffffffffu, 0x0000007fu, 0xffffffffu, 0x000000feu, 0xffffffffu, 0x0000007fu, 0x0000fffeu, 0x0000fffeu, 0x0000007fu, 0x0000007fu, 0x0000007fu, 0x000000ffu, 0x00007fffu},
{0xbbf4040fu, 0x012c0227u, 0xbbf4040fu, 0x012c0227u, 0xffffffffu, 0x000002c4u, 0xb93203e6u, 0x0000012cu, 0x000001bfu, 0x0000fffeu, 0x0000fffeu, 0x00000002u, 0x00000002u, 0x000001bfu, 0xfffffffcu, 0xfffffe00u},
{0x418116cfu, 0x8e2d993du, 0x418116cfu, 0x8e2d993du, 0x0000018cu, 0x0000018cu, 0x3ff516cfu, 0x8ca1993du, 0x00001543u, 0x00001543u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
{0x02fdffffu, 0x2bfd3df0u, 0x02fcffffu, 0x2bfd3df0u, 0x0000027du, 0x0000027eu, 0x0080027bu, 0x29813df0u, 0x00000001u, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u},
{0x02fe02fdu, 0x01ff81fcu, 0x02fd02fdu, 0x01fe81fcu, 0x000001feu, 0x000001ffu, 0x00ff02fcu, 0x000081fdu, 0x000000ffu, 0x000000ffu, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000001u, 0x00000000u, 0x00000000u},
{0x01040105u, 0xa4907f39u, 0x01040105u, 0xa4907f39u, 0x00000004u, 0x00000004u, 0x01010104u, 0xa48d7f39u, 0x00000101u, 0x00000101u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000001u, 0x00000000u, 0x00000000u},
{0x2176af45u, 0x5ff90713u, 0x2176af45u, 0x5ff90713u, 0x00000282u, 0x00000282u, 0x1ef5af44u, 0x5d780713u, 0x00000001u, 0x0000acc3u, 0x0000acc3u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u},
{0x44a6c266u, 0x4205ac81u, 0x44a6c266u, 0x4205ac81u, 0x000002afu, 0x000002afu, 0x41f8c265u, 0x3f57ac81u, 0x00000001u, 0x0000bfb7u, 0x0000bfb7u, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u},
{0xffffffffu, 0x03f70476u, 0xffffffffu, 0x03f70476u, 0x000003f8u, 0x000003f8u, 0xfeff02f5u, 0x00000476u, 0x00000001u, 0x0000fefeu, 0x0000fefeu, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000000u, 0x00000000u},
{0x03feffffu, 0x82fb02fbu, 0x03fcffffu, 0x82fb02fbu, 0x000002fdu, 0x000002ffu, 0x010002fau, 0x800002fbu, 0x00000002u, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x00000002u, 0x00000000u, 0x00000000u},
{0x750ca216u, 0x01bb81bau, 0x750ca216u, 0x01bb81bau, 0x000001bdu, 0x000001bdu, 0x7351a214u, 0x000081bau, 0x00000002u, 0x0000a059u, 0x0000a059u, 0x00000000u, 0x00000000u, 0x00000002u, 0x00000000u, 0x00000000u},
{0xcd2db6eeu, 0x81dcffffu, 0xcd2db6eeu, 0x81dcffffu, 0x000001dfu, 0x000001dfu, 0xcb50b6ecu, 0x800001dcu, 0x00000002u, 0x0000b50fu, 0x0000b50fu, 0x00000000u, 0x00000000u, 0x00000002u, 0x00000000u, 0x00000000u},
{0x027dffffu, 0x0280037eu, 0x01feffffu, 0x028002ffu, 0x000001feu, 0x0000027du, 0x000101fdu, 0x01010280u, 0x0000007fu, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x0000007fu, 0x00000000u, 0x00000000u},
{0x0c38311du, 0xffff008du, 0x0c38311du, 0xffff008du, 0x0000010cu, 0x000001f6u, 0x0ac1309eu, 0xffff012bu, 0x00003011u, 0x00003011u, 0x00000000u, 0x00000000u, 0x0000007fu, 0x0000007fu, 0x00000000u, 0x00000000u},
{0x4d70f934u, 0x0180027fu, 0x4d70f934u, 0x0180027fu, 0x000001ffu, 0x00000267u, 0x4b88f8b5u, 0x0000027fu, 0x0000007fu, 0x0000f735u, 0x0000f735u, 0x00000000u, 0x00000000u, 0x0000007fu, 0x00000000u, 0x00000000u},
{0xffffffffu, 0x037dffffu, 0xffffffffu, 0x037dffffu, 0x000003fcu, 0x000003fcu, 0xffffffffu, 0x0001036du, 0x0000007fu, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x0000007fu, 0x00000000u, 0x00000000u},
{0x00800000u, 0x017fffffu, 0x00000000u, 0x00ffffffu, 0x00000000u, 0x00000080u, 0x00000000u, 0x00ffffffu, 0x00000080u, 0x00000080u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000080u, 0x00000080u},
{0xffffffffu, 0xe2105b84u, 0xffffffffu, 0xe2105b84u, 0x000003fcu, 0x000003fcu, 0xffffffffu, 0xde945b84u, 0x00000080u, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x00000080u, 0x00000000u, 0x00000000u},
{0x01fe01feu, 0x01fe027eu, 0x00ff01feu, 0x00ff017fu, 0x000000ffu, 0x000001feu, 0x000001feu, 0x0000017fu, 0x000000ffu, 0x000000ffu, 0x00000000u, 0x00000000u, 0x000000ffu, 0x000000ffu, 0x00000000u, 0x00000000u},
{0x837cffffu, 0xcd195546u, 0x837cffffu, 0xcd195546u, 0x0000037cu, 0x0000047cu, 0x8000027cu, 0xca9c5546u, 0x000000ffu, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x000000ffu, 0x00000000u, 0x00000000u},
{0x80800082u, 0x018001ffu, 0x80800082u, 0x00810100u, 0x00000081u, 0x0000017fu, 0x80000082u, 0x00000100u, 0x000000ffu, 0x000000ffu, 0x00000000u, 0x00000000u, 0x00000001u, 0x00000001u, 0x0000007fu, 0x00000000u},
{0xffff01feu, 0x02fd80feu, 0xffff01feu, 0x01fe80feu, 0x000001feu, 0x000001feu, 0xffff00ffu, 0x000081fdu, 0x000000ffu, 0x000000ffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x000000ffu, 0x000000ffu},
{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0x000003fcu, 0x000003fcu, 0xffffffffu, 0xffffffffu, 0x000000ffu, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x000000ffu, 0x00000000u, 0x00000000u},
{0x02020105u, 0xd174ac82u, 0x02020105u, 0xd174ac82u, 0x00000004u, 0x00008100u, 0x0101027fu, 0xcff6ac82u, 0x00007fffu, 0x00007fffu, 0x00000000u, 0x00000000u, 0x00000101u, 0x00000101u, 0x00003fffu, 0x0000007fu},
{0x47a0dc79u, 0x64608fd3u, 0x47a0dc79u, 0x64608fd3u, 0x00000240u, 0x000082b6u, 0x44e9db9du, 0x63548f7bu, 0x00007fffu, 0x0000da39u, 0x0000da39u, 0x00000000u, 0x00000000u, 0x00007fffu, 0x00000000u, 0x0000007fu},
{0x4b62fbdbu, 0xf6054f65u, 0x4b62fbdbu, 0xf6054f65u, 0x000001b1u, 0x0000821du, 0x4944fc3fu, 0xf5524e03u, 0x00007fffu, 0x0000fa2au, 0x0000fa2au, 0x00000000u, 0x00000000u, 0x00007fffu, 0x0000001fu, 0x00000000u},
{0xa415dd97u, 0xab235b4bu, 0xa415dd97u, 0xab235b4bu, 0x00000279u, 0x00008235u, 0xa1dfdc59u, 0xa9e05b53u, 0x00007fffu, 0x0000db1eu, 0x0000db1eu, 0x00000000u, 0x00000000u, 0x00007fffu, 0x00000000u, 0x00000000u},
{0xffff01feu, 0x037f0181u, 0xffff01feu, 0x02800181u, 0x000001feu, 0x000080feu, 0xffff0080u, 0x01010280u, 0x00007fffu, 0x00007fffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00007fffu, 0x00007fffu},
{0xffffffffu, 0x027e02fdu, 0xffffffffu, 0x027e02fdu, 0x000003fcu, 0x000082fcu, 0xffffffffu, 0x000002fdu, 0x00007fffu, 0x0000ffffu, 0x0000ffffu, 0x00000000u, 0x00000000u, 0x00007fffu, 0x00000000u, 0x00000000u},
{0x01700100u, 0x03700370u, 0x00800100u, 0x01810181u, 0x00000080u, 0x00010070u, 0x00000100u, 0x01010181u, 0x00000080u, 0x0000fff0u, 0x0000fff0u, 0x00000000u, 0x00000000u, 0x00000080u, 0x0000fff0u, 0x0000fff0u}
};
constexpr const char* Names[16] = {
"qsad_pk_u16_u8 0",
"qsad_pk_u16_u8 1",
"mqsad_pk_u16_u8 0",
"mqsad_pk_u16_u8 1",
"mqsad_u32_u8 0",
"mqsad_u32_u8 1",
"mqsad_u32_u8 2",
"mqsad_u32_u8 3",
"max3_i16",
"max3_u16",
"min3_i16",
"min3_u16",
"med3_i16",
"med3_u16",
"alignbit_b32",
"alignbyte_b32",
};
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 integer clamp wide: 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 < 16; ++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 integer clamp wide tests passed");
return 0;
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}

View File

@ -90,19 +90,6 @@ void Run(AgcDriver::VulkanDevice& device) {
device.WaitIdle();
}
void CheckClampRefused(AgcDriver::VulkanDevice& device) {
for (const std::uint32_t opcode : {0x15au, 0x15bu, 0x15cu, 0x171u}) {
alignas(256) const std::array<std::uint32_t, 3> code{0xd400800au | (opcode << 16u), 0x041a0b04u, 0xbf810000u};
std::string refusal;
try {
static_cast<void>(Compile(device, code));
} catch (const std::exception& error) {
refusal = error.what();
}
Require(refusal.find("VOP3 source modifiers are not implemented") != std::string::npos, "VOP3 opcode " + Hex(opcode) + " with clamp was not refused");
}
}
std::uint32_t Sad(std::uint32_t lhs, std::uint32_t rhs, std::uint32_t fieldBits, bool masked) {
const std::uint32_t fieldMask = (1u << fieldBits) - 1u;
std::uint32_t sum = 0u;
@ -154,7 +141,6 @@ int main() {
FillInput();
Run(*device);
Check();
CheckClampRefused(*device);
std::puts("vop3 sad tests passed");
return 0;
} catch (const std::exception& error) {

View File

@ -752,8 +752,26 @@ bool isNativeVop3AlignOpcode(RdnaOpcode opcode) {
return opcode == RdnaOpcode::VAlignbitB32 || opcode == RdnaOpcode::VAlignbyteB32;
}
bool usesSignedSaturateClamp(RdnaOpcode opcode) {
return opcode == RdnaOpcode::VAddNcI32 || opcode == RdnaOpcode::VSubNcI32;
bool usesIntegerSaturateClamp(RdnaOpcode opcode) {
switch (opcode) {
case RdnaOpcode::VAddNcI32:
case RdnaOpcode::VSubNcI32:
case RdnaOpcode::VMadU32U24:
case RdnaOpcode::VMadI32I24:
case RdnaOpcode::VMadU16:
case RdnaOpcode::VMadI16:
case RdnaOpcode::VMadU32U16:
case RdnaOpcode::VMadI32I16:
case RdnaOpcode::VSadU8:
case RdnaOpcode::VSadHiU8:
case RdnaOpcode::VSadU16:
case RdnaOpcode::VSadU32:
case RdnaOpcode::VMsadU8:
case RdnaOpcode::VQsadPkU16U8:
case RdnaOpcode::VMqsadPkU16U8:
case RdnaOpcode::VMqsadU32U8: return true;
default: return false;
}
}
bool isCubeOpcode(RdnaOpcode opcode) {
@ -1941,7 +1959,7 @@ bool isF32DivisionStepOpcode(RdnaOpcode opcode) {
}
bool supportsNativeVop3Clamp(RdnaOpcode opcode) {
return supportsNativeVop3ResultModifiers(opcode) || usesInexactClampControl(opcode) || usesSignedSaturateClamp(opcode) ||
return supportsNativeVop3ResultModifiers(opcode) || usesInexactClampControl(opcode) || usesIntegerSaturateClamp(opcode) ||
isF32DivisionStepOpcode(opcode) || isCubeOpcode(opcode);
}
@ -1959,13 +1977,13 @@ void checkNativeVop3Modifiers(RdnaOpcode opcode, bool permlane, bool carryInOut,
return;
}
if (isNativeVop3I16TernaryOpcode(opcode)) {
if (abs != 0u || clamp != 0u || omod != 0u || neg != 0u) {
if (abs != 0u || omod != 0u || neg != 0u) {
throw std::invalid_argument("VOP3 source modifiers are not implemented");
}
return;
}
if (isNativeVop3AlignOpcode(opcode)) {
if (abs != 0u || clamp != 0u || omod != 0u || neg != 0u) {
if (abs != 0u || omod != 0u || neg != 0u) {
throw std::invalid_argument("VOP3 source modifiers are not implemented");
}
return;
@ -1983,7 +2001,7 @@ void checkNativeVop3Modifiers(RdnaOpcode opcode, bool permlane, bool carryInOut,
}
return;
}
if (usesSignedSaturateClamp(opcode)) {
if (usesIntegerSaturateClamp(opcode)) {
if (abs != 0u || opSel != 0u || omod != 0u || neg != 0u) {
throw std::invalid_argument("VOP3 source modifiers are not implemented");
}

View File

@ -193,6 +193,8 @@ private:
bool integer16Shift(const RdnaInstruction& inst, IrOpcode opcode, bool arithmetic);
bool integer16Binary(const RdnaInstruction& inst, IrOpcode opcode, bool sign);
bool integer16Mad(const RdnaInstruction& inst, bool sign, bool wide);
IrU32 saturateU16Result(const RdnaInstruction& inst, const IrU32& value, bool sign);
IrU32 wideAdd(const RdnaInstruction& inst, const IrU32& low, const IrU32& high, const IrU32& addend, bool sign);
bool vAddSubNcI32(const RdnaInstruction& inst, bool subtract);
bool integer16Ternary(const RdnaInstruction& inst, IrOpcode opcode, bool sign);
bool vMed3I16(const RdnaInstruction& inst);

View File

@ -25,6 +25,33 @@ bool TranslationContext::integer16Binary(const RdnaInstruction& inst, IrOpcode o
return true;
}
IrU32 TranslationContext::saturateU16Result(const RdnaInstruction& inst, const IrU32& value, bool sign) {
if (!inst.destination.clamp) {
return value;
}
if (!sign) {
return IrU32(ir.Emit(IrOpcode::UMin32, IrType::U32, {&value.Value(), &ir.Constant(0xffffu)}));
}
const IrU32 upper(ir.Emit(IrOpcode::SMin32, IrType::U32, {&value.Value(), &ir.Constant(0x7fffu)}));
return IrU32(ir.Emit(IrOpcode::SMax32, IrType::U32, {&upper.Value(), &ir.Constant(0xffff8000u)}));
}
IrU32 TranslationContext::wideAdd(const RdnaInstruction& inst, const IrU32& low, const IrU32& high, const IrU32& addend, bool sign) {
const IrU32 sum(ir.IAdd(low.Value(), addend.Value()));
if (!inst.destination.clamp) {
return sum;
}
const IrU32 carry(ir.Select(ir.ULessThan(sum.Value(), low.Value()), ir.Constant(1u), ir.Constant(0u)));
IrU32 top(ir.IAdd(high.Value(), carry.Value()));
if (!sign) {
return IrU32(ir.Select(ir.INotEqual(top.Value(), ir.Constant(0u)), ir.Constant(0xffffffffu), sum.Value()));
}
top = IrU32(ir.IAdd(top.Value(), ir.ShiftRightArithmetic(addend.Value(), ir.Constant(31u))));
const IrU1 overflow(ir.INotEqual(top.Value(), ir.ShiftRightArithmetic(sum.Value(), ir.Constant(31u))));
IrValue& saturated = ir.BitwiseXor(ir.ShiftRightArithmetic(top.Value(), ir.Constant(31u)), ir.Constant(0x7fffffffu));
return IrU32(ir.Select(overflow.Value(), saturated, sum.Value()));
}
bool TranslationContext::vMed3I16(const RdnaInstruction& inst) {
const IrU32 first = readU16AsU32(sourceAt(inst, 0u), true);
const IrU32 second = readU16AsU32(sourceAt(inst, 1u), true);
@ -46,11 +73,14 @@ bool TranslationContext::integer16Ternary(const RdnaInstruction& inst, IrOpcode
bool TranslationContext::integer16Mad(const RdnaInstruction& inst, bool sign, bool wide) {
const IrU32 lhs = readU16AsU32(sourceAt(inst, 0u), sign);
const IrU32 rhs = readU16AsU32(sourceAt(inst, 1u), sign);
const IrU32 addend = wide ? readU32(sourceAt(inst, 2u)) : readU16AsU32(sourceAt(inst, 2u), sign);
const IrU32 result(ir.IAdd(ir.IMul(lhs.Value(), rhs.Value()), addend.Value()));
const IrU32 product(ir.IMul(lhs.Value(), rhs.Value()));
if (wide) {
const IrU32 high(sign ? ir.ShiftRightArithmetic(product.Value(), ir.Constant(31u)) : ir.Constant(0u));
const IrU32 result = wideAdd(inst, product, high, readU32(sourceAt(inst, 2u)), sign);
writeOperand(inst.destination, &result.Value());
} else {
const IrU32 addend = readU16AsU32(sourceAt(inst, 2u), sign);
const IrU32 result = saturateU16Result(inst, IrU32(ir.IAdd(product.Value(), addend.Value())), sign);
write16Bits(inst.destination, IrU32(ir.BitwiseAnd(result.Value(), ir.Constant(0xffffu))));
}
return true;
@ -323,7 +353,8 @@ bool TranslationContext::integer24(const RdnaInstruction& inst, bool sign, bool
result = IrU32(ir.Select(ir.INotEqual(top.Value(), expected), saturated, result.Value()));
}
if (addend) {
result = IrU32(ir.IAdd(result.Value(), readU32(sourceAt(inst, 2u)).Value()));
const IrU32 productHigh(inst.destination.clamp ? ir.Emit(sign ? IrOpcode::SMulHi : IrOpcode::UMulHi, IrType::U32, {&lhs.Value(), &rhs.Value()}) : ir.Constant(0u));
result = wideAdd(inst, result, productHigh, readU32(sourceAt(inst, 2u)), sign);
}
writeOperand(inst.destination, &result.Value());
return true;
@ -377,7 +408,7 @@ bool TranslationContext::vSadU32(const RdnaInstruction& inst) {
const IrU32 lo(ir.Emit(IrOpcode::UMin32, IrType::U32, {&lhs.Value(), &rhs.Value()}));
const IrU32 hi(ir.Emit(IrOpcode::UMax32, IrType::U32, {&lhs.Value(), &rhs.Value()}));
const IrU32 addend = readU32(sourceAt(inst, 2u));
const IrU32 result(ir.IAdd(ir.ISub(hi.Value(), lo.Value()), addend.Value()));
const IrU32 result = wideAdd(inst, IrU32(ir.ISub(hi.Value(), lo.Value())), IrU32(ir.Constant(0u)), addend, false);
writeOperand(inst.destination, &result.Value());
return true;
}
@ -405,7 +436,7 @@ bool TranslationContext::subwordSad(const RdnaInstruction& inst, std::uint32_t f
sum = IrU32(ir.ShiftLeftLogical(sum.Value(), ir.Constant(sumShift)));
}
const IrU32 addend = readU32(sourceAt(inst, 2u));
const IrU32 result(ir.IAdd(sum.Value(), addend.Value()));
const IrU32 result = wideAdd(inst, sum, IrU32(ir.Constant(0u)), addend, false);
writeOperand(inst.destination, &result.Value());
return true;
}
@ -423,14 +454,15 @@ bool TranslationContext::vQsadU8(const RdnaInstruction& inst, bool masked, bool
const std::array<IrU32, 2> accumulator = readU32Pair(sourceAt(inst, 2u));
if (wide) {
const std::array<IrU32, 2> accumulatorHigh = readU32Pair(offsetOperand(sourceAt(inst, 2u), 2u));
writeU32Pair(inst.destination, {IrU32(ir.IAdd(accumulator[0].Value(), sad(0u).Value())), IrU32(ir.IAdd(accumulator[1].Value(), sad(8u).Value()))});
const IrU32 zero(ir.Constant(0u));
writeU32Pair(inst.destination, {wideAdd(inst, sad(0u), zero, accumulator[0], false), wideAdd(inst, sad(8u), zero, accumulator[1], false)});
writeU32Pair(offsetOperand(inst.destination, 2u),
{IrU32(ir.IAdd(accumulatorHigh[0].Value(), sad(16u).Value())), IrU32(ir.IAdd(accumulatorHigh[1].Value(), sad(24u).Value()))});
{wideAdd(inst, sad(16u), zero, accumulatorHigh[0], false), wideAdd(inst, sad(24u), zero, accumulatorHigh[1], false)});
return true;
}
const auto packed = [&](const IrU32& addend, std::uint32_t shift) {
const IrU32 low(ir.IAdd(addend.Value(), sad(shift).Value()));
const IrU32 high(ir.IAdd(ir.ShiftRightLogical(addend.Value(), ir.Constant(16u)), sad(shift + 8u).Value()));
const IrU32 low = saturateU16Result(inst, IrU32(ir.IAdd(ir.BitwiseAnd(addend.Value(), ir.Constant(0xffffu)), sad(shift).Value())), false);
const IrU32 high = saturateU16Result(inst, IrU32(ir.IAdd(ir.ShiftRightLogical(addend.Value(), ir.Constant(16u)), sad(shift + 8u).Value())), false);
return IrU32(ir.BitwiseOr(ir.BitwiseAnd(low.Value(), ir.Constant(0xffffu)), ir.ShiftLeftLogical(high.Value(), ir.Constant(16u))));
};
writeU32Pair(inst.destination, {packed(accumulator[0], 0u), packed(accumulator[1], 16u)});