code wiki / (root) / nx_nofloat_llm.nx

nx_nofloat_llm.nx source

↩ module page · 1835 lines · 107654 B

1// nx_nofloat_llm.nx -- CANONICAL sovereign no-float LLM library (ONE source of truth; no copy-paste debt). 2// 3// Every integer-Q16 building block for running a real GGUF transformer (Qwen2.5) in deterministic no-float: 4// - dequant of every quant the model uses (F32 / Q5_0 / Q8_0 / Q4_K / Q6_K) -> Q16 5// - by-name tensor load + window-dequant (for embeddings / LM-head rows) 6// - fixed-point transcendentals (exp, sigmoid, SiLU, sin, cos) 7// - the ONE matmul: full-precision accumulate-then-shift (the correct, non-underflowing version) 8// - RMSNorm, RoPE, attention sublayer, FFN sublayer, the lazy N-layer stack 9// 10// Gates IMPORT this instead of re-defining (CLAUDE.md #15 DRY). One canonical, correct implementation: 11// a precision/convention fix is made HERE, once, not in 25 copies. No `main` (pure library). 12// No hw writes (Rule 26). license_tier: ORIGINAL 13import "nx_syscalls.nx" 14import "nx_tier.nx" 15import "nx_le.nx" 16import "nx_tensor.nx" 17import "nx_gguf.nx" 18import "nx_gguf_load.nx" 19import "nx_thread_pool.nx" // decode-speed: banded bit-exact parallel kernels (2026-07-09) 20import "nx_vecmath.nx" 21 22const Q16: i64 = 65536 23const LOG2E: i64 = 94548 24const PC0: i64 = 65536 25const PC1: i64 = 45426 26const PC2: i64 = 15743 27const PC3: i64 = 4367 28const LN_BASE_Q16: i64 = 905421 // ln(1e6), Qwen2.5 rope base 29const HALF_PI: i64 = 102944 30const PI: i64 = 205887 31const THREE_HALF_PI: i64 = 308831 32const TWO_PI: i64 = 411775 33 34func qmul(a: i64, b: i64) -> i64 { return (a*b) >> 16 } 35func cpy(d: *i64, s: *i64, n: i64) -> i64 { var i: i64=0; while i<n { d[i]=s[i]; i=i+1 } return 0 } 36func q_i8(buf: *u8, off: i64) -> i64 { let v: i64=nx_le_read_u8(buf, off); if v>=128 { return v-256 } return v } 37 38// ---- scalar dtype decode -> Q16 (pure integer; no float touches the weights) ---- 39func f32_to_q16(bits: i64) -> i64 { let s: i64=(bits>>31)&1; let e: i64=(bits>>23)&255; let mant: i64=bits&8388607; var v: i64=0; if e==0 { v=0 } else { if e==255 { v=2147483647 } else { let m: i64=8388608+mant; let ee: i64=e-134; if ee>=0 { v=m<<ee } else { v=m>>(0-ee) } } } if s==1 { v=0-v } return v } 40func f16_to_q16(h: i64) -> i64 { let s: i64=(h>>15)&1; let e: i64=(h>>10)&31; let mant: i64=h&1023; var v: i64=0; if e==0 { v=mant>>8 } else { if e==31 { v=2147483647 } else { let m: i64=1024+mant; let ee: i64=e-9; if ee>=0 { v=m<<ee } else { v=m>>(0-ee) } } } if s==1 { v=0-v } return v } 41 42// ---- block dequants (one super-block / block each) -> Q16 ---- 43func q5_0_block(buf: *u8, off: i64, n: i64, out: *i64) -> i64 { let d: i64=f16_to_q16(nx_le_read_u16(buf, off)); let qh: i64=nx_le_read_u32(buf, off+2); let qo: i64=off+6; var j: i64=0; while j<16 { let qs: i64=nx_le_read_u8(buf, qo+j); let xh0: i64=((qh>>j)<<4)&16; let xh1: i64=(qh>>(j+12))&16; if j<n { out[j]=d*(((qs&15)|xh0)-16) } if j+16<n { out[j+16]=d*(((qs>>4)|xh1)-16) } j=j+1 } return 0 } 44func q8_0_block(buf: *u8, off: i64, n: i64, out: *i64) -> i64 { let d: i64=f16_to_q16(nx_le_read_u16(buf, off)); var j: i64=0; while j<32 { if j<n { out[j]=d*q_i8(buf, off+2+j) } j=j+1 } return 0 } 45// ⚠ Q6_K super-scale d is ~1e-5 on real trained weights -- f16_to_q16 (16 frac bits) ROUNDS IT TO 0, collapsing whole 46// super-blocks to zero (measured: ffn_down 84/896 rows survived -> the FFN down-proj died -> garbage predictions). 47// FIX 2026-07-09: decode d in Q24 (exact for every finite f16, like the Q4_K fix), compute d24*sc*(q-32) in Q24, >>8 to Q16. 48func q6k_block(buf: *u8, so: i64, n: i64, out: *i64) -> i64 { 49 let d24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so+208)); var half: i64=0 50 while half<2 { let bql: i64=so+half*64; let bqh: i64=so+128+half*32; let bsc: i64=so+192+half*8; let yb: i64=half*128; var l: i64=0 51 while l<32 { let is: i64=l/16; let ql: i64=nx_le_read_u8(buf, bql+l); let ql3: i64=nx_le_read_u8(buf, bql+l+32); let qh: i64=nx_le_read_u8(buf, bqh+l) 52 if yb+l+0<n { out[yb+l+0]=(d24*q_i8(buf,bsc+is+0)*(((ql&15)|(((qh>>0)&3)<<4))-32))>>8 } 53 if yb+l+32<n { out[yb+l+32]=(d24*q_i8(buf,bsc+is+2)*(((ql3&15)|(((qh>>2)&3)<<4))-32))>>8 } 54 if yb+l+64<n { out[yb+l+64]=(d24*q_i8(buf,bsc+is+4)*(((ql>>4)|(((qh>>4)&3)<<4))-32))>>8 } 55 if yb+l+96<n { out[yb+l+96]=(d24*q_i8(buf,bsc+is+6)*(((ql3>>4)|(((qh>>6)&3)<<4))-32))>>8 } 56 l=l+1 } 57 half=half+1 } 58 return 0 59} 60// ---- DEBT-EATEN 2026-07-15 ("eat our debt, we want state of the art"): the FULL mainstream ggml quant 61// family. Census (nx_gguf_type_census_gate) says the fleet ships F32/Q5_0/Q8_0/Q4_K/Q6_K today -- but any 62// new pack can carry Q5_K/Q3_K/Q2_K/Q4_0/Q4_1/Q5_1/F16/BF16, and the old dispatch either SILENTLY zeroed 63// (dequant_to_q16) or WRONG-DECODED as Q4_K (dequant_row's else-branch). Now: every mainstream type 64// decodes (KAT-gated, nx_gguf_dequant_kat_gate), everything else fails LOUD (-1). IQ-family (i-quants) 65// remains NAMED-unsupported (codebook arc of its own). ---- 66// Q4_0: 18B/32 = d(f16) + 16 nibble bytes; y = d*(q-8). 67func q4_0_block(buf: *u8, off: i64, n: i64, out: *i64) -> i64 { let d: i64=f16_to_q16(nx_le_read_u16(buf, off)); let qo: i64=off+2; var j: i64=0; while j<16 { let qs: i64=nx_le_read_u8(buf, qo+j); if j<n { out[j]=d*((qs&15)-8) } if j+16<n { out[j+16]=d*((qs>>4)-8) } j=j+1 } return 0 } 68// Q4_1: 20B/32 = d(f16) + m(f16) + 16 nibble bytes; y = d*q + m. 69func q4_1_block(buf: *u8, off: i64, n: i64, out: *i64) -> i64 { let d: i64=f16_to_q16(nx_le_read_u16(buf, off)); let m: i64=f16_to_q16(nx_le_read_u16(buf, off+2)); let qo: i64=off+4; var j: i64=0; while j<16 { let qs: i64=nx_le_read_u8(buf, qo+j); if j<n { out[j]=d*(qs&15)+m } if j+16<n { out[j+16]=d*(qs>>4)+m } j=j+1 } return 0 } 70// Q5_1: 24B/32 = d(f16) + m(f16) + qh(u32) + 16 nibble bytes; y = d*q5 + m (5th bit from qh). 71func q5_1_block(buf: *u8, off: i64, n: i64, out: *i64) -> i64 { let d: i64=f16_to_q16(nx_le_read_u16(buf, off)); let m: i64=f16_to_q16(nx_le_read_u16(buf, off+2)); let qh: i64=nx_le_read_u32(buf, off+4); let qo: i64=off+8; var j: i64=0; while j<16 { let qs: i64=nx_le_read_u8(buf, qo+j); let xh0: i64=((qh>>j)<<4)&16; let xh1: i64=(qh>>(j+12))&16; if j<n { out[j]=d*((qs&15)|xh0)+m } if j+16<n { out[j+16]=d*((qs>>4)|xh1)+m } j=j+1 } return 0 } 72// Q4_K/Q5_K shared 6-bit scale/min pair unpack (the proven nx_gguf_load idiom): box[0]=sc box[1]=m. 73func qk_scm(buf: *u8, sco: i64, is: i64, box: *i64) -> i64 { 74 if is < 4 { 75 box[0] = nx_le_read_u8(buf, sco+is) & 63 76 box[1] = nx_le_read_u8(buf, sco+is+4) & 63 77 } else { 78 let k: i64 = is - 4 79 let bk: i64 = nx_le_read_u8(buf, sco+k) 80 let bk4: i64 = nx_le_read_u8(buf, sco+4+k) 81 let b8k: i64 = nx_le_read_u8(buf, sco+8+k) 82 box[0] = ((bk >> 6) << 4) | (b8k & 15) 83 box[1] = ((bk4 >> 6) << 4) | (b8k >> 4) 84 } 85 return 0 86} 87// Q5_K: 176B/256 = d(f16) dmin(f16) scales[12] qh[32] qs[128]; y = d*sc*((q4|qh<<4)) - dmin*m. Q24 math -> >>8. 88func q5k_block(buf: *u8, so: i64, n: i64, out: *i64) -> i64 { 89 let d24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so)) 90 let dm24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so+2)) 91 let sco: i64=so+4 92 let qho: i64=so+16 93 let qlo: i64=so+48 94 let box: *i64 = sys_mmap(16) as *i64 95 var j: i64=0 96 while j<4 { 97 qk_scm(buf, sco, 2*j, box) 98 let sc0: i64=box[0] 99 let m0: i64=box[1] 100 qk_scm(buf, sco, 2*j+1, box) 101 let sc1: i64=box[0] 102 let m1: i64=box[1] 103 let u1: i64=1<<(2*j) 104 let u2: i64=2<<(2*j) 105 var l: i64=0 106 while l<32 { 107 let ql: i64=nx_le_read_u8(buf, qlo+j*32+l) 108 let qh: i64=nx_le_read_u8(buf, qho+l) 109 var lo: i64=ql&15 110 if (qh&u1)!=0 { lo=lo+16 } 111 var hi: i64=ql>>4 112 if (qh&u2)!=0 { hi=hi+16 } 113 if j*64+l<n { out[j*64+l]=((d24*sc0*lo)-(dm24*m0))>>8 } 114 if j*64+32+l<n { out[j*64+32+l]=((d24*sc1*hi)-(dm24*m1))>>8 } 115 l=l+1 116 } 117 j=j+1 118 } 119 return 0 120} 121// Q3_K 16 x 6-bit scale (RAW 0..63; caller subtracts 32) from the 12 packed bytes at sco. 122func q3k_sc(buf: *u8, sco: i64, is: i64) -> i64 { 123 let k: i64 = is & 3 124 var lo: i64 = 0 125 var hb: i64 = 0 126 if is < 4 { lo = nx_le_read_u8(buf, sco+k) & 15; hb = nx_le_read_u8(buf, sco+8+k) & 3 } else { 127 if is < 8 { lo = nx_le_read_u8(buf, sco+4+k) & 15; hb = (nx_le_read_u8(buf, sco+8+k) >> 2) & 3 } else { 128 if is < 12 { lo = nx_le_read_u8(buf, sco+k) >> 4; hb = (nx_le_read_u8(buf, sco+8+k) >> 4) & 3 } else { 129 lo = nx_le_read_u8(buf, sco+4+k) >> 4 130 hb = (nx_le_read_u8(buf, sco+8+k) >> 6) & 3 131 } 132 } 133 } 134 return lo | (hb << 4) 135} 136// Q3_K: 110B/256 = hmask[32] qs[64] scales[12] d(f16); y = d*(sc-32)*(q3 - (hbit?0:4)). Q24 -> >>8. 137func q3k_block(buf: *u8, so: i64, n: i64, out: *i64) -> i64 { 138 let d24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so+108)) 139 let sco: i64=so+96 140 var yi: i64=0 141 var is: i64=0 142 var mbit: i64=1 143 var half: i64=0 144 while half<2 { 145 let qb: i64=so+32+half*32 146 var shift: i64=0 147 var jj: i64=0 148 while jj<4 { 149 let dl0: i64=d24*(q3k_sc(buf, sco, is)-32) 150 is=is+1 151 var l: i64=0 152 while l<16 { 153 let qv: i64=(nx_le_read_u8(buf, qb+l)>>shift)&3 154 var t: i64=qv 155 if (nx_le_read_u8(buf, so+l)&mbit)==0 { t=t-4 } 156 if yi<n { out[yi]=(dl0*t)>>8 } 157 yi=yi+1 158 l=l+1 159 } 160 let dl1: i64=d24*(q3k_sc(buf, sco, is)-32) 161 is=is+1 162 l=0 163 while l<16 { 164 let qv2: i64=(nx_le_read_u8(buf, qb+16+l)>>shift)&3 165 var t2: i64=qv2 166 if (nx_le_read_u8(buf, so+16+l)&mbit)==0 { t2=t2-4 } 167 if yi<n { out[yi]=(dl1*t2)>>8 } 168 yi=yi+1 169 l=l+1 170 } 171 shift=shift+2 172 mbit=mbit<<1 173 jj=jj+1 174 } 175 half=half+1 176 } 177 return 0 178} 179// Q2_K: 84B/256 = scales[16] (lo4=sc hi4=m) qs[64] d(f16) dmin(f16); y = d*sc*q2 - dmin*m. Q24 -> >>8. 180func q2k_block(buf: *u8, so: i64, n: i64, out: *i64) -> i64 { 181 let d24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so+80)) 182 let dm24: i64=_gguf_f16_to_q24(nx_le_read_u16(buf, so+82)) 183 var yi: i64=0 184 var is: i64=0 185 var half: i64=0 186 while half<2 { 187 let qb: i64=so+16+half*32 188 var shift: i64=0 189 var jj: i64=0 190 while jj<4 { 191 let sb0: i64=nx_le_read_u8(buf, so+is) 192 is=is+1 193 let dl0: i64=d24*(sb0&15) 194 let ml0: i64=dm24*(sb0>>4) 195 var l: i64=0 196 while l<16 { 197 let qv: i64=(nx_le_read_u8(buf, qb+l)>>shift)&3 198 if yi<n { out[yi]=((dl0*qv)-ml0)>>8 } 199 yi=yi+1 200 l=l+1 201 } 202 let sb1: i64=nx_le_read_u8(buf, so+is) 203 is=is+1 204 let dl1: i64=d24*(sb1&15) 205 let ml1: i64=dm24*(sb1>>4) 206 l=0 207 while l<16 { 208 let qv2: i64=(nx_le_read_u8(buf, qb+16+l)>>shift)&3 209 if yi<n { out[yi]=((dl1*qv2)-ml1)>>8 } 210 yi=yi+1 211 l=l+1 212 } 213 shift=shift+2 214 jj=jj+1 215 } 216 half=half+1 217 } 218 return 0 219} 220// canonical type stride: vb[0]=values/block vb[1]=bytes/block. 0 ok, -1 = NOT A SUPPORTED TYPE (the 221// serve's fail-fast init scans every tensor through this -- unknown types refuse to serve, never zero). 222func nf_type_stride(ty: i64, vb: *i64) -> i64 { 223 if ty==NX_GGML_TYPE_F32 { vb[0]=1; vb[1]=4; return 0 } 224 if ty==NX_GGML_TYPE_F16 { vb[0]=1; vb[1]=2; return 0 } 225 if ty==NX_GGML_TYPE_BF16 { vb[0]=1; vb[1]=2; return 0 } 226 if ty==NX_GGML_TYPE_Q4_0 { vb[0]=32; vb[1]=18; return 0 } 227 if ty==NX_GGML_TYPE_Q4_1 { vb[0]=32; vb[1]=20; return 0 } 228 if ty==NX_GGML_TYPE_Q5_0 { vb[0]=32; vb[1]=22; return 0 } 229 if ty==NX_GGML_TYPE_Q5_1 { vb[0]=32; vb[1]=24; return 0 } 230 if ty==NX_GGML_TYPE_Q8_0 { vb[0]=32; vb[1]=34; return 0 } 231 if ty==NX_GGML_TYPE_Q2_K { vb[0]=256; vb[1]=84; return 0 } 232 if ty==NX_GGML_TYPE_Q3_K { vb[0]=256; vb[1]=110; return 0 } 233 if ty==NX_GGML_TYPE_Q4_K { vb[0]=256; vb[1]=144; return 0 } 234 if ty==NX_GGML_TYPE_Q5_K { vb[0]=256; vb[1]=176; return 0 } 235 if ty==NX_GGML_TYPE_Q6_K { vb[0]=256; vb[1]=210; return 0 } 236 return 0 - 1 237} 238// byte offset of value index `voff` inside a quantized blob (canonical stride; MoE expert slices ride this). 239// voff MUST be block-aligned (expert slices are: ff*D is a 256/32 multiple). -1 on unsupported type. 240func nf_expert_byteoff(ty: i64, voff: i64) -> i64 { 241 let vb: *i64 = sys_mmap(16) as *i64 242 if nf_type_stride(ty, vb) != 0 { return 0 - 1 } 243 return (voff / vb[0]) * vb[1] 244} 245// dequant n contiguous values of a tensor (any supported quant) starting at byte `base` -> Q16. -1 if unsupported. 246func dequant_to_q16(buf: *u8, base: i64, gt: i64, n: i64, out: *i64) -> i64 { 247 if gt==NX_GGML_TYPE_F32 { var i: i64=0; while i<n { out[i]=f32_to_q16(nx_le_read_u32(buf, base+i*4)); i=i+1 } return 0 } 248 if gt==NX_GGML_TYPE_F16 { var i: i64=0; while i<n { out[i]=f16_to_q16(nx_le_read_u16(buf, base+i*2)); i=i+1 } return 0 } 249 if gt==NX_GGML_TYPE_BF16 { var i: i64=0; while i<n { out[i]=f32_to_q16(nx_le_read_u16(buf, base+i*2)<<16); i=i+1 } return 0 } 250 if gt==NX_GGML_TYPE_Q4_K { nx_gguf_dequant_q4_k_q14(buf, base, n, out); var i: i64=0; while i<n { out[i]=out[i]/256; i=i+1 } return 0 } // Q4_K dequant now emits Q24 -> Q16 (/256); was Q14<<2 251 if gt==NX_GGML_TYPE_Q6_K { var sb: i64=0; while sb*256<n { var tk: i64=n-sb*256; if tk>256 { tk=256 } q6k_block(buf, base+sb*210, tk, ((out as i64)+sb*256*8) as *i64); sb=sb+1 } return 0 } 252 if gt==NX_GGML_TYPE_Q5_K { var sb: i64=0; while sb*256<n { var tk: i64=n-sb*256; if tk>256 { tk=256 } q5k_block(buf, base+sb*176, tk, ((out as i64)+sb*256*8) as *i64); sb=sb+1 } return 0 } 253 if gt==NX_GGML_TYPE_Q3_K { var sb: i64=0; while sb*256<n { var tk: i64=n-sb*256; if tk>256 { tk=256 } q3k_block(buf, base+sb*110, tk, ((out as i64)+sb*256*8) as *i64); sb=sb+1 } return 0 } 254 if gt==NX_GGML_TYPE_Q2_K { var sb: i64=0; while sb*256<n { var tk: i64=n-sb*256; if tk>256 { tk=256 } q2k_block(buf, base+sb*84, tk, ((out as i64)+sb*256*8) as *i64); sb=sb+1 } return 0 } 255 if gt==NX_GGML_TYPE_Q5_0 { var sb: i64=0; while sb*32<n { var tk: i64=n-sb*32; if tk>32 { tk=32 } q5_0_block(buf, base+sb*22, tk, ((out as i64)+sb*32*8) as *i64); sb=sb+1 } return 0 } 256 if gt==NX_GGML_TYPE_Q5_1 { var sb: i64=0; while sb*32<n { var tk: i64=n-sb*32; if tk>32 { tk=32 } q5_1_block(buf, base+sb*24, tk, ((out as i64)+sb*32*8) as *i64); sb=sb+1 } return 0 } 257 if gt==NX_GGML_TYPE_Q4_0 { var sb: i64=0; while sb*32<n { var tk: i64=n-sb*32; if tk>32 { tk=32 } q4_0_block(buf, base+sb*18, tk, ((out as i64)+sb*32*8) as *i64); sb=sb+1 } return 0 } 258 if gt==NX_GGML_TYPE_Q4_1 { var sb: i64=0; while sb*32<n { var tk: i64=n-sb*32; if tk>32 { tk=32 } q4_1_block(buf, base+sb*20, tk, ((out as i64)+sb*32*8) as *i64); sb=sb+1 } return 0 } 259 if gt==NX_GGML_TYPE_Q8_0 { var sb: i64=0; while sb*32<n { var tk: i64=n-sb*32; if tk>32 { tk=32 } q8_0_block(buf, base+sb*34, tk, ((out as i64)+sb*32*8) as *i64); sb=sb+1 } return 0 } 260 return 0 - 1 261} 262// find tensor by name + dequant up to `want` values -> out (Q16). #values, -1 not-found, -2 unsupported. 263func load_named_q16(buf: *u8, hdr: *NxGgufHeader, name: *u8, nlen: i64, out: *i64, want: i64) -> i64 { 264 let idx: nx_int = nx_gguf_find_tensor(hdr, name, nlen) 265 if idx < 0 { return 0 - 1 } 266 let ti: *NxGgufTensorInfo = nx_gguf_tensor_at(hdr, idx) 267 let nv: i64 = nx_gguf_tensor_n_values(ti) 268 var n: i64=want; if nv<n { n=nv } 269 if dequant_to_q16(buf, hdr.data_off + ti.offset, ti.ggml_type, n, out) < 0 { return 0 - 2 } 270 return n 271} 272// window-dequant row v (ne values at v*ne) of a [ne, rows] tensor (for embeddings + LM-head rows). 273// DEBT-EATEN 2026-07-15: the old 256-block tail was Q6_K-else-ASSUME-Q4_K -- any other k-quant WRONG-DECODED 274// silently. Now every mainstream type has an explicit row; anything else returns -1 (LOUD). 275func dequant_row(buf: *u8, base: i64, gt: i64, v: i64, ne: i64, out: *i64, tmp: *i64) -> i64 { 276 let vstart: i64=v*ne 277 if gt==NX_GGML_TYPE_F32 { var k: i64=0; while k<ne { out[k]=f32_to_q16(nx_le_read_u32(buf, base+(vstart+k)*4)); k=k+1 } return 0 } 278 if gt==NX_GGML_TYPE_F16 { var k: i64=0; while k<ne { out[k]=f16_to_q16(nx_le_read_u16(buf, base+(vstart+k)*2)); k=k+1 } return 0 } 279 if gt==NX_GGML_TYPE_BF16 { var k: i64=0; while k<ne { out[k]=f32_to_q16(nx_le_read_u16(buf, base+(vstart+k)*2)<<16); k=k+1 } return 0 } 280 if gt==NX_GGML_TYPE_Q8_0 { let fb: i64=vstart/32; let oi: i64=vstart-fb*32; let nb: i64=((vstart+ne-1)/32)-fb+1; var b: i64=0; while b<nb { q8_0_block(buf, base+(fb+b)*34, 32, ((tmp as i64)+b*32*8) as *i64); b=b+1 } var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } return 0 } 281 if gt==NX_GGML_TYPE_Q5_0 { let fb: i64=vstart/32; let oi: i64=vstart-fb*32; let nb: i64=((vstart+ne-1)/32)-fb+1; var b: i64=0; while b<nb { q5_0_block(buf, base+(fb+b)*22, 32, ((tmp as i64)+b*32*8) as *i64); b=b+1 } var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } return 0 } 282 if gt==NX_GGML_TYPE_Q5_1 { let fb: i64=vstart/32; let oi: i64=vstart-fb*32; let nb: i64=((vstart+ne-1)/32)-fb+1; var b: i64=0; while b<nb { q5_1_block(buf, base+(fb+b)*24, 32, ((tmp as i64)+b*32*8) as *i64); b=b+1 } var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } return 0 } 283 if gt==NX_GGML_TYPE_Q4_0 { let fb: i64=vstart/32; let oi: i64=vstart-fb*32; let nb: i64=((vstart+ne-1)/32)-fb+1; var b: i64=0; while b<nb { q4_0_block(buf, base+(fb+b)*18, 32, ((tmp as i64)+b*32*8) as *i64); b=b+1 } var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } return 0 } 284 if gt==NX_GGML_TYPE_Q4_1 { let fb: i64=vstart/32; let oi: i64=vstart-fb*32; let nb: i64=((vstart+ne-1)/32)-fb+1; var b: i64=0; while b<nb { q4_1_block(buf, base+(fb+b)*20, 32, ((tmp as i64)+b*32*8) as *i64); b=b+1 } var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } return 0 } 285 let fb: i64=vstart/256; let oi: i64=vstart-fb*256; let nb: i64=((vstart+ne-1)/256)-fb+1 286 var okb: i64=0 287 if gt==NX_GGML_TYPE_Q6_K { var b: i64=0; while b<nb { q6k_block(buf, base+(fb+b)*210, 256, ((tmp as i64)+b*256*8) as *i64); b=b+1 } okb=1 } 288 if gt==NX_GGML_TYPE_Q5_K { var b: i64=0; while b<nb { q5k_block(buf, base+(fb+b)*176, 256, ((tmp as i64)+b*256*8) as *i64); b=b+1 } okb=1 } 289 if gt==NX_GGML_TYPE_Q3_K { var b: i64=0; while b<nb { q3k_block(buf, base+(fb+b)*110, 256, ((tmp as i64)+b*256*8) as *i64); b=b+1 } okb=1 } 290 if gt==NX_GGML_TYPE_Q2_K { var b: i64=0; while b<nb { q2k_block(buf, base+(fb+b)*84, 256, ((tmp as i64)+b*256*8) as *i64); b=b+1 } okb=1 } 291 if gt==NX_GGML_TYPE_Q4_K { nx_gguf_dequant_q4_k_q14(buf, base+fb*144, nb*256, tmp); var z: i64=0; while z<nb*256 { tmp[z]=tmp[z]/256; z=z+1 } okb=1 } // Q24 -> Q16 (/256) 292 if okb==0 { return 0 - 1 } 293 var k: i64=0; while k<ne { out[k]=tmp[oi+k]; k=k+1 } 294 return 0 295} 296// build "blk.<L><suffix>" into out, return length; load that tensor. 297func build_name(out: *u8, L: i64, suffix: *u8) -> i64 { 298 out[0]=98 as u8; out[1]=108 as u8; out[2]=107 as u8; out[3]=46 as u8 299 var p: i64=4 300 if L==0 { out[p]=48 as u8; p=p+1 } else { let tmp: *u8=sys_mmap(16); var k: i64=0; var m: i64=L; while m>0 { tmp[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var i: i64=k-1; while i>=0 { out[p]=tmp[i]; p=p+1; i=i-1 } } 301 var j: i64=0; while suffix[j]!=(0 as u8) { out[p]=suffix[j]; p=p+1; j=j+1 } 302 out[p]=0 as u8 303 return p 304} 305func load_blk(buf: *u8, hdr: *NxGgufHeader, nm: *u8, L: i64, suffix: *u8, out: *i64, want: i64) -> i64 { let len: i64=build_name(nm, L, suffix); return load_named_q16(buf, hdr, nm, len, out, want) } 306 307// ---- fixed-point math ---- 308func isqrt(v: i64) -> i64 { return vm_isqrt(v) } 309func fx_exp(x: i64) -> i64 { var xm: i64=0-x; if x>0 { xm=0 } let ym: i64=(xm*LOG2E)>>16; let yi: i64=ym>>16; let yf: i64=ym-(yi<<16); let g: i64=Q16-yf; var t: i64=PC3; t=PC2+((g*t)>>16); t=PC1+((g*t)>>16); t=PC0+((g*t)>>16); t=t>>1; if yi>=31 { return 0 } return t>>yi } 310func sigmoid(x: i64) -> i64 { if x>=0 { let ex: i64=fx_exp(0-x); return (Q16*Q16)/(Q16+ex) } let ex: i64=fx_exp(x); let sp: i64=(Q16*Q16)/(Q16+ex); return Q16-sp } 311func silu(x: i64) -> i64 { return qmul(x, sigmoid(x)) } 312func sin_q(x: i64) -> i64 { let x2: i64=qmul(x,x); let x3: i64=qmul(x2,x); let x5: i64=qmul(x3,x2); let x7: i64=qmul(x5,x2); let x9: i64=qmul(x7,x2); return x - x3/6 + x5/120 - x7/5040 + x9/362880 } 313func cos_q(x: i64) -> i64 { let x2: i64=qmul(x,x); let x4: i64=qmul(x2,x2); let x6: i64=qmul(x4,x2); let x8: i64=qmul(x6,x2); return Q16 - x2/2 + x4/24 - x6/720 + x8/40320 } 314func reduce2pi(a: i64) -> i64 { var t: i64=a; while t<0 { t=t+TWO_PI } while t>=TWO_PI { t=t-TWO_PI } return t } 315func sin_full(a: i64) -> i64 { let t: i64=reduce2pi(a); if t<HALF_PI { return sin_q(t) } if t<PI { return sin_q(PI-t) } if t<THREE_HALF_PI { return 0-sin_q(t-PI) } return 0-sin_q(TWO_PI-t) } 316func cos_full(a: i64) -> i64 { let t: i64=reduce2pi(a); if t<HALF_PI { return cos_q(t) } if t<PI { return 0-cos_q(PI-t) } if t<THREE_HALF_PI { return 0-cos_q(t-PI) } return cos_q(TWO_PI-t) } 317// ⚠ NEOX pairing (Qwen2.5 GGUF = llama.cpp LLAMA_ROPE_TYPE_NEOX): rotate the two HALVES (x[i], x[i+half]) with 318// theta_i=freqs[i]=base^(-2i/d). NOT the adjacent (x[2i],x[2i+1]) variant -- that rotates the WRONG pairs at every 319// pos>0 -> coherent-looking token 0 (rope=identity) but incoherent multi-token. Matches nx_f32_rope_apply_cs_neox. (fix 2026-07-09) 320func rope_apply(v: *i64, hd: i64, pos: i64, freqs: *i64) -> i64 { let np: i64=hd/2; var i: i64=0; while i<np { let ang: i64=pos*freqs[i]; let c: i64=cos_full(ang); let s: i64=sin_full(ang); let a: i64=v[i]; let b: i64=v[i+np]; v[i]=qmul(a,c)-qmul(b,s); v[i+np]=qmul(a,s)+qmul(b,c); i=i+1 } return 0 } 321// per-head RoPE base^(-2i/hd) frequencies (Q16) -> freqs[hd/2]. 322func rope_freqs(freqs: *i64, hd: i64) -> i64 { let np: i64=hd/2; var i: i64=0; while i<np { freqs[i]=fx_exp(0-((i*LN_BASE_Q16)/np)); i=i+1 } return 0 } 323// ln(x) in Q16 for a positive integer x (2026-07-15 debt-eaten: makes rope base CONFIGURABLE from model 324// metadata instead of the hardcoded ln(1e6) const). ln(x)=p*ln2 + ln(m), m=x/2^p in [1,2) via the fast 325// atanh series ln(m)=2*(z+z^3/3+z^5/5+z^7/7), z=(m-1)/(m+1). MEASURED: ln(1e6)=905404 (true 905420, ~2e-4 326// rel = the Q16 fixed-point floor; negligible after rope's /np + exp), ln(1e4)=603606, ln(2)=45426, ln(1)=0. 327func fx_ln_int(x: i64) -> i64 { 328 if x < 1 { return 0 } 329 var p: i64 = 0 330 var v: i64 = x 331 while v > 1 { v = v >> 1; p = p + 1 } 332 // m in Q16 = (x << 16) >> p, in [65536, 131072) 333 let m: i64 = (x << 16) >> p 334 let num: i64 = m - Q16 335 let den: i64 = m + Q16 336 let z: i64 = (num << 16) / den 337 let z2: i64 = (z * z) >> 16 338 var term: i64 = z 339 var sum: i64 = z 340 term = (term * z2) >> 16 341 sum = sum + term / 3 342 term = (term * z2) >> 16 343 sum = sum + term / 5 344 term = (term * z2) >> 16 345 sum = sum + term / 7 346 let ln_m: i64 = 2 * sum 347 return p * 45426 + ln_m // 45426 = ln(2) in Q16 348} 349// per-head RoPE frequencies for an ARBITRARY base (base^(-2i/hd) = exp(-(i/np)*ln(base))). Additive: the 350// bare rope_freqs stays the compile-time-1e6 path (identity for Qwen); this one takes ln(base) in Q16 so a 351// model's real rope base (OLMoE 1e4, Qwen 1e6, Llama 5e5...) drives attention correctly. 352func rope_freqs_base(freqs: *i64, hd: i64, ln_base_q16: i64) -> i64 { let np: i64=hd/2; var i: i64=0; while i<np { freqs[i]=fx_exp(0-((i*ln_base_q16)/np)); i=i+1 } return 0 } 353 354// ---- THE matmul: GGUF [out,in], full-precision accumulate then ONE shift (no per-term underflow) ---- 355func mm_out_in(inp: *i64, W: *i64, dst: *i64, T: i64, in_dim: i64, out_dim: i64, rev: i64) -> i64 { 356 var t: i64=0 357 while t<T { var o: i64=0 358 while o<out_dim { var s: i64=0 359 if rev==0 { var k: i64=0; while k<in_dim { s=s+(inp[t*in_dim+k]*W[o*in_dim+k]); k=k+1 } } 360 else { var k: i64=in_dim-1; while k>=0 { s=s+(inp[t*in_dim+k]*W[o*in_dim+k]); k=k-1 } } 361 dst[t*out_dim+o]=s>>16; o=o+1 } 362 t=t+1 } 363 return 0 364} 365func rmsnorm_gamma_row(x: *i64, gamma: *i64, xoff: i64, D: i64, out: *i64, ooff: i64) -> i64 { 366 var ss: i64=0; var i: i64=0; while i<D { let v: i64=x[xoff+i]; ss=ss+((v*v)>>16); i=i+1 } 367 let ms: i64=ss/D; let sd: i64=isqrt((ms+1)<<16); if sd<=0 { return 0 } 368 i=0; while i<D { let nm: i64=(x[xoff+i]<<16)/sd; out[ooff+i]=qmul(nm, gamma[i]); i=i+1 } 369 return 0 370} 371// PRECISION LIFT (2026-07-09): the ~1587 massive activations dominate the RMS, so signal dims normalize to ~1e-3 and 372// keep only ~3 bits in Q16 (then gamma~0.08 halves it) -> logits imprecise. This emits the normed*gamma output in Q24 373// (8 more fractional bits, ~11 bits on the signal). Input x is Q16 (residual); output is Q24. Feed to mm_q24in. 374func rmsnorm_gamma_row_q24(x: *i64, gamma: *i64, xoff: i64, D: i64, out: *i64, ooff: i64) -> i64 { 375 var ss: i64=0; var i: i64=0; while i<D { let v: i64=x[xoff+i]; ss=ss+((v*v)>>16); i=i+1 } 376 let ms: i64=ss/D; let sd: i64=isqrt((ms+1)<<16); if sd<=0 { return 0 } 377 i=0; while i<D { let nm: i64=(x[xoff+i]<<24)/sd; out[ooff+i]=(nm*gamma[i])>>16; i=i+1 } 378 return 0 379} 380// matmul with a Q24 input (rmsnorm_gamma_row_q24 output) x Q16 weights -> Q16 output (>>24). Full-precision accumulate. 381func mm_q24in(inp: *i64, W: *i64, dst: *i64, T: i64, in_dim: i64, out_dim: i64, rev: i64) -> i64 { 382 var t: i64=0 383 while t<T { var o: i64=0 384 while o<out_dim { var s: i64=0 385 if rev==0 { var k: i64=0; while k<in_dim { s=s+(inp[t*in_dim+k]*W[o*in_dim+k]); k=k+1 } } 386 else { var k: i64=in_dim-1; while k>=0 { s=s+(inp[t*in_dim+k]*W[o*in_dim+k]); k=k-1 } } 387 dst[t*out_dim+o]=s>>24; o=o+1 } 388 t=t+1 } 389 return 0 390} 391// ==== DECODE-SPEED: shared thread pool + banded BIT-EXACT parallel kernels (2026-07-09) ===================== 392// Banding is by whole OUTPUT ELEMENT (matmul rows / vocab rows) or whole TENSOR (weight dequant) -> no partial 393// sum ever crosses a band -> results are BIT-IDENTICAL to the serial path (kvgen gate pins the tokens to prove 394// it). Single-submitter: every caller is a main-thread forward. Static POINTERS lazy-mmap'd (BSS-array trap). 395// R0q (2026-09-17): the decode floor was the accumulator's memory round trip -- _nfmm_i8_task feeds ONE int32x8 396// accumulator every 16 weights, so each __i16x16_madd waits on the previous store (measured 232 ms/token on the 1.5B 397// at 635 percent CPU, 13 GB/s streamed of a 40+ GB/s box). Four accumulators strided by 16 lanes give four independent 398// chains; the chunk total is the same integer sum in a different association order, so the result is bit-identical, 399// and each lane now holds a quarter of the products the NF_CHUNK_K bound was derived for. 400const NF_MADD_W: i64 = 16 // lanes one __i16x16_madd consumes 401const NF_R4_STRIDE: i64 = 64 // four madds per step, one per accumulator 402const NF_ACC_BYTES: i64 = 32 // one int32x8 accumulator 403const NF_ACC4_I64: i64 = 16 // four accumulators as i64 slots to zero (128 B) 404// R0r (2026-09-17) BATCHED PREFILL. The serve prefilled a ~500-token BRIGHT prompt token by token, so every prompt token 405// cost a full weight pass (97 ms, the engine's own /gen receipt, the same as a generated token) and prefill was half of a 406// ~98 s query. _nfmm_i8_task_m streams each weight row ONCE and dots it against M activation rows (the loop order 407// nx_nofloat_mm_i8_batch_gate proved amortises the weight read); per (row, output) it runs _nfmm_i8_task's own chunk loop, 408// so every cell is bit-identical to mm_pool_i8 on that row by construction. The M activation rows must stay 409// cache-resident while the weights stream: NF_PREFILL_M x 8960 (the ffn width) x 2 B = 573 KB, inside one core's L2, 410// so the caller cuts a prompt into blocks of NF_PREFILL_M positions. 411const NF_PREFILL_M: i64 = 32 412// R0r-c (2026-09-17): the batched kernel streams each weight row once per NF_PREFILL_M activation rows, so it is no longer 413// DRAM-bound (585 us per row on the decode shape against a 27.5 MB pass that would stream in 0.8 ms once per block) but 414// bound by the accumulator's memory round trip on every madd -- the chain R0q measured as a wash on the single-row path 415// because that path is DRAM-bound. The four-accumulator chunk loop is R0q's, bit-identical by integer associativity. 416// R0r-b (2026-09-17): the four-accumulator arm was a wash because the bound was never the chain, it was the accumulator's memory 417// round trip on EVERY madd (the builtin loads and stores it each call). __i16_dot keeps the int32x8 accumulator in a 418// register for a whole chunk and sums it once; the same integer sum in the same association order per lane, so it is 419// bit-identical to the madd loop (the gate's T9e/T9f). 1 = serve the builtin in the batched kernel, 0 = the madd loop. 420const NF_I8_DOT_DEFAULT: i64 = 1 421// R0s (2026-09-17): the instrument above measured a third of every batched matmul call in the SERIAL quantisation of the 422// activation rows (a division per element), repeated for every projection sharing the input. The rows are now quantised 423// by the pool through the one row routine (nf_quant_row) the serial control also calls, and the engine quantises each 424// layer input once (nf_quant_rows_pool + mm_pool_i8_mq). 0 = pool quantisation (served), 1 = the serial control (the gate's T10). 425const NF_QUANT_SERIAL_DEFAULT: i64 = 0 426const NF_MMARENA_SLOTS: i64 = 32 // ctx slots in g_nf_mmarena (128 B each): the ceiling on bands and on quantisation tasks 427// R0v (2026-09-17): the R0u profile put 68 percent of the batched prefill and about a quarter of every decode token in the 428// attention core -- scalar, one thread, one head after another. The heads are independent, and once a prefill block's K/V 429// rows are in the cache so are its (row, head) pairs under the causal count. 0 = pool attention (served), 1 = serial (the control). 430const NF_ATTN_SERIAL_DEFAULT: i64 = 0 431const NF_I8_R4M_DEFAULT: i64 = 0 // MEASURED 2026-09-17 on the decode shape, 32 rows: single-accumulator 451 us per row, four-accumulator 448 (1006 permil, a wash), so the batched kernel is not bound by the accumulator chain either; the simpler task stays served, the four-accumulator task is the gate's bit-identical control, and the bottleneck is the per-madd instruction count the compiler emits (R0r-b) 432const NF_MMCTX_M: i64 = 10 // band ctx slot: activation row count 433const NF_MMCTX_OUT: i64 = 11 // band ctx slot: out_dim, the dst row stride 434const NF_I8_R4_DEFAULT: i64 = 0 // MEASURED 2026-09-17 on the decode shape (1536 x 8960): incumbent 33.7 GB/s, four-accumulator 27.3 GB/s -- decode is DRAM-bound and the extra accumulator traffic costs 19 percent, so the incumbent stays the served path; the r4 task is kept as the gate's bit-identical control 435static g_nf_pool: *NxThreadPool 436static g_nf_ldarena: *u8 // 12 x 128B: load-task ctx (64B) + private name scratch (64B) 437static g_nf_mmarena: *u8 // 32 x 128B: matmul band ctx 438static g_nf_hdctx: *u8 // 32 x 128B: head band ctx 439static g_nf_hdrow: *u8 // 32 x 8KB : per-band dequant row 440static g_nf_hdtmp: *u8 // 32 x 32KB: per-band dequant window tmp 441static g_nf_hdout: *u8 // 32 x 16B : per-band (best_id, best_logit) 442static g_nf_xi8: *u8 // 4864 x 2 : quantized activation (i16 lanes) for the SIMD W8A8 path 443static g_nf_i8acc: *u8 // 32 x 64B : per-band int32x8 SIMD accumulator 444static g_nf_i8acc4: *u8 // R0q: 32 x 128B : per-band FOUR int32x8 accumulators (k-interleaved, independent chains) 445static g_nf_i8_r4: i64 // R0q: 0 = the single-accumulator incumbent (the served default, NF_I8_R4_DEFAULT), 1 = the four-accumulator task (the gate's bit-identical control arm) 446static g_nf_i8_r4m: i64 // R0r-c: 1 = four accumulators in the BATCHED kernel (served), 0 = the single-accumulator batched task (the gate's control) 447static g_nf_i8_dot: i64 // R0r-b: 1 = the batched kernel runs __i16_dot per chunk (served, NF_I8_DOT_DEFAULT), 0 = the per-madd loop (the gate's control) 448static g_nf_mm_us_quant: i64 // R0r-b profile: microseconds the LAST mm_pool_i8_m spent quantising the activation rows (single-threaded) 449static g_nf_mm_us_pool: i64 // R0r-b profile: microseconds the LAST mm_pool_i8_m spent in the pool (submit .. wait) 450static g_nf_quant_serial: i64 // R0s: 1 = quantise the rows serially (the gate's control), 0 = pool tasks (served) 451// R0s prefill phase profile, microseconds accumulated over the LAST nf_prefill_batched call (reset at entry) 452static g_nf_pf_us_norm: i64 453static g_nf_pf_us_qkv: i64 454static g_nf_pf_us_attn: i64 455static g_nf_pf_us_o: i64 456static g_nf_pf_us_gu: i64 457static g_nf_pf_us_silu: i64 458static g_nf_pf_us_down: i64 459static g_nf_pf_us_quant: i64 460static g_nf_attn_serial: i64 // R0v: 1 = serial attention (the control), 0 = pool tasks (served) 461static g_nf_attn_sc: *i64 // R0v: per-task attention scratch, NF_MMARENA_SLOTS x 2 x positions i64, grown on demand 462static g_nf_attn_sc_cap: i64 463static g_nf_xi8m: *u8 // R0r: the batched kernel's quantised activation rows (grown to the largest M x in_dim seen, never per call) 464static g_nf_xi8m_cap: i64 465static g_nf_sxm: *i64 // R0r: per-row activation scales for the batched kernel 466static g_nf_sxm_cap: i64 467func nf_pool() -> *NxThreadPool { 468 if (g_nf_pool as i64) == 0 { 469 g_nf_pool = nx_pool_new(0, 64) 470 g_nf_ldarena = sys_mmap(12*128) 471 g_nf_mmarena = sys_mmap(NF_MMARENA_SLOTS*128) 472 g_nf_hdctx = sys_mmap(32*128) 473 g_nf_hdrow = sys_mmap(32*8192) 474 g_nf_hdtmp = sys_mmap(32*32768) 475 g_nf_hdout = sys_mmap(32*16) 476 g_nf_xi8 = sys_mmap(4864*2) 477 g_nf_i8acc = sys_mmap(32*64) 478 g_nf_i8acc4 = sys_mmap(32*128) 479 g_nf_i8_r4 = NF_I8_R4_DEFAULT 480 g_nf_i8_r4m = NF_I8_R4M_DEFAULT 481 g_nf_i8_dot = NF_I8_DOT_DEFAULT 482 g_nf_quant_serial = NF_QUANT_SERIAL_DEFAULT 483 g_nf_attn_serial = NF_ATTN_SERIAL_DEFAULT 484 g_nf_xi8m_cap = 0 485 g_nf_sxm_cap = 0 486 } 487 return g_nf_pool 488} 489// int32x8 horizontal sum for the SIMD path. Since the 2026-07-10 sext compiler fix, *i32 loads SIGN-extend 490// natively (the manual branchless sign-extend this fn used to carry was removed in lockstep with the bless -- 491// keeping it would DOUBLE-extend and corrupt already-negative lanes). 492func nf_hsum_sx(acc: *u8) -> i64 { 493 let pp: *i32 = acc as *i32 494 var s: i64=0; var i: i64=0 495 while i<8 { s=s+(pp[i] as i64); i=i+1 } 496 return s 497} 498// one banded-matmul task: rows [lo,hi) of dst = inp x W^T, full accumulate, >>shift. 499func _nfmm_task(ctx_i: i64) -> i64 { 500 let c: *i64 = ctx_i as *i64 501 let inp: *i64=c[0] as *i64; let W: *i64=c[1] as *i64; let dst: *i64=c[2] as *i64 502 let T: i64=c[3]; let ind: i64=c[4]; let outd: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8] 503 var t: i64=0 504 while t<T { var o: i64=lo 505 while o<hi { var s: i64=0; var k: i64=0; while k<ind { s=s+(inp[t*ind+k]*W[o*ind+k]); k=k+1 } 506 if sh==24 { dst[t*outd+o]=s>>24 } else { dst[t*outd+o]=s>>16 } 507 o=o+1 } 508 t=t+1 } 509 return 0 510} 511// pooled matmul (forward accumulation order only): bit-identical to mm_out_in/mm_q24in with rev=0. 512func mm_pool(inp: *i64, W: *i64, dst: *i64, T: i64, in_dim: i64, out_dim: i64, shift: i64) -> i64 { 513 let p: *NxThreadPool = nf_pool() 514 var bands: i64 = p.n_workers 515 if bands > 32 { bands = 32 } 516 if bands > out_dim { bands = out_dim } 517 if bands < 1 { bands = 1 } 518 let per: i64 = (out_dim + bands - 1) / bands 519 let done0: i64 = nx_pool_n_completed(p) 520 var b: i64 = 0 521 while b < bands { 522 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 523 c[0]=inp as i64; c[1]=W as i64; c[2]=dst as i64; c[3]=T; c[4]=in_dim; c[5]=out_dim; c[6]=b*per 524 var hi: i64=(b+1)*per 525 if hi>out_dim { hi=out_dim } 526 c[7]=hi; c[8]=shift 527 nx_pool_submit(p, _nfmm_task, c as i64) 528 b=b+1 529 } 530 nx_pool_wait(p, done0+bands) 531 return 0 532} 533// one weight-load task: load_blk with a PRIVATE name scratch (12 concurrent tasks must not share nm). 534func _nfld_task(ctx_i: i64) -> i64 { 535 let c: *i64 = ctx_i as *i64 536 let nm: *u8 = (ctx_i + 64) as *u8 537 load_blk(c[0] as *u8, c[1] as *NxGgufHeader, nm, c[2], c[3] as *u8, c[4] as *i64, c[5]) 538 return 0 539} 540func nf_ld1(p: *NxThreadPool, i: i64, buf: *u8, hdr: *NxGgufHeader, L: i64, sfx: *u8, out: i64, want: i64) -> i64 { 541 let c: *i64 = ((g_nf_ldarena as i64) + i*128) as *i64 542 c[0]=buf as i64; c[1]=hdr as i64; c[2]=L; c[3]=sfx as i64; c[4]=out; c[5]=want 543 nx_pool_submit(p, _nfld_task, c as i64) 544 return 0 545} 546// load one layer's 12 tensors in parallel (disjoint outputs; each task private name scratch). Bit-exact. 547func nf_load_layer_pool(buf: *u8, hdr: *NxGgufHeader, L: i64, wb: *i64, ne: i64, qd: i64, kvd: i64, fd: i64) -> i64 { 548 let p: *NxThreadPool = nf_pool() 549 let done0: i64 = nx_pool_n_completed(p) 550 nf_ld1(p, 0, buf, hdr, L, ".attn_norm.weight\x00" as *u8, wb[0], ne) 551 nf_ld1(p, 1, buf, hdr, L, ".attn_q.weight\x00" as *u8, wb[1], qd*ne) 552 nf_ld1(p, 2, buf, hdr, L, ".attn_k.weight\x00" as *u8, wb[2], kvd*ne) 553 nf_ld1(p, 3, buf, hdr, L, ".attn_v.weight\x00" as *u8, wb[3], kvd*ne) 554 nf_ld1(p, 4, buf, hdr, L, ".attn_output.weight\x00" as *u8, wb[4], ne*qd) 555 nf_ld1(p, 5, buf, hdr, L, ".attn_q.bias\x00" as *u8, wb[9], qd) 556 nf_ld1(p, 6, buf, hdr, L, ".attn_k.bias\x00" as *u8, wb[10], kvd) 557 nf_ld1(p, 7, buf, hdr, L, ".attn_v.bias\x00" as *u8, wb[11], kvd) 558 nf_ld1(p, 8, buf, hdr, L, ".ffn_norm.weight\x00" as *u8, wb[5], ne) 559 nf_ld1(p, 9, buf, hdr, L, ".ffn_gate.weight\x00" as *u8, wb[6], ne*fd) 560 nf_ld1(p, 10, buf, hdr, L, ".ffn_up.weight\x00" as *u8, wb[7], ne*fd) 561 nf_ld1(p, 11, buf, hdr, L, ".ffn_down.weight\x00" as *u8, wb[8], fd*ne) 562 nx_pool_wait(p, done0+12) 563 return 0 564} 565// one head band: scan vocab rows [lo,hi), local strict-> argmax (keeps the LOWEST id on ties, matching serial). 566func _nfhd_task(ctx_i: i64) -> i64 { 567 let c: *i64 = ctx_i as *i64 568 let buf: *u8=c[0] as *u8; let base: i64=c[1]; let ty: i64=c[2]; let normed: *i64=c[3] as *i64 569 let ne: i64=c[4]; let lo: i64=c[5]; let hi: i64=c[6] 570 let row: *i64=c[7] as *i64; let tmp: *i64=c[8] as *i64; let outp: *i64=c[9] as *i64 571 var best: i64=lo 572 var bestv: i64=0-9223372036854775807 573 var v: i64=lo 574 while v<hi { 575 dequant_row(buf, base, ty, v, ne, row, tmp) 576 var s: i64=0; var k: i64=0; while k<ne { s=s+(normed[k]*row[k]); k=k+1 } 577 let lg: i64=s>>24 578 if lg>bestv { bestv=lg; best=v } 579 v=v+1 580 } 581 outp[0]=best 582 outp[1]=bestv 583 return 0 584} 585// pooled streamed LM-head argmax. hp = [buf, oh_base, oh_ty, normed(Q24), vocab, ne, out_id_ptr, out_logit_ptr]. 586// Returns the argmax id; logits use the Q24 head convention (dot >>24). Band-ascending strict-> reduce keeps the 587// lowest id on ties = identical to the serial scan. 588func head_argmax_pool(hp: *i64) -> i64 { 589 let p: *NxThreadPool = nf_pool() 590 let vocab: i64=hp[4] 591 var bands: i64 = p.n_workers 592 if bands > 32 { bands = 32 } 593 if bands > vocab { bands = vocab } 594 if bands < 1 { bands = 1 } 595 let per: i64 = (vocab + bands - 1) / bands 596 // ROOT FIX 2026-07-15: dims-sized per-call band scratch (the fixed 8192B row slot overflows at 597 // ne>1024 into the neighbor band -- same corruption class as the i32 head-cache build; see 598 // nf_dequant_head_all_i32). Freed after the wait. ~2 syscalls vs ~650ms/token here = negligible. 599 let hne: i64 = hp[5] 600 let rstride2: i64 = hne*8 601 let tstride2: i64 = hne*8 + 4096 602 let rows2: *u8 = sys_mmap(bands*rstride2) 603 let tmps2: *u8 = sys_mmap(bands*tstride2) 604 let done0: i64 = nx_pool_n_completed(p) 605 var b: i64 = 0 606 while b < bands { 607 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 608 c[0]=hp[0]; c[1]=hp[1]; c[2]=hp[2]; c[3]=hp[3]; c[4]=hp[5]; c[5]=b*per 609 var hi: i64=(b+1)*per 610 if hi>vocab { hi=vocab } 611 c[6]=hi 612 c[7]=((rows2 as i64) + b*rstride2) 613 c[8]=((tmps2 as i64) + b*tstride2) 614 c[9]=((g_nf_hdout as i64) + b*16) 615 nx_pool_submit(p, _nfhd_task, c as i64) 616 b=b+1 617 } 618 nx_pool_wait(p, done0+bands) 619 sys_munmap(rows2, bands*rstride2) 620 sys_munmap(tmps2, bands*tstride2) 621 var best: i64=0 622 var bestv: i64=0-9223372036854775807 623 var rb: i64=0 624 while rb<bands { 625 let o: *i64=((g_nf_hdout as i64) + rb*16) as *i64 626 if o[1]>bestv { bestv=o[1]; best=o[0] } 627 rb=rb+1 628 } 629 let oid: *i64=hp[6] as *i64 630 let olg: *i64=hp[7] as *i64 631 oid[0]=best 632 olg[0]=bestv 633 return best 634} 635// attention sublayer: out = x + W_o( GQA-causal-softmax( RoPE(W_q·RMSNorm x), RoPE(W_k·..), W_v·.. ) ). 636// cfg = [T, ne, n_heads, n_kv, head_dim, q_dim, kv_dim, attn_scale]. 637func attn_sublayer(x: *i64, gamma: *i64, Wq: *i64, Wk: *i64, Wv: *i64, Wo: *i64, bq: *i64, bk: *i64, bv: *i64, freqs: *i64, xn: *i64, Q: *i64, K: *i64, V: *i64, concat: *i64, sc: *i64, at: *i64, proj: *i64, out: *i64, cfg: *i64, rev: i64) -> i64 { 638 let T: i64=cfg[0]; let ne: i64=cfg[1]; let nh: i64=cfg[2]; let nkv: i64=cfg[3]; let hd: i64=cfg[4]; let qd: i64=cfg[5]; let kvd: i64=cfg[6]; let scale: i64=cfg[7] 639 var t: i64=0 640 while t<T { rmsnorm_gamma_row_q24(x, gamma, t*ne, ne, xn, t*ne); t=t+1 } 641 // pooled (bit-exact) matmuls on the forward path; rev!=0 keeps the serial reversed-accumulation path so the 642 // fwd-vs-rev determinism teeth still exercise a genuinely different reduction order. 643 if rev==0 { mm_pool(xn, Wq, Q, T, ne, qd, 24); mm_pool(xn, Wk, K, T, ne, kvd, 24); mm_pool(xn, Wv, V, T, ne, kvd, 24) } 644 else { mm_q24in(xn, Wq, Q, T, ne, qd, rev); mm_q24in(xn, Wk, K, T, ne, kvd, rev); mm_q24in(xn, Wv, V, T, ne, kvd, rev) } 645 // Qwen2 Q/K/V projection biases (added before RoPE) 646 var bt: i64=0 647 while bt<T { var bo: i64=0; while bo<qd { Q[bt*qd+bo]=Q[bt*qd+bo]+bq[bo]; bo=bo+1 } var bp: i64=0; while bp<kvd { K[bt*kvd+bp]=K[bt*kvd+bp]+bk[bp]; V[bt*kvd+bp]=V[bt*kvd+bp]+bv[bp]; bp=bp+1 } bt=bt+1 } 648 t=0 649 while t<T { var h: i64=0; while h<nh { rope_apply(((Q as i64)+(t*qd+h*hd)*8) as *i64, hd, t, freqs); h=h+1 } var hk: i64=0; while hk<nkv { rope_apply(((K as i64)+(t*kvd+hk*hd)*8) as *i64, hd, t, freqs); hk=hk+1 } t=t+1 } 650 let group: i64=nh/nkv 651 var hh: i64=0 652 while hh<nh { 653 let kv: i64=hh/group; let bq: i64=hh*hd; let bk: i64=kv*hd 654 t=0 655 while t<T { 656 let cnt: i64=t+1; var s: i64=0 657 while s<cnt { var dot: i64=0; var d: i64=0; while d<hd { dot=dot+(Q[t*qd+bq+d]*K[s*kvd+bk+d]); d=d+1 } sc[s]=qmul(dot>>16, scale); s=s+1 } 658 var mmax: i64=sc[0]; var j: i64=1; while j<cnt { if sc[j]>mmax { mmax=sc[j] } j=j+1 } 659 var sum: i64=0; j=0; while j<cnt { let e: i64=fx_exp(sc[j]-mmax); at[j]=e; sum=sum+e; j=j+1 } 660 if sum<=0 { sum=1 } 661 j=0; while j<cnt { at[j]=(at[j]<<16)/sum; j=j+1 } 662 var d2: i64=0 663 while d2<hd { var acc: i64=0; s=0; while s<cnt { acc=acc+(at[s]*V[s*kvd+bk+d2]); s=s+1 } concat[t*qd+bq+d2]=acc>>16; d2=d2+1 } 664 t=t+1 665 } 666 hh=hh+1 667 } 668 mm_out_in(concat, Wo, proj, T, qd, ne, rev) 669 var i: i64=0; while i<T*ne { out[i]=x[i]+proj[i]; i=i+1 } 670 return 0 671} 672// FFN sublayer: out = x + W_down( SiLU(W_gate·RMSNorm x) (*) W_up·RMSNorm x ). cfg = [T, ne, ffn_dim]. 673func ffn_sublayer(x: *i64, gamma: *i64, Wg: *i64, Wu: *i64, Wd: *i64, xn: *i64, gate: *i64, up: *i64, hbuf: *i64, proj: *i64, out: *i64, cfg: *i64, rev: i64) -> i64 { 674 let T: i64=cfg[0]; let ne: i64=cfg[1]; let fd: i64=cfg[2] 675 var t: i64=0 676 while t<T { rmsnorm_gamma_row_q24(x, gamma, t*ne, ne, xn, t*ne); t=t+1 } 677 mm_q24in(xn, Wg, gate, T, ne, fd, rev); mm_q24in(xn, Wu, up, T, ne, fd, rev) 678 var i: i64=0; while i<T*fd { hbuf[i]=qmul(silu(gate[i]), up[i]); i=i+1 } 679 mm_out_in(hbuf, Wd, proj, T, fd, ne, rev) 680 i=0; while i<T*ne { out[i]=x[i]+proj[i]; i=i+1 } 681 return 0 682} 683// run N transformer blocks (lazy per-layer load), x -> out. wb=9 weight buffers, sb=14 scratch (ptrs as i64). 684func run_stack(buf: *u8, hdr: *NxGgufHeader, x: *i64, out: *i64, wb: *i64, sb: *i64, nm: *u8, freqs: *i64, cfgA: *i64, cfgF: *i64, N: i64, rev: i64) -> i64 { 685 let T: i64=cfgA[0]; let ne: i64=cfgA[1]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let fd: i64=cfgF[2] 686 let gA: *i64=wb[0] as *i64; let Wq: *i64=wb[1] as *i64; let Wk: *i64=wb[2] as *i64; let Wv: *i64=wb[3] as *i64; let Wo: *i64=wb[4] as *i64 687 let gF: *i64=wb[5] as *i64; let Wg: *i64=wb[6] as *i64; let Wu: *i64=wb[7] as *i64; let Wd: *i64=wb[8] as *i64 688 let bq: *i64=wb[9] as *i64; let bk: *i64=wb[10] as *i64; let bv: *i64=wb[11] as *i64 689 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 690 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 691 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64; let cur2: *i64=sb[13] as *i64 692 cpy(cur, x, T*ne) 693 var L: i64=0 694 while L<N { 695 load_blk(buf, hdr, nm, L, ".attn_norm.weight\x00" as *u8, gA, ne) 696 load_blk(buf, hdr, nm, L, ".attn_q.weight\x00" as *u8, Wq, qd*ne) 697 load_blk(buf, hdr, nm, L, ".attn_k.weight\x00" as *u8, Wk, kvd*ne) 698 load_blk(buf, hdr, nm, L, ".attn_v.weight\x00" as *u8, Wv, kvd*ne) 699 load_blk(buf, hdr, nm, L, ".attn_output.weight\x00" as *u8, Wo, ne*qd) 700 load_blk(buf, hdr, nm, L, ".attn_q.bias\x00" as *u8, bq, qd) 701 load_blk(buf, hdr, nm, L, ".attn_k.bias\x00" as *u8, bk, kvd) 702 load_blk(buf, hdr, nm, L, ".attn_v.bias\x00" as *u8, bv, kvd) 703 load_blk(buf, hdr, nm, L, ".ffn_norm.weight\x00" as *u8, gF, ne) 704 load_blk(buf, hdr, nm, L, ".ffn_gate.weight\x00" as *u8, Wg, ne*fd) 705 load_blk(buf, hdr, nm, L, ".ffn_up.weight\x00" as *u8, Wu, ne*fd) 706 load_blk(buf, hdr, nm, L, ".ffn_down.weight\x00" as *u8, Wd, fd*ne) 707 attn_sublayer(cur, gA, Wq, Wk, Wv, Wo, bq, bk, bv, freqs, xn, Q, K, V, concat, sc, at, proj, hmid, cfgA, rev) 708 ffn_sublayer(hmid, gF, Wg, Wu, Wd, xn, gate, up, hbuf, proj, cur2, cfgF, rev) 709 cpy(cur, cur2, T*ne) 710 L=L+1 711 } 712 cpy(out, cur, T*ne) 713 return 0 714} 715// ==== KV-CACHE decode path (2026-07-09) ==================================================================== 716// Prefill captures each layer's post-bias/post-RoPE K rows + post-bias V rows; decode_step_kv forwards ONLY the 717// new token, appending to + attending over the cache. LOSSLESS vs the uncached forward: causal masking means an 718// old token's K/V at every layer depend only on tokens 0..itself, so cached decode is BIT-IDENTICAL (gate-proven 719// by matching the uncached generation token-for-token). kvc = 2N ptrs [Kc_0, Vc_0, Kc_1, Vc_1, ...], each cache 720// MAXT*kvd i64. ADDITIVE: run_stack keeps its signature (nx_cc has no arity check -- changing it would let old 721// call sites pass garbage silently). 722func run_stack_prefill_kv(buf: *u8, hdr: *NxGgufHeader, x: *i64, out: *i64, wb: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 723 let T: i64=cfgA[0]; let ne: i64=cfgA[1]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let fd: i64=cfgF[2] 724 let gA: *i64=wb[0] as *i64; let Wq: *i64=wb[1] as *i64; let Wk: *i64=wb[2] as *i64; let Wv: *i64=wb[3] as *i64; let Wo: *i64=wb[4] as *i64 725 let gF: *i64=wb[5] as *i64; let Wg: *i64=wb[6] as *i64; let Wu: *i64=wb[7] as *i64; let Wd: *i64=wb[8] as *i64 726 let bq: *i64=wb[9] as *i64; let bk: *i64=wb[10] as *i64; let bv: *i64=wb[11] as *i64 727 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 728 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 729 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64; let cur2: *i64=sb[13] as *i64 730 cpy(cur, x, T*ne) 731 var L: i64=0 732 while L<N { 733 nf_load_layer_pool(buf, hdr, L, wb, ne, qd, kvd, fd) // parallel weight load (matmuls stay serial: one-time) 734 attn_sublayer(cur, gA, Wq, Wk, Wv, Wo, bq, bk, bv, freqs, xn, Q, K, V, concat, sc, at, proj, hmid, cfgA, 0) 735 // capture this layer's K/V rows (K = post-bias post-RoPE; V = post-bias) into the layer cache 736 let Kc: *i64 = kvc[2*L] as *i64 737 let Vc: *i64 = kvc[2*L+1] as *i64 738 cpy(Kc, K, T*kvd) 739 cpy(Vc, V, T*kvd) 740 ffn_sublayer(hmid, gF, Wg, Wu, Wd, xn, gate, up, hbuf, proj, cur2, cfgF, 0) 741 cpy(cur, cur2, T*ne) 742 L=L+1 743 } 744 cpy(out, cur, T*ne) 745 return 0 746} 747// decode ONE token at absolute position `pos` against the populated caches. x1/out1 = single ne-row. Mirrors the 748// uncached math exactly: RMSNorm(Q24) -> QKV(mm_q24in) -> +bias -> RoPE(pos) -> append cache row -> GQA softmax over 749// rows 0..pos -> W_o -> residual -> ffn_sublayer(T=1). 750func decode_step_kv(buf: *u8, hdr: *NxGgufHeader, x1: *i64, out1: *i64, wb: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, pos: i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 751 let ne: i64=cfgA[1]; let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7]; let fd: i64=cfgF[2] 752 let gA: *i64=wb[0] as *i64; let Wq: *i64=wb[1] as *i64; let Wk: *i64=wb[2] as *i64; let Wv: *i64=wb[3] as *i64; let Wo: *i64=wb[4] as *i64 753 let gF: *i64=wb[5] as *i64; let Wg: *i64=wb[6] as *i64; let Wu: *i64=wb[7] as *i64; let Wd: *i64=wb[8] as *i64 754 let bq: *i64=wb[9] as *i64; let bk: *i64=wb[10] as *i64; let bv: *i64=wb[11] as *i64 755 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 756 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 757 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64; let cur2: *i64=sb[13] as *i64 758 cpy(cur, x1, ne) 759 var L: i64=0 760 while L<N { 761 nf_load_layer_pool(buf, hdr, L, wb, ne, qd, kvd, fd) // all 12 tensors in parallel (disjoint outputs) 762 rmsnorm_gamma_row_q24(cur, gA, 0, ne, xn, 0) 763 mm_pool(xn, Wq, Q, 1, ne, qd, 24) 764 mm_pool(xn, Wk, K, 1, ne, kvd, 24) 765 mm_pool(xn, Wv, V, 1, ne, kvd, 24) 766 var bo: i64=0; while bo<qd { Q[bo]=Q[bo]+bq[bo]; bo=bo+1 } 767 var bp: i64=0; while bp<kvd { K[bp]=K[bp]+bk[bp]; V[bp]=V[bp]+bv[bp]; bp=bp+1 } 768 var h: i64=0; while h<nh { rope_apply(((Q as i64)+(h*hd)*8) as *i64, hd, pos, freqs); h=h+1 } 769 var hk: i64=0; while hk<nkv { rope_apply(((K as i64)+(hk*hd)*8) as *i64, hd, pos, freqs); hk=hk+1 } 770 let Kc: *i64 = kvc[2*L] as *i64 771 let Vc: *i64 = kvc[2*L+1] as *i64 772 var ci: i64=0; while ci<kvd { Kc[pos*kvd+ci]=K[ci]; Vc[pos*kvd+ci]=V[ci]; ci=ci+1 } 773 let group: i64=nh/nkv 774 let cnt: i64=pos+1 775 var hh: i64=0 776 while hh<nh { 777 let kvh: i64=hh/group; let qb: i64=hh*hd; let kb: i64=kvh*hd 778 var s: i64=0 779 while s<cnt { var dot: i64=0; var d: i64=0; while d<hd { dot=dot+(Q[qb+d]*Kc[s*kvd+kb+d]); d=d+1 } sc[s]=qmul(dot>>16, scale); s=s+1 } 780 var mmax: i64=sc[0]; var j: i64=1; while j<cnt { if sc[j]>mmax { mmax=sc[j] } j=j+1 } 781 var sum: i64=0; j=0; while j<cnt { let e: i64=fx_exp(sc[j]-mmax); at[j]=e; sum=sum+e; j=j+1 } 782 if sum<=0 { sum=1 } 783 j=0; while j<cnt { at[j]=(at[j]<<16)/sum; j=j+1 } 784 var d2: i64=0 785 while d2<hd { var acc: i64=0; s=0; while s<cnt { acc=acc+(at[s]*Vc[s*kvd+kb+d2]); s=s+1 } concat[qb+d2]=acc>>16; d2=d2+1 } 786 hh=hh+1 787 } 788 mm_pool(concat, Wo, proj, 1, qd, ne, 16) 789 var ri: i64=0; while ri<ne { hmid[ri]=cur[ri]+proj[ri]; ri=ri+1 } 790 // inlined FFN (T=1) with pooled matmuls -- bit-exact vs ffn_sublayer 791 rmsnorm_gamma_row_q24(hmid, gF, 0, ne, xn, 0) 792 mm_pool(xn, Wg, gate, 1, ne, fd, 24) 793 mm_pool(xn, Wu, up, 1, ne, fd, 24) 794 var fi: i64=0; while fi<fd { hbuf[fi]=qmul(silu(gate[fi]), up[fi]); fi=fi+1 } 795 mm_pool(hbuf, Wd, proj, 1, fd, ne, 16) 796 var fo: i64=0; while fo<ne { cur[fo]=hmid[fo]+proj[fo]; fo=fo+1 } 797 L=L+1 798 } 799 cpy(out1, cur, ne) 800 return 0 801} 802// ==== DEQUANT-ONCE WEIGHT CACHE (2026-07-09) ============================================================= 803// MEASURED (dqprobe gate): a decode_step_kv spends ~88% of its time (1213/1363 ms) RE-DEQUANTIZING the 804// frozen weights -- every layer, every token -- even though the weights never change across decode steps. 805// Dequant ONCE into a per-layer cache and reuse => decode is bit-identical (same dequant, just not repeated) 806// but ~9x faster. wcache = N pointers, each a 12-slot wb set (identical layout to nf_load_layer_pool's). 807// ADDITIVE: brand-new functions; decode_step_kv / run_stack untouched (nx_cc has no arity check, so changing 808// a proven signature would let old call sites pass garbage silently). 809func nf_alloc_layer_wb(ne: i64, qd: i64, kvd: i64, fd: i64) -> *i64 { 810 let w: *i64 = sys_mmap(12*8) as *i64 811 w[0]=sys_mmap(ne*8) as i64; w[1]=sys_mmap(qd*ne*8) as i64; w[2]=sys_mmap(kvd*ne*8) as i64; w[3]=sys_mmap(kvd*ne*8) as i64; w[4]=sys_mmap(ne*qd*8) as i64 812 w[5]=sys_mmap(ne*8) as i64; w[6]=sys_mmap(ne*fd*8) as i64; w[7]=sys_mmap(ne*fd*8) as i64; w[8]=sys_mmap(fd*ne*8) as i64 813 w[9]=sys_mmap(qd*8) as i64; w[10]=sys_mmap(kvd*8) as i64; w[11]=sys_mmap(kvd*8) as i64 814 return w 815} 816// dequant ALL N layers ONCE into wcache (wcache[L] = a fresh 12-slot wb set). Reuses the proven 817// nf_load_layer_pool dequant verbatim => bit-identical to the per-step path, done a single time. 818func nf_dequant_all_layers(buf: *u8, hdr: *NxGgufHeader, wcache: *i64, N: i64, ne: i64, qd: i64, kvd: i64, fd: i64) -> i64 { 819 var L: i64=0 820 while L<N { 821 let w: *i64 = nf_alloc_layer_wb(ne, qd, kvd, fd) 822 nf_load_layer_pool(buf, hdr, L, w, ne, qd, kvd, fd) 823 wcache[L]=w as i64 824 L=L+1 825 } 826 return 0 827} 828// decode ONE token using the pre-dequantized wcache -> NO per-step dequant. Byte-for-byte the same math as 829// decode_step_kv (same weights, same accumulation order); only the redundant re-dequant is removed. The 830// weight pointers are derived per-layer INSIDE the loop from wcache[L] (each layer has its own buffers). 831func decode_step_kv_cached(buf: *u8, hdr: *NxGgufHeader, x1: *i64, out1: *i64, wcache: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, pos: i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 832 let ne: i64=cfgA[1]; let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7]; let fd: i64=cfgF[2] 833 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 834 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 835 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64 836 cpy(cur, x1, ne) 837 var L: i64=0 838 while L<N { 839 let wb: *i64 = wcache[L] as *i64 840 let gA: *i64=wb[0] as *i64; let Wq: *i64=wb[1] as *i64; let Wk: *i64=wb[2] as *i64; let Wv: *i64=wb[3] as *i64; let Wo: *i64=wb[4] as *i64 841 let gF: *i64=wb[5] as *i64; let Wg: *i64=wb[6] as *i64; let Wu: *i64=wb[7] as *i64; let Wd: *i64=wb[8] as *i64 842 let bq: *i64=wb[9] as *i64; let bk: *i64=wb[10] as *i64; let bv: *i64=wb[11] as *i64 843 rmsnorm_gamma_row_q24(cur, gA, 0, ne, xn, 0) 844 mm_pool(xn, Wq, Q, 1, ne, qd, 24) 845 mm_pool(xn, Wk, K, 1, ne, kvd, 24) 846 mm_pool(xn, Wv, V, 1, ne, kvd, 24) 847 var bo: i64=0; while bo<qd { Q[bo]=Q[bo]+bq[bo]; bo=bo+1 } 848 var bp: i64=0; while bp<kvd { K[bp]=K[bp]+bk[bp]; V[bp]=V[bp]+bv[bp]; bp=bp+1 } 849 var h: i64=0; while h<nh { rope_apply(((Q as i64)+(h*hd)*8) as *i64, hd, pos, freqs); h=h+1 } 850 var hk: i64=0; while hk<nkv { rope_apply(((K as i64)+(hk*hd)*8) as *i64, hd, pos, freqs); hk=hk+1 } 851 let Kc: *i64 = kvc[2*L] as *i64 852 let Vc: *i64 = kvc[2*L+1] as *i64 853 var ci: i64=0; while ci<kvd { Kc[pos*kvd+ci]=K[ci]; Vc[pos*kvd+ci]=V[ci]; ci=ci+1 } 854 let group: i64=nh/nkv 855 let cnt: i64=pos+1 856 var hh: i64=0 857 while hh<nh { 858 let kvh: i64=hh/group; let qb: i64=hh*hd; let kb: i64=kvh*hd 859 var s: i64=0 860 while s<cnt { var dot: i64=0; var d: i64=0; while d<hd { dot=dot+(Q[qb+d]*Kc[s*kvd+kb+d]); d=d+1 } sc[s]=qmul(dot>>16, scale); s=s+1 } 861 var mmax: i64=sc[0]; var j: i64=1; while j<cnt { if sc[j]>mmax { mmax=sc[j] } j=j+1 } 862 var sum: i64=0; j=0; while j<cnt { let e: i64=fx_exp(sc[j]-mmax); at[j]=e; sum=sum+e; j=j+1 } 863 if sum<=0 { sum=1 } 864 j=0; while j<cnt { at[j]=(at[j]<<16)/sum; j=j+1 } 865 var d2: i64=0 866 while d2<hd { var acc: i64=0; s=0; while s<cnt { acc=acc+(at[s]*Vc[s*kvd+kb+d2]); s=s+1 } concat[qb+d2]=acc>>16; d2=d2+1 } 867 hh=hh+1 868 } 869 mm_pool(concat, Wo, proj, 1, qd, ne, 16) 870 var ri: i64=0; while ri<ne { hmid[ri]=cur[ri]+proj[ri]; ri=ri+1 } 871 rmsnorm_gamma_row_q24(hmid, gF, 0, ne, xn, 0) 872 mm_pool(xn, Wg, gate, 1, ne, fd, 24) 873 mm_pool(xn, Wu, up, 1, ne, fd, 24) 874 var fi: i64=0; while fi<fd { hbuf[fi]=qmul(silu(gate[fi]), up[fi]); fi=fi+1 } 875 mm_pool(hbuf, Wd, proj, 1, fd, ne, 16) 876 var fo: i64=0; while fo<ne { cur[fo]=hmid[fo]+proj[fo]; fo=fo+1 } 877 L=L+1 878 } 879 cpy(out1, cur, ne) 880 return 0 881} 882// ==== DEQUANT-ONCE OUTPUT-HEAD CACHE ==================================================================== 883// head_argmax_pool re-dequantizes ALL `vocab` head rows every token (~650 ms of the per-token cost). Dequant 884// the whole [vocab x ne] head ONCE into an i64 cache; then argmax is a pure pooled dot -> LOSSLESS + flat. 885func _nfhdq_task(ctx_i: i64) -> i64 { 886 let c: *i64 = ctx_i as *i64 887 let buf: *u8=c[0] as *u8; let base: i64=c[1]; let ty: i64=c[2]; let cache: *i64=c[3] as *i64 888 let ne: i64=c[4]; let lo: i64=c[5]; let hi: i64=c[6]; let tmp: *i64=c[7] as *i64 889 var v: i64=lo 890 while v<hi { dequant_row(buf, base, ty, v, ne, ((cache as i64)+v*ne*8) as *i64, tmp); v=v+1 } 891 return 0 892} 893// dequant the full output head ONCE (banded over vocab). Returns the [vocab x ne] i64 cache base (0 on OOM). 894func nf_dequant_head_all(buf: *u8, oh_base: i64, oh_ty: i64, vocab: i64, ne: i64) -> *i64 { 895 let p: *NxThreadPool = nf_pool() 896 let cache: *i64 = sys_mmap(vocab*ne*8) as *i64 897 if (cache as i64)==0 { return cache } 898 var bands: i64 = p.n_workers 899 if bands > 32 { bands = 32 } 900 if bands > vocab { bands = vocab } 901 if bands < 1 { bands = 1 } 902 let per: i64 = (vocab + bands - 1) / bands 903 let done0: i64 = nx_pool_n_completed(p) 904 var b: i64 = 0 905 while b < bands { 906 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 907 c[0]=buf as i64; c[1]=oh_base; c[2]=oh_ty; c[3]=cache as i64; c[4]=ne; c[5]=b*per 908 var hi: i64=(b+1)*per 909 if hi>vocab { hi=vocab } 910 c[6]=hi 911 c[7]=((g_nf_hdtmp as i64) + b*32768) 912 nx_pool_submit(p, _nfhdq_task, c as i64) 913 b=b+1 914 } 915 nx_pool_wait(p, done0+bands) 916 return cache 917} 918// one cached-head argmax band: dot normed(Q24) against pre-dequantized rows [lo,hi); strict-> keeps lowest id. 919func _nfhac_task(ctx_i: i64) -> i64 { 920 let c: *i64 = ctx_i as *i64 921 let cache: *i64=c[0] as *i64; let normed: *i64=c[1] as *i64; let ne: i64=c[2]; let lo: i64=c[3]; let hi: i64=c[4]; let outp: *i64=c[5] as *i64 922 var best: i64=lo 923 var bestv: i64=0-9223372036854775807 924 var v: i64=lo 925 while v<hi { 926 let row: *i64=((cache as i64)+v*ne*8) as *i64 927 var s: i64=0; var k: i64=0; while k<ne { s=s+(normed[k]*row[k]); k=k+1 } 928 let lg: i64=s>>24 929 if lg>bestv { bestv=lg; best=v } 930 v=v+1 931 } 932 outp[0]=best; outp[1]=bestv 933 return 0 934} 935// pooled argmax over the PRE-DEQUANTIZED head cache. hcp = [cache, normed(Q24), vocab, ne, idout, lgout]. 936// Band-ascending strict-> reduce keeps the lowest id on ties = bit-identical to head_argmax_pool. 937func head_argmax_cached(hcp: *i64) -> i64 { 938 let p: *NxThreadPool = nf_pool() 939 let cache: i64=hcp[0]; let normed: i64=hcp[1]; let vocab: i64=hcp[2]; let ne: i64=hcp[3] 940 var bands: i64 = p.n_workers 941 if bands > 32 { bands = 32 } 942 if bands > vocab { bands = vocab } 943 if bands < 1 { bands = 1 } 944 let per: i64 = (vocab + bands - 1) / bands 945 let done0: i64 = nx_pool_n_completed(p) 946 var b: i64 = 0 947 while b < bands { 948 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 949 c[0]=cache; c[1]=normed; c[2]=ne; c[3]=b*per 950 var hi: i64=(b+1)*per 951 if hi>vocab { hi=vocab } 952 c[4]=hi 953 c[5]=((g_nf_hdout as i64) + b*16) 954 nx_pool_submit(p, _nfhac_task, c as i64) 955 b=b+1 956 } 957 nx_pool_wait(p, done0+bands) 958 var best: i64=0 959 var bestv: i64=0-9223372036854775807 960 var rb: i64=0 961 while rb<bands { 962 let o: *i64=((g_nf_hdout as i64) + rb*16) as *i64 963 if o[1]>bestv { bestv=o[1]; best=o[0] } 964 rb=rb+1 965 } 966 let oid: *i64=hcp[4] as *i64 967 let olg: *i64=hcp[5] as *i64 968 oid[0]=best; olg[0]=bestv 969 return best 970} 971// ==== i32 COMPACT CACHE (2026-07-09) ===================================================================== 972// MEASURED (fastgen gate): after killing re-dequant, the decode residual is MEMORY BANDWIDTH -- 2.73 GB of i64 973// weights read per token vs a 150 ms compute floor. Q16 weight values are real*65536 and |real|<32768 always, 974// so they fit EXACTLY in i32 -> halving the cache (2.73->1.43 GB) is LOSSLESS. *i32 loads SIGN-extend natively 975// since the 2026-07-10 sext compiler fix (the hot loops used to carry a manual branchless sign-extend; removed 976// in lockstep with the bless). The narrow pass counts any value that would NOT fit i32 -> nonzero = NOT lossless. 977func nf_narrow_i32(src: *i64, dst: *i32, cnt: i64) -> i64 { 978 var ovf: i64=0 979 var i: i64=0 980 while i<cnt { 981 let v: i64=src[i] 982 if v > 2147483647 { ovf=ovf+1 } 983 if v < (0-2147483648) { ovf=ovf+1 } 984 dst[i]=v as i32 985 i=i+1 986 } 987 return ovf 988} 989// one banded-matmul task with i32 weights (sign-extended on load). inp/dst stay i64. Bit-identical to _nfmm_task. 990func _nfmm_i32_task(ctx_i: i64) -> i64 { 991 let c: *i64 = ctx_i as *i64 992 let inp: *i64=c[0] as *i64; let W: *i32=c[1] as *i32; let dst: *i64=c[2] as *i64 993 let T: i64=c[3]; let ind: i64=c[4]; let outd: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8] 994 var t: i64=0 995 while t<T { var o: i64=lo 996 while o<hi { var s: i64=0; var k: i64=0 997 while k<ind { s=s+(inp[t*ind+k]*(W[o*ind+k] as i64)); k=k+1 } // *i32 sign-extends natively (2026-07-10 sext fix) 998 if sh==24 { dst[t*outd+o]=s>>24 } else { dst[t*outd+o]=s>>16 } 999 o=o+1 } 1000 t=t+1 } 1001 return 0 1002} 1003// pooled i32-weight matmul: bit-identical to mm_pool (same accumulation order, sign-extended i32 == the i64 value). 1004func mm_pool_i32(inp: *i64, W: *i32, dst: *i64, T: i64, in_dim: i64, out_dim: i64, shift: i64) -> i64 { 1005 let p: *NxThreadPool = nf_pool() 1006 var bands: i64 = p.n_workers 1007 if bands > 32 { bands = 32 } 1008 if bands > out_dim { bands = out_dim } 1009 if bands < 1 { bands = 1 } 1010 let per: i64 = (out_dim + bands - 1) / bands 1011 let done0: i64 = nx_pool_n_completed(p) 1012 var b: i64 = 0 1013 while b < bands { 1014 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1015 c[0]=inp as i64; c[1]=W as i64; c[2]=dst as i64; c[3]=T; c[4]=in_dim; c[5]=out_dim; c[6]=b*per 1016 var hi: i64=(b+1)*per 1017 if hi>out_dim { hi=out_dim } 1018 c[7]=hi; c[8]=shift 1019 nx_pool_submit(p, _nfmm_i32_task, c as i64) 1020 b=b+1 1021 } 1022 nx_pool_wait(p, done0+bands) 1023 return 0 1024} 1025// one i32 weight set: mm weights (idx 1,2,3,4,6,7,8) are i32 (4B); norms (0,5) + biases (9,10,11) stay i64. 1026func nf_alloc_layer_wb_i32(ne: i64, qd: i64, kvd: i64, fd: i64) -> *i64 { 1027 let w: *i64 = sys_mmap(12*8) as *i64 1028 w[0]=sys_mmap(ne*8) as i64; w[1]=sys_mmap(qd*ne*4) as i64; w[2]=sys_mmap(kvd*ne*4) as i64; w[3]=sys_mmap(kvd*ne*4) as i64; w[4]=sys_mmap(ne*qd*4) as i64 1029 w[5]=sys_mmap(ne*8) as i64; w[6]=sys_mmap(ne*fd*4) as i64; w[7]=sys_mmap(ne*fd*4) as i64; w[8]=sys_mmap(fd*ne*4) as i64 1030 w[9]=sys_mmap(qd*8) as i64; w[10]=sys_mmap(kvd*8) as i64; w[11]=sys_mmap(kvd*8) as i64 1031 return w 1032} 1033// dequant all N layers to a reused i64 scratch, narrow the 7 mm weights to i32 into wc32[L]. Returns total 1034// overflow count across all layers -- MUST be 0 for the i32 cache to be lossless (gate asserts it). 1035func nf_dequant_all_layers_i32(buf: *u8, hdr: *NxGgufHeader, wc32: *i64, N: i64, ne: i64, qd: i64, kvd: i64, fd: i64) -> i64 { 1036 let tmp: *i64 = nf_alloc_layer_wb(ne, qd, kvd, fd) 1037 var ovf: i64=0 1038 var L: i64=0 1039 while L<N { 1040 nf_load_layer_pool(buf, hdr, L, tmp, ne, qd, kvd, fd) 1041 let s: *i64 = nf_alloc_layer_wb_i32(ne, qd, kvd, fd) 1042 cpy(s[0] as *i64, tmp[0] as *i64, ne) 1043 ovf=ovf+nf_narrow_i32(tmp[1] as *i64, s[1] as *i32, qd*ne) 1044 ovf=ovf+nf_narrow_i32(tmp[2] as *i64, s[2] as *i32, kvd*ne) 1045 ovf=ovf+nf_narrow_i32(tmp[3] as *i64, s[3] as *i32, kvd*ne) 1046 ovf=ovf+nf_narrow_i32(tmp[4] as *i64, s[4] as *i32, ne*qd) 1047 cpy(s[5] as *i64, tmp[5] as *i64, ne) 1048 ovf=ovf+nf_narrow_i32(tmp[6] as *i64, s[6] as *i32, ne*fd) 1049 ovf=ovf+nf_narrow_i32(tmp[7] as *i64, s[7] as *i32, ne*fd) 1050 ovf=ovf+nf_narrow_i32(tmp[8] as *i64, s[8] as *i32, fd*ne) 1051 cpy(s[9] as *i64, tmp[9] as *i64, qd) 1052 cpy(s[10] as *i64, tmp[10] as *i64, kvd) 1053 cpy(s[11] as *i64, tmp[11] as *i64, kvd) 1054 wc32[L]=s as i64 1055 L=L+1 1056 } 1057 return ovf 1058} 1059// decode ONE token using the i32 compact cache. Byte-for-byte the same math as decode_step_kv_cached (the 1060// sign-extended i32 weight == the i64 value it was narrowed from), just half the weight bandwidth. 1061func decode_step_kv_cached_i32(buf: *u8, hdr: *NxGgufHeader, x1: *i64, out1: *i64, wcache: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, pos: i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 1062 let ne: i64=cfgA[1]; let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7]; let fd: i64=cfgF[2] 1063 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 1064 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 1065 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64 1066 cpy(cur, x1, ne) 1067 var L: i64=0 1068 while L<N { 1069 let wb: *i64 = wcache[L] as *i64 1070 let gA: *i64=wb[0] as *i64 1071 let Wq: *i32=wb[1] as *i32; let Wk: *i32=wb[2] as *i32; let Wv: *i32=wb[3] as *i32; let Wo: *i32=wb[4] as *i32 1072 let gF: *i64=wb[5] as *i64 1073 let Wg: *i32=wb[6] as *i32; let Wu: *i32=wb[7] as *i32; let Wd: *i32=wb[8] as *i32 1074 let bq: *i64=wb[9] as *i64; let bk: *i64=wb[10] as *i64; let bv: *i64=wb[11] as *i64 1075 rmsnorm_gamma_row_q24(cur, gA, 0, ne, xn, 0) 1076 mm_pool_i32(xn, Wq, Q, 1, ne, qd, 24) 1077 mm_pool_i32(xn, Wk, K, 1, ne, kvd, 24) 1078 mm_pool_i32(xn, Wv, V, 1, ne, kvd, 24) 1079 var bo: i64=0; while bo<qd { Q[bo]=Q[bo]+bq[bo]; bo=bo+1 } 1080 var bp: i64=0; while bp<kvd { K[bp]=K[bp]+bk[bp]; V[bp]=V[bp]+bv[bp]; bp=bp+1 } 1081 var h: i64=0; while h<nh { rope_apply(((Q as i64)+(h*hd)*8) as *i64, hd, pos, freqs); h=h+1 } 1082 var hk: i64=0; while hk<nkv { rope_apply(((K as i64)+(hk*hd)*8) as *i64, hd, pos, freqs); hk=hk+1 } 1083 let Kc: *i64 = kvc[2*L] as *i64 1084 let Vc: *i64 = kvc[2*L+1] as *i64 1085 var ci: i64=0; while ci<kvd { Kc[pos*kvd+ci]=K[ci]; Vc[pos*kvd+ci]=V[ci]; ci=ci+1 } 1086 let group: i64=nh/nkv 1087 let cnt: i64=pos+1 1088 var hh: i64=0 1089 while hh<nh { 1090 let kvh: i64=hh/group; let qb: i64=hh*hd; let kb: i64=kvh*hd 1091 var s: i64=0 1092 while s<cnt { var dot: i64=0; var d: i64=0; while d<hd { dot=dot+(Q[qb+d]*Kc[s*kvd+kb+d]); d=d+1 } sc[s]=qmul(dot>>16, scale); s=s+1 } 1093 var mmax: i64=sc[0]; var j: i64=1; while j<cnt { if sc[j]>mmax { mmax=sc[j] } j=j+1 } 1094 var sum: i64=0; j=0; while j<cnt { let e: i64=fx_exp(sc[j]-mmax); at[j]=e; sum=sum+e; j=j+1 } 1095 if sum<=0 { sum=1 } 1096 j=0; while j<cnt { at[j]=(at[j]<<16)/sum; j=j+1 } 1097 var d2: i64=0 1098 while d2<hd { var acc: i64=0; s=0; while s<cnt { acc=acc+(at[s]*Vc[s*kvd+kb+d2]); s=s+1 } concat[qb+d2]=acc>>16; d2=d2+1 } 1099 hh=hh+1 1100 } 1101 mm_pool_i32(concat, Wo, proj, 1, qd, ne, 16) 1102 var ri: i64=0; while ri<ne { hmid[ri]=cur[ri]+proj[ri]; ri=ri+1 } 1103 rmsnorm_gamma_row_q24(hmid, gF, 0, ne, xn, 0) 1104 mm_pool_i32(xn, Wg, gate, 1, ne, fd, 24) 1105 mm_pool_i32(xn, Wu, up, 1, ne, fd, 24) 1106 var fi: i64=0; while fi<fd { hbuf[fi]=qmul(silu(gate[fi]), up[fi]); fi=fi+1 } 1107 mm_pool_i32(hbuf, Wd, proj, 1, fd, ne, 16) 1108 var fo: i64=0; while fo<ne { cur[fo]=hmid[fo]+proj[fo]; fo=fo+1 } 1109 L=L+1 1110 } 1111 cpy(out1, cur, ne) 1112 return 0 1113} 1114// ---- i32 output-head cache (half the head bandwidth, lossless) ---- 1115func _nfhdq_i32_task(ctx_i: i64) -> i64 { 1116 let c: *i64 = ctx_i as *i64 1117 let buf: *u8=c[0] as *u8; let base: i64=c[1]; let ty: i64=c[2]; let cache: *i32=c[3] as *i32 1118 let ne: i64=c[4]; let lo: i64=c[5]; let hi: i64=c[6]; let tmp: *i64=c[7] as *i64; let row: *i64=c[8] as *i64 1119 var v: i64=lo 1120 while v<hi { dequant_row(buf, base, ty, v, ne, row, tmp); var k: i64=0; while k<ne { cache[v*ne+k]=(row[k]) as i32; k=k+1 } v=v+1 } 1121 return 0 1122} 1123func nf_dequant_head_all_i32(buf: *u8, oh_base: i64, oh_ty: i64, vocab: i64, ne: i64) -> *i32 { 1124 let p: *NxThreadPool = nf_pool() 1125 let cache: *i32 = sys_mmap(vocab*ne*4) as *i32 1126 if (cache as i64)==0 { return cache } 1127 var bands: i64 = p.n_workers 1128 if bands > 32 { bands = 32 } 1129 if bands > vocab { bands = vocab } 1130 if bands < 1 { bands = 1 } 1131 let per: i64 = (vocab + bands - 1) / bands 1132 // ROOT FIX 2026-07-15 (the run-to-run greedy divergence, proven by exact-repeat 1.5B runs): the old 1133 // shared arena gave each band a FIXED 8192B row (1024 i64). ne=1536 (Qwen2.5-1.5B) dequants 12288B -> 1134 // band b OVERFLOWED 4KB into band b+1's row MID-COPY -> timing-dependent corrupt cache rows, frozen 1135 // for the run (within-run determinism held; across runs diverged at near-tie logits). Per-call 1136 // DIMS-SIZED scratch: row stride ne*8, tmp stride ne*8+4096 (covers every quant type's block window 1137 // at any ne). Freed after the wait (all band tasks complete by then). 1138 let rstride: i64 = ne*8 1139 let tstride: i64 = ne*8 + 4096 1140 let rows: *u8 = sys_mmap(bands*rstride) 1141 let tmps: *u8 = sys_mmap(bands*tstride) 1142 let done0: i64 = nx_pool_n_completed(p) 1143 var b: i64 = 0 1144 while b < bands { 1145 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 1146 c[0]=buf as i64; c[1]=oh_base; c[2]=oh_ty; c[3]=cache as i64; c[4]=ne; c[5]=b*per 1147 var hi: i64=(b+1)*per 1148 if hi>vocab { hi=vocab } 1149 c[6]=hi 1150 c[7]=((tmps as i64) + b*tstride) 1151 c[8]=((rows as i64) + b*rstride) 1152 nx_pool_submit(p, _nfhdq_i32_task, c as i64) 1153 b=b+1 1154 } 1155 nx_pool_wait(p, done0+bands) 1156 sys_munmap(rows, bands*rstride) 1157 sys_munmap(tmps, bands*tstride) 1158 return cache 1159} 1160func _nfhac_i32_task(ctx_i: i64) -> i64 { 1161 let c: *i64 = ctx_i as *i64 1162 let cache: *i32=c[0] as *i32; let normed: *i64=c[1] as *i64; let ne: i64=c[2]; let lo: i64=c[3]; let hi: i64=c[4]; let outp: *i64=c[5] as *i64 1163 var best: i64=lo 1164 var bestv: i64=0-9223372036854775807 1165 var v: i64=lo 1166 while v<hi { 1167 var s: i64=0; var k: i64=0 1168 while k<ne { s=s+(normed[k]*(cache[v*ne+k] as i64)); k=k+1 } // *i32 sign-extends natively (2026-07-10 sext fix) 1169 let lg: i64=s>>24 1170 if lg>bestv { bestv=lg; best=v } 1171 v=v+1 1172 } 1173 outp[0]=best; outp[1]=bestv 1174 return 0 1175} 1176// one full-logits band over the i32 head cache: writes lgv[v] = dot(normed, row_v) >> 24 for rows [lo,hi). 1177// Bands write DISJOINT ranges of lgv -> deterministic regardless of thread count (same discipline as the 1178// banded matmuls). Added 2026-07-10 for SAMPLING (temperature/top-p needs ALL logits, not just the argmax). 1179func _nfhlg_task(ctx_i: i64) -> i64 { 1180 let c: *i64 = ctx_i as *i64 1181 let cache: *i32=c[0] as *i32; let normed: *i64=c[1] as *i64; let ne: i64=c[2]; let lo: i64=c[3]; let hi: i64=c[4]; let lgv: *i64=c[5] as *i64 1182 var v: i64=lo 1183 while v<hi { 1184 var s: i64=0; var k: i64=0 1185 while k<ne { s=s+(normed[k]*(cache[v*ne+k] as i64)); k=k+1 } 1186 lgv[v]=s>>24 1187 v=v+1 1188 } 1189 return 0 1190} 1191// pooled FULL-logits head over the i32 cache. hlp = [cache(i32), normed(Q24), vocab, ne, lgv(*i64 vocab)]. 1192// Same dots as head_argmax_cached_i32 (Q16 head convention), all of them kept. 1193func head_logits_cached_i32(hlp: *i64) -> i64 { 1194 let p: *NxThreadPool = nf_pool() 1195 let vocab: i64=hlp[2] 1196 var bands: i64 = p.n_workers 1197 if bands > 32 { bands = 32 } 1198 if bands > vocab { bands = vocab } 1199 if bands < 1 { bands = 1 } 1200 let per: i64 = (vocab + bands - 1) / bands 1201 let done0: i64 = nx_pool_n_completed(p) 1202 var b: i64 = 0 1203 while b < bands { 1204 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 1205 c[0]=hlp[0]; c[1]=hlp[1]; c[2]=hlp[3]; c[3]=b*per 1206 var hi: i64=(b+1)*per 1207 if hi>vocab { hi=vocab } 1208 c[4]=hi 1209 c[5]=hlp[4] 1210 nx_pool_submit(p, _nfhlg_task, c as i64) 1211 b=b+1 1212 } 1213 nx_pool_wait(p, done0+bands) 1214 return 0 1215} 1216// argmax over the i32 head cache. hcp = [cache(i32), normed(Q24), vocab, ne, idout, lgout]. Bit-identical. 1217func head_argmax_cached_i32(hcp: *i64) -> i64 { 1218 let p: *NxThreadPool = nf_pool() 1219 let cache: i64=hcp[0]; let normed: i64=hcp[1]; let vocab: i64=hcp[2]; let ne: i64=hcp[3] 1220 var bands: i64 = p.n_workers 1221 if bands > 32 { bands = 32 } 1222 if bands > vocab { bands = vocab } 1223 if bands < 1 { bands = 1 } 1224 let per: i64 = (vocab + bands - 1) / bands 1225 let done0: i64 = nx_pool_n_completed(p) 1226 var b: i64 = 0 1227 while b < bands { 1228 let c: *i64 = ((g_nf_hdctx as i64) + b*128) as *i64 1229 c[0]=cache; c[1]=normed; c[2]=ne; c[3]=b*per 1230 var hi: i64=(b+1)*per 1231 if hi>vocab { hi=vocab } 1232 c[4]=hi 1233 c[5]=((g_nf_hdout as i64) + b*16) 1234 nx_pool_submit(p, _nfhac_i32_task, c as i64) 1235 b=b+1 1236 } 1237 nx_pool_wait(p, done0+bands) 1238 var best: i64=0 1239 var bestv: i64=0-9223372036854775807 1240 var rb: i64=0 1241 while rb<bands { 1242 let o: *i64=((g_nf_hdout as i64) + rb*16) as *i64 1243 if o[1]>bestv { bestv=o[1]; best=o[0] } 1244 rb=rb+1 1245 } 1246 let oid: *i64=hcp[4] as *i64 1247 let olg: *i64=hcp[5] as *i64 1248 oid[0]=best; olg[0]=bestv 1249 return best 1250} 1251// ==== W8A8 SIMD DECODE PATH (Stage 2, 2026-07-09) ======================================================== 1252// PROBE-PROVEN (nx_nofloat_simd_dot_probe): __i16x16_madd (vpmaddwd) gives a 13x matmul over the scalar i64 1253// dot, at ~0.8% per-dot quantization error. LOSSY fast path (NOT bit-exact) -> ADDITIVE alongside the lossless 1254// i32 hero; the gate MEASURES whether real tokens survive. i16 lanes are *u8 buffers with manual LE nf_pack2 1255// (nx_cc has no i16 type). int8-range values keep the int32 madd accumulator overflow-safe for our K (<=304 1256// madds * ~32k < 2.1e9). Weight quant is amortized ONCE at cache build; only the activation is quantized per matmul. 1257// 1258// W12A12 (2026-09-16, search R0l attribution): the 8-bit per-TENSOR activation scale was the engine's numeric floor -- 1259// measured on BRIGHT biology the sovereign i8 rewrites scored 330 permil against 408 for the same weights through 1260// llama.cpp, and the two engines' greedy paths flip a near-tie at token twelve and never reconverge. The i16 lanes 1261// already carry the values, so the bits were free: weights and activations now quantise to +-NF_W_QMAX / +-NF_X_QMAX 1262// (16x finer each), rounded to nearest instead of truncated, and the int32 lane accumulation is folded into an i64 1263// total every NF_CHUNK_K elements so no lane can overflow. NF_CHUNK_K IS DERIVED, NOT CHOSEN: one vpmaddwd lane sums 1264// NF_MADD_PER_LANE products per 16 elements, so a chunk of C elements puts C/8 products in a lane, each at most 1265// NF_X_QMAX*NF_W_QMAX; C/8 * 4095 * 4095 <= NF_I32_ACC_MAX gives C <= 1024.5, and 1024 is the largest multiple of 1266// 16 below it (nx_nofloat_w12a12_gate pins the arithmetic and plants 2048 as the bound-breaking neg-control). 1267const NF_W_QMAX: i64 = 4095 1268const NF_X_QMAX: i64 = 4095 1269const NF_I32_ACC_MAX: i64 = 2147483647 1270const NF_MADD_PER_LANE: i64 = 8 1271const NF_CHUNK_K: i64 = 1024 1272func nf_pack2(buf: *u8, idx: i64, val: i64) -> i64 { buf[idx*2]=(val) as u8; buf[idx*2+1]=(val>>8) as u8; return 0 } 1273// round-to-nearest division clamped to +-qmax: a truncating divide biases every value toward zero, and one bias per 1274// element over an 8960-wide row is a systematic error the dot product cannot average away 1275func nf_qround(v: i64, s: i64, qmax: i64) -> i64 { 1276 var q: i64 = 0 1277 if v >= 0 { q = (v + s/2)/s } else { q = 0 - ((0 - v + s/2)/s) } 1278 if q > qmax { q = qmax } 1279 if q < 0 - qmax { q = 0 - qmax } 1280 return q 1281} 1282func nf_quant_w_i8(Wsrc: *i64, Wi8: *u8, sw: *i64, out_dim: i64, in_dim: i64) -> i64 { 1283 var o: i64=0 1284 while o<out_dim { 1285 var rm: i64=0; var k: i64=0 1286 while k<in_dim { var a: i64=Wsrc[o*in_dim+k]; if a<0 { a=0-a } if a>rm { rm=a } k=k+1 } 1287 var s: i64=rm/NF_W_QMAX; if s<1 { s=1 } 1288 sw[o]=s 1289 let rowb: i64=(Wi8 as i64)+o*in_dim*2 1290 k=0; while k<in_dim { nf_pack2(rowb as *u8, k, nf_qround(Wsrc[o*in_dim+k], s, NF_W_QMAX)); k=k+1 } 1291 o=o+1 1292 } 1293 return 0 1294} 1295func nf_alloc_layer_wb_i8(ne: i64, qd: i64, kvd: i64, fd: i64) -> *i64 { 1296 let w: *i64 = sys_mmap(19*8) as *i64 1297 w[0]=sys_mmap(ne*8) as i64 1298 w[1]=sys_mmap(qd*ne*2) as i64; w[2]=sys_mmap(qd*8) as i64 1299 w[3]=sys_mmap(kvd*ne*2) as i64; w[4]=sys_mmap(kvd*8) as i64 1300 w[5]=sys_mmap(kvd*ne*2) as i64; w[6]=sys_mmap(kvd*8) as i64 1301 w[7]=sys_mmap(ne*qd*2) as i64; w[8]=sys_mmap(ne*8) as i64 1302 w[9]=sys_mmap(ne*8) as i64 1303 w[10]=sys_mmap(ne*fd*2) as i64; w[11]=sys_mmap(fd*8) as i64 1304 w[12]=sys_mmap(ne*fd*2) as i64; w[13]=sys_mmap(fd*8) as i64 1305 w[14]=sys_mmap(fd*ne*2) as i64; w[15]=sys_mmap(ne*8) as i64 1306 w[16]=sys_mmap(qd*8) as i64; w[17]=sys_mmap(kvd*8) as i64; w[18]=sys_mmap(kvd*8) as i64 1307 return w 1308} 1309func nf_dequant_all_layers_i8(buf: *u8, hdr: *NxGgufHeader, wc8: *i64, N: i64, ne: i64, qd: i64, kvd: i64, fd: i64) -> i64 { 1310 let tmp: *i64 = nf_alloc_layer_wb(ne, qd, kvd, fd) 1311 var L: i64=0 1312 while L<N { 1313 nf_load_layer_pool(buf, hdr, L, tmp, ne, qd, kvd, fd) 1314 let s: *i64 = nf_alloc_layer_wb_i8(ne, qd, kvd, fd) 1315 cpy(s[0] as *i64, tmp[0] as *i64, ne) 1316 nf_quant_w_i8(tmp[1] as *i64, s[1] as *u8, s[2] as *i64, qd, ne) 1317 nf_quant_w_i8(tmp[2] as *i64, s[3] as *u8, s[4] as *i64, kvd, ne) 1318 nf_quant_w_i8(tmp[3] as *i64, s[5] as *u8, s[6] as *i64, kvd, ne) 1319 nf_quant_w_i8(tmp[4] as *i64, s[7] as *u8, s[8] as *i64, ne, qd) 1320 cpy(s[9] as *i64, tmp[5] as *i64, ne) 1321 nf_quant_w_i8(tmp[6] as *i64, s[10] as *u8, s[11] as *i64, fd, ne) 1322 nf_quant_w_i8(tmp[7] as *i64, s[12] as *u8, s[13] as *i64, fd, ne) 1323 nf_quant_w_i8(tmp[8] as *i64, s[14] as *u8, s[15] as *i64, ne, fd) 1324 cpy(s[16] as *i64, tmp[9] as *i64, qd) 1325 cpy(s[17] as *i64, tmp[10] as *i64, kvd) 1326 cpy(s[18] as *i64, tmp[11] as *i64, kvd) 1327 wc8[L]=s as i64 1328 L=L+1 1329 } 1330 return 0 1331} 1332func _nfmm_i8_task(ctx_i: i64) -> i64 { 1333 let c: *i64 = ctx_i as *i64 1334 let xi8: i64=c[0]; let sx: i64=c[1]; let Wi8: i64=c[2]; let sw: *i64=c[3] as *i64; let dst: *i64=c[4] as *i64 1335 let ind: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8]; let acc: *u8=c[9] as *u8 1336 var o: i64=lo 1337 while o<hi { 1338 let wb: i64 = Wi8 + o*ind*2 1339 // chunked accumulation: the int32 lanes hold at most NF_CHUNK_K/8 products each, then fold into i64 1340 var total: i64 = 0 1341 var k0: i64 = 0 1342 while k0 < ind { 1343 let z: *i64 = acc as *i64; z[0]=0; z[1]=0; z[2]=0; z[3]=0 1344 var k1: i64 = k0 + NF_CHUNK_K 1345 if k1 > ind { k1 = ind } 1346 var k: i64 = k0 1347 while k<k1 { __i16x16_madd(acc, (xi8+k*2) as *u8, (wb+k*2) as *u8); k=k+16 } 1348 total = total + nf_hsum_sx(acc) 1349 k0 = k1 1350 } 1351 dst[o]=((sx*sw[o])*total)>>sh 1352 o=o+1 1353 } 1354 return 0 1355} 1356func _nfmm_i8_task4(ctx_i: i64) -> i64 { 1357 let c: *i64 = ctx_i as *i64 1358 let xi8: i64=c[0]; let sx: i64=c[1]; let Wi8: i64=c[2]; let sw: *i64=c[3] as *i64; let dst: *i64=c[4] as *i64 1359 let ind: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8]; let acc: *u8=c[9] as *u8 1360 let a1: *u8 = ((acc as i64) + NF_ACC_BYTES) as *u8 1361 let a2: *u8 = ((acc as i64) + 2*NF_ACC_BYTES) as *u8 1362 let a3: *u8 = ((acc as i64) + 3*NF_ACC_BYTES) as *u8 1363 var o: i64=lo 1364 while o<hi { 1365 let wb: i64 = Wi8 + o*ind*2 1366 var total: i64 = 0 1367 var k0: i64 = 0 1368 while k0 < ind { 1369 let z: *i64 = acc as *i64 1370 var zi: i64 = 0 1371 while zi < NF_ACC4_I64 { z[zi] = 0; zi = zi + 1 } 1372 var k1: i64 = k0 + NF_CHUNK_K 1373 if k1 > ind { k1 = ind } 1374 var k: i64 = k0 1375 while k + NF_R4_STRIDE <= k1 { 1376 __i16x16_madd(acc, (xi8+k*2) as *u8, (wb+k*2) as *u8) 1377 __i16x16_madd(a1, (xi8+(k+NF_MADD_W)*2) as *u8, (wb+(k+NF_MADD_W)*2) as *u8) 1378 __i16x16_madd(a2, (xi8+(k+2*NF_MADD_W)*2) as *u8, (wb+(k+2*NF_MADD_W)*2) as *u8) 1379 __i16x16_madd(a3, (xi8+(k+3*NF_MADD_W)*2) as *u8, (wb+(k+3*NF_MADD_W)*2) as *u8) 1380 k = k + NF_R4_STRIDE 1381 } 1382 while k<k1 { __i16x16_madd(acc, (xi8+k*2) as *u8, (wb+k*2) as *u8); k=k+NF_MADD_W } 1383 total = total + nf_hsum_sx(acc) + nf_hsum_sx(a1) + nf_hsum_sx(a2) + nf_hsum_sx(a3) 1384 k0 = k1 1385 } 1386 dst[o]=((sx*sw[o])*total)>>sh 1387 o=o+1 1388 } 1389 return 0 1390} 1391// pooled W8A8 SIMD matmul (T=1 decode). Quantizes the single activation row once, bands the output rows. 1392// Banded by whole output row -> thread-count-invariant. in_dim MUST be a multiple of 16 (896/128/4864 all are). 1393func mm_pool_i8(x: *i64, Wi8: *u8, sw: *i64, dst: *i64, in_dim: i64, out_dim: i64, shift: i64) -> i64 { 1394 let p: *NxThreadPool = nf_pool() 1395 var xmx: i64=0; var k: i64=0 1396 while k<in_dim { var a: i64=x[k]; if a<0 { a=0-a } if a>xmx { xmx=a } k=k+1 } 1397 var sx: i64=xmx/NF_X_QMAX; if sx<1 { sx=1 } 1398 // ROOT-FIX CLASS 2026-07-15: g_nf_xi8 was a FIXED 4864x2 arena (the 0.5B's ffn) -- in_dim=8960 1399 // (Qwen2.5-1.5B ffn) would overflow it. Per-call dims-sized; freed after the wait. 1400 let xi8: *u8 = sys_mmap(in_dim*2) 1401 k=0; while k<in_dim { nf_pack2(xi8, k, nf_qround(x[k], sx, NF_X_QMAX)); k=k+1 } 1402 var bands: i64 = p.n_workers 1403 if bands > 32 { bands = 32 } 1404 if bands > out_dim { bands = out_dim } 1405 if bands < 1 { bands = 1 } 1406 let per: i64 = (out_dim + bands - 1) / bands 1407 let done0: i64 = nx_pool_n_completed(p) 1408 var b: i64=0 1409 while b<bands { 1410 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1411 c[0]=xi8 as i64; c[1]=sx; c[2]=Wi8 as i64; c[3]=sw as i64; c[4]=dst as i64; c[5]=in_dim; c[6]=b*per 1412 var hi: i64=(b+1)*per; if hi>out_dim { hi=out_dim } 1413 c[7]=hi; c[8]=shift 1414 if g_nf_i8_r4 == 1 { c[9]=((g_nf_i8acc4 as i64)+b*128); nx_pool_submit(p, _nfmm_i8_task4, c as i64) } 1415 else { c[9]=((g_nf_i8acc as i64)+b*64); nx_pool_submit(p, _nfmm_i8_task, c as i64) } 1416 b=b+1 1417 } 1418 nx_pool_wait(p, done0+bands) 1419 sys_munmap(xi8, in_dim*2) 1420 return 0 1421} 1422// R0r: M activation rows against one weight matrix, one weight pass. See the NF_PREFILL_M note for the loop order. 1423func _nfmm_i8_task_m(ctx_i: i64) -> i64 { 1424 let c: *i64 = ctx_i as *i64 1425 let xi8: i64=c[0]; let sxm: *i64=c[1] as *i64; let Wi8: i64=c[2]; let sw: *i64=c[3] as *i64; let dst: *i64=c[4] as *i64 1426 let ind: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8]; let acc: *u8=c[9] as *u8 1427 let M: i64=c[NF_MMCTX_M]; let od: i64=c[NF_MMCTX_OUT] 1428 var o: i64=lo 1429 while o<hi { 1430 let wb: i64 = Wi8 + o*ind*2 1431 var m: i64 = 0 1432 while m<M { 1433 let xr: i64 = xi8 + m*ind*2 1434 var total: i64 = 0 1435 var k0: i64 = 0 1436 while k0 < ind { 1437 var k1: i64 = k0 + NF_CHUNK_K 1438 if k1 > ind { k1 = ind } 1439 if g_nf_i8_dot == 1 { 1440 // R0r-b: one builtin per chunk, the accumulator register-resident across it. The chunk is a positive multiple 1441 // of NF_MADD_W: in_dim is one by the madd loop's own contract and NF_CHUNK_K is 64 of them. 1442 total = total + __i16_dot((xr+k0*2) as *u8, (wb+k0*2) as *u8, k1 - k0) 1443 } else { 1444 let z: *i64 = acc as *i64; z[0]=0; z[1]=0; z[2]=0; z[3]=0 1445 var k: i64 = k0 1446 while k<k1 { __i16x16_madd(acc, (xr+k*2) as *u8, (wb+k*2) as *u8); k=k+NF_MADD_W } 1447 total = total + nf_hsum_sx(acc) 1448 } 1449 k0 = k1 1450 } 1451 dst[m*od+o]=((sxm[m]*sw[o])*total)>>sh 1452 m=m+1 1453 } 1454 o=o+1 1455 } 1456 return 0 1457} 1458// R0r-c: the batched task with four k-interleaved accumulators (R0q's loop), one weight pass per block; every cell is the 1459// same integer sum as _nfmm_i8_task_m in a different association order, so it is bit-identical (the gate's T8e). 1460func _nfmm_i8_task_m4(ctx_i: i64) -> i64 { 1461 let c: *i64 = ctx_i as *i64 1462 let xi8: i64=c[0]; let sxm: *i64=c[1] as *i64; let Wi8: i64=c[2]; let sw: *i64=c[3] as *i64; let dst: *i64=c[4] as *i64 1463 let ind: i64=c[5]; let lo: i64=c[6]; let hi: i64=c[7]; let sh: i64=c[8]; let acc: *u8=c[9] as *u8 1464 let M: i64=c[NF_MMCTX_M]; let od: i64=c[NF_MMCTX_OUT] 1465 let a1: *u8 = ((acc as i64) + NF_ACC_BYTES) as *u8 1466 let a2: *u8 = ((acc as i64) + 2*NF_ACC_BYTES) as *u8 1467 let a3: *u8 = ((acc as i64) + 3*NF_ACC_BYTES) as *u8 1468 var o: i64=lo 1469 while o<hi { 1470 let wb: i64 = Wi8 + o*ind*2 1471 var m: i64 = 0 1472 while m<M { 1473 let xr: i64 = xi8 + m*ind*2 1474 var total: i64 = 0 1475 var k0: i64 = 0 1476 while k0 < ind { 1477 let z: *i64 = acc as *i64 1478 var zi: i64 = 0 1479 while zi < NF_ACC4_I64 { z[zi] = 0; zi = zi + 1 } 1480 var k1: i64 = k0 + NF_CHUNK_K 1481 if k1 > ind { k1 = ind } 1482 var k: i64 = k0 1483 while k + NF_R4_STRIDE <= k1 { 1484 __i16x16_madd(acc, (xr+k*2) as *u8, (wb+k*2) as *u8) 1485 __i16x16_madd(a1, (xr+(k+NF_MADD_W)*2) as *u8, (wb+(k+NF_MADD_W)*2) as *u8) 1486 __i16x16_madd(a2, (xr+(k+2*NF_MADD_W)*2) as *u8, (wb+(k+2*NF_MADD_W)*2) as *u8) 1487 __i16x16_madd(a3, (xr+(k+3*NF_MADD_W)*2) as *u8, (wb+(k+3*NF_MADD_W)*2) as *u8) 1488 k = k + NF_R4_STRIDE 1489 } 1490 while k<k1 { __i16x16_madd(acc, (xr+k*2) as *u8, (wb+k*2) as *u8); k=k+NF_MADD_W } 1491 total = total + nf_hsum_sx(acc) + nf_hsum_sx(a1) + nf_hsum_sx(a2) + nf_hsum_sx(a3) 1492 k0 = k1 1493 } 1494 dst[m*od+o]=((sxm[m]*sw[o])*total)>>sh 1495 m=m+1 1496 } 1497 o=o+1 1498 } 1499 return 0 1500} 1501// R0r: the batched twin of mm_pool_i8. x holds M rows of in_dim; dst receives M rows of out_dim. Each row is quantised 1502// exactly as mm_pool_i8 quantises its single row, then the bands stream the weights once per call. 1503func mm_pool_i8_m(x: *i64, M: i64, Wi8: *u8, sw: *i64, dst: *i64, in_dim: i64, out_dim: i64, shift: i64) -> i64 { 1504 nf_quant_rows_pool(x, M, in_dim) 1505 return mm_pool_i8_mq(M, Wi8, sw, dst, in_dim, out_dim, shift) 1506} 1507// R0s: ONE row quantiser, called by the serial control and by every pool task -- the arithmetic cannot differ. 1508func nf_quant_row(xr: *i64, in_dim: i64, xq: *u8, sxm: *i64, m: i64) -> i64 { 1509 var xmx: i64=0; var k: i64=0 1510 while k<in_dim { var a: i64=xr[k]; if a<0 { a=0-a } if a>xmx { xmx=a } k=k+1 } 1511 var sx: i64=xmx/NF_X_QMAX; if sx<1 { sx=1 } 1512 sxm[m]=sx 1513 k=0; while k<in_dim { nf_pack2(xq, k, nf_qround(xr[k], sx, NF_X_QMAX)); k=k+1 } 1514 return 0 1515} 1516func _nf_quant_rows_task(ctx_i: i64) -> i64 { 1517 let c: *i64 = ctx_i as *i64 1518 let x: i64=c[0]; let xi8: i64=c[1]; let sxm: *i64=c[2] as *i64; let ind: i64=c[3]; var m: i64=c[4]; let hi: i64=c[5] 1519 while m<hi { nf_quant_row((x + m*ind*8) as *i64, ind, (xi8 + m*ind*2) as *u8, sxm, m); m=m+1 } 1520 return 0 1521} 1522// R0s: quantise M activation rows of in_dim Q16 values into g_nf_xi8m / g_nf_sxm (grown to fit), one task per row 1523// up to the arena's slots, or serially under the gate's control switch. Stamps g_nf_mm_us_quant. 1524func nf_quant_rows_pool(x: *i64, M: i64, in_dim: i64) -> i64 { 1525 let need: i64 = M*in_dim*2 1526 if need > g_nf_xi8m_cap { if g_nf_xi8m_cap > 0 { sys_munmap(g_nf_xi8m, g_nf_xi8m_cap) } g_nf_xi8m = sys_mmap(need); g_nf_xi8m_cap = need } 1527 let needs: i64 = M*8 1528 if needs > g_nf_sxm_cap { if g_nf_sxm_cap > 0 { sys_munmap(g_nf_sxm as *u8, g_nf_sxm_cap) } g_nf_sxm = sys_mmap(needs) as *i64; g_nf_sxm_cap = needs } 1529 let tq0: i64 = sys_now_us() 1530 if g_nf_quant_serial == 1 { 1531 var m: i64=0 1532 while m<M { nf_quant_row(((x as i64) + m*in_dim*8) as *i64, in_dim, ((g_nf_xi8m as i64) + m*in_dim*2) as *u8, g_nf_sxm, m); m=m+1 } 1533 } else { 1534 let p: *NxThreadPool = nf_pool() 1535 var tasks: i64 = M 1536 if tasks > NF_MMARENA_SLOTS { tasks = NF_MMARENA_SLOTS } 1537 if tasks < 1 { tasks = 1 } 1538 let per: i64 = (M + tasks - 1) / tasks 1539 let done0: i64 = nx_pool_n_completed(p) 1540 var b: i64=0 1541 while b<tasks { 1542 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1543 c[0]=x as i64; c[1]=g_nf_xi8m as i64; c[2]=g_nf_sxm as i64; c[3]=in_dim; c[4]=b*per 1544 var hi: i64=(b+1)*per; if hi>M { hi=M } 1545 c[5]=hi 1546 nx_pool_submit(p, _nf_quant_rows_task, c as i64) 1547 b=b+1 1548 } 1549 nx_pool_wait(p, done0+tasks) 1550 } 1551 g_nf_mm_us_quant = sys_now_us() - tq0 1552 return 0 1553} 1554// R0s: the matmul over rows ALREADY quantised into g_nf_xi8m / g_nf_sxm by nf_quant_rows_pool (the engine quantises a 1555// layer input once and calls this for every projection that shares it). Stamps g_nf_mm_us_pool. 1556func mm_pool_i8_mq(M: i64, Wi8: *u8, sw: *i64, dst: *i64, in_dim: i64, out_dim: i64, shift: i64) -> i64 { 1557 let p: *NxThreadPool = nf_pool() 1558 let xi8: *u8 = g_nf_xi8m 1559 let sxm: *i64 = g_nf_sxm 1560 let tq1: i64 = sys_now_us() 1561 var bands: i64 = p.n_workers 1562 if bands > 32 { bands = 32 } 1563 if bands > out_dim { bands = out_dim } 1564 if bands < 1 { bands = 1 } 1565 let per: i64 = (out_dim + bands - 1) / bands 1566 let done0: i64 = nx_pool_n_completed(p) 1567 var b: i64=0 1568 while b<bands { 1569 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1570 c[0]=xi8 as i64; c[1]=sxm as i64; c[2]=Wi8 as i64; c[3]=sw as i64; c[4]=dst as i64; c[5]=in_dim; c[6]=b*per 1571 var hi: i64=(b+1)*per; if hi>out_dim { hi=out_dim } 1572 c[7]=hi; c[8]=shift; c[NF_MMCTX_M]=M; c[NF_MMCTX_OUT]=out_dim 1573 if g_nf_i8_r4m == 1 { c[9]=((g_nf_i8acc4 as i64)+b*128); nx_pool_submit(p, _nfmm_i8_task_m4, c as i64) } 1574 else { c[9]=((g_nf_i8acc as i64)+b*64); nx_pool_submit(p, _nfmm_i8_task_m, c as i64) } 1575 b=b+1 1576 } 1577 nx_pool_wait(p, done0+bands) 1578 g_nf_mm_us_pool = sys_now_us() - tq1 1579 return 0 1580} 1581// R0r: T prompt positions [pos0, pos0+T) through the W8A8 cache with ONE pass per weight matrix per block. Row t's norm, 1582// bias, rope and attention are the per-token step's own calls at pos0+t, in order (row t attends the cache rows the 1583// earlier rows just wrote), so the cache rows and the last row's state are bit-identical to T calls of 1584// decode_step_kv_cached_i8; that sequential path stays as nx_nofloat_prefill_gate's control. xT holds T rows of ne 1585// (the dequantised embeddings); out1 receives the last row's final hidden state, exactly what the per-token step 1586// leaves in out1 for the last position. The scratch bundle is already NSV_MAXT rows deep in every slot. 1587func nf_prefill_batched(buf: *u8, hdr: *NxGgufHeader, xT: *i64, out1: *i64, wc8: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, pos0: i64, T: i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 1588 let ne: i64=cfgA[1]; let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let fd: i64=cfgF[2] 1589 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 1590 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 1591 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64 1592 if T < 1 { return 0 } 1593 cpy(cur, xT, T*ne) 1594 g_nf_pf_us_norm=0; g_nf_pf_us_qkv=0; g_nf_pf_us_attn=0; g_nf_pf_us_o=0; g_nf_pf_us_gu=0; g_nf_pf_us_silu=0; g_nf_pf_us_down=0; g_nf_pf_us_quant=0 1595 var L: i64=0 1596 while L<N { 1597 let wb: *i64 = wc8[L] as *i64 1598 let gA: *i64=wb[0] as *i64 1599 let Wq: *u8=wb[1] as *u8; let swq: *i64=wb[2] as *i64 1600 let Wk: *u8=wb[3] as *u8; let swk: *i64=wb[4] as *i64 1601 let Wv: *u8=wb[5] as *u8; let swv: *i64=wb[6] as *i64 1602 let Wo: *u8=wb[7] as *u8; let swo: *i64=wb[8] as *i64 1603 let gF: *i64=wb[9] as *i64 1604 let Wg: *u8=wb[10] as *u8; let swg: *i64=wb[11] as *i64 1605 let Wu: *u8=wb[12] as *u8; let swu: *i64=wb[13] as *i64 1606 let Wd: *u8=wb[14] as *u8; let swd: *i64=wb[15] as *i64 1607 let bq: *i64=wb[16] as *i64; let bk: *i64=wb[17] as *i64; let bv: *i64=wb[18] as *i64 1608 let tp0: i64 = sys_now_us() 1609 var t: i64=0 1610 while t<T { rmsnorm_gamma_row_q24(cur, gA, t*ne, ne, xn, t*ne); t=t+1 } 1611 let tp1: i64 = sys_now_us() 1612 g_nf_pf_us_norm = g_nf_pf_us_norm + (tp1 - tp0) 1613 // R0s: the normed rows are quantised ONCE and shared by q, k and v 1614 nf_quant_rows_pool(xn, T, ne) 1615 g_nf_pf_us_quant = g_nf_pf_us_quant + g_nf_mm_us_quant 1616 mm_pool_i8_mq(T, Wq, swq, Q, ne, qd, 24) 1617 mm_pool_i8_mq(T, Wk, swk, K, ne, kvd, 24) 1618 mm_pool_i8_mq(T, Wv, swv, V, ne, kvd, 24) 1619 let tp2: i64 = sys_now_us() 1620 g_nf_pf_us_qkv = g_nf_pf_us_qkv + (tp2 - tp1) 1621 t=0 1622 while t<T { 1623 let Qt: *i64 = ((Q as i64)+t*qd*8) as *i64 1624 let Kt: *i64 = ((K as i64)+t*kvd*8) as *i64 1625 let Vt: *i64 = ((V as i64)+t*kvd*8) as *i64 1626 var bo: i64=0; while bo<qd { Qt[bo]=Qt[bo]+bq[bo]; bo=bo+1 } 1627 var bp: i64=0; while bp<kvd { Kt[bp]=Kt[bp]+bk[bp]; Vt[bp]=Vt[bp]+bv[bp]; bp=bp+1 } 1628 var h: i64=0; while h<nh { rope_apply(((Qt as i64)+(h*hd)*8) as *i64, hd, pos0+t, freqs); h=h+1 } 1629 var hk: i64=0; while hk<nkv { rope_apply(((Kt as i64)+(hk*hd)*8) as *i64, hd, pos0+t, freqs); hk=hk+1 } 1630 t=t+1 1631 } 1632 // R0v: the block's rows are biased and rotated above; their attention runs as (row, head) pairs in the pool 1633 nf_attn_kv_block(Q, K, V, kvc, L, pos0, T, cfgA, sc, at, concat) 1634 let tp3: i64 = sys_now_us() 1635 g_nf_pf_us_attn = g_nf_pf_us_attn + (tp3 - tp2) 1636 mm_pool_i8_m(concat, T, Wo, swo, proj, qd, ne, 16) 1637 g_nf_pf_us_quant = g_nf_pf_us_quant + g_nf_mm_us_quant 1638 var ri: i64=0; while ri<T*ne { hmid[ri]=cur[ri]+proj[ri]; ri=ri+1 } 1639 let tp4: i64 = sys_now_us() 1640 g_nf_pf_us_o = g_nf_pf_us_o + (tp4 - tp3) 1641 t=0 1642 while t<T { rmsnorm_gamma_row_q24(hmid, gF, t*ne, ne, xn, t*ne); t=t+1 } 1643 let tp5: i64 = sys_now_us() 1644 g_nf_pf_us_norm = g_nf_pf_us_norm + (tp5 - tp4) 1645 // R0s: quantised once, shared by gate and up 1646 nf_quant_rows_pool(xn, T, ne) 1647 g_nf_pf_us_quant = g_nf_pf_us_quant + g_nf_mm_us_quant 1648 mm_pool_i8_mq(T, Wg, swg, gate, ne, fd, 24) 1649 mm_pool_i8_mq(T, Wu, swu, up, ne, fd, 24) 1650 let tp6: i64 = sys_now_us() 1651 g_nf_pf_us_gu = g_nf_pf_us_gu + (tp6 - tp5) 1652 nf_silu_rows_pool(gate, up, hbuf, T, fd) 1653 let tp7: i64 = sys_now_us() 1654 g_nf_pf_us_silu = g_nf_pf_us_silu + (tp7 - tp6) 1655 mm_pool_i8_m(hbuf, T, Wd, swd, proj, fd, ne, 16) 1656 g_nf_pf_us_quant = g_nf_pf_us_quant + g_nf_mm_us_quant 1657 var fo: i64=0; while fo<T*ne { cur[fo]=hmid[fo]+proj[fo]; fo=fo+1 } 1658 g_nf_pf_us_down = g_nf_pf_us_down + (sys_now_us() - tp7) 1659 L=L+1 1660 } 1661 cpy(out1, ((cur as i64)+(T-1)*ne*8) as *i64, ne) 1662 return 0 1663} 1664// decode ONE token via the W8A8 SIMD weight cache. Same structure as decode_step_kv_cached; the 7 projection 1665// matmuls go through mm_pool_i8 (13x faster, ~0.8% lossy). Attention/softmax/residual stay exact i64. 1666func decode_step_kv_cached_i8(buf: *u8, hdr: *NxGgufHeader, x1: *i64, out1: *i64, wc8: *i64, sb: *i64, nm: *u8, freqs: *i64, kvc: *i64, pos: i64, cfgA: *i64, cfgF: *i64, N: i64) -> i64 { 1667 let ne: i64=cfgA[1]; let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7]; let fd: i64=cfgF[2] 1668 let xn: *i64=sb[0] as *i64; let Q: *i64=sb[1] as *i64; let K: *i64=sb[2] as *i64; let V: *i64=sb[3] as *i64; let concat: *i64=sb[4] as *i64 1669 let sc: *i64=sb[5] as *i64; let at: *i64=sb[6] as *i64; let proj: *i64=sb[7] as *i64; let gate: *i64=sb[8] as *i64; let up: *i64=sb[9] as *i64 1670 let hbuf: *i64=sb[10] as *i64; let hmid: *i64=sb[11] as *i64; let cur: *i64=sb[12] as *i64 1671 cpy(cur, x1, ne) 1672 var L: i64=0 1673 while L<N { 1674 let wb: *i64 = wc8[L] as *i64 1675 let gA: *i64=wb[0] as *i64 1676 let Wq: *u8=wb[1] as *u8; let swq: *i64=wb[2] as *i64 1677 let Wk: *u8=wb[3] as *u8; let swk: *i64=wb[4] as *i64 1678 let Wv: *u8=wb[5] as *u8; let swv: *i64=wb[6] as *i64 1679 let Wo: *u8=wb[7] as *u8; let swo: *i64=wb[8] as *i64 1680 let gF: *i64=wb[9] as *i64 1681 let Wg: *u8=wb[10] as *u8; let swg: *i64=wb[11] as *i64 1682 let Wu: *u8=wb[12] as *u8; let swu: *i64=wb[13] as *i64 1683 let Wd: *u8=wb[14] as *u8; let swd: *i64=wb[15] as *i64 1684 let bq: *i64=wb[16] as *i64; let bk: *i64=wb[17] as *i64; let bv: *i64=wb[18] as *i64 1685 rmsnorm_gamma_row_q24(cur, gA, 0, ne, xn, 0) 1686 mm_pool_i8(xn, Wq, swq, Q, ne, qd, 24) 1687 mm_pool_i8(xn, Wk, swk, K, ne, kvd, 24) 1688 mm_pool_i8(xn, Wv, swv, V, ne, kvd, 24) 1689 var bo: i64=0; while bo<qd { Q[bo]=Q[bo]+bq[bo]; bo=bo+1 } 1690 var bp: i64=0; while bp<kvd { K[bp]=K[bp]+bk[bp]; V[bp]=V[bp]+bv[bp]; bp=bp+1 } 1691 var h: i64=0; while h<nh { rope_apply(((Q as i64)+(h*hd)*8) as *i64, hd, pos, freqs); h=h+1 } 1692 var hk: i64=0; while hk<nkv { rope_apply(((K as i64)+(hk*hd)*8) as *i64, hd, pos, freqs); hk=hk+1 } 1693 nf_attn_kv_core(Q, K, V, kvc, L, pos, cfgA, sc, at, concat) 1694 mm_pool_i8(concat, Wo, swo, proj, qd, ne, 16) 1695 var ri: i64=0; while ri<ne { hmid[ri]=cur[ri]+proj[ri]; ri=ri+1 } 1696 rmsnorm_gamma_row_q24(hmid, gF, 0, ne, xn, 0) 1697 mm_pool_i8(xn, Wg, swg, gate, ne, fd, 24) 1698 mm_pool_i8(xn, Wu, swu, up, ne, fd, 24) 1699 var fi: i64=0; while fi<fd { hbuf[fi]=qmul(silu(gate[fi]), up[fi]); fi=fi+1 } 1700 mm_pool_i8(hbuf, Wd, swd, proj, fd, ne, 16) 1701 var fo: i64=0; while fo<ne { cur[fo]=hmid[fo]+proj[fo]; fo=fo+1 } 1702 L=L+1 1703 } 1704 cpy(out1, cur, ne) 1705 return 0 1706} 1707 1708// ==== SHARED ATTENTION CORE (extracted 2026-09-02 for the LM4 resident-Q4_K decoder) ========================== 1709// Exactly the arithmetic decode_step_kv_cached_i8 carried inline: write K/V for pos into the cache of layer L, 1710// then per head score over 0..pos, Q16 softmax, weighted V sum -> concat. Exact i64 throughout; only the 1711// projections around it are lossy. ONE copy, so the i8 and Q4_K decoders cannot disagree on attention. 1712// R0v: ONE per-head attention routine -- the serial control and every pool task call it, so the arithmetic cannot differ. 1713// scores over cnt cached positions, fixed-point softmax, weighted V sum into the head's slice of concat. 1714func nf_attn_head(Q: *i64, Kc: *i64, Vc: *i64, concat: *i64, hh: i64, cnt: i64, hd: i64, kvd: i64, group: i64, scale: i64, sc: *i64, at: *i64) -> i64 { 1715 let kvh: i64=hh/group; let qb: i64=hh*hd; let kb: i64=kvh*hd 1716 var s: i64=0 1717 while s<cnt { var dot: i64=0; var d: i64=0; while d<hd { dot=dot+(Q[qb+d]*Kc[s*kvd+kb+d]); d=d+1 } sc[s]=qmul(dot>>16, scale); s=s+1 } 1718 var mmax: i64=sc[0]; var j: i64=1; while j<cnt { if sc[j]>mmax { mmax=sc[j] } j=j+1 } 1719 var sum: i64=0; j=0; while j<cnt { let e: i64=fx_exp(sc[j]-mmax); at[j]=e; sum=sum+e; j=j+1 } 1720 if sum<=0 { sum=1 } 1721 j=0; while j<cnt { at[j]=(at[j]<<16)/sum; j=j+1 } 1722 var d2: i64=0 1723 while d2<hd { var acc: i64=0; s=0; while s<cnt { acc=acc+(at[s]*Vc[s*kvd+kb+d2]); s=s+1 } concat[qb+d2]=acc>>16; d2=d2+1 } 1724 return 0 1725} 1726// R0v: per-task scratch -- slot b owns two runs of cntmax i64 (scores, weights) inside one arena grown on demand. 1727func nf_attn_scratch(cntmax: i64) -> i64 { 1728 let need: i64 = NF_MMARENA_SLOTS * 2 * cntmax * 8 1729 if need > g_nf_attn_sc_cap { if g_nf_attn_sc_cap > 0 { sys_munmap(g_nf_attn_sc as *u8, g_nf_attn_sc_cap) } g_nf_attn_sc = sys_mmap(need) as *i64; g_nf_attn_sc_cap = need } 1730 return 0 1731} 1732// R0v: a task owns a range of (row, head) pairs p in [c[10], c[11]): row = p / nh, head = p mod nh; row r attends to 1733// pos0 + r + 1 cached positions (the causal count), reading its Q row and writing its concat row. 1734func _nf_attn_pairs_task(ctx_i: i64) -> i64 { 1735 let c: *i64 = ctx_i as *i64 1736 let Q: i64=c[0]; let Kc: *i64=c[1] as *i64; let Vc: *i64=c[2] as *i64; let cat: i64=c[3] 1737 let nh: i64=c[4]; let pos0: i64=c[5]; let hd: i64=c[6]; let kvd: i64=c[7]; let group: i64=c[8]; let scale: i64=c[9] 1738 var p: i64=c[10]; let pe: i64=c[11]; let qd: i64=c[12]; let sc: *i64=c[13] as *i64; let at: *i64=c[14] as *i64 1739 while p<pe { 1740 let row: i64 = p / nh 1741 let hh: i64 = p - row*nh 1742 nf_attn_head((Q + row*qd*8) as *i64, Kc, Vc, (cat + row*qd*8) as *i64, hh, pos0+row+1, hd, kvd, group, scale, sc, at) 1743 p=p+1 1744 } 1745 return 0 1746} 1747// R0v: T x nh (row, head) pairs over the arena's slots; the single-token core is the T=1 case. 1748func nf_attn_pairs_pool(Q: *i64, Kc: *i64, Vc: *i64, concat: *i64, nh: i64, pos0: i64, T: i64, hd: i64, kvd: i64, group: i64, scale: i64, qd: i64) -> i64 { 1749 let p: *NxThreadPool = nf_pool() 1750 let cntmax: i64 = pos0 + T 1751 nf_attn_scratch(cntmax) 1752 let npairs: i64 = T*nh 1753 var tasks: i64 = npairs 1754 if tasks > NF_MMARENA_SLOTS { tasks = NF_MMARENA_SLOTS } 1755 if tasks < 1 { tasks = 1 } 1756 let per: i64 = (npairs + tasks - 1) / tasks 1757 let done0: i64 = nx_pool_n_completed(p) 1758 var b: i64=0 1759 while b<tasks { 1760 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1761 c[0]=Q as i64; c[1]=Kc as i64; c[2]=Vc as i64; c[3]=concat as i64; c[4]=nh; c[5]=pos0; c[6]=hd; c[7]=kvd; c[8]=group; c[9]=scale 1762 c[10]=b*per 1763 var pe: i64=(b+1)*per; if pe>npairs { pe=npairs } 1764 c[11]=pe; c[12]=qd 1765 c[13]=((g_nf_attn_sc as i64) + (b*2)*cntmax*8) 1766 c[14]=((g_nf_attn_sc as i64) + (b*2+1)*cntmax*8) 1767 nx_pool_submit(p, _nf_attn_pairs_task, c as i64) 1768 b=b+1 1769 } 1770 nx_pool_wait(p, done0+tasks) 1771 return 0 1772} 1773// attention for ONE token at position pos: write its K/V row into the layer's cache, then every head (serial control or 1774// pool tasks); the signature is unchanged so decode_step_kv_cached_i8 and the sequential prefill are untouched. 1775func nf_attn_kv_core(Q: *i64, K: *i64, V: *i64, kvc: *i64, L: i64, pos: i64, cfgA: *i64, sc: *i64, at: *i64, concat: *i64) -> i64 { 1776 let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7] 1777 let Kc: *i64 = kvc[2*L] as *i64 1778 let Vc: *i64 = kvc[2*L+1] as *i64 1779 var ci: i64=0; while ci<kvd { Kc[pos*kvd+ci]=K[ci]; Vc[pos*kvd+ci]=V[ci]; ci=ci+1 } 1780 let group: i64=nh/nkv 1781 if g_nf_attn_serial == 1 { 1782 var hh: i64=0 1783 while hh<nh { nf_attn_head(Q, Kc, Vc, concat, hh, pos+1, hd, kvd, group, scale, sc, at); hh=hh+1 } 1784 return 0 1785 } 1786 return nf_attn_pairs_pool(Q, Kc, Vc, concat, nh, pos, 1, hd, kvd, group, scale, qd) 1787} 1788// R0v: attention for a prefill block of T rows at positions pos0..pos0+T-1: all K/V rows into the cache first, then every 1789// (row, head) pair -- row r reads pos0+r+1 positions, exactly what the per-token loop read when it reached r. 1790func nf_attn_kv_block(Q: *i64, K: *i64, V: *i64, kvc: *i64, L: i64, pos0: i64, T: i64, cfgA: *i64, sc: *i64, at: *i64, concat: *i64) -> i64 { 1791 let nh: i64=cfgA[2]; let nkv: i64=cfgA[3]; let hd: i64=cfgA[4]; let qd: i64=cfgA[5]; let kvd: i64=cfgA[6]; let scale: i64=cfgA[7] 1792 let Kc: *i64 = kvc[2*L] as *i64 1793 let Vc: *i64 = kvc[2*L+1] as *i64 1794 var t: i64=0 1795 while t<T { var ci: i64=0; while ci<kvd { Kc[(pos0+t)*kvd+ci]=K[t*kvd+ci]; Vc[(pos0+t)*kvd+ci]=V[t*kvd+ci]; ci=ci+1 } t=t+1 } 1796 let group: i64=nh/nkv 1797 if g_nf_attn_serial == 1 { 1798 t=0 1799 while t<T { 1800 var hh: i64=0 1801 while hh<nh { nf_attn_head(((Q as i64)+t*qd*8) as *i64, Kc, Vc, ((concat as i64)+t*qd*8) as *i64, hh, pos0+t+1, hd, kvd, group, scale, sc, at); hh=hh+1 } 1802 t=t+1 1803 } 1804 return 0 1805 } 1806 return nf_attn_pairs_pool(Q, Kc, Vc, concat, nh, pos0, T, hd, kvd, group, scale, qd) 1807} 1808// R0v: the silu/qmul rows of a prefill block as pool tasks (elementwise, so any split is bit-identical). 1809func _nf_silu_rows_task(ctx_i: i64) -> i64 { 1810 let c: *i64 = ctx_i as *i64 1811 let gate: *i64=c[0] as *i64; let up: *i64=c[1] as *i64; let hb: *i64=c[2] as *i64 1812 var i: i64=c[3]; let e: i64=c[4] 1813 while i<e { hb[i]=qmul(silu(gate[i]), up[i]); i=i+1 } 1814 return 0 1815} 1816func nf_silu_rows_pool(gate: *i64, up: *i64, hbuf: *i64, T: i64, fd: i64) -> i64 { 1817 let p: *NxThreadPool = nf_pool() 1818 let n: i64 = T*fd 1819 var tasks: i64 = T 1820 if tasks > NF_MMARENA_SLOTS { tasks = NF_MMARENA_SLOTS } 1821 if tasks < 1 { tasks = 1 } 1822 let per: i64 = (n + tasks - 1) / tasks 1823 let done0: i64 = nx_pool_n_completed(p) 1824 var b: i64=0 1825 while b<tasks { 1826 let c: *i64 = ((g_nf_mmarena as i64) + b*128) as *i64 1827 c[0]=gate as i64; c[1]=up as i64; c[2]=hbuf as i64; c[3]=b*per 1828 var e: i64=(b+1)*per; if e>n { e=n } 1829 c[4]=e 1830 nx_pool_submit(p, _nf_silu_rows_task, c as i64) 1831 b=b+1 1832 } 1833 nx_pool_wait(p, done0+tasks) 1834 return 0 1835}