Skip to content

WASI-0.3.0-official inventory sweep: docs truth-sweep + system-clock host support#149

Merged
chaploud merged 6 commits into
mainfrom
develop/wasm30-wasi03-inventory-sweep
Jul 17, 2026
Merged

WASI-0.3.0-official inventory sweep: docs truth-sweep + system-clock host support#149
chaploud merged 6 commits into
mainfrom
develop/wasm30-wasi03-inventory-sweep

Conversation

@chaploud

Copy link
Copy Markdown
Contributor

Summary

WASI 0.3.0 was officially released 2026-06-11 (release, Bytecode Alliance announcement). This PR is the inventory sweep of zwasm's Wasm 3.0 / WASI 0.3 completeness claims against that now-official spec (reference clones pulled to current upstream), in three commits:

1. Docs/meta truth-sweep (695f8bd03)

Claim-vs-reality fixes found by the inventory:

  • zwasm --help claimed interp (default) — the real default is auto (JIT + interp fallback); also --cache/--cache-clear were missing from the usage text
  • canon.zig still said "utf16/latin1 pending" (D-502 implemented them); the 3.0 spec runner docstring still said "SKELETON" (it JIT-executes with real accounting)
  • migration guide claimed GC is "opt-in (-Dgc, default off)" — measured reality: -Dgc is inert (its only reader is an unconsumed re-export); GC ships with the default -Dwasm=v3_0 and its strip lever is the wasm-level DCE → new debt D-525 (wire per ADR-0115 §3 or retire)
  • README gains a WASI 0.3 row (core, opt-in -Dwasi=p3) and the complete 9-proposal list (custom annotations was missing)
  • reference_clones.md: WASI monorepo (spec v0.3.0 + living WIT) / component-model / wasi-testsuite rows; the per-interface wasi-* repos were archived upstream 2025-11-25; version-pin section rewritten for the wg-3.0 / spec_pin.yaml reality
  • proposal_watch.md: 2026-07-17 review entry recording the official-0.3.0 WIT diff; the WASI roadmap table no longer claims a stack-switching gate (contradicted its own header callout)
  • New debt: D-523 (the committed wasip3 fixtures actually import wasi 0.2.6 — the pinned rust wasip3 nightly predates the release; regen needs a toolchain bump), D-524 (official-0.3.0 host residual: async wait-until/wait-for, version-gating note)

2. Official 0.3.0 clocks host surface (c4848d44b)

0.3.0 renamed wasi:clocks/wall-clocksystem-clock with instant{seconds: s64, nanoseconds: u32} and added get-resolution to both clocks. This ships the sync half:

  • adapter rows + P2Op/P1Target extensions (0.3.0 names are collision-free with 0.2 under version-stripped matching)
  • clockTimeNsSigned (i96 — pre-1970 instants representable, floored {-1s, 999999999ns} split per the WIT example, unit-tested) + clockResNs, with the existing P1 paths refactored on top (behavior-preserving)
  • hand-built component fixture wasi_p3_systemclock.{wat,wasm} asserting real values (seconds past 2017, 0 < resolution ≤ 1s) via exit code; verified e2e through the CLI (exit 0)

DA-critique check #12: 19/20 → the signed-instant finding fixed in the same commit.

3. Handover refresh (79bff0e26, 3dd212153)

Test plan

  • zig build test green locally (aarch64-macos)
  • fixture e2e via zwasm run (exit 0)
  • gate_commit.sh green on all commits
  • CI ci-required 3-OS gate

Bench record skipped per the per-merge policy (no engine/interp/JIT hot-path change).

chaploud added 6 commits July 17, 2026 15:13
Inventory sweep against the officially released WASI 0.3.0 spec
(2026-06-11, ~/Documents/OSS/WASI) + refreshed reference clones:

- zwasm --help: engine default is auto (was claiming interp);
  list --cache/--cache-clear (shipped 2.2.0)
- main.zig doc: .cwasm v0.1/ADR-0039 -> v0.5/ADR-0203; engine default
- canon.zig: drop the stale "utf16/latin1 pending" claims (D-502
  implemented them at the canon layer)
- spec_assert_runner_wasm_3_0: docstring said SKELETON; it JIT-executes
  with real pass/fail accounting since Phase 10
- gc/register.zig: GC shipped; note the unwired enable_gc gate (D-525)
- README: WASI 0.3 row (core, opt-in -Dwasi=p3, official-0.3.0 deltas
  pending); complete the 9-proposal list (custom annotations)
- migration guide: GC is on by default (-Dgc is inert, not "opt-in");
  WASI 0.3.0 released (drop "until it settles")
- CHANGELOG: Unreleased entries + 9-proposal list completion
- reference_clones.md: add WASI monorepo / component-model /
  wasi-testsuite / wasmtime-py rows; wasi-* per-interface repos archived
  upstream 2025-11-25; version-pin section rewritten for the wg-3.0 /
  spec_pin.yaml reality; fix the ClojureWasm path
- proposal_watch.md: 2026-07-17 review entry (official-0.3.0 WIT diff:
  system-clock rename, instant{s64,u32}, monotonic wait-until/wait-for
  async funcs, wasi:io deletion; wasip3 fixtures actually import 0.2.6);
  WASI roadmap table de-staled (0.2 row said ACTIVE, 0.3 row claimed a
  stack-switching gate contradicting the header callout)
- debt.yaml: D-523 (fixture regen vs official 0.3.0 toolchain),
  D-524 (official-0.3.0 host-surface residual), D-525 (-Dgc inert)
…esolution

WASI 0.3.0 (released 2026-06-11) renamed wasi:clocks/wall-clock to
system-clock with instant{seconds: s64, nanoseconds: u32} and added
get-resolution to both clocks. Serve the sync half on the component host:

- adapter: P2Op clocks_system_now / clocks_system_get_resolution /
  clocks_monotonic_get_resolution + table rows (version-stripped names
  are 0.3.0-unique, no 0.2 collision) + P1Target.clock_res_get
- clocks.zig: clockTimeNsSigned (i96; pre-1970 instants representable)
  + clockResNs, with clockTimeNs/clockResGet refactored on top
  (behavior-preserving)
- trampolines: p2SystemNow writes the floored instant split
  (instantFromNs: {-1s, 999999999ns} = 1ns before epoch, per the WIT
  example; unit-tested), get-resolution returns duration(u64) directly
- fixture test/component/wasi_p3_systemclock.{wat,wasm}: asserts real
  values (seconds past 2017, 0 < resolution <= 1s) via exit code, plus
  adapter classify/p1Target unit tests

The async monotonic wait-until/wait-for need a scheduler-wired timer
waitable — tracked as D-524. DA-critique 19/20 -> signed-instant fix
applied (check #12).
…tures (D-523 refined)

rust-overlay input updated (2026-07-17). Nightlies 2026-07-08/-16 cannot
-Z build-std wasm32-wasip3 (upstream std/sys/env/wasi.rs OsStringExt
regression) — 2026-06-24 is the newest-known-good and compiles std's
wasip3 0.6.0+wasi-0.3.0-rc crate; recipe re-verified, test-wasi-p3 green.

Measured refutation recorded in D-523: the regenerated fixtures STILL
import wasi 0.2.6 — the imports come from the borrowed wasip2 wasi-libc
(sync-main stdio/env/clock route through -lc), not the nightly's std
bindings, so a nightly bump alone cannot discharge the official-WIT
fixture gap. Discharge now = wasip3-native wasi-libc upstream OR the
wit-bindgen fixture path.
Bundled into the sweep PR per user grant (single CI run). Tag v2.3.0 is
cut after merge (user-granted in-session per ADR-0156); the Homebrew
formula in clojurewasm/homebrew-tap points at the v2.3.0 release assets.
@chaploud

Copy link
Copy Markdown
Contributor Author

Scope extended per user grant (bundle everything into one CI run):

  • wasip3 gen toolchain repinned nightly-2026-06-14 → 2026-06-24 + fixtures regenerated (591af344d). Measured refutation: the 0.2.6 imports come from the borrowed wasip2 wasi-libc, not the nightly pin — std has pinned wasip3 0.6.0+wasi-0.3.0-rc since ~April and the regen still emits all-0.2.6. Nightlies 2026-07-08/-16 cannot -Z build-std wasip3 at all (upstream OsStringExt std regression) — 06-24 is newest-known-good. D-523 refined accordingly (discharge = wasip3-native wasi-libc upstream, or the wit-bindgen fixture path). zig build test-wasi-p3 + full unit suite green on the regenerated corpus.
  • v2.3.0 release prep (fed022fd0): CHANGELOG [2.3.0] - 2026-07-17, build.zig.zon 2.2.1 → 2.3.0, README Install section (Homebrew brew install clojurewasm/tap/zwasm + release binaries). Merge + tag v2.3.0 are user-granted in-session (ADR-0156); the tap formula is pushed after the release assets exist (needs their sha256).

@chaploud
chaploud merged commit b958638 into main Jul 17, 2026
5 checks passed
@chaploud
chaploud deleted the develop/wasm30-wasi03-inventory-sweep branch July 17, 2026 08:11
chaploud added a commit that referenced this pull request Jul 17, 2026
…lds DCE feature.gc (#150)

`check_build_dce` FAILed on `main` (all six `-Dwasm=v1_0` / `-Dwasm=v2_0`
rows): `instruction.wasm_3_0.ref_test_ops.gcRefMatchesNonNullCore` was
present in every non-GC binary.

Root cause: ADR-0203 D1 (D-516 position-independence) holds the GC /
subtyping JIT helpers as `JitRuntime` fn-pointer FIELDS whose DEFAULT is
the real helper (so a reloaded `.cwasm` resolves the address in the
running process — no setup wiring). A field default `= jitGcRefTest`
takes the helper's ADDRESS unconditionally, and `JitRuntime` is
instantiated in every build, so the whole GC cohort
(`jitGcAlloc` / `jitGcArray*` / `jitGcRefTest` / `jitGcRefCast` /
`jitCallIndirectResolve`) — and its `feature/gc/*` reach — stayed live
even in a v1_0 build. Only the one symbol whose namespace path contains
`wasm_3_0` tripped the `nm`-grep gate, but the entire cohort leaked.

The emit sites read the helper via `@offsetOf` (an offset, not an
address) and are comptime-fenced behind the wasm_3_0 op dispatch, so they
DCE fine — the struct field default is the only always-live address-take.

Fix: guard each helper body with `if (comptime !wasm_v3_plus) @Panic(...)`
as the first statement. In a sub-v3 build the comptime-false remainder is
un-analysed, so the default points at a bare `@panic` stub with no GC
references and the GC code DCEs. v3 builds are unaffected (guard is
`if (comptime false)` → real body runs; ADR-0203 D1 intact). A sub-v3
build can never emit a GC / subtyping op (the validator rejects them), so
the stub is unreachable — `@panic` is the correct default (mirrors
`defaultReifyExnref`).

Verified: v1_0 + v2_0 forbidden-symbol scan CLEAN on both arm64-native
and x86_64-linux cross (the CI leg's arch); v1_0:p1 `.text` also drops
2957749 → 2614800 B as the leaked GC code is reclaimed. `zig build test`
+ lint green. The regression shipped 2026-07-09 (ADR-0203) but surfaced
only now: `check_build_dce` runs in the push-to-main extended leg and the
intervening main pushes were doc-only (heavy legs skipped) or
concurrency-cancelled, so the gate never completed until #149.

Lesson: 2026-07-18-fnptr-field-default-defeats-build-option-dce.
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