mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
The threads support and the memory64 support each worked alone but were kept apart by two gates: shared memory64 modules were rejected at translation, and every 0xFE atomic was rejected on a memory64 memory. A wasm64-wasip1-threads toolchain now exists, so compose them. - atomicEA64 mirrors simdEA64's overflow-guarded address math; every atomic op body moves into an ea-keyed core, the wasm32 helpers become one-line wrappers through atomicEA, and each gets an _m64 twin through atomicEA64 (loads/stores/RMW families, wait/notify). Contended fast paths and memory orders are moved, not rewritten. - The 0xFE lowering decodes the memarg offset as u64 under memory64 (the one place still reading u32), passes it as Const64, and selects the _m64 helper names; the inline atomic fast path becomes width-correct instead of truncating. - wasi_thread_spawn under memory64 passes the start_arg as i64 (it is a linear-memory pointer): threadSpawn splits into a shared launch core with per-width entries, and the module wires a threadStart64 field for the widened wasi_thread_start export. - memoryGrow64 gains the no-relocation branch for shared memories a grow must never move the backing array out from under peer agents. A shared memory64 requires a declared maximum: the reservation is the mapping ceiling, and the memory64 hard cap is not a ceiling. - WasiStubs gains the trivial Sched_yield64 LP64 twin (musl's spin paths import sched_yield; single-threaded modules never did). - New shared+i64 fixtures mirror the wasm32 threads fixtures and run under the race detector; an atomicEA64 unit test covers beyond-4GiB addresses, overflow and misalignment; a regression test pins that unshared memory64 modules may use atomics. Generated output for wasm32 modules is byte-identical. Verified end to end: a wasm64-wasip1-threads C program (pthread_create x4 over a shared counter) transpiles and runs correctly on goroutines, including under -race; llama.cpp built for wasm64-wasip1-threads decodes correctly with ggml n_threads parallelism. Co-authored-by: Claude Fable 5 <[email protected]>
62 lines
3.2 KiB
WebAssembly Text Format
62 lines
3.2 KiB
WebAssembly Text Format
;; cg_atomics_m64.wat — cg_atomics.wat on a shared memory64: the same
|
|
;; single-agent threads-proposal atomics (loads, stores, every RMW family
|
|
;; incl. subword lanes, cmpxchg, fence, notify, zero-timeout wait) with i64
|
|
;; addressing, so every op routes through the _m64 helper family. Results are
|
|
;; deterministic observable values the driving test hardcodes.
|
|
(module
|
|
(memory i64 1 1 shared)
|
|
(func (export "rmw32") (result i32)
|
|
(i32.atomic.store (i64.const 16) (i32.const 100))
|
|
(drop (i32.atomic.rmw.add (i64.const 16) (i32.const 5)))
|
|
(drop (i32.atomic.rmw.sub (i64.const 16) (i32.const 1)))
|
|
(drop (i32.atomic.rmw.and (i64.const 16) (i32.const 0xff)))
|
|
(drop (i32.atomic.rmw.or (i64.const 16) (i32.const 0x100)))
|
|
(drop (i32.atomic.rmw.xor (i64.const 16) (i32.const 0x1)))
|
|
(i32.atomic.load (i64.const 16)))
|
|
(func (export "rmw64") (result i64)
|
|
(i64.atomic.store (i64.const 24) (i64.const 4000000000))
|
|
(drop (i64.atomic.rmw.add (i64.const 24) (i64.const 4000000000)))
|
|
(i64.atomic.load (i64.const 24)))
|
|
(func (export "xchg") (result i32)
|
|
(i32.atomic.store (i64.const 32) (i32.const 7))
|
|
;; old value (7) + new value (9) observed
|
|
(i32.add (i32.atomic.rmw.xchg (i64.const 32) (i32.const 9))
|
|
(i32.atomic.load (i64.const 32))))
|
|
(func (export "cmpxchg") (result i32)
|
|
(i32.atomic.store (i64.const 40) (i32.const 5))
|
|
(drop (i32.atomic.rmw.cmpxchg (i64.const 40) (i32.const 5) (i32.const 6))) ;; hits
|
|
(drop (i32.atomic.rmw.cmpxchg (i64.const 40) (i32.const 5) (i32.const 7))) ;; misses
|
|
(i32.atomic.load (i64.const 40)))
|
|
(func (export "subword") (result i32)
|
|
;; byte lanes at 48..51: store 0xAA at 49, add 1 at lane, read back word
|
|
(i32.atomic.store (i64.const 48) (i32.const 0))
|
|
(i32.atomic.store8 (i64.const 49) (i32.const 0xaa))
|
|
(drop (i32.atomic.rmw8.add_u (i64.const 49) (i32.const 1)))
|
|
(drop (i32.atomic.rmw16.xchg_u (i64.const 50) (i32.const 0x1234)))
|
|
(i32.atomic.load (i64.const 48)))
|
|
(func (export "subword_cmpxchg") (result i32)
|
|
(i32.atomic.store8 (i64.const 56) (i32.const 3))
|
|
(drop (i32.atomic.rmw8.cmpxchg_u (i64.const 56) (i32.const 3) (i32.const 9)))
|
|
(i32.atomic.load8_u (i64.const 56)))
|
|
(func (export "wait_notify") (result i32)
|
|
(i32.atomic.store (i64.const 64) (i32.const 1))
|
|
;; not-equal (expected 0, actual 1) => 1; notify returns 0 woken
|
|
(i32.add
|
|
(memory.atomic.wait32 (i64.const 64) (i32.const 0) (i64.const 0))
|
|
(memory.atomic.notify (i64.const 64) (i32.const 5))))
|
|
(func (export "fenced_load") (result i32)
|
|
(i32.atomic.store (i64.const 72) (i32.const 42))
|
|
(atomic.fence)
|
|
(i32.atomic.load (i64.const 72)))
|
|
(func (export "store_neg") (result i32)
|
|
;; Negative constant operands: the inline-atomic emitter renders the
|
|
;; store value through an unsigned cast, which must be a runtime
|
|
;; conversion — `uint32(-1)` as a constant expression fails to
|
|
;; compile. Locks the force-hoist of atomic-store value operands.
|
|
(i32.atomic.store (i64.const 80) (i32.const -1))
|
|
(i64.atomic.store (i64.const 88) (i64.const -0x8000000000000000))
|
|
(i32.add
|
|
(i32.atomic.load (i64.const 80))
|
|
(i32.wrap_i64 (i64.atomic.load (i64.const 88)))))
|
|
)
|