Files
velzie 87bb59ed15 Close the SIMD decoder gap: by-element, widening, structure loads
Real numeric code walked straight into the parts of AdvSIMD that were
never decoded. A C matmul plus dot-product benchmark hit six distinct
families before it would run, so this fills in all of them.

  - by-element (`fmla v0.4s, v1.4s, v2.s[3]`): the lane index is spread
    over three fields and assembled differently per width. Once the lane
    is splatted these are the three-same operations exactly, so
    `three_same` was split to take a value for its second operand and the
    lowerings are shared outright.
  - three-different (`smull`, `uaddw`, `sabdl`, `smlal`): widen, then
    operate at the wider size. `Q` picks which half of the sources to
    read rather than the register width.
  - the width-changing shifts (`sshll`/`sxtl`/`shrn`), which had been
    decoding but falling out of the lifter as illegal.
  - `fcvtl`/`fcvtn`, single <-> double across a vector.
  - single-structure `ld1`/`st1`/`ld1r`, and `ld2`/`st2`, which are a
    pair of shuffles in each direction.
  - `fccmp`, which had an ISA variant and a lifter arm already and only
    ever needed the decoder to reach them.

Two real bugs fell out. `fmov v0.2d, #0.5` was decoded as `mvni` and had
its bits inverted, because the inverting test admitted cmode 1111. And
`scvtf v0.2d, v0.2d` was emitting f64x2.convert_low_i32x4_s: wasm has no
i64x2 <-> f64x2 conversion at all, so the 64-bit arrangements now convert
lane by lane with the scalar instructions, and the opcode table panics
rather than substituting a wrong width again.

Corpus: 311 undecoded -> 116, operand-text diffs 473 -> 2, mnemonic
mismatches still 0, over 752k instructions. What is left undecoded is
ld3/ld4, the halving-narrow and saturating-doubling multiplies, half
precision and the fixed-point converts.

Validated against QEMU: a matmul/dot/classify benchmark and a NEON
structure-load test driven through ACLE intrinsics, both added to the
suite (35 comparisons), plus 2500 fuzz cases.
2026-08-05 05:20:42 -04:00

70 lines
2.2 KiB
C

// Numeric kernels chosen so gcc -O3 emits fmla-by-element, fccmp and the
// scalar AdvSIMD forms: matmul and dot products index one operand's lane,
// and the min/max reductions compile to conditional compares.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define N 96
static float A[N][N], B[N][N], C[N][N];
static void matmul(void) {
memset(C, 0, sizeof C);
for (int i = 0; i < N; i++)
for (int k = 0; k < N; k++) {
float a = A[i][k];
for (int j = 0; j < N; j++) C[i][j] += a * B[k][j];
}
}
static double dotd(const double *x, const double *y, int n) {
double s = 0;
for (int i = 0; i < n; i++) s += x[i] * y[i];
return s;
}
// Conditional float compares: gcc turns the chained test into fccmp.
static int classify(double x, double lo, double hi) {
if (x > lo && x < hi) return 1;
if (x <= lo || isnan(x)) return 2;
return 3;
}
int main(void) {
unsigned s = 12345;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) {
s = s * 1103515245u + 12345u;
A[i][j] = (float)((s >> 16) % 1000) / 32.0f;
s = s * 1103515245u + 12345u;
B[i][j] = (float)((s >> 16) % 1000) / 32.0f;
}
matmul();
double sum = 0, mx = -1e30, mn = 1e30;
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++) {
sum += C[i][j];
if (C[i][j] > mx) mx = C[i][j];
if (C[i][j] < mn) mn = C[i][j];
}
printf("matmul sum=%.4f max=%.4f min=%.4f\n", sum, mx, mn);
static double x[1024], y[1024];
for (int i = 0; i < 1024; i++) { x[i] = i * 0.5 - 100.0; y[i] = 1.0 / (i + 1); }
printf("dot=%.9f\n", dotd(x, y, 1024));
int c[4] = {0};
for (int i = 0; i < 1000; i++) {
double v = (i % 7 == 0) ? nan("") : (double)i - 500.0;
c[classify(v, -200.0, 200.0)]++;
}
printf("classify %d %d %d\n", c[1], c[2], c[3]);
// Scalar AdvSIMD: gcc uses cmge/abs on d registers for 64-bit lanes.
long long acc = 0;
for (int i = 0; i < 4096; i++) { long long v = (i * 37) % 501 - 250; acc += llabs(v); }
printf("absacc=%lld\n", acc);
return 0;
}