#!/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(" 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))