mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
The VU's largest value is 0x7FFFFFFF, one binade above FLT_MAX, the same range the EE FPU has. So an exponent-255 word is an ordinary number on the way in and on the way out, and "overflow" starts above it rather than above FLT_MAX. Both engines put the boundary a binade lower. vuDouble() rewrites an exponent-255 operand as 0x7F7FFFFF and VU_MAC_UPDATE() calls every exponent-255 result an overflow; the arm64 COP2 macro emitters clamp the result to +/-FLT_MAX and raise neither O nor U. microVU's per-op operand clamps approximate the same thing from a list of games rather than a rule. 68 rows off an SCPH-90000 through VU0 macro mode, scored per engine and per column, with what each engine cannot yet reproduce recorded per case so a fix trips the test as loudly as a regression. Nothing is fixed here. Two of the rows are structural rather than about range. An overflowed product does not become 0x7FFFFFFF before the accumulate: an addend of -0x7FFFFFFF cannot cancel it. An underflowed product does become zero before it. And the multiplier is the EE's, with the same one-ULP deficit decided by ft's mantissa alone. The harness grows two things the rows need: a VADDA encoder, and an opt-out from Run()'s VU0 JIT-vs-interp auto-diff for tests that score each engine against a hardware capture instead of against the other engine.
112 lines
10 KiB
C++
112 lines
10 KiB
C++
// GENERATED by captures/vusat/gen_autocases.py from a capture taken on an
|
|
// SCPH-90000 (hw-run1.out, byte-identical to hw-run2.out). Do not edit by hand.
|
|
//
|
|
// VU0 macro-mode FMAC at the top and the bottom of its range. Each row seeds
|
|
// fs/ft/acc into all four lanes of vf1/vf2/vf3, runs
|
|
//
|
|
// vadda.xyzw $ACC, $vf3, $vf7 ; vf7 is zero, so ACC = acc
|
|
// <op>.xyzw $vf4, $vf1, $vf2
|
|
//
|
|
// and records vf4 (identical in all four lanes), the MAC flag and the status
|
|
// flag. The status flag is cleared before the pair, so it carries both ops.
|
|
//
|
|
// bad_interp / bad_jit are not from the console: they are what this tree
|
|
// currently fails to reproduce, per column, regenerated from the test binary's
|
|
// own dump. Clearing a bit is how a fix lands.
|
|
#pragma once
|
|
#include <common/Pcsx2Types.h>
|
|
|
|
namespace console_vusat {
|
|
|
|
enum VuSatOp { VS_ADD, VS_SUB, VS_MUL, VS_MADD, VS_MSUB, VS_MAX, VS_MINI };
|
|
|
|
// Columns a case can be wrong in, independently.
|
|
enum { VSB_VALUE = 1, VSB_MAC = 2, VSB_STAT = 4 };
|
|
|
|
struct VuSatCase {
|
|
u8 op;
|
|
u32 fs, ft, acc; // seeded into all four lanes of vf1 / vf2 / vf3
|
|
u32 out; // vf4 on the console, all four lanes
|
|
u16 mac, stat; // VI17 and VI16 on the console
|
|
u8 bad_interp, bad_jit;
|
|
const char* what;
|
|
};
|
|
|
|
inline constexpr VuSatCase kVuSatCases[] = {
|
|
{VS_ADD, 0x3F800000u, 0x3F800000u, 0x00000000u, 0x40000000u, 0x0000u, 0x0040u, 0, 0, "CONTROL 1.0+1.0, plumbing"},
|
|
{VS_ADD, 0x3F800000u, 0xBF800000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "CONTROL liveness 1.0-1.0: value and MAC flag must both differ from row 0"},
|
|
{VS_MUL, 0x3F000000u, 0x7F800000u, 0x00000000u, 0x7F000000u, 0x0000u, 0x0040u, 1, 1, "A 0.5*2^128 full 7F000000 | operand clamped 7EFFFFFF"},
|
|
{VS_MUL, 0x3F000000u, 0x7FFFFFFFu, 0x00000000u, 0x7F7FFFFEu, 0x0000u, 0x0040u, 1, 1, "A 0.5*max full 7F7FFFFF | operand clamped 7EFFFFFF"},
|
|
{VS_ADD, 0x7FFFFFFFu, 0xFF800000u, 0x00000000u, 0x7F7FFFFEu, 0x0000u, 0x0040u, 7, 1, "A max + -2^128 full 7F7FFFFE | operand clamped 00000000"},
|
|
{VS_SUB, 0x7FFFFFFFu, 0x7F800000u, 0x00000000u, 0x7F7FFFFEu, 0x0000u, 0x0040u, 7, 1, "A max - 2^128 full 7F7FFFFE | operand clamped 00000000"},
|
|
{VS_ADD, 0x7F800000u, 0xFF800000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 7, "A 2^128 + -2^128 zero under either reading"},
|
|
{VS_MUL, 0x7F7FFFFFu, 0x40000000u, 0x00000000u, 0x7FFFFFFFu, 0x0000u, 0x0040u, 1, 1, "B FLT_MAX*2 exactly 7FFFFFFF if the range runs that far"},
|
|
{VS_MUL, 0x7F7FFFFFu, 0xC0000000u, 0x00000000u, 0xFFFFFFFFu, 0x00F0u, 0x00C2u, 1, 1, "B FLT_MAX*-2 sign mirror of the row above"},
|
|
{VS_ADD, 0x7FFFFFFFu, 0x74000000u, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "B max + 1ulp first value past the top: saturate or wrap"},
|
|
{VS_MUL, 0x7F800000u, 0x40000000u, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "B 2^128*2 one binade past the top"},
|
|
{VS_MUL, 0x7F800000u, 0xC0000000u, 0x00000000u, 0xFFFFFFFFu, 0xF0F0u, 0x02CAu, 7, 7, "B 2^128*-2 sign mirror"},
|
|
{VS_ADD, 0x7FFFFFFFu, 0x7FFFFFFFu, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "B max+max"},
|
|
{VS_MUL, 0x7F800000u, 0x7F800000u, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "B 2^128*2^128 far past the top"},
|
|
{VS_SUB, 0xFFFFFFFFu, 0x7FFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0xF0F0u, 0x02CAu, 7, 7, "B -max-max"},
|
|
{VS_MUL, 0x7F000000u, 0x3F800001u, 0x00000000u, 0x7F000001u, 0x0000u, 0x0040u, 0, 0, "B 2^127*(1+2^-23) exp FE, in range under every reading"},
|
|
{VS_MUL, 0x7F800000u, 0x3F800001u, 0x00000000u, 0x7F800001u, 0x0000u, 0x0040u, 1, 1, "B 2^128*(1+2^-23) exp FF and representable: does exp FF alone raise O?"},
|
|
{VS_ADD, 0x7F800000u, 0x74000000u, 0x00000000u, 0x7F800001u, 0x0000u, 0x0040u, 1, 1, "B 2^128 + 1ulp exp FF and representable, via the adder"},
|
|
{VS_MUL, 0x00800000u, 0x3F000000u, 0x00000000u, 0x00000000u, 0x0F0Fu, 0x0145u, 6, 6, "C 2^-126*0.5 first value below the bottom"},
|
|
{VS_MUL, 0x80800000u, 0x3F000000u, 0x00000000u, 0x80000000u, 0x0FFFu, 0x01C7u, 6, 6, "C -2^-126*0.5 sign of a flushed result"},
|
|
{VS_MUL, 0x00800000u, 0x00800000u, 0x00000000u, 0x00000000u, 0x0F0Fu, 0x0145u, 6, 6, "C 2^-126*2^-126 far below the bottom"},
|
|
{VS_ADD, 0x00800000u, 0x80800001u, 0x00000000u, 0x80000000u, 0x0FFFu, 0x01C7u, 6, 6, "C 2^-126 + -(2^-126+1ulp) tiny negative from the adder"},
|
|
{VS_MUL, 0x00400000u, 0x40000000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "C denormal operand *2 flushed 00000000 | kept 00800000"},
|
|
{VS_ADD, 0x00400000u, 0x00400000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "C denormal operand + itself flushed 00000000 | kept 00800000"},
|
|
{VS_MUL, 0x00000000u, 0x7F800000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 7, "D 0*2^128"},
|
|
{VS_MUL, 0x80000000u, 0x7F800000u, 0x00000000u, 0x80000000u, 0x00FFu, 0x00C3u, 0, 7, "D -0*2^128"},
|
|
{VS_MUL, 0x00000000u, 0x7FFFFFFFu, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 7, "D 0*max"},
|
|
{VS_MADD, 0x00000000u, 0x7F800000u, 0x3F800000u, 0x3F800000u, 0x0000u, 0x0040u, 4, 5, "D 1.0 + 0*2^128"},
|
|
{VS_MADD, 0x7F800000u, 0x40800000u, 0xFFFFFFFFu, 0x7FFFFFFFu, 0xF000u, 0x0288u, 7, 7, "E -max + 2^130 saturated product 00000000 | wide product 7FFFFFFF"},
|
|
{VS_MSUB, 0x7F800000u, 0x40800000u, 0x7FFFFFFFu, 0xFFFFFFFFu, 0xF0F0u, 0x028Au, 7, 7, "E max - 2^130 saturated product 00000000 | wide product FFFFFFFF"},
|
|
{VS_MADD, 0x7F800000u, 0xC0800000u, 0x7FFFFFFFu, 0xFFFFFFFFu, 0xF0F0u, 0x028Au, 7, 7, "E max + -2^130 saturated product 00000000 | wide product FFFFFFFF"},
|
|
{VS_MADD, 0x7F800000u, 0x40800000u, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "E 0 + 2^130 both readings overflow"},
|
|
{VS_MADD, 0x00800000u, 0x3F000000u, 0x00800000u, 0x00800000u, 0x0000u, 0x0140u, 4, 4, "E 2^-126 + 2^-127 flushed product 00800000 | wide product 00C00000"},
|
|
{VS_MSUB, 0x00800000u, 0x3F000000u, 0x00800000u, 0x00800000u, 0x0000u, 0x0140u, 4, 4, "E 2^-126 - 2^-127 flushed product 00800000 | wide product 00400000->0"},
|
|
{VS_MAX, 0x7F800000u, 0x7FFFFFFFu, 0x00000000u, 0x7FFFFFFFu, 0x000Fu, 0x0041u, 0, 0, "F max(2^128, max)"},
|
|
{VS_MAX, 0xFF800000u, 0x7F800000u, 0x00000000u, 0x7F800000u, 0x000Fu, 0x0041u, 0, 0, "F max(-2^128, 2^128)"},
|
|
{VS_MINI, 0xFF800000u, 0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0x000Fu, 0x0041u, 0, 0, "F mini(-2^128, -max)"},
|
|
{VS_MAX, 0x00000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "F max(+0, -0) integer compare 00000000"},
|
|
{VS_MINI, 0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x000Fu, 0x0041u, 0, 0, "F mini(+0, -0) integer compare 80000000"},
|
|
{VS_MAX, 0x00400000u, 0x00000000u, 0x00000000u, 0x00400000u, 0x000Fu, 0x0041u, 0, 0, "F max(denormal, 0) operand flushed 00000000 | kept 00400000"},
|
|
{VS_MINI, 0x00400000u, 0x00800000u, 0x00000000u, 0x00400000u, 0x000Fu, 0x0041u, 0, 0, "F mini(denormal, 2^-126)"},
|
|
{VS_ADD, 0x7FFFFFFFu, 0x3F800000u, 0x00000000u, 0x7FFFFFFFu, 0x0000u, 0x0040u, 1, 1, "G max + 1.0 far below one ULP: chop"},
|
|
{VS_SUB, 0x7F800000u, 0x74000000u, 0x00000000u, 0x7F7FFFFEu, 0x0000u, 0x0040u, 1, 1, "G 2^128 - 1ulp"},
|
|
{VS_SUB, 0x3F800000u, 0x3F800000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "G 1.0-1.0 sign of an exact zero"},
|
|
{VS_ADD, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "G -0 + +0"},
|
|
{VS_ADD, 0x80000000u, 0x80000000u, 0x00000000u, 0x80000000u, 0x00FFu, 0x00C3u, 0, 0, "G -0 + -0"},
|
|
{VS_MUL, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00FFu, 0x00C3u, 0, 0, "G -0 * +0"},
|
|
{VS_MAX, 0x00000000u, 0x80000000u, 0x3F800000u, 0x00000000u, 0x0000u, 0x0000u, 0, 0, "H max -> 0, seed 1.0 -> no flag: 000F if MAX writes MAC, 0000 if not"},
|
|
{VS_MINI, 0x00000000u, 0x80000000u, 0x3F800000u, 0x80000000u, 0x0000u, 0x0000u, 0, 0, "H mini -> -0, seed 1.0: 00FF if MINI writes MAC, 0000 if not"},
|
|
{VS_MAX, 0x3F800000u, 0xBF800000u, 0x00000000u, 0x3F800000u, 0x000Fu, 0x0041u, 0, 0, "H max -> 1.0, seed 0 -> Z: 0000 if MAX writes MAC, 000F if not"},
|
|
{VS_MINI, 0x3F800000u, 0xBF800000u, 0x00000000u, 0xBF800000u, 0x000Fu, 0x0041u, 0, 0, "H mini -> -1.0, seed 0: 00F0 if MINI writes MAC, 000F if not"},
|
|
{VS_MADD, 0x7F800000u, 0x40000000u, 0xFFFFFFFFu, 0x7FFFFFFFu, 0xF000u, 0x0288u, 7, 7, "I -max + 2^129 saturated product 00000000 | exact wide product 74000000"},
|
|
{VS_MADD, 0x7F7FFFFFu, 0x40800000u, 0xFFFFFFFFu, 0x7FFFFFFFu, 0xF000u, 0x0288u, 7, 7, "I -max + 2*max saturated product 00000000 | exact wide product 7FFFFFFF no O"},
|
|
{VS_MADD, 0x7F800000u, 0x3F800000u, 0x7F800000u, 0x7FFFFFFFu, 0xF000u, 0x0208u, 7, 7, "I 2^128 + 2^128 in-range product, sum past the top"},
|
|
{VS_MUL, 0x7FFFFFFFu, 0x3F000000u, 0x00000000u, 0x7F7FFFFFu, 0x0000u, 0x0040u, 1, 0, "J max*0.5 case 3 swapped"},
|
|
{VS_MUL, 0x7FFFFFFFu, 0x3F800000u, 0x00000000u, 0x7FFFFFFFu, 0x0000u, 0x0040u, 1, 1, "J max*1.0"},
|
|
{VS_MUL, 0x3F800000u, 0x7FFFFFFFu, 0x00000000u, 0x7FFFFFFEu, 0x0000u, 0x0040u, 1, 1, "J 1.0*max swapped"},
|
|
{VS_MUL, 0x3EAAAAABu, 0x40400000u, 0x00000000u, 0x3F800000u, 0x0000u, 0x0040u, 0, 0, "J (1/3)*3 exact 1.00000002980232239"},
|
|
{VS_MUL, 0x40400000u, 0x3EAAAAABu, 0x00000000u, 0x3F800000u, 0x0000u, 0x0040u, 0, 0, "J 3*(1/3) swapped"},
|
|
{VS_MUL, 0x3F800001u, 0x3F800001u, 0x00000000u, 0x3F800002u, 0x0000u, 0x0040u, 0, 0, "J (1+2^-23)^2 exact 1+2^-22+2^-46"},
|
|
{VS_MUL, 0x3FB504F3u, 0x3FB504F3u, 0x00000000u, 0x3FFFFFFFu, 0x0000u, 0x0040u, 0, 0, "J sqrt2^2 exact 1.9999999830"},
|
|
{VS_ADD, 0x3F800000u, 0x33C00000u, 0x00000000u, 0x3F800000u, 0x0000u, 0x0040u, 0, 0, "K 1.0 + 1.5*2^-24 chop 3F800000 | nearest 3F800001"},
|
|
{VS_ADD, 0xBF800000u, 0xB3C00000u, 0x00000000u, 0xBF800000u, 0x00F0u, 0x00C2u, 0, 0, "K -1.0 - 1.5*2^-24 chop BF800000 | nearest BF800001"},
|
|
{VS_ADD, 0x7FFFFFFFu, 0x73FFFFFFu, 0x00000000u, 0x7FFFFFFFu, 0x0000u, 0x0040u, 1, 1, "K max + just under 1ulp chop 7FFFFFFF, no O"},
|
|
{VS_ADD, 0xFF800000u, 0xFF800000u, 0x00000000u, 0xFFFFFFFFu, 0xF0F0u, 0x02CAu, 7, 7, "L -2^128 + -2^128"},
|
|
{VS_SUB, 0x7FFFFFFFu, 0xFF800000u, 0x00000000u, 0x7FFFFFFFu, 0xF000u, 0x0248u, 7, 7, "L max - -2^128"},
|
|
{VS_MSUB, 0x00800000u, 0x3F800000u, 0x00800000u, 0x00000000u, 0x000Fu, 0x0041u, 0, 0, "L 2^-126 - 2^-126 exact zero, Z without U"},
|
|
{VS_MUL, 0x00800000u, 0xBF000000u, 0x00000000u, 0x80000000u, 0x0FFFu, 0x01C7u, 6, 6, "L 2^-126 * -0.5 sign of a flushed product"},
|
|
};
|
|
|
|
inline constexpr int kVuSatCaseCount = 68;
|
|
// Column-misses, not case-misses: a case wrong in two columns counts twice.
|
|
inline constexpr int kVuSatBadInterp = 76;
|
|
inline constexpr int kVuSatBadJit = 84;
|
|
|
|
} // namespace console_vusat
|