nx_bert_ce_lib.nx source
↩ module page · 346 lines · 17919 B
1// nx_bert_ce_lib.nx -- SOVEREIGN BERT cross-encoder (BertForSequenceClassification with one logit) composed from
2// the estate's own f32 tower: nx_safetensors_load (weights), vit_mhsa and vit_mhsa_pool (multi-head attention
3// with biases, the ViT block's kernel), nx_f32_layernorm, nx_f32_matmul_t and its pooled twin, nx_f32_gelu_vec and
4// nx_f32_tanh. The model is a LICENSED DATA ASSET (Apache-2.0, mirrored with provenance under knowledge/fetched/)
5// read by NishiLang from the first byte; nothing foreign executes. BERT differs from the ViT block already in the
6// tree in three places and only those are written here: POST-norm residuals (LayerNorm after each add), an
7// embedding layer of word + absolute position + token type followed by LayerNorm, and the pooler (tanh of a dense
8// over the [CLS] row) with the classifier. Depth, width, heads, intermediate size, position count and the LayerNorm
9// epsilon are read from the model's config.json at load, never from a constant. Search rung R0, 2026-09-14.
10// license_tier: ORIGINAL
11import "nx_syscalls.nx"
12import "nx_f32.nx"
13import "nx_f32_cvt.nx"
14import "nx_f32_div.nx"
15import "nx_f32_activations.nx"
16import "nx_f32_matmul_t.nx"
17import "nx_f32_layernorm.nx"
18import "nx_f32_gelu.nx"
19import "nx_thread_pool.nx"
20import "nx_safetensors_load.nx"
21import "nx_vit_encoder_layer.nx"
22
23// g-box slots; the caller mmaps BC_G_BYTES
24const BC_G_HID: i64 = 0
25const BC_G_LAYERS: i64 = 1
26const BC_G_HEADS: i64 = 2
27const BC_G_INTER: i64 = 3
28const BC_G_MAXPOS: i64 = 4
29const BC_G_VOCAB: i64 = 5
30const BC_G_EPS: i64 = 6 // f32 bits, from config layer_norm_eps
31const BC_G_SCALE: i64 = 7 // f32 bits, 1 / sqrt(head_dim)
32const BC_G_WEMB: i64 = 8
33const BC_G_PEMB: i64 = 9
34const BC_G_TEMB: i64 = 10
35const BC_G_ELNG: i64 = 11
36const BC_G_ELNB: i64 = 12
37const BC_G_LAYERW: i64 = 13 // *i64, BC_L_SLOTS pointers per layer
38const BC_G_POOLW: i64 = 14
39const BC_G_POOLB: i64 = 15
40const BC_G_CLSW: i64 = 16
41const BC_G_CLSB: i64 = 17
42const BC_G_LOADED: i64 = 18
43const BC_G_MISSING: i64 = 19 // *u8 name of the first tensor or field that failed, for the announce
44const BC_G_POOL: i64 = 20 // *NxThreadPool, or 0 for the serial path
45const BC_G_SLOTS: i64 = 21
46const BC_G_BYTES: i64 = 168
47// per-layer weight slots (HF BertLayer tensor set)
48const BC_L_WQ: i64 = 0
49const BC_L_BQ: i64 = 1
50const BC_L_WK: i64 = 2
51const BC_L_BK: i64 = 3
52const BC_L_WV: i64 = 4
53const BC_L_BV: i64 = 5
54const BC_L_WO: i64 = 6
55const BC_L_BO: i64 = 7
56const BC_L_LN1G: i64 = 8
57const BC_L_LN1B: i64 = 9
58const BC_L_W1: i64 = 10
59const BC_L_B1: i64 = 11
60const BC_L_W2: i64 = 12
61const BC_L_B2: i64 = 13
62const BC_L_LN2G: i64 = 14
63const BC_L_LN2B: i64 = 15
64const BC_L_SLOTS: i64 = 16
65// the longest tensor name in this family is under 70 bytes; bc_lname REFUSES a name that would not fit
66const BC_NAME_CAP: i64 = 256
67const BC_DTYPE_CAP: i64 = 16
68const BC_DECIMAL: i64 = 10
69const BC_ASCII_ZERO: i64 = 48
70const BC_ASCII_NINE: i64 = 57
71const BC_COLON: i64 = 58
72const BC_SPACE: i64 = 32
73const BC_MINUS: i64 = 45
74const BC_PLUS: i64 = 43
75const BC_DOT: i64 = 46
76const BC_LOWER_E: i64 = 101
77const BC_UPPER_E: i64 = 69
78const BC_MILLI: i64 = 1000
79const BC_MICRO: i64 = 1000000
80
81// index of the first value byte after a JSON key, or -1
82func bc_skip_to_value(buf: *u8, n: i64, key: *u8) -> i64 {
83 let p: i64 = stl_find(buf, n, 0, key)
84 if p < 0 { return 0 - 1 }
85 var i: i64 = p
86 var go: i64 = 1
87 while go == 1 { if i >= n { go = 0 } else { if buf[i] == (BC_COLON as u8) { go = 0 } else { i = i + 1 } } }
88 if i >= n { return 0 - 1 }
89 i = i + 1
90 while i < n { if buf[i] == (BC_SPACE as u8) { i = i + 1 } else { return i } }
91 return 0 - 1
92}
93
94func bc_cfg_int(buf: *u8, n: i64, key: *u8) -> i64 {
95 let i: i64 = bc_skip_to_value(buf, n, key)
96 if i < 0 { return 0 - 1 }
97 let e: *i64 = sys_mmap(8) as *i64
98 return stl_parse_int(buf, i, e)
99}
100
101func bc_is_digit(c: i64) -> i64 { if c >= BC_ASCII_ZERO { if c <= BC_ASCII_NINE { return 1 } } return 0 }
102
103// a decimal with optional fraction and exponent (1e-12, 1e-05, 0.00001) as f32 bits, or -1 when absent
104func bc_cfg_f32(buf: *u8, n: i64, key: *u8) -> i64 {
105 var i: i64 = bc_skip_to_value(buf, n, key)
106 if i < 0 { return 0 - 1 }
107 var mant: i64 = 0
108 var fracd: i64 = 0
109 var exp10: i64 = 0
110 while bc_is_digit(buf[i] as i64) == 1 { mant = mant * BC_DECIMAL + ((buf[i] as i64) - BC_ASCII_ZERO); i = i + 1 }
111 if buf[i] == (BC_DOT as u8) {
112 i = i + 1
113 while bc_is_digit(buf[i] as i64) == 1 { mant = mant * BC_DECIMAL + ((buf[i] as i64) - BC_ASCII_ZERO); fracd = fracd + 1; i = i + 1 }
114 }
115 var ise: i64 = 0
116 if buf[i] == (BC_LOWER_E as u8) { ise = 1 }
117 if buf[i] == (BC_UPPER_E as u8) { ise = 1 }
118 if ise == 1 {
119 i = i + 1
120 var neg: i64 = 0
121 if buf[i] == (BC_MINUS as u8) { neg = 1; i = i + 1 }
122 if buf[i] == (BC_PLUS as u8) { i = i + 1 }
123 var ex: i64 = 0
124 while bc_is_digit(buf[i] as i64) == 1 { ex = ex * BC_DECIMAL + ((buf[i] as i64) - BC_ASCII_ZERO); i = i + 1 }
125 if neg == 1 { ex = 0 - ex }
126 exp10 = ex
127 }
128 var k: i64 = exp10 - fracd
129 var f: i64 = nx_i32_to_f32(mant)
130 let ten: i64 = nx_i32_to_f32(BC_DECIMAL)
131 while k < 0 { f = nx_f32_div(f, ten); k = k + 1 }
132 while k > 0 { f = nx_f32_mul(f, ten); k = k - 1 }
133 return f
134}
135
136// one tensor into a fresh i64-per-element buffer; 0 when absent or of an unreadable dtype
137func bc_tensor(buf: *u8, hlen: i64, dstart: i64, name: *u8, dt: *u8, offs: *i64) -> i64 {
138 if stl_tensor(buf, hlen, name, dt, offs) == 0 { return 0 }
139 let nbytes: i64 = offs[1] - offs[0]
140 if nbytes <= 0 { return 0 }
141 // one i64 per element; the smallest element the loader reads is 2 bytes, so 4x the byte count covers every dtype
142 let out: *i64 = sys_mmap(nbytes * 4) as *i64
143 if stl_read_f32(buf, dstart, offs, dt, out) < 0 { return 0 }
144 return out as i64
145}
146
147func bc_cat(dst: *u8, pos: i64, s: *u8) -> i64 {
148 var i: i64 = 0
149 while s[i] != (0 as u8) { if pos + i < BC_NAME_CAP - 1 { dst[pos + i] = s[i] } i = i + 1 }
150 return pos + i
151}
152
153// bert.encoder.layer.<l>.<suffix> into scr; 1 if it fit, 0 if it would overflow the scratch
154func bc_lname(scr: *u8, layer: i64, suffix: *u8) -> i64 {
155 var p: i64 = bc_cat(scr, 0, "bert.encoder.layer." as *u8)
156 let tens: i64 = layer / BC_DECIMAL
157 if tens > 0 { if p < BC_NAME_CAP - 1 { scr[p] = (tens + BC_ASCII_ZERO) as u8 } p = p + 1 }
158 if p < BC_NAME_CAP - 1 { scr[p] = ((layer - tens * BC_DECIMAL) + BC_ASCII_ZERO) as u8 }
159 p = p + 1
160 p = bc_cat(scr, p, "." as *u8)
161 p = bc_cat(scr, p, suffix)
162 if p >= BC_NAME_CAP - 1 { return 0 }
163 scr[p] = 0 as u8
164 return 1
165}
166
167// record the failing name durably (scr is reused) and return 0
168func bc_fail(g: *i64, name: *u8) -> i64 {
169 let keep: *u8 = sys_mmap(BC_NAME_CAP)
170 var i: i64 = 0
171 while name[i] != (0 as u8) { if i < BC_NAME_CAP - 1 { keep[i] = name[i] } i = i + 1 }
172 if i > BC_NAME_CAP - 1 { i = BC_NAME_CAP - 1 }
173 keep[i] = 0 as u8
174 g[BC_G_MISSING] = keep as i64
175 g[BC_G_LOADED] = 0
176 return 0
177}
178
179// one per-layer tensor into lw[layer*BC_L_SLOTS + slot]; 1 ok, 0 failed (the name is left in scr)
180func bc_lload(lw: *i64, mb: *u8, hlen: i64, dstart: i64, scr: *u8, layer: i64, slot: i64, suffix: *u8, dt: *u8, offs: *i64) -> i64 {
181 if bc_lname(scr, layer, suffix) == 0 { return 0 }
182 let p: i64 = bc_tensor(mb, hlen, dstart, scr, dt, offs)
183 if p == 0 { return 0 }
184 lw[layer * BC_L_SLOTS + slot] = p
185 return 1
186}
187
188// load config.json + model.safetensors; 1 loaded, 0 refused with g[BC_G_MISSING] naming what was absent
189func bc_load(g: *i64, model_path: *u8, config_path: *u8) -> i64 {
190 g[BC_G_LOADED] = 0
191 g[BC_G_MISSING] = ("none" as *u8) as i64
192 let lb: *i64 = sys_mmap(8) as *i64
193 let cb: *u8 = sys_read_file(config_path, lb)
194 if lb[0] <= 0 { return bc_fail(g, "config.json" as *u8) }
195 let cn: i64 = lb[0]
196 let hid: i64 = bc_cfg_int(cb, cn, "\"hidden_size\"" as *u8)
197 let layers: i64 = bc_cfg_int(cb, cn, "\"num_hidden_layers\"" as *u8)
198 let heads: i64 = bc_cfg_int(cb, cn, "\"num_attention_heads\"" as *u8)
199 let inter: i64 = bc_cfg_int(cb, cn, "\"intermediate_size\"" as *u8)
200 let maxpos: i64 = bc_cfg_int(cb, cn, "\"max_position_embeddings\"" as *u8)
201 let vocab: i64 = bc_cfg_int(cb, cn, "\"vocab_size\"" as *u8)
202 let epsb: i64 = bc_cfg_f32(cb, cn, "\"layer_norm_eps\"" as *u8)
203 if hid < 1 { return bc_fail(g, "config hidden_size" as *u8) }
204 if layers < 1 { return bc_fail(g, "config num_hidden_layers" as *u8) }
205 if heads < 1 { return bc_fail(g, "config num_attention_heads" as *u8) }
206 if inter < 1 { return bc_fail(g, "config intermediate_size" as *u8) }
207 if maxpos < 1 { return bc_fail(g, "config max_position_embeddings" as *u8) }
208 if vocab < 1 { return bc_fail(g, "config vocab_size" as *u8) }
209 if epsb < 0 { return bc_fail(g, "config layer_norm_eps" as *u8) }
210 let hd: i64 = hid / heads
211 if hd * heads != hid { return bc_fail(g, "config hidden_size not divisible by heads" as *u8) }
212 let one: i64 = nx_i32_to_f32(1)
213 g[BC_G_HID] = hid
214 g[BC_G_LAYERS] = layers
215 g[BC_G_HEADS] = heads
216 g[BC_G_INTER] = inter
217 g[BC_G_MAXPOS] = maxpos
218 g[BC_G_VOCAB] = vocab
219 g[BC_G_EPS] = epsb
220 g[BC_G_SCALE] = nx_f32_div(one, nx_f32_sqrt(nx_i32_to_f32(hd)))
221 let mb: *u8 = sys_read_file(model_path, lb)
222 if lb[0] <= 0 { return bc_fail(g, "model.safetensors" as *u8) }
223 let hlen: i64 = stl_header_len(mb)
224 let dstart: i64 = stl_data_start(mb)
225 if hlen <= 0 { return bc_fail(g, "safetensors header" as *u8) }
226 if dstart > lb[0] { return bc_fail(g, "safetensors header" as *u8) }
227 let dt: *u8 = sys_mmap(BC_DTYPE_CAP)
228 let offs: *i64 = sys_mmap(16) as *i64
229 g[BC_G_WEMB] = bc_tensor(mb, hlen, dstart, "bert.embeddings.word_embeddings.weight" as *u8, dt, offs)
230 if g[BC_G_WEMB] == 0 { return bc_fail(g, "bert.embeddings.word_embeddings.weight" as *u8) }
231 g[BC_G_PEMB] = bc_tensor(mb, hlen, dstart, "bert.embeddings.position_embeddings.weight" as *u8, dt, offs)
232 if g[BC_G_PEMB] == 0 { return bc_fail(g, "bert.embeddings.position_embeddings.weight" as *u8) }
233 g[BC_G_TEMB] = bc_tensor(mb, hlen, dstart, "bert.embeddings.token_type_embeddings.weight" as *u8, dt, offs)
234 if g[BC_G_TEMB] == 0 { return bc_fail(g, "bert.embeddings.token_type_embeddings.weight" as *u8) }
235 g[BC_G_ELNG] = bc_tensor(mb, hlen, dstart, "bert.embeddings.LayerNorm.weight" as *u8, dt, offs)
236 if g[BC_G_ELNG] == 0 { return bc_fail(g, "bert.embeddings.LayerNorm.weight" as *u8) }
237 g[BC_G_ELNB] = bc_tensor(mb, hlen, dstart, "bert.embeddings.LayerNorm.bias" as *u8, dt, offs)
238 if g[BC_G_ELNB] == 0 { return bc_fail(g, "bert.embeddings.LayerNorm.bias" as *u8) }
239 let lw: *i64 = sys_mmap(layers * BC_L_SLOTS * 8) as *i64
240 let scr: *u8 = sys_mmap(BC_NAME_CAP)
241 var l: i64 = 0
242 while l < layers {
243 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_WQ, "attention.self.query.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
244 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_BQ, "attention.self.query.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
245 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_WK, "attention.self.key.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
246 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_BK, "attention.self.key.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
247 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_WV, "attention.self.value.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
248 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_BV, "attention.self.value.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
249 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_WO, "attention.output.dense.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
250 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_BO, "attention.output.dense.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
251 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_LN1G, "attention.output.LayerNorm.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
252 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_LN1B, "attention.output.LayerNorm.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
253 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_W1, "intermediate.dense.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
254 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_B1, "intermediate.dense.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
255 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_W2, "output.dense.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
256 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_B2, "output.dense.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
257 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_LN2G, "output.LayerNorm.weight" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
258 if bc_lload(lw, mb, hlen, dstart, scr, l, BC_L_LN2B, "output.LayerNorm.bias" as *u8, dt, offs) == 0 { return bc_fail(g, scr) }
259 l = l + 1
260 }
261 g[BC_G_LAYERW] = lw as i64
262 g[BC_G_POOLW] = bc_tensor(mb, hlen, dstart, "bert.pooler.dense.weight" as *u8, dt, offs)
263 if g[BC_G_POOLW] == 0 { return bc_fail(g, "bert.pooler.dense.weight" as *u8) }
264 g[BC_G_POOLB] = bc_tensor(mb, hlen, dstart, "bert.pooler.dense.bias" as *u8, dt, offs)
265 if g[BC_G_POOLB] == 0 { return bc_fail(g, "bert.pooler.dense.bias" as *u8) }
266 g[BC_G_CLSW] = bc_tensor(mb, hlen, dstart, "classifier.weight" as *u8, dt, offs)
267 if g[BC_G_CLSW] == 0 { return bc_fail(g, "classifier.weight" as *u8) }
268 g[BC_G_CLSB] = bc_tensor(mb, hlen, dstart, "classifier.bias" as *u8, dt, offs)
269 if g[BC_G_CLSB] == 0 { return bc_fail(g, "classifier.bias" as *u8) }
270 g[BC_G_LOADED] = 1
271 return 1
272}
273
274// the relevance logit (f32 bits) for a tokenised pair ids[0..T) with token types; pooled when g[BC_G_POOL] is set
275func bc_forward(g: *i64, ids: *i64, types: *i64, T: i64) -> i64 {
276 if g[BC_G_LOADED] != 1 { return 0 }
277 let D: i64 = g[BC_G_HID]
278 let L: i64 = g[BC_G_LAYERS]
279 let nh: i64 = g[BC_G_HEADS]
280 let I: i64 = g[BC_G_INTER]
281 let hd: i64 = D / nh
282 let eps: i64 = g[BC_G_EPS]
283 let scale: i64 = g[BC_G_SCALE]
284 let wemb: *i64 = g[BC_G_WEMB] as *i64
285 let pemb: *i64 = g[BC_G_PEMB] as *i64
286 let temb: *i64 = g[BC_G_TEMB] as *i64
287 let e: *i64 = sys_mmap(8 * T * D) as *i64
288 let x: *i64 = sys_mmap(8 * T * D) as *i64
289 var t: i64 = 0
290 while t < T {
291 let id: i64 = ids[t]
292 let ty: i64 = types[t]
293 var d: i64 = 0
294 while d < D { e[t * D + d] = __f32_add(__f32_add(wemb[id * D + d], pemb[t * D + d]), temb[ty * D + d]); d = d + 1 }
295 t = t + 1
296 }
297 nx_f32_layernorm(e, g[BC_G_ELNG] as *i64, g[BC_G_ELNB] as *i64, T, D, eps, x)
298 let attn: *i64 = sys_mmap(8 * T * D) as *i64
299 let h: *i64 = sys_mmap(8 * T * D) as *i64
300 let hn: *i64 = sys_mmap(8 * T * D) as *i64
301 let f1: *i64 = sys_mmap(8 * T * I) as *i64
302 let m2: *i64 = sys_mmap(8 * T * D) as *i64
303 let y: *i64 = sys_mmap(8 * T * D) as *i64
304 let lw: *i64 = g[BC_G_LAYERW] as *i64
305 let pool: *NxThreadPool = g[BC_G_POOL] as *NxThreadPool
306 let pooled: i64 = g[BC_G_POOL]
307 var l: i64 = 0
308 while l < L {
309 let b: i64 = l * BC_L_SLOTS
310 if pooled != 0 {
311 vit_mhsa_pool(pool, x, lw[b + BC_L_WQ] as *i64, lw[b + BC_L_BQ] as *i64, lw[b + BC_L_WK] as *i64, lw[b + BC_L_BK] as *i64, lw[b + BC_L_WV] as *i64, lw[b + BC_L_BV] as *i64, lw[b + BC_L_WO] as *i64, lw[b + BC_L_BO] as *i64, T, D, nh, hd, scale, attn)
312 } else {
313 vit_mhsa(x, lw[b + BC_L_WQ] as *i64, lw[b + BC_L_BQ] as *i64, lw[b + BC_L_WK] as *i64, lw[b + BC_L_BK] as *i64, lw[b + BC_L_WV] as *i64, lw[b + BC_L_BV] as *i64, lw[b + BC_L_WO] as *i64, lw[b + BC_L_BO] as *i64, T, D, nh, hd, scale, attn)
314 }
315 var i: i64 = 0
316 while i < T * D { h[i] = __f32_add(x[i], attn[i]); i = i + 1 }
317 nx_f32_layernorm(h, lw[b + BC_L_LN1G] as *i64, lw[b + BC_L_LN1B] as *i64, T, D, eps, hn)
318 if pooled != 0 { nx_f32_matmul_t_pool(pool, hn, lw[b + BC_L_W1] as *i64, f1, T, D, I) } else { nx_f32_matmul_t(hn, lw[b + BC_L_W1] as *i64, f1, T, D, I) }
319 vit_bias_add(f1, lw[b + BC_L_B1] as *i64, T, I)
320 nx_f32_gelu_vec(f1, T * I)
321 if pooled != 0 { nx_f32_matmul_t_pool(pool, f1, lw[b + BC_L_W2] as *i64, m2, T, I, D) } else { nx_f32_matmul_t(f1, lw[b + BC_L_W2] as *i64, m2, T, I, D) }
322 vit_bias_add(m2, lw[b + BC_L_B2] as *i64, T, D)
323 i = 0
324 while i < T * D { y[i] = __f32_add(hn[i], m2[i]); i = i + 1 }
325 nx_f32_layernorm(y, lw[b + BC_L_LN2G] as *i64, lw[b + BC_L_LN2B] as *i64, T, D, eps, x)
326 l = l + 1
327 }
328 // pooler over the [CLS] row (row 0 of x), then the classifier
329 let p: *i64 = sys_mmap(8 * D) as *i64
330 nx_f32_matmul_t(x, g[BC_G_POOLW] as *i64, p, 1, D, D)
331 vit_bias_add(p, g[BC_G_POOLB] as *i64, 1, D)
332 var pd: i64 = 0
333 while pd < D { p[pd] = nx_f32_tanh(p[pd]); pd = pd + 1 }
334 let lg: *i64 = sys_mmap(8) as *i64
335 nx_f32_matmul_t(p, g[BC_G_CLSW] as *i64, lg, 1, D, 1)
336 let clsb: *i64 = g[BC_G_CLSB] as *i64
337 let logit: i64 = __f32_add(lg[0], clsb[0])
338 sys_munmap(e as *u8, 8 * T * D); sys_munmap(x as *u8, 8 * T * D); sys_munmap(attn as *u8, 8 * T * D)
339 sys_munmap(h as *u8, 8 * T * D); sys_munmap(hn as *u8, 8 * T * D); sys_munmap(f1 as *u8, 8 * T * I)
340 sys_munmap(m2 as *u8, 8 * T * D); sys_munmap(y as *u8, 8 * T * D); sys_munmap(p as *u8, 8 * D); sys_munmap(lg as *u8, 8)
341 return logit
342}
343
344// f32 bits -> integer thousandths (truncated toward zero), for printing and for ordering
345func bc_f32_milli(x: i64) -> i64 { return __f32_to_i64(__f32_mul(x, __f32_from_i64(BC_MILLI))) }
346func bc_f32_micro(x: i64) -> i64 { return __f32_to_i64(__f32_mul(x, __f32_from_i64(BC_MICRO))) }