Skip to content

runtime/wasm: add WASI single-worker scheduler (based on xgo-dev #2192, #2208) - #100

Closed
cpunion wants to merge 17 commits into
codex/fork-wasm-a-x-base-20260801from
codex/fork-wasm-wasi-single-worker-20260801
Closed

runtime/wasm: add WASI single-worker scheduler (based on xgo-dev #2192, #2208)#100
cpunion wants to merge 17 commits into
codex/fork-wasm-a-x-base-20260801from
codex/fork-wasm-wasi-single-worker-20260801

Conversation

@cpunion

@cpunion cpunion commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Depends on xgo-dev#2192 and xgo-dev#2208.

Fork-only staging PR for xgo-dev#2152. Its base branch is the validated merge of xgo-dev#2192 and xgo-dev#2208 so this PR shows only the W increment and does not add CI load to xgo-dev/llgo.

Problem

GOOS=wasip1 GOARCH=wasm llgo build can produce a module after xgo-dev#2192, but goroutines still select the pthread runtime. The resulting executable imports shared host memory and WASI pthread facilities, so ordinary single-worker Wasmtime execution cannot run Go scheduler behavior.

The continuation implementation also needs a clear ownership boundary. Scheduler code should not depend on whether a backend uses Emscripten Fiber, a raw Asyncify save area, a future resumable ABI, or standardized stack switching.

The J32/P1 type model also previously used 4-byte maximum alignment even though LLVM wasm32 aligns 64-bit scalars to 8 bytes. For nested structs this made Go unsafe.Offsetof differ from LLVM field addresses: runtimeContext.g was 324 bytes to Go but 328 bytes to LLVM, placing later scheduler fields outside the 376-byte allocation.

Implementation

  • Make WASI pthread scheduling an explicit LLGO_WASI_THREADS=1 compatibility mode.
  • Add a raw wasm32 execution-context backend and a one-M/one-P FIFO scheduler for default WASI Preview1 builds.
  • Run package initialization and main.main as the first schedulable task while keeping the host entry on the system stack.
  • Keep backend-specific stack and Asyncify storage ownership in runtime/internal/wasmcontext.
  • Keep queue ownership and scheduling policy in runtime/internal/runtime; the continuation package does not inspect G/M/P state.
  • Let the runtime supply root-aware allocation callbacks only during cold context creation and destruction; hot-path Swap, Resume, and Suspend calls remain concrete.
  • Declare Asyncify as a typed target capability and instrument only final executables. Archives and shared libraries remain unchanged.
  • Preserve the pthread source selection when LLGO_WASI_THREADS=1 is set.
  • Keep 4-byte J32/P1 words and pointers while using LLVM-compatible 8-byte maximum alignment. The scheduler fixture compares unsafe.Offsetof/Sizeof with actual field addresses and array stride.

This is the scheduler/context foundation only. Blocking channels, timers, host async integration, wasm GC and safepoints, and optional WASI Preview2 support remain separate work tracked by xgo-dev#2152.

Validation

All local commands were bounded with GOMAXPROCS=2, GOMEMLIMIT=6GiB, and -p=1 where applicable.

  • macOS arm64, Go 1.26.5, LLVM 19: go test ./internal/build ./internal/crosscompile ./ssa passes.
  • Runtime packages runtime/internal/wasmcontext, runtime/internal/runqueue, runtime/internal/clite/emscripten, and runtime/internal/runtime pass/build.
  • J64 (GOOS=js GOARCH=wasm), J32 (-target wasm), and P1 (GOOS=wasip1 GOARCH=wasm) scheduler and deadlock fixtures pass.
  • P1 validates with wasm-tools --features all and runs under Wasmtime 39.0.1.
  • Explicit LLGO_WASI_THREADS=1 P1 runtime selection builds.
  • Changed helpers are covered: wasmcontext and runqueue are 100%; genMainModule is 96.1%; defineWasmMainTask and effectiveTypeSizes are 100%; wasm post-link main paths are covered.
  • The new layout acceptance test fails with the old compiler (panic: uint64 field layout mismatch) and passes on J32, J64, and P1 after the fix; P1 also passes wasm-tools validate --features all.
  • Correct alignment grows the J32/P1 per-G runtimeContext allocation from the incorrect 376 B to 384 B (+8 B); the 64 KiB stack and 64 KiB Asyncify reservations are unchanged. On the same B fixture, J32 wasm changes from 554,724 B to 554,612 B (-112 B), P1 from 531,242 B to 530,866 B (-376 B), and the J32 JS loader is byte-size identical. J64 and all native/embedded type models are unchanged.
  • The updated runtime/wasm: add single-worker Asyncify scheduler xgo-dev/llgo#2192 parent is complete: 40 checks pass across Ubuntu, macOS, both supported Go versions, wasm runtime execution, LTO, coverage, and Codecov; only release publication is intentionally skipped. The pre-layout-fix fork head completed 39 checks across Ubuntu, macOS, both supported Go versions, wasm runtime execution, LTO, coverage, release artifacts, and embedded targets, with only release publication intentionally skipped. The layout-fix head 50866cd89 also completed 39 checks across Ubuntu, macOS, Go 1.24/1.26, wasm runtime execution, LTO, coverage, release artifacts, and embedded targets; only release publication was intentionally skipped.

Independent diff over the xgo-dev#2192 + xgo-dev#2208 base: 32 files, +1,438/-105. No previously runnable test is skipped or ignored.

cpunion added 16 commits July 28, 2026 12:57
…y-scheduler' into codex/wasm-wasi-single-worker
…y-scheduler' into codex/wasm-wasi-single-worker
…/fork-wasm-wasi-single-worker-20260801

# Conflicts:
#	runtime/internal/runtime/g_pthread.go
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

50866cd89e6a | workflow run | long-term charts

Program measurements

Platform Workload File size vs main Build vs main Run vs main
Linux cprintf 18624 B +0.4% (worse) 285.213 ms -30.8% (better) 1.068 ms -31.2% (better)
Linux fmtprintf 2217960 B +0.2% (worse) 2.603 s -32.5% (better) 2.122 ms -35.4% (better)
Linux println 71584 B -1.5% (better) 275.579 ms -31.1% (better) 1.423 ms -15.0% (better)
macOS cprintf 84672 B +0.0% 361.354 ms -12.8% (better) 2.487 ms -30.7% (better)
macOS fmtprintf 2361520 B +0.7% (worse) 4.177 s +6.0% (worse) 21.277 ms -9.0% (better)
macOS println 125712 B -1.0% (better) 365.962 ms -11.7% (better) 3.787 ms -47.8% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs main
Linux BenchmarkLookupPCRandom 9.632 ns/op -28.5% (better)
Linux BenchmarkMergeCompilerFlags 111.700 ns/op -27.7% (better)
Linux BenchmarkMergeLinkerFlags 72.820 ns/op -28.1% (better)
Linux BenchmarkChannelBuffered 29.440 ns/op -15.3% (better)
Linux BenchmarkChannelHandoff 18757 ns/op -41.4% (better)
Linux BenchmarkDefer 34.070 ns/op -39.5% (better)
Linux BenchmarkDirectCall 1.363 ns/op -12.8% (better)
Linux BenchmarkGlobalRead 1.364 ns/op -12.6% (better)
Linux BenchmarkGlobalWrite 2.177 ns/op -12.7% (better)
Linux BenchmarkGoroutine 24159 ns/op -68.0% (better)
Linux BenchmarkInterfaceCall 6.822 ns/op -12.5% (better)
Linux BenchmarkRuntimeGetG 1.636 ns/op -69.2% (better)
macOS BenchmarkLookupPCRandom 17.260 ns/op +38.7% (worse)
macOS BenchmarkMergeCompilerFlags 157.600 ns/op +26.6% (worse)
macOS BenchmarkMergeLinkerFlags 117.300 ns/op +69.1% (worse)
macOS BenchmarkChannelBuffered 41.610 ns/op +50.1% (worse)
macOS BenchmarkChannelHandoff 8572 ns/op +22.3% (worse)
macOS BenchmarkDefer 41.230 ns/op +10.2% (worse)
macOS BenchmarkDirectCall 1.230 ns/op -15.3% (better)
macOS BenchmarkGlobalRead 1.066 ns/op -12.0% (better)
macOS BenchmarkGlobalWrite 1.185 ns/op -20.3% (better)
macOS BenchmarkGoroutine 40618 ns/op +28.3% (worse)
macOS BenchmarkInterfaceCall 5.202 ns/op -3.6% (better)
macOS BenchmarkRuntimeGetG 2.531 ns/op -1.8% (better)

Compared only with the latest matching platform in the main series.

Warning

  • Persistent publishing is unavailable because no data token is configured.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant