Skip to content

[Based on #125] runtime/wasm: integrate Go-style resumable backend - #134

Closed
cpunion wants to merge 28 commits into
codex/fork-wasm-runtime-hardening-20260802from
codex/fork-wasm-resumable-integration-20260802
Closed

[Based on #125] runtime/wasm: integrate Go-style resumable backend#134
cpunion wants to merge 28 commits into
codex/fork-wasm-runtime-hardening-20260802from
codex/fork-wasm-resumable-integration-20260802

Conversation

@cpunion

@cpunion cpunion commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Depends on #125.

Summary

Integrate the experimental Go-style resumable ABI with the complete single-worker wasm runtime behavior already staged through B/C/D/S/H.

This carries the validated K core from #127 onto the hardening baseline and adds the runtime/compiler integration needed for:

  • channel, select, sync, timer, and host-wait suspension;
  • precise roots for running and suspended goroutines;
  • cooperative safepoints and the default single-worker wasm collector;
  • panic/defer/recover root-chain restoration;
  • J32, J64, and P1 lifecycle hardening without Asyncify.

After this PR is green, #127 can be closed as superseded by this complete integration branch.

Implementation

  • Keep actual //go:wasmimport declarations synchronous while allowing same-name LLGo patch definitions to use the resumable ABI.
  • Route ABI type metadata and indirect Go entry points through generated resume start functions.
  • Retain compiler GC-root allocas and their derived pointers in persistent continuation frames.
  • Give each resumable G and the scheduler its own registered GC-root context, switching ownership only at scheduler boundaries.
  • Restore the active root chain before the scheduler panic catch handles defer/recover/Goexit.
  • Add a generation-checked JS host timer dispatcher; stale callbacks cannot re-enter the scheduler.
  • Keep P1 host waiting synchronous and preserve the explicit wasi-threads compatibility path.
  • Select all new runtime and host files only for the private llgo.wasm_resume configuration.

Cost comparison against #125

Same hardening fixture and default optimization, Apple M4 Max. Runtime values are medians from five alternating processes with 1,000 blocked Gs.

Profile Asyncify roundtrip K roundtrip Ratio Asyncify live bytes/G K live bytes/G
J32 9.243 us 165.617 us 17.92x slower about 131.6 KiB about 2.55 KiB
J64 11.324 us 96.516 us 8.52x slower about 132.1 KiB about 3.03 KiB
P1 1.818 us 174.184 us 95.81x slower about 131.6 KiB about 2.57 KiB
  • At 10,000 blocked Gs, J32/J64 Asyncify exceed Emscripten's 2 GiB memory limit. K completes with about 27.7/30.4 MiB live heap and about 49.8/50.1 MiB HeapSys. P1 Asyncify completes at about 1.316 GiB live heap; K uses about 25.5 MiB.
  • Combined JS loader + wasm size changes from 2,567,482 to 2,276,762 bytes for J32 (-11.32%) and 2,840,922 to 2,593,225 bytes for J64 (-8.72%). P1 wasm changes from 2,062,364 to 1,701,475 bytes (-17.50%).
  • Three interleaved -a -p=1 J32 builds have medians of 16.364 s for Asyncify and 29.991 s for K (+83.27%).
  • Temporary runtime.MemStats instrumentation around the J32 channel loop measures 20,739 B/op and 12.010 allocations/op for K versus 93 B/op and 2.001 allocations/op for Asyncify. A 100 us V8 sampling profile attributes about 65% of wasm samples to allocator/bitmap paths. The current 2 KiB frame arena therefore repeatedly allocates and drops overflow blocks on deep channel call paths; this is the primary measured runtime cost, rather than Context.Run dispatch alone.
  • The memory and code-size gains are substantial, but the execution and compile-time gates do not pass. K must remain opt-in. The next optimization should retain lazily allocated per-G high-water frame blocks and generate an inline arena fast path, while keeping shallow/untouched G storage sparse.

Validation

Local resource limits: GOMAXPROCS=2, GOMEMLIMIT=4GiB, -p=1.

  • J32/J64/P1: scheduler normal/deadlock, timers, GC, and runtime hardening all execute successfully.
  • P1 artifacts pass wasm-tools validate --features all and execute with Wasmtime 39.
  • J32 hardening executes at -O0, -O3, ThinLTO, and Full LTO.
  • go test ./internal/wasmresume ./ssa ./cl passes; package coverage is 94.7%, 93.3%, and 97.4%.
  • go test ./internal/build passes.
  • Runtime gcroot, wasmresume, and wasmevent tests pass with 94.1%, 91.9%, and 94.4% coverage.
  • The hardening fixture covers indirect suspension, panic/recover, scheduler handoffs, blocked-G roots, C boundaries, heap growth, and shutdown.
  • Fork CI passes all 39 required checks with one expected release skip. One Go 1.26 browser worker-page timing failure passed an unchanged single-job rerun; the same Chrome case also passes 20/20 locally.

cpunion added 28 commits August 2, 2026 12:31
@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 2, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

685e037e1e57 | workflow run | long-term charts

Program measurements

Platform Workload File size vs main Build vs main Run vs main
Linux cprintf 18816 B +1.5% (worse) 249.975 ms -39.4% (better) 822.591 us -47.0% (better)
Linux fmtprintf 2219448 B +0.3% (worse) 2.587 s -32.9% (better) 1.639 ms -50.1% (better)
Linux println 71776 B -1.2% (better) 244.350 ms -38.9% (better) 1.033 ms -38.3% (better)
macOS cprintf 84672 B +0.0% 507.171 ms +22.4% (worse) 3.647 ms +1.6% (worse)
macOS fmtprintf 2361968 B +0.7% (worse) 3.522 s -10.6% (better) 19.462 ms -16.7% (better)
macOS println 125712 B -1.0% (better) 435.647 ms +5.1% (worse) 4.871 ms -32.9% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs main
Linux BenchmarkLookupPCRandom 8.078 ns/op -40.1% (better)
Linux BenchmarkMergeCompilerFlags 101.100 ns/op -34.6% (better)
Linux BenchmarkMergeLinkerFlags 65.850 ns/op -35.0% (better)
Linux BenchmarkChannelBuffered 39.790 ns/op +14.5% (worse)
Linux BenchmarkChannelHandoff 23938 ns/op -25.3% (better)
Linux BenchmarkDefer 37.190 ns/op -34.0% (better)
Linux BenchmarkDirectCall 0.305 ns/op -80.5% (better)
Linux BenchmarkGlobalRead 0.353 ns/op -77.4% (better)
Linux BenchmarkGlobalWrite 6.975 ns/op +179.7% (worse)
Linux BenchmarkGoroutine 48240 ns/op -36.2% (better)
Linux BenchmarkInterfaceCall 3.993 ns/op -48.8% (better)
Linux BenchmarkRuntimeGetG 0.724 ns/op -86.4% (better)
macOS BenchmarkLookupPCRandom 12.950 ns/op +4.1% (worse)
macOS BenchmarkMergeCompilerFlags 145.800 ns/op +17.1% (worse)
macOS BenchmarkMergeLinkerFlags 81.250 ns/op +17.2% (worse)
macOS BenchmarkChannelBuffered 36.570 ns/op +31.9% (worse)
macOS BenchmarkChannelHandoff 12221 ns/op +74.4% (worse)
macOS BenchmarkDefer 69.310 ns/op +85.2% (worse)
macOS BenchmarkDirectCall 1.703 ns/op +17.2% (worse)
macOS BenchmarkGlobalRead 1.213 ns/op +0.2% (worse)
macOS BenchmarkGlobalWrite 1.462 ns/op -1.7% (better)
macOS BenchmarkGoroutine 25764 ns/op -18.6% (better)
macOS BenchmarkInterfaceCall 5.925 ns/op +9.8% (worse)
macOS BenchmarkRuntimeGetG 3.022 ns/op +17.3% (worse)

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