nx_wasm_vm_bounds_20260912.nx source
↩ module page · 757 lines · 48350 B
1// nx_wasm_vm.nx F618 wasm-SIMD FLIP (2026-07-21, landed from the proven rung-1 twin). Adds interpretation of the
2// 0xFD-prefixed wasm-SIMD subset the nx_wasm_v128 emitter produces (v128.load, i8x16.sub_sat_u,
3// v128.or, i16x8.extadd_pairwise_i8x16_u/_s, i32x4.extadd_pairwise_i16x8_u, i32x4.extract_lane),
4// byte-level-exact per the spec, so the faithdiff-class differential can prove SIMD == scalar == native.
5// v128 VALUES ride the untyped i64 stack as POINTERS into a 4096-slot x 16B ring arena (values are
6// consumed within an op sequence; the SAD kernel holds <=3 live). wm_skip_imm ALSO learns 0xFD --
7// without that the structured-control forward scanners desync on any function containing SIMD.
8// FLIPPED into the shared VM 2026-07-21 (F618); nx_v128_gate T2/T3 + faithdiff hold it GREEN.
9// nx_wasm_vm.nx -- a SOVEREIGN WebAssembly virtual machine: parses a .wasm BINARY and executes its exported functions
10// over a linear memory, pure NishiLang, no third-party runtime (no node/wasmtime/wasmer). Purpose: close the last
11// verification gap for shipped wasm -- run the COMPILED nx_vcodec.wasm offline and prove it computes bit-exact to the
12// gate-proven NATIVE codec (a functional gate on the actual shipped bytes, which most toolchains never do; they trust
13// the compiler). This is R0+R1: the parser (sections -> module) + a stack-machine executor for the opcode subset our
14// nx_wat_compiler emits (i32/i64 const/arith/shift/compare, local.get/set/tee, i64/i32 load/store(+8/16), call,
15// block/loop/if/else/end/br/br_if/br_table/return/drop/select, memory.size/grow, wrap/extend). license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nx_f32_hw.nx" // f32 opcodes execute on the SAME SSE bit-pattern ops the native build uses
18const K_MAGIC_65536: i64 = 65536
19const K_MAGIC_1024: i64 = 1024
20const K_MAGIC_2048: i64 = 2048
21const K_MAGIC_262144: i64 = 262144
22const K_MAGIC_4096: i64 = 4096
23const K_MAGIC_131072: i64 = 131072
24const K_MAGIC_2654435761: i64 = 2654435761
25const K_MAGIC_4000000: i64 = 4000000
26const K_MAGIC_32768: i64 = 32768
27const K_MAGIC_2147483648: i64 = 2147483648
28const K_MAGIC_4294967296: i64 = 4294967296
29const K_MAGIC_4090: i64 = 4090
30const K_MAGIC_65530: i64 = 65530
31const K_MAGIC_262140: i64 = 262140
32const K_MAGIC_999999: i64 = 999999
33
34// F618 v128 slot arena (module-level FLAG/POINTER statics live at the TOP, before any reader -- the
35// fwd-static rule). Scalar statics are the safe pattern; lazy mmap init on first SIMD op.
36static g_v128_arena: *u8
37static g_v128_idx: i64
38
39// ---------------- module representation ----------------
40struct WasmMod {
41 bytes: *u8, // the whole .wasm image
42 len: i64,
43 n_types: i64,
44 type_nparam: *i64, // params per type index
45 type_nres: *i64, // results per type index
46 n_imports: i64, // number of imported functions (func index space starts after these)
47 n_funcs: i64, // number of DEFINED functions
48 func_type: *i64, // type index per defined function
49 func_code: *i64, // byte offset of the first instruction (after the locals declaration)
50 func_end: *i64, // byte offset just past the function body
51 func_nloc: *i64, // total locals INCLUDING params (slots the executor must allocate)
52 mem: *u8, // linear memory
53 mem_bytes: i64,
54 n_exports: i64,
55 exp_name_off: *i64, // byte offset of each export name in bytes
56 exp_name_len: *i64,
57 exp_kind: *i64, // 0=func
58 exp_index: *i64, // func index for func exports
59 vstack: *i64, // shared operand-stack pool
60 vsp: i64, // operand stack pointer
61 locals: *i64, // shared locals pool (bump-allocated per call frame)
62 lsp: i64, // locals pointer
63 ctrl_kind: *i64, // control stack: 0=block,1=loop
64 ctrl_pc: *i64, // control target pc (end for block, body-start for loop)
65 csp: i64, // control stack pointer
66 memo_k: *i64, // match_end/match_else MEMO keys (startpos*2+kind; 0=empty) -- the dispatch-machine
67 memo_v: *i64, // lowering re-enters the same blocks millions of times; without this a full-game
68 // render vet forward-scans kilobytes per entry (added 2026-07-02, ~100x speedup)
69}
70
71// ---------------- byte readers ----------------
72func wv_u8(b: *u8, pos: *i64) -> i64 { let p: i64 = pos[0]; pos[0] = p + 1; return b[p] as i64 }
73func wv_uleb(b: *u8, pos: *i64) -> i64 {
74 var result: i64 = 0; var shift: i64 = 0; var p: i64 = pos[0]
75 while 1 == 1 {
76 let byte: i64 = b[p] as i64; p = p + 1
77 result = result | ((byte & 0x7F) << shift)
78 if (byte & 0x80) == 0 { pos[0] = p; return result }
79 shift = shift + 7
80 }
81 return 0
82}
83func wv_sleb(b: *u8, pos: *i64) -> i64 {
84 var result: i64 = 0; var shift: i64 = 0; var p: i64 = pos[0]
85 while 1 == 1 {
86 let byte: i64 = b[p] as i64; p = p + 1
87 result = result | ((byte & 0x7F) << shift); shift = shift + 7
88 if (byte & 0x80) == 0 {
89 if shift < 64 { if (byte & 0x40) != 0 { result = result | ((0 - 1) << shift) } }
90 pos[0] = p; return result
91 }
92 }
93 return 0
94}
95
96// ---------------- parser ----------------
97// Metadata is checked in a read-only pass before allocating or writing index tables.
98// These are WebAssembly binary-format widths, not corpus-size limits.
99const WMP_U32_BYTES: i64 = 5
100const WMP_PAGE_BYTES: i64 = 65536
101const WMP_WASM32_PAGES: i64 = 65536
102struct WmReader { bytes: *u8, pos: i64, end: i64, error: i64 }
103func wmp_u8(r: *WmReader) -> i64 {
104 if r.error != 0 { return 0 }
105 if r.pos >= r.end { r.error = 0 - 2; return 0 }
106 let v: i64 = r.bytes[r.pos] as i64
107 r.pos = r.pos + 1
108 return v
109}
110func wmp_u32(r: *WmReader) -> i64 {
111 var v: i64 = 0; var i: i64 = 0
112 while i < WMP_U32_BYTES {
113 let x: i64 = wmp_u8(r)
114 if r.error != 0 { return 0 }
115 if i == WMP_U32_BYTES - 1 { if (x & 0xF0) != 0 { r.error = 0 - 2; return 0 } }
116 v = v | ((x & 0x7F) << (i * 7))
117 if (x & 0x80) == 0 { return v }
118 i = i + 1
119 }
120 r.error = 0 - 2; return 0
121}
122func wmp_skip(r: *WmReader, n: i64) -> i64 {
123 if n < 0 { r.error = 0 - 2; return 0 }
124 if n > r.end - r.pos { r.error = 0 - 2; return 0 }
125 r.pos = r.pos + n; return 0
126}
127func wmp_count(r: *WmReader, minimum_bytes: i64) -> i64 {
128 let n: i64 = wmp_u32(r)
129 if n > (r.end - r.pos) / minimum_bytes { r.error = 0 - 2; return 0 }
130 return n
131}
132func wmp_valtype(v: i64) -> i64 {
133 if v == 0x7F || v == 0x7E || v == 0x7D || v == 0x7C || v == 0x7B || v == 0x70 || v == 0x6F { return 1 }
134 return 0
135}
136func wmp_limits(r: *WmReader, memory: i64) -> i64 {
137 let flags: i64 = wmp_u32(r)
138 if flags > 3 { r.error = 0 - 2 }
139 if memory == 0 { if flags > 1 { r.error = 0 - 2 } }
140 if flags == 2 { r.error = 0 - 2 }
141 let minimum: i64 = wmp_u32(r)
142 var maximum: i64 = minimum
143 if (flags & 1) != 0 { maximum = wmp_u32(r) }
144 if maximum < minimum { r.error = 0 - 2 }
145 if memory == 1 { if maximum > WMP_WASM32_PAGES { r.error = 0 - 2 } }
146 return minimum
147}
148// During preflight, recover parameter counts from validated type bytes without table writes.
149func wmp_params(b: *u8, start: i64, end: i64, wanted: i64, r: *WmReader) -> i64 {
150 r.bytes = b; r.pos = start; r.end = end; r.error = 0
151 var i: i64 = 0
152 while i <= wanted {
153 wmp_u8(r)
154 let np: i64 = wmp_u32(r)
155 if i == wanted { return np }
156 wmp_skip(r, np)
157 let nr: i64 = wmp_u32(r); wmp_skip(r, nr)
158 i = i + 1
159 }
160 return 0
161}
162func wmp_scan(mod: *WasmMod, fill: i64) -> i64 {
163 let b: *u8 = mod.bytes; let n: i64 = mod.len
164 let r: *WmReader = sys_mmap(32) as *WmReader
165 let fr: *WmReader = sys_mmap(32) as *WmReader
166 let tr: *WmReader = sys_mmap(32) as *WmReader
167 if (r as i64) <= 0 || (fr as i64) <= 0 || (tr as i64) <= 0 { return 0 - 3 }
168 r.bytes = b; r.pos = 8; r.end = n; r.error = 0
169 var nt: i64 = 0; var nf: i64 = 0; var ne: i64 = 0; var ni: i64 = 0
170 var memory_count: i64 = 0; var pages: i64 = 0; var code_count: i64 = 0
171 var type_start: i64 = 0; var type_end: i64 = 0; var func_start: i64 = 0; var func_end: i64 = 0
172 var seen: i64 = 0
173 while r.pos < n && r.error == 0 {
174 r.end = n
175 let sid: i64 = wmp_u8(r); let size: i64 = wmp_u32(r)
176 if size > n - r.pos { r.error = 0 - 2 } else {
177 let send: i64 = r.pos + size
178 r.end = send
179 if sid > 12 { r.error = 0 - 2 }
180 if sid != 0 {
181 if (seen & (1 << sid)) != 0 { r.error = 0 - 2 }
182 seen = seen | (1 << sid)
183 }
184 if sid == 1 {
185 nt = wmp_count(r, 3); type_start = r.pos; type_end = send
186 var i: i64 = 0
187 while i < nt && r.error == 0 {
188 if wmp_u8(r) != 0x60 { r.error = 0 - 2 }
189 let np: i64 = wmp_count(r, 1)
190 if np > K_MAGIC_262144 { r.error = 0 - 2 }
191 var j: i64 = 0
192 while j < np && r.error == 0 { if wmp_valtype(wmp_u8(r)) == 0 { r.error = 0 - 2 }; j = j + 1 }
193 let nr: i64 = wmp_count(r, 1)
194 j = 0
195 while j < nr && r.error == 0 { if wmp_valtype(wmp_u8(r)) == 0 { r.error = 0 - 2 }; j = j + 1 }
196 if fill == 1 && r.error == 0 { mod.type_nparam[i] = np; mod.type_nres[i] = nr }
197 i = i + 1
198 }
199 } else { if sid == 2 {
200 let count: i64 = wmp_count(r, 3); var i: i64 = 0
201 while i < count && r.error == 0 {
202 let ml: i64 = wmp_u32(r); wmp_skip(r, ml)
203 let fl: i64 = wmp_u32(r); wmp_skip(r, fl)
204 let kind: i64 = wmp_u8(r)
205 if kind == 0 { let ty: i64 = wmp_u32(r); if ty >= nt { r.error = 0 - 2 }; ni = ni + 1 }
206 else { if kind == 1 { let rt: i64 = wmp_u8(r); if rt != 0x70 && rt != 0x6F { r.error = 0 - 2 }; wmp_limits(r, 0) }
207 else { if kind == 2 { pages = wmp_limits(r, 1); memory_count = memory_count + 1 }
208 else { if kind == 3 { if wmp_valtype(wmp_u8(r)) == 0 { r.error = 0 - 2 }; if wmp_u8(r) > 1 { r.error = 0 - 2 } }
209 else { r.error = 0 - 2 } } } }
210 i = i + 1
211 }
212 } else { if sid == 3 {
213 nf = wmp_count(r, 1); func_start = r.pos; func_end = send
214 var i: i64 = 0
215 while i < nf && r.error == 0 {
216 let ty: i64 = wmp_u32(r)
217 if ty >= nt { r.error = 0 - 2 }
218 if fill == 1 && r.error == 0 { mod.func_type[i] = ty }
219 i = i + 1
220 }
221 } else { if sid == 5 {
222 let count: i64 = wmp_count(r, 2); var i: i64 = 0
223 while i < count && r.error == 0 { pages = wmp_limits(r, 1); memory_count = memory_count + 1; i = i + 1 }
224 } else { if sid == 7 {
225 ne = wmp_count(r, 3); var i: i64 = 0
226 while i < ne && r.error == 0 {
227 let nl: i64 = wmp_u32(r); let name_off: i64 = r.pos
228 wmp_skip(r, nl)
229 let kind: i64 = wmp_u8(r); let index: i64 = wmp_u32(r)
230 if kind > 3 { r.error = 0 - 2 }
231 if kind == 0 { if index >= ni + nf { r.error = 0 - 2 } }
232 if kind == 2 { if index >= memory_count { r.error = 0 - 2 } }
233 if fill == 1 && r.error == 0 {
234 mod.exp_name_off[i] = name_off; mod.exp_name_len[i] = nl
235 mod.exp_kind[i] = kind; mod.exp_index[i] = index
236 }
237 i = i + 1
238 }
239 } else { if sid == 10 {
240 code_count = wmp_count(r, 2)
241 if code_count != nf { r.error = 0 - 2 }
242 fr.bytes = b; fr.pos = func_start; fr.end = func_end; fr.error = 0
243 var i: i64 = 0
244 while i < code_count && r.error == 0 {
245 let size: i64 = wmp_u32(r)
246 if size < 2 || size > send - r.pos { r.error = 0 - 2 } else {
247 let bend: i64 = r.pos + size; r.end = bend
248 let ty: i64 = wmp_u32(fr)
249 if fr.error != 0 || ty >= nt { r.error = 0 - 2 } else {
250 let np: i64 = wmp_params(b, type_start, type_end, ty, tr)
251 var locals: i64 = np; let groups: i64 = wmp_count(r, 2); var j: i64 = 0
252 while j < groups && r.error == 0 {
253 let count: i64 = wmp_u32(r)
254 if count > K_MAGIC_262144 - locals { r.error = 0 - 2 } else { locals = locals + count }
255 if wmp_valtype(wmp_u8(r)) == 0 { r.error = 0 - 2 }
256 j = j + 1
257 }
258 if tr.error != 0 { r.error = 0 - 2 }
259 if r.pos >= bend || b[bend - 1] != (0x0B as u8) { r.error = 0 - 2 }
260 if fill == 1 && r.error == 0 {
261 mod.func_nloc[i] = locals; mod.func_code[i] = r.pos; mod.func_end[i] = bend
262 }
263 }
264 r.pos = bend; r.end = send
265 }
266 i = i + 1
267 }
268 } else { r.pos = send } } } } } }
269 if r.error == 0 && r.pos != send { r.error = 0 - 2 }
270 r.end = n
271 }
272 }
273 if memory_count > 1 || code_count != nf { r.error = 0 - 2 }
274 let error: i64 = r.error
275 sys_munmap(fr as *u8, 32); sys_munmap(tr as *u8, 32); sys_munmap(r as *u8, 32)
276 if error != 0 { return error }
277 mod.n_types = nt; mod.n_funcs = nf; mod.n_imports = ni; mod.n_exports = ne
278 mod.mem_bytes = pages * WMP_PAGE_BYTES
279 return 0
280}
281func wmp_array(count: i64) -> *i64 { return sys_mmap((count + 1) * 8) as *i64 }
282func wm_parse(mod: *WasmMod) -> i64 {
283 let b: *u8 = mod.bytes
284 if (b as i64) <= 0 || mod.len < 8 { return 0 - 1 }
285 if b[0] != (0 as u8) || b[1] != (0x61 as u8) || b[2] != (0x73 as u8) || b[3] != (0x6D as u8) { return 0 - 1 }
286 if b[4] != (1 as u8) || b[5] != (0 as u8) || b[6] != (0 as u8) || b[7] != (0 as u8) { return 0 - 1 }
287 let checked: i64 = wmp_scan(mod, 0)
288 if checked != 0 { return checked }
289 mod.type_nparam = wmp_array(mod.n_types); mod.type_nres = wmp_array(mod.n_types)
290 mod.func_type = wmp_array(mod.n_funcs); mod.func_code = wmp_array(mod.n_funcs)
291 mod.func_end = wmp_array(mod.n_funcs); mod.func_nloc = wmp_array(mod.n_funcs)
292 mod.exp_name_off = wmp_array(mod.n_exports); mod.exp_name_len = wmp_array(mod.n_exports)
293 mod.exp_kind = wmp_array(mod.n_exports); mod.exp_index = wmp_array(mod.n_exports)
294 if (mod.type_nparam as i64) <= 0 || (mod.type_nres as i64) <= 0 || (mod.func_type as i64) <= 0 || (mod.func_code as i64) <= 0 || (mod.func_end as i64) <= 0 || (mod.func_nloc as i64) <= 0 || (mod.exp_name_off as i64) <= 0 || (mod.exp_name_len as i64) <= 0 || (mod.exp_kind as i64) <= 0 || (mod.exp_index as i64) <= 0 { return 0 - 3 }
295 if mod.mem_bytes > 0 { mod.mem = sys_mmap(mod.mem_bytes); if (mod.mem as i64) <= 0 { return 0 - 3 } }
296 return wmp_scan(mod, 1)
297}
298
299// compare an export name (in the module bytes) to a C-string needle; 1 if equal.
300func wm_name_eq(b: *u8, off: i64, len: i64, needle: *u8) -> i64 {
301 var i: i64 = 0
302 while i < len { if b[off + i] != needle[i] { return 0 } i = i + 1 }
303 if needle[len] != (0 as u8) { return 0 } // needle must end exactly
304 return 1
305}
306// find an exported function's GLOBAL function index by name, or -1.
307func wm_find_export(mod: *WasmMod, needle: *u8) -> i64 {
308 var i: i64 = 0
309 while i < mod.n_exports {
310 if mod.exp_kind[i] == 0 {
311 if wm_name_eq(mod.bytes, mod.exp_name_off[i], mod.exp_name_len[i], needle) == 1 { return mod.exp_index[i] }
312 }
313 i = i + 1
314 }
315 return 0 - 1
316}
317// allocate a module + its arrays around a loaded .wasm image.
318func wm_new(bytes: *u8, len: i64) -> *WasmMod {
319 let mod: *WasmMod = sys_mmap(256) as *WasmMod
320 mod.bytes = bytes; mod.len = len
321 // Metadata tables are sized only after the complete metadata preflight.
322 mod.type_nparam = 0 as *i64; mod.type_nres = 0 as *i64
323 mod.func_type = 0 as *i64; mod.func_code = 0 as *i64
324 mod.func_end = 0 as *i64; mod.func_nloc = 0 as *i64
325 mod.exp_name_off = 0 as *i64; mod.exp_name_len = 0 as *i64
326 mod.exp_kind = 0 as *i64; mod.exp_index = 0 as *i64
327 mod.mem = 0 as *u8
328 mod.vstack = sys_mmap(K_MAGIC_65536*8) as *i64
329 mod.locals = sys_mmap(K_MAGIC_262144*8) as *i64
330 mod.ctrl_kind = sys_mmap(K_MAGIC_4096*8) as *i64
331 mod.ctrl_pc = sys_mmap(K_MAGIC_4096*8) as *i64
332 mod.memo_k = sys_mmap(K_MAGIC_131072*8) as *i64
333 mod.memo_v = sys_mmap(K_MAGIC_131072*8) as *i64
334 mod.vsp = 0; mod.lsp = 0; mod.csp = 0
335 return mod
336}
337// scan-memo: open-address hash keyed by startpos*2+kind (key 0 = empty slot; code offsets are always > 0)
338func wm_memo_get(mod: *WasmMod, key: i64) -> i64 {
339 var i: i64 = (key * K_MAGIC_2654435761) % K_MAGIC_131072
340 if i < 0 { i = i + K_MAGIC_131072 }
341 var probes: i64 = 0
342 while probes < K_MAGIC_131072 {
343 if mod.memo_k[i] == key { return mod.memo_v[i] }
344 if mod.memo_k[i] == 0 { return 0 }
345 i = i + 1
346 if i >= K_MAGIC_131072 { i = 0 }
347 probes = probes + 1
348 }
349 return 0
350}
351func wm_memo_put(mod: *WasmMod, key: i64, val: i64) -> i64 {
352 var i: i64 = (key * K_MAGIC_2654435761) % K_MAGIC_131072
353 if i < 0 { i = i + K_MAGIC_131072 }
354 var probes: i64 = 0
355 while probes < K_MAGIC_131072 {
356 if mod.memo_k[i] == 0 { mod.memo_k[i] = key; mod.memo_v[i] = val; return 0 }
357 if mod.memo_k[i] == key { mod.memo_v[i] = val; return 0 }
358 i = i + 1
359 if i >= K_MAGIC_131072 { i = 0 }
360 probes = probes + 1
361 }
362 return 0 - 1
363}
364
365// ---------------- executor ----------------
366// faithful little-endian 8-byte memory access (exactly what i64.load/i64.store do in the shipped wasm).
367func wm_ld64(m: *u8, a: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v = v | ((m[a+i] as i64) << (i*8)); i=i+1 } return v }
368func wm_st64(m: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<8 { m[a+i] = ((v >> (i*8)) & 0xFF) as u8; i=i+1 } return 0 }
369// LN33: the 4-byte cells the i32 atomic family works on (little-endian, exactly like the 8-byte pair above).
370func wm_ld32(m: *u8, a: i64) -> i64 { var v: i64=0; var i: i64=0; while i<4 { v = v | ((m[a+i] as i64) << (i*8)); i=i+1 } return v }
371func wm_st32(m: *u8, a: i64, v: i64) -> i64 { var i: i64=0; while i<4 { m[a+i] = ((v >> (i*8)) & 0xFF) as u8; i=i+1 } return 0 }
372
373// advance past an opcode's immediate operands (for the structured-control forward scan). pc points just AFTER the op byte.
374func wm_skip_imm(b: *u8, op: i64, pos: *i64) -> i64 {
375 if op == 0x02 { wv_u8(b, pos); return 0 } // block: blocktype byte
376 if op == 0x03 { wv_u8(b, pos); return 0 } // loop: blocktype byte
377 if op == 0x04 { wv_u8(b, pos); return 0 } // if: blocktype byte
378 if op == 0x0C { wv_uleb(b, pos); return 0 } // br
379 if op == 0x0D { wv_uleb(b, pos); return 0 } // br_if
380 if op == 0x0E { let c: i64 = wv_uleb(b, pos); var i: i64=0; while i<=c { wv_uleb(b, pos); i=i+1 } return 0 } // br_table
381 if op == 0x10 { wv_uleb(b, pos); return 0 } // call
382 if op == 0x20 { wv_uleb(b, pos); return 0 } // local.get
383 if op == 0x21 { wv_uleb(b, pos); return 0 } // local.set
384 if op == 0x22 { wv_uleb(b, pos); return 0 } // local.tee
385 if op == 0x41 { wv_sleb(b, pos); return 0 } // i32.const
386 if op == 0x42 { wv_sleb(b, pos); return 0 } // i64.const
387 if op >= 0x28 { if op <= 0x3E { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } } // load/store memarg (align, offset)
388 if op == 0xFD { // F618 SIMD prefix: uleb sub-opcode + per-sub immediates
389 let fsub: i64 = wv_uleb(b, pos)
390 if fsub == 0 { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } // v128.load memarg
391 if fsub == 11 { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } // v128.store memarg
392 if fsub == 27 { wv_u8(b, pos); return 0 } // i32x4.extract_lane laneidx
393 return 0 // sub_sat_u/or/extadds: no immediate
394 }
395 if op == 0xFE { // LN33 threads prefix: uleb sub-opcode + memarg (fence: one flag byte)
396 let asub: i64 = wv_uleb(b, pos)
397 if asub == 3 { wv_u8(b, pos); return 0 }
398 wv_uleb(b, pos); wv_uleb(b, pos)
399 return 0
400 }
401 return 0 // all other ops: no immediate
402}
403// find the pc just past the `end` that matches the block/loop/if opened at *pos (pos points after the opener's blocktype).
404func wm_match_end(b: *u8, startpos: i64) -> i64 {
405 let pos: *i64 = sys_mmap(8) as *i64; pos[0] = startpos
406 var depth: i64 = 1
407 var steps: i64 = 0
408 while depth > 0 {
409 // runaway guard: a scanner that never closes = an opcode whose immediates wm_skip_imm doesn't know
410 // (desync). Fail LOUD with the last opcode instead of walking off the mapping (was a silent SIGSEGV).
411 steps = steps + 1
412 if steps > K_MAGIC_4000000 { sys_write(2, "SCAN-RUNAWAY match_end\n" as *u8, 23); sys_exit(9) }
413 let op: i64 = wv_u8(b, pos)
414 if op == 0x02 { depth = depth + 1 } else {
415 if op == 0x03 { depth = depth + 1 } else {
416 if op == 0x04 { depth = depth + 1 } else {
417 if op == 0x0B { depth = depth - 1 } else {
418 wm_skip_imm(b, op, pos)
419 } } } }
420 if op == 0x02 { wv_u8(b, pos) } // also skip the opener blocktype we counted
421 if op == 0x03 { wv_u8(b, pos) }
422 if op == 0x04 { wv_u8(b, pos) }
423 }
424 let rme: i64 = pos[0]; sys_munmap(pos as *u8, 8); return rme // free the scan cursor (was a per-open leak)
425}
426
427// find where execution goes when an `if` condition is FALSE: the position after the matching `else` opcode, or --
428// if there is no else -- the position after the matching `end` (i.e., skip the whole construct). startpos is just
429// after the if's blocktype byte.
430func wm_match_else(b: *u8, startpos: i64) -> i64 {
431 let pos: *i64 = sys_mmap(8) as *i64; pos[0] = startpos
432 var depth: i64 = 1
433 var steps: i64 = 0
434 while depth > 0 {
435 steps = steps + 1
436 if steps > K_MAGIC_4000000 { sys_write(2, "SCAN-RUNAWAY match_else\n" as *u8, 24); sys_exit(9) }
437 let op: i64 = wv_u8(b, pos)
438 if op == 0x02 { depth = depth + 1; wv_u8(b, pos) } else {
439 if op == 0x03 { depth = depth + 1; wv_u8(b, pos) } else {
440 if op == 0x04 { depth = depth + 1; wv_u8(b, pos) } else {
441 if op == 0x05 { if depth == 1 { let r1: i64 = pos[0]; sys_munmap(pos as *u8, 8); return r1 } } else {
442 if op == 0x0B { depth = depth - 1 } else {
443 wm_skip_imm(b, op, pos)
444 } } } } }
445 }
446 let r2: i64 = pos[0]; sys_munmap(pos as *u8, 8); return r2 // free the scan cursor (was a per-if leak)
447}
448// perform a branch to label depth `lbl` (0 = innermost): loop re-enters at its body start (keep its frame),
449// block/if exit to past their end (drop their frame). Sets pos[0] and the control stack pointer.
450func wm_do_br(mod: *WasmMod, pos: *i64, lbl: i64) -> i64 {
451 let t: i64 = mod.csp - 1 - lbl
452 pos[0] = mod.ctrl_pc[t]
453 if mod.ctrl_kind[t] == 1 { mod.csp = t + 1 } else { mod.csp = t }
454 return 0
455}
456// call defined function fidx with nargs i64 args in args[]; returns the i64 result (0 if void). Faithful wasm exec.
457// FLAT dispatch (one `if op == X` per opcode -- this codebase's idiom; avoids deep if/else brace fragility).
458// --- fail-fast diagnostic helpers: a faithful VM should never trip these; firing => real corruption ---
459func wm_phexf(v: i64, fd: i64) -> i64 {
460 let d: *u8 = "0123456789abcdef" as *u8
461 let h: *u8 = sys_mmap(2)
462 h[0] = d[(v >> 4) & 0xF]; h[1] = d[v & 0xF]
463 sys_write(fd, h, 2); return 0
464}
465func wm_pn2f(v: i64, fd: i64) -> i64 {
466 let b: *u8 = sys_mmap(28); var x: i64 = v
467 if x < 0 { sys_write(fd, "-" as *u8, 1); x = 0 - x }
468 if x == 0 { sys_write(fd, "0" as *u8, 1); return 0 }
469 var d: i64 = 0; var y: i64 = x
470 while y > 0 { d = d + 1; y = y / 10 }
471 var i: i64 = d - 1; y = x
472 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 }
473 sys_write(fd, b, d); return 0
474}
475func wm_pn2(v: i64) -> i64 { return wm_pn2f(v, 2) }
476func wm_phex(v: i64) -> i64 { return wm_phexf(v, 2) }
477func wm_trap1(code: i64, op: i64, a: i64, bb: i64, fd: i64) -> i64 {
478 sys_write(fd, "VM-TRAP code=" as *u8, 13); wm_pn2f(code, fd)
479 sys_write(fd, " op=0x" as *u8, 6); wm_phexf(op, fd)
480 sys_write(fd, " a=" as *u8, 3); wm_pn2f(a, fd)
481 sys_write(fd, " b=" as *u8, 3); wm_pn2f(bb, fd)
482 sys_write(fd, "\n" as *u8, 1)
483 return 0
484}
485func wm_trap(code: i64, op: i64, a: i64, bb: i64) -> i64 {
486 // BOTH streams: stderr capture gets eaten by some harness layers (learned 2026-07-02) -- stdout survives
487 wm_trap1(code, op, a, bb, 1)
488 wm_trap1(code, op, a, bb, 2)
489 sys_exit(8); return 0
490}
491func wm_call(mod: *WasmMod, fidx: i64, args: *i64, nargs: i64) -> i64 {
492 let b: *u8 = mod.bytes
493 let di: i64 = fidx - mod.n_imports
494 let nloc: i64 = mod.func_nloc[di]
495 let lbase: i64 = mod.lsp
496 mod.lsp = mod.lsp + nloc
497 let L: *i64 = mod.locals
498 var i: i64 = 0
499 while i < nloc { if i < nargs { L[lbase+i] = args[i] } else { L[lbase+i] = 0 } i = i + 1 }
500 let vbase: i64 = mod.vsp
501 let cbase: i64 = mod.csp
502 let V: *i64 = mod.vstack
503 let m: *u8 = mod.mem
504 let pos: *i64 = sys_mmap(8) as *i64
505 pos[0] = mod.func_code[di]
506 let cend: i64 = mod.func_end[di]
507 var retval: i64 = 0
508 var running: i64 = 1
509 while running == 1 {
510 if pos[0] >= cend { running = 0 }
511 if running == 1 {
512 let op: i64 = wv_u8(b, pos)
513 // control flow
514 if op == 0x0B { if mod.csp <= cbase { running = 0 } else { mod.csp = mod.csp - 1 } }
515 if op == 0x02 {
516 wv_u8(b, pos)
517 var e02: i64 = wm_memo_get(mod, pos[0] * 2)
518 if e02 == 0 { e02 = wm_match_end(b, pos[0]); wm_memo_put(mod, pos[0] * 2, e02) }
519 mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = e02; mod.csp = mod.csp + 1
520 }
521 if op == 0x03 { wv_u8(b, pos); mod.ctrl_kind[mod.csp] = 1; mod.ctrl_pc[mod.csp] = pos[0]; mod.csp = mod.csp + 1 }
522 if op == 0x04 {
523 wv_u8(b, pos)
524 var endp: i64 = wm_memo_get(mod, pos[0] * 2)
525 if endp == 0 { endp = wm_match_end(b, pos[0]); wm_memo_put(mod, pos[0] * 2, endp) }
526 var elsep: i64 = wm_memo_get(mod, pos[0] * 2 + 1)
527 if elsep == 0 { elsep = wm_match_else(b, pos[0]); wm_memo_put(mod, pos[0] * 2 + 1, elsep) }
528 mod.vsp = mod.vsp - 1; let cond: i64 = V[mod.vsp]
529 if cond != 0 { mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = endp; mod.csp = mod.csp + 1 }
530 if cond == 0 {
531 if elsep < endp { mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = endp; mod.csp = mod.csp + 1; pos[0] = elsep }
532 if elsep >= endp { pos[0] = endp }
533 }
534 }
535 if op == 0x05 { mod.csp = mod.csp - 1; pos[0] = mod.ctrl_pc[mod.csp] }
536 if op == 0x0C { let lbl: i64 = wv_uleb(b, pos); wm_do_br(mod, pos, lbl) }
537 if op == 0x0D { let lbl: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; if V[mod.vsp] != 0 { wm_do_br(mod, pos, lbl) } }
538 if op == 0x0E {
539 let cnt: i64 = wv_uleb(b, pos)
540 mod.vsp = mod.vsp - 1; var idx: i64 = V[mod.vsp]
541 if idx < 0 { idx = cnt }
542 if idx > cnt { idx = cnt }
543 var lbl: i64 = 0; var k: i64 = 0
544 while k <= cnt { let l: i64 = wv_uleb(b, pos); if k == idx { lbl = l } k = k + 1 }
545 wm_do_br(mod, pos, lbl)
546 }
547 if op == 0x0F { if mod.vsp > vbase { retval = V[mod.vsp - 1] } running = 0 }
548 if op == 0x10 {
549 let cf: i64 = wv_uleb(b, pos)
550 let cdi: i64 = cf - mod.n_imports
551 let cnp: i64 = mod.type_nparam[mod.func_type[cdi]]
552 mod.vsp = mod.vsp - cnp
553 let r: i64 = wm_call(mod, cf, ((mod.vstack as i64) + mod.vsp*8) as *i64, cnp)
554 if mod.type_nres[mod.func_type[cdi]] > 0 { V[mod.vsp] = r; mod.vsp = mod.vsp + 1 }
555 }
556 // values / locals
557 if op == 0x20 { let x: i64 = wv_uleb(b, pos); V[mod.vsp] = L[lbase + x]; mod.vsp = mod.vsp + 1 }
558 if op == 0x21 { let x: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; L[lbase + x] = V[mod.vsp] }
559 if op == 0x22 { let x: i64 = wv_uleb(b, pos); L[lbase + x] = V[mod.vsp - 1] }
560 if op == 0x41 { V[mod.vsp] = wv_sleb(b, pos); mod.vsp = mod.vsp + 1 }
561 if op == 0x42 { V[mod.vsp] = wv_sleb(b, pos); mod.vsp = mod.vsp + 1 }
562 if op == 0x1A { mod.vsp = mod.vsp - 1 }
563 // memory (faithful 8-byte, exactly as the wat specifies)
564 if op == 0x29 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a29: i64 = V[mod.vsp-1] + o2; if a29 < 0 { wm_trap(6, op, a29, 0) } if a29 + 8 > mod.mem_bytes { wm_trap(6, op, a29, mod.mem_bytes) } V[mod.vsp-1] = wm_ld64(m, a29) }
565 if op == 0x28 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); V[mod.vsp-1] = wm_ld64(m, V[mod.vsp-1] + o2) & 0xFFFFFFFF }
566 if op == 0x37 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; let v: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a37: i64 = V[mod.vsp] + o2; if a37 < 0 { wm_trap(7, op, a37, 0) } if a37 + 8 > mod.mem_bytes { wm_trap(7, op, a37, mod.mem_bytes) } wm_st64(m, a37, v) }
567 if op == 0x36 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; let v: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; wm_st64(m, V[mod.vsp] + o2, v) }
568 // byte-width memory (the fixed backend now emits these for u8 arrays)
569 if op == 0x31 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a31: i64 = V[mod.vsp-1] + o2; if a31 < 0 { wm_trap(6, op, a31, 0) } if a31 >= mod.mem_bytes { wm_trap(6, op, a31, mod.mem_bytes) } V[mod.vsp-1] = m[a31] as i64 }
570 if op == 0x3C { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; let v: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a3c: i64 = V[mod.vsp] + o2; if a3c < 0 { wm_trap(7, op, a3c, 0) } if a3c >= mod.mem_bytes { wm_trap(7, op, a3c, mod.mem_bytes) } m[a3c] = (v & 0xFF) as u8 }
571 // full subword-memory family (2026-07-10 sext debt fix): signed loads sign-extend via
572 // arithmetic (the VM's own *u8 reads zero-extend by language semantics -- unchanged);
573 // 16/32-bit widths were previously NOT implemented at all (backend could emit -> VM had no op).
574 if op == 0x30 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a30: i64 = V[mod.vsp-1] + o2; if a30 < 0 { wm_trap(6, op, a30, 0) } if a30 >= mod.mem_bytes { wm_trap(6, op, a30, mod.mem_bytes) } var v30: i64 = m[a30] as i64; if v30 >= 128 { v30 = v30 - 256 } V[mod.vsp-1] = v30 }
575 if op == 0x33 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a33: i64 = V[mod.vsp-1] + o2; if a33 < 0 { wm_trap(6, op, a33, 0) } if a33 + 2 > mod.mem_bytes { wm_trap(6, op, a33, mod.mem_bytes) } V[mod.vsp-1] = (m[a33] as i64) | ((m[a33+1] as i64) << 8) }
576 if op == 0x32 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a32: i64 = V[mod.vsp-1] + o2; if a32 < 0 { wm_trap(6, op, a32, 0) } if a32 + 2 > mod.mem_bytes { wm_trap(6, op, a32, mod.mem_bytes) } var v32: i64 = (m[a32] as i64) | ((m[a32+1] as i64) << 8); if v32 >= K_MAGIC_32768 { v32 = v32 - K_MAGIC_65536 } V[mod.vsp-1] = v32 }
577 if op == 0x35 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a35: i64 = V[mod.vsp-1] + o2; if a35 < 0 { wm_trap(6, op, a35, 0) } if a35 + 4 > mod.mem_bytes { wm_trap(6, op, a35, mod.mem_bytes) } V[mod.vsp-1] = (m[a35] as i64) | ((m[a35+1] as i64) << 8) | ((m[a35+2] as i64) << 16) | ((m[a35+3] as i64) << 24) }
578 if op == 0x34 { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); let a34: i64 = V[mod.vsp-1] + o2; if a34 < 0 { wm_trap(6, op, a34, 0) } if a34 + 4 > mod.mem_bytes { wm_trap(6, op, a34, mod.mem_bytes) } var v34: i64 = (m[a34] as i64) | ((m[a34+1] as i64) << 8) | ((m[a34+2] as i64) << 16) | ((m[a34+3] as i64) << 24); if v34 >= K_MAGIC_2147483648 { v34 = v34 - K_MAGIC_4294967296 } V[mod.vsp-1] = v34 }
579 if op == 0x3D { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; let v3d: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a3d: i64 = V[mod.vsp] + o2; if a3d < 0 { wm_trap(7, op, a3d, 0) } if a3d + 2 > mod.mem_bytes { wm_trap(7, op, a3d, mod.mem_bytes) } m[a3d] = (v3d & 0xFF) as u8; m[a3d+1] = ((v3d >> 8) & 0xFF) as u8 }
580 if op == 0x3E { wv_uleb(b, pos); let o2: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; let v3e: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a3e: i64 = V[mod.vsp] + o2; if a3e < 0 { wm_trap(7, op, a3e, 0) } if a3e + 4 > mod.mem_bytes { wm_trap(7, op, a3e, mod.mem_bytes) } m[a3e] = (v3e & 0xFF) as u8; m[a3e+1] = ((v3e >> 8) & 0xFF) as u8; m[a3e+2] = ((v3e >> 16) & 0xFF) as u8; m[a3e+3] = ((v3e >> 24) & 0xFF) as u8 }
581 // conversions
582 if op == 0xA7 { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF }
583 if op == 0xAD { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF }
584 // f32 (added 2026-07-02 for the mineworld render vet): values are f32 BIT PATTERNS in the low 32
585 // bits of the untyped i64 slots -- exactly nx_f32_hw's convention, so each op IS the native SSE op.
586 if op == 0xB4 { V[mod.vsp-1] = f32_of(V[mod.vsp-1]) } // f32.convert_i64_s
587 if op == 0xAE { V[mod.vsp-1] = f32_int(V[mod.vsp-1]) } // i64.trunc_f32_s
588 if op == 0xBE { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } // f32.reinterpret_i32
589 if op == 0xBC { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } // i32.reinterpret_f32
590 if op == 0x92 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_add(V[mod.vsp-1], V[mod.vsp]) } // f32.add
591 if op == 0x94 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_mul(V[mod.vsp-1], V[mod.vsp]) } // f32.mul
592 if op == 0x95 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_div(V[mod.vsp-1], V[mod.vsp]) } // f32.div
593 // i64 arithmetic
594 if op == 0x7C { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] + V[mod.vsp] }
595 if op == 0x7D { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] - V[mod.vsp] }
596 if op == 0x7E { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] * V[mod.vsp] }
597 if op == 0x7F { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] / V[mod.vsp] }
598 if op == 0x81 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] % V[mod.vsp] }
599 if op == 0x83 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] & V[mod.vsp] }
600 if op == 0x84 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] | V[mod.vsp] }
601 if op == 0x85 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] ^ V[mod.vsp] }
602 if op == 0x86 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] << V[mod.vsp] }
603 if op == 0x87 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] >> V[mod.vsp] }
604 // i64 compares (push 1/0)
605 if op == 0x51 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]==V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
606 if op == 0x52 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]!=V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
607 if op == 0x53 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]<V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
608 if op == 0x55 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]>V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
609 if op == 0x57 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]<=V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
610 if op == 0x59 { mod.vsp=mod.vsp-1; if V[mod.vsp-1]>=V[mod.vsp] { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
611 if op == 0x50 { if V[mod.vsp-1]==0 { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } }
612 // --- LN33 wasm threads (0xFE prefix): the single-thread TWIN of the nx_atomic family. One thread means
613 // every atomic is its plain op; wait32 can never be woken here so it answers 1 (not-equal) or 2 (timed
614 // out) without ever blocking; notify wakes nobody (0); fence is a no-op. Bounds-checked like every access.
615 if op == 0xFE {
616 let asub: i64 = wv_uleb(b, pos)
617 var aoff: i64 = 0
618 if asub == 3 { wv_u8(b, pos) } else { wv_uleb(b, pos); aoff = wv_uleb(b, pos) }
619 if asub == 0x10 { let a10: i64 = V[mod.vsp-1] + aoff; if a10 < 0 { wm_trap(6, op, a10, 0) } if a10 + 4 > mod.mem_bytes { wm_trap(6, op, a10, mod.mem_bytes) } V[mod.vsp-1] = wm_ld32(m, a10) }
620 if asub == 0x11 { let a11: i64 = V[mod.vsp-1] + aoff; if a11 < 0 { wm_trap(6, op, a11, 0) } if a11 + 8 > mod.mem_bytes { wm_trap(6, op, a11, mod.mem_bytes) } V[mod.vsp-1] = wm_ld64(m, a11) }
621 if asub == 0x17 { mod.vsp = mod.vsp - 1; let v17: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a17: i64 = V[mod.vsp] + aoff; if a17 < 0 { wm_trap(7, op, a17, 0) } if a17 + 4 > mod.mem_bytes { wm_trap(7, op, a17, mod.mem_bytes) } wm_st32(m, a17, v17 & 0xFFFFFFFF) }
622 if asub == 0x18 { mod.vsp = mod.vsp - 1; let v18: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let a18: i64 = V[mod.vsp] + aoff; if a18 < 0 { wm_trap(7, op, a18, 0) } if a18 + 8 > mod.mem_bytes { wm_trap(7, op, a18, mod.mem_bytes) } wm_st64(m, a18, v18) }
623 if asub == 0x1E { mod.vsp = mod.vsp - 1; let v1e: i64 = V[mod.vsp]; let a1e: i64 = V[mod.vsp-1] + aoff; if a1e < 0 { wm_trap(7, op, a1e, 0) } if a1e + 4 > mod.mem_bytes { wm_trap(7, op, a1e, mod.mem_bytes) } let o1e: i64 = wm_ld32(m, a1e); wm_st32(m, a1e, (o1e + v1e) & 0xFFFFFFFF); V[mod.vsp-1] = o1e }
624 if asub == 0x1F { mod.vsp = mod.vsp - 1; let v1f: i64 = V[mod.vsp]; let a1f: i64 = V[mod.vsp-1] + aoff; if a1f < 0 { wm_trap(7, op, a1f, 0) } if a1f + 8 > mod.mem_bytes { wm_trap(7, op, a1f, mod.mem_bytes) } let o1f: i64 = wm_ld64(m, a1f); wm_st64(m, a1f, o1f + v1f); V[mod.vsp-1] = o1f }
625 if asub == 0x25 { mod.vsp = mod.vsp - 1; let v25: i64 = V[mod.vsp]; let a25: i64 = V[mod.vsp-1] + aoff; if a25 < 0 { wm_trap(7, op, a25, 0) } if a25 + 4 > mod.mem_bytes { wm_trap(7, op, a25, mod.mem_bytes) } let o25: i64 = wm_ld32(m, a25); wm_st32(m, a25, (o25 - v25) & 0xFFFFFFFF); V[mod.vsp-1] = o25 }
626 if asub == 0x26 { mod.vsp = mod.vsp - 1; let v26: i64 = V[mod.vsp]; let a26: i64 = V[mod.vsp-1] + aoff; if a26 < 0 { wm_trap(7, op, a26, 0) } if a26 + 8 > mod.mem_bytes { wm_trap(7, op, a26, mod.mem_bytes) } let o26: i64 = wm_ld64(m, a26); wm_st64(m, a26, o26 - v26); V[mod.vsp-1] = o26 }
627 if asub == 0x41 { mod.vsp = mod.vsp - 1; let v41: i64 = V[mod.vsp]; let a41: i64 = V[mod.vsp-1] + aoff; if a41 < 0 { wm_trap(7, op, a41, 0) } if a41 + 4 > mod.mem_bytes { wm_trap(7, op, a41, mod.mem_bytes) } let o41: i64 = wm_ld32(m, a41); wm_st32(m, a41, v41 & 0xFFFFFFFF); V[mod.vsp-1] = o41 }
628 if asub == 0x42 { mod.vsp = mod.vsp - 1; let v42: i64 = V[mod.vsp]; let a42: i64 = V[mod.vsp-1] + aoff; if a42 < 0 { wm_trap(7, op, a42, 0) } if a42 + 8 > mod.mem_bytes { wm_trap(7, op, a42, mod.mem_bytes) } let o42: i64 = wm_ld64(m, a42); wm_st64(m, a42, v42); V[mod.vsp-1] = o42 }
629 if asub == 0x48 { mod.vsp = mod.vsp - 1; let n48: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let e48: i64 = V[mod.vsp]; let a48: i64 = V[mod.vsp-1] + aoff; if a48 < 0 { wm_trap(7, op, a48, 0) } if a48 + 4 > mod.mem_bytes { wm_trap(7, op, a48, mod.mem_bytes) } let o48: i64 = wm_ld32(m, a48); if o48 == (e48 & 0xFFFFFFFF) { wm_st32(m, a48, n48 & 0xFFFFFFFF) } V[mod.vsp-1] = o48 }
630 if asub == 0x49 { mod.vsp = mod.vsp - 1; let n49: i64 = V[mod.vsp]; mod.vsp = mod.vsp - 1; let e49: i64 = V[mod.vsp]; let a49: i64 = V[mod.vsp-1] + aoff; if a49 < 0 { wm_trap(7, op, a49, 0) } if a49 + 8 > mod.mem_bytes { wm_trap(7, op, a49, mod.mem_bytes) } let o49: i64 = wm_ld64(m, a49); if o49 == e49 { wm_st64(m, a49, n49) } V[mod.vsp-1] = o49 }
631 if asub == 0x01 { mod.vsp = mod.vsp - 1; mod.vsp = mod.vsp - 1; let e01: i64 = V[mod.vsp]; let a01: i64 = V[mod.vsp-1] + aoff; if a01 < 0 { wm_trap(6, op, a01, 0) } if a01 + 4 > mod.mem_bytes { wm_trap(6, op, a01, mod.mem_bytes) } var r01: i64 = 2; if wm_ld32(m, a01) != (e01 & 0xFFFFFFFF) { r01 = 1 } V[mod.vsp-1] = r01 }
632 if asub == 0x00 { mod.vsp = mod.vsp - 1; let a00: i64 = V[mod.vsp-1] + aoff; if a00 < 0 { wm_trap(6, op, a00, 0) } if a00 + 4 > mod.mem_bytes { wm_trap(6, op, a00, mod.mem_bytes) } V[mod.vsp-1] = 0 }
633 }
634 // --- F618 wasm-SIMD subset (0xFD prefix). v128 values = pointers into the 16B-slot ring arena. ---
635 if op == 0xFD {
636 if (g_v128_arena as i64) == 0 { g_v128_arena = sys_mmap(K_MAGIC_65536) }
637 let fxsub: i64 = wv_uleb(b, pos)
638 if fxsub == 0 { // v128.load memarg
639 wv_uleb(b, pos)
640 let fxo: i64 = wv_uleb(b, pos)
641 let fxa: i64 = V[mod.vsp-1] + fxo
642 if fxa < 0 { wm_trap(6, 0xFD, fxa, 0) }
643 if fxa + 16 > mod.mem_bytes { wm_trap(6, 0xFD, fxa, mod.mem_bytes) }
644 let fxsl: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
645 g_v128_idx = g_v128_idx + 1
646 let fxsp: *u8 = fxsl as *u8
647 var fxk: i64 = 0
648 while fxk < 16 { fxsp[fxk] = m[fxa + fxk]; fxk = fxk + 1 }
649 V[mod.vsp-1] = fxsl
650 }
651 if fxsub == 0x73 { // i8x16.sub_sat_u: pop b, a -> per-byte max(a-b, 0)
652 mod.vsp = mod.vsp - 1
653 let fxpb: *u8 = V[mod.vsp] as *u8
654 let fxpa: *u8 = V[mod.vsp-1] as *u8
655 let fxsl2: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
656 g_v128_idx = g_v128_idx + 1
657 let fxso: *u8 = fxsl2 as *u8
658 var fxk2: i64 = 0
659 while fxk2 < 16 {
660 var fxd: i64 = (fxpa[fxk2] as i64) - (fxpb[fxk2] as i64)
661 if fxd < 0 { fxd = 0 }
662 fxso[fxk2] = fxd as u8
663 fxk2 = fxk2 + 1 }
664 V[mod.vsp-1] = fxsl2
665 }
666 if fxsub == 0x50 { // v128.or: pop b, a -> per-byte a|b
667 mod.vsp = mod.vsp - 1
668 let fxob: *u8 = V[mod.vsp] as *u8
669 let fxoa: *u8 = V[mod.vsp-1] as *u8
670 let fxsl3: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
671 g_v128_idx = g_v128_idx + 1
672 let fxoo: *u8 = fxsl3 as *u8
673 var fxk3: i64 = 0
674 while fxk3 < 16 {
675 let fxv3: i64 = (fxoa[fxk3] as i64) | (fxob[fxk3] as i64)
676 fxoo[fxk3] = fxv3 as u8
677 fxk3 = fxk3 + 1 }
678 V[mod.vsp-1] = fxsl3
679 }
680 if fxsub == 0x7D { // i16x8.extadd_pairwise_i8x16_u: 16 u8 -> 8 u16 pair sums (LE)
681 let fxea: *u8 = V[mod.vsp-1] as *u8
682 let fxsl4: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
683 g_v128_idx = g_v128_idx + 1
684 let fxeo: *u8 = fxsl4 as *u8
685 var fxj: i64 = 0
686 while fxj < 8 {
687 let fxs: i64 = (fxea[fxj*2] as i64) + (fxea[fxj*2+1] as i64)
688 fxeo[fxj*2] = (fxs & 0xFF) as u8
689 fxeo[fxj*2+1] = ((fxs >> 8) & 0xFF) as u8
690 fxj = fxj + 1 }
691 V[mod.vsp-1] = fxsl4
692 }
693 if fxsub == 0x7C { // i16x8.extadd_pairwise_i8x16_s (SIGNED twin -- the mutation gate's tooth)
694 let fxsa: *u8 = V[mod.vsp-1] as *u8
695 let fxsl5: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
696 g_v128_idx = g_v128_idx + 1
697 let fxsoo: *u8 = fxsl5 as *u8
698 var fxj2: i64 = 0
699 while fxj2 < 8 {
700 var fxv0: i64 = fxsa[fxj2*2] as i64
701 if fxv0 >= 128 { fxv0 = fxv0 - 256 }
702 var fxv1: i64 = fxsa[fxj2*2+1] as i64
703 if fxv1 >= 128 { fxv1 = fxv1 - 256 }
704 let fxs2: i64 = (fxv0 + fxv1) & 0xFFFF
705 fxsoo[fxj2*2] = (fxs2 & 0xFF) as u8
706 fxsoo[fxj2*2+1] = ((fxs2 >> 8) & 0xFF) as u8
707 fxj2 = fxj2 + 1 }
708 V[mod.vsp-1] = fxsl5
709 }
710 if fxsub == 0x7F { // i32x4.extadd_pairwise_i16x8_u: 8 u16 -> 4 u32 pair sums (LE)
711 let fxwa: *u8 = V[mod.vsp-1] as *u8
712 let fxsl6: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16
713 g_v128_idx = g_v128_idx + 1
714 let fxwo: *u8 = fxsl6 as *u8
715 var fxj3: i64 = 0
716 while fxj3 < 4 {
717 let fxw0: i64 = (fxwa[fxj3*4] as i64) | ((fxwa[fxj3*4+1] as i64) << 8)
718 let fxw1: i64 = (fxwa[fxj3*4+2] as i64) | ((fxwa[fxj3*4+3] as i64) << 8)
719 let fxs3: i64 = fxw0 + fxw1
720 fxwo[fxj3*4] = (fxs3 & 0xFF) as u8
721 fxwo[fxj3*4+1] = ((fxs3 >> 8) & 0xFF) as u8
722 fxwo[fxj3*4+2] = ((fxs3 >> 16) & 0xFF) as u8
723 fxwo[fxj3*4+3] = ((fxs3 >> 24) & 0xFF) as u8
724 fxj3 = fxj3 + 1 }
725 V[mod.vsp-1] = fxsl6
726 }
727 if fxsub == 0x1B { // i32x4.extract_lane <laneidx>: v128 slot -> u32 lane (LE)
728 let fxln: i64 = wv_u8(b, pos)
729 let fxxa: *u8 = V[mod.vsp-1] as *u8
730 let fxoff: i64 = fxln * 4
731 let fxr: i64 = (fxxa[fxoff] as i64) | ((fxxa[fxoff+1] as i64) << 8) | ((fxxa[fxoff+2] as i64) << 16) | ((fxxa[fxoff+3] as i64) << 24)
732 V[mod.vsp-1] = fxr
733 }
734 }
735 // --- fail-fast state-corruption guards (never trip on a faithful run) ---
736 if mod.csp < cbase { wm_trap(1, op, mod.csp, cbase) }
737 if mod.csp >= K_MAGIC_4090 { wm_trap(2, op, mod.csp, 0) }
738 if mod.vsp < vbase { wm_trap(3, op, mod.vsp, vbase) }
739 if mod.vsp >= K_MAGIC_65530 { wm_trap(4, op, mod.vsp, 0) }
740 if mod.lsp >= K_MAGIC_262140 { wm_trap(5, op, mod.lsp, 0) }
741 }
742 }
743 if running == 0 { if mod.vsp > vbase { retval = V[mod.vsp - 1] } }
744 mod.vsp = vbase
745 mod.lsp = lbase
746 mod.csp = cbase
747 sys_munmap(pos as *u8, 8) // free this frame's code cursor (was a per-call leak; recursion-safe LIFO)
748 return retval
749}
750// convenience: run an exported function by name with up to 5 args.
751func wm_run(mod: *WasmMod, name: *u8, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64, na: i64) -> i64 {
752 let fidx: i64 = wm_find_export(mod, name)
753 if fidx < 0 { return 0 - K_MAGIC_999999 }
754 let args: *i64 = sys_mmap(8*8) as *i64
755 args[0]=a0; args[1]=a1; args[2]=a2; args[3]=a3; args[4]=a4
756 return wm_call(mod, fidx, args, na)
757}