nx_i8dot32a_kat.nx source
↩ module page · 57 lines · 1999 B
1// nx_i8dot32a_kat.nx -- KAT for the AVX2 __f32_i8dot32a intrinsic.
2// __f32_i8dot32a(a:*i8[32], b:*f32[32]) = sum_{j<32} sext(a[j]) * b[j], with
3// 2-accumulator AVX2 summation. Exact-INTEGER inputs (int8 x small-int f32,
4// all products+sums exactly representable) so the result is order-INDEPENDENT
5// -> must equal BOTH the scalar reference AND the SSE __f32_i8dot32 exactly.
6// That proves the AVX2 codegen (vpmovsxbd/vcvtdq2ps/vmulps/vaddps + 2-acc
7// combine + hsum) computes the right values. expect_exit: 0 (77/78/79 fail)
8import "nx_syscalls.nx"
9import "nx_f32.nx"
10import "nx_f32_cvt.nx"
11
12func k_st4(p: *u8, idx: i64, bits: i64) -> i64 {
13 p[idx*4+0] = bits as u8
14 p[idx*4+1] = (bits>>8) as u8
15 p[idx*4+2] = (bits>>16) as u8
16 p[idx*4+3] = (bits>>24) as u8
17 return 0
18}
19
20func main() -> i64 {
21 let a: *u8 = sys_mmap(32)
22 let b: *u8 = sys_mmap(32 * 4)
23 var j: i64 = 0
24 while j < 32 {
25 a[j] = (j - 16) as u8
26 k_st4(b, j, nx_i32_to_f32((j % 3) + 1))
27 j = j + 1
28 }
29 // scalar reference
30 var ref: i64 = 0
31 var k: i64 = 0
32 while k < 32 {
33 ref = __f32_add(ref, __f32_mul(nx_i32_to_f32(k - 16), nx_i32_to_f32((k % 3) + 1)))
34 k = k + 1
35 }
36 // AVX2 == scalar AND AVX2 == SSE (exact-int -> order-independent)
37 if __f32_i8dot32a(a, b) != ref { return 77 }
38 if __f32_i8dot32a(a, b) != __f32_i8dot32(a, b) { return 77 }
39
40 // all +1
41 var j2: i64 = 0
42 while j2 < 32 { a[j2] = 1 as u8; j2 = j2 + 1 }
43 var ref2: i64 = 0
44 var k2: i64 = 0
45 while k2 < 32 { ref2 = __f32_add(ref2, nx_i32_to_f32((k2 % 3) + 1)); k2 = k2 + 1 }
46 if __f32_i8dot32a(a, b) != ref2 { return 78 }
47
48 // negatives, all -2
49 var j3: i64 = 0
50 while j3 < 32 { a[j3] = (0 - 2) as u8; j3 = j3 + 1 }
51 var ref3: i64 = 0
52 var k3: i64 = 0
53 while k3 < 32 { ref3 = __f32_add(ref3, __f32_mul(nx_i32_to_f32(0 - 2), nx_i32_to_f32((k3 % 3) + 1))); k3 = k3 + 1 }
54 if __f32_i8dot32a(a, b) != ref3 { return 79 }
55
56 return 0
57}