Files
UmbraTrans/umbratrans_web/_capability_probe.py
2026-10-18 19:01:05 -07:00

183 lines
6.8 KiB
Python

# Copyright 2026 Umbra Tools (umbratools.com)
# SPDX-License-Identifier: Apache-2.0
"""The C probe, kept in a string so the module has no build step.
Design notes that are not obvious from the code, and which matter:
**One translation unit per feature.** A single source containing every intrinsic cannot
be built with `-mavx2` alone: the FMA, AVX-512 and AES intrinsics then fail to inline
(`always_inline` against a target lacking the flag is a hard error). That was the first
version and it reported every feature unverified on a machine that has most of them. So
the source below is compiled once per feature with `__FEATURE__` substituted.
**Intrinsics, not inline assembly.** The first attempt used inline asm and every operand
count was wrong. Intrinsics let the compiler get the encoding right, and the result is
verified by disassembly anyway.
**Volatile inputs.** At `-O2` with constant arrays, GCC folded `a*b` at compile time and
emitted no instruction, so a probe that "passed" would have proved nothing and reported
every CPU as supporting AVX. The arrays are `volatile` and seeded from `argc`, so the
load must happen at run time.
**Wrong answers are unverified, not absent.** If the program runs and computes something
wrong, the CPU executed the instruction. Reporting `absent` would be a lie about the
hardware; it is a bug in the probe.
"""
_C_SOURCE = r"""
#include <immintrin.h>
#include <emmintrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
/* volatile: these must be loaded at run time so the compiler cannot fold. */
static volatile double vin[8];
static volatile double vcoef[8];
static volatile int iin[8];
static volatile int icoef[8];
static double elapsed(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
}
int main(int argc, char **argv) {
double d[8];
int iv[8];
for (int i = 0; i < 8; i++) {
vin[i] = 1.5; vcoef[i] = 2.0;
iin[i] = i + 1; icoef[i] = i + 1;
}
/* Seed from argc so the values are unknowable at compile time. */
if (argc > 99) { vin[0] = 3.0; iin[0] = 7; }
/* The volatile arrays are cast away once, explicitly: the load intrinsics take a
* non-volatile pointer, and casting here keeps the qualifier from silently
* disabling the optimisation we depend on. */
const double *vinp = (const double *)vin;
const double *vcoefp = (const double *)vcoef;
const int *iinp = (const int *)iin;
const int *icoefp = (const int *)icoef;
#if __FEATURE_INDEX__ == 0
{
__m128d a = _mm_loadu_pd(vinp), b = _mm_loadu_pd(vcoefp);
_mm_storeu_pd(d, _mm_mul_pd(a, b));
if (fabs(d[0] - 3.0) > 1e-9) return 3;
}
#elif __FEATURE_INDEX__ == 1
{
/* CMPNEQPD is SSE4.2. On an older target this is rejected at build time,
* which is the correct outcome: the toolchain cannot express the feature.
*
* The result is a *mask* - all ones in the 64-bit lane where the operands
* differ, zero where they match - not a double. Storing it through
* _mm_storeu_pd and reading a double back gives NaN, which reads as a wrong
* answer and made this probe report unverified on every machine. The first
* version did exactly that. */
unsigned long long mask[2];
__m128d a = _mm_loadu_pd(vinp), b = _mm_loadu_pd(vcoefp);
_mm_storeu_si64(mask, _mm_castpd_si128(_mm_cmpneq_pd(a, b)));
/* 1.5 != 2.0 -> mask, 2.5 != 2.0 -> no mask. Seeded from argc, so both
* lanes are unknowable at compile time. */
if (mask[0] != 0xFFFFFFFFFFFFFFFFULL) return 3;
if (mask[1] != 0) return 3;
}
#elif __FEATURE_INDEX__ == 2
{
__m256d a = _mm256_loadu_pd(vinp), b = _mm256_loadu_pd(vcoefp);
_mm256_storeu_pd(d, _mm256_mul_pd(a, b));
if (fabs(d[0] - 3.0) > 1e-9) return 3;
}
#elif __FEATURE_INDEX__ == 3
{
__m256i a = _mm256_loadu_si256((const __m256i *)iinp);
__m256i b = _mm256_loadu_si256((const __m256i *)icoefp);
_mm256_storeu_si256((__m256i *)iv, _mm256_add_epi32(a, b));
if (iv[0] != 2 || iv[7] != 16) return 3;
}
#elif __FEATURE_INDEX__ == 4
{
/* a*b + a: the result differs from either input, so a compiler that split
* this into a multiply and an add, or folded it to a multiply, is caught. */
__m256d a = _mm256_loadu_pd(vinp), b = _mm256_loadu_pd(vcoefp);
_mm256_storeu_pd(d, _mm256_fmadd_pd(a, b, a));
if (fabs(d[0] - 4.5) > 1e-9) return 3;
}
#elif __FEATURE_INDEX__ == 5
{
__m512d a = _mm512_loadu_pd(vinp), b = _mm512_loadu_pd(vcoefp);
_mm512_storeu_pd(d, _mm512_mul_pd(a, b));
if (fabs(d[0] - 3.0) > 1e-9) return 3;
}
#elif __FEATURE_INDEX__ == 6
{
unsigned long long a = 0x9e3779b97f4a7c15ULL, r = 0;
/* RORX is BMI2. On an older target the assembler rejects it. */
__asm__ volatile("rorx $13, %1, %0" : "=r"(r) : "r"(a));
if (r == a) return 3;
}
#elif __FEATURE_INDEX__ == 7
{
unsigned char state[16], plain[16];
__m128i k, block;
memcpy(plain, "0123456789abcdef", 16);
memcpy(state, plain, 16);
block = _mm_loadu_si128((const __m128i *)state);
k = _mm_setzero_si128();
k = _mm_aesenc_si128(k, block);
k = _mm_aesenclast_si128(k, block);
_mm_storeu_si128((__m128i *)state, k);
/* No reference AES here, so the assertion is that the bytes changed: the
* instruction ran and did AES work rather than being a no-op copy. */
if (!memcmp(state, plain, 16)) return 3;
}
#else
#error "unknown feature index"
#endif
{
double t0, t1, acc = 0.0;
t0 = elapsed();
for (int i = 0; i < 300000; i++) acc += (double)i * 0.5;
t1 = elapsed();
(void)acc;
printf("%.6f\n", t1 - t0);
}
return 0;
}
"""
#: The mnemonic each feature must produce, checked in the disassembly. If the compiler
#: folded the probe away, none of these appear and the feature is reported unverified.
#: Lists, because different compilers choose different encodings.
_EMITTED = {
"sse2": ("mulpd", "addpd"),
"sse4_2": ("cmpneqpd", "cmpltpd", "cmpneqpd"),
"avx": ("vmulpd", "vaddpd"),
"avx2": ("vpaddd", "vpaddq"),
"fma": ("vfmadd", "vfnmadd"),
"avx512f": ("vmulpd", "vaddpd"),
"bmi2": ("rorx",),
"aes_ni": ("aesenc",),
}
#: Compiler flag per feature. Without the explicit flag the compiler targets its own
#: build machine and emits baseline instructions, so the probe would pass everywhere.
_FEATURE_FLAGS = {
"sse2": ["-msse2"],
"sse4_2": ["-msse4.2"],
"avx": ["-mavx"],
"avx2": ["-mavx2"],
"fma": ["-mfma"],
"avx512f": ["-mavx512f"],
"bmi2": ["-mbmi2"],
"aes_ni": ["-maes"],
}