#!/usr/bin/env python3 # Emits the C source of the smoke check "mul/div libcalls", with kExp # holding the expected checksums computed here (C semantics). # # Every multiply and divide helper in runtime/src/libgcc.s on N operand # pairs per operation: an xorshift32 stream (shifts and XORs only, so the # generator needs no helper it tests), each operand masked to a size class # (full 32 bits, 16, 9, 8 bits, 4 bits, sign-extended 16 / 8 bits) so every # fast path and its boundaries are taken, plus the word-boundary values in # kEdge. Results fold into one checksum per operation. # # Mailbox: 0x025000 = bitmask of operations whose checksum differs, # 0x025002 = 0xC0DE when done. import sys N = int(sys.argv[1]) if len(sys.argv) > 1 else 300 EDGE = [0x00000000, 0x00000001, 0x00000002, 0x00000003, 0x0000007F, 0x00000080, 0x000000FF, 0x00000100, 0x00000101, 0x000001FF, 0x00007FFF, 0x00008000, 0x00008001, 0x0000FFFF, 0x00010000, 0x00010001, 0x0001FFFF, 0x00FFFFFF, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFF0000, 0xFFFF7FFF, 0xFFFF8000, 0xFFFFFF80, 0xFFFFFFFE, 0xFFFFFFFF, 0x12345678, 0xFEDCBA98, 0x0000ABCD] M32 = 0xFFFFFFFF def xs(x): x ^= (x << 13) & M32 x ^= x >> 17 x ^= (x << 5) & M32 return x def cls(v, c): # Size class c (0..7) of the raw 32-bit value v. if c == 0: return v if c == 1: return v & 0xFFFF if c == 2: return v & 0x1FF if c == 3: return v & 0xFF if c == 4: return v & 0xF if c == 5: return (v & 0xFFFF) | (0xFFFF0000 if v & 0x8000 else 0) if c == 6: return (v & 0xFF) | (0xFFFFFF00 if v & 0x80 else 0) return v & 0x7FFFFFFF def s32(x): x &= M32 return x - (1 << 32) if x & 0x80000000 else x def s16(x): x &= 0xFFFF return x - 0x10000 if x & 0x8000 else x def tdiv(a, b): q = abs(a) // abs(b) return -q if (a < 0) != (b < 0) else q def tmod(a, b): return a - tdiv(a, b) * b OPS = ['mulU32', 'mulU16x', 'mulS16x', 'mulU16', 'divU32', 'modU32', 'divS32', 'modS32', 'divU16', 'modU16', 'divS16', 'modS16'] def ev(op, a, b): if op == 'mulU32': return (a * b) & M32 if op == 'mulU16x': return ((a & 0xFFFF) * (b & 0xFFFF)) & M32 if op == 'mulS16x': return (s16(a) * s16(b)) & M32 if op == 'mulU16': return (a * b) & 0xFFFF if op == 'divU32': return a // b if op == 'modU32': return a % b if op == 'divS32': return tdiv(s32(a), s32(b)) & M32 if op == 'modS32': return tmod(s32(a), s32(b)) & M32 if op == 'divU16': return (a & 0xFFFF) // (b & 0xFFFF) if op == 'modU16': return (a & 0xFFFF) % (b & 0xFFFF) if op == 'divS16': return tdiv(s16(a), s16(b)) & 0xFFFF return tmod(s16(a), s16(b)) & 0xFFFF def fix(op, a, b): # The divisor must not be zero, and INT_MIN / -1 is undefined. if op.startswith('div') or op.startswith('mod'): w16 = op.endswith('16') if (b & 0xFFFF if w16 else b) == 0: b |= 1 if op.endswith('S32') and a == 0x80000000 and b == M32: b = 1 if op.endswith('S16') and (a & 0xFFFF) == 0x8000 and (b & 0xFFFF) == 0xFFFF: b = 1 return a, b def rot(h): return ((h << 5) | (h >> 27)) & M32 exp = [] for op in OPS: h = 0 x = 0x2545F491 pairs = [(a, b) for a in EDGE for b in EDGE] for i in range(N): x = xs(x) a = cls(x, (x >> 8) & 7) x = xs(x) b = cls(x, (x >> 8) & 7) pairs.append((a, b)) for a, b in pairs: a, b = fix(op, a, b) h = rot(h) ^ ev(op, a, b) exp.append(h) print('''// Generated by tests/regress/genMulDiv.py: every multiply / divide helper // on the word-boundary values and an xorshift32 stream of size-classed // operands; one checksum per operation against kExp (host C semantics). // Mailbox: 0x025000 = bitmask of mismatching operations, 0x025002 = // 0xC0DE when done. int is 16 bits and long 32 here. typedef unsigned short u16; typedef short s16; typedef unsigned long u32; typedef long s32; #define NOI __attribute__((noinline)) static #define NOPS %d #define NRAND %d #define NEDGE %d static const u32 kEdge[NEDGE] = { %s }; static const u32 kExp[NOPS] = { %s }; NOI u32 mulU32(u32 a, u32 b) { return a * b; } NOI u32 mulU16x(u16 a, u16 b) { return (u32)a * (u32)b; } NOI s32 mulS16x(s16 a, s16 b) { return (s32)a * (s32)b; } NOI u16 mulU16(u16 a, u16 b) { return (u16)(a * b); } NOI u32 divU32(u32 a, u32 b) { return a / b; } NOI u32 modU32(u32 a, u32 b) { return a %% b; } NOI s32 divS32(s32 a, s32 b) { return a / b; } NOI s32 modS32(s32 a, s32 b) { return a %% b; } NOI u16 divU16(u16 a, u16 b) { return a / b; } NOI u16 modU16(u16 a, u16 b) { return a %% b; } NOI s16 divS16(s16 a, s16 b) { return a / b; } NOI s16 modS16(s16 a, s16 b) { return a %% b; } static u32 xs(u32 x) { x ^= x << 13; x ^= x >> 17; x ^= x << 5; return x; } static u32 cls(u32 v, u16 c) { switch (c) { case 0: return v; case 1: return v & 0xFFFFUL; case 2: return v & 0x1FFUL; case 3: return v & 0xFFUL; case 4: return v & 0xFUL; case 5: return (v & 0xFFFFUL) | ((v & 0x8000UL) ? 0xFFFF0000UL : 0UL); case 6: return (v & 0xFFUL) | ((v & 0x80UL) ? 0xFFFFFF00UL : 0UL); default: return v & 0x7FFFFFFFUL; } } static u32 ev(u16 op, u32 a, u32 b) { if (op >= 4) { u16 w16 = (op >= 8); if ((w16 ? (b & 0xFFFFUL) : b) == 0UL) { b |= 1UL; } if ((op == 6 || op == 7) && a == 0x80000000UL && b == 0xFFFFFFFFUL) { b = 1UL; } if ((op == 10 || op == 11) && (a & 0xFFFFUL) == 0x8000UL && (b & 0xFFFFUL) == 0xFFFFUL) { b = 1UL; } } switch (op) { case 0: return mulU32(a, b); case 1: return mulU16x((u16)a, (u16)b); case 2: return (u32)mulS16x((s16)a, (s16)b); case 3: return mulU16((u16)a, (u16)b); case 4: return divU32(a, b); case 5: return modU32(a, b); case 6: return (u32)divS32((s32)a, (s32)b); case 7: return (u32)modS32((s32)a, (s32)b); case 8: return divU16((u16)a, (u16)b); case 9: return modU16((u16)a, (u16)b); case 10: return (u16)divS16((s16)a, (s16)b); default: return (u16)modS16((s16)a, (s16)b); } } int main(void) { u16 bad = 0; u16 op; *(volatile u16 *)0x025002UL = 0u; for (op = 0; op < NOPS; op++) { u32 h = 0UL; u32 x = 0x2545F491UL; u16 i; u16 j; for (i = 0; i < NEDGE; i++) { for (j = 0; j < NEDGE; j++) { h = ((h << 5) | (h >> 27)) ^ ev(op, kEdge[i], kEdge[j]); } } for (i = 0; i < NRAND; i++) { u32 a; u32 b; x = xs(x); a = cls(x, (u16)((x >> 8) & 7UL)); x = xs(x); b = cls(x, (u16)((x >> 8) & 7UL)); h = ((h << 5) | (h >> 27)) ^ ev(op, a, b); } if (h != kExp[op]) { bad |= (u16)(1u << op); } } *(volatile u16 *)0x025000UL = bad; *(volatile u16 *)0x025002UL = 0xC0DEu; for (;;) { } return 0; }''' % (len(OPS), N, len(EDGE), ', '.join('0x%08XUL' % v for v in EDGE), ', '.join('0x%08XUL' % v for v in exp)))