Vectors for kernels: masks and select, shuffles and byte lookup, dot_add, @cpu and cpu_has; --cpu native on Nix

- comparisons of vectors give masks (boolxN): select(m, a, b), m.any/all/count, & | ^ !;
  f32x8(list, i, fill) and v.store_part(list, i) for the end of a list (masked load/store)
- v.shuffle(constant lanes), v.lo(), v.hi(), concat(a, b), table.lookup(indices):
  pshufb / vpshufb on x86 (SSSE3 and up), tbl on ARM64 (checked in the assembly, LLVM 18 and 21)
- acc.dot_add(a, b): i32 lanes gain 4 byte products each, exactly; vpdpbusd with AVX-VNNI or AVX-512 VNNI,
  usdot/sdot/udot on ARM64, the sum written out elsewhere (pmaddwd); run here with all three paths agreeing
- @cpu("avx2,fma") fn: LLVM target-features for one function (+evex512 for AVX-512, which LLVM 18+ needs);
  cpu_has("avx2"): CPUID + XGETBV on x86, hwcaps on Linux ARM64, sysctl on macOS; a wide vector can't be passed
  between functions with different @cpu (its registers differ), and the compiler says so
- the compiler asks clang which features --cpu means (its predefined macros, cached), so dot_add picks the instruction
- Nix's clang wrapper dropped -march=native in nix-shell (reproduced): finch tells it NIX_ENFORCE_NO_NATIVE=0, shell.nix too,
  and warns if --cpu native still made no difference

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Psc42moPLwP43Cmb9oZqSR
This commit is contained in:
Claude 2026-11-01 15:41:47 +00:00
commit 04f1f94a21
No known key found for this signature in database
23 changed files with 964 additions and 45 deletions

50
tests/run/masks.fch Normal file
View file

@ -0,0 +1,50 @@
// Masks: comparisons of vectors, select, any/all/count, and the end of a list
fn relu(f32x8 v) -> f32x8 {
return select(v > 0.0, v, 0.0)
}
// the sum of a list of any length: whole vectors, then the last piece with its missing lanes as 0
fn total([]f32 xs) -> f32 {
acc := f32x8(0.0)
i := 0
while i < xs.len {
acc = acc + f32x8(xs, i, 0.0)
i += 8
}
return acc.sum()
}
// add 1 to every number of a list, in place, the last piece too
fn bump([]f32 xs) {
i := 0
while i < xs.len {
(f32x8(xs, i, 0.0) + 1.0).store_part(xs, i)
i += 8
}
}
fn main() {
v := f32x8(-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, -4.0, 5.0)
m := v > 0.0
print(m, m.count(), m.any(), m.all(), m[3])
print(relu(v), select(!m, v, -1.0))
print((v >= -1.0) & (v < 3.0), (v == 0.0) | (v == 5.0), m ^ (v > 1.0))
a := i32x4(1, 5, 3, 7)
b := i32x4(4, 4, 4, 4)
print(select(a > b, a, b), (a == a).all(), (a != b).all())
u := u8x16(200)
print((u > u8x16(100)).all(), i32x4(a > b), f32x4(a < b))
k := i32x4(0, 2, 0, -1)
print(boolx4(k), boolx4(true, false, true, false))
[]f32 xs
for n in 0..13 {
xs.push(f32(n))
}
print(total(xs))
bump(xs)
print(xs)
print(f32x8(xs, 8, -1.0), f32x8(xs, 13, 9.0), f32x8(xs, 100, 7.0))
}