From b6376737bb69d4d2e699c8a07eed52baf904ae28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Thu, 1 Oct 2026 22:17:16 +0200 Subject: [PATCH] feat(shader): implement the missing non-f64 VOPC compares Fifty VOPC opcodes threw in the decoder: cmpx f/o/u/tru for f32, cmpx f/t for i32/u32, the 64-bit integer compares except the seven already present, and the f16 f/o/u/nge/nlg/ngt/nle/nlt/tru compares and their cmpx forms. 64-bit gt/le/ge are built from SLessThan64/ULessThan64 by swapping and negating, as the IR has no other 64-bit orderings. emitFloatOrderedCompare gains the f16 and cmpx forms. Co-Authored-By: Claude --- .../tests/execution/VopcCompare.cpp | 237 ++++++++++++++++++ .../include/RdnaDecoder/RdnaOpcode.hpp | 50 ++++ .../recompiler/RdnaDecoder/src/RdnaOpcode.cpp | 50 ++++ .../RdnaDecoder/src/RdnaVectorOpDecoder.cpp | 101 ++++++++ .../Translation/TranslationContext.hpp | 3 +- .../Translation/src/CompareInstructions.cpp | 15 +- .../Translation/src/VectorInstructions.cpp | 154 +++++++++++- 7 files changed, 603 insertions(+), 7 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/VopcCompare.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/VopcCompare.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/VopcCompare.cpp new file mode 100644 index 000000000..79df28ea5 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/VopcCompare.cpp @@ -0,0 +1,237 @@ +#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 = 8; +constexpr std::uint32_t Results = 4; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array CompareCode{ + 0x34020083, 0x34060082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xe0302008, 0x80000601, + 0xe030200c, 0x80000701, 0xe0302010, 0x80000801, 0xe0302014, 0x80000901, 0xe0302018, 0x80000e01, + 0xe030201c, 0x80000f01, 0x7e140280, 0x7e160280, 0xbf8c3f70, 0xbe9e037e, 0x7e280280, 0x7c200b04, + 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04290114, 0xbe9e037e, 0x7e280280, 0x7c2e0b04, 0x7e280281, + 0xbefe031e, 0xd76f000a, 0x04290314, 0xbe9e037e, 0x7e280280, 0x7c300b04, 0x7e280281, 0xbefe031e, + 0xd76f000a, 0x04290514, 0xbe9e037e, 0x7e280280, 0x7c3e0b04, 0x7e280281, 0xbefe031e, 0xd76f000a, + 0x04290714, 0xbe9e037e, 0x7e280280, 0x7d200b04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04290914, + 0xbe9e037e, 0x7e280280, 0x7d2e0b04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04290b14, 0xbe9e037e, + 0x7e280280, 0x7da00b04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04290d14, 0xbe9e037e, 0x7e280280, + 0x7dae0b04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04290f14, 0x7d401106, 0xd5010014, 0x01a90280, + 0xd76f000a, 0x04291114, 0x7d421106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291314, 0x7d461106, + 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291514, 0x7d481106, 0xd5010014, 0x01a90280, 0xd76f000a, + 0x04291714, 0x7d4a1106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291914, 0x7d4c1106, 0xd5010014, + 0x01a90280, 0xd76f000a, 0x04291b14, 0x7d4e1106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291d14, + 0xbe9e037e, 0x7e280280, 0x7d601106, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04291f14, 0xbe9e037e, + 0x7e280280, 0x7d621106, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292114, 0xbe9e037e, 0x7e280280, + 0x7d641106, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292314, 0xbe9e037e, 0x7e280280, 0x7d661106, + 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292514, 0xbe9e037e, 0x7e280280, 0x7d681106, 0x7e280281, + 0xbefe031e, 0xd76f000a, 0x04292714, 0xbe9e037e, 0x7e280280, 0x7d6c1106, 0x7e280281, 0xbefe031e, + 0xd76f000a, 0x04292914, 0xbe9e037e, 0x7e280280, 0x7d6e1106, 0x7e280281, 0xbefe031e, 0xd76f000a, + 0x04292b14, 0x7dc01106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04292d14, 0x7dc61106, 0xd5010014, + 0x01a90280, 0xd76f000a, 0x04292f14, 0x7dcc1106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04293114, + 0x7dce1106, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04293314, 0xbe9e037e, 0x7e280280, 0x7de01106, + 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293514, 0xbe9e037e, 0x7e280280, 0x7de21106, 0x7e280281, + 0xbefe031e, 0xd76f000a, 0x04293714, 0xbe9e037e, 0x7e280280, 0x7de41106, 0x7e280281, 0xbefe031e, + 0xd76f000a, 0x04293914, 0xbe9e037e, 0x7e280280, 0x7de61106, 0x7e280281, 0xbefe031e, 0xd76f000a, + 0x04293b14, 0xbe9e037e, 0x7e280280, 0x7de81106, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293d14, + 0xbe9e037e, 0x7e280280, 0x7dec1106, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293f14, 0xbe9e037e, + 0x7e280280, 0x7dee1106, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d0114, 0x7d901f0e, 0xd5010014, + 0x01a90280, 0xd76f000b, 0x042d0314, 0x7d9e1f0e, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0514, + 0x7dd01f0e, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0714, 0x7dd21f0e, 0xd5010014, 0x01a90280, + 0xd76f000b, 0x042d0914, 0x7dd41f0e, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0b14, 0x7dd61f0e, + 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0d14, 0x7dd81f0e, 0xd5010014, 0x01a90280, 0xd76f000b, + 0x042d0f14, 0x7ddc1f0e, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d1114, 0x7dde1f0e, 0xd5010014, + 0x01a90280, 0xd76f000b, 0x042d1314, 0xbe9e037e, 0x7e280280, 0x7db01f0e, 0x7e280281, 0xbefe031e, + 0xd76f000b, 0x042d1514, 0xbe9e037e, 0x7e280280, 0x7dba1f0e, 0x7e280281, 0xbefe031e, 0xd76f000b, + 0x042d1714, 0xbe9e037e, 0x7e280280, 0x7dbe1f0e, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d1914, + 0xbe9e037e, 0x7e280280, 0x7df01f0e, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d1b14, 0xbe9e037e, + 0x7e280280, 0x7df21f0e, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d1d14, 0xbe9e037e, 0x7e280280, + 0x7df41f0e, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d1f14, 0xbe9e037e, 0x7e280280, 0x7df81f0e, + 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d2114, 0xbe9e037e, 0x7e280280, 0x7dfe1f0e, 0x7e280281, + 0xbefe031e, 0xd76f000b, 0x042d2314, 0xd4e9026a, 0x00021f0e, 0xd5010014, 0x01a90280, 0xd76f000b, + 0x042d2514, 0xd4a3006a, 0x00020d08, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d2714, 0xbe9e037e, + 0x7e280280, 0xd4f6007e, 0x00020d08, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d2914, 0xbe9e037e, + 0x7e280280, 0xd417017e, 0x00020b04, 0x7e280281, 0xbefe031e, 0xd76f000b, 0x042d2b14, 0xe0702000, + 0x80010a03, 0xe0702004, 0x80010b03, 0xbf810000, +}; + +constexpr std::array FloatEdges{ + 0x7fc00000u, 0x3f800000u, 0x80000000u, 0x00000000u, 0x7f800000u, 0xff800000u, + 0xbf800000u, 0x3f800000u, 0x00000001u, 0x40490fdbu, 0xffc00001u, 0xc0000000u, +}; + +constexpr std::array HalfEdges{ + 0x7e00u, 0x3c00u, 0x8000u, 0x0000u, 0x7c00u, 0xfc00u, 0xbc00u, 0x3c00u, 0x0001u, 0x4248u, 0xfe01u, 0xc000u, +}; + +constexpr std::array IntegerEdges{ + 0u, ~0ull, 0x8000000000000000ull, 0x7fffffffffffffffull, 1u, 0x00000001ffffffffull, 0x0000000100000000ull, 0xffffffff00000000ull, +}; + +float HalfValue(std::uint32_t bits) { + const auto exponent = (bits >> 10u) & 0x1fu; + const auto fraction = bits & 0x3ffu; + float magnitude = 0.0f; + if (exponent == 0x1fu) { + magnitude = fraction != 0u ? std::numeric_limits::quiet_NaN() : std::numeric_limits::infinity(); + } else { + magnitude = exponent == 0u ? std::ldexp(static_cast(fraction), -24) : std::ldexp(static_cast(fraction | 0x400u), static_cast(exponent) - 25); + } + return (bits & 0x8000u) != 0u ? -magnitude : magnitude; +} + +void FillInput() { + std::uint64_t state = 0x853c49e6748fea9bull; + const auto next = [&] { + state = state * 6364136223846793005ull + 1442695040888963407ull; + return static_cast(state >> 33u); + }; + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + auto* words = &Input[tid * Inputs]; + words[0] = FloatEdges[tid % FloatEdges.size()]; + words[1] = FloatEdges[(tid / FloatEdges.size() + tid) % FloatEdges.size()]; + const std::uint64_t a = tid < 16u ? IntegerEdges[tid % IntegerEdges.size()] : (static_cast(next()) << 32u) | next(); + const std::uint64_t b = tid < 16u ? IntegerEdges[(tid / IntegerEdges.size() + tid * 3u) % IntegerEdges.size()] : (tid % 5u == 0u ? a : (static_cast(next()) << 32u) | next()); + words[2] = static_cast(a); + words[3] = static_cast(a >> 32u); + words[4] = static_cast(b); + words[5] = static_cast(b >> 32u); + words[6] = 0xabcd0000u | HalfEdges[tid % HalfEdges.size()]; + words[7] = 0x12340000u | HalfEdges[(tid / HalfEdges.size() + tid * 5u) % HalfEdges.size()]; + } +} + +std::array BufferDescriptor(const void* data, std::uint32_t count) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu) | (4u << 16u), count, 0x01016facu}; +} + +void Run(AgcDriver::VulkanDevice& device) { + Output.fill(0xdeadbeefu); + std::vector userData(8, 0u); + const auto input = BufferDescriptor(Input.data(), static_cast(Input.size())); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size())); + std::copy(input.begin(), input.end(), userData.begin()); + std::copy(output.begin(), output.end(), userData.begin() + 4); + const std::span code(CompareCode); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0, {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 auto* in = &Input[tid * Inputs]; + const float fa = std::bit_cast(in[0]); + const float fb = std::bit_cast(in[1]); + const std::uint64_t ua = in[2] | (static_cast(in[3]) << 32u); + const std::uint64_t ub = in[4] | (static_cast(in[5]) << 32u); + const auto sa = static_cast(ua); + const auto sb = static_cast(ub); + const float ha = HalfValue(in[6] & 0xffffu); + const float hb = HalfValue(in[7] & 0xffffu); + const std::array expected{ + false, + !std::isnan(fa) && !std::isnan(fb), + std::isnan(fa) || std::isnan(fb), + true, + false, + true, + false, + true, + false, + sa < sb, + sa <= sb, + sa > sb, + sa != sb, + sa >= sb, + true, + false, + sa < sb, + sa == sb, + sa <= sb, + sa > sb, + sa >= sb, + true, + false, + ua <= ub, + ua >= ub, + true, + false, + ua < ub, + ua == ub, + ua <= ub, + ua > ub, + ua >= ub, + true, + false, + !std::isnan(ha) && !std::isnan(hb), + std::isnan(ha) || std::isnan(hb), + !(ha >= hb), + !(ha < hb || ha > hb), + !(ha > hb), + !(ha <= hb), + !(ha < hb), + true, + false, + ha < hb || ha > hb, + !std::isnan(ha) && !std::isnan(hb), + std::isnan(ha) || std::isnan(hb), + !(ha >= hb), + !(ha < hb || ha > hb), + !(ha <= hb), + true, + !(ha >= std::fabs(hb)), + sb <= sa, + ub >= ua, + !std::isnan(fa) && !std::isnan(fb), + }; + for (std::uint32_t k = 0; k < expected.size(); ++k) { + const bool actual = ((Output[tid * Results + k / 32u] >> (k % 32u)) & 1u) != 0u; + Require(actual == expected[k], "vopc compares: thread " + std::to_string(tid) + " compare " + std::to_string(k) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected[k])); + } + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + FillInput(); + Run(*device); + Check(); + std::puts("vopc compare 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 52440fadb..dff48c4a9 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp @@ -453,6 +453,56 @@ enum class RdnaOpcode : std::uint16_t { VCmpNeU64, VCmpxNeI64, VCmpxNeU64, + VCmpxFF32, + VCmpxOF32, + VCmpxUF32, + VCmpxTruF32, + VCmpxFI32, + VCmpxTI32, + VCmpxFU32, + VCmpxTU32, + VCmpFI64, + VCmpLtI64, + VCmpLeI64, + VCmpGtI64, + VCmpNeI64, + VCmpGeI64, + VCmpTI64, + VCmpxFI64, + VCmpxLtI64, + VCmpxEqI64, + VCmpxLeI64, + VCmpxGtI64, + VCmpxGeI64, + VCmpxTI64, + VCmpFU64, + VCmpLeU64, + VCmpGeU64, + VCmpTU64, + VCmpxFU64, + VCmpxLtU64, + VCmpxEqU64, + VCmpxLeU64, + VCmpxGtU64, + VCmpxGeU64, + VCmpxTU64, + VCmpFF16, + VCmpOF16, + VCmpUF16, + VCmpNgeF16, + VCmpNlgF16, + VCmpNgtF16, + VCmpNleF16, + VCmpNltF16, + VCmpTruF16, + VCmpxFF16, + VCmpxLgF16, + VCmpxOF16, + VCmpxUF16, + VCmpxNgeF16, + VCmpxNlgF16, + VCmpxNleF16, + VCmpxTruF16, VCmpxLtU32, VCmpxEqU32, VCmpxLeU32, diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp index 032c76070..ee08fecdc 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp @@ -427,6 +427,56 @@ bool IsVectorAluOpcode(RdnaOpcode opcode) { case RdnaOpcode::VCmpGtU64: case RdnaOpcode::VCmpNeU64: case RdnaOpcode::VCmpxNeI64: + case RdnaOpcode::VCmpxFF32: + case RdnaOpcode::VCmpxOF32: + case RdnaOpcode::VCmpxUF32: + case RdnaOpcode::VCmpxTruF32: + case RdnaOpcode::VCmpxFI32: + case RdnaOpcode::VCmpxTI32: + case RdnaOpcode::VCmpxFU32: + case RdnaOpcode::VCmpxTU32: + case RdnaOpcode::VCmpFI64: + case RdnaOpcode::VCmpLtI64: + case RdnaOpcode::VCmpLeI64: + case RdnaOpcode::VCmpGtI64: + case RdnaOpcode::VCmpNeI64: + case RdnaOpcode::VCmpGeI64: + case RdnaOpcode::VCmpTI64: + case RdnaOpcode::VCmpxFI64: + case RdnaOpcode::VCmpxLtI64: + case RdnaOpcode::VCmpxEqI64: + case RdnaOpcode::VCmpxLeI64: + case RdnaOpcode::VCmpxGtI64: + case RdnaOpcode::VCmpxGeI64: + case RdnaOpcode::VCmpxTI64: + case RdnaOpcode::VCmpFU64: + case RdnaOpcode::VCmpLeU64: + case RdnaOpcode::VCmpGeU64: + case RdnaOpcode::VCmpTU64: + case RdnaOpcode::VCmpxFU64: + case RdnaOpcode::VCmpxLtU64: + case RdnaOpcode::VCmpxEqU64: + case RdnaOpcode::VCmpxLeU64: + case RdnaOpcode::VCmpxGtU64: + case RdnaOpcode::VCmpxGeU64: + case RdnaOpcode::VCmpxTU64: + case RdnaOpcode::VCmpFF16: + case RdnaOpcode::VCmpOF16: + case RdnaOpcode::VCmpUF16: + case RdnaOpcode::VCmpNgeF16: + case RdnaOpcode::VCmpNlgF16: + case RdnaOpcode::VCmpNgtF16: + case RdnaOpcode::VCmpNleF16: + case RdnaOpcode::VCmpNltF16: + case RdnaOpcode::VCmpTruF16: + case RdnaOpcode::VCmpxFF16: + case RdnaOpcode::VCmpxLgF16: + case RdnaOpcode::VCmpxOF16: + case RdnaOpcode::VCmpxUF16: + case RdnaOpcode::VCmpxNgeF16: + case RdnaOpcode::VCmpxNlgF16: + case RdnaOpcode::VCmpxNleF16: + case RdnaOpcode::VCmpxTruF16: case RdnaOpcode::VCmpxNeU64: case RdnaOpcode::VCmpxLtU32: case RdnaOpcode::VCmpxEqU32: diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp index a4d6ec2bc..c59e3e7fe 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp @@ -314,6 +314,56 @@ constexpr VopcOpcodeInfo vopcOpcodes[] = { {0xfbu, RdnaOpcode::VCmpxNgtF16}, {0xfdu, RdnaOpcode::VCmpxNeqF16}, {0xfeu, RdnaOpcode::VCmpxNltF16}, + {0x10u, RdnaOpcode::VCmpxFF32}, + {0x17u, RdnaOpcode::VCmpxOF32}, + {0x18u, RdnaOpcode::VCmpxUF32}, + {0x1fu, RdnaOpcode::VCmpxTruF32}, + {0x90u, RdnaOpcode::VCmpxFI32}, + {0x97u, RdnaOpcode::VCmpxTI32}, + {0xd0u, RdnaOpcode::VCmpxFU32}, + {0xd7u, RdnaOpcode::VCmpxTU32}, + {0xa0u, RdnaOpcode::VCmpFI64, false}, + {0xa1u, RdnaOpcode::VCmpLtI64, false}, + {0xa3u, RdnaOpcode::VCmpLeI64, false}, + {0xa4u, RdnaOpcode::VCmpGtI64, false}, + {0xa5u, RdnaOpcode::VCmpNeI64, false}, + {0xa6u, RdnaOpcode::VCmpGeI64, false}, + {0xa7u, RdnaOpcode::VCmpTI64, false}, + {0xb0u, RdnaOpcode::VCmpxFI64, false}, + {0xb1u, RdnaOpcode::VCmpxLtI64, false}, + {0xb2u, RdnaOpcode::VCmpxEqI64, false}, + {0xb3u, RdnaOpcode::VCmpxLeI64, false}, + {0xb4u, RdnaOpcode::VCmpxGtI64, false}, + {0xb6u, RdnaOpcode::VCmpxGeI64, false}, + {0xb7u, RdnaOpcode::VCmpxTI64, false}, + {0xe0u, RdnaOpcode::VCmpFU64, false}, + {0xe3u, RdnaOpcode::VCmpLeU64, false}, + {0xe6u, RdnaOpcode::VCmpGeU64, false}, + {0xe7u, RdnaOpcode::VCmpTU64, false}, + {0xf0u, RdnaOpcode::VCmpxFU64, false}, + {0xf1u, RdnaOpcode::VCmpxLtU64, false}, + {0xf2u, RdnaOpcode::VCmpxEqU64, false}, + {0xf3u, RdnaOpcode::VCmpxLeU64, false}, + {0xf4u, RdnaOpcode::VCmpxGtU64, false}, + {0xf6u, RdnaOpcode::VCmpxGeU64, false}, + {0xf7u, RdnaOpcode::VCmpxTU64, false}, + {0xc8u, RdnaOpcode::VCmpFF16}, + {0xcfu, RdnaOpcode::VCmpOF16}, + {0xe8u, RdnaOpcode::VCmpUF16}, + {0xe9u, RdnaOpcode::VCmpNgeF16}, + {0xeau, RdnaOpcode::VCmpNlgF16}, + {0xebu, RdnaOpcode::VCmpNgtF16}, + {0xecu, RdnaOpcode::VCmpNleF16}, + {0xeeu, RdnaOpcode::VCmpNltF16}, + {0xefu, RdnaOpcode::VCmpTruF16}, + {0xd8u, RdnaOpcode::VCmpxFF16}, + {0xddu, RdnaOpcode::VCmpxLgF16}, + {0xdfu, RdnaOpcode::VCmpxOF16}, + {0xf8u, RdnaOpcode::VCmpxUF16}, + {0xf9u, RdnaOpcode::VCmpxNgeF16}, + {0xfau, RdnaOpcode::VCmpxNlgF16}, + {0xfcu, RdnaOpcode::VCmpxNleF16}, + {0xffu, RdnaOpcode::VCmpxTruF16}, }; constexpr VectorOpcodeInfo vop3Opcodes[] = { @@ -664,6 +714,27 @@ bool isVopcFloatCompareOpcode(RdnaOpcode opcode) { case RdnaOpcode::VCmpxNgtF16: case RdnaOpcode::VCmpxNeqF16: case RdnaOpcode::VCmpxNltF16: + case RdnaOpcode::VCmpxFF32: + case RdnaOpcode::VCmpxOF32: + case RdnaOpcode::VCmpxUF32: + case RdnaOpcode::VCmpxTruF32: + case RdnaOpcode::VCmpFF16: + case RdnaOpcode::VCmpOF16: + case RdnaOpcode::VCmpUF16: + case RdnaOpcode::VCmpNgeF16: + case RdnaOpcode::VCmpNlgF16: + case RdnaOpcode::VCmpNgtF16: + case RdnaOpcode::VCmpNleF16: + case RdnaOpcode::VCmpNltF16: + case RdnaOpcode::VCmpTruF16: + case RdnaOpcode::VCmpxFF16: + case RdnaOpcode::VCmpxLgF16: + case RdnaOpcode::VCmpxOF16: + case RdnaOpcode::VCmpxUF16: + case RdnaOpcode::VCmpxNgeF16: + case RdnaOpcode::VCmpxNlgF16: + case RdnaOpcode::VCmpxNleF16: + case RdnaOpcode::VCmpxTruF16: case RdnaOpcode::VCmpClassF32: case RdnaOpcode::VCmpxClassF32: return true; default: return false; @@ -1141,6 +1212,36 @@ bool isVopcCompareExec(RdnaOpcode opcode) { case RdnaOpcode::VCmpxGeU32: case RdnaOpcode::VCmpxNeI64: case RdnaOpcode::VCmpxNeU64: + case RdnaOpcode::VCmpxFF32: + case RdnaOpcode::VCmpxOF32: + case RdnaOpcode::VCmpxUF32: + case RdnaOpcode::VCmpxTruF32: + case RdnaOpcode::VCmpxFI32: + case RdnaOpcode::VCmpxTI32: + case RdnaOpcode::VCmpxFU32: + case RdnaOpcode::VCmpxTU32: + case RdnaOpcode::VCmpxFI64: + case RdnaOpcode::VCmpxLtI64: + case RdnaOpcode::VCmpxEqI64: + case RdnaOpcode::VCmpxLeI64: + case RdnaOpcode::VCmpxGtI64: + case RdnaOpcode::VCmpxGeI64: + case RdnaOpcode::VCmpxTI64: + case RdnaOpcode::VCmpxFU64: + case RdnaOpcode::VCmpxLtU64: + case RdnaOpcode::VCmpxEqU64: + case RdnaOpcode::VCmpxLeU64: + case RdnaOpcode::VCmpxGtU64: + case RdnaOpcode::VCmpxGeU64: + case RdnaOpcode::VCmpxTU64: + case RdnaOpcode::VCmpxFF16: + case RdnaOpcode::VCmpxLgF16: + case RdnaOpcode::VCmpxOF16: + case RdnaOpcode::VCmpxUF16: + case RdnaOpcode::VCmpxNgeF16: + case RdnaOpcode::VCmpxNlgF16: + case RdnaOpcode::VCmpxNleF16: + case RdnaOpcode::VCmpxTruF16: case RdnaOpcode::VCmpxLtI16: case RdnaOpcode::VCmpxEqI16: case RdnaOpcode::VCmpxLeI16: diff --git a/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp b/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp index f9b643bf7..05b5cbdc8 100644 --- a/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp +++ b/core/shader/recompiler/Translation/include/Translation/TranslationContext.hpp @@ -117,7 +117,8 @@ private: void emitIntegerCompare(const RdnaInstruction& inst, IrOpcode opcode, IrType type, bool scalar, bool cmpx); void emitInteger16Compare(const RdnaInstruction& inst, IrOpcode opcode, bool signedValue, bool cmpx); void emitFloatCompare(const RdnaInstruction& inst, IrOpcode opcode, bool half, bool cmpx); - void emitFloatOrderedCompare(const RdnaInstruction& inst, bool ordered); + void emitInteger64Order(const RdnaInstruction& inst, bool signedValue, bool swap, bool negate, bool cmpx); + void emitFloatOrderedCompare(const RdnaInstruction& inst, bool ordered, bool half, bool cmpx); void emitFloatClassCompare(const RdnaInstruction& inst, bool cmpx); void vCvtF32Ubyte(const RdnaInstruction& inst, std::uint32_t byteIndex); void vCvtF32U32(const RdnaInstruction& inst); diff --git a/core/shader/recompiler/Translation/src/CompareInstructions.cpp b/core/shader/recompiler/Translation/src/CompareInstructions.cpp index 3446c6d07..5613f91ef 100644 --- a/core/shader/recompiler/Translation/src/CompareInstructions.cpp +++ b/core/shader/recompiler/Translation/src/CompareInstructions.cpp @@ -54,14 +54,21 @@ void TranslationContext::emitFloatCompare(const RdnaInstruction& inst, IrOpcode emitCompareResult(inst, IrU1(ir.Emit(opcode, IrType::U1, {lhs, rhs})), false, cmpx); } -void TranslationContext::emitFloatOrderedCompare(const RdnaInstruction& inst, bool ordered) { - IrValue* lhs = readOperand(sourceAt(inst, 0u), IrType::F32); - IrValue* rhs = readOperand(sourceAt(inst, 1u), IrType::F32); +void TranslationContext::emitInteger64Order(const RdnaInstruction& inst, bool signedValue, bool swap, bool negate, bool cmpx) { + IrValue* lhs = readOperand(sourceAt(inst, swap ? 1u : 0u), IrType::U64); + IrValue* rhs = readOperand(sourceAt(inst, swap ? 0u : 1u), IrType::U64); + IrValue& less = ir.Emit(signedValue ? IrOpcode::SLessThan64 : IrOpcode::ULessThan64, IrType::U1, {lhs, rhs}); + emitCompareResult(inst, IrU1(negate ? ir.LogicalNot(less) : less), false, cmpx); +} + +void TranslationContext::emitFloatOrderedCompare(const RdnaInstruction& inst, bool ordered, bool half, bool cmpx) { + IrValue* lhs = half ? &readF16AsF32(sourceAt(inst, 0u)).Value() : readOperand(sourceAt(inst, 0u), IrType::F32); + IrValue* rhs = half ? &readF16AsF32(sourceAt(inst, 1u)).Value() : readOperand(sourceAt(inst, 1u), IrType::F32); IrValue& lhsNan = ir.Emit(IrOpcode::FPIsNan32, IrType::U1, {lhs}); IrValue& rhsNan = ir.Emit(IrOpcode::FPIsNan32, IrType::U1, {rhs}); IrValue& unordered = ir.LogicalOr(lhsNan, rhsNan); IrValue& result = ordered ? ir.LogicalNot(unordered) : unordered; - emitCompareResult(inst, IrU1(result), false, false); + emitCompareResult(inst, IrU1(result), false, cmpx); } void TranslationContext::emitFloatClassCompare(const RdnaInstruction& inst, bool cmpx) { diff --git a/core/shader/recompiler/Translation/src/VectorInstructions.cpp b/core/shader/recompiler/Translation/src/VectorInstructions.cpp index d1351b06b..f62db3f89 100644 --- a/core/shader/recompiler/Translation/src/VectorInstructions.cpp +++ b/core/shader/recompiler/Translation/src/VectorInstructions.cpp @@ -145,6 +145,156 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) { case RdnaOpcode::VCmpxNeU64: emitIntegerCompare(inst, IrOpcode::INotEqual64, IrType::U64, false, true); return true; + case RdnaOpcode::VCmpxFF32: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxOF32: + emitFloatOrderedCompare(inst, true, false, true); + return true; + case RdnaOpcode::VCmpxUF32: + emitFloatOrderedCompare(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxTruF32: + emitCompareConstant(inst, true, false, true); + return true; + case RdnaOpcode::VCmpxFI32: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxTI32: + emitCompareConstant(inst, true, false, true); + return true; + case RdnaOpcode::VCmpxFU32: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxTU32: + emitCompareConstant(inst, true, false, true); + return true; + case RdnaOpcode::VCmpFI64: + emitCompareConstant(inst, false, false, false); + return true; + case RdnaOpcode::VCmpLtI64: + emitInteger64Order(inst, true, false, false, false); + return true; + case RdnaOpcode::VCmpLeI64: + emitInteger64Order(inst, true, true, true, false); + return true; + case RdnaOpcode::VCmpGtI64: + emitInteger64Order(inst, true, true, false, false); + return true; + case RdnaOpcode::VCmpNeI64: + emitIntegerCompare(inst, IrOpcode::INotEqual64, IrType::U64, false, false); + return true; + case RdnaOpcode::VCmpGeI64: + emitInteger64Order(inst, true, false, true, false); + return true; + case RdnaOpcode::VCmpTI64: + emitCompareConstant(inst, true, false, false); + return true; + case RdnaOpcode::VCmpxFI64: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxLtI64: + emitInteger64Order(inst, true, false, false, true); + return true; + case RdnaOpcode::VCmpxEqI64: + emitIntegerCompare(inst, IrOpcode::IEqual64, IrType::U64, false, true); + return true; + case RdnaOpcode::VCmpxLeI64: + emitInteger64Order(inst, true, true, true, true); + return true; + case RdnaOpcode::VCmpxGtI64: + emitInteger64Order(inst, true, true, false, true); + return true; + case RdnaOpcode::VCmpxGeI64: + emitInteger64Order(inst, true, false, true, true); + return true; + case RdnaOpcode::VCmpxTI64: + emitCompareConstant(inst, true, false, true); + return true; + case RdnaOpcode::VCmpFU64: + emitCompareConstant(inst, false, false, false); + return true; + case RdnaOpcode::VCmpLeU64: + emitInteger64Order(inst, false, true, true, false); + return true; + case RdnaOpcode::VCmpGeU64: + emitInteger64Order(inst, false, false, true, false); + return true; + case RdnaOpcode::VCmpTU64: + emitCompareConstant(inst, true, false, false); + return true; + case RdnaOpcode::VCmpxFU64: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxLtU64: + emitInteger64Order(inst, false, false, false, true); + return true; + case RdnaOpcode::VCmpxEqU64: + emitIntegerCompare(inst, IrOpcode::IEqual64, IrType::U64, false, true); + return true; + case RdnaOpcode::VCmpxLeU64: + emitInteger64Order(inst, false, true, true, true); + return true; + case RdnaOpcode::VCmpxGtU64: + emitInteger64Order(inst, false, true, false, true); + return true; + case RdnaOpcode::VCmpxGeU64: + emitInteger64Order(inst, false, false, true, true); + return true; + case RdnaOpcode::VCmpxTU64: + emitCompareConstant(inst, true, false, true); + return true; + case RdnaOpcode::VCmpFF16: + emitCompareConstant(inst, false, false, false); + return true; + case RdnaOpcode::VCmpOF16: + emitFloatOrderedCompare(inst, true, true, false); + return true; + case RdnaOpcode::VCmpUF16: + emitFloatOrderedCompare(inst, false, true, false); + return true; + case RdnaOpcode::VCmpNgeF16: + emitFloatCompare(inst, IrOpcode::FPUnordLessThan32, true, false); + return true; + case RdnaOpcode::VCmpNlgF16: + emitFloatCompare(inst, IrOpcode::FPUnordEqual32, true, false); + return true; + case RdnaOpcode::VCmpNgtF16: + emitFloatCompare(inst, IrOpcode::FPUnordLessThanEqual32, true, false); + return true; + case RdnaOpcode::VCmpNleF16: + emitFloatCompare(inst, IrOpcode::FPUnordGreaterThan32, true, false); + return true; + case RdnaOpcode::VCmpNltF16: + emitFloatCompare(inst, IrOpcode::FPUnordGreaterThanEqual32, true, false); + return true; + case RdnaOpcode::VCmpTruF16: + emitCompareConstant(inst, true, false, false); + return true; + case RdnaOpcode::VCmpxFF16: + emitCompareConstant(inst, false, false, true); + return true; + case RdnaOpcode::VCmpxLgF16: + emitFloatCompare(inst, IrOpcode::FPOrdNotEqual32, true, true); + return true; + case RdnaOpcode::VCmpxOF16: + emitFloatOrderedCompare(inst, true, true, true); + return true; + case RdnaOpcode::VCmpxUF16: + emitFloatOrderedCompare(inst, false, true, true); + return true; + case RdnaOpcode::VCmpxNgeF16: + emitFloatCompare(inst, IrOpcode::FPUnordLessThan32, true, true); + return true; + case RdnaOpcode::VCmpxNlgF16: + emitFloatCompare(inst, IrOpcode::FPUnordEqual32, true, true); + return true; + case RdnaOpcode::VCmpxNleF16: + emitFloatCompare(inst, IrOpcode::FPUnordGreaterThan32, true, true); + return true; + case RdnaOpcode::VCmpxTruF16: + emitCompareConstant(inst, true, false, true); + return true; case RdnaOpcode::VCmpEqU16: emitInteger16Compare(inst, IrOpcode::IEqual32, false, false); return true; @@ -335,10 +485,10 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) { emitFloatCompare(inst, IrOpcode::FPUnordGreaterThanEqual32, true, true); return true; case RdnaOpcode::VCmpOF32: - emitFloatOrderedCompare(inst, true); + emitFloatOrderedCompare(inst, true, false, false); return true; case RdnaOpcode::VCmpUF32: - emitFloatOrderedCompare(inst, false); + emitFloatOrderedCompare(inst, false, false, false); return true; case RdnaOpcode::VCmpClassF32: emitFloatClassCompare(inst, false);