217 lines
8.1 KiB
Python
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))
|