code wiki / _hdl_build / nx_wat_compiler.nx

nx_wat_compiler.nx source

↩ module page · 944 lines · 49877 B

1// nx_wat_compiler.nx -- G-WASM-001 milestone 3: the SOVEREIGN WAT-text -> WASM-binary pipeline 2// end to end (lexer + parser + binary emit), reusing leb128.nx. Reads a .wat file (the dialect 3// nxc2 --target wat emits) and writes a .wasm. NO node/python/WABT in this organ; verified 4// byte-exact vs WABT used ONCE as an alignment oracle, then dropped. 5// Subset this milestone handles (the f.wat function): (module (memory (export "memory") N) 6// (func (export "name") (param i64)* (result i64)? (local i64)* <body of: local.get/local.set/ 7// i64.const/i64.add> )). Remaining opcodes (control flow/call/load/store/etc) = next iterations. 8// usage: nx_wat_compiler <in.wat> <out.wasm> 9// STATUS: byte-exact vs WABT on REAL nxc2 output (g.wat: import + 2 funcs + call + br_table + 10// block/loop/br + mixed i32/i64 locals + arith). Handles: type-dedup, import section, multi- 11// function (function/code N entries), func symbol table + call, top-level & inline exports, 12// memory, named/numeric locals, 20+ opcodes, control flow + label depth, br_table. 13// GAMES: COMPLETE -- compiles nx_dungeon_alpha_wasm (a real game) byte-identical to WABT (9176B), 14// output instantiates + runs (GAME-LOGIC-OK). Handles flat if/else/end + i64.load/store/load8_u/store8. 15// GOTCHA fixed: i64.load8_u = 0x31 (0x30 is load8_s); buffers sized in BYTES not entries (overflow=corruption). 16// The full nx->wat->wasm path is sovereign; WABT/node only ever one-time align/verify oracles. 17// F618 wasm-SIMD FLIP (2026-07-21): + v128.load/store, i8x16.sub_sat_u, v128.or, extadd-pairwise 18// u8->u16->u32 (_u and the _s mutation twin), i32x4.extract_lane <lane>, and `(local $x v128)` 19// (valtype 0x7B). All 0xFD-prefixed, sub-opcodes spec-verified. Unknown mnemonics still REFUSE. 20// license_tier: ORIGINAL 21import "leb128.nx" 22import "nx_syscalls.nx" 23const K_MAGIC_65536: i64 = 65536 24const K_MAGIC_4194304: i64 = 4194304 25const K_MAGIC_2097152: i64 = 2097152 26const K_MAGIC_524288: i64 = 524288 27const K_MAGIC_8388608: i64 = 8388608 28 29func w8(b: *u8, p: i64, v: i64) -> i64 { b[p] = v as u8; return p + 1 } 30func wcopy(dst: *u8, dp: i64, src: *u8, n: i64) -> i64 { 31 var i: i64 = 0 32 while i < n { dst[dp + i] = src[i]; i = i + 1 } 33 return dp + n 34} 35func emit_section(out: *u8, p: i64, id: i64, sc: *u8, sclen: i64) -> i64 { 36 var q: i64 = w8(out, p, id) 37 q = q + uleb128_encode(sclen, out, q) 38 q = wcopy(out, q, sc, sclen) 39 return q 40} 41// LN33: refuse by name with the length DERIVED from the literal (a hand-counted length beside a string is a 42// second copy of its shape and the two drift). 43func wc_refuse(msg: *u8) -> i64 { 44 var n: i64 = 0 45 while msg[n] != (0 as u8) { n = n + 1 } 46 sys_write(1, msg, n) 47 sys_exit(1) 48 return 1 49} 50// LN33: the memtype limits flags byte -- 0 min only, 1 min+max, 3 shared (which the threads proposal 51// only allows together with a maximum; the callers refuse the shared-without-max case before asking). 52func wc_mem_flags(has_max: i64, shared: i64) -> i64 { 53 if shared == 1 { return 3 } 54 if has_max == 1 { return 1 } 55 return 0 56} 57func is_ws(c: i64) -> i64 { 58 if c == 32 { return 1 } 59 if c == 9 { return 1 } 60 if c == 10 { return 1 } 61 if c == 13 { return 1 } 62 return 0 63} 64func is_delim(c: i64) -> i64 { 65 if is_ws(c) == 1 { return 1 } 66 if c == 40 { return 1 } 67 if c == 41 { return 1 } 68 return 0 69} 70// lexer state ls[]: 0=pos 1=kind(0 eof,1 '(',2 ')',3 atom,4 string) 2=tok_start 3=tok_len 71func lex_next(buf: *u8, n: i64, ls: *i64) -> i64 { 72 var pos: i64 = ls[0] 73 var skip: i64 = 1 74 while skip == 1 { 75 skip = 0 76 var ws_go: i64 = 1 77 while ws_go == 1 { 78 if pos >= n { ws_go = 0 } else { 79 if is_ws(buf[pos] as i64) == 1 { pos = pos + 1 } else { ws_go = 0 } 80 } 81 } 82 if pos < n { 83 if buf[pos] == (59 as u8) { 84 if pos + 1 < n { 85 if buf[pos + 1] == (59 as u8) { 86 var cm: i64 = 1 87 while cm == 1 { 88 if pos >= n { cm = 0 } else { 89 if buf[pos] == (10 as u8) { cm = 0 } else { pos = pos + 1 } 90 } 91 } 92 skip = 1 93 } 94 } 95 } 96 } 97 } 98 if pos >= n { ls[1] = 0; ls[0] = pos; return 0 } 99 let c: i64 = buf[pos] as i64 100 if c == 40 { ls[1] = 1; ls[2] = pos; ls[3] = 1; ls[0] = pos + 1; return 0 } 101 if c == 41 { ls[1] = 2; ls[2] = pos; ls[3] = 1; ls[0] = pos + 1; return 0 } 102 if c == 34 { 103 let st: i64 = pos + 1 104 var q: i64 = st 105 var closed: i64=0 106 while q<n { 107 if buf[q]==(34 as u8) { closed=1 } 108 if closed==1 { ls[1]=4;ls[2]=st;ls[3]=q-st;ls[0]=q+1;return 0 } 109 q=q+1 110 } 111 if closed==0 { return wc_refuse("WATC LEX: unterminated string at end of input\n" as *u8) } 112 ls[1] = 4; ls[2] = st; ls[3] = q - st; ls[0] = q + 1; return 0 113 } 114 let st2: i64 = pos 115 var e: i64 = pos 116 var done: i64 = 0 117 while done == 0 { 118 if e >= n { done = 1 } else { 119 if is_delim(buf[e] as i64) == 1 { done = 1 } else { e = e + 1 } 120 } 121 } 122 ls[1] = 3; ls[2] = st2; ls[3] = e - st2; ls[0] = e 123 return 0 124} 125func atom_eq(buf: *u8, s: i64, l: i64, kw: *u8) -> i64 { 126 var i: i64 = 0 127 while i < l { if buf[s + i] != kw[i] { return 0 } i = i + 1 } 128 if kw[l] != (0 as u8) { return 0 } 129 return 1 130} 131func atom_int(buf: *u8, s: i64, l: i64) -> i64 { 132 var i: i64 = 0 133 var neg: i64 = 0 134 if l > 0 { if buf[s] == (45 as u8) { neg = 1; i = 1 } } 135 var v: i64 = 0 136 while i < l { v = v * 10 + ((buf[s + i] as i64) - 48); i = i + 1 } 137 if neg == 1 { return 0 - v } 138 return v 139} 140// classify a body mnemonic -> op code, or -1 unknown. Operand ops: 0/1=local.get/set 141// (uleb local idx), 2=i64.const / 16=i32.const (sleb). Rest are single-byte. 142// Native dialect opcode; binary encoding follows WebAssembly bulk-memory instructions. 143const WC_OP_MEMORY_INIT: i64 = 74 144const WC_BULK_PREFIX: i64 = 0xfc 145const WC_MEMORY_INIT: i64 = 8 146const WC_DATA_COUNT_SECTION: i64 = 12 147 148func instr_op(buf: *u8, s: i64, l: i64) -> i64 { 149 if atom_eq(buf, s, l, "memory.init" as *u8) == 1 { return WC_OP_MEMORY_INIT } 150 if atom_eq(buf, s, l, "local.get" as *u8) == 1 { return 0 } 151 if atom_eq(buf, s, l, "local.set" as *u8) == 1 { return 1 } 152 if atom_eq(buf, s, l, "i64.const" as *u8) == 1 { return 2 } 153 if atom_eq(buf, s, l, "i64.add" as *u8) == 1 { return 3 } 154 if atom_eq(buf, s, l, "i64.sub" as *u8) == 1 { return 4 } 155 if atom_eq(buf, s, l, "i64.mul" as *u8) == 1 { return 5 } 156 if atom_eq(buf, s, l, "i64.and" as *u8) == 1 { return 6 } 157 if atom_eq(buf, s, l, "i64.shr_s" as *u8) == 1 { return 7 } 158 if atom_eq(buf, s, l, "i64.div_s" as *u8) == 1 { return 8 } 159 if atom_eq(buf, s, l, "i64.rem_s" as *u8) == 1 { return 9 } 160 if atom_eq(buf, s, l, "i64.eq" as *u8) == 1 { return 10 } 161 if atom_eq(buf, s, l, "i64.ne" as *u8) == 1 { return 11 } 162 if atom_eq(buf, s, l, "i64.lt_s" as *u8) == 1 { return 12 } 163 if atom_eq(buf, s, l, "i64.gt_s" as *u8) == 1 { return 13 } 164 if atom_eq(buf, s, l, "i64.le_s" as *u8) == 1 { return 14 } 165 if atom_eq(buf, s, l, "i64.ge_s" as *u8) == 1 { return 15 } 166 if atom_eq(buf, s, l, "i32.const" as *u8) == 1 { return 16 } 167 if atom_eq(buf, s, l, "i32.wrap_i64" as *u8) == 1 { return 17 } 168 if atom_eq(buf, s, l, "i64.extend_i32_u" as *u8) == 1 { return 18 } 169 if atom_eq(buf, s, l, "unreachable" as *u8) == 1 { return 19 } 170 if atom_eq(buf, s, l, "i64.or" as *u8) == 1 { return 20 } 171 if atom_eq(buf, s, l, "i64.shl" as *u8) == 1 { return 21 } 172 if atom_eq(buf, s, l, "i64.xor" as *u8) == 1 { return 22 } 173 if atom_eq(buf, s, l, "br" as *u8) == 1 { return 28 } 174 if atom_eq(buf, s, l, "br_table" as *u8) == 1 { return 29 } 175 if atom_eq(buf, s, l, "call" as *u8) == 1 { return 30 } 176 if atom_eq(buf, s, l, "if" as *u8) == 1 { return 25 } 177 if atom_eq(buf, s, l, "else" as *u8) == 1 { return 26 } 178 if atom_eq(buf, s, l, "end" as *u8) == 1 { return 27 } 179 if atom_eq(buf, s, l, "i64.load" as *u8) == 1 { return 31 } 180 if atom_eq(buf, s, l, "i64.store" as *u8) == 1 { return 32 } 181 if atom_eq(buf, s, l, "i64.load8_u" as *u8) == 1 { return 33 } 182 if atom_eq(buf, s, l, "i64.store8" as *u8) == 1 { return 34 } 183 // full subword-memory family (2026-07-10 sext debt fix): the emitter now picks _s/_u per the sext 184 // bit, and 16/32-bit widths were previously UNASSEMBLABLE here (emitter could emit them -> WATC 185 // REFUSED). Opcodes: load8_s=0x30 load16_s/u=0x32/0x33 load32_s/u=0x34/0x35 store16=0x3D store32=0x3E. 186 if atom_eq(buf, s, l, "i64.load8_s" as *u8) == 1 { return 35 } 187 if atom_eq(buf, s, l, "i64.load16_s" as *u8) == 1 { return 36 } 188 if atom_eq(buf, s, l, "i64.load16_u" as *u8) == 1 { return 37 } 189 if atom_eq(buf, s, l, "i64.load32_s" as *u8) == 1 { return 38 } 190 if atom_eq(buf, s, l, "i64.load32_u" as *u8) == 1 { return 39 } 191 if atom_eq(buf, s, l, "i64.store16" as *u8) == 1 { return 48 } 192 if atom_eq(buf, s, l, "i64.store32" as *u8) == 1 { return 49 } 193 if atom_eq(buf, s, l, "f32.add" as *u8) == 1 { return 40 } 194 if atom_eq(buf, s, l, "f32.sub" as *u8) == 1 { return 41 } 195 if atom_eq(buf, s, l, "f32.mul" as *u8) == 1 { return 42 } 196 if atom_eq(buf, s, l, "f32.div" as *u8) == 1 { return 43 } 197 if atom_eq(buf, s, l, "f32.convert_i64_s" as *u8) == 1 { return 44 } 198 if atom_eq(buf, s, l, "i64.trunc_f32_s" as *u8) == 1 { return 45 } 199 if atom_eq(buf, s, l, "i32.reinterpret_f32" as *u8) == 1 { return 46 } 200 if atom_eq(buf, s, l, "f32.reinterpret_i32" as *u8) == 1 { return 47 } 201 // F618 wasm-SIMD subset (0xFD prefix; sub-opcodes spec-verified) 202 if atom_eq(buf, s, l, "v128.load" as *u8) == 1 { return 50 } 203 if atom_eq(buf, s, l, "i8x16.sub_sat_u" as *u8) == 1 { return 51 } 204 if atom_eq(buf, s, l, "v128.or" as *u8) == 1 { return 52 } 205 if atom_eq(buf, s, l, "i16x8.extadd_pairwise_i8x16_u" as *u8) == 1 { return 53 } 206 if atom_eq(buf, s, l, "i32x4.extadd_pairwise_i16x8_u" as *u8) == 1 { return 54 } 207 if atom_eq(buf, s, l, "i32x4.extract_lane" as *u8) == 1 { return 55 } 208 if atom_eq(buf, s, l, "i16x8.extadd_pairwise_i8x16_s" as *u8) == 1 { return 56 } 209 if atom_eq(buf, s, l, "v128.store" as *u8) == 1 { return 57 } 210 // memory.grow (2026-07-29, the sys_mmap 0-stub root fix): the emitter's $nx_wasm_mmap helper is 211 // stateless grow-per-allocation, so the ONLY new opcode the language needed was grow itself. 212 if atom_eq(buf, s, l, "memory.grow" as *u8) == 1 { return 58 } 213 // LN33 wasm threads (0xFE prefix, sub-opcodes per the threads proposal; memarg align = natural: 2 for the 214 // 32-bit family, 3 for the 64-bit family, offset 0 -- the emitter never folds an offset in). 215 if atom_eq(buf, s, l, "i32.atomic.load" as *u8) == 1 { return 59 } 216 if atom_eq(buf, s, l, "i32.atomic.store" as *u8) == 1 { return 60 } 217 if atom_eq(buf, s, l, "i32.atomic.rmw.add" as *u8) == 1 { return 61 } 218 if atom_eq(buf, s, l, "i32.atomic.rmw.sub" as *u8) == 1 { return 62 } 219 if atom_eq(buf, s, l, "i32.atomic.rmw.xchg" as *u8) == 1 { return 63 } 220 if atom_eq(buf, s, l, "i32.atomic.rmw.cmpxchg" as *u8) == 1 { return 64 } 221 if atom_eq(buf, s, l, "memory.atomic.wait32" as *u8) == 1 { return 65 } 222 if atom_eq(buf, s, l, "memory.atomic.notify" as *u8) == 1 { return 66 } 223 if atom_eq(buf, s, l, "i64.atomic.load" as *u8) == 1 { return 67 } 224 if atom_eq(buf, s, l, "i64.atomic.store" as *u8) == 1 { return 68 } 225 if atom_eq(buf, s, l, "i64.atomic.rmw.add" as *u8) == 1 { return 69 } 226 if atom_eq(buf, s, l, "i64.atomic.rmw.sub" as *u8) == 1 { return 70 } 227 if atom_eq(buf, s, l, "i64.atomic.rmw.xchg" as *u8) == 1 { return 71 } 228 if atom_eq(buf, s, l, "i64.atomic.rmw.cmpxchg" as *u8) == 1 { return 72 } 229 if atom_eq(buf, s, l, "atomic.fence" as *u8) == 1 { return 73 } 230 return 0 - 1 231} 232func emit_instr(body: *u8, bp: i64, op: i64, arg: i64) -> i64 { 233 var q: i64 = bp 234 if op == WC_OP_MEMORY_INIT { 235 q = w8(body,q,WC_BULK_PREFIX) 236 q = q + uleb128_encode(WC_MEMORY_INIT,body,q) 237 q = q + uleb128_encode(arg,body,q) 238 q = w8(body,q,0) 239 } 240 if op == 0 { q = w8(body, q, 0x20); q = q + uleb128_encode(arg, body, q) } 241 if op == 1 { q = w8(body, q, 0x21); q = q + uleb128_encode(arg, body, q) } 242 if op == 2 { q = w8(body, q, 0x42); q = q + sleb128_encode(arg, body, q) } 243 if op == 3 { q = w8(body, q, 0x7C) } 244 if op == 4 { q = w8(body, q, 0x7D) } 245 if op == 5 { q = w8(body, q, 0x7E) } 246 if op == 6 { q = w8(body, q, 0x83) } 247 if op == 7 { q = w8(body, q, 0x87) } 248 if op == 8 { q = w8(body, q, 0x7F) } 249 if op == 9 { q = w8(body, q, 0x81) } 250 if op == 10 { q = w8(body, q, 0x51) } 251 if op == 11 { q = w8(body, q, 0x52) } 252 if op == 12 { q = w8(body, q, 0x53) } 253 if op == 13 { q = w8(body, q, 0x55) } 254 if op == 14 { q = w8(body, q, 0x57) } 255 if op == 15 { q = w8(body, q, 0x59) } 256 if op == 16 { q = w8(body, q, 0x41); q = q + sleb128_encode(arg, body, q) } 257 if op == 17 { q = w8(body, q, 0xA7) } 258 if op == 18 { q = w8(body, q, 0xAD) } 259 if op == 19 { q = w8(body, q, 0x00) } 260 if op == 20 { q = w8(body, q, 0x84) } 261 if op == 21 { q = w8(body, q, 0x86) } 262 if op == 22 { q = w8(body, q, 0x85) } 263 if op == 23 { q = w8(body, q, 0x02); q = w8(body, q, 0x40) } 264 if op == 24 { q = w8(body, q, 0x03); q = w8(body, q, 0x40) } 265 if op == 25 { q = w8(body, q, 0x04); q = w8(body, q, 0x40) } 266 if op == 26 { q = w8(body, q, 0x05) } 267 if op == 27 { q = w8(body, q, 0x0B) } 268 if op == 28 { q = w8(body, q, 0x0C); q = q + uleb128_encode(arg, body, q) } 269 if op == 30 { q = w8(body, q, 0x10); q = q + uleb128_encode(arg, body, q) } 270 if op == 31 { q = w8(body, q, 0x29); q = w8(body, q, 3); q = w8(body, q, 0) } 271 if op == 32 { q = w8(body, q, 0x37); q = w8(body, q, 3); q = w8(body, q, 0) } 272 if op == 33 { q = w8(body, q, 0x31); q = w8(body, q, 0); q = w8(body, q, 0) } 273 if op == 34 { q = w8(body, q, 0x3C); q = w8(body, q, 0); q = w8(body, q, 0) } 274 // subword-memory family (sext fix): align exponent = natural (0 for 8-bit, 1 for 16, 2 for 32) 275 if op == 35 { q = w8(body, q, 0x30); q = w8(body, q, 0); q = w8(body, q, 0) } // i64.load8_s 276 if op == 36 { q = w8(body, q, 0x32); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.load16_s 277 if op == 37 { q = w8(body, q, 0x33); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.load16_u 278 if op == 38 { q = w8(body, q, 0x34); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.load32_s 279 if op == 39 { q = w8(body, q, 0x35); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.load32_u 280 if op == 48 { q = w8(body, q, 0x3D); q = w8(body, q, 1); q = w8(body, q, 0) } // i64.store16 281 if op == 49 { q = w8(body, q, 0x3E); q = w8(body, q, 2); q = w8(body, q, 0) } // i64.store32 282 if op == 40 { q = w8(body, q, 0x92) } // f32.add 283 if op == 41 { q = w8(body, q, 0x93) } // f32.sub 284 if op == 42 { q = w8(body, q, 0x94) } // f32.mul 285 if op == 43 { q = w8(body, q, 0x95) } // f32.div 286 if op == 44 { q = w8(body, q, 0xB4) } // f32.convert_i64_s 287 if op == 45 { q = w8(body, q, 0xAE) } // i64.trunc_f32_s 288 if op == 46 { q = w8(body, q, 0xBC) } // i32.reinterpret_f32 289 if op == 47 { q = w8(body, q, 0xBE) } // f32.reinterpret_i32 290 // F618 wasm-SIMD subset: 0xFD prefix + LEB128 sub-opcode (all subs here < 0x80 = single byte). 291 // memarg for v128.load/store: align exponent 0 (byte-aligned hint -- SAD reads ARE unaligned at 292 // arbitrary motion offsets; unaligned v128 access is architecturally legal in wasm), offset 0. 293 if op == 50 { q = w8(body, q, 0xFD); q = w8(body, q, 0x00); q = w8(body, q, 0); q = w8(body, q, 0) } // v128.load 294 if op == 51 { q = w8(body, q, 0xFD); q = w8(body, q, 0x73) } // i8x16.sub_sat_u 295 if op == 52 { q = w8(body, q, 0xFD); q = w8(body, q, 0x50) } // v128.or 296 if op == 53 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7D) } // i16x8.extadd_pairwise_i8x16_u 297 if op == 54 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7F) } // i32x4.extadd_pairwise_i16x8_u 298 if op == 55 { q = w8(body, q, 0xFD); q = w8(body, q, 0x1B); q = w8(body, q, arg) } // i32x4.extract_lane <lane> 299 if op == 56 { q = w8(body, q, 0xFD); q = w8(body, q, 0x7C) } // i16x8.extadd_pairwise_i8x16_s (mutation-gate twin) 300 if op == 57 { q = w8(body, q, 0xFD); q = w8(body, q, 0x0B); q = w8(body, q, 0); q = w8(body, q, 0) } // v128.store 301 if op == 58 { q = w8(body, q, 0x40); q = w8(body, q, 0x00) } // memory.grow (memidx 0) 302 // LN33 wasm threads: 0xFE prefix + sub-opcode + memarg(align, offset 0); atomic.fence carries one 0x00 flag byte 303 if op == 59 { q = w8(body, q, 0xFE); q = w8(body, q, 0x10); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.load 304 if op == 60 { q = w8(body, q, 0xFE); q = w8(body, q, 0x17); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.store 305 if op == 61 { q = w8(body, q, 0xFE); q = w8(body, q, 0x1E); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.rmw.add 306 if op == 62 { q = w8(body, q, 0xFE); q = w8(body, q, 0x25); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.rmw.sub 307 if op == 63 { q = w8(body, q, 0xFE); q = w8(body, q, 0x41); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.rmw.xchg 308 if op == 64 { q = w8(body, q, 0xFE); q = w8(body, q, 0x48); q = w8(body, q, 2); q = w8(body, q, 0) } // i32.atomic.rmw.cmpxchg 309 if op == 65 { q = w8(body, q, 0xFE); q = w8(body, q, 0x01); q = w8(body, q, 2); q = w8(body, q, 0) } // memory.atomic.wait32 310 if op == 66 { q = w8(body, q, 0xFE); q = w8(body, q, 0x00); q = w8(body, q, 2); q = w8(body, q, 0) } // memory.atomic.notify 311 if op == 67 { q = w8(body, q, 0xFE); q = w8(body, q, 0x11); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.load 312 if op == 68 { q = w8(body, q, 0xFE); q = w8(body, q, 0x18); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.store 313 if op == 69 { q = w8(body, q, 0xFE); q = w8(body, q, 0x1F); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.rmw.add 314 if op == 70 { q = w8(body, q, 0xFE); q = w8(body, q, 0x26); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.rmw.sub 315 if op == 71 { q = w8(body, q, 0xFE); q = w8(body, q, 0x42); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.rmw.xchg 316 if op == 72 { q = w8(body, q, 0xFE); q = w8(body, q, 0x49); q = w8(body, q, 3); q = w8(body, q, 0) } // i64.atomic.rmw.cmpxchg 317 if op == 73 { q = w8(body, q, 0xFE); q = w8(body, q, 0x03); q = w8(body, q, 0x00) } // atomic.fence 318 return q 319} 320// op takes an operand we read from the next atom? 0/1=local idx, 2/16=const. 321func op_has_operand(op: i64) -> i64 { 322 if op == WC_OP_MEMORY_INIT { return 1 } 323 if op == 0 { return 1 } 324 if op == 1 { return 1 } 325 if op == 2 { return 1 } 326 if op == 16 { return 1 } 327 if op == 55 { return 1 } // i32x4.extract_lane <laneidx> (plain int atom, emitted as one raw byte) 328 return 0 329} 330func op_is_local(op: i64) -> i64 { 331 if op == 0 { return 1 } 332 if op == 1 { return 1 } 333 return 0 334} 335// resolve a local operand atom: "$name" -> index via the name table, else parse int 336func resolve_local(buf: *u8, s: i64, l: i64, lns: *i64, lnl: *i64, cnt: i64) -> i64 { 337 if buf[s] != (36 as u8) { return atom_int(buf, s, l) } 338 var i: i64 = 0 339 while i < cnt { 340 if lns[i] >= 0 { if lnl[i] == l { 341 var j: i64 = 0 342 var same: i64 = 1 343 while j < l { if buf[lns[i] + j] != buf[s + j] { same = 0 } j = j + 1 } 344 if same == 1 { return i } 345 } } 346 i = i + 1 347 } 348 return 0 349} 350func emit_name(sc: *u8, s: i64, buf: *u8, ns: i64, nl: i64) -> i64 { 351 var q: i64 = s + uleb128_encode(nl, sc, s) 352 q = wcopy(sc, q, ((buf as i64) + ns) as *u8, nl) 353 return q 354} 355 356// emit the code-body locals vec from ltype[np..ntot-1] as run-length groups by type 357func emit_locals(body: *u8, bp: i64, ltype: *i64, np: i64, ntot: i64) -> i64 { 358 var groups: i64 = 0 359 var gi: i64 = np 360 while gi < ntot { 361 let t: i64 = ltype[gi] 362 var go: i64 = 1 363 while go == 1 { if gi >= ntot { go = 0 } else { if ltype[gi] == t { gi = gi + 1 } else { go = 0 } } } 364 groups = groups + 1 365 } 366 var q: i64 = bp + uleb128_encode(groups, body, bp) 367 gi = np 368 while gi < ntot { 369 let t: i64 = ltype[gi] 370 var run: i64 = 0 371 var go2: i64 = 1 372 while go2 == 1 { if gi >= ntot { go2 = 0 } else { if ltype[gi] == t { run = run + 1; gi = gi + 1 } else { go2 = 0 } } } 373 q = q + uleb128_encode(run, body, q) 374 q = w8(body, q, t) 375 } 376 return q 377} 378// emit one functype (all-i64 params/results, matching nxc2 signatures) into sc at s 379func emit_functype(sc: *u8, s: i64, np: i64, nr: i64) -> i64 { 380 var q: i64 = w8(sc, s, 0x60) 381 q = q + uleb128_encode(np, sc, q) 382 var i: i64 = 0 383 while i < np { q = w8(sc, q, 0x7E); i = i + 1 } 384 q = q + uleb128_encode(nr, sc, q) 385 i = 0 386 while i < nr { q = w8(sc, q, 0x7E); i = i + 1 } 387 return q 388} 389// dedup a (nparams,nresults) signature into the type table; return its typeidx 390func sig_find_or_add(tnp: *i64, tnr: *i64, ntp: *i64, np: i64, nr: i64) -> i64 { 391 var i: i64 = 0 392 let nt: i64 = ntp[0] 393 while i < nt { if tnp[i] == np { if tnr[i] == nr { return i } } i = i + 1 } 394 tnp[nt] = np; tnr[nt] = nr; ntp[0] = nt + 1 395 return nt 396} 397func substr_eq(buf: *u8, a: i64, b: i64, l: i64) -> i64 { 398 var j: i64 = 0 399 while j < l { if buf[a + j] != buf[b + j] { return 0 } j = j + 1 } 400 return 1 401} 402// resolve a br target label name -> relative depth (innermost open block = 0) 403func resolve_label(buf: *u8, s: i64, l: i64, lks: *i64, lkl: *i64, lsp: i64) -> i64 { 404 var i: i64 = lsp - 1 405 while i >= 0 { 406 if lkl[i] == l { if substr_eq(buf, s, lks[i], l) == 1 { return (lsp - 1) - i } } 407 i = i - 1 408 } 409 return 0 410} 411 412// resolve a call target $name -> funcidx (ftab = imports first, then defined funcs, in order) 413func resolve_func(buf: *u8, s: i64, l: i64, fts: *i64, ftl: *i64, nf: i64) -> i64 { 414 var i: i64 = 0 415 while i < nf { if ftl[i] == l { if substr_eq(buf, s, fts[i], l) == 1 { return i } } i = i + 1 } 416 return 0 417} 418// pass 0: build the func symbol table by scanning every (func $name (incl import funcs, which 419// appear first in nxc2 output) so calls (even forward) resolve to a funcidx. nimp[0]=#imports. 420func prescan_ftab(buf: *u8, n: i64, fts: *i64, ftl: *i64, nimp: *i64) -> i64 { 421 let ls2: *i64 = sys_mmap(64) as *i64 422 ls2[0] = 0 423 var nf: i64 = 0 424 var ni: i64 = 0 425 var depth: i64 = 0 426 var in_export: i64 = 0 427 var exp_depth: i64 = 0 428 var pend_imp: i64 = 0 429 var go: i64 = 1 430 while go == 1 { 431 lex_next(buf, n, ls2) 432 if ls2[1] == 0 { go = 0 } 433 if ls2[1] == 1 { depth = depth + 1 } 434 if ls2[1] == 2 { depth = depth - 1; if in_export == 1 { if depth < exp_depth { in_export = 0 } } } 435 if ls2[1] == 3 { 436 if atom_eq(buf, ls2[2], ls2[3], "export" as *u8) == 1 { in_export = 1; exp_depth = depth } 437 // LN33: only a FUNC import occupies the function index space; a memory import (the shared-memory 438 // door emits `(import "env" "memory" (memory MIN MAX shared))`) must not shift every defined 439 // function's index by one, so an import counts only when its descriptor turns out to be a func. 440 if atom_eq(buf, ls2[2], ls2[3], "import" as *u8) == 1 { pend_imp = 1 } 441 if atom_eq(buf, ls2[2], ls2[3], "memory" as *u8) == 1 { pend_imp = 0 } 442 if atom_eq(buf, ls2[2], ls2[3], "func" as *u8) == 1 { 443 if pend_imp == 1 { ni = ni + 1; pend_imp = 0 } 444 if in_export == 0 { 445 lex_next(buf, n, ls2) 446 if ls2[1] == 3 { fts[nf] = ls2[2]; ftl[nf] = ls2[3]; nf = nf + 1 } 447 } 448 } 449 } 450 } 451 nimp[0] = ni 452 return nf 453} 454 455const WCD_ASCII_ZERO: i64 = 48 456const WCD_ASCII_NINE: i64 = 57 457const WCD_ASCII_A: i64 = 65 458const WCD_ASCII_F: i64 = 70 459const WCD_ASCII_a: i64 = 97 460const WCD_ASCII_f: i64 = 102 461const WCD_HEX_RADIX: i64 = 16 462const WCD_ESCAPE: i64 = 92 463const WCD_I32_MAX: i64 = 2147483647 464const WCD_U32_MAX: i64 = 4294967295 465const WCD_U32_MODULUS: i64 = 4294967296 466const WCD_SEGMENT_HEADER_MAX: i64 = 1+1+5+1+5 // flags, i32.const, sLEB32, end, uLEB32 length. 467const WCD_SECTION_HEADER_MAX: i64 = 1+5+5 // section ID, section length, segment count. 468const WCD_I32_CONST: i64 = 0x41 469const WCD_END: i64 = 0x0b 470 471func wc_hex(c: i64) -> i64 { 472 if c>=WCD_ASCII_ZERO { if c<=WCD_ASCII_NINE { return c-WCD_ASCII_ZERO } } 473 if c>=WCD_ASCII_a { if c<=WCD_ASCII_f { return c-WCD_ASCII_a+10 } } 474 if c>=WCD_ASCII_A { if c<=WCD_ASCII_F { return c-WCD_ASCII_A+10 } } 475 return 0-1 476} 477// The native emitter uses active memory-0 segments with fully hex-escaped bytes. 478func wc_data(buf: *u8,n: i64,ls: *i64,dst: *u8,at: i64,cap: i64) -> i64 { 479 lex_next(buf,n,ls) 480 var passive: i64=0 481 var offset: i64=0 482 if ls[1]==4 { passive=1 } else { 483 if ls[1]!=1 { return wc_refuse("WATC DATA: expected offset expression or passive byte string\n" as *u8) } 484 lex_next(buf,n,ls) 485 if atom_eq(buf,ls[2],ls[3],"i32.const" as *u8)!=1 { return wc_refuse("WATC DATA: requires i32.const offset\n" as *u8) } 486 lex_next(buf,n,ls) 487 if ls[1]!=3 { return wc_refuse("WATC DATA: expected decimal offset\n" as *u8) } 488 if ls[3]==0 { return wc_refuse("WATC DATA: empty offset\n" as *u8) } 489 var digit: i64=0 490 var checked_offset: i64=0 491 while digit<ls[3] { 492 let c: i64=buf[ls[2]+digit] as i64 493 if c<WCD_ASCII_ZERO { return wc_refuse("WATC DATA: invalid decimal offset\n" as *u8) } 494 if c>WCD_ASCII_NINE { return wc_refuse("WATC DATA: invalid decimal offset\n" as *u8) } 495 let value: i64=c-WCD_ASCII_ZERO 496 if checked_offset>(WCD_U32_MAX-value)/10 { return wc_refuse("WATC DATA: decimal offset exceeds wasm32\n" as *u8) } 497 checked_offset=checked_offset*10+value 498 digit=digit+1 499 } 500 offset=checked_offset 501 if offset<0 { return wc_refuse("WATC DATA: negative offset\n" as *u8) } 502 if offset>WCD_U32_MAX { return wc_refuse("WATC DATA: offset exceeds wasm32\n" as *u8) } 503 lex_next(buf,n,ls) 504 if ls[1]!=2 { return wc_refuse("WATC DATA: offset expression not closed\n" as *u8) } 505 lex_next(buf,n,ls) 506 } 507 if ls[1]!=4 { return wc_refuse("WATC DATA: expected byte string\n" as *u8) } 508 let start: i64=ls[2] 509 let length: i64=ls[3] 510 if length%3!=0 { return wc_refuse("WATC DATA: native byte string requires hex escapes\n" as *u8) } 511 let count: i64=length/3 512 // Flags + opcode + signed i32 LEB + end + u32 length LEB: at most WCD_SEGMENT_HEADER_MAX bytes. 513 if cap-at<WCD_SEGMENT_HEADER_MAX { return wc_refuse("WATC DATA: metadata extent exceeds output\n" as *u8) } 514 if count>cap-at-WCD_SEGMENT_HEADER_MAX { return wc_refuse("WATC DATA: payload extent exceeds output\n" as *u8) } 515 var p: i64=at 516 p=w8(dst,p,passive) 517 if passive==0 { 518 p=w8(dst,p,WCD_I32_CONST) 519 var signed_offset: i64=offset 520 if offset>WCD_I32_MAX { signed_offset=offset-WCD_U32_MODULUS } 521 p=p+sleb128_encode(signed_offset,dst,p) 522 p=w8(dst,p,WCD_END) 523 } 524 p=p+uleb128_encode(count,dst,p) 525 var j: i64=0 526 while j<length { 527 if buf[start+j]!=(WCD_ESCAPE as u8) { return wc_refuse("WATC DATA: expected hex escape\n" as *u8) } 528 let hi: i64=wc_hex(buf[start+j+1] as i64) 529 let lo: i64=wc_hex(buf[start+j+2] as i64) 530 if hi<0 { return wc_refuse("WATC DATA: invalid hex digit\n" as *u8) } 531 if lo<0 { return wc_refuse("WATC DATA: invalid hex digit\n" as *u8) } 532 p=w8(dst,p,hi*WCD_HEX_RADIX+lo) 533 j=j+3 534 } 535 lex_next(buf,n,ls) 536 if ls[1]!=2 { return wc_refuse("WATC DATA: segment not closed\n" as *u8) } 537 return p 538} 539 540func wc_compile_file(inp: *u8, outp: *u8) -> i64 { 541 let lenp: *i64 = sys_mmap(16) as *i64 542 let buf: *u8 = sys_read_file(inp, lenp) 543 if buf == (0 as i64) as *u8 { sys_write(1, "WATC REFUSED: cannot read input\n" as *u8, 32); sys_exit(1); return 1 } 544 let n: i64 = lenp[0] 545 if n<0 { return wc_refuse("WATC DATA: negative input extent\n" as *u8) } 546 if n>WCD_I32_MAX { return wc_refuse("WATC DATA: input exceeds wasm32 module extent\n" as *u8) } 547 let data_cap: i64=n*2+WCD_SECTION_HEADER_MAX 548 let data_bytes: *u8=sys_mmap(data_cap) 549 if (data_bytes as i64)<=0 { return wc_refuse("WATC DATA: allocation failed\n" as *u8) } 550 var data_len: i64=0 551 var data_count: i64=0 552 let ls: *i64 = sys_mmap(64) as *i64 553 ls[0] = 0 554 // pass 0: func symbol table (imports first, then defined funcs, by $name) 555 // CAPACITY HARDENING 2026-07-05: the video-client module with the sovereign codec = 382 funcs / 556 // 46K lines / 192 exports -- the old caps (256 funcs, 64 per-func slots, 32K instrs) silently 557 // OVERFLOWED and the wraparound surfaced as "unknown mnemonic" at line 1. All pools sized 16-64x 558 // with the biggest real module in mind; a dev-tool's mmap is free. 559 let fts: *i64 = sys_mmap(K_MAGIC_65536) as *i64 560 let ftl: *i64 = sys_mmap(K_MAGIC_65536) as *i64 561 let nimp: *i64 = sys_mmap(16) as *i64 562 let nftot: i64 = prescan_ftab(buf, n, fts, ftl, nimp) 563 let num_imports: i64 = nimp[0] 564 // module-level state 565 var has_import: i64 = 0 566 var imp_mod_s: i64 = 0 567 var imp_mod_l: i64 = 0 568 var imp_fld_s: i64 = 0 569 var imp_fld_l: i64 = 0 570 var imp_np: i64 = 0 571 var imp_nr: i64 = 0 572 var has_mem: i64 = 0 573 var mem_min: i64 = 1 574 var mem_ns: i64 = 0 575 var mem_nl: i64 = 0 576 // LN33: memory limits (max, shared) and the imported-memory form -- flags byte 0 min-only, 1 min+max, 3 shared+max 577 var mem_max: i64 = 0 578 var mem_has_max: i64 = 0 579 var mem_shared: i64 = 0 580 var mem_imported: i64 = 0 581 var imm_mod_s: i64 = 0 582 var imm_mod_l: i64 = 0 583 var imm_fld_s: i64 = 0 584 var imm_fld_l: i64 = 0 585 // shared pools across all functions 586 let in_op: *i64 = sys_mmap(K_MAGIC_4194304) as *i64 587 let in_arg: *i64 = sys_mmap(K_MAGIC_4194304) as *i64 588 var ninstr: i64 = 0 589 let lns: *i64 = sys_mmap(K_MAGIC_2097152) as *i64 590 let lnl: *i64 = sys_mmap(K_MAGIC_2097152) as *i64 591 let ltype: *i64 = sys_mmap(K_MAGIC_2097152) as *i64 592 var ntot: i64 = 0 593 var pend_s: i64 = 0 - 1 594 var pend_l: i64 = 0 595 let lks: *i64 = sys_mmap(K_MAGIC_524288) as *i64 596 let lkl: *i64 = sys_mmap(K_MAGIC_524288) as *i64 597 var lsp: i64 = 0 598 let bt_pool: *i64 = sys_mmap(K_MAGIC_2097152) as *i64 599 var btp: i64 = 0 600 // per-defined-function arrays (sig, export, local pool range, instr range) 601 let fexp_s: *i64 = sys_mmap(K_MAGIC_65536) as *i64 602 let fexp_l: *i64 = sys_mmap(K_MAGIC_65536) as *i64 603 let fnp: *i64 = sys_mmap(K_MAGIC_65536) as *i64 604 let fnr: *i64 = sys_mmap(K_MAGIC_65536) as *i64 605 let fnl: *i64 = sys_mmap(K_MAGIC_65536) as *i64 606 let flbase: *i64 = sys_mmap(K_MAGIC_65536) as *i64 607 let fist: *i64 = sys_mmap(K_MAGIC_65536) as *i64 608 let fic: *i64 = sys_mmap(K_MAGIC_65536) as *i64 609 var NF: i64 = 0 610 // ( module ... NOTE: a `handled` flag stops a consumed inner group's ')' from 611 // prematurely closing the parent loop (NishiLang has no break / else-if). 612 lex_next(buf, n, ls) // ( 613 lex_next(buf, n, ls) // module 614 var top: i64 = 1 615 while top == 1 { 616 lex_next(buf, n, ls) 617 var h1: i64 = 0 618 if ls[1] == 1 { 619 h1 = 1 620 lex_next(buf, n, ls) // section keyword atom 621 if atom_eq(buf,ls[2],ls[3],"data" as *u8)==1 { 622 data_len=wc_data(buf,n,ls,data_bytes,data_len,data_cap) 623 data_count=data_count+1 624 } 625 if atom_eq(buf, ls[2], ls[3], "import" as *u8) == 1 { 626 lex_next(buf, n, ls) // "env" 627 let im_s: i64 = ls[2] 628 let im_l: i64 = ls[3] 629 lex_next(buf, n, ls) // "nx_syscall" | "memory" 630 let if_s: i64 = ls[2] 631 let if_l: i64 = ls[3] 632 lex_next(buf, n, ls) // ( of (func ...) | (memory ...) 633 lex_next(buf, n, ls) // func | memory 634 var fg: i64 = 0 635 if atom_eq(buf, ls[2], ls[3], "memory" as *u8) == 1 { 636 // LN33: (import "env" "memory" (memory MIN MAX shared)) -- the shared-memory door. The module 637 // imports the ONE memory every Worker instantiates against; it is not a function import. 638 mem_imported = 1; has_mem = 1 639 imm_mod_s = im_s; imm_mod_l = im_l; imm_fld_s = if_s; imm_fld_l = if_l 640 var mi_n: i64 = 0 641 var mdi: i64 = 1 642 while mdi > 0 { 643 lex_next(buf, n, ls) 644 if ls[1] == 3 { 645 if atom_eq(buf, ls[2], ls[3], "shared" as *u8) == 1 { mem_shared = 1 } else { 646 if mi_n == 0 { mem_min = atom_int(buf, ls[2], ls[3]) } else { mem_max = atom_int(buf, ls[2], ls[3]); mem_has_max = 1 } 647 mi_n = mi_n + 1 648 } 649 } 650 if ls[1] == 2 { mdi = 0 } 651 if ls[1] == 0 { mdi = 0 } 652 } 653 } else { 654 has_import = 1 655 imp_mod_s = im_s; imp_mod_l = im_l; imp_fld_s = if_s; imp_fld_l = if_l 656 lex_next(buf, n, ls) // $name (import func name) 657 fg = 1 658 } 659 while fg > 0 { 660 lex_next(buf, n, ls) 661 var hi: i64 = 0 662 if ls[1] == 1 { 663 hi = 1 664 lex_next(buf, n, ls) // param|result 665 if atom_eq(buf, ls[2], ls[3], "param" as *u8) == 1 { 666 var pi: i64 = 1 667 while pi > 0 { lex_next(buf, n, ls); if ls[1] == 2 { pi = 0 } if ls[1] == 3 { imp_np = imp_np + 1 } if ls[1] == 0 { pi = 0 } } 668 } 669 if atom_eq(buf, ls[2], ls[3], "result" as *u8) == 1 { 670 var ri: i64 = 1 671 while ri > 0 { lex_next(buf, n, ls); if ls[1] == 2 { ri = 0 } if ls[1] == 3 { imp_nr = imp_nr + 1 } if ls[1] == 0 { ri = 0 } } 672 } 673 } 674 if hi == 0 { if ls[1] == 2 { fg = 0 } } // func's ) 675 if ls[1] == 0 { fg = 0 } 676 } 677 lex_next(buf, n, ls) // ) closing (import ...) 678 } 679 if atom_eq(buf, ls[2], ls[3], "memory" as *u8) == 1 { 680 has_mem = 1 681 var mem_top_n: i64 = 0 682 var md: i64 = 1 683 while md > 0 { 684 lex_next(buf, n, ls) 685 var h2: i64 = 0 686 if ls[1] == 1 { 687 h2 = 1 688 lex_next(buf, n, ls) // export 689 lex_next(buf, n, ls) // "memory" string 690 mem_ns = ls[2]; mem_nl = ls[3] 691 lex_next(buf, n, ls) // ) 692 } 693 // LN33: (memory MIN) | (memory MIN MAX) | (memory MIN MAX shared) -- first int is the minimum, 694 // the second the maximum; the `shared` atom asks for limits flag 3 (which needs the maximum). 695 if h2 == 0 { if ls[1] == 3 { 696 if atom_eq(buf, ls[2], ls[3], "shared" as *u8) == 1 { mem_shared = 1 } else { 697 if mem_top_n == 0 { mem_min = atom_int(buf, ls[2], ls[3]) } else { mem_max = atom_int(buf, ls[2], ls[3]); mem_has_max = 1 } 698 mem_top_n = mem_top_n + 1 699 } 700 } } 701 if h2 == 0 { if ls[1] == 2 { md = 0 } } 702 if ls[1] == 0 { md = 0 } 703 } 704 } 705 if atom_eq(buf, ls[2], ls[3], "func" as *u8) == 1 { 706 let F: i64 = NF 707 let fbase: i64 = ntot 708 let fist0: i64 = ninstr 709 lsp = 0 710 fexp_s[F] = 0; fexp_l[F] = 0; fnp[F] = 0; fnr[F] = 0; fnl[F] = 0 711 lex_next(buf, n, ls) // optional $name or first group/atom 712 var have_tok: i64 = 1 713 if ls[1] == 3 { have_tok = 0 } 714 var fd: i64 = 1 715 while fd > 0 { 716 if have_tok == 0 { lex_next(buf, n, ls) } 717 have_tok = 0 718 var h3: i64 = 0 719 if ls[1] == 1 { 720 h3 = 1 721 lex_next(buf, n, ls) // export|param|result|local 722 if atom_eq(buf, ls[2], ls[3], "export" as *u8) == 1 { 723 lex_next(buf, n, ls); fexp_s[F] = ls[2]; fexp_l[F] = ls[3] 724 lex_next(buf, n, ls) // ) 725 } 726 if atom_eq(buf, ls[2], ls[3], "param" as *u8) == 1 { 727 pend_s = 0 - 1 728 var pp: i64 = 1 729 while pp > 0 { lex_next(buf, n, ls); if ls[1] == 2 { pp = 0 } 730 if ls[1] == 3 { 731 if buf[ls[2]] == (36 as u8) { pend_s = ls[2]; pend_l = ls[3] } else { lns[ntot] = pend_s; lnl[ntot] = pend_l; ltype[ntot] = 126; if atom_eq(buf, ls[2], ls[3], "i32" as *u8) == 1 { ltype[ntot] = 127 } ntot = ntot + 1; fnp[F] = fnp[F] + 1; pend_s = 0 - 1 } 732 } 733 if ls[1] == 0 { pp = 0 } } 734 } 735 if atom_eq(buf, ls[2], ls[3], "result" as *u8) == 1 { 736 var rr: i64 = 1 737 while rr > 0 { lex_next(buf, n, ls); if ls[1] == 2 { rr = 0 } if ls[1] == 3 { fnr[F] = fnr[F] + 1 } if ls[1] == 0 { rr = 0 } } 738 } 739 if atom_eq(buf, ls[2], ls[3], "local" as *u8) == 1 { 740 pend_s = 0 - 1 741 var lc: i64 = 1 742 while lc > 0 { lex_next(buf, n, ls); if ls[1] == 2 { lc = 0 } 743 if ls[1] == 3 { 744 if buf[ls[2]] == (36 as u8) { pend_s = ls[2]; pend_l = ls[3] } else { lns[ntot] = pend_s; lnl[ntot] = pend_l; ltype[ntot] = 126; if atom_eq(buf, ls[2], ls[3], "i32" as *u8) == 1 { ltype[ntot] = 127 } if atom_eq(buf, ls[2], ls[3], "v128" as *u8) == 1 { ltype[ntot] = 123 } ntot = ntot + 1; fnl[F] = fnl[F] + 1; pend_s = 0 - 1 } 745 } 746 if ls[1] == 0 { lc = 0 } } 747 } 748 if atom_eq(buf, ls[2], ls[3], "block" as *u8) == 1 { 749 in_op[ninstr] = 23; in_arg[ninstr] = 0; ninstr = ninstr + 1 750 lex_next(buf, n, ls); lks[lsp] = ls[2]; lkl[lsp] = ls[3]; lsp = lsp + 1 751 } 752 if atom_eq(buf, ls[2], ls[3], "loop" as *u8) == 1 { 753 in_op[ninstr] = 24; in_arg[ninstr] = 0; ninstr = ninstr + 1 754 lex_next(buf, n, ls); lks[lsp] = ls[2]; lkl[lsp] = ls[3]; lsp = lsp + 1 755 } 756 } 757 if h3 == 0 { if ls[1] == 3 { 758 let op: i64 = instr_op(buf, ls[2], ls[3]) 759 if op < 0 { sys_write(1, "WATC REFUSED: unknown mnemonic: " as *u8, 31); sys_write(1, ((buf as i64) + ls[2]) as *u8, ls[3]); sys_write(1, "\n" as *u8, 1); sys_exit(1); return 1 } 760 if op == 29 { 761 let btoff: i64 = btp 762 var nlab: i64 = 0 763 var btgo: i64 = 1 764 while btgo == 1 { 765 lex_next(buf, n, ls) 766 if ls[1] == 3 { bt_pool[btoff + 1 + nlab] = resolve_label(buf, ls[2], ls[3], lks, lkl, lsp); nlab = nlab + 1 } 767 if ls[1] == 2 { btgo = 0 } 768 if ls[1] == 0 { btgo = 0 } 769 } 770 bt_pool[btoff] = nlab - 1 771 btp = btoff + 1 + nlab 772 in_op[ninstr] = 29; in_arg[ninstr] = btoff; ninstr = ninstr + 1 773 } 774 if op == 25 { in_op[ninstr] = 25; in_arg[ninstr] = 0; ninstr = ninstr + 1; lks[lsp] = 0; lkl[lsp] = 0; lsp = lsp + 1 } 775 if op == 27 { if lsp > 0 { lsp = lsp - 1 } in_op[ninstr] = 27; in_arg[ninstr] = 0; ninstr = ninstr + 1 } 776 if op != 29 { if op != 25 { if op != 27 { 777 var arg: i64 = 0 778 if op == 28 { lex_next(buf, n, ls); arg = resolve_label(buf, ls[2], ls[3], lks, lkl, lsp) } 779 if op == 30 { lex_next(buf, n, ls); arg = resolve_func(buf, ls[2], ls[3], fts, ftl, nftot) } 780 if op_has_operand(op) == 1 { 781 lex_next(buf, n, ls) 782 if op_is_local(op) == 1 { arg = resolve_local(buf, ls[2], ls[3], ((lns as i64) + fbase * 8) as *i64, ((lnl as i64) + fbase * 8) as *i64, ntot - fbase) } else { arg = atom_int(buf, ls[2], ls[3]) } 783 } 784 in_op[ninstr] = op; in_arg[ninstr] = arg; ninstr = ninstr + 1 785 } } } 786 } } 787 if h3 == 0 { if ls[1] == 2 { 788 if lsp > 0 { lsp = lsp - 1; in_op[ninstr] = 27; in_arg[ninstr] = 0; ninstr = ninstr + 1 } else { fd = 0 } 789 } } 790 if ls[1] == 0 { fd = 0 } 791 } 792 flbase[F] = fbase; fist[F] = fist0; fic[F] = ninstr - fist0 793 NF = NF + 1 794 } 795 if atom_eq(buf, ls[2], ls[3], "export" as *u8) == 1 { 796 lex_next(buf, n, ls) // "name" 797 let ename_s: i64 = ls[2] 798 let ename_l: i64 = ls[3] 799 lex_next(buf, n, ls) // ( 800 lex_next(buf, n, ls) // func|memory|... 801 var is_func: i64 = 0 802 if atom_eq(buf, ls[2], ls[3], "func" as *u8) == 1 { is_func = 1 } 803 // LN33: `(export "memory" (memory 0))` is how an IMPORTED memory is exported; an inline 804 // `(memory (export "memory") N)` already set the name and keeps it. 805 if atom_eq(buf, ls[2], ls[3], "memory" as *u8) == 1 { if mem_nl == 0 { mem_ns = ename_s; mem_nl = ename_l } } 806 lex_next(buf, n, ls) // $name or index 807 if is_func == 1 { 808 let fidx: i64 = resolve_func(buf, ls[2], ls[3], fts, ftl, nftot) 809 let defidx: i64 = fidx - num_imports 810 if defidx >= 0 { fexp_s[defidx] = ename_s; fexp_l[defidx] = ename_l } 811 } 812 lex_next(buf, n, ls) // ) of (func ...) 813 lex_next(buf, n, ls) // ) of (export ...) 814 } 815 } 816 if h1 == 0 { if ls[1] == 2 { top = 0 } } 817 if ls[1] == 0 { top = 0 } 818 } 819 // ---- emit ---- 820 let out: *u8 = sys_mmap(K_MAGIC_8388608+data_len+WCD_SECTION_HEADER_MAX) 821 let sc: *u8 = sys_mmap(K_MAGIC_8388608+data_len+WCD_SECTION_HEADER_MAX) 822 let body: *u8 = sys_mmap(K_MAGIC_2097152) 823 var p: i64 = 0 824 p = w8(out, p, 0); p = w8(out, p, 0x61); p = w8(out, p, 0x73); p = w8(out, p, 0x6D) 825 p = w8(out, p, 1); p = w8(out, p, 0); p = w8(out, p, 0); p = w8(out, p, 0) 826 // type table dedup (import + every func sig; nxc2 sigs all-i64) 827 let tnp: *i64 = sys_mmap(K_MAGIC_65536) as *i64 828 let tnr: *i64 = sys_mmap(K_MAGIC_65536) as *i64 829 let ntp: *i64 = sys_mmap(16) as *i64 830 ntp[0] = 0 831 let ftypeidx: *i64 = sys_mmap(K_MAGIC_65536) as *i64 832 var imp_tidx: i64 = 0 833 if has_import == 1 { imp_tidx = sig_find_or_add(tnp, tnr, ntp, imp_np, imp_nr) } 834 var fj: i64 = 0 835 while fj < NF { ftypeidx[fj] = sig_find_or_add(tnp, tnr, ntp, fnp[fj], fnr[fj]); fj = fj + 1 } 836 // type section 837 var s: i64 = 0 838 s = s + uleb128_encode(ntp[0], sc, s) 839 var ti: i64 = 0 840 while ti < ntp[0] { s = emit_functype(sc, s, tnp[ti], tnr[ti]); ti = ti + 1 } 841 p = emit_section(out, p, 1, sc, s) 842 // import section (LN33: a memory import rides beside the optional func import; the func entry is emitted 843 // first so the function index space is exactly what prescan_ftab counted) 844 if has_import + mem_imported > 0 { 845 s = 0 846 s = s + uleb128_encode(has_import + mem_imported, sc, s) 847 if has_import == 1 { 848 s = emit_name(sc, s, buf, imp_mod_s, imp_mod_l) 849 s = emit_name(sc, s, buf, imp_fld_s, imp_fld_l) 850 s = w8(sc, s, 0) 851 s = s + uleb128_encode(imp_tidx, sc, s) 852 } 853 if mem_imported == 1 { 854 if mem_shared == 1 { if mem_has_max == 0 { wc_refuse("WATC REFUSED: a shared memory needs a maximum -- (memory MIN MAX shared)\n" as *u8) } } 855 s = emit_name(sc, s, buf, imm_mod_s, imm_mod_l) 856 s = emit_name(sc, s, buf, imm_fld_s, imm_fld_l) 857 s = w8(sc, s, 2) 858 s = w8(sc, s, wc_mem_flags(mem_has_max, mem_shared)) 859 s = s + uleb128_encode(mem_min, sc, s) 860 if mem_has_max == 1 { s = s + uleb128_encode(mem_max, sc, s) } 861 } 862 p = emit_section(out, p, 2, sc, s) 863 } 864 // function section: NF entries 865 s = 0 866 s = s + uleb128_encode(NF, sc, s) 867 fj = 0 868 while fj < NF { s = s + uleb128_encode(ftypeidx[fj], sc, s); fj = fj + 1 } 869 p = emit_section(out, p, 3, sc, s) 870 // memory section (LN33: an IMPORTED memory has no memory section of its own; a defined one carries its 871 // limits flags -- 0 min only, 1 min+max, 3 shared+max -- and a shared memory without a maximum is refused) 872 if has_mem == 1 { if mem_imported == 0 { 873 if mem_shared == 1 { if mem_has_max == 0 { wc_refuse("WATC REFUSED: a shared memory needs a maximum -- (memory MIN MAX shared)\n" as *u8) } } 874 s = 0; s = w8(sc, s, 1); s = w8(sc, s, wc_mem_flags(mem_has_max, mem_shared)); s = s + uleb128_encode(mem_min, sc, s) 875 if mem_has_max == 1 { s = s + uleb128_encode(mem_max, sc, s) } 876 p = emit_section(out, p, 5, sc, s) 877 } } 878 // export section: memory (if any) + every func that has an export name 879 s = 0 880 var nexp: i64 = 0 881 if has_mem == 1 { nexp = nexp + 1 } 882 fj = 0 883 while fj < NF { if fexp_l[fj] > 0 { nexp = nexp + 1 } fj = fj + 1 } 884 s = s + uleb128_encode(nexp, sc, s) 885 if has_mem == 1 { s = emit_name(sc, s, buf, mem_ns, mem_nl); s = w8(sc, s, 2); s = w8(sc, s, 0) } 886 fj = 0 887 while fj < NF { if fexp_l[fj] > 0 { s = emit_name(sc, s, buf, fexp_s[fj], fexp_l[fj]); s = w8(sc, s, 0); s = s + uleb128_encode(num_imports + fj, sc, s) } fj = fj + 1 } 888 p = emit_section(out, p, 7, sc, s) 889 // Data count precedes code so memory.init indices can be validated during streaming compilation. 890 if data_count>0 { 891 s=uleb128_encode(data_count,sc,0) 892 p=emit_section(out,p,WC_DATA_COUNT_SECTION,sc,s) 893 } 894 // code section: NF entries (each func's locals vec + body + end) 895 s = 0 896 s = s + uleb128_encode(NF, sc, s) 897 fj = 0 898 while fj < NF { 899 var bp: i64 = 0 900 bp = emit_locals(body, bp, ltype, flbase[fj] + fnp[fj], flbase[fj] + fnp[fj] + fnl[fj]) 901 var ii: i64 = fist[fj] 902 let iend: i64 = fist[fj] + fic[fj] 903 while ii < iend { 904 if in_op[ii] == 29 { 905 let off2: i64 = in_arg[ii] 906 let cnt2: i64 = bt_pool[off2] 907 bp = w8(body, bp, 0x0E) 908 bp = bp + uleb128_encode(cnt2, body, bp) 909 var k2: i64 = 0 910 while k2 < cnt2 { bp = bp + uleb128_encode(bt_pool[off2 + 1 + k2], body, bp); k2 = k2 + 1 } 911 bp = bp + uleb128_encode(bt_pool[off2 + 1 + cnt2], body, bp) 912 } else { 913 if in_op[ii]==WC_OP_MEMORY_INIT { 914 if in_arg[ii]<0 { return wc_refuse("WATC memory.init: negative data index\n" as *u8) } 915 if in_arg[ii]>=data_count { return wc_refuse("WATC memory.init: data index outside declared segments\n" as *u8) } 916 } 917 bp = emit_instr(body, bp, in_op[ii], in_arg[ii]) 918 } 919 ii = ii + 1 920 } 921 bp = w8(body, bp, 0x0B) 922 s = s + uleb128_encode(bp, sc, s) 923 s = wcopy(sc, s, body, bp) 924 fj = fj + 1 925 } 926 p = emit_section(out, p, 10, sc, s) 927 if data_count>0 { 928 s=uleb128_encode(data_count,sc,0) 929 s=wcopy(sc,s,data_bytes,data_len) 930 p=emit_section(out,p,11,sc,s) 931 } 932 // write 933 let fd: i64 = sys_openat_wr(outp, 0x1a4) 934 if fd < 0 { sys_write(1, "WATC REFUSED: cannot write output\n" as *u8, 34); sys_exit(1); return 1 } 935 sys_write(fd, out, p); sys_close(fd) 936 sys_write(1, "WATC GREEN: wat->wasm\n" as *u8, 22) 937 sys_exit(0) 938 return 0 939} 940 941func main(argc: i64, argv: *i64) -> i64 { 942 if argc < 3 { sys_write(1, "usage: nx_wat_compiler <in.wat> <out.wasm>\n" as *u8, 43); sys_exit(2); return 2 } 943 return wc_compile_file(argv[1] as *u8, argv[2] as *u8) 944}