mirror of
https://github.com/hanzoai/wasm2go.git
synced 2026-10-30 15:06:48 +00:00
Adds the wasm threads proposal to the codegen and, on top of it, a
copy-on-write memory-sharing facility for embedders that stand up many
instances of the same module.
Threads
- Lower the 0xFE atomic family (load/store/rmw/cmpxchg across widths and
sub-word lanes, plus wait/notify) to sync/atomic-based module helpers.
- wasi_thread_spawn runs each guest thread on a goroutine. A shared memory
is allocated at its declared ceiling once and never relocates, so every
agent's cached base pointer stays valid; growth is a lone atomic store.
- Passive data segments, per-agent globals, coherent plain access for
shared memories, and multi-package output for threaded modules.
- A shared memory's ceiling is a runtime value the embedder chooses, not
the binary's declared maximum.
Copy-on-write shared memory
- NewWithMemory lets the caller supply the linear memory, and memory.init
compares before it writes — so a copy-on-write map of a pre-initialized
image survives the start section instead of being faulted private.
- NewSharedImage builds an initialized image once per process and maps it
copy-on-write into every instance: the data segments (identical in every
instance, read-mostly) then cost almost nothing per instance.
- NewSharedSnapshot / NewFromSnapshot share a WHOLE initialized instance —
memory and wasm globals both — so instances re-run nothing at all.
SaveGlobals/RestoreGlobals carry the initialized state that lives outside
linear memory (the stack pointer and other mutable globals).
Also: coverage for the threads/atomics helpers, a WASM2GO_SSA_PASSES_OFF
diagnostic knob, an AtomicWait timeout margin, and thread-trap visibility.
Co-authored-by: Claude Opus 4.8 <[email protected]>
59 lines
1.8 KiB
WebAssembly Text Format
59 lines
1.8 KiB
WebAssembly Text Format
;; A module shaped like LLVM's shared-memory output: the data segments are
|
|
;; PASSIVE and a start function installs them with memory.init. That is the
|
|
;; layout the shared-image runtime exists for — the segment destinations are
|
|
;; wasm constants the host never sees until the start section runs them, which
|
|
;; is why the Module has to learn dataEnd at run time rather than at codegen.
|
|
;;
|
|
;; Two segments, deliberately out of order, so dataEnd is a max and not "the
|
|
;; last one wins".
|
|
;;
|
|
;; The start section also leaves two marks that let a test tell the two kinds of
|
|
;; image apart: it BUMPS A COUNTER in memory (so a re-run is visible) and it
|
|
;; SETS A MUTABLE GLOBAL to a value its declared initializer does not have (so a
|
|
;; global that was not restored is visible). Between them they stand in for what
|
|
;; a real toolchain's start section does — installing the segments and setting
|
|
;; up the thread-local base — and for why a snapshot has to carry the globals.
|
|
(module
|
|
(memory (export "memory") 2)
|
|
|
|
(global $marker (mut i32) (i32.const 0))
|
|
|
|
(data $late "END") ;; goes high, installed first
|
|
(data $early "hello shared image")
|
|
|
|
(func $start
|
|
;; memory.init dst src len
|
|
i32.const 4096
|
|
i32.const 0
|
|
i32.const 3
|
|
memory.init $late
|
|
i32.const 1024
|
|
i32.const 0
|
|
i32.const 18
|
|
memory.init $early
|
|
|
|
;; runs++ at byte 100
|
|
i32.const 100
|
|
i32.const 100
|
|
i32.load8_u
|
|
i32.const 1
|
|
i32.add
|
|
i32.store8
|
|
|
|
;; the global the declared initializer does not give us
|
|
i32.const 42
|
|
global.set $marker)
|
|
(start $start)
|
|
|
|
(func (export "peek") (param i32) (result i32)
|
|
local.get 0
|
|
i32.load8_u)
|
|
|
|
(func (export "poke") (param i32) (param i32)
|
|
local.get 0
|
|
local.get 1
|
|
i32.store8)
|
|
|
|
(func (export "marker") (result i32)
|
|
global.get $marker))
|