nx_wasm_threads_gate.nx
buildroot/runtime/nx_wasm_threads_gate.nx
about
nx_wasm_threads_gate.nx -- LN33 / LN34 REFEREE (lang.plan): wasm threads from the sovereign compiler, ONE family.
THE FAMILY IS THE INCUMBENT'S: the parser builtins __atomic_load_i64 / __atomic_store_i64 / __atomic_cas_i64 /
__atomic_faa_i64 / __atomic_fence (nx_atom.nx wraps them) lower to OP_ATOMIC_* in the IR; the x86-64 backend
lowers those to LOCK-prefixed instructions, the rv64 backend to AMO and lr/sc (LN34), and the wasm backend to the
0xFE family over a module-declared shared memory -- with __syscall(98 futex, addr, WAIT|WAKE, val) lowering to
memory.atomic.wait32 / memory.atomic.notify. Under --nothreads the SAME ops lower to plain single-thread twins.
SUBJECTS, forked by their SERVED name first: ./nx_compile_wat.elf (the WAT backend driver over nx_wasm.nx) and
./nx_wat_compiler.elf (WAT -> wasm); the VM twin nx_wasm_vm is composed IN-PROCESS so its 0xFE semantics are
measured here rather than trusted.
FIXTURES are assembled at RUNTIME under the fixture dir (argv[1], default /tmp/nx_wasm_threads_gate) and use the
builtins directly, so no library has to be copied beside them: a shared-declaring module, a plain module (the
neutrality control), a shared-without-maximum module (the emitter must refuse by name), a hand-written WAT with a
shared memory and no maximum (the WAT compiler must refuse by name), and the --nothreads twin of the shared module.
THE BROWSER HALF of the done-rule -- two Workers over ONE shared WebAssembly.Memory behind real cross-origin
isolation, the atomic counter exact and the plain-store twin losing updates -- lives on the CDP wire
(nishi-ops/ac_cdp.py with AC_CDP_SERVE), because a gate on the NAS has no browser; its numbers are journaled on
lang.plan LN33, and this gate proves everything BELOW the browser and says so in its verdict note.
nx_wasm_threads_gate [fixture-dir]
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 4 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_tool_run.nxnx_wasm_vm.nx
imported by: nobody (leaf or entry point)
structs
| none |
consts
| 26 | const TG_DIR_DEFAULT: *u8 = "/tmp/nx_wasm_threads_gate" |
| 27 | const TG_CAP: i64 = 1048576 // diagnostic capture for a forked tool (the WAT itself goes to a file) |
| 28 | const TG_TMO_MS: i64 = 60000 |
| 29 | const TG_MODE_755: i64 = 493 |
| 30 | const TG_MODE_644: i64 = 420 |
| 31 | const TG_PATH_BYTES: i64 = 1024 |
| 32 | const TG_TICKS: i64 = 1000 |
| 33 | const TG_CELL_A: i64 = 64 |
| 34 | const TG_CELL_B: i64 = 256 |
| 35 | const TG_CELL_C: i64 = 512 |
| 36 | const TG_EXIT_EMIT_REFUSED: i64 = 3 |
| 37 | const TG_EXIT_WATC_REFUSED: i64 = 1 |
| 38 | const TG_PAGE_BYTES: i64 = 65536 |
| 40 | const TG_EXP_CAS: i64 = 1009 |
| 41 | const TG_EXP_FUTEX: i64 = 10 |
| 42 | const TG_EXP_FUTEX_TWIN: i64 = 0 |
| 43 | const TG_EXP_BIG: i64 = 10000000003 |
| 45 | const TG_FX_SHARED: *u8 = "static nx_wasm_pages_req: i64 = 1\nstatic nx_wasm_pages_max: i64 = 2\nstatic nx_wasm_shared_req: i64 = 1\nconst MO: i64 = 5\nfunc tick(p: i64, n: i64) -> i64 {\n let c: *i64 = p as *i64\n var i: i64 = 0\n while i < n { __atomic_faa_i64(c, 1, MO); i = i + 1 }\n return __atomic_load_i64(c, MO)\n}\nfunc tick_racy(p: i64, n: i64) -> i64 {\n let q: *i64 = p as *i64\n var i: i64 = 0\n while i < n { q[0] = q[0] + 1; i = i + 1 }\n return q[0]\n}\nfunc probe_cas(p: i64) -> i64 {\n let c: *i64 = p as *i64\n let s: i64 = __atomic_store_i64(c, 7, MO)\n let a: i64 = __atomic_cas_i64(c, 7, 9, MO)\n let b: i64 = __atomic_cas_i64(c, 7, 11, MO)\n let v: i64 = __atomic_load_i64(c, MO)\n return a * 1000 + b * 100 + v + s\n}\nfunc probe_futex(p: i64) -> i64 {\n let c: *i64 = p as *i64\n let s: i64 = __atomic_store_i64(c, 5, MO)\n let w: i64 = __syscall(98, p, 128, 6, 0, 0, 0)\n let nt: i64 = __syscall(98, p, 129, 1, 0, 0, 0)\n let f: i64 = __atomic_fence(MO)\n return w * 10 + nt + s + f\n}\nfunc probe_big(p: i64) -> i64 {\n let c: *i64 = p as *i64\n let s: i64 = __atomic_store_i64(c, 5000000000, MO)\n let old: i64 = __atomic_faa_i64(c, 3, MO)\n return old + __atomic_load_i64(c, MO) + s\n}\n" |
| 46 | const TG_FX_PLAIN: *u8 = "func plain(a: i64, b: i64) -> i64 { return a + b }\n" |
| 47 | const TG_FX_NOMAX: *u8 = "static nx_wasm_pages_req: i64 = 1\nstatic nx_wasm_shared_req: i64 = 1\nfunc tick(p: i64, n: i64) -> i64 { return __atomic_faa_i64(p as *i64, n, 5) }\n" |
| 48 | const TG_WAT_NOMAX: *u8 = "(module\n (memory (export \"memory\") 1 shared)\n (func (export \"f\") (result i64)\n i64.const 1\n )\n)\n" |
functions
| 50 | func tg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 51 | func tg_path(dir: *u8, name: *u8) -> *u8 |
| 59 | func tg_write(path: *u8, s: *u8) -> i64 |
| 66 | func tg_exists(path: *u8) -> i64 |
| 72 | func tg_pick3(a: *u8, b: *u8, c: *u8) -> *u8 |
| 79 | func tg_has(buf: *u8, n: i64, needle: *u8) -> i64 |
| 97 | func tg_has3(buf: *u8, n: i64, a: i64, b: i64, c: i64) -> i64 |
| 107 | func tg_run(exe: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, out: *u8, outlen: *i64) -> i64 |
| 113 | func tg_run5(exe: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, a5: *u8, out: *u8, outlen: *i64) -> i64 |
| 125 | func tg_vm_call2(bytes: *u8, len: i64, name: *u8, a: i64, b: i64, nargs: i64, ok: *i64) -> i64 |
| 139 | func main(argc: i64, argv: *i64) -> i64 |