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

240 lines
7.3 KiB
Python
Executable file

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