From 11bc2799452f8a84818eb2e57f8e59067637801d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Thu, 1 Oct 2026 22:39:48 +0200 Subject: [PATCH 1/2] feat(shader): implement v_div_scale_f32, v_div_fmas_f32, v_div_fixup_f32 and s_denorm_mode These make up the precise f32 division macro compilers emit for a / b. No reference emulator implements them and the ISA pseudocode disagrees with the hardware, so the rules were measured on an RDNA2 GPU: - div_scale decides from the exponent fields only: 2^64 or 2^-64 scaling and VCC for differences >= 96 or <= -96, a denormal or huge (>= 253) denominator and a numerator exponent <= 24; a zero input gives 0xffc00000. - div_fmas scales by 2^64 when the exponent of S2 is >= 128, else 2^-64, before the single rounding (the ISA says a fixed 2^32). - div_fixup follows the ISA, and a NaN quotient with finite inputs becomes +-inf. s_denorm_mode is accepted and ignored; listed in TechnicalDebt. Co-Authored-By: Claude --- .../tests/execution/Division.cpp | 691 ++++++++++++++++++ .../include/RdnaDecoder/RdnaOpcode.hpp | 4 + .../recompiler/RdnaDecoder/src/RdnaOpcode.cpp | 3 + .../RdnaDecoder/src/RdnaScalarOpDecoder.cpp | 4 +- .../RdnaDecoder/src/RdnaVectorOpDecoder.cpp | 12 +- .../Translation/TranslationContext.hpp | 3 + .../Translation/src/FloatInstructions.cpp | 96 +++ .../Translation/src/ScalarInstructions.cpp | 1 + .../Translation/src/VectorInstructions.cpp | 6 + docs/dev/TechnicalDebt.md | 1 + 10 files changed, 818 insertions(+), 3 deletions(-) create mode 100644 core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp new file mode 100644 index 000000000..ea91c11fc --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp @@ -0,0 +1,691 @@ +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 32; +constexpr std::uint32_t Stride = 4; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array HelperCode{ + 0x34020082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xe0302008, 0x80000601, 0xe030200c, + 0x80000701, 0xbf8c3f70, 0xd56d140a, 0x041a0b04, 0xd501000b, 0x00510280, 0x7d8a0e80, 0xd56f000c, + 0x041a0b04, 0xd55f000d, 0x041a0b04, 0xe0702000, 0x80010a01, 0xe0702004, 0x80010b01, 0xe0702008, + 0x80010c01, 0xe070200c, 0x80010d01, 0xbf810000, +}; + +alignas(256) constexpr std::array DivisionCode{ + 0x34020082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xbf8c3f70, 0xd56d1408, 0x04120b05, + 0xd56d6a09, 0x04120b04, 0x7e145508, 0xbfa5000f, 0xd54b000b, 0x23ca1508, 0x5614150b, 0x10161509, + 0xd54b000c, 0x24261708, 0x5616150c, 0xd54b0008, 0x24261708, 0xbfa5000c, 0xd56f0008, 0x042e1508, + 0xd55f000a, 0x04120b08, 0xe0702000, 0x80010a01, 0xbf810000, +}; + +alignas(256) constexpr std::array FmaCode{ + 0x34020082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xe0302008, 0x80000601, 0xbf8c3f70, + 0xd54b000a, 0x041a0b04, 0xe0702000, 0x80010a01, 0xbf810000, +}; + +struct HelperVector { + std::uint32_t s0, s1, s2, vcc; + std::uint32_t scale, scaleVcc, fmas, fixup; +}; + +constexpr HelperVector HelperVectors[] = { + {0x6447899fu, 0xdf6dabe5u, 0x407fa3f9u, 1u, 0x6447899fu, 0u, 0xff800000u, 0xe447899fu}, + {0x205765cfu, 0xd03cee5eu, 0x205765cfu, 0u, 0x405765cfu, 1u, 0xb11ef754u, 0xa05765cfu}, + {0xe46366bau, 0xb26bd3d1u, 0xe46366bau, 0u, 0xe46366bau, 1u, 0xe46366bau, 0x646366bau}, + {0x00ef1dbeu, 0x00ef1dbeu, 0xbf800000u, 1u, 0x20ef1dbeu, 1u, 0x9f800000u, 0x80ef1dbeu}, + {0xff48b595u, 0xff48b595u, 0x7fc00000u, 1u, 0xdf48b595u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x7fc00000u, 0x7f0cfc80u, 0x7fc00000u, 0u, 0x7fc00000u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x8f7fdc41u, 0x8f7fdc41u, 0xc12da640u, 1u, 0xaf7fdc41u, 1u, 0xe12da640u, 0x0f7fdc41u}, + {0x7f800000u, 0x7f800000u, 0x8087bc0au, 1u, 0x7f800000u, 1u, 0x7f800000u, 0x80000000u}, + {0x00d2a4bcu, 0x00800000u, 0x00d2a4bcu, 0u, 0x20d2a4bcu, 0u, 0x00d2a4bcu, 0x00d2a4bcu}, + {0xbf6e2b6eu, 0xbf6e2b6eu, 0x3fd849f1u, 1u, 0xbf6e2b6eu, 0u, 0x20238a2au, 0xbf6e2b6eu}, + {0x647c2f4du, 0xb2411a77u, 0xb25f6279u, 1u, 0x647c2f4du, 0u, 0xb73e39bbu, 0x647c2f4du}, + {0x9e5afe34u, 0x9e5afe34u, 0x7f7fffffu, 1u, 0xbe5afe34u, 1u, 0x7f800000u, 0x9e5afe34u}, + {0xbfba5facu, 0xbfba5facu, 0xffc00001u, 1u, 0xdfba5facu, 1u, 0xffc00001u, 0xffc00001u}, + {0x7f800001u, 0x7f800001u, 0xbf9b99f7u, 1u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x206ab9f3u, 0xd0096643u, 0x206ab9f3u, 1u, 0x406ab9f3u, 1u, 0x90fbf68du, 0xa06ab9f3u}, + {0xc1720b19u, 0x0f017877u, 0xc1720b19u, 0u, 0xc1720b19u, 1u, 0xc1720b19u, 0xc1720b19u}, + {0xff44e998u, 0x00800000u, 0xff44e998u, 0u, 0xff44e998u, 1u, 0xff44e998u, 0xff44e998u}, + {0xf32da6bcu, 0x32158aaau, 0xb206ddc4u, 1u, 0xf32da6bcu, 0u, 0xc5cae035u, 0xf32da6bcu}, + {0x80000000u, 0x80000000u, 0x7f236db9u, 1u, 0xffc00000u, 1u, 0x7f800000u, 0xff800000u}, + {0x7f7fffffu, 0x647d538bu, 0x7f7fffffu, 0u, 0x7f7fffffu, 0u, 0x7f800000u, 0x7f7fffffu}, + {0x80000000u, 0x80000000u, 0xff432955u, 1u, 0xffc00000u, 1u, 0xff800000u, 0x7f800000u}, + {0xc02a4c09u, 0x9e332b73u, 0x32222edau, 1u, 0xc02a4c09u, 0u, 0x12222edau, 0xc02a4c09u}, + {0x645bd0bbu, 0xff55d263u, 0x645bd0bbu, 0u, 0x445bd0bbu, 0u, 0xff800000u, 0xe45bd0bbu}, + {0xfe8f9e21u, 0xfe8f9e21u, 0xfed9b0c7u, 0u, 0xde8f9e21u, 0u, 0x7f800000u, 0x7e8f9e21u}, + {0x7f68b88eu, 0x7f68b88eu, 0x647b3a94u, 1u, 0x5f68b88eu, 0u, 0x7f800000u, 0x7f68b88eu}, + {0xbffaa90eu, 0x1e624a54u, 0x40caf523u, 1u, 0xbffaa90eu, 0u, 0x60caf523u, 0x3ffaa90eu}, + {0x7f7fffffu, 0x7f765e02u, 0x7f7fffffu, 0u, 0x5f7fffffu, 0u, 0x7f800000u, 0x7f7fffffu}, + {0x646a79ccu, 0x646a79ccu, 0x34012ffcu, 0u, 0x646a79ccu, 1u, 0x7f800000u, 0x646a79ccu}, + {0xcb1c1ab7u, 0x32457ea2u, 0x80dbcf65u, 1u, 0xeb1c1ab7u, 1u, 0x9df0db8eu, 0xcb1c1ab7u}, + {0x3f800000u, 0x3f800000u, 0x3fc5c487u, 1u, 0x3f800000u, 0u, 0x2022e244u, 0x3f800000u}, + {0x3fb65eddu, 0x4b2d9078u, 0x40b04781u, 0u, 0x3fb65eddu, 0u, 0x4b774a12u, 0x3fb65eddu}, + {0xbf800000u, 0x1e54a2f3u, 0xbf800000u, 0u, 0xbf800000u, 0u, 0xbf800000u, 0xbf800000u}, + {0xe401f99cu, 0x7fc00000u, 0xe401f99cu, 0u, 0xc401f99cu, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x7f800000u, 0x64367202u, 0x7f800000u, 0u, 0x7f800000u, 0u, 0x7f800000u, 0x7f800000u}, + {0x808beb57u, 0xff800000u, 0x808beb57u, 0u, 0x808beb57u, 1u, 0x7f800000u, 0x00000000u}, + {0x2d3579c7u, 0x2d3579c7u, 0xdd147edcu, 1u, 0x4d3579c7u, 1u, 0xfd147edcu, 0xad3579c7u}, + {0x1e1208a1u, 0x1e1208a1u, 0xce5288a4u, 1u, 0x3e1208a1u, 1u, 0xee5288a4u, 0x9e1208a1u}, + {0xbff9c597u, 0x3fa8e4deu, 0xbff9c597u, 1u, 0xbff9c597u, 0u, 0xa090d5e2u, 0xbff9c597u}, + {0xe470e6bdu, 0xe470e6bdu, 0xb47af746u, 0u, 0xe470e6bdu, 1u, 0x7f800000u, 0x6470e6bdu}, + {0xffc00001u, 0xffc00001u, 0x1e20c8acu, 1u, 0xffc00001u, 1u, 0xffc00001u, 0xffc00001u}, + {0xf3299ccbu, 0x40548d2bu, 0x40173047u, 0u, 0xf3299ccbu, 0u, 0xf40cd360u, 0x73299ccbu}, + {0x7f236db9u, 0x7f236db9u, 0x80000000u, 1u, 0xffc00000u, 1u, 0x7f800000u, 0x80000000u}, + {0x7f800001u, 0x7f800001u, 0x3f96a6b3u, 1u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x3faf6061u, 0x3faf6061u, 0x0c98e836u, 1u, 0x3faf6061u, 1u, 0x1ff049d2u, 0x3faf6061u}, + {0x20cfc9b4u, 0xd01a7e81u, 0x20cfc9b4u, 0u, 0x20cfc9b4u, 0u, 0xb17acc09u, 0xa0cfc9b4u}, + {0x80000000u, 0x00eee942u, 0x80000000u, 0u, 0xffc00000u, 0u, 0x80000000u, 0x80000000u}, + {0xff48b595u, 0x7fc00000u, 0xff48b595u, 0u, 0xdf48b595u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x4b4629bbu, 0x4b4629bbu, 0x9452cc68u, 1u, 0x4b4629bbu, 1u, 0x37196494u, 0xcb4629bbu}, + {0x3c39c1eau, 0x3c39c1eau, 0x8a45f6adu, 1u, 0x3c39c1eau, 1u, 0x1906c9d7u, 0xbc39c1eau}, + {0xffc00001u, 0xffc00001u, 0x7f38e817u, 1u, 0xffc00001u, 0u, 0xffc00001u, 0xffc00001u}, + {0x5cfabac2u, 0x2d3020f8u, 0x5cfabac2u, 0u, 0x5cfabac2u, 0u, 0x5cfabac2u, 0x5cfabac2u}, + {0xf30fc634u, 0x3fc9858fu, 0xe4335bfdu, 0u, 0xf30fc634u, 0u, 0xf3625b42u, 0xf30fc634u}, + {0xbfba5facu, 0xffc00001u, 0xbfba5facu, 0u, 0xbfba5facu, 1u, 0xffc00001u, 0xffc00001u}, + {0x1e5b0043u, 0x00800000u, 0x1e5b0043u, 0u, 0x1e5b0043u, 0u, 0x1e5b0043u, 0x1e5b0043u}, + {0x00f745cfu, 0x00f745cfu, 0x7f7fffffu, 1u, 0x20f745cfu, 1u, 0x7f800000u, 0x00f745cfu}, + {0x80dba64cu, 0x80dba64cu, 0xff800000u, 1u, 0xa0dba64cu, 1u, 0xff800000u, 0x7f800000u}, + {0x647d538bu, 0x7f7fffffu, 0x647d538bu, 0u, 0x447d538bu, 0u, 0x7f800000u, 0x647d538bu}, + {0xdd147edcu, 0x2d3579c7u, 0xdd147edcu, 0u, 0xdd147edcu, 1u, 0xdd147edcu, 0xdd147edcu}, + {0x3fc8a7f4u, 0xc042ca5eu, 0x7f0dd4c6u, 0u, 0x3fc8a7f4u, 1u, 0x7f0dd4c6u, 0xbfc8a7f4u}, + {0xbff9fa49u, 0xbff9fa49u, 0x3ff12ad6u, 0u, 0xbff9fa49u, 0u, 0x40b65721u, 0xbff9fa49u}, + {0xf344217cu, 0xbfbdc5beu, 0x8f413bb0u, 1u, 0xff800000u, 1u, 0x53916438u, 0x7344217cu}, + {0x8a45f6adu, 0x3c39c1eau, 0x8a45f6adu, 1u, 0xaa45f6adu, 1u, 0x80000000u, 0x8a45f6adu}, + {0xbfe7ec61u, 0xbfe7ec61u, 0x0c381f29u, 1u, 0xbfe7ec61u, 1u, 0x20521c71u, 0xbfe7ec61u}, + {0x4b6706c6u, 0x4b6706c6u, 0x94337308u, 0u, 0x4b6706c6u, 1u, 0x57507d3au, 0xcb6706c6u}, + {0x3f96a6b3u, 0x3f96a6b3u, 0x7f800001u, 1u, 0x5f96a6b3u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x7fc00000u, 0xff48b595u, 0x7fc00000u, 0u, 0x7fc00000u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0xe436197du, 0xbfe2dd06u, 0x7f5c69d8u, 1u, 0xe436197du, 1u, 0x7f800000u, 0xe436197du}, + {0x0f2a3ce5u, 0xb23b52c9u, 0xbf56298cu, 0u, 0x0f2a3ce5u, 0u, 0xbf56298cu, 0x0f2a3ce5u}, + {0x3f800000u, 0x3fc5c487u, 0x3f800000u, 0u, 0x3f800000u, 0u, 0x4022e244u, 0x3f800000u}, + {0x0c98e836u, 0x3faf6061u, 0x0c98e836u, 1u, 0x2c98e836u, 1u, 0x00000000u, 0x0c98e836u}, + {0xbffb2a03u, 0xbffb2a03u, 0x8c212161u, 0u, 0xbffb2a03u, 1u, 0x40766b69u, 0x3ffb2a03u}, + {0xff44e998u, 0xff44e998u, 0x00800000u, 1u, 0xdf44e998u, 1u, 0x7f800000u, 0x80000000u}, + {0x3f551100u, 0x3f551100u, 0xbfc9821fu, 1u, 0x3f551100u, 0u, 0x9f61aef3u, 0xbf551100u}, + {0x0f5dc50cu, 0x4046142au, 0x3f8552c6u, 1u, 0x0f5dc50cu, 0u, 0x1f8552c6u, 0x0f5dc50cu}, + {0xff77d27cu, 0xff77d27cu, 0xbf800000u, 1u, 0xdf77d27cu, 1u, 0x7f800000u, 0x7f77d27cu}, + {0xe40a117du, 0xbf800000u, 0xe40a117du, 0u, 0xe40a117du, 0u, 0x00000000u, 0x640a117du}, + {0xbfbfa2d8u, 0xbfbfa2d8u, 0x8fb794d4u, 1u, 0xbfbfa2d8u, 1u, 0x200f7466u, 0x3fbfa2d8u}, + {0x7fc00000u, 0x7fc00000u, 0xff48b595u, 1u, 0x7fc00000u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x406b12ffu, 0x402f0b9bu, 0xbfebcec0u, 1u, 0x406b12ffu, 0u, 0x210342ccu, 0xc06b12ffu}, + {0x3fbfb16fu, 0x7f800000u, 0x3fbfb16fu, 0u, 0x3fbfb16fu, 1u, 0x7f800000u, 0x00000000u}, + {0x1e20c8acu, 0x1e20c8acu, 0xffc00001u, 1u, 0x3e20c8acu, 1u, 0xffc00001u, 0xffc00001u}, + {0x7f236db9u, 0x80000000u, 0x7f236db9u, 0u, 0xffc00000u, 1u, 0x7f236db9u, 0xff800000u}, + {0x3f96a6b3u, 0x7f800001u, 0x3f96a6b3u, 0u, 0x3f96a6b3u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x5d3c8ab9u, 0x2d45dce4u, 0x5d3c8ab9u, 0u, 0x5d3c8ab9u, 1u, 0x5d3c8ab9u, 0x5d3c8ab9u}, + {0x1e54a2f3u, 0x1e54a2f3u, 0xbf800000u, 1u, 0x1e54a2f3u, 0u, 0x9f800000u, 0x9e54a2f3u}, + {0x7f765e02u, 0x7f7fffffu, 0x7f765e02u, 0u, 0x5f765e02u, 0u, 0x7f800000u, 0x7f765e02u}, + {0x6449aa84u, 0xff670fe9u, 0x6449aa84u, 1u, 0x4449aa84u, 0u, 0xff800000u, 0xe449aa84u}, + {0x00eee942u, 0x00eee942u, 0x80000000u, 1u, 0xffc00000u, 0u, 0x00000000u, 0x80000000u}, + {0x64367202u, 0x64367202u, 0x7f800000u, 1u, 0x64367202u, 0u, 0x7f800000u, 0x7f800000u}, + {0xff432955u, 0xff432955u, 0x80000000u, 1u, 0xffc00000u, 1u, 0x7f800000u, 0x00000000u}, + {0xffc00001u, 0x1e20c8acu, 0xffc00001u, 0u, 0xffc00001u, 1u, 0xffc00001u, 0xffc00001u}, + {0x80a78d45u, 0x80a78d45u, 0x30ec8485u, 1u, 0xa0a78d45u, 1u, 0x10ec8485u, 0x80a78d45u}, + {0x7341c050u, 0xb26e3f7cu, 0xbfefd8eeu, 1u, 0x7341c050u, 0u, 0xc63450d7u, 0x7341c050u}, + {0x5074fc8au, 0x5074fc8au, 0x20bfb05cu, 1u, 0x5074fc8au, 0u, 0x416a7260u, 0x5074fc8au}, + {0x1e47efb3u, 0x1e47efb3u, 0xffc00001u, 1u, 0x3e47efb3u, 1u, 0xffc00001u, 0xffc00001u}, + {0x80fa7818u, 0x3fc7ca5au, 0x80ffa22bu, 0u, 0xa0fa7818u, 1u, 0x81a1a535u, 0x80fa7818u}, + {0x7f7fffffu, 0x7f7fffffu, 0x00f745cfu, 1u, 0x5f7fffffu, 1u, 0x7f800000u, 0x00000000u}, + {0xfed9b0c7u, 0xfe8f9e21u, 0xfed9b0c7u, 1u, 0xded9b0c7u, 0u, 0x7f800000u, 0x7ed9b0c7u}, + {0xa095da99u, 0xd04f2e7bu, 0xa095da99u, 1u, 0xa095da99u, 0u, 0x11728deeu, 0x2095da99u}, + {0x405e2128u, 0x5f706ffbu, 0x00d8ff9cu, 0u, 0x605e2128u, 1u, 0x6050a040u, 0x00000000u}, + {0x7f800000u, 0x8087bc0au, 0x7f800000u, 0u, 0x7f800000u, 1u, 0xffc00000u, 0xff800000u}, + {0x342e1260u, 0xe476a68cu, 0x342e1260u, 0u, 0x542e1260u, 1u, 0xd927b6e7u, 0xb42e1260u}, + {0x64129b3bu, 0xcb22539eu, 0x3f7c72acu, 0u, 0x64129b3bu, 0u, 0xefb9ec3cu, 0xe4129b3bu}, + {0x0cd303a0u, 0x3ff8566fu, 0x0cd303a0u, 1u, 0x2cd303a0u, 1u, 0x00000000u, 0x0cd303a0u}, + {0x0082f0eeu, 0x00000000u, 0x0082f0eeu, 0u, 0xffc00000u, 0u, 0x0082f0eeu, 0x7f800000u}, + {0x00800000u, 0x1e5b0043u, 0x00800000u, 0u, 0x20800000u, 0u, 0x00800000u, 0x00800000u}, + {0x80000000u, 0x80000000u, 0x00eee942u, 1u, 0xffc00000u, 0u, 0x00000000u, 0xff800000u}, + {0xbfbd4abcu, 0x407aaf70u, 0x4078dc75u, 1u, 0xbfbd4abcu, 0u, 0xdff3b9e8u, 0x3fbd4abcu}, + {0xf366228eu, 0xb220f56fu, 0xff6f2483u, 0u, 0xf366228eu, 1u, 0xff6f2483u, 0x7366228eu}, + {0x9e037acau, 0x9e037acau, 0x00800000u, 1u, 0xbe037acau, 0u, 0x00000000u, 0x9e037acau}, + {0xff800000u, 0xff800000u, 0x7f480adbu, 1u, 0xff800000u, 0u, 0x7f800000u, 0x80000000u}, + {0x7eb2f686u, 0x7eb2f686u, 0xb24327d1u, 0u, 0x5eb2f686u, 1u, 0x7f800000u, 0x80000000u}, + {0xcb66d08fu, 0x4b1b3f81u, 0x325f1b96u, 1u, 0xcb66d08fu, 0u, 0xb70bf988u, 0x4b66d08fu}, + {0x7f38e817u, 0xffc00001u, 0x7f38e817u, 0u, 0x5f38e817u, 0u, 0xffc00001u, 0xffc00001u}, + {0x80dba64cu, 0xff800000u, 0x80dba64cu, 0u, 0x80dba64cu, 1u, 0x7f800000u, 0x00000000u}, + {0x8c212161u, 0xbffb2a03u, 0x8c212161u, 1u, 0xac212161u, 1u, 0x00000000u, 0x0c212161u}, + {0xce44df4eu, 0x1e0b7a24u, 0xce44df4eu, 0u, 0xce44df4eu, 1u, 0xce44df4eu, 0xce44df4eu}, + {0x809b6f32u, 0x809b6f32u, 0x7fc00000u, 1u, 0xa09b6f32u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0x3faac0f5u, 0xcb4e4c92u, 0xc0e39c19u, 1u, 0x3faac0f5u, 0u, 0xeb899a5bu, 0x3faac0f5u}, + {0xb24327d1u, 0x7eb2f686u, 0xb24327d1u, 1u, 0xb24327d1u, 1u, 0xd1886d9eu, 0x80000000u}, + {0x1e54a2f3u, 0xbf800000u, 0x1e54a2f3u, 0u, 0x1e54a2f3u, 0u, 0x00000000u, 0x9e54a2f3u}, + {0xbf9b99f7u, 0xbf9b99f7u, 0x7f800001u, 1u, 0xdf9b99f7u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0xffc00001u, 0x1e47efb3u, 0xffc00001u, 0u, 0xffc00001u, 1u, 0xffc00001u, 0xffc00001u}, + {0x3fa8e4deu, 0x3fa8e4deu, 0xbff9c597u, 1u, 0x3fa8e4deu, 0u, 0x9e5759b8u, 0xbfa8e4deu}, + {0x94337308u, 0x4b6706c6u, 0x94337308u, 0u, 0xb4337308u, 1u, 0xa021f18cu, 0x94337308u}, + {0x34012ffcu, 0x646a79ccu, 0x34012ffcu, 0u, 0x54012ffcu, 1u, 0x58eca6a6u, 0x34012ffcu}, + {0x1e5b0043u, 0x1e5b0043u, 0x00800000u, 1u, 0x3e5b0043u, 0u, 0x00000000u, 0x1e5b0043u}, + {0x7f0cfc80u, 0x7fc00000u, 0x7f0cfc80u, 0u, 0x5f0cfc80u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0x80c6f82fu, 0x80c6f82fu, 0x30ef013cu, 0u, 0xa0c6f82fu, 1u, 0x30ef013cu, 0x80c6f82fu}, + {0xa078f817u, 0x50779f20u, 0xa078f817u, 0u, 0xc078f817u, 1u, 0xb170d21fu, 0xa078f817u}, + {0x7f045f1du, 0x80000000u, 0x7f045f1du, 0u, 0xffc00000u, 1u, 0x7f045f1du, 0xff800000u}, + {0x736cc6a0u, 0xdf27424au, 0x80b256deu, 0u, 0x7f800000u, 1u, 0xff800000u, 0x00000000u}, + {0xce51a0b9u, 0x9e5f7bf1u, 0xce51a0b9u, 1u, 0xce51a0b9u, 1u, 0xee51a0b9u, 0x4e51a0b9u}, + {0xff800000u, 0x7f480adbu, 0xff800000u, 0u, 0xff800000u, 0u, 0xff800000u, 0xff800000u}, + {0xad64260cu, 0xad64260cu, 0xdccb0301u, 0u, 0xad64260cu, 0u, 0xdccb0301u, 0x2d64260cu}, + {0xb26ef1d1u, 0xfee6b9efu, 0xb26ef1d1u, 1u, 0xb26ef1d1u, 1u, 0x51d75aceu, 0x00000000u}, + {0xe40a117du, 0xe40a117du, 0xbf800000u, 1u, 0xe40a117du, 0u, 0x6894edb8u, 0x640a117du}, + {0x7e923a93u, 0x7e923a93u, 0x7ef130f1u, 0u, 0x5e923a93u, 0u, 0x7f800000u, 0x7e923a93u}, + {0xffc00001u, 0xffc00001u, 0x9e50a6acu, 1u, 0xffc00001u, 1u, 0xffc00001u, 0xffc00001u}, + {0xff77d27cu, 0xbf800000u, 0xff77d27cu, 0u, 0xff77d27cu, 1u, 0x00000000u, 0x7f77d27cu}, + {0xff800000u, 0xff800000u, 0x80dba64cu, 1u, 0xff800000u, 1u, 0x7f800000u, 0x00000000u}, + {0xd01a7e81u, 0xd01a7e81u, 0x20cfc9b4u, 0u, 0xd01a7e81u, 0u, 0x60ba78e3u, 0xd01a7e81u}, + {0xbf976e24u, 0xbf976e24u, 0x8c367fd8u, 1u, 0xbf976e24u, 1u, 0x1fb3263cu, 0x3f976e24u}, + {0x80000000u, 0xff432955u, 0x80000000u, 0u, 0xffc00000u, 1u, 0x00000000u, 0x00000000u}, + {0x9e037acau, 0x00800000u, 0x9e037acau, 0u, 0x9e037acau, 0u, 0x9e037acau, 0x9e037acau}, + {0x809b6f32u, 0x7fc00000u, 0x809b6f32u, 0u, 0x809b6f32u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0xc019b9b1u, 0x3fbac948u, 0xe467dd2fu, 0u, 0xc019b9b1u, 0u, 0xe467dd2fu, 0xc019b9b1u}, + {0x7fc00000u, 0x00d7c929u, 0x7fc00000u, 0u, 0x7fc00000u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0xff800000u, 0xff800000u, 0x808beb57u, 1u, 0xff800000u, 1u, 0x7f800000u, 0x00000000u}, + {0xe401f99cu, 0xe401f99cu, 0x7fc00000u, 1u, 0xe401f99cu, 0u, 0x7fc00000u, 0x7fc00000u}, + {0xb262f40cu, 0xb262f40cu, 0xe43875cdu, 0u, 0xd262f40cu, 1u, 0xe43875cdu, 0x3262f40cu}, + {0x7fc00000u, 0x7fc00000u, 0x7f0cfc80u, 1u, 0x7fc00000u, 0u, 0x7fc00000u, 0x7fc00000u}, + {0xb47af746u, 0xe470e6bdu, 0xb47af746u, 1u, 0xd47af746u, 1u, 0x396c2a05u, 0x347af746u}, + {0xbf800000u, 0x00ef1dbeu, 0xbf800000u, 0u, 0xbf800000u, 1u, 0xbf800000u, 0xbf800000u}, + {0x1e47efb3u, 0xffc00001u, 0x1e47efb3u, 0u, 0x1e47efb3u, 1u, 0xffc00001u, 0xffc00001u}, + {0x7f7fffffu, 0x7f7fffffu, 0x647d538bu, 1u, 0x5f7fffffu, 0u, 0x7f800000u, 0x7f7fffffu}, + {0x407c8334u, 0x407cf173u, 0x80cd8fceu, 1u, 0x607c8334u, 1u, 0x21797f50u, 0xc07c8334u}, + {0x7f0a90fbu, 0x9e7cdbaeu, 0x8f3f5606u, 1u, 0x7f0a90fbu, 0u, 0xbe08dd9fu, 0x7f0a90fbu}, + {0x7fc00000u, 0x80b7d094u, 0x7fc00000u, 0u, 0x7fc00000u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0x1e52f93au, 0xff800000u, 0x1e52f93au, 0u, 0x1e52f93au, 1u, 0xff800000u, 0x80000000u}, + {0xbf800000u, 0xe40a117du, 0xbf800000u, 0u, 0xbf800000u, 0u, 0x640a117du, 0x3f800000u}, + {0xe456cf53u, 0xdf6ba6bbu, 0xff1ade6fu, 0u, 0xe456cf53u, 0u, 0x7f800000u, 0x6456cf53u}, + {0x80dbb509u, 0xbfb46fbeu, 0x4056ef34u, 1u, 0x80dbb509u, 0u, 0x6056ef34u, 0x80dbb509u}, + {0x7f7fffffu, 0x7f7fffffu, 0x9e5afe34u, 1u, 0x5f7fffffu, 1u, 0x7f800000u, 0x80000000u}, + {0xe429dc3au, 0x3fd26166u, 0x6402c97eu, 0u, 0xe429dc3au, 0u, 0xe4146511u, 0x6429dc3au}, + {0x3fac0a5au, 0x7f800001u, 0x3fac0a5au, 0u, 0x3fac0a5au, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x8fa520fcu, 0xbf844da8u, 0x8fa520fcu, 0u, 0xafa520fcu, 1u, 0x0d31a6ceu, 0x0fa520fcu}, + {0x00ef1dbeu, 0xbf800000u, 0x00ef1dbeu, 0u, 0x20ef1dbeu, 1u, 0x00000000u, 0x80ef1dbeu}, + {0x80b7d094u, 0x80b7d094u, 0x7fc00000u, 1u, 0xa0b7d094u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0xf35f1da4u, 0x3fd43502u, 0x80fd3351u, 0u, 0xff800000u, 1u, 0xf3b8f2bfu, 0xf35f1da4u}, + {0x8f2fe40bu, 0xcb7cb57eu, 0x3fa55c5eu, 1u, 0x8f2fe40bu, 0u, 0x1fa55c5eu, 0x8f2fe40bu}, + {0x202241aau, 0xbf8f1594u, 0x406babcau, 1u, 0x202241aau, 0u, 0x606babcau, 0xa02241aau}, + {0x808beb57u, 0x808beb57u, 0xff800000u, 1u, 0xa08beb57u, 1u, 0xff800000u, 0x7f800000u}, + {0x00a8472du, 0x4b14fbebu, 0x808eeff1u, 1u, 0x20a8472du, 1u, 0x00000000u, 0x80a8472du}, + {0xbf844da8u, 0xbf844da8u, 0x8fa520fcu, 0u, 0xbf844da8u, 1u, 0x3f88c05au, 0x3f844da8u}, + {0xffc00001u, 0xffc00001u, 0xbfba5facu, 1u, 0xffc00001u, 1u, 0xffc00001u, 0xffc00001u}, + {0xbf60fb0cu, 0xbf60fb0cu, 0xbfadba63u, 0u, 0xbf60fb0cu, 0u, 0xbf15bc7bu, 0x3f60fb0cu}, + {0x8fb794d4u, 0xbfbfa2d8u, 0x8fb794d4u, 1u, 0xafb794d4u, 1u, 0x00000000u, 0x0fb794d4u}, + {0xff800000u, 0xff800000u, 0x1e52f93au, 1u, 0xff800000u, 1u, 0x7f800000u, 0x80000000u}, + {0x00e50eb4u, 0xbfe8ea81u, 0x32223f5bu, 1u, 0x00e50eb4u, 0u, 0x12223f5bu, 0x80e50eb4u}, + {0xff800000u, 0x80dba64cu, 0xff800000u, 0u, 0xff800000u, 1u, 0xffc00000u, 0x7f800000u}, + {0xbfc9821fu, 0x3f551100u, 0xbfc9821fu, 1u, 0xbfc9821fu, 0u, 0xa0389c62u, 0xbfc9821fu}, + {0xfee6b9efu, 0xfee6b9efu, 0xb26ef1d1u, 1u, 0xdee6b9efu, 1u, 0x7f800000u, 0x00000000u}, + {0x00d2a4bcu, 0x00d2a4bcu, 0x00800000u, 1u, 0x20d2a4bcu, 0u, 0x00000000u, 0x00d2a4bcu}, + {0x7f800001u, 0x3fac0a5au, 0x7f800001u, 0u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x9e5f7bf1u, 0x9e5f7bf1u, 0xce51a0b9u, 0u, 0xbe5f7bf1u, 1u, 0xce51a0b9u, 0x1e5f7bf1u}, + {0x6424c449u, 0xb20a5527u, 0x6424c449u, 0u, 0x6424c449u, 1u, 0x6424c449u, 0xe424c449u}, + {0x3fe06be1u, 0x5f158c37u, 0x4040cd86u, 1u, 0x3fe06be1u, 0u, 0x7f800000u, 0x3fe06be1u}, + {0x1e20c8acu, 0xffc00001u, 0x1e20c8acu, 0u, 0x1e20c8acu, 1u, 0xffc00001u, 0xffc00001u}, + {0x9e70fb36u, 0xbf800000u, 0x9e70fb36u, 0u, 0x9e70fb36u, 0u, 0x00000000u, 0x1e70fb36u}, + {0x7ef130f1u, 0x7e923a93u, 0x7ef130f1u, 1u, 0x5ef130f1u, 0u, 0x7f800000u, 0x7ef130f1u}, + {0xce5288a4u, 0x1e1208a1u, 0xce5288a4u, 1u, 0xce5288a4u, 1u, 0xee5288a4u, 0xce5288a4u}, + {0xc07a4dc6u, 0xcb34440eu, 0x809616ddu, 0u, 0xe07a4dc6u, 1u, 0x4c30413au, 0x407a4dc6u}, + {0x00bb046cu, 0x00bb046cu, 0x308b9b8au, 0u, 0x20bb046cu, 1u, 0x308b9b8au, 0x00bb046cu}, + {0xb26bd3d1u, 0xb26bd3d1u, 0xe46366bau, 1u, 0xd26bd3d1u, 1u, 0xff800000u, 0x326bd3d1u}, + {0x7ec83127u, 0x7ec83127u, 0xb27dbacfu, 0u, 0x5ec83127u, 1u, 0x7f800000u, 0x80000000u}, + {0x3fc5fb11u, 0x5f11a8b1u, 0x8f73a618u, 0u, 0x5fc5fb11u, 1u, 0x5f614b55u, 0x80000000u}, + {0x647b3a94u, 0x7f68b88eu, 0x647b3a94u, 1u, 0x447b3a94u, 0u, 0x7f800000u, 0x647b3a94u}, + {0xbfb5d6b5u, 0xbfcf8ccau, 0xbfb5d6b5u, 1u, 0xbfb5d6b5u, 0u, 0x1f62050bu, 0x3fb5d6b5u}, + {0xbf800000u, 0x9e70fb36u, 0xbf800000u, 0u, 0xbf800000u, 0u, 0xbf800000u, 0x3f800000u}, + {0x1e52f93au, 0x1e52f93au, 0xff800000u, 1u, 0x3e52f93au, 1u, 0xff800000u, 0xff800000u}, + {0x3fbfb16fu, 0x3fbfb16fu, 0x7f800000u, 1u, 0x5fbfb16fu, 1u, 0x7f800000u, 0x7f800000u}, + {0x00000000u, 0x00000000u, 0x0082f0eeu, 1u, 0xffc00000u, 0u, 0x00000000u, 0x7f800000u}, + {0x8f6d0aceu, 0x3f8246e1u, 0xbf3a8373u, 1u, 0x8f6d0aceu, 0u, 0x9f3a8373u, 0x8f6d0aceu}, + {0x7f38e817u, 0x7f38e817u, 0xffc00001u, 1u, 0x5f38e817u, 0u, 0xffc00001u, 0xffc00001u}, + {0x7f480adbu, 0x7f480adbu, 0xff800000u, 1u, 0x5f480adbu, 0u, 0xff800000u, 0xff800000u}, + {0x2d3020f8u, 0x2d3020f8u, 0x5cfabac2u, 0u, 0x2d3020f8u, 0u, 0x5cfabac2u, 0x2d3020f8u}, + {0x00d7c929u, 0x00d7c929u, 0x7fc00000u, 1u, 0x20d7c929u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0x64367202u, 0x7f800000u, 0x64367202u, 0u, 0x44367202u, 0u, 0x7f800000u, 0x00000000u}, + {0xff64c467u, 0x32730f8du, 0x3fc544e7u, 0u, 0xff64c467u, 0u, 0xf2593453u, 0x7f64c467u}, + {0x9e50a6acu, 0x9e50a6acu, 0xffc00001u, 1u, 0xbe50a6acu, 1u, 0xffc00001u, 0xffc00001u}, + {0xc12da640u, 0x8f7fdc41u, 0xc12da640u, 0u, 0xc12da640u, 1u, 0xc12da640u, 0x412da640u}, + {0xb20a5527u, 0xb20a5527u, 0x6424c449u, 1u, 0xd20a5527u, 1u, 0x7f800000u, 0xb20a5527u}, + {0xe454deddu, 0x3fac7804u, 0xc03d668fu, 0u, 0xe454deddu, 0u, 0xe48f6988u, 0xe454deddu}, + {0x7f7fffffu, 0x00f745cfu, 0x7f7fffffu, 0u, 0x7f7fffffu, 1u, 0x7f7fffffu, 0x7f7fffffu}, + {0xe43875cdu, 0xb262f40cu, 0xe43875cdu, 0u, 0xe43875cdu, 1u, 0xe43875cdu, 0x643875cdu}, + {0x0cf54f3du, 0x3f92bbe1u, 0x0cf54f3du, 0u, 0x2cf54f3du, 1u, 0x0d83a16cu, 0x0cf54f3du}, + {0x3ff8566fu, 0x3ff8566fu, 0x0cd303a0u, 0u, 0x3ff8566fu, 1u, 0x4070e794u, 0x3ff8566fu}, + {0xbfb5b358u, 0x1e466e03u, 0xff1d9d01u, 1u, 0xbfb5b358u, 1u, 0xff800000u, 0xbfb5b358u}, + {0x30ec8485u, 0x80a78d45u, 0x30ec8485u, 0u, 0x30ec8485u, 1u, 0x30ec8485u, 0xb0ec8485u}, + {0xbf800000u, 0xbf800000u, 0x9e70fb36u, 1u, 0xbf800000u, 0u, 0x1f800000u, 0x3f800000u}, + {0x7fc00000u, 0x7fc00000u, 0x80b7d094u, 1u, 0x7fc00000u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0x00800000u, 0x00d2a4bcu, 0x00800000u, 0u, 0x20800000u, 0u, 0x00800000u, 0x00800000u}, + {0x00d7c929u, 0x7fc00000u, 0x00d7c929u, 0u, 0x00d7c929u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0xa0126ddfu, 0xb2686f07u, 0x80d55493u, 1u, 0xc0126ddfu, 1u, 0x00000000u, 0x20126ddfu}, + {0xbf800000u, 0xbf800000u, 0x1e54a2f3u, 1u, 0xbf800000u, 0u, 0x1f800000u, 0xbf800000u}, + {0x647d538bu, 0x647d538bu, 0x7f7fffffu, 1u, 0x647d538bu, 0u, 0x7f800000u, 0x647d538bu}, + {0x80000000u, 0x7f045f1du, 0x80000000u, 0u, 0xffc00000u, 1u, 0x80000000u, 0x80000000u}, + {0x7f045f1du, 0x7f045f1du, 0x80000000u, 1u, 0xffc00000u, 1u, 0x7f800000u, 0x80000000u}, + {0xe471d0ceu, 0x40018127u, 0x809c24bcu, 1u, 0xff800000u, 1u, 0xc4f4a86eu, 0xe471d0ceu}, + {0x7fc00000u, 0x809b6f32u, 0x7fc00000u, 0u, 0x7fc00000u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0x0c381f29u, 0xbfe7ec61u, 0x0c381f29u, 1u, 0x2c381f29u, 1u, 0x80000000u, 0x8c381f29u}, + {0x00800000u, 0xff44e998u, 0x00800000u, 0u, 0x00800000u, 1u, 0xc044e998u, 0x80000000u}, + {0x40741c08u, 0xdf6dba69u, 0x642826beu, 0u, 0x40741c08u, 0u, 0x6427440eu, 0xc0741c08u}, + {0x00000000u, 0x0082f0eeu, 0x00000000u, 0u, 0xffc00000u, 0u, 0x00000000u, 0x00000000u}, + {0x7fc00000u, 0x7fc00000u, 0x809b6f32u, 1u, 0x7fc00000u, 1u, 0x7fc00000u, 0x7fc00000u}, + {0xff800000u, 0x808beb57u, 0xff800000u, 0u, 0xff800000u, 1u, 0xffc00000u, 0x7f800000u}, + {0x0f017877u, 0x0f017877u, 0xc1720b19u, 1u, 0x2f017877u, 1u, 0xe1720b19u, 0x8f017877u}, + {0x7f800001u, 0x7f800001u, 0x3fac0a5au, 1u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x7f800001u, 0x3f96a6b3u, 0x7f800001u, 0u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x7f800000u, 0x7f800000u, 0x3fbfb16fu, 1u, 0x7f800000u, 1u, 0x7f800000u, 0x00000000u}, + {0x7f800001u, 0xbf9b99f7u, 0x7f800001u, 0u, 0x7f800001u, 1u, 0x7fc00001u, 0x7fc00001u}, + {0x80000000u, 0x80000000u, 0x7f045f1du, 1u, 0xffc00000u, 1u, 0x7f800000u, 0xff800000u}, + {0xdd58a55cu, 0xad4c8fbbu, 0xdd58a55cu, 0u, 0xdd58a55cu, 1u, 0xdd58a55cu, 0x5d58a55cu}, + {0xe476a68cu, 0xe476a68cu, 0x342e1260u, 1u, 0xe476a68cu, 1u, 0x696da481u, 0xe476a68cu}, + {0x7f800000u, 0x7f800000u, 0x64367202u, 1u, 0x7f800000u, 0u, 0x7f800000u, 0x00000000u}, + {0xff670fe9u, 0xff670fe9u, 0x6449aa84u, 0u, 0xdf670fe9u, 0u, 0x7f800000u, 0xff670fe9u}, + {0x7f7fffffu, 0x7f7fffffu, 0x7f765e02u, 1u, 0x5f7fffffu, 0u, 0x7f800000u, 0x7f7fffffu}, + {0x308b9b8au, 0x00bb046cu, 0x308b9b8au, 1u, 0x308b9b8au, 1u, 0x108b9b8au, 0x308b9b8au}, + {0xb27dbacfu, 0x7ec83127u, 0xb27dbacfu, 1u, 0xb27dbacfu, 1u, 0xd1c66aa9u, 0x80000000u}, + {0xbf800000u, 0xbf800000u, 0x00ef1dbeu, 1u, 0xbf800000u, 1u, 0x1f800000u, 0xbf800000u}, + {0xad4c8fbbu, 0xad4c8fbbu, 0xdd58a55cu, 1u, 0xcd4c8fbbu, 1u, 0xfd58a55cu, 0x2d4c8fbbu}, + {0x8087bc0au, 0x7f800000u, 0x8087bc0au, 0u, 0x8087bc0au, 1u, 0xff800000u, 0x80000000u}, + {0x8c367fd8u, 0xbf976e24u, 0x8c367fd8u, 1u, 0xac367fd8u, 1u, 0x00000000u, 0x0c367fd8u}, + {0x00f745cfu, 0x7f7fffffu, 0x00f745cfu, 0u, 0x00f745cfu, 1u, 0x40f745ceu, 0x00000000u}, + {0xcb7cf32du, 0xbfe52415u, 0xbf6a1e62u, 0u, 0xcb7cf32du, 0u, 0x4be2692eu, 0x4b7cf32du}, +}; + +struct DivisionVector { + std::uint32_t numerator, denominator, quotient; +}; + +constexpr DivisionVector DivisionVectors[] = { + {0xe3dee157u, 0x80000000u, 0x7f800000u}, + {0x7f800000u, 0x00000000u, 0x7f800000u}, + {0x00000000u, 0x7d286f49u, 0x00000000u}, + {0x80000000u, 0x2de8e39fu, 0x80000000u}, + {0x80000000u, 0x80000000u, 0xffc00000u}, + {0xae69fdd9u, 0x7f800000u, 0x80000000u}, + {0x158f198fu, 0x7fc00000u, 0x7fc00000u}, + {0x7fc00000u, 0xa28563e3u, 0x7fc00000u}, + {0x8ab448f2u, 0xff800000u, 0x00000000u}, + {0xab517ff7u, 0x80000000u, 0x7f800000u}, + {0x7d97a78fu, 0x00000000u, 0x7f800000u}, + {0x00800000u, 0xff800000u, 0x80000000u}, + {0x07d72584u, 0x7f800000u, 0x00000000u}, + {0x7f800000u, 0x028e5995u, 0x7f800000u}, + {0x7f800000u, 0x8fd1d2fcu, 0xff800000u}, + {0x7f800000u, 0xc8fcfac8u, 0xff800000u}, + {0xffb58830u, 0x0b03c189u, 0xfff58830u}, + {0x00000000u, 0x00000000u, 0xffc00000u}, + {0x4b176ae2u, 0x7f800000u, 0x00000000u}, + {0xff800000u, 0xd128a061u, 0x7f800000u}, + {0x7f800000u, 0xa16ffa0fu, 0xff800000u}, + {0x7f800000u, 0xeb57c3eau, 0xff800000u}, + {0x65a318c4u, 0x7fc00000u, 0x7fc00000u}, + {0x7f800000u, 0x36653c58u, 0x7f800000u}, + {0x5c8d01a1u, 0xff800000u, 0x80000000u}, + {0x80000000u, 0x6a029abbu, 0x80000000u}, + {0x34adc63fu, 0x7fc00000u, 0x7fc00000u}, + {0xff800000u, 0x669ee8c3u, 0xff800000u}, + {0x3f800000u, 0xff800000u, 0x80000000u}, + {0x58e93279u, 0x00000000u, 0x7f800000u}, + {0xff800000u, 0x7fc00000u, 0x7fc00000u}, + {0x7fc00000u, 0xb2176d74u, 0x7fc00000u}, + {0x8625b5e8u, 0xff800000u, 0x00000000u}, + {0x00800000u, 0x7f800000u, 0x00000000u}, + {0x7f800000u, 0x4857edb0u, 0x7f800000u}, + {0xff800000u, 0xfb94c85eu, 0x7f800000u}, + {0x80000000u, 0xbe30d5ddu, 0x00000000u}, + {0x4a404d78u, 0x80000000u, 0xff800000u}, + {0x665bf5f3u, 0x7fc00000u, 0x7fc00000u}, + {0x7f800000u, 0x93355305u, 0xff800000u}, + {0x00800000u, 0x317f714au, 0x0e804783u}, + {0xf9b2d54eu, 0x3b51431du, 0xfddac65fu}, + {0x8aa68ff0u, 0xc4bc830au, 0x0562313cu}, + {0x876d9e88u, 0xa9d96c3eu, 0x1d0be3d3u}, + {0x6d6267c5u, 0x0b4d7042u, 0x7f800000u}, + {0x4f00ed00u, 0x7f7fffffu, 0x0f00ed01u}, + {0xe2d26af2u, 0x9a4f604bu, 0x7f800000u}, + {0xc242b44cu, 0x02e4ea2cu, 0xfed9bde4u}, + {0xd0829117u, 0xa207b0b7u, 0x6df6556cu}, + {0x772d2c58u, 0x475e77e8u, 0x6f47465au}, + {0x608d6c6fu, 0x21a7aea0u, 0x7e57e93eu}, + {0x6f478919u, 0xb8842300u, 0xf64149efu}, + {0xc9c59d3du, 0x116dd253u, 0xf7d4b824u}, + {0x85433206u, 0x1d2bb171u, 0xa7918576u}, + {0xfa67e0eau, 0x3899423au, 0xff800000u}, + {0xd436dfa1u, 0x80dba74au, 0x7f800000u}, + {0x8777deb5u, 0xc30f8bb4u, 0x03dd06b0u}, + {0x605572a8u, 0x1a67e330u, 0x7f800000u}, + {0x79125ecfu, 0x0b437b7fu, 0x7f800000u}, + {0xc8ca1f2du, 0x097f84f8u, 0xfeca807fu}, + {0xbd0da9c7u, 0x0fc66a44u, 0xecb6c6fdu}, + {0xf14b1163u, 0x22a238c4u, 0xff800000u}, + {0x7ad9ca61u, 0x073d9b37u, 0x7f800000u}, + {0x66643d1du, 0x8bbbd39bu, 0xff800000u}, + {0x89732b55u, 0x0ba81e0au, 0xbd39247bu}, + {0xfd3209aau, 0x1ef111a3u, 0xff800000u}, + {0xee8e6759u, 0xc00c6767u, 0x6e01d2b8u}, + {0xf40fc2c0u, 0xa475dd1eu, 0x7f800000u}, + {0xead5c772u, 0xa0cbc3e9u, 0x7f800000u}, + {0xe5e62b46u, 0xabc8c04au, 0x7992c1ccu}, + {0xc02f4f0cu, 0x71f212d8u, 0x8db964ebu}, + {0xccf2a65du, 0x07ccf97eu, 0xff800000u}, + {0x808dc1feu, 0xbbbef5adu, 0x043e0a43u}, + {0xf59975c9u, 0xb604be74u, 0x7f13f9ceu}, + {0xf18e480bu, 0xa7115af0u, 0x7f800000u}, + {0xd6a95beau, 0x220a4e4eu, 0xf41cbd3eu}, + {0xe69ca704u, 0xb5a11c35u, 0x7078eaadu}, + {0x75c45443u, 0x0595db97u, 0x7f800000u}, + {0x7918670au, 0x9dac557eu, 0xff800000u}, + {0x730a5a92u, 0x3dd816b8u, 0x74a3e853u}, + {0xb51c2ba2u, 0xeb40a1edu, 0x094f8b24u}, + {0x0ae9aa6cu, 0x866defe2u, 0xc3fb6787u}, + {0x9bd226aeu, 0xd8b390e9u, 0x0295cd4bu}, + {0x01bb6a0eu, 0x93abb870u, 0xad8bb2b9u}, + {0x88a561b0u, 0x2f723b1eu, 0x98aec847u}, + {0x0390d452u, 0x0515369eu, 0x3df87a84u}, + {0x08bebe3du, 0x46b7577bu, 0x01852ad3u}, + {0xe9c12158u, 0x2223c9b7u, 0xff800000u}, + {0x7c6cefc9u, 0x4583ea92u, 0x7665e70eu}, + {0x2e53b212u, 0xe5a8791du, 0x8820d6c5u}, + {0x017897b0u, 0x91b73337u, 0xaf2db065u}, + {0x76224957u, 0x8c165b20u, 0xff800000u}, + {0x86a3f032u, 0x2525c397u, 0xa0fd2e2cu}, + {0x3e883abbu, 0xfc0d4986u, 0x81f6d5f3u}, + {0x9e372e18u, 0x5c818534u, 0x813507a5u}, + {0xdeb312d7u, 0x9acace22u, 0x7f800000u}, + {0x5237b396u, 0x0744f739u, 0x7f800000u}, + {0x71de638fu, 0xb6b87fbau, 0xfa9a4976u}, + {0xcc37d5b4u, 0x95083d46u, 0x76acb78du}, + {0xd299d8fau, 0x9686a46fu, 0x7b9241ffu}, + {0x224a7c93u, 0xdaa84d03u, 0x8719fff0u}, + {0x53bfd745u, 0x8e9e4e95u, 0xff800000u}, + {0xe4cc99e2u, 0xad5066b2u, 0x76fb54ffu}, + {0xd8e97425u, 0x97d9db39u, 0x7f800000u}, + {0xe2db4583u, 0xa928eb28u, 0x792627d1u}, + {0x2c454528u, 0x68c6c2a5u, 0x02fe14a6u}, + {0x428f0081u, 0x7a2f8b6bu, 0x07d08adfu}, + {0x01d953efu, 0x3f800000u, 0x01d953efu}, + {0x7121b471u, 0x8088f485u, 0xff800000u}, + {0xf1f7c04bu, 0x39387616u, 0xf82beaeau}, + {0x6d51832eu, 0x2de5afd7u, 0x7ee983acu}, + {0x81bf79a9u, 0x00800000u, 0xc0bf79a9u}, + {0x07d230aau, 0x9acd5dbbu, 0xac8301b5u}, + {0x584ce07fu, 0x0c0e914au, 0x7f800000u}, + {0x913415a0u, 0xca029043u, 0x06b08c74u}, + {0x7aa397efu, 0xb2ec5109u, 0xff800000u}, + {0xf3ac9c17u, 0x827fdebau, 0x7f800000u}, + {0xcde216e6u, 0x83ee1066u, 0x7f800000u}, + {0x839bc54au, 0x20d9c620u, 0xa2371cfdu}, + {0x3775f1ccu, 0x01cce5d7u, 0x7519a457u}, + {0x0e532d25u, 0xc04f0d5bu, 0x8d828cb1u}, + {0xfde3772eu, 0x4c0b2b28u, 0xf1513600u}, + {0x7c6432b3u, 0xaa19e870u, 0xff800000u}, + {0x79abed21u, 0x092e8685u, 0x7f800000u}, + {0x14fcceacu, 0x4e2eb80du, 0x0639353bu}, + {0x4318f5f3u, 0xf97d69fbu, 0x891a858bu}, + {0x640bced3u, 0x9b1af89eu, 0xff800000u}, + {0xe0d29c29u, 0x27524c08u, 0xf90030c6u}, + {0xe5d38cafu, 0xb742f8e4u, 0x6e0ae211u}, + {0x84d418fau, 0x087a2413u, 0xbbd910c8u}, + {0xcfa7ea5cu, 0x21a0fe65u, 0xed8580d2u}, + {0x796273d7u, 0x42c30582u, 0x7614a11du}, + {0xfa8aa86eu, 0x037e7c79u, 0xff800000u}, + {0xea63e3cdu, 0x16da0286u, 0xff800000u}, + {0x598945e1u, 0xa7122831u, 0xf1f07073u}, + {0xfc238cb3u, 0x8ff12928u, 0x7f800000u}, + {0xcce93ef2u, 0x91d1fed5u, 0x7a8e2c09u}, + {0x86b96fd5u, 0xbcf0632du, 0x09457b04u}, + {0x7f7fffffu, 0xca896e5fu, 0xf46e6ea0u}, + {0xb05c8919u, 0x00800000u, 0xef5c8919u}, + {0x606e469bu, 0x8393b4ecu, 0xff800000u}, + {0x69399f53u, 0x2d527809u, 0x7b61c744u}, + {0x72650882u, 0x39f93b37u, 0x77eb40deu}, + {0x398bfb21u, 0x7846fc97u, 0x00b4168cu}, + {0x92f2cb83u, 0x40287cd4u, 0x92387387u}, + {0xf0c8a771u, 0x996de56au, 0x7f800000u}, + {0xff5681d6u, 0xa60673cbu, 0x7f800000u}, + {0xb7597f52u, 0x6cf2fb88u, 0x89e52643u}, + {0xea9dd599u, 0x9fa0bae8u, 0x7f800000u}, + {0x481abc64u, 0x93f33641u, 0xf3a2df35u}, + {0x8eb269d4u, 0x46243494u, 0x880b1347u}, + {0x724d0132u, 0x85df67aau, 0xff800000u}, + {0x57956b63u, 0x962cdb4eu, 0xff800000u}, + {0x6fc42592u, 0x1903c22au, 0x7f800000u}, + {0x7d0acd93u, 0x0d2dc6ecu, 0x7f800000u}, + {0xe6211b36u, 0x3700e1deu, 0xeea000dfu}, + {0x9b90cc8cu, 0x58698b1au, 0x829eb8ebu}, + {0xdc3b6104u, 0x190a7096u, 0xff800000u}, + {0x5b84018du, 0x1e866429u, 0x7c7b74dcu}, + {0x5be372c9u, 0xfef8a195u, 0x9c6a307cu}, + {0x517e3583u, 0xdda25371u, 0xb3487405u}, + {0x72ef0eefu, 0xb0bdfa76u, 0xff800000u}, + {0xb0158865u, 0x4d2cf2a2u, 0xa25d573cu}, + {0xdc60970du, 0x1a8e69c4u, 0xff800000u}, + {0xd5e9cd6bu, 0x5ea838eeu, 0xb6b1e652u}, + {0x658b36fbu, 0x35c0066bu, 0x6f399870u}, + {0x0a1e2700u, 0x8da375e7u, 0xbbf7afc9u}, + {0x5b7da581u, 0xb2f0cb15u, 0xe806d51cu}, + {0x7f7fffffu, 0x0433daa0u, 0x7f800000u}, + {0x7f7fffffu, 0x95ac086du, 0xff800000u}, + {0x4e0861a7u, 0xf6d980e1u, 0x96a0851fu}, + {0xc9541dd2u, 0x2f3fd229u, 0xd98d8b02u}, + {0xbc9d2461u, 0x23df3638u, 0xd83439a3u}, + {0x6dc5a629u, 0x4568fbc4u, 0x67d92cc6u}, + {0x3c9ba9b1u, 0xacb2c820u, 0xcf5ee551u}, + {0x1421b85cu, 0xa69c6409u, 0xad045ca6u}, + {0x53beba29u, 0xf31704c7u, 0xa021a7f3u}, + {0x82192998u, 0x31c876a7u, 0x8fc3983eu}, + {0x4294e66eu, 0x99dd8951u, 0xe82c1060u}, + {0x3a4e195cu, 0x5fc513ffu, 0x1a05dbe8u}, + {0x8d200897u, 0x32fc6f55u, 0x99a24b2fu}, + {0x1141536au, 0xa39b7df9u, 0xad1f24fdu}, + {0xd3385b3au, 0x063379a0u, 0xff800000u}, + {0x27c60c1bu, 0x28dd41c1u, 0x3e65255bu}, + {0x7ce1a972u, 0xd2a87d68u, 0xe9ab6ed9u}, + {0x7cbafd62u, 0x2979b8beu, 0x7f800000u}, + {0xb9326a7fu, 0x69d3e6b0u, 0x8ed78bd8u}, + {0x63ae014au, 0x5224b4f6u, 0x510739d8u}, + {0xf38151ccu, 0xce17d36eu, 0x64da0d12u}, + {0xe1603a5cu, 0xef4341ecu, 0x3192fdceu}, + {0x0e4af104u, 0x8785972au, 0xc64272e4u}, + {0x0d375114u, 0x9f1eb6fau, 0xad93d748u}, + {0xb5901165u, 0x38be3792u, 0xbc41e41eu}, + {0xef33cc9du, 0xb0ae4904u, 0x7e040cb8u}, + {0x67aa9fd7u, 0xba1f3754u, 0xed092beau}, + {0x5a1e348bu, 0xea23192eu, 0xaf7851dbu}, + {0xe6d4014au, 0x4dd05433u, 0xd8824238u}, + {0x56a55786u, 0x024f7d8du, 0x7f800000u}, + {0xa2122049u, 0x1feb66bau, 0xc19ee9b0u}, + {0xd132c43bu, 0x1dea2471u, 0xf2c37469u}, + {0x432e8ba9u, 0x2e88d47cu, 0x5423481au}, + {0x1850323bu, 0x1832e2dau, 0x3f94f8f6u}, + {0x00800000u, 0x25625546u, 0x1a90c71cu}, + {0x6cc95afbu, 0x378328fcu, 0x74c4810au}, + {0xe990e85eu, 0x3a842b5du, 0xee8c5622u}, + {0x7e879109u, 0x110a438bu, 0x7f800000u}, + {0x74329dceu, 0xac33996au, 0xff800000u}, + {0x4a35bb0fu, 0x6993f7bcu, 0x201d34f0u}, + {0x388a8a0du, 0xdd233765u, 0x9ad94b70u}, + {0x7d1fff1bu, 0x5650867au, 0x66446c37u}, + {0xaa59390au, 0xc06961c1u, 0x296e4660u}, + {0x287f2d31u, 0xb32aa5a1u, 0xb4bf678bu}, + {0x65f19f8cu, 0x68233954u, 0x3d3d7b0au}, + {0x7b3dfc68u, 0x6ad2b4f6u, 0x4fe6d330u}, + {0xe54c5dc1u, 0xadba7e3eu, 0x770c446au}, + {0xd7423212u, 0x61d0b08au, 0xb4ee385cu}, + {0x7961bae1u, 0xefaddaa7u, 0xc926318fu}, + {0x23837b8fu, 0xa677b68eu, 0xbc87e198u}, + {0x4a21f060u, 0xe82da385u, 0xa16ec02fu}, + {0xc08ac2d7u, 0x57a7239fu, 0xa85488d7u}, + {0x5b2575dau, 0xaa261591u, 0xf07f09d1u}, + {0xe8e4400eu, 0x744d1bbdu, 0xb40e7122u}, + {0xf759815eu, 0x8c44c54au, 0x7f800000u}, + {0xb92685b4u, 0xe1b2dd90u, 0x16ee5579u}, + {0xc0238882u, 0xff19d84cu, 0x00880f8du}, + {0xd3e8101eu, 0x265827c9u, 0xed096b80u}, + {0xcabf5e75u, 0xae028fa1u, 0x5c3b9d79u}, + {0xdaad9cf6u, 0x4dfe8850u, 0xcc2e9d36u}, + {0xb9254881u, 0x20d130b9u, 0xd7ca449au}, + {0x43cd5cf2u, 0x6d6146dau, 0x15e95ed7u}, + {0x13ebd9adu, 0x3744c369u, 0x1c196d5au}, + {0xc9f5a0acu, 0x60672221u, 0xa90806dfu}, + {0x359fc7dbu, 0xc40c5e6cu, 0xb111b38bu}, + {0xfb249d39u, 0x054862aeu, 0xff800000u}, + {0xebe35952u, 0xc705485au, 0x645a5687u}, + {0xeae5a25au, 0x2285a751u, 0xff800000u}, + {0x060a73c8u, 0xbad0c9d8u, 0x8aa9c261u}, + {0x1abec008u, 0xb1b65371u, 0xa885ea03u}, + {0xb3e17130u, 0x146a397fu, 0xdef666acu}, + {0x40bbb0bbu, 0x79fd8458u, 0x063d8756u}, + {0x3f800000u, 0x2cf06166u, 0x52085136u}, + {0x080b6982u, 0x387b8778u, 0x0f0de3e0u}, + {0x7ad9ca61u, 0x073d9b37u, 0x7f800000u}, + {0xd220503bu, 0xf05b4232u, 0x213b2d64u}, + {0xd36f7e11u, 0x548e1cf5u, 0xbe57b56du}, + {0xf198249au, 0x04bbdebfu, 0xff800000u}, + {0xe537475du, 0x114da108u, 0xff800000u}, + {0xaf2fc53du, 0x44fc093bu, 0xa9b288f1u}, + {0xe61f7edcu, 0x23008969u, 0xff800000u}, + {0x80c73e60u, 0xbab63089u, 0x058bfb49u}, + {0xfae7a548u, 0xb4ac1359u, 0x7f800000u}, + {0xd5425622u, 0x274a6b92u, 0xed75c6cau}, + {0x25f1076bu, 0x9636719bu, 0xcf291a41u}, + {0xb4406ecbu, 0x4112106fu, 0xb2a8a251u}, + {0xaa6e3b43u, 0x32ec8ca0u, 0xb700e906u}, + {0x2210592fu, 0x0ac8fdd3u, 0x56b7dabeu}, +}; + +std::array BufferDescriptor(const void* data, std::uint32_t count) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu) | (4u << 16u), count, 0x01016facu}; +} + +class Shader { +public: + Shader(AgcDriver::VulkanDevice& device, std::span code) : device(device), code(code) { + std::vector userData(8, 0u); + const auto input = BufferDescriptor(Input.data(), static_cast(Input.size())); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size())); + std::copy(input.begin(), input.end(), userData.begin()); + std::copy(output.begin(), output.end(), userData.begin() + 4); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + result = ShaderRecompiler::Recompile(request); + } + + void Run() { + Output.fill(0xdeadbeefu); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); + } + +private: + AgcDriver::VulkanDevice& device; + std::span code; + ShaderRecompiler::RecompileResult result; +}; + +bool IsNan(std::uint32_t bits) { + return (bits & 0x7fffffffu) > 0x7f800000u; +} + +bool Same(std::uint32_t actual, std::uint32_t expected) { + return actual == expected || (IsNan(actual) && IsNan(expected)); +} + +std::string Hex(std::uint32_t value) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x", value); + return text; +} + +bool HostFusesFma(AgcDriver::VulkanDevice& device) { + Shader shader(device, FmaCode); + Input.fill(0u); + Input[0] = 0xbff2fb6du; + Input[1] = 0xbcaac14cu; + Input[2] = 0xbd22126eu; + shader.Run(); + return Output[0] == 0x2f8e8ae0u; +} + +void CheckHelpers(AgcDriver::VulkanDevice& device, bool fused) { + Shader shader(device, HelperCode); + constexpr std::uint32_t count = sizeof(HelperVectors) / sizeof(HelperVectors[0]); + for (std::uint32_t first = 0; first < count; first += Threads) { + Input.fill(0u); + for (std::uint32_t lane = 0; lane < Threads && first + lane < count; ++lane) { + const auto& vector = HelperVectors[first + lane]; + std::copy_n(std::array{vector.s0, vector.s1, vector.s2, vector.vcc}.begin(), 4, Input.begin() + lane * Stride); + } + shader.Run(); + for (std::uint32_t lane = 0; lane < Threads && first + lane < count; ++lane) { + const auto& vector = HelperVectors[first + lane]; + const auto* out = &Output[lane * Stride]; + const std::string where = "division helpers: vector " + std::to_string(first + lane) + " (" + Hex(vector.s0) + ", " + Hex(vector.s1) + ", " + Hex(vector.s2) + ")"; + if (vector.s0 == vector.s1 || vector.s0 == vector.s2) { + Require(Same(out[0], vector.scale) && out[1] == vector.scaleVcc, where + ": v_div_scale_f32 is " + Hex(out[0]) + " vcc " + std::to_string(out[1]) + ", expected " + Hex(vector.scale) + " vcc " + std::to_string(vector.scaleVcc)); + } + Require(!fused || Same(out[2], vector.fmas), where + " vcc " + std::to_string(vector.vcc) + ": v_div_fmas_f32 is " + Hex(out[2]) + ", expected " + Hex(vector.fmas)); + Require(Same(out[3], vector.fixup), where + ": v_div_fixup_f32 is " + Hex(out[3]) + ", expected " + Hex(vector.fixup)); + } + } +} + +void CheckDivision(AgcDriver::VulkanDevice& device) { + Shader shader(device, DivisionCode); + constexpr std::uint32_t count = sizeof(DivisionVectors) / sizeof(DivisionVectors[0]); + for (std::uint32_t first = 0; first < count; first += Threads) { + Input.fill(0u); + for (std::uint32_t lane = 0; lane < Threads && first + lane < count; ++lane) { + Input[lane * Stride] = DivisionVectors[first + lane].numerator; + Input[lane * Stride + 1] = DivisionVectors[first + lane].denominator; + } + shader.Run(); + for (std::uint32_t lane = 0; lane < Threads && first + lane < count; ++lane) { + const auto& vector = DivisionVectors[first + lane]; + const auto actual = Output[lane * Stride]; + Require(Same(actual, vector.quotient), "division macro: " + Hex(vector.numerator) + " / " + Hex(vector.denominator) + " is " + Hex(actual) + ", expected " + Hex(vector.quotient)); + } + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + const bool fused = HostFusesFma(*device); + CheckHelpers(*device, fused); + if (fused) { + CheckDivision(*device); + } else { + std::puts("the device splits fma into a multiply and an add, so v_div_fmas_f32 and the division macro are not checked"); + } + std::puts("division tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp index 74a73cd99..a13ec68d0 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp @@ -408,6 +408,9 @@ enum class RdnaOpcode : std::uint16_t { VFrexpExpI32F64, VFrexpMantF64, VFractF64, + VDivScaleF32, + VDivFmasF32, + VDivFixupF32, VCvtPknormI16F32, VCvtPknormU16F32, VCvtPkU16U32, @@ -886,6 +889,7 @@ enum class RdnaOpcode : std::uint16_t { SMemrealtime, BufferGl0Inv, BufferGl1Inv, + SDenormMode, Exp, VMulHiI32I24, VMulHiU32U24, diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp index c85252645..56e3db502 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaOpcode.cpp @@ -358,6 +358,9 @@ bool IsVectorAluOpcode(RdnaOpcode opcode) { case RdnaOpcode::VFrexpExpI32F64: case RdnaOpcode::VFrexpMantF64: case RdnaOpcode::VFractF64: + case RdnaOpcode::VDivScaleF32: + case RdnaOpcode::VDivFmasF32: + case RdnaOpcode::VDivFixupF32: case RdnaOpcode::VCvtPknormI16F32: case RdnaOpcode::VCvtPknormU16F32: case RdnaOpcode::VCvtPkU16U32: diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp index 17ec5a2cc..6d46f1617 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaScalarOpDecoder.cpp @@ -214,6 +214,7 @@ RdnaOpcode decodeSoppOpcode(std::uint32_t opcode) { case 0x21u: return RdnaOpcode::SClause; case 0x22u: return RdnaOpcode::SWaitIdle; case 0x23u: return RdnaOpcode::SWaitcntDepctr; + case 0x25u: return RdnaOpcode::SDenormMode; case 0x28u: return RdnaOpcode::STtracedata; default: throw std::invalid_argument("unsupported SOPP opcode " + std::to_string(opcode)); } @@ -231,7 +232,8 @@ bool isSoppWaitOpcode(RdnaOpcode opcode) { opcode == RdnaOpcode::SSleep || opcode == RdnaOpcode::SSetprio || opcode == RdnaOpcode::SSendmsg || opcode == RdnaOpcode::STrap || opcode == RdnaOpcode::STtracedata || opcode == RdnaOpcode::SInstPrefetch || opcode == RdnaOpcode::SClause || opcode == RdnaOpcode::SCbranchCdbg || opcode == RdnaOpcode::SIcacheInv || - opcode == RdnaOpcode::SIncperflevel || opcode == RdnaOpcode::SDecperflevel || opcode == RdnaOpcode::SWaitIdle; + opcode == RdnaOpcode::SIncperflevel || opcode == RdnaOpcode::SDecperflevel || opcode == RdnaOpcode::SWaitIdle || + opcode == RdnaOpcode::SDenormMode; } std::uint32_t scalarDestinationDwordCount(RdnaOpcode opcode) { diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp index 5a9a8f172..19b4b1038 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp @@ -549,6 +549,9 @@ constexpr VectorOpcodeInfo vop3Opcodes[] = { {0x166u, RdnaOpcode::VMinF64}, {0x167u, RdnaOpcode::VMaxF64}, {0x168u, RdnaOpcode::VLdexpF64}, + {0x15fu, RdnaOpcode::VDivFixupF32}, + {0x16du, RdnaOpcode::VDivScaleF32}, + {0x16fu, RdnaOpcode::VDivFmasF32}, {0x363u, RdnaOpcode::VBfmB32}, {0x364u, RdnaOpcode::VBcntU32B32}, {0x365u, RdnaOpcode::VMbcntLoU32B32}, @@ -680,7 +683,7 @@ bool isVop3BCarryOutOpcode(RdnaOpcode opcode) { } bool isVop3BMadU64Opcode(RdnaOpcode opcode) { - return opcode == RdnaOpcode::VMadU64U32 || opcode == RdnaOpcode::VMadI64I32; + return opcode == RdnaOpcode::VMadU64U32 || opcode == RdnaOpcode::VMadI64I32 || opcode == RdnaOpcode::VDivScaleF32; } bool isPermlaneOpcode(RdnaOpcode opcode) { @@ -1758,6 +1761,8 @@ bool supportsNativeVop3SourceModifiers(RdnaOpcode opcode) { case RdnaOpcode::VMin3F32: case RdnaOpcode::VMax3F32: case RdnaOpcode::VMed3F32: + case RdnaOpcode::VDivFmasF32: + case RdnaOpcode::VDivFixupF32: case RdnaOpcode::VLdexpF32: case RdnaOpcode::VFmaF64: case RdnaOpcode::VAddF64: @@ -1830,7 +1835,7 @@ void checkNativeVop3Modifiers(RdnaOpcode opcode, bool permlane, bool carryInOut, } if (carryInOut || scalarDst) { const bool floatCompare = scalarDst && isVopcFloatCompareOpcode(opcode); - if (clamp != 0u || omod != 0u || (neg != 0u && !floatCompare)) { + if (clamp != 0u || omod != 0u || (neg != 0u && !floatCompare && opcode != RdnaOpcode::VDivScaleF32)) { throw std::invalid_argument("VOP3 source modifiers are not implemented"); } return; @@ -2110,6 +2115,9 @@ RdnaInstruction DecodeRdnaVop3(std::uint32_t programCounter, std::span(-96))})); + const IrU1 denominatorDenormal(ir.IEqual(denominatorExponent.Value(), ir.Constant(0u))); + const IrU1 denominatorHuge(ir.Emit(IrOpcode::UGreaterThanEqual32, IrType::U1, {&denominatorExponent.Value(), &ir.Constant(253u)})); + const IrU1 numeratorTiny(ir.Emit(IrOpcode::ULessThanEqual32, IrType::U1, {&numeratorExponent.Value(), &ir.Constant(24u)})); + const IrU1 isDenominator(ir.IEqual(bits.Value(), denominator.Value())); + const IrU1 notNearMax(ir.LogicalNot(nearMax.Value())); + const IrU1 notDenormal(ir.LogicalNot(denominatorDenormal.Value())); + const IrU1 normalRange(ir.LogicalAnd(notNearMax.Value(), notDenormal.Value())); + const IrU1 belowHuge(ir.LogicalAnd(normalRange.Value(), ir.LogicalNot(denominatorHuge.Value()))); + const IrU1 scaleUp(ir.LogicalOr( + ir.LogicalOr(ir.LogicalAnd(nearMax.Value(), isDenominator.Value()), ir.LogicalAnd(notNearMax.Value(), denominatorDenormal.Value())), + ir.LogicalAnd(belowHuge.Value(), ir.LogicalOr(ir.LogicalAnd(nearMin.Value(), ir.LogicalNot(isDenominator.Value())), + ir.LogicalAnd(ir.LogicalNot(nearMin.Value()), numeratorTiny.Value()))))); + const IrU1 scaleDown(ir.LogicalAnd(ir.LogicalAnd(normalRange.Value(), denominatorHuge.Value()), + ir.LogicalOr(ir.LogicalNot(nearMin.Value()), isDenominator.Value()))); + const IrF32 up(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &ir.ConstantF32(18446744073709551616.0f)})); + const IrF32 down(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &ir.ConstantF32(5.42101086242752217e-20f)})); + const IrU32 scaled(ir.Select(scaleUp.Value(), ir.BitCastU32(up.Value()), ir.Select(scaleDown.Value(), ir.BitCastU32(down.Value()), bits.Value()))); + const auto isZero = [&](IrU32 word) { return IrU1(ir.IEqual(ir.BitwiseAnd(word.Value(), ir.Constant(0x7fffffffu)), ir.Constant(0u))); }; + const IrU1 zero(ir.LogicalOr(isZero(denominator).Value(), isZero(numerator).Value())); + const IrU32 result(ir.Select(zero.Value(), ir.Constant(0xffc00000u), scaled.Value())); + writeOperand(inst.destination, &ir.BitCastF32(result.Value())); + writeMask(inst.destination2, IrU1(ir.LogicalOr(nearMax.Value(), ir.LogicalAnd(notDenormal.Value(), nearMin.Value())))); + return true; +} + +bool TranslationContext::vDivFmasF32(const RdnaInstruction& inst) { + RdnaOperand vcc{}; + vcc.kind = RdnaOperandKind::VccLo; + const IrU1 scale = readMask(vcc); + IrValue* lhs = readOperand(sourceAt(inst, 0u), IrType::F32); + IrValue* rhs = readOperand(sourceAt(inst, 1u), IrType::F32); + IrValue* addend = readOperand(sourceAt(inst, 2u), IrType::F32); + const auto exponent = [&](IrValue& value) { + return IrU32(ir.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {&ir.BitCastU32(value), &ir.Constant(23u), &ir.Constant(8u)})); + }; + const IrU1 up(ir.Emit(IrOpcode::UGreaterThanEqual32, IrType::U1, {&exponent(*addend).Value(), &ir.Constant(128u)})); + IrValue& power = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&up.Value(), &ir.ConstantF32(18446744073709551616.0f), &ir.ConstantF32(5.42101086242752217e-20f)}); + IrValue& plain = ir.Emit(IrOpcode::FPFma32, IrType::F32, {lhs, rhs, addend}); + const IrU32 plainExponent = exponent(plain); + const IrU1 plainInfinite(ir.IEqual(ir.BitwiseAnd(ir.BitCastU32(plain), ir.Constant(0x7fffffffu)), ir.Constant(0x7f800000u))); + const IrU1 rescale(ir.LogicalOr(ir.LogicalAnd(up.Value(), ir.IEqual(plainExponent.Value(), ir.Constant(0u))), ir.LogicalAnd(ir.LogicalNot(up.Value()), plainInfinite.Value()))); + IrValue& lhsMagnitude = ir.BitwiseAnd(ir.BitCastU32(*lhs), ir.Constant(0x7fffffffu)); + IrValue& rhsMagnitude = ir.BitwiseAnd(ir.BitCastU32(*rhs), ir.Constant(0x7fffffffu)); + const IrU1 lhsLarger(ir.UGreaterThan(lhsMagnitude, rhsMagnitude)); + const IrU1 scaleLhs(ir.LogicalOr(ir.LogicalAnd(up.Value(), ir.LogicalNot(lhsLarger.Value())), ir.LogicalAnd(ir.LogicalNot(up.Value()), lhsLarger.Value()))); + IrValue& scaled = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&scaleLhs.Value(), lhs, rhs}); + IrValue& other = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&scaleLhs.Value(), rhs, lhs}); + IrValue& rescaled = ir.Emit(IrOpcode::FPFma32, IrType::F32, {&ir.Emit(IrOpcode::FPMul32, IrType::F32, {&scaled, &power}), &other, + &ir.Emit(IrOpcode::FPMul32, IrType::F32, {addend, &power})}); + IrValue& afterwards = ir.Emit(IrOpcode::FPMul32, IrType::F32, {&plain, &power}); + IrValue& scaledResult = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&rescale.Value(), &rescaled, &afterwards}); + writeOperand(inst.destination, &ir.Emit(IrOpcode::SelectF32, IrType::F32, {&scale.Value(), &scaledResult, &plain})); + return true; +} + +bool TranslationContext::vDivFixupF32(const RdnaInstruction& inst) { + const IrU32 quotient(ir.BitCastU32(*readOperand(sourceAt(inst, 0u), IrType::F32))); + const IrU32 denominator(ir.BitCastU32(*readOperand(sourceAt(inst, 1u), IrType::F32))); + const IrU32 numerator(ir.BitCastU32(*readOperand(sourceAt(inst, 2u), IrType::F32))); + const auto magnitude = [&](IrU32 word) { return IrU32(ir.BitwiseAnd(word.Value(), ir.Constant(0x7fffffffu))); }; + const auto isNan = [&](IrU32 word) { return IrU1(ir.UGreaterThan(magnitude(word).Value(), ir.Constant(0x7f800000u))); }; + const auto isInf = [&](IrU32 word) { return IrU1(ir.IEqual(magnitude(word).Value(), ir.Constant(0x7f800000u))); }; + const auto isZero = [&](IrU32 word) { return IrU1(ir.IEqual(magnitude(word).Value(), ir.Constant(0u))); }; + const auto exponent = [&](IrU32 word) { + return IrU32(ir.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {&word.Value(), &ir.Constant(23u), &ir.Constant(8u)})); + }; + const IrU32 sign(ir.BitwiseAnd(ir.BitwiseXor(denominator.Value(), numerator.Value()), ir.Constant(0x80000000u))); + const IrU32 infinity(ir.BitwiseOr(sign.Value(), ir.Constant(0x7f800000u))); + const IrU32 difference(ir.ISub(exponent(numerator).Value(), exponent(denominator).Value())); + const IrU1 underflow(ir.Emit(IrOpcode::SLessThan32, IrType::U1, {&difference.Value(), &ir.Constant(static_cast(-150))})); + IrU32 result(ir.BitwiseOr(sign.Value(), magnitude(quotient).Value())); + result = IrU32(ir.Select(isNan(quotient).Value(), infinity.Value(), result.Value())); + result = IrU32(ir.Select(underflow.Value(), sign.Value(), result.Value())); + result = IrU32(ir.Select(ir.LogicalOr(isInf(denominator).Value(), isZero(numerator).Value()), sign.Value(), result.Value())); + result = IrU32(ir.Select(ir.LogicalOr(isZero(denominator).Value(), isInf(numerator).Value()), infinity.Value(), result.Value())); + const IrU1 invalid(ir.LogicalOr(ir.LogicalAnd(isZero(denominator).Value(), isZero(numerator).Value()), ir.LogicalAnd(isInf(denominator).Value(), isInf(numerator).Value()))); + result = IrU32(ir.Select(invalid.Value(), ir.Constant(0xffc00000u), result.Value())); + result = IrU32(ir.Select(isNan(denominator).Value(), ir.BitwiseOr(denominator.Value(), ir.Constant(0x400000u)), result.Value())); + result = IrU32(ir.Select(isNan(numerator).Value(), ir.BitwiseOr(numerator.Value(), ir.Constant(0x400000u)), result.Value())); + writeOperand(inst.destination, &ir.BitCastF32(result.Value())); + return true; +} + bool TranslationContext::floatUnary(const RdnaInstruction& inst, IrOpcode opcode) { IrValue* argument = readOperand(sourceAt(inst, 0u), IrOpcodeArgumentType(opcode, 0u)); IrValue& result = ir.Emit(opcode, IrOpcodeType(opcode), {argument}); diff --git a/core/shader/recompiler/Translation/src/ScalarInstructions.cpp b/core/shader/recompiler/Translation/src/ScalarInstructions.cpp index 0c88d096b..d4eba2f76 100644 --- a/core/shader/recompiler/Translation/src/ScalarInstructions.cpp +++ b/core/shader/recompiler/Translation/src/ScalarInstructions.cpp @@ -377,6 +377,7 @@ bool TranslationContext::emitScalar(const RdnaInstruction& inst) { case RdnaOpcode::SIcacheInv: case RdnaOpcode::SIncperflevel: case RdnaOpcode::SDecperflevel: + case RdnaOpcode::SDenormMode: emitControlNop(); return true; case RdnaOpcode::SWaitcntDepctr: diff --git a/core/shader/recompiler/Translation/src/VectorInstructions.cpp b/core/shader/recompiler/Translation/src/VectorInstructions.cpp index 2782618ee..3a5ccd5eb 100644 --- a/core/shader/recompiler/Translation/src/VectorInstructions.cpp +++ b/core/shader/recompiler/Translation/src/VectorInstructions.cpp @@ -924,6 +924,12 @@ bool TranslationContext::emitVector(const RdnaInstruction& inst) { return floatBinary(inst, IrOpcode::FPMin32, false); case RdnaOpcode::VMaxF32: return floatBinary(inst, IrOpcode::FPMax32, false); + case RdnaOpcode::VDivScaleF32: + return vDivScaleF32(inst); + case RdnaOpcode::VDivFmasF32: + return vDivFmasF32(inst); + case RdnaOpcode::VDivFixupF32: + return vDivFixupF32(inst); case RdnaOpcode::VLdexpF32: return floatBinary(inst, IrOpcode::FPLdexp, false); case RdnaOpcode::VMacF32: diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index a66170e1c..1458600bd 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -19,6 +19,7 @@ Throughout the project, every function at every stage either **does exactly what - The shader recompiler [skips baryctric coordinates](../../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212). - [libSceAvPlayer](../../core/libs/prx/libSceAvPlayer/Export.cpp): `sceAvPlayerSetLogCallback` accepts a callback that is never called, as the player produces no log messages, and `sceAvPlayerSetAvailableBandwidth` has no effect, as it governs HLS sources, which `sceAvPlayerAddSource` does not implement. - [sceAvPlayerSetTrickSpeed](../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) with a negative speed runs the clock backwards but delivers no frames; when a forward speed is set again, playback resumes from the rewound time. +- The shader recompiler [ignores `s_denorm_mode`](../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp): denormals follow the Vulkan driver's float controls, not the mode the shader sets (the f32 division macro switches it around its Newton-Raphson steps). ### Unknown function info From 1a5507698b7c083ecdaab93d6b19084a6db0f1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Franch?= Date: Sun, 4 Oct 2026 08:33:11 +0200 Subject: [PATCH 2/2] fix(shader): choose v_div_scale_f32/v_div_fmas_f32 NaNs on the bits and refuse s_denorm_mode v_div_scale_f32 returns a NaN source unchanged instead of scaling it, and v_div_fmas_f32 quiets the first NaN input or returns the default NaN 0xffc00000, so results no longer depend on the driver's NaNs. The test compares NaN results bit for bit. s_denorm_mode throws: the recompiler does not pin a denormal mode, so no value matches what it models. Co-Authored-By: Claude --- .../tests/execution/Division.cpp | 20 ++++++------------- .../Translation/src/FloatInstructions.cpp | 14 +++++++++++-- .../Translation/src/ScalarInstructions.cpp | 4 +++- docs/dev/TechnicalDebt.md | 1 - 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp index ea91c11fc..001c96d22 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/Division.cpp @@ -30,8 +30,8 @@ alignas(256) constexpr std::array HelperCode{ alignas(256) constexpr std::array DivisionCode{ 0x34020082, 0xe0302000, 0x80000401, 0xe0302004, 0x80000501, 0xbf8c3f70, 0xd56d1408, 0x04120b05, - 0xd56d6a09, 0x04120b04, 0x7e145508, 0xbfa5000f, 0xd54b000b, 0x23ca1508, 0x5614150b, 0x10161509, - 0xd54b000c, 0x24261708, 0x5616150c, 0xd54b0008, 0x24261708, 0xbfa5000c, 0xd56f0008, 0x042e1508, + 0xd56d6a09, 0x04120b04, 0x7e145508, 0xbf800000, 0xd54b000b, 0x23ca1508, 0x5614150b, 0x10161509, + 0xd54b000c, 0x24261708, 0x5616150c, 0xd54b0008, 0x24261708, 0xbf800000, 0xd56f0008, 0x042e1508, 0xd55f000a, 0x04120b08, 0xe0702000, 0x80010a01, 0xbf810000, }; @@ -604,14 +604,6 @@ private: ShaderRecompiler::RecompileResult result; }; -bool IsNan(std::uint32_t bits) { - return (bits & 0x7fffffffu) > 0x7f800000u; -} - -bool Same(std::uint32_t actual, std::uint32_t expected) { - return actual == expected || (IsNan(actual) && IsNan(expected)); -} - std::string Hex(std::uint32_t value) { char text[16]; std::snprintf(text, sizeof(text), "0x%08x", value); @@ -643,10 +635,10 @@ void CheckHelpers(AgcDriver::VulkanDevice& device, bool fused) { const auto* out = &Output[lane * Stride]; const std::string where = "division helpers: vector " + std::to_string(first + lane) + " (" + Hex(vector.s0) + ", " + Hex(vector.s1) + ", " + Hex(vector.s2) + ")"; if (vector.s0 == vector.s1 || vector.s0 == vector.s2) { - Require(Same(out[0], vector.scale) && out[1] == vector.scaleVcc, where + ": v_div_scale_f32 is " + Hex(out[0]) + " vcc " + std::to_string(out[1]) + ", expected " + Hex(vector.scale) + " vcc " + std::to_string(vector.scaleVcc)); + Require(out[0] == vector.scale && out[1] == vector.scaleVcc, where + ": v_div_scale_f32 is " + Hex(out[0]) + " vcc " + std::to_string(out[1]) + ", expected " + Hex(vector.scale) + " vcc " + std::to_string(vector.scaleVcc)); } - Require(!fused || Same(out[2], vector.fmas), where + " vcc " + std::to_string(vector.vcc) + ": v_div_fmas_f32 is " + Hex(out[2]) + ", expected " + Hex(vector.fmas)); - Require(Same(out[3], vector.fixup), where + ": v_div_fixup_f32 is " + Hex(out[3]) + ", expected " + Hex(vector.fixup)); + Require(!fused || out[2] == vector.fmas, where + " vcc " + std::to_string(vector.vcc) + ": v_div_fmas_f32 is " + Hex(out[2]) + ", expected " + Hex(vector.fmas)); + Require(out[3] == vector.fixup, where + ": v_div_fixup_f32 is " + Hex(out[3]) + ", expected " + Hex(vector.fixup)); } } } @@ -664,7 +656,7 @@ void CheckDivision(AgcDriver::VulkanDevice& device) { for (std::uint32_t lane = 0; lane < Threads && first + lane < count; ++lane) { const auto& vector = DivisionVectors[first + lane]; const auto actual = Output[lane * Stride]; - Require(Same(actual, vector.quotient), "division macro: " + Hex(vector.numerator) + " / " + Hex(vector.denominator) + " is " + Hex(actual) + ", expected " + Hex(vector.quotient)); + Require(actual == vector.quotient, "division macro: " + Hex(vector.numerator) + " / " + Hex(vector.denominator) + " is " + Hex(actual) + ", expected " + Hex(vector.quotient)); } } } diff --git a/core/shader/recompiler/Translation/src/FloatInstructions.cpp b/core/shader/recompiler/Translation/src/FloatInstructions.cpp index 94dabbf23..4307ca9c9 100644 --- a/core/shader/recompiler/Translation/src/FloatInstructions.cpp +++ b/core/shader/recompiler/Translation/src/FloatInstructions.cpp @@ -287,7 +287,10 @@ bool TranslationContext::vDivScaleF32(const RdnaInstruction& inst) { ir.LogicalOr(ir.LogicalNot(nearMin.Value()), isDenominator.Value()))); const IrF32 up(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &ir.ConstantF32(18446744073709551616.0f)})); const IrF32 down(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &ir.ConstantF32(5.42101086242752217e-20f)})); - const IrU32 scaled(ir.Select(scaleUp.Value(), ir.BitCastU32(up.Value()), ir.Select(scaleDown.Value(), ir.BitCastU32(down.Value()), bits.Value()))); + const IrU1 valueNan(ir.UGreaterThan(ir.BitwiseAnd(bits.Value(), ir.Constant(0x7fffffffu)), ir.Constant(0x7f800000u))); + const IrU1 scaleUpNumber(ir.LogicalAnd(scaleUp.Value(), ir.LogicalNot(valueNan.Value()))); + const IrU1 scaleDownNumber(ir.LogicalAnd(scaleDown.Value(), ir.LogicalNot(valueNan.Value()))); + const IrU32 scaled(ir.Select(scaleUpNumber.Value(), ir.BitCastU32(up.Value()), ir.Select(scaleDownNumber.Value(), ir.BitCastU32(down.Value()), bits.Value()))); const auto isZero = [&](IrU32 word) { return IrU1(ir.IEqual(ir.BitwiseAnd(word.Value(), ir.Constant(0x7fffffffu)), ir.Constant(0u))); }; const IrU1 zero(ir.LogicalOr(isZero(denominator).Value(), isZero(numerator).Value())); const IrU32 result(ir.Select(zero.Value(), ir.Constant(0xffc00000u), scaled.Value())); @@ -322,7 +325,14 @@ bool TranslationContext::vDivFmasF32(const RdnaInstruction& inst) { &ir.Emit(IrOpcode::FPMul32, IrType::F32, {addend, &power})}); IrValue& afterwards = ir.Emit(IrOpcode::FPMul32, IrType::F32, {&plain, &power}); IrValue& scaledResult = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&rescale.Value(), &rescaled, &afterwards}); - writeOperand(inst.destination, &ir.Emit(IrOpcode::SelectF32, IrType::F32, {&scale.Value(), &scaledResult, &plain})); + const auto isNan = [&](IrValue& value) { return IrU1(ir.UGreaterThan(ir.BitwiseAnd(ir.BitCastU32(value), ir.Constant(0x7fffffffu)), ir.Constant(0x7f800000u))); }; + const auto quiet = [&](IrValue& value) { return IrU32(ir.BitwiseOr(ir.BitCastU32(value), ir.Constant(0x400000u))); }; + IrValue& computed = ir.Emit(IrOpcode::SelectF32, IrType::F32, {&scale.Value(), &scaledResult, &plain}); + IrU32 result(ir.Select(isNan(computed).Value(), ir.Constant(0xffc00000u), ir.BitCastU32(computed))); + result = IrU32(ir.Select(isNan(*addend).Value(), quiet(*addend).Value(), result.Value())); + result = IrU32(ir.Select(isNan(*rhs).Value(), quiet(*rhs).Value(), result.Value())); + result = IrU32(ir.Select(isNan(*lhs).Value(), quiet(*lhs).Value(), result.Value())); + writeOperand(inst.destination, &ir.BitCastF32(result.Value())); return true; } diff --git a/core/shader/recompiler/Translation/src/ScalarInstructions.cpp b/core/shader/recompiler/Translation/src/ScalarInstructions.cpp index d4eba2f76..28a9f089d 100644 --- a/core/shader/recompiler/Translation/src/ScalarInstructions.cpp +++ b/core/shader/recompiler/Translation/src/ScalarInstructions.cpp @@ -1,6 +1,7 @@ #include "Translation/ScalarInstructions.hpp" #include "Translation/TranslationContext.hpp" #include +#include namespace ShaderRecompiler { @@ -377,9 +378,10 @@ bool TranslationContext::emitScalar(const RdnaInstruction& inst) { case RdnaOpcode::SIcacheInv: case RdnaOpcode::SIncperflevel: case RdnaOpcode::SDecperflevel: - case RdnaOpcode::SDenormMode: emitControlNop(); return true; + case RdnaOpcode::SDenormMode: + throw std::runtime_error("s_denorm_mode " + std::to_string(inst.source0.value) + " at pc " + std::to_string(inst.programCounter) + ": the recompiler does not model denormal modes"); case RdnaOpcode::SWaitcntDepctr: case RdnaOpcode::SWaitIdle: emitWaitcnt(); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 1458600bd..a66170e1c 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -19,7 +19,6 @@ Throughout the project, every function at every stage either **does exactly what - The shader recompiler [skips baryctric coordinates](../../core/shader/recompiler/Recompiler.cpp) (is not even passed to SpirvTargetOptions at row 212). - [libSceAvPlayer](../../core/libs/prx/libSceAvPlayer/Export.cpp): `sceAvPlayerSetLogCallback` accepts a callback that is never called, as the player produces no log messages, and `sceAvPlayerSetAvailableBandwidth` has no effect, as it governs HLS sources, which `sceAvPlayerAddSource` does not implement. - [sceAvPlayerSetTrickSpeed](../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) with a negative speed runs the clock backwards but delivers no frames; when a forward speed is set again, playback resumes from the rewound time. -- The shader recompiler [ignores `s_denorm_mode`](../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp): denormals follow the Vulkan driver's float controls, not the mode the shader sets (the f32 division macro switches it around its Newton-Raphson steps). ### Unknown function info