65816-llvm-mos/tests/fpNan/genFpCmpConst.py
2026-10-06 17:01:51 -05:00

217 lines
8.1 KiB
Python

#!/usr/bin/env python3
# genFpCmpConst.py - emits the C source of the smoke check "float/double
# compares against constants", with kExp (the expected answers) computed
# here from Python's IEEE 754 compares.
#
# python3 tests/fpNan/genFpCmpConst.py f # float
# python3 tests/fpNan/genFpCmpConst.py d 1 2 # double, first half
# python3 tests/fpNan/genFpCmpConst.py d 2 2 # double, second half
#
# PART / NPARTS split the tests so each binary stays well below the IIgs
# I/O window at $C000 (the whole double table is ~54 KB of code).
#
# W65816's combineFPSetCCConst turns a compare the operand's bit pattern
# alone decides (x against a zero, an infinity or any non-NaN constant for
# == / !=, fabs(x) against a constant >= 0, x against itself, the ordered /
# unordered tests) into integer compares instead of a soft-float libcall.
# This check runs every C comparison operator, its negation (LLVM's
# unordered predicates), isunordered and islessgreater and their negations
# against a set of constants, on x and on fabs(x), plus the classification
# builtins, each as a branch and as a 0/1 value, on 34 values: signed
# zeros, subnormals, normals and their neighbours one ulp away, the largest
# finite values, infinities and quiet, signaling and all-ones NaNs of both
# signs. Compares the combine leaves alone run the libcall, so the table
# also pins the helpers' NaN contract.
#
# Mailbox: 0x025000 = mismatches, 0x025002 = first failing case
# (function index * NV + value index), 0x025004 = 0xC0DE when done.
import math
import struct
import sys
KIND = sys.argv[1] if len(sys.argv) > 1 else "f"
PART = int(sys.argv[2]) if len(sys.argv) > 2 else 1
NPARTS = int(sys.argv[3]) if len(sys.argv) > 3 else 1
if KIND not in ("f", "d") or not 1 <= PART <= NPARTS:
sys.exit("usage: genFpCmpConst.py f|d [PART NPARTS]")
if KIND == "f":
T = "float"
BITS_T = "u32"
SUF = "UL"
FABS = "__builtin_fabsf"
VALS = [
0x00000000, 0x80000000, 0x00000001, 0x80000001, 0x007FFFFF,
0x807FFFFF, 0x00800000, 0x80800000, 0x3F7FFFFF, 0x3F800000,
0x3F800001, 0xBF7FFFFF, 0xBF800000, 0xBF800001, 0x401FFFFF,
0x40200000, 0x40200001, 0xC01FFFFF, 0xC0200000, 0xC0200001,
0x7F7FFFFF, 0xFF7FFFFF, 0x7F800000, 0xFF800000, 0x7FC00000,
0xFFC00000, 0x7F800001, 0xFF800001, 0x7FFFFFFF, 0xFFFFFFFF,
0x7F7FFFFE, 0x00000002, 0x7F000000, 0xFF000000,
]
def decode(b):
return struct.unpack("<f", struct.pack("<I", b))[0]
# C literal (exact hex float) and its value.
def lit(x):
if math.isinf(x):
return "(-__builtin_inff())" if x < 0 else "__builtin_inff()"
return x.hex() + "f"
CONSTS = [0.0, -0.0, 1.0, -1.0, 2.5, -2.5, math.inf, -math.inf,
decode(0x00000001), decode(0x7F7FFFFF)]
ABS_CONSTS = [0.0, -0.0, 1.0, 2.5, decode(0x00000001),
decode(0x7F7FFFFF), math.inf, -1.0]
else:
T = "double"
BITS_T = "u64"
SUF = "ULL"
FABS = "__builtin_fabs"
VALS = [
0x0000000000000000, 0x8000000000000000, 0x0000000000000001,
0x8000000000000001, 0x000FFFFFFFFFFFFF, 0x800FFFFFFFFFFFFF,
0x0010000000000000, 0x8010000000000000, 0x3FEFFFFFFFFFFFFF,
0x3FF0000000000000, 0x3FF0000000000001, 0xBFEFFFFFFFFFFFFF,
0xBFF0000000000000, 0xBFF0000000000001, 0x4003FFFFFFFFFFFF,
0x4004000000000000, 0x4004000000000001, 0xC003FFFFFFFFFFFF,
0xC004000000000000, 0xC004000000000001, 0x7FEFFFFFFFFFFFFF,
0xFFEFFFFFFFFFFFFF, 0x7FF0000000000000, 0xFFF0000000000000,
0x7FF8000000000000, 0xFFF8000000000000, 0x7FF0000000000001,
0xFFF0000000000001, 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
0x7FEFFFFFFFFFFFFE, 0x0000000000000002, 0x0000000100000000,
0x8000000100000000,
]
def decode(b):
return struct.unpack("<d", struct.pack("<Q", b))[0]
def lit(x):
if math.isinf(x):
return "(-__builtin_inf())" if x < 0 else "__builtin_inf()"
return x.hex()
CONSTS = [0.0, -0.0, 1.0, -1.0, 2.5, -2.5, math.inf, -math.inf,
decode(0x0000000000000001), decode(0x7FEFFFFFFFFFFFFF)]
ABS_CONSTS = [0.0, -0.0, 1.0, 2.5, decode(0x0000000000000001),
decode(0x7FEFFFFFFFFFFFFF), math.inf, -1.0]
def isun(a, b):
return math.isnan(a) or math.isnan(b)
# (C expression with A and B for the operands, Python predicate).
PREDS = [
("A < B", lambda a, b: a < b),
("A <= B", lambda a, b: a <= b),
("A > B", lambda a, b: a > b),
("A >= B", lambda a, b: a >= b),
("A == B", lambda a, b: a == b),
("A != B", lambda a, b: a != b),
("!(A < B)", lambda a, b: not a < b),
("!(A <= B)", lambda a, b: not a <= b),
("!(A > B)", lambda a, b: not a > b),
("!(A >= B)", lambda a, b: not a >= b),
("__builtin_isunordered(A, B)", isun),
("!__builtin_isunordered(A, B)", lambda a, b: not isun(a, b)),
("__builtin_islessgreater(A, B)", lambda a, b: a < b or a > b),
("!__builtin_islessgreater(A, B)", lambda a, b: not (a < b or a > b)),
]
# Every test: (C expression of a, Python predicate of a).
tests = []
for c in CONSTS:
for expr, fn in PREDS:
tests.append((expr.replace("A", "a").replace("B", lit(c)),
(lambda fn, c: lambda a: fn(a, c))(fn, c)))
for c in ABS_CONSTS:
for expr, fn in PREDS:
tests.append((expr.replace("A", FABS + "(a)").replace("B", lit(c)),
(lambda fn, c: lambda a: fn(abs(a), c))(fn, c)))
for expr, fn in PREDS:
tests.append((expr.replace("A", "a").replace("B", "a"),
(lambda fn: lambda a: fn(a, a))(fn)))
tests += [
("__builtin_isnan(a)", lambda a: math.isnan(a)),
("!__builtin_isnan(a)", lambda a: not math.isnan(a)),
("__builtin_isinf(a)", lambda a: math.isinf(a)),
("__builtin_isfinite(a)", lambda a: math.isfinite(a)),
]
per = (len(tests) + NPARTS - 1) // NPARTS
tests = tests[(PART - 1) * per:PART * per]
nv = len(VALS)
nf = 2 * len(tests)
bits = []
for expr, fn in tests:
for form in range(2):
for v in VALS:
bits.append(1 if fn(decode(v)) else 0)
exp = bytearray((len(bits) + 7) // 8)
for i, b in enumerate(bits):
if b:
exp[i >> 3] |= 1 << (i & 7)
out = []
w = out.append
w("// Float / double compares against constants, generated by")
w("// tests/fpNan/genFpCmpConst.py %s %d %d; see there." % (KIND, PART, NPARTS))
w("typedef unsigned char u8;")
w("typedef unsigned short u16;")
w("typedef unsigned long u32;")
w("typedef unsigned long long u64;")
w('#define BAR() __asm__ volatile("" ::: "memory")')
w("#define NOI __attribute__((noinline)) static")
w("#define NV %d" % nv)
w("#define NF %d" % nf)
w("typedef %s T;" % T)
w("static const %s kBits[NV] = {" % BITS_T)
for i in range(0, nv, 4):
w(" " + " ".join("0x%X%s," % (v, SUF) for v in VALS[i:i + 4]))
w("};")
w("static const u8 kExp[%d] = {" % len(exp))
for i in range(0, len(exp), 16):
w(" " + " ".join("0x%02X," % b for b in exp[i:i + 16]))
w("};")
for k, (expr, fn) in enumerate(tests):
w("NOI u16 b%d(T a) { if (%s) { BAR(); return 1u; } return 0u; }"
% (k, expr))
w("NOI u16 v%d(T a) { return (u16)(%s); }" % (k, expr))
w("typedef u16 (*Fn)(T);")
w("static Fn const kFn[NF] = {")
for k in range(len(tests)):
w(" b%d, v%d," % (k, k))
w("};")
w("static u16 gBad;")
w("static u16 gFirst = 0xFFFFu;")
w("static T val(u16 i) {")
w(" union { %s u; T x; } c;" % BITS_T)
w(" c.u = kBits[i];")
w(" return c.x;")
w("}")
w("int main(void) {")
w(" u16 f;")
w(" u16 i;")
w(" u16 bit = 0u;")
w(" for (f = 0u; f < NF; f++) {")
w(" for (i = 0u; i < NV; i++) {")
w(" u16 want = (u16)((kExp[bit >> 3] >> (bit & 7u)) & 1u);")
w(" if ((kFn[f](val(i)) != 0u) != want) {")
w(" if (gBad == 0u) {")
w(" gFirst = bit;")
w(" }")
w(" gBad++;")
w(" }")
w(" bit++;")
w(" }")
w(" }")
w(" *(volatile u16 *)0x025000UL = gBad;")
w(" *(volatile u16 *)0x025002UL = gFirst;")
w(" *(volatile u16 *)0x025004UL = 0xC0DEu;")
w(" for (;;) {")
w(" }")
w(" return 0;")
w("}")
print("\n".join(out))