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}