Pantheon/tests/test_gdnc_compact.c

180 lines
5.7 KiB
C

/* SPDX-License-Identifier: AGPL-3.0-or-later */
/**
* @file test_gdnc_compact.c
* @brief L11b: the gdnc reader's on-card decay tile + gate runs (foundry/tt/kernels/
* ftgc_compact.h) against (1) the hardware fp32 multiply under FTZ+DAZ and (2) the
* host builders `ft_tt_op_gdnc_build_decay(_at)` / `_build_gate(_at)`, BIT FOR BIT.
* x86 is the authority: run it on an x86 host.
*
* make USE_TT=1 tests/test_gdnc_compact && ./tests/test_gdnc_compact
*
* Red: `--red` flips one rounding (ties go up) and must FAIL.
*/
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(__x86_64__)
#include <xmmintrin.h>
#endif
#include "tt/ft_tt_op_gdnc.h"
#include "tt/kernels/ftgc_compact.h"
static int g_red;
static uint32_t f2u(float f)
{
uint32_t u;
memcpy(&u, &f, 4);
return u;
}
static float u2f(uint32_t u)
{
float f;
memcpy(&f, &u, 4);
return f;
}
static uint64_t g_rng = 0x9E3779B97F4A7C15ull;
static uint32_t rnd(void)
{
g_rng ^= g_rng << 13;
g_rng ^= g_rng >> 7;
g_rng ^= g_rng << 17;
return (uint32_t)(g_rng >> 16);
}
/** The card's multiply, or the red mutation of it (ties away from even). */
static uint32_t card_mul(uint32_t a, uint32_t b)
{
uint32_t r = ftgc_mul(a, b);
if (g_red && ((rnd() & 1023u) == 0u))
r ^= 1u;
return r;
}
static void set_ftz_daz(void)
{
#if defined(__x86_64__)
_mm_setcsr(_mm_getcsr() | 0x8040u); /* FTZ | DAZ */
#elif defined(__aarch64__)
uint64_t fpcr;
__asm__ volatile("mrs %0, fpcr" : "=r"(fpcr));
fpcr |= 1ull << 24; /* FZ: AArch64 flushes on tininess BEFORE rounding — not the host's */
__asm__ volatile("msr fpcr, %0" : : "r"(fpcr));
fprintf(stderr, "test_gdnc_compact: AArch64 FZ — informational; x86 is the authority\n");
#else
fprintf(stderr, "test_gdnc_compact: not x86 — the hardware leg is not the host's\n");
#endif
}
static int mul_leg(void)
{
volatile float va, vb;
uint64_t n, bad = 0;
for (n = 0; n < 200000000ull; n++) {
uint32_t a = rnd() & 0x7FFFFFFFu, b = rnd() & 0x7FFFFFFFu;
if (n & 1u) { /* near the underflow edge: exponents summing to ~127-126 */
a = (a & 0x807FFFFFu) | ((60u + (rnd() % 8u)) << 23);
b = (b & 0x807FFFFFu) | ((rnd() % 8u) << 23);
}
if ((a & 0x7F800000u) == 0x7F800000u || (b & 0x7F800000u) == 0x7F800000u)
continue;
va = u2f(a);
vb = u2f(b);
if (f2u(va * vb) != card_mul(a, b)) {
if (bad < 5)
fprintf(stderr, " mul %08x * %08x: hw %08x card %08x\n", a, b,
f2u(va * vb), ftgc_mul(a, b));
bad++;
}
}
printf("mul leg: %llu of %llu differ\n", (unsigned long long)bad, (unsigned long long)n);
return bad != 0;
}
static float draw_decay(void)
{
/* exp(g) with g from -20 (1e-9: underflows inside a chunk) to 0 */
const double g = -20.0 * pow((double)(rnd() % 1000000u) / 1e6, 3.0);
return (float)exp(g);
}
static int builder_leg(void)
{
uint64_t cases = 0, bad = 0;
unsigned trial;
float scale;
{
float d1 = 1.0f, b1 = 0.0f, out[FT_TT_OP_GDNC_GATE_STRIDE];
if (ft_tt_op_gdnc_build_gate(&d1, &b1, 1u, 128u, out, FT_TT_OP_GDNC_GATE_STRIDE) !=
FT_TT_OK)
return 1;
scale = out[FT_TT_OP_GDNC_GATE_PS * 32u];
}
for (trial = 0; trial < 20000u; trial++) {
const uint32_t first_row = (trial & 1u) ? 16u : 0u;
const uint32_t max_rows = 32u - first_row;
const uint32_t chunk_rows = 1u + rnd() % max_rows;
float decay[32], beta[32], host_gate[FT_TT_OP_GDNC_GATE_STRIDE];
unsigned char host_tile[FT_TT_OP_GDNC_FP32_TILE_BYTES];
uint32_t card_decay[32], card_beta[32], card_tile[1024], card_gate[160];
uint32_t i;
for (i = 0; i < 32u; i++) {
decay[i] = (i < chunk_rows) ? draw_decay() : 0.0f;
beta[i] = (i < chunk_rows) ? (float)(rnd() % 1000000u) / 1e6f : 0.0f;
card_decay[i] = f2u(decay[i]);
card_beta[i] = f2u(beta[i]);
}
if (ft_tt_op_gdnc_build_decay_at(decay, first_row, chunk_rows, host_tile,
sizeof(host_tile)) != FT_TT_OK ||
ft_tt_op_gdnc_build_gate_at(decay, beta, first_row, chunk_rows, 128u, host_gate,
FT_TT_OP_GDNC_GATE_STRIDE) != FT_TT_OK)
return 1;
ftgc_compact_decay_tile(card_decay, first_row, chunk_rows, card_tile);
ftgc_compact_gate(card_decay, card_beta, first_row, chunk_rows, f2u(scale), card_gate);
if (g_red)
card_tile[ftgc_tile_index(chunk_rows - 1u + first_row, first_row)] ^= 1u;
cases++;
if (memcmp(host_tile, card_tile, sizeof(host_tile)) != 0 ||
memcmp(host_gate, card_gate, sizeof(card_gate)) != 0) {
if (bad < 5)
fprintf(stderr, " builder case %u: first_row %u rows %u — tile %s, gate %s\n",
trial, first_row, chunk_rows,
memcmp(host_tile, card_tile, sizeof(host_tile)) ? "DIFFERS" : "same",
memcmp(host_gate, card_gate, sizeof(card_gate)) ? "DIFFERS" : "same");
bad++;
}
}
printf("builder leg: %llu of %llu cases differ (scale %08x)\n", (unsigned long long)bad,
(unsigned long long)cases, f2u(scale));
return bad != 0;
}
int main(int argc, char **argv)
{
int fail;
g_red = argc > 1 && strcmp(argv[1], "--red") == 0;
set_ftz_daz();
fail = mul_leg();
fail |= builder_leg();
printf("%s%s\n", fail ? "FAIL" : "PASS", g_red ? " (red arm: FAIL is the pass)" : "");
return fail;
}