code wiki / (root) / nx_wasm_vm.nx

nx_wasm_vm.nx source

↩ module page · 640 lines · 38033 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// Parse the whole module into mod (arrays pre-mmap'd by the caller / wm_new). Returns 0 on success, negative on error. 98func wm_parse(mod: *WasmMod) -> i64 { 99 let b: *u8 = mod.bytes 100 let n: i64 = mod.len 101 let pos: *i64 = sys_mmap(8) as *i64 102 pos[0] = 0 103 // magic + version 104 if b[0] != (0 as u8) { return 0 - 1 } 105 if b[1] != (0x61 as u8) { return 0 - 1 } 106 if b[2] != (0x73 as u8) { return 0 - 1 } 107 if b[3] != (0x6d as u8) { return 0 - 1 } 108 pos[0] = 8 109 mod.n_types = 0; mod.n_imports = 0; mod.n_funcs = 0; mod.n_exports = 0; mod.mem_bytes = 0 110 var fcount: i64 = 0 // running count of DEFINED functions seen in the function section 111 while pos[0] < n { 112 let sid: i64 = wv_u8(b, pos) 113 let ssize: i64 = wv_uleb(b, pos) 114 let send: i64 = pos[0] + ssize 115 if sid == 1 { 116 // TYPE section 117 let ct: i64 = wv_uleb(b, pos) 118 mod.n_types = ct 119 var i: i64 = 0 120 while i < ct { 121 let form: i64 = wv_u8(b, pos) // 0x60 func 122 let np: i64 = wv_uleb(b, pos) 123 var k: i64 = 0; while k < np { wv_u8(b, pos); k = k + 1 } 124 let nr: i64 = wv_uleb(b, pos) 125 var k2: i64 = 0; while k2 < nr { wv_u8(b, pos); k2 = k2 + 1 } 126 mod.type_nparam[i] = np 127 mod.type_nres[i] = nr 128 i = i + 1 129 } 130 } else { if sid == 2 { 131 // IMPORT section -- count function imports (they occupy the low func-index range) 132 let ci: i64 = wv_uleb(b, pos) 133 var i: i64 = 0 134 while i < ci { 135 let ml: i64 = wv_uleb(b, pos); var k: i64 = 0; while k < ml { wv_u8(b, pos); k = k + 1 } 136 let fl: i64 = wv_uleb(b, pos); var k2: i64 = 0; while k2 < fl { wv_u8(b, pos); k2 = k2 + 1 } 137 let kind: i64 = wv_u8(b, pos) 138 if kind == 0 { wv_uleb(b, pos); mod.n_imports = mod.n_imports + 1 } 139 else { if kind == 1 { wv_u8(b, pos); wv_u8(b, pos); let fl2: i64 = wv_uleb(b, pos); if fl2 != 0 { wv_uleb(b, pos) } } 140 else { if kind == 2 { let fl3: i64 = wv_uleb(b, pos); wv_uleb(b, pos); if fl3 != 0 { wv_uleb(b, pos) } } 141 else { wv_u8(b, pos); wv_uleb(b, pos) } } } 142 i = i + 1 143 } 144 } else { if sid == 3 { 145 // FUNCTION section -- type index per defined function 146 let cf: i64 = wv_uleb(b, pos) 147 mod.n_funcs = cf 148 var i: i64 = 0 149 while i < cf { mod.func_type[i] = wv_uleb(b, pos); i = i + 1 } 150 } else { if sid == 5 { 151 // MEMORY section -- first memory's minimum size (pages of 64 KiB) 152 let cm: i64 = wv_uleb(b, pos) 153 if cm > 0 { 154 let flags: i64 = wv_u8(b, pos) 155 let minp: i64 = wv_uleb(b, pos) 156 if (flags & 1) != 0 { wv_uleb(b, pos) } 157 mod.mem_bytes = minp * K_MAGIC_65536 158 // ALLOCATE the linear memory (found 2026-07-02: mem stayed the wm_new NULL -> the FIRST 159 // load/store in any standalone module segfaulted at the raw address, sailing past the 160 // bounds guards whose mem_bytes was correctly set) 161 mod.mem = sys_mmap(mod.mem_bytes) 162 var j: i64 = 1 163 while j < cm { let f: i64 = wv_u8(b, pos); wv_uleb(b, pos); if (f & 1) != 0 { wv_uleb(b, pos) } j = j + 1 } 164 } 165 } else { if sid == 7 { 166 // EXPORT section 167 let ce: i64 = wv_uleb(b, pos) 168 mod.n_exports = ce 169 var i: i64 = 0 170 while i < ce { 171 let nl: i64 = wv_uleb(b, pos) 172 mod.exp_name_off[i] = pos[0] 173 mod.exp_name_len[i] = nl 174 var k: i64 = 0; while k < nl { wv_u8(b, pos); k = k + 1 } 175 mod.exp_kind[i] = wv_u8(b, pos) 176 mod.exp_index[i] = wv_uleb(b, pos) 177 i = i + 1 178 } 179 } else { if sid == 10 { 180 // CODE section -- per defined function: locals decl then the instruction body 181 let cc: i64 = wv_uleb(b, pos) 182 var i: i64 = 0 183 while i < cc { 184 let bodysize: i64 = wv_uleb(b, pos) 185 let bodystart: i64 = pos[0] 186 let bodyend: i64 = bodystart + bodysize 187 // locals declaration 188 let nld: i64 = wv_uleb(b, pos) 189 var nloc: i64 = 0 190 var d: i64 = 0 191 while d < nld { let cnt: i64 = wv_uleb(b, pos); wv_u8(b, pos); nloc = nloc + cnt; d = d + 1 } 192 let tyidx: i64 = mod.func_type[i] 193 mod.func_nloc[i] = mod.type_nparam[tyidx] + nloc 194 mod.func_code[i] = pos[0] 195 mod.func_end[i] = bodyend 196 pos[0] = bodyend 197 i = i + 1 198 } 199 fcount = cc 200 } else { 201 // skip any other section 202 } } } } } } 203 pos[0] = send 204 } 205 if fcount == 0 { fcount = mod.n_funcs } 206 return 0 207} 208 209// compare an export name (in the module bytes) to a C-string needle; 1 if equal. 210func wm_name_eq(b: *u8, off: i64, len: i64, needle: *u8) -> i64 { 211 var i: i64 = 0 212 while i < len { if b[off + i] != needle[i] { return 0 } i = i + 1 } 213 if needle[len] != (0 as u8) { return 0 } // needle must end exactly 214 return 1 215} 216// find an exported function's GLOBAL function index by name, or -1. 217func wm_find_export(mod: *WasmMod, needle: *u8) -> i64 { 218 var i: i64 = 0 219 while i < mod.n_exports { 220 if mod.exp_kind[i] == 0 { 221 if wm_name_eq(mod.bytes, mod.exp_name_off[i], mod.exp_name_len[i], needle) == 1 { return mod.exp_index[i] } 222 } 223 i = i + 1 224 } 225 return 0 - 1 226} 227// allocate a module + its arrays around a loaded .wasm image. 228func wm_new(bytes: *u8, len: i64) -> *WasmMod { 229 let mod: *WasmMod = sys_mmap(256) as *WasmMod 230 mod.bytes = bytes; mod.len = len 231 mod.type_nparam = sys_mmap(K_MAGIC_1024*8) as *i64 232 mod.type_nres = sys_mmap(K_MAGIC_1024*8) as *i64 233 mod.func_type = sys_mmap(K_MAGIC_2048*8) as *i64 234 mod.func_code = sys_mmap(K_MAGIC_2048*8) as *i64 235 mod.func_end = sys_mmap(K_MAGIC_2048*8) as *i64 236 mod.func_nloc = sys_mmap(K_MAGIC_2048*8) as *i64 237 mod.exp_name_off = sys_mmap(K_MAGIC_1024*8) as *i64 238 mod.exp_name_len = sys_mmap(K_MAGIC_1024*8) as *i64 239 mod.exp_kind = sys_mmap(K_MAGIC_1024*8) as *i64 240 mod.exp_index = sys_mmap(K_MAGIC_1024*8) as *i64 241 mod.mem = 0 as *u8 242 mod.vstack = sys_mmap(K_MAGIC_65536*8) as *i64 243 mod.locals = sys_mmap(K_MAGIC_262144*8) as *i64 244 mod.ctrl_kind = sys_mmap(K_MAGIC_4096*8) as *i64 245 mod.ctrl_pc = sys_mmap(K_MAGIC_4096*8) as *i64 246 mod.memo_k = sys_mmap(K_MAGIC_131072*8) as *i64 247 mod.memo_v = sys_mmap(K_MAGIC_131072*8) as *i64 248 mod.vsp = 0; mod.lsp = 0; mod.csp = 0 249 return mod 250} 251// scan-memo: open-address hash keyed by startpos*2+kind (key 0 = empty slot; code offsets are always > 0) 252func wm_memo_get(mod: *WasmMod, key: i64) -> i64 { 253 var i: i64 = (key * K_MAGIC_2654435761) % K_MAGIC_131072 254 if i < 0 { i = i + K_MAGIC_131072 } 255 var probes: i64 = 0 256 while probes < K_MAGIC_131072 { 257 if mod.memo_k[i] == key { return mod.memo_v[i] } 258 if mod.memo_k[i] == 0 { return 0 } 259 i = i + 1 260 if i >= K_MAGIC_131072 { i = 0 } 261 probes = probes + 1 262 } 263 return 0 264} 265func wm_memo_put(mod: *WasmMod, key: i64, val: i64) -> i64 { 266 var i: i64 = (key * K_MAGIC_2654435761) % K_MAGIC_131072 267 if i < 0 { i = i + K_MAGIC_131072 } 268 var probes: i64 = 0 269 while probes < K_MAGIC_131072 { 270 if mod.memo_k[i] == 0 { mod.memo_k[i] = key; mod.memo_v[i] = val; return 0 } 271 if mod.memo_k[i] == key { mod.memo_v[i] = val; return 0 } 272 i = i + 1 273 if i >= K_MAGIC_131072 { i = 0 } 274 probes = probes + 1 275 } 276 return 0 - 1 277} 278 279// ---------------- executor ---------------- 280// faithful little-endian 8-byte memory access (exactly what i64.load/i64.store do in the shipped wasm). 281func 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 } 282func 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 } 283 284// advance past an opcode's immediate operands (for the structured-control forward scan). pc points just AFTER the op byte. 285func wm_skip_imm(b: *u8, op: i64, pos: *i64) -> i64 { 286 if op == 0x02 { wv_u8(b, pos); return 0 } // block: blocktype byte 287 if op == 0x03 { wv_u8(b, pos); return 0 } // loop: blocktype byte 288 if op == 0x04 { wv_u8(b, pos); return 0 } // if: blocktype byte 289 if op == 0x0C { wv_uleb(b, pos); return 0 } // br 290 if op == 0x0D { wv_uleb(b, pos); return 0 } // br_if 291 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 292 if op == 0x10 { wv_uleb(b, pos); return 0 } // call 293 if op == 0x20 { wv_uleb(b, pos); return 0 } // local.get 294 if op == 0x21 { wv_uleb(b, pos); return 0 } // local.set 295 if op == 0x22 { wv_uleb(b, pos); return 0 } // local.tee 296 if op == 0x41 { wv_sleb(b, pos); return 0 } // i32.const 297 if op == 0x42 { wv_sleb(b, pos); return 0 } // i64.const 298 if op >= 0x28 { if op <= 0x3E { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } } // load/store memarg (align, offset) 299 if op == 0xFD { // F618 SIMD prefix: uleb sub-opcode + per-sub immediates 300 let fsub: i64 = wv_uleb(b, pos) 301 if fsub == 0 { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } // v128.load memarg 302 if fsub == 11 { wv_uleb(b, pos); wv_uleb(b, pos); return 0 } // v128.store memarg 303 if fsub == 27 { wv_u8(b, pos); return 0 } // i32x4.extract_lane laneidx 304 return 0 // sub_sat_u/or/extadds: no immediate 305 } 306 return 0 // all other ops: no immediate 307} 308// find the pc just past the `end` that matches the block/loop/if opened at *pos (pos points after the opener's blocktype). 309func wm_match_end(b: *u8, startpos: i64) -> i64 { 310 let pos: *i64 = sys_mmap(8) as *i64; pos[0] = startpos 311 var depth: i64 = 1 312 var steps: i64 = 0 313 while depth > 0 { 314 // runaway guard: a scanner that never closes = an opcode whose immediates wm_skip_imm doesn't know 315 // (desync). Fail LOUD with the last opcode instead of walking off the mapping (was a silent SIGSEGV). 316 steps = steps + 1 317 if steps > K_MAGIC_4000000 { sys_write(2, "SCAN-RUNAWAY match_end\n" as *u8, 23); sys_exit(9) } 318 let op: i64 = wv_u8(b, pos) 319 if op == 0x02 { depth = depth + 1 } else { 320 if op == 0x03 { depth = depth + 1 } else { 321 if op == 0x04 { depth = depth + 1 } else { 322 if op == 0x0B { depth = depth - 1 } else { 323 wm_skip_imm(b, op, pos) 324 } } } } 325 if op == 0x02 { wv_u8(b, pos) } // also skip the opener blocktype we counted 326 if op == 0x03 { wv_u8(b, pos) } 327 if op == 0x04 { wv_u8(b, pos) } 328 } 329 let rme: i64 = pos[0]; sys_munmap(pos as *u8, 8); return rme // free the scan cursor (was a per-open leak) 330} 331 332// find where execution goes when an `if` condition is FALSE: the position after the matching `else` opcode, or -- 333// if there is no else -- the position after the matching `end` (i.e., skip the whole construct). startpos is just 334// after the if's blocktype byte. 335func wm_match_else(b: *u8, startpos: i64) -> i64 { 336 let pos: *i64 = sys_mmap(8) as *i64; pos[0] = startpos 337 var depth: i64 = 1 338 var steps: i64 = 0 339 while depth > 0 { 340 steps = steps + 1 341 if steps > K_MAGIC_4000000 { sys_write(2, "SCAN-RUNAWAY match_else\n" as *u8, 24); sys_exit(9) } 342 let op: i64 = wv_u8(b, pos) 343 if op == 0x02 { depth = depth + 1; wv_u8(b, pos) } else { 344 if op == 0x03 { depth = depth + 1; wv_u8(b, pos) } else { 345 if op == 0x04 { depth = depth + 1; wv_u8(b, pos) } else { 346 if op == 0x05 { if depth == 1 { let r1: i64 = pos[0]; sys_munmap(pos as *u8, 8); return r1 } } else { 347 if op == 0x0B { depth = depth - 1 } else { 348 wm_skip_imm(b, op, pos) 349 } } } } } 350 } 351 let r2: i64 = pos[0]; sys_munmap(pos as *u8, 8); return r2 // free the scan cursor (was a per-if leak) 352} 353// perform a branch to label depth `lbl` (0 = innermost): loop re-enters at its body start (keep its frame), 354// block/if exit to past their end (drop their frame). Sets pos[0] and the control stack pointer. 355func wm_do_br(mod: *WasmMod, pos: *i64, lbl: i64) -> i64 { 356 let t: i64 = mod.csp - 1 - lbl 357 pos[0] = mod.ctrl_pc[t] 358 if mod.ctrl_kind[t] == 1 { mod.csp = t + 1 } else { mod.csp = t } 359 return 0 360} 361// call defined function fidx with nargs i64 args in args[]; returns the i64 result (0 if void). Faithful wasm exec. 362// FLAT dispatch (one `if op == X` per opcode -- this codebase's idiom; avoids deep if/else brace fragility). 363// --- fail-fast diagnostic helpers: a faithful VM should never trip these; firing => real corruption --- 364func wm_phexf(v: i64, fd: i64) -> i64 { 365 let d: *u8 = "0123456789abcdef" as *u8 366 let h: *u8 = sys_mmap(2) 367 h[0] = d[(v >> 4) & 0xF]; h[1] = d[v & 0xF] 368 sys_write(fd, h, 2); return 0 369} 370func wm_pn2f(v: i64, fd: i64) -> i64 { 371 let b: *u8 = sys_mmap(28); var x: i64 = v 372 if x < 0 { sys_write(fd, "-" as *u8, 1); x = 0 - x } 373 if x == 0 { sys_write(fd, "0" as *u8, 1); return 0 } 374 var d: i64 = 0; var y: i64 = x 375 while y > 0 { d = d + 1; y = y / 10 } 376 var i: i64 = d - 1; y = x 377 while i >= 0 { b[i] = (48 + (y % 10)) as u8; y = y / 10; i = i - 1 } 378 sys_write(fd, b, d); return 0 379} 380func wm_pn2(v: i64) -> i64 { return wm_pn2f(v, 2) } 381func wm_phex(v: i64) -> i64 { return wm_phexf(v, 2) } 382func wm_trap1(code: i64, op: i64, a: i64, bb: i64, fd: i64) -> i64 { 383 sys_write(fd, "VM-TRAP code=" as *u8, 13); wm_pn2f(code, fd) 384 sys_write(fd, " op=0x" as *u8, 6); wm_phexf(op, fd) 385 sys_write(fd, " a=" as *u8, 3); wm_pn2f(a, fd) 386 sys_write(fd, " b=" as *u8, 3); wm_pn2f(bb, fd) 387 sys_write(fd, "\n" as *u8, 1) 388 return 0 389} 390func wm_trap(code: i64, op: i64, a: i64, bb: i64) -> i64 { 391 // BOTH streams: stderr capture gets eaten by some harness layers (learned 2026-07-02) -- stdout survives 392 wm_trap1(code, op, a, bb, 1) 393 wm_trap1(code, op, a, bb, 2) 394 sys_exit(8); return 0 395} 396func wm_call(mod: *WasmMod, fidx: i64, args: *i64, nargs: i64) -> i64 { 397 let b: *u8 = mod.bytes 398 let di: i64 = fidx - mod.n_imports 399 let nloc: i64 = mod.func_nloc[di] 400 let lbase: i64 = mod.lsp 401 mod.lsp = mod.lsp + nloc 402 let L: *i64 = mod.locals 403 var i: i64 = 0 404 while i < nloc { if i < nargs { L[lbase+i] = args[i] } else { L[lbase+i] = 0 } i = i + 1 } 405 let vbase: i64 = mod.vsp 406 let cbase: i64 = mod.csp 407 let V: *i64 = mod.vstack 408 let m: *u8 = mod.mem 409 let pos: *i64 = sys_mmap(8) as *i64 410 pos[0] = mod.func_code[di] 411 let cend: i64 = mod.func_end[di] 412 var retval: i64 = 0 413 var running: i64 = 1 414 while running == 1 { 415 if pos[0] >= cend { running = 0 } 416 if running == 1 { 417 let op: i64 = wv_u8(b, pos) 418 // control flow 419 if op == 0x0B { if mod.csp <= cbase { running = 0 } else { mod.csp = mod.csp - 1 } } 420 if op == 0x02 { 421 wv_u8(b, pos) 422 var e02: i64 = wm_memo_get(mod, pos[0] * 2) 423 if e02 == 0 { e02 = wm_match_end(b, pos[0]); wm_memo_put(mod, pos[0] * 2, e02) } 424 mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = e02; mod.csp = mod.csp + 1 425 } 426 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 } 427 if op == 0x04 { 428 wv_u8(b, pos) 429 var endp: i64 = wm_memo_get(mod, pos[0] * 2) 430 if endp == 0 { endp = wm_match_end(b, pos[0]); wm_memo_put(mod, pos[0] * 2, endp) } 431 var elsep: i64 = wm_memo_get(mod, pos[0] * 2 + 1) 432 if elsep == 0 { elsep = wm_match_else(b, pos[0]); wm_memo_put(mod, pos[0] * 2 + 1, elsep) } 433 mod.vsp = mod.vsp - 1; let cond: i64 = V[mod.vsp] 434 if cond != 0 { mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = endp; mod.csp = mod.csp + 1 } 435 if cond == 0 { 436 if elsep < endp { mod.ctrl_kind[mod.csp] = 0; mod.ctrl_pc[mod.csp] = endp; mod.csp = mod.csp + 1; pos[0] = elsep } 437 if elsep >= endp { pos[0] = endp } 438 } 439 } 440 if op == 0x05 { mod.csp = mod.csp - 1; pos[0] = mod.ctrl_pc[mod.csp] } 441 if op == 0x0C { let lbl: i64 = wv_uleb(b, pos); wm_do_br(mod, pos, lbl) } 442 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) } } 443 if op == 0x0E { 444 let cnt: i64 = wv_uleb(b, pos) 445 mod.vsp = mod.vsp - 1; var idx: i64 = V[mod.vsp] 446 if idx < 0 { idx = cnt } 447 if idx > cnt { idx = cnt } 448 var lbl: i64 = 0; var k: i64 = 0 449 while k <= cnt { let l: i64 = wv_uleb(b, pos); if k == idx { lbl = l } k = k + 1 } 450 wm_do_br(mod, pos, lbl) 451 } 452 if op == 0x0F { if mod.vsp > vbase { retval = V[mod.vsp - 1] } running = 0 } 453 if op == 0x10 { 454 let cf: i64 = wv_uleb(b, pos) 455 let cdi: i64 = cf - mod.n_imports 456 let cnp: i64 = mod.type_nparam[mod.func_type[cdi]] 457 mod.vsp = mod.vsp - cnp 458 let r: i64 = wm_call(mod, cf, ((mod.vstack as i64) + mod.vsp*8) as *i64, cnp) 459 if mod.type_nres[mod.func_type[cdi]] > 0 { V[mod.vsp] = r; mod.vsp = mod.vsp + 1 } 460 } 461 // values / locals 462 if op == 0x20 { let x: i64 = wv_uleb(b, pos); V[mod.vsp] = L[lbase + x]; mod.vsp = mod.vsp + 1 } 463 if op == 0x21 { let x: i64 = wv_uleb(b, pos); mod.vsp = mod.vsp - 1; L[lbase + x] = V[mod.vsp] } 464 if op == 0x22 { let x: i64 = wv_uleb(b, pos); L[lbase + x] = V[mod.vsp - 1] } 465 if op == 0x41 { V[mod.vsp] = wv_sleb(b, pos); mod.vsp = mod.vsp + 1 } 466 if op == 0x42 { V[mod.vsp] = wv_sleb(b, pos); mod.vsp = mod.vsp + 1 } 467 if op == 0x1A { mod.vsp = mod.vsp - 1 } 468 // memory (faithful 8-byte, exactly as the wat specifies) 469 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) } 470 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 } 471 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) } 472 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) } 473 // byte-width memory (the fixed backend now emits these for u8 arrays) 474 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 } 475 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 } 476 // full subword-memory family (2026-07-10 sext debt fix): signed loads sign-extend via 477 // arithmetic (the VM's own *u8 reads zero-extend by language semantics -- unchanged); 478 // 16/32-bit widths were previously NOT implemented at all (backend could emit -> VM had no op). 479 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 } 480 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) } 481 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 } 482 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) } 483 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 } 484 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 } 485 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 } 486 // conversions 487 if op == 0xA7 { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } 488 if op == 0xAD { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } 489 // f32 (added 2026-07-02 for the mineworld render vet): values are f32 BIT PATTERNS in the low 32 490 // bits of the untyped i64 slots -- exactly nx_f32_hw's convention, so each op IS the native SSE op. 491 if op == 0xB4 { V[mod.vsp-1] = f32_of(V[mod.vsp-1]) } // f32.convert_i64_s 492 if op == 0xAE { V[mod.vsp-1] = f32_int(V[mod.vsp-1]) } // i64.trunc_f32_s 493 if op == 0xBE { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } // f32.reinterpret_i32 494 if op == 0xBC { V[mod.vsp-1] = V[mod.vsp-1] & 0xFFFFFFFF } // i32.reinterpret_f32 495 if op == 0x92 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_add(V[mod.vsp-1], V[mod.vsp]) } // f32.add 496 if op == 0x94 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_mul(V[mod.vsp-1], V[mod.vsp]) } // f32.mul 497 if op == 0x95 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = f32_div(V[mod.vsp-1], V[mod.vsp]) } // f32.div 498 // i64 arithmetic 499 if op == 0x7C { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] + V[mod.vsp] } 500 if op == 0x7D { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] - V[mod.vsp] } 501 if op == 0x7E { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] * V[mod.vsp] } 502 if op == 0x7F { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] / V[mod.vsp] } 503 if op == 0x81 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] % V[mod.vsp] } 504 if op == 0x83 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] & V[mod.vsp] } 505 if op == 0x84 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] | V[mod.vsp] } 506 if op == 0x85 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] ^ V[mod.vsp] } 507 if op == 0x86 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] << V[mod.vsp] } 508 if op == 0x87 { mod.vsp=mod.vsp-1; V[mod.vsp-1] = V[mod.vsp-1] >> V[mod.vsp] } 509 // i64 compares (push 1/0) 510 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 } } 511 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 } } 512 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 } } 513 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 } } 514 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 } } 515 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 } } 516 if op == 0x50 { if V[mod.vsp-1]==0 { V[mod.vsp-1]=1 } else { V[mod.vsp-1]=0 } } 517 // --- F618 wasm-SIMD subset (0xFD prefix). v128 values = pointers into the 16B-slot ring arena. --- 518 if op == 0xFD { 519 if (g_v128_arena as i64) == 0 { g_v128_arena = sys_mmap(K_MAGIC_65536) } 520 let fxsub: i64 = wv_uleb(b, pos) 521 if fxsub == 0 { // v128.load memarg 522 wv_uleb(b, pos) 523 let fxo: i64 = wv_uleb(b, pos) 524 let fxa: i64 = V[mod.vsp-1] + fxo 525 if fxa < 0 { wm_trap(6, 0xFD, fxa, 0) } 526 if fxa + 16 > mod.mem_bytes { wm_trap(6, 0xFD, fxa, mod.mem_bytes) } 527 let fxsl: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 528 g_v128_idx = g_v128_idx + 1 529 let fxsp: *u8 = fxsl as *u8 530 var fxk: i64 = 0 531 while fxk < 16 { fxsp[fxk] = m[fxa + fxk]; fxk = fxk + 1 } 532 V[mod.vsp-1] = fxsl 533 } 534 if fxsub == 0x73 { // i8x16.sub_sat_u: pop b, a -> per-byte max(a-b, 0) 535 mod.vsp = mod.vsp - 1 536 let fxpb: *u8 = V[mod.vsp] as *u8 537 let fxpa: *u8 = V[mod.vsp-1] as *u8 538 let fxsl2: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 539 g_v128_idx = g_v128_idx + 1 540 let fxso: *u8 = fxsl2 as *u8 541 var fxk2: i64 = 0 542 while fxk2 < 16 { 543 var fxd: i64 = (fxpa[fxk2] as i64) - (fxpb[fxk2] as i64) 544 if fxd < 0 { fxd = 0 } 545 fxso[fxk2] = fxd as u8 546 fxk2 = fxk2 + 1 } 547 V[mod.vsp-1] = fxsl2 548 } 549 if fxsub == 0x50 { // v128.or: pop b, a -> per-byte a|b 550 mod.vsp = mod.vsp - 1 551 let fxob: *u8 = V[mod.vsp] as *u8 552 let fxoa: *u8 = V[mod.vsp-1] as *u8 553 let fxsl3: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 554 g_v128_idx = g_v128_idx + 1 555 let fxoo: *u8 = fxsl3 as *u8 556 var fxk3: i64 = 0 557 while fxk3 < 16 { 558 let fxv3: i64 = (fxoa[fxk3] as i64) | (fxob[fxk3] as i64) 559 fxoo[fxk3] = fxv3 as u8 560 fxk3 = fxk3 + 1 } 561 V[mod.vsp-1] = fxsl3 562 } 563 if fxsub == 0x7D { // i16x8.extadd_pairwise_i8x16_u: 16 u8 -> 8 u16 pair sums (LE) 564 let fxea: *u8 = V[mod.vsp-1] as *u8 565 let fxsl4: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 566 g_v128_idx = g_v128_idx + 1 567 let fxeo: *u8 = fxsl4 as *u8 568 var fxj: i64 = 0 569 while fxj < 8 { 570 let fxs: i64 = (fxea[fxj*2] as i64) + (fxea[fxj*2+1] as i64) 571 fxeo[fxj*2] = (fxs & 0xFF) as u8 572 fxeo[fxj*2+1] = ((fxs >> 8) & 0xFF) as u8 573 fxj = fxj + 1 } 574 V[mod.vsp-1] = fxsl4 575 } 576 if fxsub == 0x7C { // i16x8.extadd_pairwise_i8x16_s (SIGNED twin -- the mutation gate's tooth) 577 let fxsa: *u8 = V[mod.vsp-1] as *u8 578 let fxsl5: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 579 g_v128_idx = g_v128_idx + 1 580 let fxsoo: *u8 = fxsl5 as *u8 581 var fxj2: i64 = 0 582 while fxj2 < 8 { 583 var fxv0: i64 = fxsa[fxj2*2] as i64 584 if fxv0 >= 128 { fxv0 = fxv0 - 256 } 585 var fxv1: i64 = fxsa[fxj2*2+1] as i64 586 if fxv1 >= 128 { fxv1 = fxv1 - 256 } 587 let fxs2: i64 = (fxv0 + fxv1) & 0xFFFF 588 fxsoo[fxj2*2] = (fxs2 & 0xFF) as u8 589 fxsoo[fxj2*2+1] = ((fxs2 >> 8) & 0xFF) as u8 590 fxj2 = fxj2 + 1 } 591 V[mod.vsp-1] = fxsl5 592 } 593 if fxsub == 0x7F { // i32x4.extadd_pairwise_i16x8_u: 8 u16 -> 4 u32 pair sums (LE) 594 let fxwa: *u8 = V[mod.vsp-1] as *u8 595 let fxsl6: i64 = (g_v128_arena as i64) + (g_v128_idx % K_MAGIC_4096) * 16 596 g_v128_idx = g_v128_idx + 1 597 let fxwo: *u8 = fxsl6 as *u8 598 var fxj3: i64 = 0 599 while fxj3 < 4 { 600 let fxw0: i64 = (fxwa[fxj3*4] as i64) | ((fxwa[fxj3*4+1] as i64) << 8) 601 let fxw1: i64 = (fxwa[fxj3*4+2] as i64) | ((fxwa[fxj3*4+3] as i64) << 8) 602 let fxs3: i64 = fxw0 + fxw1 603 fxwo[fxj3*4] = (fxs3 & 0xFF) as u8 604 fxwo[fxj3*4+1] = ((fxs3 >> 8) & 0xFF) as u8 605 fxwo[fxj3*4+2] = ((fxs3 >> 16) & 0xFF) as u8 606 fxwo[fxj3*4+3] = ((fxs3 >> 24) & 0xFF) as u8 607 fxj3 = fxj3 + 1 } 608 V[mod.vsp-1] = fxsl6 609 } 610 if fxsub == 0x1B { // i32x4.extract_lane <laneidx>: v128 slot -> u32 lane (LE) 611 let fxln: i64 = wv_u8(b, pos) 612 let fxxa: *u8 = V[mod.vsp-1] as *u8 613 let fxoff: i64 = fxln * 4 614 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) 615 V[mod.vsp-1] = fxr 616 } 617 } 618 // --- fail-fast state-corruption guards (never trip on a faithful run) --- 619 if mod.csp < cbase { wm_trap(1, op, mod.csp, cbase) } 620 if mod.csp >= K_MAGIC_4090 { wm_trap(2, op, mod.csp, 0) } 621 if mod.vsp < vbase { wm_trap(3, op, mod.vsp, vbase) } 622 if mod.vsp >= K_MAGIC_65530 { wm_trap(4, op, mod.vsp, 0) } 623 if mod.lsp >= K_MAGIC_262140 { wm_trap(5, op, mod.lsp, 0) } 624 } 625 } 626 if running == 0 { if mod.vsp > vbase { retval = V[mod.vsp - 1] } } 627 mod.vsp = vbase 628 mod.lsp = lbase 629 mod.csp = cbase 630 sys_munmap(pos as *u8, 8) // free this frame's code cursor (was a per-call leak; recursion-safe LIFO) 631 return retval 632} 633// convenience: run an exported function by name with up to 5 args. 634func wm_run(mod: *WasmMod, name: *u8, a0: i64, a1: i64, a2: i64, a3: i64, a4: i64, na: i64) -> i64 { 635 let fidx: i64 = wm_find_export(mod, name) 636 if fidx < 0 { return 0 - K_MAGIC_999999 } 637 let args: *i64 = sys_mmap(8*8) as *i64 638 args[0]=a0; args[1]=a1; args[2]=a2; args[3]=a3; args[4]=a4 639 return wm_call(mod, fidx, args, na) 640}