240 lines
7.3 KiB
Python
Executable file
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)))
|