Files
gexandClaude Opus 5 14712ccdcb NEON: lane arithmetic, permutes, reductions and conversions
Covers the three-same, two-register-misc, across-lanes, permute, extract,
table-lookup, copy and shift-by-immediate groups. Undecoded instructions
across the corpus drop from 2,442 to 317 (0.05%), still with zero mnemonic
mismatches.

The lever that made this tractable is i8x16.shuffle: trn, uzp, zip, ext, the
rev family and element dup/ins are all fixed byte permutations, so one IR
node covers eight instruction families. bitselect covers bsl/bit/bif, and
swizzle is tbl with a single table register.

Two decoder bugs the corpus found: ins (general) has op == 0, not 1, so
"mov v20.s[1], w16" never decoded; and bit 23 is part of the size field for
the integer two-register-misc forms, so testing it alone sent "rev64 v0.4s"
down the floating-point path.

Where wasm has no equivalent the lowering is spelled out rather than skipped:
horizontal reductions fold in log2(lanes) shuffle-and-combine steps, pairwise
add deinterleaves first, absolute difference is max-minus-min so it cannot
overflow at the extremes, and multi-register tbl biases each swizzle.

A -O3 vectorised program is now part of the integration ladder, so the corpus
tracks what gcc's vectoriser emits rather than what we thought to test.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01KcidnrZ5aLqKoBJBqdjx9d
2026-08-05 03:12:00 -04:00

29 lines
1.8 KiB
C

/* Auto-vectorising loops, for the NEON path (PLAN §5.7).
*
* Compiled at -O3 these become NEON: lane-wise arithmetic, horizontal
* reductions, shuffles and the load/store forms. The point is not the
* arithmetic but that gcc's vectoriser picks the instructions, so the corpus
* tracks what real compilers emit rather than what we thought to test.
*/
__attribute__((noinline)) static int isum(const int *a, int n){ int s=0; for(int i=0;i<n;i++) s+=a[i]; return s; }
__attribute__((noinline)) static float fsum(const float *a, int n){ float s=0; for(int i=0;i<n;i++) s+=a[i]*a[i]; return s; }
__attribute__((noinline)) static void saxpy(float *y,const float *x,float a,int n){ for(int i=0;i<n;i++) y[i]=a*x[i]+y[i]; }
__attribute__((noinline)) static int imax(const int *a,int n){ int m=a[0]; for(int i=1;i<n;i++) if(a[i]>m) m=a[i]; return m; }
__attribute__((noinline)) static int cnt(const unsigned char *a,int n){ int c=0; for(int i=0;i<n;i++) c+=__builtin_popcount(a[i]); return c; }
__attribute__((noinline)) static void rev(unsigned *d,const unsigned *s,int n){ for(int i=0;i<n;i++) d[i]=__builtin_bswap32(s[i]); }
int main(void){
enum { N = 1024 };
static int ia[N]; static float fa[N], fb[N]; static unsigned char ba[N]; static unsigned ua[N], ub[N];
unsigned seed=7;
for (int i=0;i<N;i++){ seed=seed*1103515245u+12345u; ia[i]=(int)(seed>>17)-16384; fa[i]=(float)((seed>>20)%1000)/7.0f; fb[i]=1.0f; ba[i]=(unsigned char)(seed>>8); ua[i]=seed; }
printf("isum=%d\n", isum(ia,N));
printf("fsum=%.4f\n", fsum(fa,N));
saxpy(fb,fa,2.5f,N); printf("saxpy=%.4f %.4f\n", fb[0], fb[N-1]);
printf("imax=%d\n", imax(ia,N));
printf("popcount=%d\n", cnt(ba,N));
rev(ub,ua,N); printf("bswap=%08x %08x\n", ub[0], ub[N-1]);
double d=0; for(int i=0;i<N;i++) d += (double)fa[i]/(1.0+i);
printf("dsum=%.8f\n", d);
return 0;
}