mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Every flag-setting instruction packed N, Z, C and V into c->nzcv and every reader unpacked them. recomp/flags.py now runs a reaching-definitions pass per function: a compare stores its operands in a local Flags F, and a reader whose definitions are all of one kind computes its condition from them, so cmp and b.cond become one host compare. CCMP, FCMP, MSR NZCV and ADCS write the packed form, which readers reached by several kinds also use. Calls and returns publish nothing (AAPCS64 neither passes nor preserves NZCV); tail branches, falls into the next function and br through rt_dispatch publish c->nzcv, since discovery can start a function inside another one. Functions with an unresolved computed br keep flags in c->nzcv. 1.4.3 main: 492k direct and 89k packed flag reads, .text 342 -> 299 MiB. Fight bench (bench-fight2-2026-10-27b, frames 5400-7500, uncapped) 13.15 and 13.63 ms vs 13.9 before. tests/flags_check.py checks the new helpers against the packed ones; tests/diff/func.py no longer compares NZCV, which generated code leaves untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
112 lines
5.9 KiB
C
112 lines
5.9 KiB
C
// The function-local flag helpers of runtime/cpu.h (Flags, fc_*, fnz_*, nz_*, adc*f) against the packed helpers
|
|
// (subs64() and friends, COND_xx), which tests/diff/lift.py checks against Unicorn. Built and run by
|
|
// tests/flags_check.py; prints the first mismatches and exits 1 on any.
|
|
#include <stdio.h>
|
|
|
|
#include "cpu.h"
|
|
|
|
static const uint64_t EDGE[] = {0, 1, 2, 0x7f, 0x80, 0xff, 0x7fff, 0x8000, 0xffff, 0x7fffffff, 0x80000000,
|
|
0x80000001, 0xfffffffe, 0xffffffff, 0x100000000ull, 0x7fffffffffffffffull,
|
|
0x8000000000000000ull, 0x8000000000000001ull, 0xfffffffffffffffeull,
|
|
0xffffffffffffffffull, 0x123456789abcdef0ull, 0xfedcba9876543210ull};
|
|
#define NEDGE (sizeof EDGE / sizeof EDGE[0])
|
|
|
|
static uint64_t rng = 0x9E3779B97F4A7C15ull;
|
|
static uint64_t next(void) {
|
|
rng ^= rng << 13, rng ^= rng >> 7, rng ^= rng << 17;
|
|
return rng;
|
|
}
|
|
|
|
static int fails;
|
|
static void bad(const char *what, int cc, uint64_t a, uint64_t b, uint64_t got, uint64_t want) {
|
|
if (fails++ < 20)
|
|
printf("%s cc %d a %llx b %llx: got %llx want %llx\n", what, cc, (unsigned long long)a, (unsigned long long)b,
|
|
(unsigned long long)got, (unsigned long long)want);
|
|
}
|
|
|
|
static int cond(const Cpu *c, int cc) {
|
|
switch (cc) {
|
|
case CC_EQ: return COND_EQ(c);
|
|
case CC_NE: return COND_NE(c);
|
|
case CC_CS: return COND_CS(c);
|
|
case CC_CC: return COND_CC(c);
|
|
case CC_MI: return COND_MI(c);
|
|
case CC_PL: return COND_PL(c);
|
|
case CC_VS: return COND_VS(c);
|
|
case CC_VC: return COND_VC(c);
|
|
case CC_HI: return COND_HI(c);
|
|
case CC_LS: return COND_LS(c);
|
|
case CC_GE: return COND_GE(c);
|
|
case CC_LT: return COND_LT(c);
|
|
case CC_GT: return COND_GT(c);
|
|
default: return COND_LE(c);
|
|
}
|
|
}
|
|
|
|
// The condition argument is not constant here, so this also covers the switch default paths.
|
|
#define CHECK(NAME, OLD, NEW, FC, FNZ) \
|
|
do { \
|
|
Cpu c = {0}; \
|
|
Flags f = {0}; \
|
|
uint64_t ro = OLD, rn = NEW; \
|
|
if (ro != rn) bad(NAME " result", -1, a, b, rn, ro); \
|
|
for (int cc = 0; cc < 14; cc++) \
|
|
if (!FC(&f, cc) != !cond(&c, cc)) bad(NAME, cc, a, b, FC(&f, cc), cond(&c, cc)); \
|
|
f.nz = 0; \
|
|
if (FNZ(&f) != c.nzcv) bad(NAME " packed", -1, a, b, FNZ(&f), c.nzcv); \
|
|
Flags g = {.nz = c.nzcv}; \
|
|
for (int cc = 0; cc < 14; cc++) \
|
|
if (!fc_nz(&g, cc) != !cond(&c, cc)) bad(NAME " nz", cc, a, b, fc_nz(&g, cc), cond(&c, cc)); \
|
|
} while (0)
|
|
|
|
static void one(uint64_t a, uint64_t b) {
|
|
uint32_t a32 = (uint32_t)a, b32 = (uint32_t)b;
|
|
CHECK("sub64", subs64(&c, a, b), fsub64(&f, a, b), fc_sub64, fnz_sub64);
|
|
CHECK("sub32", subs32(&c, a32, b32), fsub32(&f, a32, b32), fc_sub32, fnz_sub32);
|
|
CHECK("add64", adds64(&c, a, b), fadd64(&f, a, b), fc_add64, fnz_add64);
|
|
CHECK("add32", adds32(&c, a32, b32), fadd32(&f, a32, b32), fc_add32, fnz_add32);
|
|
CHECK("logic64", logic64(&c, a & b), flogic64(&f, a & b), fc_logic64, fnz_logic64);
|
|
CHECK("logic32", logic32(&c, a32 & b32), flogic32(&f, a32 & b32), fc_logic32, fnz_logic32);
|
|
{
|
|
Cpu c = {0};
|
|
if (nz_sub64(a, b) != (subs64(&c, a, b), c.nzcv)) bad("nz_sub64", -1, a, b, nz_sub64(a, b), c.nzcv);
|
|
if (nz_sub32(a32, b32) != (subs32(&c, a32, b32), c.nzcv)) bad("nz_sub32", -1, a, b, nz_sub32(a32, b32), c.nzcv);
|
|
if (nz_add64(a, b) != (adds64(&c, a, b), c.nzcv)) bad("nz_add64", -1, a, b, nz_add64(a, b), c.nzcv);
|
|
if (nz_add32(a32, b32) != (adds32(&c, a32, b32), c.nzcv)) bad("nz_add32", -1, a, b, nz_add32(a32, b32), c.nzcv);
|
|
}
|
|
for (int cin = 0; cin < 2; cin++) {
|
|
Cpu c = {.nzcv = (uint64_t)cin << 29};
|
|
Flags f = {0};
|
|
if (adc64f(cin, a, b) != adc64(&c, a, b)) bad("adc64", cin, a, b, adc64f(cin, a, b), adc64(&c, a, b));
|
|
if (sbc64f(cin, a, b) != sbc64(&c, a, b)) bad("sbc64", cin, a, b, sbc64f(cin, a, b), sbc64(&c, a, b));
|
|
if (adc32f(cin, a32, b32) != adc32(&c, a32, b32)) bad("adc32", cin, a, b, adc32f(cin, a32, b32), adc32(&c, a32, b32));
|
|
if (sbc32f(cin, a32, b32) != sbc32(&c, a32, b32)) bad("sbc32", cin, a, b, sbc32f(cin, a32, b32), sbc32(&c, a32, b32));
|
|
uint64_t r;
|
|
#define CARRY(NAME, NEW, OLD) \
|
|
c.nzcv = (uint64_t)cin << 29; \
|
|
r = NEW; \
|
|
if (r != OLD || f.nz != c.nzcv) bad(NAME, cin, a, b, f.nz, c.nzcv);
|
|
CARRY("adcs64", adcs64f(&f, cin, a, b), adcs64(&c, a, b))
|
|
CARRY("sbcs64", sbcs64f(&f, cin, a, b), sbcs64(&c, a, b))
|
|
CARRY("adcs32", adcs32f(&f, cin, a32, b32), adcs32(&c, a32, b32))
|
|
CARRY("sbcs32", sbcs32f(&f, cin, a32, b32), sbcs32(&c, a32, b32))
|
|
}
|
|
}
|
|
|
|
int main(void) {
|
|
long n = 0;
|
|
for (size_t i = 0; i < NEDGE; i++)
|
|
for (size_t j = 0; j < NEDGE; j++, n++) one(EDGE[i], EDGE[j]);
|
|
for (int k = 0; k < 2000000; k++, n++) {
|
|
uint64_t a = next(), b = next();
|
|
switch (k & 3) {
|
|
case 1: b = a; break; // equal operands
|
|
case 2: b = a + (next() & 3) - 1; break; // neighbours
|
|
case 3: a &= 0xff, b &= 0xff; break; // small values
|
|
}
|
|
one(a, b);
|
|
}
|
|
printf("%ld operand pairs, %d mismatches\n", n, fails);
|
|
return fails != 0;
|
|
}
|