Merge pull request #365 from DotDebian/feat/vopc-f64-compares
feat(shader): implement the VOPC f64 compares
This commit is contained in:
commit
b4b261a806
|
|
@ -0,0 +1,217 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
|
||||
#include "Recompiler.hpp"
|
||||
#include "VulkanTestDevice.hpp"
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
constexpr std::uint32_t Compares = 41;
|
||||
constexpr std::uint32_t RandomRounds = 4;
|
||||
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, 271> CompareCode{
|
||||
0x34020083, 0x34060082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xe0302008, 0x80000601,
|
||||
0xe030200c, 0x80000701, 0xe0302010, 0x80000801, 0xe0302014, 0x80000901, 0xe0302018, 0x80000e01,
|
||||
0xe030201c, 0x80000f01, 0x7e140280, 0x7e160280, 0xbf8c3f70, 0x7c400d04, 0xd5010014, 0x01a90280,
|
||||
0xd76f000a, 0x04290114, 0x7c420d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04290314, 0x7c440d04,
|
||||
0xd5010014, 0x01a90280, 0xd76f000a, 0x04290514, 0x7c460d04, 0xd5010014, 0x01a90280, 0xd76f000a,
|
||||
0x04290714, 0x7c480d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04290914, 0x7c4a0d04, 0xd5010014,
|
||||
0x01a90280, 0xd76f000a, 0x04290b14, 0x7c4c0d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04290d14,
|
||||
0x7c4e0d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04290f14, 0x7c500d04, 0xd5010014, 0x01a90280,
|
||||
0xd76f000a, 0x04291114, 0x7c520d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291314, 0x7c540d04,
|
||||
0xd5010014, 0x01a90280, 0xd76f000a, 0x04291514, 0x7c560d04, 0xd5010014, 0x01a90280, 0xd76f000a,
|
||||
0x04291714, 0x7c580d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291914, 0x7c5a0d04, 0xd5010014,
|
||||
0x01a90280, 0xd76f000a, 0x04291b14, 0x7c5c0d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291d14,
|
||||
0x7c5e0d04, 0xd5010014, 0x01a90280, 0xd76f000a, 0x04291f14, 0x7d501104, 0xd5010014, 0x01a90280,
|
||||
0xd76f000a, 0x04292114, 0xbe9e037e, 0x7e280280, 0x7c600d04, 0x7e280281, 0xbefe031e, 0xd76f000a,
|
||||
0x04292314, 0xbe9e037e, 0x7e280280, 0x7c620d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292514,
|
||||
0xbe9e037e, 0x7e280280, 0x7c640d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292714, 0xbe9e037e,
|
||||
0x7e280280, 0x7c660d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292914, 0xbe9e037e, 0x7e280280,
|
||||
0x7c680d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292b14, 0xbe9e037e, 0x7e280280, 0x7c6a0d04,
|
||||
0x7e280281, 0xbefe031e, 0xd76f000a, 0x04292d14, 0xbe9e037e, 0x7e280280, 0x7c6c0d04, 0x7e280281,
|
||||
0xbefe031e, 0xd76f000a, 0x04292f14, 0xbe9e037e, 0x7e280280, 0x7c6e0d04, 0x7e280281, 0xbefe031e,
|
||||
0xd76f000a, 0x04293114, 0xbe9e037e, 0x7e280280, 0x7c700d04, 0x7e280281, 0xbefe031e, 0xd76f000a,
|
||||
0x04293314, 0xbe9e037e, 0x7e280280, 0x7c720d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293514,
|
||||
0xbe9e037e, 0x7e280280, 0x7c740d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293714, 0xbe9e037e,
|
||||
0x7e280280, 0x7c760d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293914, 0xbe9e037e, 0x7e280280,
|
||||
0x7c780d04, 0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293b14, 0xbe9e037e, 0x7e280280, 0x7c7a0d04,
|
||||
0x7e280281, 0xbefe031e, 0xd76f000a, 0x04293d14, 0xbe9e037e, 0x7e280280, 0x7c7c0d04, 0x7e280281,
|
||||
0xbefe031e, 0xd76f000a, 0x04293f14, 0xbe9e037e, 0x7e280280, 0x7c7e0d04, 0x7e280281, 0xbefe031e,
|
||||
0xd76f000b, 0x042d0114, 0xbe9e037e, 0x7e280280, 0x7d701104, 0x7e280281, 0xbefe031e, 0xd76f000b,
|
||||
0x042d0314, 0x7c4208ff, 0x40080000, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0514, 0x7c4c08f2,
|
||||
0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0714, 0x7c4408f8, 0xd5010014, 0x01a90280, 0xd76f000b,
|
||||
0x042d0914, 0x7c420881, 0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0b14, 0xd421016a, 0x00020d04,
|
||||
0xd5010014, 0x01a90280, 0xd76f000b, 0x042d0d14, 0xd4a8016a, 0x00021104, 0xd5010014, 0x01a90280,
|
||||
0xd76f000b, 0x042d0f14, 0xbe9e037e, 0x7e280280, 0xd43b037e, 0x00020d04, 0x7e280281, 0xbefe031e,
|
||||
0xd76f000b, 0x042d1114, 0xe0702000, 0x80010a03, 0xe0702004, 0x80010b03, 0xbf810000,
|
||||
};
|
||||
|
||||
constexpr std::array<std::uint64_t, 20> Edges{
|
||||
0x7ff8000000000000ull, 0x7ff0000000000001ull, 0xfff8000000000001ull, 0x7ff0000100000000ull, 0x7ff0000000000000ull,
|
||||
0xfff0000000000000ull, 0x0000000000000000ull, 0x8000000000000000ull, 0x0000000000000001ull, 0x0000000000000002ull,
|
||||
0x800fffffffffffffull, 0x0010000000000000ull, 0x3ff0000000000000ull, 0x3ff0000000000001ull, 0xbff0000000000000ull,
|
||||
0x4008000000000000ull, 0xc008000000000000ull, 0x3fc45f306dc9c882ull, 0x3fc45f3060000000ull, 0x7fefffffffffffffull,
|
||||
};
|
||||
|
||||
constexpr std::array<std::uint32_t, 16> Masks{
|
||||
0x001u, 0x002u, 0x004u, 0x008u, 0x010u, 0x020u, 0x040u, 0x080u, 0x100u, 0x200u, 0x3ffu, 0x000u, 0x155u, 0x2aau, 0xfffffc00u, 0x207u,
|
||||
};
|
||||
|
||||
constexpr std::uint32_t EdgeRounds = (Edges.size() * Edges.size() + Threads - 1u) / Threads;
|
||||
|
||||
void FillInput(std::uint32_t round) {
|
||||
std::uint64_t state = 0x853c49e6748fea9bull + round;
|
||||
const auto next = [&] {
|
||||
state = state * 6364136223846793005ull + 1442695040888963407ull;
|
||||
const auto high = static_cast<std::uint32_t>(state >> 32u);
|
||||
state = state * 6364136223846793005ull + 1442695040888963407ull;
|
||||
return (static_cast<std::uint64_t>(high) << 32u) | static_cast<std::uint32_t>(state >> 32u);
|
||||
};
|
||||
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
|
||||
std::uint64_t a = 0u;
|
||||
std::uint64_t b = 0u;
|
||||
std::uint32_t mask = 0u;
|
||||
if (round < EdgeRounds) {
|
||||
const std::uint32_t pair = round * Threads + tid;
|
||||
const std::uint32_t first = pair % Edges.size();
|
||||
const std::uint32_t second = (pair / Edges.size()) % Edges.size();
|
||||
a = Edges[first];
|
||||
b = Edges[second];
|
||||
mask = Masks[(first + second) % Masks.size()];
|
||||
} else {
|
||||
a = next();
|
||||
b = tid % 5u == 0u ? a : (tid % 7u == 0u ? a ^ 0x8000000000000000ull : next());
|
||||
mask = static_cast<std::uint32_t>(next());
|
||||
}
|
||||
auto* words = &Input[tid * Inputs];
|
||||
words[0] = static_cast<std::uint32_t>(a);
|
||||
words[1] = static_cast<std::uint32_t>(a >> 32u);
|
||||
words[2] = static_cast<std::uint32_t>(b);
|
||||
words[3] = static_cast<std::uint32_t>(b >> 32u);
|
||||
words[4] = mask;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::uint32_t, 4> BufferDescriptor(const void* data, std::uint32_t count) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(data);
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (4u << 16u), count, 0x01016facu};
|
||||
}
|
||||
|
||||
void Run(AgcDriver::VulkanDevice& device) {
|
||||
Output.fill(0xdeadbeefu);
|
||||
std::vector<std::uint32_t> userData(8, 0u);
|
||||
const auto input = BufferDescriptor(Input.data(), static_cast<std::uint32_t>(Input.size()));
|
||||
const auto output = BufferDescriptor(Output.data(), static_cast<std::uint32_t>(Output.size()));
|
||||
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(CompareCode);
|
||||
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}, 0, {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();
|
||||
}
|
||||
|
||||
std::uint32_t ClassBit(std::uint64_t bits) {
|
||||
const bool negative = (bits >> 63u) != 0u;
|
||||
switch (std::fpclassify(std::bit_cast<double>(bits))) {
|
||||
case FP_NAN: return (bits & 0x0008000000000000ull) != 0u ? 1u : 0u;
|
||||
case FP_INFINITE: return negative ? 2u : 9u;
|
||||
case FP_NORMAL: return negative ? 3u : 8u;
|
||||
case FP_SUBNORMAL: return negative ? 4u : 7u;
|
||||
default: return negative ? 5u : 6u;
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassMatch(std::uint64_t bits, std::uint32_t mask) {
|
||||
return ((mask >> ClassBit(bits)) & 1u) != 0u;
|
||||
}
|
||||
|
||||
void Check(std::uint32_t round) {
|
||||
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
|
||||
const auto* in = &Input[tid * Inputs];
|
||||
const std::uint64_t abits = in[0] | (static_cast<std::uint64_t>(in[1]) << 32u);
|
||||
const std::uint64_t bbits = in[2] | (static_cast<std::uint64_t>(in[3]) << 32u);
|
||||
const std::uint32_t mask = in[4];
|
||||
const double a = std::bit_cast<double>(abits);
|
||||
const double b = std::bit_cast<double>(bbits);
|
||||
const bool unordered = std::isnan(a) || std::isnan(b);
|
||||
const std::array<bool, 17> base{
|
||||
false,
|
||||
a < b,
|
||||
a == b,
|
||||
a <= b,
|
||||
a > b,
|
||||
a < b || a > b,
|
||||
a >= b,
|
||||
!unordered,
|
||||
unordered,
|
||||
!(a >= b),
|
||||
!(a < b || a > b),
|
||||
!(a > b),
|
||||
!(a <= b),
|
||||
!(a == b),
|
||||
!(a < b),
|
||||
true,
|
||||
ClassMatch(abits, mask),
|
||||
};
|
||||
std::array<bool, Compares> expected{};
|
||||
for (std::uint32_t k = 0; k < base.size(); ++k) {
|
||||
expected[k] = base[k];
|
||||
expected[base.size() + k] = base[k];
|
||||
}
|
||||
expected[34] = 3.0 < a;
|
||||
expected[35] = 1.0 >= a;
|
||||
expected[36] = std::bit_cast<double>(0x3fc45f306dc9c882ull) == a;
|
||||
expected[37] = std::numeric_limits<double>::denorm_min() < a;
|
||||
expected[38] = std::fabs(a) < b;
|
||||
expected[39] = ClassMatch(abits & 0x7fffffffffffffffull, mask);
|
||||
expected[40] = !(std::fabs(a) > std::fabs(b));
|
||||
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 f64 compares: round " + std::to_string(round) + " 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;
|
||||
for (std::uint32_t round = 0; round < EdgeRounds + RandomRounds; ++round) {
|
||||
FillInput(round);
|
||||
Run(*device);
|
||||
Check(round);
|
||||
}
|
||||
std::puts("vopc f64 compare tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -580,6 +580,40 @@ enum class RdnaOpcode : std::uint16_t {
|
|||
VCmpxNlgF16,
|
||||
VCmpxNleF16,
|
||||
VCmpxTruF16,
|
||||
VCmpFF64,
|
||||
VCmpLtF64,
|
||||
VCmpEqF64,
|
||||
VCmpLeF64,
|
||||
VCmpGtF64,
|
||||
VCmpLgF64,
|
||||
VCmpGeF64,
|
||||
VCmpOF64,
|
||||
VCmpUF64,
|
||||
VCmpNgeF64,
|
||||
VCmpNlgF64,
|
||||
VCmpNgtF64,
|
||||
VCmpNleF64,
|
||||
VCmpNeqF64,
|
||||
VCmpNltF64,
|
||||
VCmpTruF64,
|
||||
VCmpClassF64,
|
||||
VCmpxFF64,
|
||||
VCmpxLtF64,
|
||||
VCmpxEqF64,
|
||||
VCmpxLeF64,
|
||||
VCmpxGtF64,
|
||||
VCmpxLgF64,
|
||||
VCmpxGeF64,
|
||||
VCmpxOF64,
|
||||
VCmpxUF64,
|
||||
VCmpxNgeF64,
|
||||
VCmpxNlgF64,
|
||||
VCmpxNgtF64,
|
||||
VCmpxNleF64,
|
||||
VCmpxNeqF64,
|
||||
VCmpxNltF64,
|
||||
VCmpxTruF64,
|
||||
VCmpxClassF64,
|
||||
VCmpxLtU32,
|
||||
VCmpxEqU32,
|
||||
VCmpxLeU32,
|
||||
|
|
|
|||
|
|
@ -529,6 +529,40 @@ bool IsVectorAluOpcode(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VCmpxNlgF16:
|
||||
case RdnaOpcode::VCmpxNleF16:
|
||||
case RdnaOpcode::VCmpxTruF16:
|
||||
case RdnaOpcode::VCmpFF64:
|
||||
case RdnaOpcode::VCmpLtF64:
|
||||
case RdnaOpcode::VCmpEqF64:
|
||||
case RdnaOpcode::VCmpLeF64:
|
||||
case RdnaOpcode::VCmpGtF64:
|
||||
case RdnaOpcode::VCmpLgF64:
|
||||
case RdnaOpcode::VCmpGeF64:
|
||||
case RdnaOpcode::VCmpOF64:
|
||||
case RdnaOpcode::VCmpUF64:
|
||||
case RdnaOpcode::VCmpNgeF64:
|
||||
case RdnaOpcode::VCmpNlgF64:
|
||||
case RdnaOpcode::VCmpNgtF64:
|
||||
case RdnaOpcode::VCmpNleF64:
|
||||
case RdnaOpcode::VCmpNeqF64:
|
||||
case RdnaOpcode::VCmpNltF64:
|
||||
case RdnaOpcode::VCmpTruF64:
|
||||
case RdnaOpcode::VCmpClassF64:
|
||||
case RdnaOpcode::VCmpxFF64:
|
||||
case RdnaOpcode::VCmpxLtF64:
|
||||
case RdnaOpcode::VCmpxEqF64:
|
||||
case RdnaOpcode::VCmpxLeF64:
|
||||
case RdnaOpcode::VCmpxGtF64:
|
||||
case RdnaOpcode::VCmpxLgF64:
|
||||
case RdnaOpcode::VCmpxGeF64:
|
||||
case RdnaOpcode::VCmpxOF64:
|
||||
case RdnaOpcode::VCmpxUF64:
|
||||
case RdnaOpcode::VCmpxNgeF64:
|
||||
case RdnaOpcode::VCmpxNlgF64:
|
||||
case RdnaOpcode::VCmpxNgtF64:
|
||||
case RdnaOpcode::VCmpxNleF64:
|
||||
case RdnaOpcode::VCmpxNeqF64:
|
||||
case RdnaOpcode::VCmpxNltF64:
|
||||
case RdnaOpcode::VCmpxTruF64:
|
||||
case RdnaOpcode::VCmpxClassF64:
|
||||
case RdnaOpcode::VCmpxNeU64:
|
||||
case RdnaOpcode::VCmpxLtU32:
|
||||
case RdnaOpcode::VCmpxEqU32:
|
||||
|
|
|
|||
|
|
@ -384,6 +384,40 @@ constexpr VopcOpcodeInfo vopcOpcodes[] = {
|
|||
{0xfau, RdnaOpcode::VCmpxNlgF16},
|
||||
{0xfcu, RdnaOpcode::VCmpxNleF16},
|
||||
{0xffu, RdnaOpcode::VCmpxTruF16},
|
||||
{0x20u, RdnaOpcode::VCmpFF64, false},
|
||||
{0x21u, RdnaOpcode::VCmpLtF64, false},
|
||||
{0x22u, RdnaOpcode::VCmpEqF64, false},
|
||||
{0x23u, RdnaOpcode::VCmpLeF64, false},
|
||||
{0x24u, RdnaOpcode::VCmpGtF64, false},
|
||||
{0x25u, RdnaOpcode::VCmpLgF64, false},
|
||||
{0x26u, RdnaOpcode::VCmpGeF64, false},
|
||||
{0x27u, RdnaOpcode::VCmpOF64, false},
|
||||
{0x28u, RdnaOpcode::VCmpUF64, false},
|
||||
{0x29u, RdnaOpcode::VCmpNgeF64, false},
|
||||
{0x2au, RdnaOpcode::VCmpNlgF64, false},
|
||||
{0x2bu, RdnaOpcode::VCmpNgtF64, false},
|
||||
{0x2cu, RdnaOpcode::VCmpNleF64, false},
|
||||
{0x2du, RdnaOpcode::VCmpNeqF64, false},
|
||||
{0x2eu, RdnaOpcode::VCmpNltF64, false},
|
||||
{0x2fu, RdnaOpcode::VCmpTruF64, false},
|
||||
{0x30u, RdnaOpcode::VCmpxFF64, false},
|
||||
{0x31u, RdnaOpcode::VCmpxLtF64, false},
|
||||
{0x32u, RdnaOpcode::VCmpxEqF64, false},
|
||||
{0x33u, RdnaOpcode::VCmpxLeF64, false},
|
||||
{0x34u, RdnaOpcode::VCmpxGtF64, false},
|
||||
{0x35u, RdnaOpcode::VCmpxLgF64, false},
|
||||
{0x36u, RdnaOpcode::VCmpxGeF64, false},
|
||||
{0x37u, RdnaOpcode::VCmpxOF64, false},
|
||||
{0x38u, RdnaOpcode::VCmpxUF64, false},
|
||||
{0x39u, RdnaOpcode::VCmpxNgeF64, false},
|
||||
{0x3au, RdnaOpcode::VCmpxNlgF64, false},
|
||||
{0x3bu, RdnaOpcode::VCmpxNgtF64, false},
|
||||
{0x3cu, RdnaOpcode::VCmpxNleF64, false},
|
||||
{0x3du, RdnaOpcode::VCmpxNeqF64, false},
|
||||
{0x3eu, RdnaOpcode::VCmpxNltF64, false},
|
||||
{0x3fu, RdnaOpcode::VCmpxTruF64, false},
|
||||
{0xa8u, RdnaOpcode::VCmpClassF64, false},
|
||||
{0xb8u, RdnaOpcode::VCmpxClassF64, false},
|
||||
};
|
||||
|
||||
constexpr VectorOpcodeInfo vop3Opcodes[] = {
|
||||
|
|
@ -716,7 +750,50 @@ bool isVop1FloatResultOpcode(RdnaOpcode opcode) {
|
|||
}
|
||||
}
|
||||
|
||||
bool isVopcFloat64CompareOpcode(RdnaOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case RdnaOpcode::VCmpFF64:
|
||||
case RdnaOpcode::VCmpLtF64:
|
||||
case RdnaOpcode::VCmpEqF64:
|
||||
case RdnaOpcode::VCmpLeF64:
|
||||
case RdnaOpcode::VCmpGtF64:
|
||||
case RdnaOpcode::VCmpLgF64:
|
||||
case RdnaOpcode::VCmpGeF64:
|
||||
case RdnaOpcode::VCmpOF64:
|
||||
case RdnaOpcode::VCmpUF64:
|
||||
case RdnaOpcode::VCmpNgeF64:
|
||||
case RdnaOpcode::VCmpNlgF64:
|
||||
case RdnaOpcode::VCmpNgtF64:
|
||||
case RdnaOpcode::VCmpNleF64:
|
||||
case RdnaOpcode::VCmpNeqF64:
|
||||
case RdnaOpcode::VCmpNltF64:
|
||||
case RdnaOpcode::VCmpTruF64:
|
||||
case RdnaOpcode::VCmpClassF64:
|
||||
case RdnaOpcode::VCmpxFF64:
|
||||
case RdnaOpcode::VCmpxLtF64:
|
||||
case RdnaOpcode::VCmpxEqF64:
|
||||
case RdnaOpcode::VCmpxLeF64:
|
||||
case RdnaOpcode::VCmpxGtF64:
|
||||
case RdnaOpcode::VCmpxLgF64:
|
||||
case RdnaOpcode::VCmpxGeF64:
|
||||
case RdnaOpcode::VCmpxOF64:
|
||||
case RdnaOpcode::VCmpxUF64:
|
||||
case RdnaOpcode::VCmpxNgeF64:
|
||||
case RdnaOpcode::VCmpxNlgF64:
|
||||
case RdnaOpcode::VCmpxNgtF64:
|
||||
case RdnaOpcode::VCmpxNleF64:
|
||||
case RdnaOpcode::VCmpxNeqF64:
|
||||
case RdnaOpcode::VCmpxNltF64:
|
||||
case RdnaOpcode::VCmpxTruF64:
|
||||
case RdnaOpcode::VCmpxClassF64: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isVopcFloatCompareOpcode(RdnaOpcode opcode) {
|
||||
if (isVopcFloat64CompareOpcode(opcode)) {
|
||||
return true;
|
||||
}
|
||||
switch (opcode) {
|
||||
case RdnaOpcode::VCmpFF32:
|
||||
case RdnaOpcode::VCmpLtF32:
|
||||
|
|
@ -1323,7 +1400,24 @@ bool isVopcCompareExec(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VCmpxGeF16:
|
||||
case RdnaOpcode::VCmpxNgtF16:
|
||||
case RdnaOpcode::VCmpxNeqF16:
|
||||
case RdnaOpcode::VCmpxNltF16: return true;
|
||||
case RdnaOpcode::VCmpxNltF16:
|
||||
case RdnaOpcode::VCmpxFF64:
|
||||
case RdnaOpcode::VCmpxLtF64:
|
||||
case RdnaOpcode::VCmpxEqF64:
|
||||
case RdnaOpcode::VCmpxLeF64:
|
||||
case RdnaOpcode::VCmpxGtF64:
|
||||
case RdnaOpcode::VCmpxLgF64:
|
||||
case RdnaOpcode::VCmpxGeF64:
|
||||
case RdnaOpcode::VCmpxOF64:
|
||||
case RdnaOpcode::VCmpxUF64:
|
||||
case RdnaOpcode::VCmpxNgeF64:
|
||||
case RdnaOpcode::VCmpxNlgF64:
|
||||
case RdnaOpcode::VCmpxNgtF64:
|
||||
case RdnaOpcode::VCmpxNleF64:
|
||||
case RdnaOpcode::VCmpxNeqF64:
|
||||
case RdnaOpcode::VCmpxNltF64:
|
||||
case RdnaOpcode::VCmpxTruF64:
|
||||
case RdnaOpcode::VCmpxClassF64: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1774,6 +1868,9 @@ RdnaInstruction DecodeRdnaVopc(std::uint32_t programCounter, std::span<const std
|
|||
|
||||
if (src0 == 249u) {
|
||||
instruction.op = lookupVectorOpcode(vopcOpcodes, opcode, "VOPC SDWA modifier is not supported for opcode");
|
||||
if (isVopcFloat64CompareOpcode(instruction.op)) {
|
||||
throw std::invalid_argument("VOPC SDWA modifier is not supported for opcode");
|
||||
}
|
||||
instruction.destination.kind = isVopcCompareExec(instruction.op) ? RdnaOperandKind::ExecLo : RdnaOperandKind::VccLo;
|
||||
decodeVopcSdwa(programCounter, code, wordIndex, vsrc1, instruction);
|
||||
return instruction;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ private:
|
|||
IrU32 readU32(const RdnaOperand& operand);
|
||||
std::array<IrU32, 2> readU32Pair(const RdnaOperand& operand);
|
||||
IrU64 readU64(const RdnaOperand& operand);
|
||||
std::array<IrU32, 2> readF64Bits(const RdnaOperand& operand);
|
||||
IrF32 readF16LaneAsF32(const RdnaOperand& operand, bool highLane, bool packed = false);
|
||||
IrF32 readF16AsF32(const RdnaOperand& operand);
|
||||
IrU32 readF16SourceBits(const RdnaOperand& operand);
|
||||
|
|
@ -120,6 +121,10 @@ private:
|
|||
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);
|
||||
IrU1 float64IsNan(const std::array<IrU32, 2>& bits);
|
||||
IrU64 float64OrderKey(const std::array<IrU32, 2>& bits);
|
||||
void emitFloat64Compare(const RdnaInstruction& inst, bool less, bool equal, bool greater, bool unordered, bool cmpx);
|
||||
void emitFloat64ClassCompare(const RdnaInstruction& inst, bool cmpx);
|
||||
void vCvtF32Ubyte(const RdnaInstruction& inst, std::uint32_t byteIndex);
|
||||
void vCvtF32U32(const RdnaInstruction& inst);
|
||||
void vCvtF32I32(const RdnaInstruction& inst);
|
||||
|
|
|
|||
|
|
@ -79,6 +79,74 @@ void TranslationContext::emitFloatClassCompare(const RdnaInstruction& inst, bool
|
|||
emitCompareResult(inst, IrU1(ir.Emit(IrOpcode::FPCmpClass32, IrType::U1, {value, mask})), false, cmpx);
|
||||
}
|
||||
|
||||
IrU1 TranslationContext::float64IsNan(const std::array<IrU32, 2>& bits) {
|
||||
IrValue& magnitude = ir.ConstructU64(bits[0].Value(), ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x7fffffffu)));
|
||||
return IrU1(ir.Emit(IrOpcode::UGreaterThan64, IrType::U1, {&magnitude, &ir.ConstantU64(0x7ff0000000000000ull)}));
|
||||
}
|
||||
|
||||
IrU64 TranslationContext::float64OrderKey(const std::array<IrU32, 2>& bits) {
|
||||
IrValue& magnitude = ir.ConstructU64(bits[0].Value(), ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x7fffffffu)));
|
||||
IrValue& signWord = ir.ShiftRightArithmetic(bits[1].Value(), ir.Constant(31u));
|
||||
IrValue& signMask = ir.ConstructU64(signWord, signWord);
|
||||
IrValue& negativeMagnitude = ir.Emit(IrOpcode::BitwiseAnd64, IrType::U64, {&magnitude, &signMask});
|
||||
IrValue& doubled = ir.Emit(IrOpcode::ShiftLeftLogical64, IrType::U64, {&negativeMagnitude, &ir.Constant(1u)});
|
||||
return IrU64(ir.Emit(IrOpcode::ISub64, IrType::U64, {&magnitude, &doubled}));
|
||||
}
|
||||
|
||||
void TranslationContext::emitFloat64Compare(const RdnaInstruction& inst, bool less, bool equal, bool greater, bool unordered, bool cmpx) {
|
||||
const std::array<IrU32, 2> lhs = readF64Bits(sourceAt(inst, 0u));
|
||||
const std::array<IrU32, 2> rhs = readF64Bits(sourceAt(inst, 1u));
|
||||
const IrU1 lhsNan = float64IsNan(lhs);
|
||||
const IrU1 rhsNan = float64IsNan(rhs);
|
||||
IrValue& isUnordered = ir.LogicalOr(lhsNan.Value(), rhsNan.Value());
|
||||
const IrU64 lhsKey = float64OrderKey(lhs);
|
||||
const IrU64 rhsKey = float64OrderKey(rhs);
|
||||
IrValue* relation = nullptr;
|
||||
const auto include = [&](IrValue& term) {
|
||||
relation = relation == nullptr ? &term : &ir.LogicalOr(*relation, term);
|
||||
};
|
||||
if (less) {
|
||||
include(ir.Emit(IrOpcode::SLessThan64, IrType::U1, {&lhsKey.Value(), &rhsKey.Value()}));
|
||||
}
|
||||
if (equal) {
|
||||
include(ir.Emit(IrOpcode::IEqual64, IrType::U1, {&lhsKey.Value(), &rhsKey.Value()}));
|
||||
}
|
||||
if (greater) {
|
||||
include(ir.Emit(IrOpcode::SLessThan64, IrType::U1, {&rhsKey.Value(), &lhsKey.Value()}));
|
||||
}
|
||||
if (relation == nullptr) {
|
||||
relation = &ir.ConstantBool(false);
|
||||
}
|
||||
IrValue& result = unordered ? ir.LogicalOr(isUnordered, *relation) : ir.LogicalAnd(ir.LogicalNot(isUnordered), *relation);
|
||||
emitCompareResult(inst, IrU1(result), false, cmpx);
|
||||
}
|
||||
|
||||
void TranslationContext::emitFloat64ClassCompare(const RdnaInstruction& inst, bool cmpx) {
|
||||
const RdnaOperand& maskOperand = sourceAt(inst, 1u);
|
||||
if (maskOperand.absolute || maskOperand.negate) {
|
||||
throw std::runtime_error("TranslationContext::emitFloat64ClassCompare class mask does not take source modifiers");
|
||||
}
|
||||
const std::array<IrU32, 2> bits = readF64Bits(sourceAt(inst, 0u));
|
||||
const IrU32 mask = readU32(maskOperand);
|
||||
IrValue& zero = ir.Constant(0u);
|
||||
IrValue& negative = ir.INotEqual(ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x80000000u)), zero);
|
||||
IrValue& exponent = ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x7ff00000u));
|
||||
IrValue& fraction = ir.BitwiseOr(ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x000fffffu)), bits[0].Value());
|
||||
IrValue& fractionNonZero = ir.INotEqual(fraction, zero);
|
||||
IrValue& quiet = ir.INotEqual(ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x00080000u)), zero);
|
||||
IrValue& nan = ir.Select(quiet, ir.Constant(1u), zero);
|
||||
IrValue& infinity = ir.Select(negative, ir.Constant(2u), ir.Constant(9u));
|
||||
IrValue& normal = ir.Select(negative, ir.Constant(3u), ir.Constant(8u));
|
||||
IrValue& denormal = ir.Select(negative, ir.Constant(4u), ir.Constant(7u));
|
||||
IrValue& signedZero = ir.Select(negative, ir.Constant(5u), ir.Constant(6u));
|
||||
IrValue& exponentMax = ir.Select(fractionNonZero, nan, infinity);
|
||||
IrValue& exponentZero = ir.Select(fractionNonZero, denormal, signedZero);
|
||||
IrValue& finite = ir.Select(ir.IEqual(exponent, zero), exponentZero, normal);
|
||||
IrValue& index = ir.Select(ir.IEqual(exponent, ir.Constant(0x7ff00000u)), exponentMax, finite);
|
||||
IrValue& selected = ir.BitwiseAnd(ir.ShiftRightLogical(mask.Value(), index), ir.Constant(1u));
|
||||
emitCompareResult(inst, IrU1(ir.INotEqual(selected, zero)), false, cmpx);
|
||||
}
|
||||
|
||||
void TranslateCompareInstruction(TranslationContext& context, const RdnaInstruction& instruction) {
|
||||
throw std::runtime_error("TranslateCompareInstruction not implemented");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,25 @@ IrU64 TranslationContext::readU64(const RdnaOperand& operand) {
|
|||
return IrU64(ir.ConstructU64(pair[0].Value(), pair[1].Value()));
|
||||
}
|
||||
|
||||
std::array<IrU32, 2> TranslationContext::readF64Bits(const RdnaOperand& operand) {
|
||||
std::array<IrU32, 2> bits{IrU32(ir.Constant(0u)), IrU32(ir.Constant(operand.value))};
|
||||
if (operand.kind == RdnaOperandKind::FloatInlineConstant && operand.value == 0x3e22f983u) {
|
||||
bits = {IrU32(ir.Constant(0x6dc9c882u)), IrU32(ir.Constant(0x3fc45f30u))};
|
||||
} else if (operand.kind != RdnaOperandKind::LiteralConstant) {
|
||||
RdnaOperand source = operand;
|
||||
source.absolute = false;
|
||||
source.negate = false;
|
||||
bits = readU32Pair(source);
|
||||
}
|
||||
if (operand.absolute) {
|
||||
bits[1] = IrU32(ir.BitwiseAnd(bits[1].Value(), ir.Constant(0x7fffffffu)));
|
||||
}
|
||||
if (operand.negate) {
|
||||
bits[1] = IrU32(ir.BitwiseXor(bits[1].Value(), ir.Constant(0x80000000u)));
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
std::array<IrU32, 2> TranslationContext::extractU64(IrU64 value) {
|
||||
return {IrU32(ir.CompositeExtract(value.Value(), 0u)), IrU32(ir.CompositeExtract(value.Value(), 1u))};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,6 +534,108 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) {
|
|||
case RdnaOpcode::VCmpxClassF32:
|
||||
emitFloatClassCompare(inst, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpFF64:
|
||||
emitCompareConstant(inst, false, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpLtF64:
|
||||
emitFloat64Compare(inst, true, false, false, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpEqF64:
|
||||
emitFloat64Compare(inst, false, true, false, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpLeF64:
|
||||
emitFloat64Compare(inst, true, true, false, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpGtF64:
|
||||
emitFloat64Compare(inst, false, false, true, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpLgF64:
|
||||
emitFloat64Compare(inst, true, false, true, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpGeF64:
|
||||
emitFloat64Compare(inst, false, true, true, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpOF64:
|
||||
emitFloat64Compare(inst, true, true, true, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpUF64:
|
||||
emitFloat64Compare(inst, false, false, false, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNgeF64:
|
||||
emitFloat64Compare(inst, true, false, false, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNlgF64:
|
||||
emitFloat64Compare(inst, false, true, false, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNgtF64:
|
||||
emitFloat64Compare(inst, true, true, false, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNleF64:
|
||||
emitFloat64Compare(inst, false, false, true, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNeqF64:
|
||||
emitFloat64Compare(inst, true, false, true, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpNltF64:
|
||||
emitFloat64Compare(inst, false, true, true, true, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpTruF64:
|
||||
emitCompareConstant(inst, true, false, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpClassF64:
|
||||
emitFloat64ClassCompare(inst, false);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxFF64:
|
||||
emitCompareConstant(inst, false, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxLtF64:
|
||||
emitFloat64Compare(inst, true, false, false, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxEqF64:
|
||||
emitFloat64Compare(inst, false, true, false, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxLeF64:
|
||||
emitFloat64Compare(inst, true, true, false, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxGtF64:
|
||||
emitFloat64Compare(inst, false, false, true, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxLgF64:
|
||||
emitFloat64Compare(inst, true, false, true, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxGeF64:
|
||||
emitFloat64Compare(inst, false, true, true, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxOF64:
|
||||
emitFloat64Compare(inst, true, true, true, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxUF64:
|
||||
emitFloat64Compare(inst, false, false, false, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNgeF64:
|
||||
emitFloat64Compare(inst, true, false, false, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNlgF64:
|
||||
emitFloat64Compare(inst, false, true, false, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNgtF64:
|
||||
emitFloat64Compare(inst, true, true, false, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNleF64:
|
||||
emitFloat64Compare(inst, false, false, true, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNeqF64:
|
||||
emitFloat64Compare(inst, true, false, true, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxNltF64:
|
||||
emitFloat64Compare(inst, false, true, true, true, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxTruF64:
|
||||
emitCompareConstant(inst, true, false, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCmpxClassF64:
|
||||
emitFloat64ClassCompare(inst, true);
|
||||
return true;
|
||||
case RdnaOpcode::VCvtF32Ubyte0:
|
||||
vCvtF32Ubyte(inst, 0u);
|
||||
return true;
|
||||
|
|
|
|||
Loading…
Reference in New Issue