code wiki / (root) / simd.nx

simd.nx source

↩ module page · 203 lines · 6623 B

1// simd.nx -- SIMD vector types + intrinsics (spec + soft-fallback). 2// 3// Target: 8-16x speedup on every numeric routine NishiLang ships 4// (sha256, chacha20, base64, CRCs, etc.) via target-native 5// vector instructions (AVX-512, RVV, NEON). 6// 7// Two-phase approach: 8// 9// Phase A (this file): define the SIMD type API + soft-fallback 10// implementation. Every SIMD op is a regular i64 function 11// that manipulates an "8x i64" packed buffer one lane at a 12// time. No speedup yet, but callers can write SIMD-shaped 13// code today. 14// 15// Phase B (riscv.nx backend work, pending): pattern-match these 16// functions during codegen and replace with single vector 17// instructions. Same source code compiles to 8-16x 18// faster machine code. 19// 20// Why this order: API commitment FIRST means library authors 21// can start writing SIMD-shaped routines now. The speedup 22// lands later without breaking any caller. 23// 24// Invariants: 25// V1 Vec64 is 8x i64 = 64 bytes = one AVX-512 register / two 26// AVX2 regs / one RVV VLEN-512 register / four NEON regs. 27// V2 Ops are pure functions of operand lanes -- no side 28// effects, no aliasing concerns. 29// V3 Memory ops (load/store) take aligned pointers; caller 30// ensures 64-byte alignment for max throughput. 31 32import "syscalls.nx" 33 34// 64-byte vector = 8 i64 lanes. Stored as a heap pointer to an 35// 8-element i64 array. Future hardware-backed versions will use 36// register allocation for this. 37struct Vec64 { 38 lane0: i64, lane1: i64, lane2: i64, lane3: i64, 39 lane4: i64, lane5: i64, lane6: i64, lane7: i64, 40} 41 42const VEC64_BYTES: i64 = 64 43 44// ===== construction ================================================= 45 46func vec64_zero() -> *Vec64 { 47 let raw: *u8 = sys_mmap(VEC64_BYTES) 48 let v: *Vec64 = raw as *Vec64 49 v.lane0 = 0; v.lane1 = 0; v.lane2 = 0; v.lane3 = 0 50 v.lane4 = 0; v.lane5 = 0; v.lane6 = 0; v.lane7 = 0 51 return v 52} 53 54// Broadcast: all lanes = scalar. 55func vec64_splat(x: i64) -> *Vec64 { 56 let v: *Vec64 = vec64_zero() 57 v.lane0 = x; v.lane1 = x; v.lane2 = x; v.lane3 = x 58 v.lane4 = x; v.lane5 = x; v.lane6 = x; v.lane7 = x 59 return v 60} 61 62// Load from aligned memory. Caller guarantees p is 64-byte aligned. 63// Future: compiles to `vld1q_u64` / `vmovdqa64` / RVV `vle64.v`. 64func vec64_load(p: *i64) -> *Vec64 { 65 let v: *Vec64 = vec64_zero() 66 v.lane0 = p[0]; v.lane1 = p[1]; v.lane2 = p[2]; v.lane3 = p[3] 67 v.lane4 = p[4]; v.lane5 = p[5]; v.lane6 = p[6]; v.lane7 = p[7] 68 return v 69} 70 71// Store to aligned memory. Future: single `vmovdqa64` / `vst1q_u64`. 72func vec64_store(v: *Vec64, p: *i64) -> i64 { 73 p[0] = v.lane0; p[1] = v.lane1; p[2] = v.lane2; p[3] = v.lane3 74 p[4] = v.lane4; p[5] = v.lane5; p[6] = v.lane6; p[7] = v.lane7 75 return 0 76} 77 78// ===== arithmetic ==================================================== 79 80// Lane-wise add. a[i] + b[i]. Future: `vpaddq` / `vadd.vv`. 81func vec64_add(a: *Vec64, b: *Vec64) -> *Vec64 { 82 let r: *Vec64 = vec64_zero() 83 r.lane0 = a.lane0 + b.lane0 84 r.lane1 = a.lane1 + b.lane1 85 r.lane2 = a.lane2 + b.lane2 86 r.lane3 = a.lane3 + b.lane3 87 r.lane4 = a.lane4 + b.lane4 88 r.lane5 = a.lane5 + b.lane5 89 r.lane6 = a.lane6 + b.lane6 90 r.lane7 = a.lane7 + b.lane7 91 return r 92} 93 94// Lane-wise subtract. 95func vec64_sub(a: *Vec64, b: *Vec64) -> *Vec64 { 96 let r: *Vec64 = vec64_zero() 97 r.lane0 = a.lane0 - b.lane0 98 r.lane1 = a.lane1 - b.lane1 99 r.lane2 = a.lane2 - b.lane2 100 r.lane3 = a.lane3 - b.lane3 101 r.lane4 = a.lane4 - b.lane4 102 r.lane5 = a.lane5 - b.lane5 103 r.lane6 = a.lane6 - b.lane6 104 r.lane7 = a.lane7 - b.lane7 105 return r 106} 107 108// Lane-wise 64-bit multiply (low result). 109func vec64_mul(a: *Vec64, b: *Vec64) -> *Vec64 { 110 let r: *Vec64 = vec64_zero() 111 r.lane0 = a.lane0 * b.lane0 112 r.lane1 = a.lane1 * b.lane1 113 r.lane2 = a.lane2 * b.lane2 114 r.lane3 = a.lane3 * b.lane3 115 r.lane4 = a.lane4 * b.lane4 116 r.lane5 = a.lane5 * b.lane5 117 r.lane6 = a.lane6 * b.lane6 118 r.lane7 = a.lane7 * b.lane7 119 return r 120} 121 122// ===== bitwise ======================================================= 123 124func vec64_and(a: *Vec64, b: *Vec64) -> *Vec64 { 125 let r: *Vec64 = vec64_zero() 126 r.lane0 = a.lane0 & b.lane0 127 r.lane1 = a.lane1 & b.lane1 128 r.lane2 = a.lane2 & b.lane2 129 r.lane3 = a.lane3 & b.lane3 130 r.lane4 = a.lane4 & b.lane4 131 r.lane5 = a.lane5 & b.lane5 132 r.lane6 = a.lane6 & b.lane6 133 r.lane7 = a.lane7 & b.lane7 134 return r 135} 136 137func vec64_or(a: *Vec64, b: *Vec64) -> *Vec64 { 138 let r: *Vec64 = vec64_zero() 139 r.lane0 = a.lane0 | b.lane0 140 r.lane1 = a.lane1 | b.lane1 141 r.lane2 = a.lane2 | b.lane2 142 r.lane3 = a.lane3 | b.lane3 143 r.lane4 = a.lane4 | b.lane4 144 r.lane5 = a.lane5 | b.lane5 145 r.lane6 = a.lane6 | b.lane6 146 r.lane7 = a.lane7 | b.lane7 147 return r 148} 149 150func vec64_xor(a: *Vec64, b: *Vec64) -> *Vec64 { 151 let r: *Vec64 = vec64_zero() 152 r.lane0 = a.lane0 ^ b.lane0 153 r.lane1 = a.lane1 ^ b.lane1 154 r.lane2 = a.lane2 ^ b.lane2 155 r.lane3 = a.lane3 ^ b.lane3 156 r.lane4 = a.lane4 ^ b.lane4 157 r.lane5 = a.lane5 ^ b.lane5 158 r.lane6 = a.lane6 ^ b.lane6 159 r.lane7 = a.lane7 ^ b.lane7 160 return r 161} 162 163// Shift left by constant count (uniform shift across lanes). 164func vec64_shl(v: *Vec64, n: i64) -> *Vec64 { 165 let r: *Vec64 = vec64_zero() 166 r.lane0 = v.lane0 << n 167 r.lane1 = v.lane1 << n 168 r.lane2 = v.lane2 << n 169 r.lane3 = v.lane3 << n 170 r.lane4 = v.lane4 << n 171 r.lane5 = v.lane5 << n 172 r.lane6 = v.lane6 << n 173 r.lane7 = v.lane7 << n 174 return r 175} 176 177// ===== reduction ===================================================== 178 179// Horizontal sum of all lanes. Future: log-fold via vector- 180// reduction intrinsics (`vredsum.vs` etc.). 181func vec64_hsum(v: *Vec64) -> i64 { 182 return v.lane0 + v.lane1 + v.lane2 + v.lane3 + v.lane4 + v.lane5 + v.lane6 + v.lane7 183} 184 185// Compile-only smoke. 186func main() -> i64 { 187 // Build [1..8] vs [10..17], verify sums + XOR. 188 let a: *Vec64 = vec64_zero() 189 a.lane0 = 1; a.lane1 = 2; a.lane2 = 3; a.lane3 = 4 190 a.lane4 = 5; a.lane5 = 6; a.lane6 = 7; a.lane7 = 8 191 192 let b: *Vec64 = vec64_splat(10) 193 194 let c: *Vec64 = vec64_add(a, b) 195 if c.lane0 != 11 { return 1 } 196 if c.lane7 != 18 { return 2 } 197 if vec64_hsum(c) != 116 { return 3 } // 11+12+...+18 = 116 198 199 let d: *Vec64 = vec64_xor(a, a) 200 if vec64_hsum(d) != 0 { return 4 } 201 202 return 0 203}