feat(shader): implement basic f64 VALU ops
Decode and translate v_add/mul/fma/min/max/ldexp_f64, v_trunc/ceil/floor/ rndne/fract_f64, v_frexp_mant/exp_i32_f64 and the f64 <-> f32/i32/u32 conversions. f64 values stay as their raw bits (U64) in the IR and are bitcast to double in SPIR-V, which needs the Float64 capability: the device enables shaderFloat64 when it also preserves signed zero, inf and NaN for 64-bit floats, and the shader declares SignedZeroInfNanPreserve 64. Semantics measured on RDNA2 hardware (8209 inputs per op): literals are the high dword, the 1/(2*pi) inline constant is the exact double, omod is ignored, clamp maps NaN to +0, min/max quiet a signaling NaN and drop a quiet one, NaN results are quiet, and the integer conversions saturate with NaN -> 0. Truncation, rounding and ldexp are done on the bits so they do not depend on the driver: lavapipe returned wrong results for f64 RoundEven and Trunc, and GLSL Ldexp is undefined past +-1024. Fma is NoContraction so RADV keeps it fused. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b0a4a991cb
commit
b0afa04c77
|
|
@ -813,6 +813,8 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
require(available.vertexPipelineStoresAndAtomics && available.fragmentStoresAndAtomics, "graphics shader buffer writes and atomics are unavailable");
|
||||
VkPhysicalDeviceFeatures enabled{};
|
||||
enabled.shaderInt64 = VK_TRUE;
|
||||
enabled.shaderFloat64 = available.shaderFloat64 && floatControls.shaderSignedZeroInfNanPreserveFloat64;
|
||||
if (enabled.shaderFloat64) state->capabilities.push_back(spv::CapabilityFloat64);
|
||||
enabled.vertexPipelineStoresAndAtomics = VK_TRUE;
|
||||
enabled.fragmentStoresAndAtomics = VK_TRUE;
|
||||
enabled.tessellationShader = available.tessellationShader;
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
|
|||
capability == spv::CapabilityInt64 ||
|
||||
capability == spv::CapabilityInt16 ||
|
||||
capability == spv::CapabilityFloat16 ||
|
||||
capability == spv::CapabilityFloat64 ||
|
||||
capability == spv::CapabilityStorageBuffer8BitAccess ||
|
||||
capability == spv::CapabilityPhysicalStorageBufferAddresses ||
|
||||
capability == spv::CapabilitySampledImageArrayDynamicIndexing ||
|
||||
|
|
@ -280,6 +281,11 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
|
|||
for (std::size_t i = 5; i < count; ++i) Require(module.interface.insert(instruction[i]).second, "duplicate SPIR-V interface ID");
|
||||
break;
|
||||
case spv::OpExecutionMode:
|
||||
if (count == 4 && instruction[2] == spv::ExecutionModeSignedZeroInfNanPreserve) {
|
||||
Require(instruction[3] == 32u || instruction[3] == 64u, "SignedZeroInfNanPreserve requires Float32 or Float64");
|
||||
executionModeTargets.insert(instruction[1]);
|
||||
break;
|
||||
}
|
||||
Require(count >= 3 && module.modes.emplace(instruction[2], std::vector<std::uint32_t>(instruction.begin() + 3, instruction.end())).second, "duplicate or malformed execution mode");
|
||||
executionModeTargets.insert(instruction[1]);
|
||||
if (instruction[2] == spv::ExecutionModeOriginUpperLeft) upperLeft = true;
|
||||
|
|
@ -338,10 +344,6 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
|
|||
Require(entries == 1 && memoryModels == 1, "SPIR-V must contain one entry point and memory model");
|
||||
Require(entryPoint != 0 && std::all_of(executionModeTargets.begin(), executionModeTargets.end(), [&](auto target) { return target == entryPoint; }), "execution mode refers to a different entry point");
|
||||
Require(!fragment || upperLeft, "fragment coordinates must use an upper-left origin");
|
||||
if (const auto preserve = module.modes.find(spv::ExecutionModeSignedZeroInfNanPreserve); preserve != module.modes.end()) {
|
||||
Require(preserve->second == std::vector<std::uint32_t>{32u}, "SignedZeroInfNanPreserve requires Float32");
|
||||
module.modes.erase(preserve);
|
||||
}
|
||||
const auto mode = [&](std::uint32_t name, std::vector<std::uint32_t> operands) {
|
||||
const auto it = module.modes.find(name);
|
||||
Require(it != module.modes.end() && it->second == operands, "missing or incompatible shader execution mode");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
#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, 30> Code{
|
||||
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0xd564000a, 0x02020d04, 0xd565000c,
|
||||
0x02020d04, 0xd54c000e, 0x041a0d04, 0xd54c0010, 0x84320d04, 0xd5660012, 0x02020d04, 0xd5670014,
|
||||
0x02020d04, 0xd5680016, 0x02020d04, 0xd5640318, 0x22020d04, 0xe0781000, 0x80010a03, 0xe0781010,
|
||||
0x80010e03, 0xe0781020, 0x80011203, 0xe0781030, 0x80011603, 0xbf810000,
|
||||
};
|
||||
|
||||
constexpr std::uint32_t Rows[32][4] = {
|
||||
{0x00000000u, 0x3ff00000u, 0x80000000u, 0x40000000u},
|
||||
{0x00000000u, 0x3ff80000u, 0x7fffffffu, 0x40000000u},
|
||||
{0xffffffffu, 0x7fefffffu, 0xfffff7ccu, 0x3ff00000u},
|
||||
{0x00000000u, 0xbff00000u, 0xfffffbcdu, 0x3ff00000u},
|
||||
{0x00000000u, 0x3fe00000u, 0x00000401u, 0x3ff00000u},
|
||||
{0x00000000u, 0xfff00000u, 0xffc00000u, 0x41dfffffu},
|
||||
{0x8a59a474u, 0xbefa45c5u, 0x12eaf8e8u, 0xc020f641u},
|
||||
{0x00000000u, 0x3ff00000u, 0x00000001u, 0x7ff00000u},
|
||||
{0x00000001u, 0xfff80000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u},
|
||||
{0x00000000u, 0xbff00000u, 0x00000000u, 0x7ff00000u},
|
||||
{0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff00000u},
|
||||
{0xd9787a36u, 0xfff7c837u, 0x00000000u, 0x41f00000u},
|
||||
{0x00000000u, 0x3ff80000u, 0xffffffffu, 0xffefffffu},
|
||||
{0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u},
|
||||
{0x047b57e5u, 0x02f22376u, 0xc66ab30du, 0x8e9a2140u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0xbff00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0xbfe00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x3ff80000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x40040000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0xc0040000u},
|
||||
{0x00000000u, 0x00000000u, 0xffffffffu, 0x7fefffffu},
|
||||
{0x00000000u, 0x00000000u, 0xffffffffu, 0xffefffffu},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x7ff80000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000001u, 0xfff80000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000001u, 0x7ff00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x7ff40000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x41e00000u}
|
||||
};
|
||||
constexpr std::uint32_t Expected[32][16] = {
|
||||
{0x80000000u, 0x40080000u, 0x80000000u, 0x40000000u, 0x80000000u, 0x40100000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x80000000u, 0x40000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00001u},
|
||||
{0x7fffffffu, 0x400c0000u, 0xbffffffeu, 0x40080000u, 0x9fffffffu, 0x40140000u, 0x00000000u, 0x3cb00000u, 0x00000000u, 0x3ff80000u, 0x7fffffffu, 0x40000000u, 0x00000000u, 0x7ff00000u, 0xfffffffcu, 0x3fe00001u},
|
||||
{0xffffffffu, 0x7fefffffu, 0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0xfffff7ccu, 0x3ff00000u, 0xffffffffu, 0x7fefffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffefffffu},
|
||||
{0x79a00000u, 0x3eafffffu, 0xfffffbcdu, 0xbff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbff00000u, 0xfffffbcdu, 0x3ff00000u, 0x00000000u, 0x80000000u, 0x79a00000u, 0x3eafffffu},
|
||||
{0x00000401u, 0x3ff80000u, 0x00000401u, 0x3fe00000u, 0x00000602u, 0x3ff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u, 0x00000401u, 0x3ff00000u, 0x00000000u, 0x7ff00000u, 0x00000802u, 0x3fe00000u},
|
||||
{0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xfff00000u, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u},
|
||||
{0x5ba3aa33u, 0xc020f644u, 0x220b9c1cu, 0x3f2bda21u, 0x38c9d6dcu, 0xc020f625u, 0x35aa5b80u, 0xbbc1a7fdu, 0x12eaf8e8u, 0xc020f641u, 0x8a59a474u, 0xbefa45c5u, 0x00000000u, 0xfff00000u, 0xca32479du, 0x4020f63du},
|
||||
{0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000000u, 0x40000000u, 0x00000001u, 0x7ff80000u},
|
||||
{0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x7ff00000u},
|
||||
{0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0xfff80000u},
|
||||
{0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u, 0xd9787a36u, 0xffffc837u},
|
||||
{0xffffffffu, 0xffefffffu, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0xffffffffu, 0xffefffffu, 0x00000000u, 0x3ff80000u, 0x00000000u, 0x3fe80000u, 0xffffffffu, 0x7fefffffu},
|
||||
{0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff80000u},
|
||||
{0xc66ab30du, 0x8e9a2140u, 0x00000000u, 0x80000000u, 0xc66ab30du, 0x8e9a2140u, 0x00000000u, 0x80000000u, 0xc66ab30du, 0x8e9a2140u, 0x047b57e5u, 0x02f22376u, 0x00000000u, 0x00000000u, 0xc66ab30du, 0x0e9a2140u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u},
|
||||
{0x00000000u, 0xbff00000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u},
|
||||
{0x00000000u, 0x3fe00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u},
|
||||
{0x00000000u, 0xbfe00000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xbfe00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xbfe00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u},
|
||||
{0x00000000u, 0x3ff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff80000u},
|
||||
{0x00000000u, 0x40040000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40040000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40040000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40040000u},
|
||||
{0x00000000u, 0xc0040000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xc0040000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xc0040000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40040000u},
|
||||
{0xffffffffu, 0x7fefffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0x7fefffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xffffffffu, 0x7fefffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0x7fefffffu},
|
||||
{0xffffffffu, 0xffefffffu, 0x00000000u, 0x80000000u, 0xffffffffu, 0xffefffffu, 0x00000000u, 0x00000000u, 0xffffffffu, 0xffefffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0xffffffffu, 0x7fefffffu},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x7ff80000u},
|
||||
{0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000001u, 0x7ff80000u},
|
||||
{0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000001u, 0x7ff80000u, 0x00000000u, 0x00000000u, 0x00000001u, 0x7ff80000u},
|
||||
{0x00000000u, 0x7ffc0000u, 0x00000000u, 0x7ffc0000u, 0x00000000u, 0x7ffc0000u, 0x00000000u, 0x7ffc0000u, 0x00000000u, 0x7ffc0000u, 0x00000000u, 0x7ffc0000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x7ffc0000u},
|
||||
{0x00000000u, 0x41e00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x41e00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x41e00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x41e00000u}
|
||||
};
|
||||
constexpr const char* Names[16] = {"add lo", "add hi", "mul lo", "mul hi", "fma lo", "fma hi", "fma error lo", "fma error hi", "min lo", "min hi", "max lo", "max hi", "ldexp lo", "ldexp hi", "add abs neg lo", "add abs neg hi"};
|
||||
|
||||
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("f64 arithmetic: 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("f64 arithmetic tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
#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, 28> Code{
|
||||
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0x7e142104, 0x7e180905, 0x7e1c2d05,
|
||||
0xd5648010, 0x02020d06, 0xd5650112, 0x3a020d06, 0xd5640014, 0x0201ff06, 0x40080000, 0xd5980016,
|
||||
0x22010106, 0xd54c0018, 0x13e1e506, 0xe0781000, 0x80010a03, 0xe0781010, 0x80010e03, 0xe0781020,
|
||||
0x80011203, 0xe0781030, 0x80011603, 0xbf810000,
|
||||
};
|
||||
|
||||
constexpr std::uint32_t Rows[32][4] = {
|
||||
{0x7f800001u, 0x00000001u, 0x00000000u, 0x00000000u},
|
||||
{0x8e317041u, 0x00000000u, 0x00000000u, 0x7ff00000u},
|
||||
{0x7f7fffffu, 0xa7abe1c2u, 0x00100000u, 0xc1e00000u},
|
||||
{0xff800000u, 0x7e9508cbu, 0x00000000u, 0xfff00000u},
|
||||
{0x7f800000u, 0xffffffffu, 0xf0000000u, 0x47efffffu},
|
||||
{0xffffffffu, 0x00000001u, 0x00000000u, 0x80000000u},
|
||||
{0x80000000u, 0x7fffffffu, 0x00000000u, 0xbff00000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000001u, 0x43300000u},
|
||||
{0x6513270eu, 0x00000000u, 0x00000000u, 0x80000000u},
|
||||
{0x128b2f33u, 0x7fffffffu, 0x00000000u, 0x3ff00000u},
|
||||
{0x0ed90475u, 0x7fffffffu, 0x00000000u, 0x3fe00000u},
|
||||
{0x36f675ccu, 0x00000000u, 0x00000000u, 0xbfe00000u},
|
||||
{0x3d9c1724u, 0x7fffffffu, 0x00000000u, 0x40040000u},
|
||||
{0x6cad4a26u, 0x7fffffffu, 0x00000000u, 0xc0040000u},
|
||||
{0x7f7fffffu, 0x00000000u, 0xffffffffu, 0x7fefffffu},
|
||||
{0xf9ebdaccu, 0x00000000u, 0xffffffffu, 0xffefffffu},
|
||||
{0x8e81973eu, 0x00000001u, 0x00000000u, 0x7ff00000u},
|
||||
{0x7fc00000u, 0x00000001u, 0x00000000u, 0xfff00000u},
|
||||
{0x5f557203u, 0x7fffffffu, 0x00000000u, 0x41e00000u},
|
||||
{0x0f4205b4u, 0x00000001u, 0x00100000u, 0xc1e00000u},
|
||||
{0x7f150524u, 0x7fffffffu, 0xffc00000u, 0x41dfffffu},
|
||||
{0x7731af10u, 0x80000000u, 0x00000000u, 0x41f00000u},
|
||||
{0x5c90a958u, 0x00000001u, 0xffffffffu, 0xbfefffffu},
|
||||
{0x7f800000u, 0x00000001u, 0xffffffffu, 0x3fefffffu},
|
||||
{0x14f4733fu, 0xffffffffu, 0x00000001u, 0x43300000u},
|
||||
{0x86734721u, 0xffffffffu, 0x00000000u, 0x43400000u},
|
||||
{0x9be4bcfcu, 0x00000000u, 0xe0000000u, 0x47efffffu},
|
||||
{0x1e398f10u, 0x80000000u, 0xefffffffu, 0x47efffffu},
|
||||
{0x26e87555u, 0x80000000u, 0x00000000u, 0x36a00000u},
|
||||
{0x6bf46c69u, 0x0a097c97u, 0x00000000u, 0x36900000u},
|
||||
{0x80000000u, 0x7fffffffu, 0xffffffffu, 0x369fffffu},
|
||||
{0x92b1d3f2u, 0xffffffffu, 0xffffffffu, 0x380fffffu}
|
||||
};
|
||||
constexpr std::uint32_t Expected[32][16] = {
|
||||
{0x20000000u, 0x7ff80000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0x40080000u, 0x00000000u, 0x80000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x20000000u, 0xb9c62e08u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u},
|
||||
{0xe0000000u, 0x47efffffu, 0x8f800000u, 0xc1d61507u, 0x38400000u, 0x41e4f57cu, 0x00000000u, 0x00000000u, 0x00200000u, 0x43d00000u, 0xff600000u, 0xc1dfffffu, 0x00200000u, 0x41e00000u, 0x000ae834u, 0xc1e00000u},
|
||||
{0x00000000u, 0xfff00000u, 0x32c00000u, 0x41dfa542u, 0x32c00000u, 0x41dfa542u, 0x00000000u, 0x00000000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u},
|
||||
{0x00000000u, 0x7ff00000u, 0x00000000u, 0xbff00000u, 0xffe00000u, 0x41efffffu, 0x00000000u, 0x3ff00000u, 0xe0000008u, 0xcfefffffu, 0xf0000000u, 0x47efffffu, 0xf0000000u, 0xc7efffffu, 0xf0000000u, 0x47efffffu},
|
||||
{0xe0000000u, 0xffffffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40080000u, 0x00000000u, 0x00000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x00000000u, 0x80000000u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x40000000u, 0x00000000u, 0x3ff00000u, 0xe48d8de0u, 0xbfeae833u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000002u, 0xc6700000u, 0x00000004u, 0x43300000u, 0x00000001u, 0xc3300000u, 0x00000001u, 0x43300000u},
|
||||
{0xc0000000u, 0x44a264e1u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x40080000u, 0x00000000u, 0x00000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x60000000u, 0x3a5165e6u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x40100000u, 0x00000000u, 0xbff00000u, 0x0db93910u, 0x3ff28be6u},
|
||||
{0xa0000000u, 0x39db208eu, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xbfd00000u, 0x00000000u, 0x400c0000u, 0x00000000u, 0x80000000u, 0x1b727220u, 0x3fe517ccu},
|
||||
{0x80000000u, 0x3edeceb9u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fd00000u, 0x00000000u, 0x40040000u, 0x00000000u, 0x3ff00000u, 0xc91b1bbfu, 0xbfd5d067u},
|
||||
{0x80000000u, 0x3fb382e4u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xc0190000u, 0x00000000u, 0x40160000u, 0x00000000u, 0xc0000000u, 0x06dc9c88u, 0x400545f3u},
|
||||
{0xc0000000u, 0x4595a944u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x40190000u, 0x00000000u, 0x3fe00000u, 0x00000000u, 0x40080000u, 0xf9236378u, 0xc002ba0cu},
|
||||
{0xe0000000u, 0x47efffffu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xfff00000u, 0xffffffffu, 0x7fefffffu, 0xffffffffu, 0xffefffffu, 0xffffffffu, 0x7fefffffu},
|
||||
{0x80000000u, 0xc73d7b59u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x7ff00000u, 0xffffffffu, 0xffefffffu, 0xffffffffu, 0x7fefffffu, 0xffffffffu, 0xffefffffu},
|
||||
{0xc0000000u, 0xb9d032e7u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff00000u},
|
||||
{0x60000000u, 0x43eaae40u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xc3d00000u, 0x00600000u, 0x41e00000u, 0x00000000u, 0xc1e00000u, 0x000517ccu, 0x41e00000u},
|
||||
{0x80000000u, 0x39e840b6u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00200000u, 0x43d00000u, 0xff600000u, 0xc1dfffffu, 0x00200000u, 0x41e00000u, 0x000ae834u, 0xc1e00000u},
|
||||
{0x80000000u, 0x47e2a0a4u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0x00000000u, 0x3ff00000u, 0xff800000u, 0xc3cfffffu, 0x00400000u, 0x41e00000u, 0xffc00000u, 0xc1dfffffu, 0xffca2f98u, 0x41dfffffu},
|
||||
{0x00000000u, 0x46e635e2u, 0x00000000u, 0xc1e00000u, 0x00000000u, 0x41e00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xc3f00000u, 0x00300000u, 0x41f00000u, 0x00000000u, 0xc1f00000u, 0x00028be6u, 0x41f00000u},
|
||||
{0x00000000u, 0x4392152bu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0xfffffffeu, 0x3fefffffu, 0x00000000u, 0x40000000u, 0x00000000u, 0x3ff00000u, 0xe48d8ddeu, 0xbfeae833u},
|
||||
{0x00000000u, 0x7ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0xfffffffeu, 0xbfefffffu, 0x00000000u, 0x40100000u, 0x00000000u, 0x80000000u, 0x0db93910u, 0x3ff28be6u},
|
||||
{0xe0000000u, 0x3a9e8e67u, 0x00000000u, 0xbff00000u, 0xffe00000u, 0x41efffffu, 0x00000000u, 0x3ff00000u, 0x00000002u, 0xc6700000u, 0x00000004u, 0x43300000u, 0x00000001u, 0xc3300000u, 0x00000001u, 0x43300000u},
|
||||
{0x20000000u, 0xb8ce68e4u, 0x00000000u, 0xbff00000u, 0xffe00000u, 0x41efffffu, 0x00000000u, 0x3ff00000u, 0x00000000u, 0xc6900000u, 0x00000002u, 0x43400000u, 0x00000000u, 0xc3400000u, 0x00000000u, 0x43400000u},
|
||||
{0x80000000u, 0xbb7c979fu, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0xc0000020u, 0xcfefffffu, 0xe0000000u, 0x47efffffu, 0xe0000000u, 0xc7efffffu, 0xe0000000u, 0x47efffffu},
|
||||
{0x00000000u, 0x3bc731e2u, 0x00000000u, 0xc1e00000u, 0x00000000u, 0x41e00000u, 0x00000000u, 0x3ff00000u, 0xe0000006u, 0xcfefffffu, 0xefffffffu, 0x47efffffu, 0xefffffffu, 0xc7efffffu, 0xefffffffu, 0x47efffffu},
|
||||
{0xa0000000u, 0x3cdd0eaau, 0x00000000u, 0xc1e00000u, 0x00000000u, 0x41e00000u, 0x00000000u, 0x36b00000u, 0x00000000u, 0xad500000u, 0x00000000u, 0x40080000u, 0x00000000u, 0x80000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x20000000u, 0x457e8d8du, 0x2e000000u, 0x41a412f9u, 0x2e000000u, 0x41a412f9u, 0x00000000u, 0x36a00000u, 0x00000000u, 0xad300000u, 0x00000000u, 0x40080000u, 0x00000000u, 0x80000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x00000000u, 0x80000000u, 0xffc00000u, 0x41dfffffu, 0xffc00000u, 0x41dfffffu, 0xffffffffu, 0x36afffffu, 0xfffffffeu, 0xad4fffffu, 0x00000000u, 0x40080000u, 0x00000000u, 0x80000000u, 0x6dc9c882u, 0x3fc45f30u},
|
||||
{0x40000000u, 0xba563a7eu, 0x00000000u, 0xbff00000u, 0xffe00000u, 0x41efffffu, 0xffffffffu, 0x381fffffu, 0xfffffffeu, 0xb02fffffu, 0x00000000u, 0x40080000u, 0x00000000u, 0x80000000u, 0x6dc9c882u, 0x3fc45f30u}
|
||||
};
|
||||
constexpr const char* Names[16] = {"cvt f64 f32 lo", "cvt f64 f32 hi", "cvt f64 i32 lo", "cvt f64 i32 hi", "cvt f64 u32 lo", "cvt f64 u32 hi", "add clamp lo", "add clamp hi", "mul abs neg omod lo", "mul abs neg omod hi", "literal lo", "literal hi", "ceil neg lo", "ceil neg hi", "fma inv2pi omod lo", "fma inv2pi omod hi"};
|
||||
|
||||
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("f64 conversions: 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("f64 conversions tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
#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, 25> Code{
|
||||
0x34020084, 0x34060086, 0xe0381000, 0x80000401, 0xbf8c3f70, 0x7e142f04, 0x7e183104, 0x7e1c3504,
|
||||
0x7e203304, 0x7e247d04, 0x7e287b04, 0xd58f0016, 0x0a010104, 0x7e2e7904, 0x7e300704, 0x7e322b04,
|
||||
0xe0781000, 0x80010a03, 0xe0781010, 0x80010e03, 0xe0781020, 0x80011203, 0xe0781030, 0x80011603,
|
||||
0xbf810000,
|
||||
};
|
||||
|
||||
constexpr std::uint32_t Rows[32][4] = {
|
||||
{0x00000000u, 0xbc300000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x3c300000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0xbcb00000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff00000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0xfff00000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0xc0040000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000001u, 0xfff80000u, 0x00000000u, 0x00000000u},
|
||||
{0xc5f99476u, 0x4153959fu, 0x00000000u, 0x36a00000u},
|
||||
{0x33e8e7aau, 0x40a1e71cu, 0x7716dc6du, 0xbfdd8066u},
|
||||
{0x2d0e58dcu, 0x402fc4a6u, 0x00000000u, 0x40040000u},
|
||||
{0x26fc54b4u, 0x41019e75u, 0x00000000u, 0x36900000u},
|
||||
{0x8e200000u, 0x41d6c5deu, 0x672322eeu, 0xbfe815b9u},
|
||||
{0x32000000u, 0x41844f58u, 0x36863aecu, 0x3f36aa62u},
|
||||
{0x6170c9b6u, 0x409029c3u, 0x00000001u, 0x43300000u},
|
||||
{0x1bc00000u, 0x41c94321u, 0xffe00000u, 0x41efffffu},
|
||||
{0x960a8110u, 0x40bd6500u, 0x959d133du, 0xf977edf4u},
|
||||
{0xbacb6dd4u, 0x4110b770u, 0xe5000000u, 0xc1c3ad7cu},
|
||||
{0xc1666540u, 0x40f9a56fu, 0xa60b9c70u, 0x3e3cd351u},
|
||||
{0xdfafda62u, 0x419599f6u, 0x00000000u, 0x43e00000u},
|
||||
{0x1a712470u, 0x4063fcf2u, 0xb4746bf2u, 0xbe2efc36u},
|
||||
{0xf2604f52u, 0x412cb34eu, 0x00000000u, 0x41f00000u},
|
||||
{0x2ae097ecu, 0x405db192u, 0x8ac80000u, 0xc1f74d24u},
|
||||
{0xa7dbdaa2u, 0x4176b608u, 0x7079bceau, 0x94b19e9fu},
|
||||
{0x9f3e5890u, 0x407b14c4u, 0xff7f6a4eu, 0x42986654u},
|
||||
{0x50795a94u, 0x4147ce51u, 0x00000001u, 0x7ff00000u},
|
||||
{0x19e8099cu, 0x40198ad8u, 0x4402dc9cu, 0x4998ff49u},
|
||||
{0x076663a8u, 0x413adf07u, 0xffffffffu, 0x3fefffffu},
|
||||
{0xe6000000u, 0x41ada4d6u, 0x87db66a6u, 0x3fa852e9u},
|
||||
{0x8ce86774u, 0x40c06401u, 0x00000000u, 0x3ff00000u},
|
||||
{0x98b83010u, 0x40d1d266u, 0x00000000u, 0xbff00000u}
|
||||
};
|
||||
constexpr std::uint32_t Expected[32][16] = {
|
||||
{0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x80000000u, 0xffffffffu, 0x3fefffffu, 0x00000000u, 0xbfe00000u, 0xa1800000u, 0xffffffc5u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x00000000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3c300000u, 0x00000000u, 0x3fe00000u, 0x21800000u, 0xffffffc5u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xbff00000u, 0x00000000u, 0x80000000u, 0xfffffffeu, 0x3fefffffu, 0x00000000u, 0xbfe00000u, 0xa5800000u, 0xffffffcdu, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0x7ff00000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0x7ff00000u, 0x7f800000u, 0x00000000u, 0x7fffffffu, 0xffffffffu},
|
||||
{0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x3ff00000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x3fe00000u, 0x3f800000u, 0x00000001u, 0x00000001u, 0x00000001u},
|
||||
{0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff00000u, 0x00000000u, 0xfff80000u, 0x00000000u, 0xfff00000u, 0xff800000u, 0x00000000u, 0x80000000u, 0x00000000u},
|
||||
{0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x00000000u, 0x7ff80000u, 0x7fc00000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
||||
{0x00000000u, 0xc0000000u, 0x00000000u, 0xc0000000u, 0x00000000u, 0xc0080000u, 0x00000000u, 0xc0000000u, 0x00000000u, 0x3fe00000u, 0x00000000u, 0xbfe40000u, 0xc0200000u, 0x00000002u, 0xfffffffeu, 0x00000000u},
|
||||
{0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0x00000001u, 0xfff80000u, 0xffc00000u, 0x00000000u, 0x00000000u, 0x00000000u},
|
||||
{0xc0000000u, 0x4153959fu, 0x00000000u, 0x415395a0u, 0xc0000000u, 0x4153959fu, 0xc0000000u, 0x4153959fu, 0xd8000000u, 0x3fb7e651u, 0xc5f99476u, 0x3fe3959fu, 0x4a9cacfeu, 0x00000017u, 0x004e567fu, 0x004e567fu},
|
||||
{0x00000000u, 0x40a1e600u, 0x00000000u, 0x40a1e800u, 0x00000000u, 0x40a1e600u, 0x00000000u, 0x40a1e800u, 0x8e7aa000u, 0x3fe1c33eu, 0x33e8e7aau, 0x3fe1e71cu, 0x450f38e2u, 0x0000000cu, 0x000008f3u, 0x000008f3u},
|
||||
{0x00000000u, 0x402e0000u, 0x00000000u, 0x40300000u, 0x00000000u, 0x402e0000u, 0x00000000u, 0x40300000u, 0xd0e58dc0u, 0x3fec4a62u, 0x2d0e58dcu, 0x3fefc4a6u, 0x417e2531u, 0x00000004u, 0x0000000fu, 0x0000000fu},
|
||||
{0x00000000u, 0x41019e70u, 0x00000000u, 0x41019e78u, 0x00000000u, 0x41019e70u, 0x00000000u, 0x41019e78u, 0x52d00000u, 0x3fe49bf1u, 0x26fc54b4u, 0x3fe19e75u, 0x480cf3a9u, 0x00000012u, 0x000233ceu, 0x000233ceu},
|
||||
{0x8e000000u, 0x41d6c5deu, 0x8e400000u, 0x41d6c5deu, 0x8e000000u, 0x41d6c5deu, 0x8e000000u, 0x41d6c5deu, 0x00000000u, 0x3fe00000u, 0x8e200000u, 0x3fe6c5deu, 0x4eb62ef4u, 0x0000001fu, 0x5b177a38u, 0x5b177a38u},
|
||||
{0x30000000u, 0x41844f58u, 0x38000000u, 0x41844f58u, 0x30000000u, 0x41844f58u, 0x30000000u, 0x41844f58u, 0x00000000u, 0x3fd00000u, 0x32000000u, 0x3fe44f58u, 0x4c227ac2u, 0x0000001au, 0x0289eb06u, 0x0289eb06u},
|
||||
{0x00000000u, 0x40902800u, 0x00000000u, 0x40902c00u, 0x00000000u, 0x40902800u, 0x00000000u, 0x40902800u, 0x0c9b6000u, 0x3fdc3617u, 0x6170c9b6u, 0x3fe029c3u, 0x44814e1bu, 0x0000000bu, 0x0000040au, 0x0000040au},
|
||||
{0x1b800000u, 0x41c94321u, 0x1c000000u, 0x41c94321u, 0x1b800000u, 0x41c94321u, 0x1c000000u, 0x41c94321u, 0x00000000u, 0x3fe00000u, 0x1bc00000u, 0x3fe94321u, 0x4e4a1909u, 0x0000001eu, 0x32864237u, 0x32864237u},
|
||||
{0x00000000u, 0x40bd6500u, 0x00000000u, 0x40bd6600u, 0x00000000u, 0x40bd6500u, 0x00000000u, 0x40bd6500u, 0x22000000u, 0x3f62c150u, 0x960a8110u, 0x3fed6500u, 0x45eb2805u, 0x0000000du, 0x00001d65u, 0x00001d65u},
|
||||
{0x00000000u, 0x4110b770u, 0x00000000u, 0x4110b774u, 0x00000000u, 0x4110b770u, 0x00000000u, 0x4110b770u, 0xba800000u, 0x3fc7596du, 0xbacb6dd4u, 0x3fe0b770u, 0x4885bb86u, 0x00000013u, 0x00042ddcu, 0x00042ddcu},
|
||||
{0x00000000u, 0x40f9a560u, 0x00000000u, 0x40f9a570u, 0x00000000u, 0x40f9a560u, 0x00000000u, 0x40f9a570u, 0xca800000u, 0x3fef82ccu, 0xc1666540u, 0x3fe9a56fu, 0x47cd2b7eu, 0x00000011u, 0x00019a56u, 0x00019a56u},
|
||||
{0xdc000000u, 0x419599f6u, 0xe0000000u, 0x419599f6u, 0xdc000000u, 0x419599f6u, 0xe0000000u, 0x419599f6u, 0x10000000u, 0x3fed7ed3u, 0xdfafda62u, 0x3fe599f6u, 0x4caccfb7u, 0x0000001bu, 0x05667db7u, 0x05667db7u},
|
||||
{0x00000000u, 0x4063e000u, 0x00000000u, 0x40640000u, 0x00000000u, 0x4063e000u, 0x00000000u, 0x40640000u, 0x71247000u, 0x3fecf21au, 0x1a712470u, 0x3fe3fcf2u, 0x431fe791u, 0x00000008u, 0x0000009fu, 0x0000009fu},
|
||||
{0x00000000u, 0x412cb34eu, 0x00000000u, 0x412cb350u, 0x00000000u, 0x412cb34eu, 0x00000000u, 0x412cb34eu, 0xea400000u, 0x3fde4c09u, 0xf2604f52u, 0x3fecb34eu, 0x49659a78u, 0x00000014u, 0x000e59a7u, 0x000e59a7u},
|
||||
{0x00000000u, 0x405d8000u, 0x00000000u, 0x405dc000u, 0x00000000u, 0x405d8000u, 0x00000000u, 0x405dc000u, 0x704bf600u, 0x3fe8c915u, 0x2ae097ecu, 0x3fedb192u, 0x42ed8c91u, 0x00000007u, 0x00000076u, 0x00000076u},
|
||||
{0xa0000000u, 0x4176b608u, 0xb0000000u, 0x4176b608u, 0xa0000000u, 0x4176b608u, 0xa0000000u, 0x4176b608u, 0x88000000u, 0x3fdf6f6au, 0xa7dbdaa2u, 0x3fe6b608u, 0x4bb5b045u, 0x00000019u, 0x016b608au, 0x016b608au},
|
||||
{0x00000000u, 0x407b1000u, 0x00000000u, 0x407b2000u, 0x00000000u, 0x407b1000u, 0x00000000u, 0x407b1000u, 0xf9624000u, 0x3fd3127cu, 0x9f3e5890u, 0x3feb14c4u, 0x43d8a625u, 0x00000009u, 0x000001b1u, 0x000001b1u},
|
||||
{0x00000000u, 0x4147ce51u, 0x80000000u, 0x4147ce51u, 0x00000000u, 0x4147ce51u, 0x80000000u, 0x4147ce51u, 0xa5000000u, 0x3fe41e56u, 0x50795a94u, 0x3fe7ce51u, 0x4a3e728bu, 0x00000016u, 0x002f9ca2u, 0x002f9ca2u},
|
||||
{0x00000000u, 0x40180000u, 0x00000000u, 0x401c0000u, 0x00000000u, 0x40180000u, 0x00000000u, 0x40180000u, 0x9e8099c0u, 0x3fd8ad81u, 0x19e8099cu, 0x3fe98ad8u, 0x40cc56c1u, 0x00000003u, 0x00000006u, 0x00000006u},
|
||||
{0x00000000u, 0x413adf07u, 0x00000000u, 0x413adf08u, 0x00000000u, 0x413adf07u, 0x00000000u, 0x413adf07u, 0xa0000000u, 0x3f9d998eu, 0x076663a8u, 0x3feadf07u, 0x49d6f838u, 0x00000015u, 0x001adf07u, 0x001adf07u},
|
||||
{0xe6000000u, 0x41ada4d6u, 0xe6000000u, 0x41ada4d6u, 0xe6000000u, 0x41ada4d6u, 0xe6000000u, 0x41ada4d6u, 0x00000000u, 0x00000000u, 0xe6000000u, 0x3feda4d6u, 0x4d6d26b7u, 0x0000001cu, 0x0ed26b73u, 0x0ed26b73u},
|
||||
{0x00000000u, 0x40c06400u, 0x00000000u, 0x40c06480u, 0x00000000u, 0x40c06400u, 0x00000000u, 0x40c06400u, 0x77400000u, 0x3f88ce86u, 0x8ce86774u, 0x3fe06401u, 0x4603200cu, 0x0000000eu, 0x000020c8u, 0x000020c8u},
|
||||
{0x00000000u, 0x40d1d240u, 0x00000000u, 0x40d1d280u, 0x00000000u, 0x40d1d240u, 0x00000000u, 0x40d1d280u, 0x18080000u, 0x3fe34c5cu, 0x98b83010u, 0x3fe1d266u, 0x468e9335u, 0x0000000fu, 0x00004749u, 0x00004749u}
|
||||
};
|
||||
constexpr const char* Names[16] = {"trunc lo", "trunc hi", "ceil lo", "ceil hi", "floor lo", "floor hi", "rndne lo", "rndne hi", "fract lo", "fract hi", "frexp mant lo", "frexp mant hi", "cvt f32", "frexp exp", "cvt i32", "cvt u32"};
|
||||
|
||||
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("f64 rounding: 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("f64 rounding tests passed");
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << error.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +73,7 @@ function(add_shader_recompiler target runtimeTarget)
|
|||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/ScalarInstructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/VectorInstructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/FloatInstructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/Float64Instructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/IntegerInstructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/CompareInstructions.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Translation/src/ControlFlowInstructions.cpp
|
||||
|
|
@ -115,6 +116,7 @@ function(add_shader_recompiler target runtimeTarget)
|
|||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvModule.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAnalysis.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAlu/SpirvAluEmitterMath.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAlu/SpirvAluEmitterRegisters.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvFlowEmitter.cpp
|
||||
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvImageEmitter.cpp
|
||||
|
|
|
|||
|
|
@ -245,6 +245,26 @@ enum class IrOpcode : std::uint16_t {
|
|||
FPCeil32,
|
||||
FPTrunc32,
|
||||
FPFract32,
|
||||
FPAdd64,
|
||||
FPMul64,
|
||||
FPFma64,
|
||||
FPMin64,
|
||||
FPMax64,
|
||||
FPSaturate64,
|
||||
FPLdexp64,
|
||||
FPRoundEven64,
|
||||
FPFloor64,
|
||||
FPCeil64,
|
||||
FPTrunc64,
|
||||
FPFract64,
|
||||
FPFrexpMant64,
|
||||
FPFrexpExp64,
|
||||
ConvertF32F64,
|
||||
ConvertF64F32,
|
||||
ConvertF64S32,
|
||||
ConvertF64U32,
|
||||
ConvertS32F64,
|
||||
ConvertU32F64,
|
||||
LaneId,
|
||||
WriteLane,
|
||||
Permlane16U32,
|
||||
|
|
@ -409,6 +429,7 @@ struct ExportFlags {
|
|||
[[nodiscard]] IrType IrOpcodeType(IrOpcode opcode);
|
||||
[[nodiscard]] IrType IrOpcodeArgumentType(IrOpcode opcode, std::size_t index);
|
||||
[[nodiscard]] bool IrOpcodeHasSideEffects(IrOpcode opcode);
|
||||
[[nodiscard]] bool IsFloat64Opcode(IrOpcode opcode);
|
||||
[[nodiscard]] BufferAccess BufferAccessOf(IrOpcode opcode);
|
||||
[[nodiscard]] std::uint32_t BufferComponentCount(IrOpcode opcode);
|
||||
[[nodiscard]] SharedAccess SharedAccessOf(IrOpcode opcode);
|
||||
|
|
|
|||
|
|
@ -286,6 +286,26 @@ constexpr std::array<OpcodeMeta, static_cast<std::size_t>(IrOpcode::Count)> Meta
|
|||
makeMeta("FPCeil32", F32, F32),
|
||||
makeMeta("FPTrunc32", F32, F32),
|
||||
makeMeta("FPFract32", F32, F32),
|
||||
makeMeta("FPAdd64", U64, U64, U64),
|
||||
makeMeta("FPMul64", U64, U64, U64),
|
||||
makeMeta("FPFma64", U64, U64, U64, U64),
|
||||
makeMeta("FPMin64", U64, U64, U64),
|
||||
makeMeta("FPMax64", U64, U64, U64),
|
||||
makeMeta("FPSaturate64", U64, U64),
|
||||
makeMeta("FPLdexp64", U64, U64, U32),
|
||||
makeMeta("FPRoundEven64", U64, U64),
|
||||
makeMeta("FPFloor64", U64, U64),
|
||||
makeMeta("FPCeil64", U64, U64),
|
||||
makeMeta("FPTrunc64", U64, U64),
|
||||
makeMeta("FPFract64", U64, U64),
|
||||
makeMeta("FPFrexpMant64", U64, U64),
|
||||
makeMeta("FPFrexpExp64", U32, U64),
|
||||
makeMeta("ConvertF32F64", F32, U64),
|
||||
makeMeta("ConvertF64F32", U64, F32),
|
||||
makeMeta("ConvertF64S32", U64, U32),
|
||||
makeMeta("ConvertF64U32", U64, U32),
|
||||
makeMeta("ConvertS32F64", U32, U64),
|
||||
makeMeta("ConvertU32F64", U32, U64),
|
||||
makeMeta("LaneId", U32),
|
||||
makeMeta("WriteLane", U32, U32, U32, U32),
|
||||
makeMeta("Permlane16U32", U32, U32, U32, U32, U1),
|
||||
|
|
@ -609,6 +629,10 @@ ImageOpcodeInfo ImageOpcodeInfoOf(IrOpcode opcode) {
|
|||
}
|
||||
}
|
||||
|
||||
bool IsFloat64Opcode(IrOpcode opcode) {
|
||||
return opcode >= IrOpcode::FPAdd64 && opcode <= IrOpcode::ConvertU32F64;
|
||||
}
|
||||
|
||||
bool IrOpcodeHasSideEffects(IrOpcode opcode) {
|
||||
const BufferAccess bufferAccess = BufferAccessOf(opcode);
|
||||
if (bufferAccess == BufferAccess::Write || bufferAccess == BufferAccess::Atomic) {
|
||||
|
|
|
|||
|
|
@ -384,6 +384,25 @@ enum class RdnaOpcode : std::uint16_t {
|
|||
VSatPkU8I16,
|
||||
VMulLegacyF32,
|
||||
VMacLegacyF32,
|
||||
VFmaF64,
|
||||
VAddF64,
|
||||
VMulF64,
|
||||
VMinF64,
|
||||
VMaxF64,
|
||||
VLdexpF64,
|
||||
VCvtI32F64,
|
||||
VCvtF64I32,
|
||||
VCvtF32F64,
|
||||
VCvtF64F32,
|
||||
VCvtU32F64,
|
||||
VCvtF64U32,
|
||||
VTruncF64,
|
||||
VCeilF64,
|
||||
VRndneF64,
|
||||
VFloorF64,
|
||||
VFrexpExpI32F64,
|
||||
VFrexpMantF64,
|
||||
VFractF64,
|
||||
VCvtPknormI16F32,
|
||||
VCvtPknormU16F32,
|
||||
VCvtPkU16U32,
|
||||
|
|
|
|||
|
|
@ -334,6 +334,25 @@ bool IsVectorAluOpcode(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VSatPkU8I16:
|
||||
case RdnaOpcode::VMulLegacyF32:
|
||||
case RdnaOpcode::VMacLegacyF32:
|
||||
case RdnaOpcode::VFmaF64:
|
||||
case RdnaOpcode::VAddF64:
|
||||
case RdnaOpcode::VMulF64:
|
||||
case RdnaOpcode::VMinF64:
|
||||
case RdnaOpcode::VMaxF64:
|
||||
case RdnaOpcode::VLdexpF64:
|
||||
case RdnaOpcode::VCvtI32F64:
|
||||
case RdnaOpcode::VCvtF64I32:
|
||||
case RdnaOpcode::VCvtF32F64:
|
||||
case RdnaOpcode::VCvtF64F32:
|
||||
case RdnaOpcode::VCvtU32F64:
|
||||
case RdnaOpcode::VCvtF64U32:
|
||||
case RdnaOpcode::VTruncF64:
|
||||
case RdnaOpcode::VCeilF64:
|
||||
case RdnaOpcode::VRndneF64:
|
||||
case RdnaOpcode::VFloorF64:
|
||||
case RdnaOpcode::VFrexpExpI32F64:
|
||||
case RdnaOpcode::VFrexpMantF64:
|
||||
case RdnaOpcode::VFractF64:
|
||||
case RdnaOpcode::VCvtPknormI16F32:
|
||||
case RdnaOpcode::VCvtPknormU16F32:
|
||||
case RdnaOpcode::VCvtPkU16U32:
|
||||
|
|
|
|||
|
|
@ -128,6 +128,19 @@ constexpr VectorOpcodeInfo vop1Opcodes[] = {
|
|||
{0x22u, RdnaOpcode::VCeilF32},
|
||||
{0x23u, RdnaOpcode::VRndneF32},
|
||||
{0x24u, RdnaOpcode::VFloorF32},
|
||||
{0x03u, RdnaOpcode::VCvtI32F64},
|
||||
{0x04u, RdnaOpcode::VCvtF64I32},
|
||||
{0x0fu, RdnaOpcode::VCvtF32F64},
|
||||
{0x10u, RdnaOpcode::VCvtF64F32},
|
||||
{0x15u, RdnaOpcode::VCvtU32F64},
|
||||
{0x16u, RdnaOpcode::VCvtF64U32},
|
||||
{0x17u, RdnaOpcode::VTruncF64},
|
||||
{0x18u, RdnaOpcode::VCeilF64},
|
||||
{0x19u, RdnaOpcode::VRndneF64},
|
||||
{0x1au, RdnaOpcode::VFloorF64},
|
||||
{0x3cu, RdnaOpcode::VFrexpExpI32F64},
|
||||
{0x3du, RdnaOpcode::VFrexpMantF64},
|
||||
{0x3eu, RdnaOpcode::VFractF64},
|
||||
{0x25u, RdnaOpcode::VExpF32},
|
||||
{0x27u, RdnaOpcode::VLogF32},
|
||||
{0x2bu, RdnaOpcode::VRcpIflagF32},
|
||||
|
|
@ -192,6 +205,19 @@ constexpr VectorOpcodeInfo vop3EncodedVop1Opcodes[] = {
|
|||
{0x22u, RdnaOpcode::VCeilF32},
|
||||
{0x23u, RdnaOpcode::VRndneF32},
|
||||
{0x24u, RdnaOpcode::VFloorF32},
|
||||
{0x03u, RdnaOpcode::VCvtI32F64},
|
||||
{0x04u, RdnaOpcode::VCvtF64I32},
|
||||
{0x0fu, RdnaOpcode::VCvtF32F64},
|
||||
{0x10u, RdnaOpcode::VCvtF64F32},
|
||||
{0x15u, RdnaOpcode::VCvtU32F64},
|
||||
{0x16u, RdnaOpcode::VCvtF64U32},
|
||||
{0x17u, RdnaOpcode::VTruncF64},
|
||||
{0x18u, RdnaOpcode::VCeilF64},
|
||||
{0x19u, RdnaOpcode::VRndneF64},
|
||||
{0x1au, RdnaOpcode::VFloorF64},
|
||||
{0x3cu, RdnaOpcode::VFrexpExpI32F64},
|
||||
{0x3du, RdnaOpcode::VFrexpMantF64},
|
||||
{0x3eu, RdnaOpcode::VFractF64},
|
||||
{0x25u, RdnaOpcode::VExpF32},
|
||||
{0x27u, RdnaOpcode::VLogF32},
|
||||
{0x2bu, RdnaOpcode::VRcpIflagF32},
|
||||
|
|
@ -510,6 +536,12 @@ constexpr VectorOpcodeInfo vop3Opcodes[] = {
|
|||
{0x362u, RdnaOpcode::VLdexpF32},
|
||||
{0x312u, RdnaOpcode::VCvtPknormI16F16},
|
||||
{0x313u, RdnaOpcode::VCvtPknormU16F16},
|
||||
{0x14cu, RdnaOpcode::VFmaF64},
|
||||
{0x164u, RdnaOpcode::VAddF64},
|
||||
{0x165u, RdnaOpcode::VMulF64},
|
||||
{0x166u, RdnaOpcode::VMinF64},
|
||||
{0x167u, RdnaOpcode::VMaxF64},
|
||||
{0x168u, RdnaOpcode::VLdexpF64},
|
||||
{0x363u, RdnaOpcode::VBfmB32},
|
||||
{0x364u, RdnaOpcode::VBcntU32B32},
|
||||
{0x365u, RdnaOpcode::VMbcntLoU32B32},
|
||||
|
|
@ -705,6 +737,17 @@ bool isVop1FloatSourceOpcode(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VCeilF32:
|
||||
case RdnaOpcode::VRndneF32:
|
||||
case RdnaOpcode::VFloorF32:
|
||||
case RdnaOpcode::VCvtI32F64:
|
||||
case RdnaOpcode::VCvtF32F64:
|
||||
case RdnaOpcode::VCvtU32F64:
|
||||
case RdnaOpcode::VCvtF64F32:
|
||||
case RdnaOpcode::VTruncF64:
|
||||
case RdnaOpcode::VCeilF64:
|
||||
case RdnaOpcode::VRndneF64:
|
||||
case RdnaOpcode::VFloorF64:
|
||||
case RdnaOpcode::VFrexpExpI32F64:
|
||||
case RdnaOpcode::VFrexpMantF64:
|
||||
case RdnaOpcode::VFractF64:
|
||||
case RdnaOpcode::VExpF32:
|
||||
case RdnaOpcode::VLogF32:
|
||||
case RdnaOpcode::VRsqF32:
|
||||
|
|
@ -747,6 +790,16 @@ bool isVop1FloatResultOpcode(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VCeilF32:
|
||||
case RdnaOpcode::VRndneF32:
|
||||
case RdnaOpcode::VFloorF32:
|
||||
case RdnaOpcode::VCvtF64I32:
|
||||
case RdnaOpcode::VCvtF64U32:
|
||||
case RdnaOpcode::VCvtF32F64:
|
||||
case RdnaOpcode::VCvtF64F32:
|
||||
case RdnaOpcode::VTruncF64:
|
||||
case RdnaOpcode::VCeilF64:
|
||||
case RdnaOpcode::VRndneF64:
|
||||
case RdnaOpcode::VFloorF64:
|
||||
case RdnaOpcode::VFrexpMantF64:
|
||||
case RdnaOpcode::VFractF64:
|
||||
case RdnaOpcode::VFrexpMantF32:
|
||||
case RdnaOpcode::VExpF32:
|
||||
case RdnaOpcode::VLogF32:
|
||||
|
|
@ -1527,6 +1580,11 @@ std::uint32_t nativeVop3SourceCount(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VWritelaneB32:
|
||||
case RdnaOpcode::VCvtPkrtzF16F32:
|
||||
case RdnaOpcode::VLdexpF32:
|
||||
case RdnaOpcode::VAddF64:
|
||||
case RdnaOpcode::VMulF64:
|
||||
case RdnaOpcode::VMinF64:
|
||||
case RdnaOpcode::VMaxF64:
|
||||
case RdnaOpcode::VLdexpF64:
|
||||
case RdnaOpcode::VBfmB32:
|
||||
case RdnaOpcode::VBcntU32B32:
|
||||
case RdnaOpcode::VCvtPknormI16F32:
|
||||
|
|
@ -1693,7 +1751,13 @@ bool supportsNativeVop3SourceModifiers(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VMin3F32:
|
||||
case RdnaOpcode::VMax3F32:
|
||||
case RdnaOpcode::VMed3F32:
|
||||
case RdnaOpcode::VLdexpF32: return true;
|
||||
case RdnaOpcode::VLdexpF32:
|
||||
case RdnaOpcode::VFmaF64:
|
||||
case RdnaOpcode::VAddF64:
|
||||
case RdnaOpcode::VMulF64:
|
||||
case RdnaOpcode::VMinF64:
|
||||
case RdnaOpcode::VMaxF64:
|
||||
case RdnaOpcode::VLdexpF64: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1715,6 +1779,12 @@ bool supportsNativeVop3ResultModifiers(RdnaOpcode opcode) {
|
|||
case RdnaOpcode::VFmaF16:
|
||||
case RdnaOpcode::VCvtPkrtzF16F32:
|
||||
case RdnaOpcode::VLdexpF32:
|
||||
case RdnaOpcode::VFmaF64:
|
||||
case RdnaOpcode::VAddF64:
|
||||
case RdnaOpcode::VMulF64:
|
||||
case RdnaOpcode::VMinF64:
|
||||
case RdnaOpcode::VMaxF64:
|
||||
case RdnaOpcode::VLdexpF64:
|
||||
case RdnaOpcode::VMin3F32:
|
||||
case RdnaOpcode::VMax3F32:
|
||||
case RdnaOpcode::VMed3F32: return true;
|
||||
|
|
@ -1764,7 +1834,7 @@ void checkNativeVop3Modifiers(RdnaOpcode opcode, bool permlane, bool carryInOut,
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (opcode == RdnaOpcode::VLdexpF32) {
|
||||
if (opcode == RdnaOpcode::VLdexpF32 || opcode == RdnaOpcode::VLdexpF64) {
|
||||
if ((abs & ~1u) != 0u || opSel != 0u || (neg & ~1u) != 0u) {
|
||||
throw std::invalid_argument("VOP3 source modifiers are not implemented");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ struct SpirvRequirements {
|
|||
bool functionScratch = false;
|
||||
bool pixelValidMask = false;
|
||||
bool bufferInt64Atomics = false;
|
||||
bool float64 = false;
|
||||
bool coherentBuffers = false;
|
||||
std::vector<std::uint32_t> capabilities;
|
||||
std::vector<std::string> extensions;
|
||||
|
|
|
|||
|
|
@ -179,6 +179,26 @@ std::uint32_t EmitFPFloor32(SpirvEmitterState& state, std::uint32_t arg0);
|
|||
std::uint32_t EmitFPCeil32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPTrunc32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPFract32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPAdd64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint32_t EmitFPMul64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint32_t EmitFPFma64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1, std::uint32_t arg2);
|
||||
std::uint32_t EmitFPMin64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint32_t EmitFPMax64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint32_t EmitFPSaturate64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPLdexp64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint32_t EmitFPRoundEven64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPFloor64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPCeil64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPTrunc64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPFract64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPFrexpMant64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitFPFrexpExp64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertF32F64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertF64F32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertF64S32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertF64U32(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertS32F64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertU32F64(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitBitCastF16U16(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertU32U16(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
std::uint32_t EmitConvertU32U8(SpirvEmitterState& state, std::uint32_t arg0);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,290 @@
|
|||
#include "SpirvBackend/SpirvEmitterInstructions.hpp"
|
||||
#include <spirv/unified1/GLSL.std.450.h>
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
namespace {
|
||||
|
||||
struct F64Bits {
|
||||
std::uint32_t low = 0;
|
||||
std::uint32_t high = 0;
|
||||
};
|
||||
|
||||
std::uint32_t TypeF64(SpirvEmitterState& state) {
|
||||
return state.module.Type(spv::OpTypeFloat, 64u);
|
||||
}
|
||||
|
||||
std::uint32_t ConstantF64(SpirvEmitterState& state, std::uint64_t bits) {
|
||||
return state.module.Constant(spv::OpConstant, TypeF64(state), static_cast<std::uint32_t>(bits), static_cast<std::uint32_t>(bits >> 32u));
|
||||
}
|
||||
|
||||
std::uint32_t ToF64(SpirvEmitterState& state, std::uint32_t bits) {
|
||||
return Unary(state, spv::OpBitcast, TypeF64(state), bits);
|
||||
}
|
||||
|
||||
std::uint32_t FromF64(SpirvEmitterState& state, std::uint32_t value) {
|
||||
return Unary(state, spv::OpBitcast, TypeU64(state), value);
|
||||
}
|
||||
|
||||
F64Bits Split(SpirvEmitterState& state, std::uint32_t bits) {
|
||||
F64Bits result{state.module.AllocateId(), state.module.AllocateId()};
|
||||
state.module.AddFunction(spv::OpCompositeExtract, TypeU32(state), result.low, bits, 0u);
|
||||
state.module.AddFunction(spv::OpCompositeExtract, TypeU32(state), result.high, bits, 1u);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::uint32_t Join(SpirvEmitterState& state, F64Bits bits) {
|
||||
const auto result = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpCompositeConstruct, TypeU64(state), result, bits.low, bits.high);
|
||||
return result;
|
||||
}
|
||||
|
||||
F64Bits SelectBits(SpirvEmitterState& state, std::uint32_t condition, F64Bits trueValue, F64Bits falseValue) {
|
||||
return {Select(state, TypeU32(state), condition, trueValue.low, falseValue.low), Select(state, TypeU32(state), condition, trueValue.high, falseValue.high)};
|
||||
}
|
||||
|
||||
std::uint32_t Exact(SpirvEmitterState& state, std::uint32_t opcode, std::uint32_t lhs, std::uint32_t rhs) {
|
||||
const auto result = Binary(state, opcode, TypeF64(state), lhs, rhs);
|
||||
state.module.AddAnnotation(spv::OpDecorate, result, spv::DecorationNoContraction);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename... TArguments>
|
||||
std::uint32_t Glsl(SpirvEmitterState& state, std::uint32_t type, std::uint32_t opcode, TArguments... arguments) {
|
||||
const auto result = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpExtInst, type, result, GlslStd450(state), opcode, arguments...);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct F64Class {
|
||||
F64Bits bits;
|
||||
std::uint32_t nan = 0;
|
||||
std::uint32_t signalingNan = 0;
|
||||
std::uint32_t zero = 0;
|
||||
std::uint32_t negative = 0;
|
||||
};
|
||||
|
||||
F64Class Classify(SpirvEmitterState& state, std::uint32_t value) {
|
||||
F64Class cls;
|
||||
cls.bits = Split(state, value);
|
||||
const auto absHigh = Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, ConstantU32(state, 0x7fffffffu));
|
||||
const auto aboveInf = Binary(state, spv::OpUGreaterThan, TypeBool(state), absHigh, ConstantU32(state, 0x7ff00000u));
|
||||
const auto infHigh = Binary(state, spv::OpIEqual, TypeBool(state), absHigh, ConstantU32(state, 0x7ff00000u));
|
||||
const auto lowNonzero = Binary(state, spv::OpINotEqual, TypeBool(state), cls.bits.low, ConstantU32(state, 0u));
|
||||
cls.nan = Binary(state, spv::OpLogicalOr, TypeBool(state), aboveInf, Binary(state, spv::OpLogicalAnd, TypeBool(state), infHigh, lowNonzero));
|
||||
const auto quietBit = Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, ConstantU32(state, 0x00080000u));
|
||||
const auto signaling = Binary(state, spv::OpIEqual, TypeBool(state), quietBit, ConstantU32(state, 0u));
|
||||
cls.signalingNan = Binary(state, spv::OpLogicalAnd, TypeBool(state), cls.nan, signaling);
|
||||
const auto magnitude = Binary(state, spv::OpBitwiseOr, TypeU32(state), absHigh, cls.bits.low);
|
||||
cls.zero = Binary(state, spv::OpIEqual, TypeBool(state), magnitude, ConstantU32(state, 0u));
|
||||
cls.negative = Binary(state, spv::OpINotEqual, TypeBool(state), absHigh, cls.bits.high);
|
||||
return cls;
|
||||
}
|
||||
|
||||
F64Bits Quiet(SpirvEmitterState& state, F64Bits bits) {
|
||||
return {bits.low, Binary(state, spv::OpBitwiseOr, TypeU32(state), bits.high, ConstantU32(state, 0x00080000u))};
|
||||
}
|
||||
|
||||
std::uint32_t QuietResult(SpirvEmitterState& state, std::uint32_t value) {
|
||||
const auto cls = Classify(state, FromF64(state, value));
|
||||
return Join(state, SelectBits(state, cls.nan, Quiet(state, cls.bits), cls.bits));
|
||||
}
|
||||
|
||||
std::uint32_t MinMax(SpirvEmitterState& state, std::uint32_t lhs, std::uint32_t rhs, bool maxValue) {
|
||||
const auto a = Classify(state, lhs);
|
||||
const auto b = Classify(state, rhs);
|
||||
const auto ordered = Binary(state, maxValue ? spv::OpFOrdGreaterThan : spv::OpFOrdLessThan, TypeBool(state), ToF64(state, lhs), ToF64(state, rhs));
|
||||
auto result = SelectBits(state, ordered, a.bits, b.bits);
|
||||
const auto bothZero = Binary(state, spv::OpLogicalAnd, TypeBool(state), a.zero, b.zero);
|
||||
const auto zero = maxValue ? SelectBits(state, a.negative, b.bits, a.bits) : SelectBits(state, a.negative, a.bits, b.bits);
|
||||
result = SelectBits(state, bothZero, zero, result);
|
||||
result = SelectBits(state, a.nan, b.bits, result);
|
||||
result = SelectBits(state, b.nan, a.bits, result);
|
||||
result = SelectBits(state, b.signalingNan, Quiet(state, b.bits), result);
|
||||
result = SelectBits(state, a.signalingNan, Quiet(state, a.bits), result);
|
||||
return Join(state, result);
|
||||
}
|
||||
|
||||
std::uint32_t Trunc(SpirvEmitterState& state, std::uint32_t value) {
|
||||
const auto cls = Classify(state, value);
|
||||
const auto exponent = Binary(state, spv::OpBitwiseAnd, TypeU32(state), Binary(state, spv::OpShiftRightLogical, TypeU32(state), cls.bits.high, ConstantU32(state, 20u)), ConstantU32(state, 0x7ffu));
|
||||
const auto fractionBits = Binary(state, spv::OpISub, TypeU32(state), ConstantU32(state, 1075u), exponent);
|
||||
const auto highShift = Binary(state, spv::OpISub, TypeU32(state), Glsl(state, TypeU32(state), GLSLstd450UMax, fractionBits, ConstantU32(state, 32u)), ConstantU32(state, 32u));
|
||||
const auto lowShift = Glsl(state, TypeU32(state), GLSLstd450UMin, fractionBits, ConstantU32(state, 31u));
|
||||
const auto wideFraction = Binary(state, spv::OpUGreaterThanEqual, TypeBool(state), fractionBits, ConstantU32(state, 32u));
|
||||
const auto lowMask = Select(state, TypeU32(state), wideFraction, ConstantU32(state, 0u), Binary(state, spv::OpShiftLeftLogical, TypeU32(state), ConstantU32(state, 0xffffffffu), lowShift));
|
||||
const auto highMask = Binary(state, spv::OpShiftLeftLogical, TypeU32(state), ConstantU32(state, 0xffffffffu), highShift);
|
||||
const F64Bits masked{Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.low, lowMask), Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, highMask)};
|
||||
const F64Bits signedZero{ConstantU32(state, 0u), Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, ConstantU32(state, 0x80000000u))};
|
||||
const auto belowOne = Binary(state, spv::OpULessThan, TypeBool(state), exponent, ConstantU32(state, 1023u));
|
||||
const auto integral = Binary(state, spv::OpUGreaterThanEqual, TypeBool(state), exponent, ConstantU32(state, 1075u));
|
||||
auto result = SelectBits(state, belowOne, signedZero, masked);
|
||||
result = SelectBits(state, integral, cls.bits, result);
|
||||
return ToF64(state, Join(state, SelectBits(state, cls.nan, Quiet(state, cls.bits), result)));
|
||||
}
|
||||
|
||||
std::uint32_t Floor(SpirvEmitterState& state, std::uint32_t bits) {
|
||||
const auto truncated = Trunc(state, bits);
|
||||
const auto below = Binary(state, spv::OpFOrdLessThan, TypeBool(state), ToF64(state, bits), truncated);
|
||||
return Select(state, TypeF64(state), below, Exact(state, spv::OpFSub, truncated, ConstantF64(state, 0x3ff0000000000000ull)), truncated);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPAdd64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1) {
|
||||
return QuietResult(state, Exact(state, spv::OpFAdd, ToF64(state, arg0), ToF64(state, arg1)));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPMul64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1) {
|
||||
return QuietResult(state, Exact(state, spv::OpFMul, ToF64(state, arg0), ToF64(state, arg1)));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPFma64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1, std::uint32_t arg2) {
|
||||
const auto result = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpExtInst, TypeF64(state), result, GlslStd450(state), GLSLstd450Fma, ToF64(state, arg0), ToF64(state, arg1), ToF64(state, arg2));
|
||||
state.module.AddAnnotation(spv::OpDecorate, result, spv::DecorationNoContraction);
|
||||
return QuietResult(state, result);
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPMin64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1) {
|
||||
return MinMax(state, arg0, arg1, false);
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPMax64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1) {
|
||||
return MinMax(state, arg0, arg1, true);
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPSaturate64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto value = ToF64(state, arg0);
|
||||
const auto positive = Binary(state, spv::OpFOrdGreaterThan, TypeBool(state), value, ConstantF64(state, 0u));
|
||||
const auto belowOne = Binary(state, spv::OpFOrdLessThan, TypeBool(state), value, ConstantF64(state, 0x3ff0000000000000ull));
|
||||
const auto upper = Select(state, TypeF64(state), belowOne, value, ConstantF64(state, 0x3ff0000000000000ull));
|
||||
return FromF64(state, Select(state, TypeF64(state), positive, upper, ConstantF64(state, 0u)));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPLdexp64(SpirvEmitterState& state, std::uint32_t arg0, std::uint32_t arg1) {
|
||||
const auto cls = Classify(state, arg0);
|
||||
const auto exponentOf = [&](std::uint32_t high) {
|
||||
return Binary(state, spv::OpBitwiseAnd, TypeU32(state), Binary(state, spv::OpShiftRightLogical, TypeU32(state), high, ConstantU32(state, 20u)), ConstantU32(state, 0x7ffu));
|
||||
};
|
||||
const auto subnormal = Binary(state, spv::OpIEqual, TypeBool(state), exponentOf(cls.bits.high), ConstantU32(state, 0u));
|
||||
const auto scaled = Exact(state, spv::OpFMul, ToF64(state, arg0), ConstantF64(state, 0x43f0000000000000ull));
|
||||
const auto normal = Split(state, FromF64(state, Select(state, TypeF64(state), subnormal, scaled, ToF64(state, arg0))));
|
||||
const auto bias = Select(state, TypeU32(state), subnormal, ConstantU32(state, 1022u + 64u), ConstantU32(state, 1022u));
|
||||
const auto exponent = Glsl(state, TypeI32(state), GLSLstd450SClamp, Unary(state, spv::OpBitcast, TypeI32(state), arg1), ConstantI32(state, -2200), ConstantI32(state, 2200));
|
||||
const auto shift = Binary(state, spv::OpISub, TypeU32(state), exponentOf(normal.high), bias);
|
||||
const auto total = Glsl(state, TypeI32(state), GLSLstd450SClamp, Binary(state, spv::OpIAdd, TypeI32(state), Unary(state, spv::OpBitcast, TypeI32(state), shift), exponent), ConstantI32(state, -1100), ConstantI32(state, 1100));
|
||||
const auto mantissa = Join(state, {normal.low, Binary(state, spv::OpBitwiseOr, TypeU32(state), Binary(state, spv::OpBitwiseAnd, TypeU32(state), normal.high, ConstantU32(state, 0x800fffffu)), ConstantU32(state, 1022u << 20u))});
|
||||
const auto power = [&](std::uint32_t value) {
|
||||
const auto biased = Binary(state, spv::OpIAdd, TypeI32(state), value, ConstantI32(state, 1023));
|
||||
const auto high = Binary(state, spv::OpShiftLeftLogical, TypeU32(state), Unary(state, spv::OpBitcast, TypeU32(state), biased), ConstantU32(state, 20u));
|
||||
return ToF64(state, Join(state, {ConstantU32(state, 0u), high}));
|
||||
};
|
||||
const auto first = Glsl(state, TypeI32(state), GLSLstd450SClamp, total, ConstantI32(state, -1021), ConstantI32(state, 1023));
|
||||
const auto second = Binary(state, spv::OpISub, TypeI32(state), total, first);
|
||||
const auto exact = Exact(state, spv::OpFMul, ToF64(state, mantissa), power(first));
|
||||
const auto result = Split(state, FromF64(state, Exact(state, spv::OpFMul, exact, power(Glsl(state, TypeI32(state), GLSLstd450SMax, second, ConstantI32(state, -60))))));
|
||||
const auto special = Binary(state, spv::OpIEqual, TypeBool(state), exponentOf(cls.bits.high), ConstantU32(state, 0x7ffu));
|
||||
const auto passthrough = Binary(state, spv::OpLogicalOr, TypeBool(state), cls.zero, special);
|
||||
return Join(state, SelectBits(state, passthrough, SelectBits(state, cls.nan, Quiet(state, cls.bits), cls.bits), result));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPRoundEven64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto cls = Classify(state, arg0);
|
||||
const auto value = ToF64(state, arg0);
|
||||
const auto magnitude = Glsl(state, TypeF64(state), GLSLstd450FAbs, value);
|
||||
const auto shift = ConstantF64(state, 0x4330000000000000ull);
|
||||
const auto rounded = Split(state, FromF64(state, Exact(state, spv::OpFSub, Exact(state, spv::OpFAdd, magnitude, shift), shift)));
|
||||
const auto sign = Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, ConstantU32(state, 0x80000000u));
|
||||
const F64Bits signedRounded{rounded.low, Binary(state, spv::OpBitwiseOr, TypeU32(state), rounded.high, sign)};
|
||||
const auto integral = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), magnitude, shift);
|
||||
const auto result = SelectBits(state, integral, cls.bits, signedRounded);
|
||||
return Join(state, SelectBits(state, cls.nan, Quiet(state, cls.bits), result));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPFloor64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
return FromF64(state, Floor(state, arg0));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPCeil64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto truncated = Trunc(state, arg0);
|
||||
const auto above = Binary(state, spv::OpFOrdGreaterThan, TypeBool(state), ToF64(state, arg0), truncated);
|
||||
return FromF64(state, Select(state, TypeF64(state), above, Exact(state, spv::OpFAdd, truncated, ConstantF64(state, 0x3ff0000000000000ull)), truncated));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPTrunc64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
return FromF64(state, Trunc(state, arg0));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPFract64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto value = ToF64(state, arg0);
|
||||
const auto fraction = Exact(state, spv::OpFSub, value, Floor(state, arg0));
|
||||
const auto belowOne = ConstantF64(state, 0x3fefffffffffffffull);
|
||||
const auto overflow = Binary(state, spv::OpFOrdGreaterThan, TypeBool(state), fraction, belowOne);
|
||||
return QuietResult(state, Select(state, TypeF64(state), overflow, belowOne, fraction));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPFrexpMant64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto cls = Classify(state, arg0);
|
||||
const auto split = Glsl(state, state.module.Type(spv::OpTypeStruct, TypeF64(state), TypeI32(state)), GLSLstd450FrexpStruct, ToF64(state, arg0));
|
||||
const auto mantissa = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpCompositeExtract, TypeF64(state), mantissa, split, 0u);
|
||||
const auto exponentBits = Binary(state, spv::OpBitwiseAnd, TypeU32(state), cls.bits.high, ConstantU32(state, 0x7ff00000u));
|
||||
const auto special = Binary(state, spv::OpIEqual, TypeBool(state), exponentBits, ConstantU32(state, 0x7ff00000u));
|
||||
const auto passthrough = SelectBits(state, cls.nan, Quiet(state, cls.bits), cls.bits);
|
||||
return Join(state, SelectBits(state, special, passthrough, Split(state, FromF64(state, mantissa))));
|
||||
}
|
||||
|
||||
std::uint32_t EmitFPFrexpExp64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto bits = Split(state, arg0);
|
||||
const auto split = Glsl(state, state.module.Type(spv::OpTypeStruct, TypeF64(state), TypeI32(state)), GLSLstd450FrexpStruct, ToF64(state, arg0));
|
||||
const auto exponent = state.module.AllocateId();
|
||||
state.module.AddFunction(spv::OpCompositeExtract, TypeI32(state), exponent, split, 1u);
|
||||
const auto exponentBits = Binary(state, spv::OpBitwiseAnd, TypeU32(state), bits.high, ConstantU32(state, 0x7ff00000u));
|
||||
const auto special = Binary(state, spv::OpIEqual, TypeBool(state), exponentBits, ConstantU32(state, 0x7ff00000u));
|
||||
return Select(state, TypeU32(state), special, ConstantU32(state, 0u), Unary(state, spv::OpBitcast, TypeU32(state), exponent));
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertF32F64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto converted = Unary(state, spv::OpBitcast, TypeU32(state), Unary(state, spv::OpFConvert, TypeF32(state), ToF64(state, arg0)));
|
||||
const auto quiet = Binary(state, spv::OpBitwiseOr, TypeU32(state), converted, ConstantU32(state, 0x00400000u));
|
||||
return Unary(state, spv::OpBitcast, TypeF32(state), Select(state, TypeU32(state), Classify(state, arg0).nan, quiet, converted));
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertF64F32(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto converted = Split(state, FromF64(state, Unary(state, spv::OpFConvert, TypeF64(state), arg0)));
|
||||
const auto nan = Binary(state, spv::OpFUnordNotEqual, TypeBool(state), arg0, arg0);
|
||||
return Join(state, SelectBits(state, nan, Quiet(state, converted), converted));
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertF64S32(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
return FromF64(state, Unary(state, spv::OpConvertSToF, TypeF64(state), Unary(state, spv::OpBitcast, TypeI32(state), arg0)));
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertF64U32(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
return FromF64(state, Unary(state, spv::OpConvertUToF, TypeF64(state), arg0));
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertS32F64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto value = ToF64(state, arg0);
|
||||
const auto low = Binary(state, spv::OpFOrdLessThanEqual, TypeBool(state), value, ConstantF64(state, 0xc1e0000000000000ull));
|
||||
const auto high = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), value, ConstantF64(state, 0x41e0000000000000ull));
|
||||
const auto ordered = Binary(state, spv::OpFOrdEqual, TypeBool(state), value, value);
|
||||
const auto safe = Select(state, TypeF64(state), ordered, value, ConstantF64(state, 0u));
|
||||
const auto clamped = Trunc(state, FromF64(state, Select(state, TypeF64(state), Binary(state, spv::OpLogicalOr, TypeBool(state), low, high), ConstantF64(state, 0u), safe)));
|
||||
const auto converted = Unary(state, spv::OpBitcast, TypeU32(state), Unary(state, spv::OpConvertFToS, TypeI32(state), clamped));
|
||||
const auto saturated = Select(state, TypeU32(state), high, ConstantU32(state, 0x7fffffffu), converted);
|
||||
return Select(state, TypeU32(state), low, ConstantU32(state, 0x80000000u), saturated);
|
||||
}
|
||||
|
||||
std::uint32_t EmitConvertU32F64(SpirvEmitterState& state, std::uint32_t arg0) {
|
||||
const auto value = ToF64(state, arg0);
|
||||
const auto low = Binary(state, spv::OpFUnordLessThanEqual, TypeBool(state), value, ConstantF64(state, 0u));
|
||||
const auto high = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), value, ConstantF64(state, 0x41f0000000000000ull));
|
||||
const auto clamped = Trunc(state, FromF64(state, Select(state, TypeF64(state), Binary(state, spv::OpLogicalOr, TypeBool(state), low, high), ConstantF64(state, 0u), value)));
|
||||
const auto converted = Unary(state, spv::OpConvertFToU, TypeU32(state), clamped);
|
||||
const auto saturated = Select(state, TypeU32(state), high, ConstantU32(state, 0xffffffffu), converted);
|
||||
return Select(state, TypeU32(state), low, ConstantU32(state, 0u), saturated);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -70,6 +70,9 @@ SpirvRequirements AnalyzeProgramRequirements(const IrProgram& program) {
|
|||
if (BufferAccessOf(inst->Opcode()) == BufferAccess::Atomic && inst->Type() == IrType::U64) {
|
||||
requirements.bufferInt64Atomics = true;
|
||||
}
|
||||
if (IsFloat64Opcode(inst->Opcode())) {
|
||||
requirements.float64 = true;
|
||||
}
|
||||
const auto addressAccess = AddressOpcodeInfoOf(inst->Opcode()).access;
|
||||
if (addressAccess != AddressAccess::None) {
|
||||
const auto memoryIndex = inst->Flags<MemoryFlags>().index;
|
||||
|
|
|
|||
|
|
@ -419,6 +419,26 @@ void EmitDirectInstruction(SpirvValueEmitContext& ctx, const IrValue& inst) {
|
|||
case IrOpcode::FPCeil32: return Invoke(EmitFPCeil32, ctx, inst);
|
||||
case IrOpcode::FPTrunc32: return Invoke(EmitFPTrunc32, ctx, inst);
|
||||
case IrOpcode::FPFract32: return Invoke(EmitFPFract32, ctx, inst);
|
||||
case IrOpcode::FPAdd64: return Invoke(EmitFPAdd64, ctx, inst);
|
||||
case IrOpcode::FPMul64: return Invoke(EmitFPMul64, ctx, inst);
|
||||
case IrOpcode::FPFma64: return Invoke(EmitFPFma64, ctx, inst);
|
||||
case IrOpcode::FPMin64: return Invoke(EmitFPMin64, ctx, inst);
|
||||
case IrOpcode::FPMax64: return Invoke(EmitFPMax64, ctx, inst);
|
||||
case IrOpcode::FPSaturate64: return Invoke(EmitFPSaturate64, ctx, inst);
|
||||
case IrOpcode::FPLdexp64: return Invoke(EmitFPLdexp64, ctx, inst);
|
||||
case IrOpcode::FPRoundEven64: return Invoke(EmitFPRoundEven64, ctx, inst);
|
||||
case IrOpcode::FPFloor64: return Invoke(EmitFPFloor64, ctx, inst);
|
||||
case IrOpcode::FPCeil64: return Invoke(EmitFPCeil64, ctx, inst);
|
||||
case IrOpcode::FPTrunc64: return Invoke(EmitFPTrunc64, ctx, inst);
|
||||
case IrOpcode::FPFract64: return Invoke(EmitFPFract64, ctx, inst);
|
||||
case IrOpcode::FPFrexpMant64: return Invoke(EmitFPFrexpMant64, ctx, inst);
|
||||
case IrOpcode::FPFrexpExp64: return Invoke(EmitFPFrexpExp64, ctx, inst);
|
||||
case IrOpcode::ConvertF32F64: return Invoke(EmitConvertF32F64, ctx, inst);
|
||||
case IrOpcode::ConvertF64F32: return Invoke(EmitConvertF64F32, ctx, inst);
|
||||
case IrOpcode::ConvertF64S32: return Invoke(EmitConvertF64S32, ctx, inst);
|
||||
case IrOpcode::ConvertF64U32: return Invoke(EmitConvertF64U32, ctx, inst);
|
||||
case IrOpcode::ConvertS32F64: return Invoke(EmitConvertS32F64, ctx, inst);
|
||||
case IrOpcode::ConvertU32F64: return Invoke(EmitConvertU32F64, ctx, inst);
|
||||
case IrOpcode::LaneId: return Invoke(EmitLaneId, ctx, inst);
|
||||
case IrOpcode::WriteLane: return Invoke(EmitWriteLane, ctx, inst);
|
||||
case IrOpcode::Permlane16U32: return Invoke(EmitPermlane16U32, ctx, inst);
|
||||
|
|
|
|||
|
|
@ -166,6 +166,13 @@ void DefineModule(SpirvEmitterState& state) {
|
|||
state.module.EmitCapability(spv::CapabilitySignedZeroInfNanPreserve);
|
||||
state.module.EmitExtension("SPV_KHR_float_controls");
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeSignedZeroInfNanPreserve, 32u);
|
||||
if (state.requirements.float64) {
|
||||
if (std::find(state.supportedCapabilities.begin(), state.supportedCapabilities.end(), static_cast<std::uint32_t>(spv::CapabilityFloat64)) == state.supportedCapabilities.end()) {
|
||||
throw std::runtime_error("64-bit float instructions need the Float64 capability, which the device lacks");
|
||||
}
|
||||
state.module.EmitCapability(spv::CapabilityFloat64);
|
||||
state.module.AddExecutionMode(state.mainFunc, spv::ExecutionModeSignedZeroInfNanPreserve, 64u);
|
||||
}
|
||||
if (const auto* workgroup = ShaderWorkgroupInput(state)) {
|
||||
const std::uint32_t derivativeDefault = state.requirements.computeDerivatives ? 2u : 1u;
|
||||
std::uint32_t localX = workgroup->threadsNum[0] != 0u ? workgroup->threadsNum[0] : derivativeDefault;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ private:
|
|||
std::array<IrU32, 2> readU32Pair(const RdnaOperand& operand);
|
||||
IrU64 readU64(const RdnaOperand& operand);
|
||||
std::array<IrU32, 2> readF64Bits(const RdnaOperand& operand);
|
||||
IrU64 readF64(const RdnaOperand& operand);
|
||||
IrF32 readF16LaneAsF32(const RdnaOperand& operand, bool highLane, bool packed = false);
|
||||
IrF32 readF16AsF32(const RdnaOperand& operand);
|
||||
IrU32 readF16SourceBits(const RdnaOperand& operand);
|
||||
|
|
@ -162,6 +163,7 @@ private:
|
|||
bool vSatPkU8I16(const RdnaInstruction& inst);
|
||||
bool vMulLegacyF32(const RdnaInstruction& inst, bool accumulate);
|
||||
void emitFloat16ClassCompare(const RdnaInstruction& inst, bool cmpx);
|
||||
bool float64Operation(const RdnaInstruction& inst, IrOpcode opcode);
|
||||
bool vDot2cF32F16(const RdnaInstruction& inst);
|
||||
bool vCubeidF32(const RdnaInstruction& inst);
|
||||
bool vCubescF32(const RdnaInstruction& inst);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
#include "Translation/TranslationContext.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ShaderRecompiler {
|
||||
|
||||
IrU64 TranslationContext::readF64(const RdnaOperand& operand) {
|
||||
std::array<IrU32, 2> pair{IrU32(ir.Constant(0u)), readRawU32(operand)};
|
||||
if (operand.kind == RdnaOperandKind::FloatInlineConstant && operand.value == 0x3e22f983u) {
|
||||
pair = {IrU32(ir.Constant(0x6dc9c882u)), IrU32(ir.Constant(0x3fc45f30u))};
|
||||
} else if (operand.kind != RdnaOperandKind::LiteralConstant) {
|
||||
pair = readU32Pair(plainOperand(operand));
|
||||
}
|
||||
if (operand.absolute) {
|
||||
pair[1] = IrU32(ir.BitwiseAnd(pair[1].Value(), ir.Constant(0x7fffffffu)));
|
||||
}
|
||||
if (operand.negate) {
|
||||
pair[1] = IrU32(ir.BitwiseXor(pair[1].Value(), ir.Constant(0x80000000u)));
|
||||
}
|
||||
return IrU64(ir.ConstructU64(pair[0].Value(), pair[1].Value()));
|
||||
}
|
||||
|
||||
bool TranslationContext::float64Operation(const RdnaInstruction& inst, IrOpcode opcode) {
|
||||
const std::size_t count = IrOpcodeOperandCount(opcode);
|
||||
std::array<IrValue*, 3> args{};
|
||||
for (std::uint32_t index = 0u; index < count; ++index) {
|
||||
const RdnaOperand& operand = sourceAt(inst, index);
|
||||
switch (IrOpcodeArgumentType(opcode, index)) {
|
||||
case IrType::U64: args[index] = &readF64(operand).Value(); break;
|
||||
case IrType::F32: args[index] = readOperand(operand, IrType::F32); break;
|
||||
default: args[index] = &readU32(operand).Value(); break;
|
||||
}
|
||||
}
|
||||
const IrType type = IrOpcodeType(opcode);
|
||||
IrValue* result = count == 1u ? &ir.Emit(opcode, type, {args[0]}) : count == 2u ? &ir.Emit(opcode, type, {args[0], args[1]}) : &ir.Emit(opcode, type, {args[0], args[1], args[2]});
|
||||
RdnaOperand destination = inst.destination;
|
||||
destination.omod = 0u;
|
||||
if (type == IrType::U64 && destination.clamp) {
|
||||
result = &ir.Emit(IrOpcode::FPSaturate64, IrType::U64, {result});
|
||||
}
|
||||
writeOperand(destination, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -835,6 +835,44 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) {
|
|||
return vDivFixupF16(inst);
|
||||
case RdnaOpcode::VFrexpMantF32:
|
||||
return vFrexpMantF32(inst);
|
||||
case RdnaOpcode::VAddF64:
|
||||
return float64Operation(inst, IrOpcode::FPAdd64);
|
||||
case RdnaOpcode::VMulF64:
|
||||
return float64Operation(inst, IrOpcode::FPMul64);
|
||||
case RdnaOpcode::VFmaF64:
|
||||
return float64Operation(inst, IrOpcode::FPFma64);
|
||||
case RdnaOpcode::VMinF64:
|
||||
return float64Operation(inst, IrOpcode::FPMin64);
|
||||
case RdnaOpcode::VMaxF64:
|
||||
return float64Operation(inst, IrOpcode::FPMax64);
|
||||
case RdnaOpcode::VLdexpF64:
|
||||
return float64Operation(inst, IrOpcode::FPLdexp64);
|
||||
case RdnaOpcode::VTruncF64:
|
||||
return float64Operation(inst, IrOpcode::FPTrunc64);
|
||||
case RdnaOpcode::VCeilF64:
|
||||
return float64Operation(inst, IrOpcode::FPCeil64);
|
||||
case RdnaOpcode::VRndneF64:
|
||||
return float64Operation(inst, IrOpcode::FPRoundEven64);
|
||||
case RdnaOpcode::VFloorF64:
|
||||
return float64Operation(inst, IrOpcode::FPFloor64);
|
||||
case RdnaOpcode::VFractF64:
|
||||
return float64Operation(inst, IrOpcode::FPFract64);
|
||||
case RdnaOpcode::VFrexpMantF64:
|
||||
return float64Operation(inst, IrOpcode::FPFrexpMant64);
|
||||
case RdnaOpcode::VFrexpExpI32F64:
|
||||
return float64Operation(inst, IrOpcode::FPFrexpExp64);
|
||||
case RdnaOpcode::VCvtF32F64:
|
||||
return float64Operation(inst, IrOpcode::ConvertF32F64);
|
||||
case RdnaOpcode::VCvtF64F32:
|
||||
return float64Operation(inst, IrOpcode::ConvertF64F32);
|
||||
case RdnaOpcode::VCvtF64I32:
|
||||
return float64Operation(inst, IrOpcode::ConvertF64S32);
|
||||
case RdnaOpcode::VCvtF64U32:
|
||||
return float64Operation(inst, IrOpcode::ConvertF64U32);
|
||||
case RdnaOpcode::VCvtI32F64:
|
||||
return float64Operation(inst, IrOpcode::ConvertS32F64);
|
||||
case RdnaOpcode::VCvtU32F64:
|
||||
return float64Operation(inst, IrOpcode::ConvertU32F64);
|
||||
case RdnaOpcode::VRcpF32:
|
||||
return floatUnary(inst, IrOpcode::FPRecip32);
|
||||
case RdnaOpcode::VRcpIflagF32:
|
||||
|
|
|
|||
Loading…
Reference in New Issue