code wiki / (root) / nx_wasm_vm_bounds_20260912.nx

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}