⏺ WASM Tier 1 is productionized and verified end-to-end. #25

Merged
navicore merged 2 commits from wasm-t1 into main 2026-06-20 14:24:51 +00:00
Owner

What landed

plgc build --target wasm32-wasi prog.pl -o prog.wasm is now
a real command — the hand-script from the spike is gone.

  • lib.rs — Target enum; compile_files takes a target and
    routes to link_ir (native) or link_wasm; feature-gated
    WASM_RUNTIME_LIB.
  • codegen/program.rs — target threads through to emit the
    wasm32-wasi triple and name the entry __main_argc_argv (the
    wasi startup bridge) instead of main. The IR body is
    unchanged — same musttail chains.
  • link.rs — link_wasm: locates rustup's llc/rust-lld and
    the wasm target's self-contained wasi-libc via rustc
    --print sysroot, runs llc -mattr=+tail-call → wasm-ld, with
    helpful errors naming the exact rustup command when a
    piece is missing.
  • build.rs + Cargo.toml — a wasm cargo feature embeds the
    wasm runtime archive. Default cargo install plgc is
    byte-for-byte unchanged and needs no wasm toolchain.
  • main.rs — --target flag, parse_target, .wasm default
    output extension.
  • justfile — build-runtime-wasm, install-wasm, and a local
    wasm-smoke test.
  • Docs — first-class docs/wasm-target.md, linked from
    SUMMARY.md and compiler-usage.md; ROADMAP M10 recorded; the
    Future "cross-compilation" bullet narrowed to Tier 2.

Verified

  • Byte-identical to native across atom/int/findall queries
    (just wasm-smoke, 3/3).
  • Constant stack on wasm (the gate result holds in the real
    plgc path).
  • Default build unaffected — native cargo
    build/clippy/tests all green without the feature.
  • Clean failure modes — wasm request on a non-wasm plgc and
    an unknown --target both give actionable errors, no crash.
  • Full workspace suite (24 binaries) green, fmt clean,
    clippy clean on both feature sets.

Honest scope notes

  • Local-only, as agreed — wasm-smoke is a just recipe, not
    a CI test; the runner doesn't have the wasm toolchain yet.
    CI wiring is the deferred follow-up (recorded in M10).
  • plgc run --target wasm32-wasi (compile-and-exec via a
    bundled engine) isn't wired — build to .wasm is the Tier 1
    deliverable; you run it with wasmtime run.
  • The toolchain discovery reaches into rustup's internal
    layout (/lib/rustlib/.../bin), which is the known
    brittleness of the "reuse Rust llvm-tools" choice — it
    errors clearly if the layout/components aren't present.
What landed plgc build --target wasm32-wasi prog.pl -o prog.wasm is now a real command — the hand-script from the spike is gone. - lib.rs — Target enum; compile_files takes a target and routes to link_ir (native) or link_wasm; feature-gated WASM_RUNTIME_LIB. - codegen/program.rs — target threads through to emit the wasm32-wasi triple and name the entry __main_argc_argv (the wasi startup bridge) instead of main. The IR body is unchanged — same musttail chains. - link.rs — link_wasm: locates rustup's llc/rust-lld and the wasm target's self-contained wasi-libc via rustc --print sysroot, runs llc -mattr=+tail-call → wasm-ld, with helpful errors naming the exact rustup command when a piece is missing. - build.rs + Cargo.toml — a wasm cargo feature embeds the wasm runtime archive. Default cargo install plgc is byte-for-byte unchanged and needs no wasm toolchain. - main.rs — --target flag, parse_target, .wasm default output extension. - justfile — build-runtime-wasm, install-wasm, and a local wasm-smoke test. - Docs — first-class docs/wasm-target.md, linked from SUMMARY.md and compiler-usage.md; ROADMAP M10 recorded; the Future "cross-compilation" bullet narrowed to Tier 2. Verified - Byte-identical to native across atom/int/findall queries (just wasm-smoke, 3/3). - Constant stack on wasm (the gate result holds in the real plgc path). - Default build unaffected — native cargo build/clippy/tests all green without the feature. - Clean failure modes — wasm request on a non-wasm plgc and an unknown --target both give actionable errors, no crash. - Full workspace suite (24 binaries) green, fmt clean, clippy clean on both feature sets. Honest scope notes - Local-only, as agreed — wasm-smoke is a just recipe, not a CI test; the runner doesn't have the wasm toolchain yet. CI wiring is the deferred follow-up (recorded in M10). - plgc run --target wasm32-wasi (compile-and-exec via a bundled engine) isn't wired — build to .wasm is the Tier 1 deliverable; you run it with wasmtime run. - The toolchain discovery reaches into rustup's internal layout (<sysroot>/lib/rustlib/.../bin), which is the known brittleness of the "reuse Rust llvm-tools" choice — it errors clearly if the layout/components aren't present.
⏺ WASM Tier 1 is productionized and verified end-to-end.
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 59s
b4916c3eb6
What landed

  plgc build --target wasm32-wasi prog.pl -o prog.wasm is now
  a real command — the hand-script from the spike is gone.

  - lib.rs — Target enum; compile_files takes a target and
  routes to link_ir (native) or link_wasm; feature-gated
  WASM_RUNTIME_LIB.
  - codegen/program.rs — target threads through to emit the
  wasm32-wasi triple and name the entry __main_argc_argv (the
  wasi startup bridge) instead of main. The IR body is
  unchanged — same musttail chains.
  - link.rs — link_wasm: locates rustup's llc/rust-lld and
  the wasm target's self-contained wasi-libc via rustc
  --print sysroot, runs llc -mattr=+tail-call → wasm-ld, with
  helpful errors naming the exact rustup command when a
  piece is missing.
  - build.rs + Cargo.toml — a wasm cargo feature embeds the
  wasm runtime archive. Default cargo install plgc is
  byte-for-byte unchanged and needs no wasm toolchain.
  - main.rs — --target flag, parse_target, .wasm default
  output extension.
  - justfile — build-runtime-wasm, install-wasm, and a local
  wasm-smoke test.
  - Docs — first-class docs/wasm-target.md, linked from
  SUMMARY.md and compiler-usage.md; ROADMAP M10 recorded; the
  Future "cross-compilation" bullet narrowed to Tier 2.

  Verified

  - Byte-identical to native across atom/int/findall queries
  (just wasm-smoke, 3/3).
  - Constant stack on wasm (the gate result holds in the real
  plgc path).
  - Default build unaffected — native cargo
  build/clippy/tests all green without the feature.
  - Clean failure modes — wasm request on a non-wasm plgc and
  an unknown --target both give actionable errors, no crash.
  - Full workspace suite (24 binaries) green, fmt clean,
  clippy clean on both feature sets.

  Honest scope notes

  - Local-only, as agreed — wasm-smoke is a just recipe, not
  a CI test; the runner doesn't have the wasm toolchain yet.
  CI wiring is the deferred follow-up (recorded in M10).
  - plgc run --target wasm32-wasi (compile-and-exec via a
  bundled engine) isn't wired — build to .wasm is the Tier 1
  deliverable; you run it with wasmtime run.
  - The toolchain discovery reaches into rustup's internal
  layout (<sysroot>/lib/rustlib/.../bin), which is the known
  brittleness of the "reuse Rust llvm-tools" choice — it
  errors clearly if the layout/components aren't present.
Author
Owner

Review — WASM Tier 1 productionization

This is what "additive new target on a working compiler" should look like. The native path is unchanged byte-for-byte; the wasm path is strictly opt-in (--features wasm); the IR body is identical between targets — only the triple, the entry symbol, and the linker driver differ. The years of discipline around musttail pay off here exactly: LLVM's wasm backend lowers musttail to return_call, so the constant-stack guarantee that took PRs #20/#24 to nail down for native carries through for wasm with zero IR changes.

Strong calls worth naming:

  • Default cargo install plgc is byte-for-byte unchanged. WASM_RUNTIME_LIB: Option<&[u8]> is None without the feature; no native code path is altered; the Target::Wasm arm fails with an actionable "rebuild with --features wasm" before reaching the linker. This is the right opt-in shape.
  • -mattr=+tail-call is the load-bearing flag. The PR description's claim "If the tail-call feature is ever missing, llc fails at build time — it never silently emits a non-tail call" is what makes "constant stack on wasm" defensible. Build-time enforcement, not runtime hope.
  • __main_argc_argv as the entry symbol. This is the correct wasi-libc bridge (_start → __main_void → __main_argc_argv). Getting this wrong produces a module that loads-but-does-nothing — a confusing failure mode you've sidestepped by naming it explicitly.
  • Build-time toolchain probes name the recovery command. Missing llc/rust-lldrustup component add llvm-tools-preview. Missing wasi-libc → rustup target add wasm32-wasip1. Missing wasm runtime archive → just build-runtime-wasm. Each error names the exact next step. UX discipline throughout.
  • CLI accepts both wasm32-wasi and wasm32-wasip1. Legacy name and Rust's current name both work. Good usability call — the WASI alias is widely-known; the wasip1 form is what Rust prints internally.
  • No new dependencies. Reuses rustup's bundled tooling rather than adding a crate. Lower attack surface, no upstream coordination cost.
  • The Cargo feature is named wasm, not wasm32-wasi. Future-proofs against Tier 2 (V8 isolates / wasm32-unknown-unknown) adding bullets to the feature without renaming.
  • The wasm path comment honestly acknowledges the native build.rs limitation. "The wasm archive is a primary build of plg-runtime, so its uplifted top-level name is reliable (unlike the native deps/ case above)" — directly references the PR #15 mtime story I've been tracking. Useful continuity for whoever inherits this file.

Issues below — none are blockers.

Real concerns

1. The toolchain discovery reaches into rustup's internal layout. The PR description names this explicitly, and the error message recovers well, but it's worth one line of ROADMAP/wasm-target.md recording the strategy: "track rustup's <sysroot>/lib/rustlib/<host>/bin/ layout; if it ever moves, switch to llvm-config or the wasi-sdk distribution." The layout has been stable for years and Rust is unlikely to change it without warning, but if it ever does, the breakage is silent ("file not found" → user thinks their install is broken). A line in the doc saves a future contributor (or your future self) the diagnostic time.

Two small durability follow-ups inside wasm_toolchain:

  • rustc -vV then parsing host: is the host-triple discovery. rustc --print host-tuple (documented stable since ~1.85) does the same without string parsing. Marginal but more durable.
  • The wasi-libc check looks for self_contained/libc.a and reports "needs the wasm32-wasip1 std" — fine when the target is unconfigured, but if self_contained/ exists with a different missing file (e.g. partial install), the message is misleading. Adding a self_contained.exists() first-step check would distinguish "target not added" from "target partially broken."

2. The "constant stack on wasm" claim is structural, not exercised at depth. The wasm-smoke runs three flat queries against examples/deps.pl — they prove the wire contract round-trips, but they don't exercise the musttail-as-return_call lowering at the recursion depths where it actually matters. PR #24's call1_recursion_runs_in_constant_c_stack runs 100k-deep under a 512KB native stack; an analogous wasmtime run --wasm max-stack-size=… invocation against a recursive Prolog program would close the structural-vs-measured gap. The just recipe could grow one more case: compile count(0). count(N) :- N > 0, N1 is N - 1, call(count(N1))., run with a constrained engine stack, assert success at 100k. That's the test that earns the "constant stack on wasm" claim.

Small observations

3. --O3 on the CLI silently becomes -O2 at the llc layer for wasm. The source comment explains: "llc's -O3 buys little for our IR and -O2 is what the gate proved." Reasonable, but a user reading --debug=false and assuming "release optimization" doesn't know they're getting -O2. One line in wasm-target.md — "wasm builds top out at LLVM -O2" — closes it.

4. --allow-undefined defers symbol resolution to instantiation. Standard wasm-ld pattern; correct for a WASI module. The trade-off is that a missing runtime import surfaces at wasmtime run time, not at link time. The wasm-smoke happens to catch this end-to-end because it runs the module, so the coverage is there — worth one comment in link_wasm noting "undefined imports survive link; the smoke test's actual run is what verifies them."

5. Tail-call feature requires a recent wasm engine. wasmtime ≥ 0.40 or so, recent wasmer, recent V8 (for the eventual Tier 2 path). Engines without it will error at instantiation with a "tail-call" reference. The error class is fine (clear and actionable), but a one-line "needs a wasm engine with the tail-call proposal" in wasm-target.md would head off "why doesn't this run on old wasmtime?" questions.

6. Three hard-coded Target::Native literals in main.rs (build path's compile_files, run path's compile_files, script-mode's compile_files). Two of those (run and script-mode) genuinely can't be wasm. The build path is the only place target is computed from the CLI. The explicit literal isn't wrong, but a compile_files_native(..) helper for the two paths that can't change would document the intent and shrink the call sites. Stylistic.

7. The parse_target function returns Err for Some("native") to map to Native — but the CLI flag accepts None | Some("native") as native. That's consistent with "no flag = native, explicit --target native = native, anything else = error". Worth confirming the docs list native as an accepted value (the current error message says "supported: native, wasm32-wasi", so it does). Good — the CLI surface, the error message, and the docs all agree.

What's good

  • musttail from PRs #20/#24 carries through to wasm with zero IR changes. This is the architectural payoff. The discipline of "every cross-predicate transfer is musttail" looked native-specific at the time but is exactly what makes wasm-target work. The PR description names this; it deserves naming again.
  • Strictly opt-in feature with byte-for-byte preservation of the default install. The boring case (a user cargo install plgcs for native work) is unaffected. The cost lands only on contributors / users who actively want wasm. This is how to add capability without taxing the default user.
  • One-source-of-truth pattern for target switching. Target enum + two switches in codegen (triple, entry name) + a parallel link_wasm function. No hidden conditionals scattered through unrelated files. If a Tier 2 target needs another switch (e.g. no main at all, exported functions only), the seam is in two known places.
  • Build.rs panic with named recovery command. --features wasm without the runtime archive panics with just build-runtime-wasm as the recovery. This is correct cargo idiom (build script failures are how cargo communicates configuration problems) and the error message is honest about cause and cure.
  • Bidirectional naming acceptance for wasm targets (wasm32-wasi and wasm32-wasip1). Avoids one of the most common "but I typed the right thing!" failure modes.
  • The wasm-smoke recipe is exactly the right scope for Tier 1. Local-only, three queries, byte-identical comparison, runs against examples/deps.pl. Catches "did the toolchain regress" on a contributor's machine without CI infrastructure investment. CI wiring as a follow-up (ROADMAP M10) is the right scope discipline.
  • Docs name what's NOT in scope. The "Edge / serverless (not yet)" section in wasm-target.md is honest about V8 isolates / wasm32-unknown-unknown being Tier 2. Future users searching for Cloudflare Workers find the doc and know where to look without filing an issue.
  • Built-in toolchain checks happen at link time, not at compile time. A user who runs plgc check or plgc compile-to-ir (the IR-only path) doesn't pay for missing wasm tools. Only the linker step requires them. This keeps the lint/check loop fast even on a wasm-enabled plgc.

Suggested order

#1's two sub-items (rustc --print host-tuple and a one-line strategy note in the docs) are small durability improvements and worth doing before merge. #2 (a deep-recursion case in wasm-smoke) is the one substantive add — it earns the "constant stack on wasm" claim in the same way PR #24 earned it natively. #3+ are observations.

## Review — WASM Tier 1 productionization This is what "additive new target on a working compiler" should look like. The native path is unchanged byte-for-byte; the wasm path is strictly opt-in (`--features wasm`); the IR *body* is identical between targets — only the triple, the entry symbol, and the linker driver differ. The years of discipline around `musttail` pay off here exactly: LLVM's wasm backend lowers `musttail` to `return_call`, so the constant-stack guarantee that took PRs #20/#24 to nail down for native carries through for wasm with zero IR changes. Strong calls worth naming: - **Default `cargo install plgc` is byte-for-byte unchanged.** `WASM_RUNTIME_LIB: Option<&[u8]>` is `None` without the feature; no native code path is altered; the `Target::Wasm` arm fails with an actionable "rebuild with `--features wasm`" before reaching the linker. This is the right opt-in shape. - **`-mattr=+tail-call` is the load-bearing flag.** The PR description's claim "If the tail-call feature is ever missing, llc fails at build time — it never silently emits a non-tail call" is what makes "constant stack on wasm" defensible. Build-time enforcement, not runtime hope. - **`__main_argc_argv` as the entry symbol.** This is the correct wasi-libc bridge (`_start → __main_void → __main_argc_argv`). Getting this wrong produces a module that loads-but-does-nothing — a confusing failure mode you've sidestepped by naming it explicitly. - **Build-time toolchain probes name the recovery command.** Missing `llc`/`rust-lld` → `rustup component add llvm-tools-preview`. Missing wasi-libc → `rustup target add wasm32-wasip1`. Missing wasm runtime archive → `just build-runtime-wasm`. Each error names the exact next step. UX discipline throughout. - **CLI accepts both `wasm32-wasi` and `wasm32-wasip1`.** Legacy name and Rust's current name both work. Good usability call — the WASI alias is widely-known; the wasip1 form is what Rust prints internally. - **No new dependencies.** Reuses rustup's bundled tooling rather than adding a crate. Lower attack surface, no upstream coordination cost. - **The Cargo feature is named `wasm`, not `wasm32-wasi`.** Future-proofs against Tier 2 (V8 isolates / `wasm32-unknown-unknown`) adding bullets to the feature without renaming. - **The wasm path comment honestly acknowledges the native build.rs limitation.** "The wasm archive is a *primary* build of plg-runtime, so its uplifted top-level name is reliable (unlike the native deps/ case above)" — directly references the PR #15 mtime story I've been tracking. Useful continuity for whoever inherits this file. Issues below — none are blockers. ### Real concerns **1. The toolchain discovery reaches into rustup's internal layout.** The PR description names this explicitly, and the error message recovers well, but it's worth one line of ROADMAP/wasm-target.md recording the strategy: "track rustup's `<sysroot>/lib/rustlib/<host>/bin/` layout; if it ever moves, switch to `llvm-config` or the wasi-sdk distribution." The layout has been stable for years and Rust is unlikely to change it without warning, but if it ever does, the breakage is silent ("file not found" → user thinks their install is broken). A line in the doc saves a future contributor (or your future self) the diagnostic time. Two small durability follow-ups inside `wasm_toolchain`: - `rustc -vV` then parsing `host:` is the host-triple discovery. `rustc --print host-tuple` (documented stable since ~1.85) does the same without string parsing. Marginal but more durable. - The wasi-libc check looks for `self_contained/libc.a` and reports "needs the `wasm32-wasip1` std" — fine when the target is unconfigured, but if `self_contained/` exists with a *different* missing file (e.g. partial install), the message is misleading. Adding a `self_contained.exists()` first-step check would distinguish "target not added" from "target partially broken." **2. The "constant stack on wasm" claim is structural, not exercised at depth.** The wasm-smoke runs three flat queries against `examples/deps.pl` — they prove the wire contract round-trips, but they don't exercise the musttail-as-return_call lowering at the recursion depths where it actually matters. PR #24's `call1_recursion_runs_in_constant_c_stack` runs 100k-deep under a 512KB native stack; an analogous `wasmtime run --wasm max-stack-size=…` invocation against a recursive Prolog program would close the structural-vs-measured gap. The just recipe could grow one more case: compile `count(0). count(N) :- N > 0, N1 is N - 1, call(count(N1)).`, run with a constrained engine stack, assert success at 100k. That's the test that earns the "constant stack on wasm" claim. ### Small observations **3. `--O3` on the CLI silently becomes `-O2` at the llc layer for wasm.** The source comment explains: "llc's -O3 buys little for our IR and -O2 is what the gate proved." Reasonable, but a user reading `--debug=false` and assuming "release optimization" doesn't know they're getting `-O2`. One line in `wasm-target.md` — "wasm builds top out at LLVM `-O2`" — closes it. **4. `--allow-undefined` defers symbol resolution to instantiation.** Standard wasm-ld pattern; correct for a WASI module. The trade-off is that a missing runtime import surfaces at `wasmtime run` time, not at link time. The wasm-smoke happens to catch this end-to-end because it *runs* the module, so the coverage is there — worth one comment in `link_wasm` noting "undefined imports survive link; the smoke test's actual run is what verifies them." **5. Tail-call feature requires a recent wasm engine.** wasmtime ≥ 0.40 or so, recent wasmer, recent V8 (for the eventual Tier 2 path). Engines without it will error at instantiation with a "tail-call" reference. The error class is fine (clear and actionable), but a one-line "needs a wasm engine with the tail-call proposal" in `wasm-target.md` would head off "why doesn't this run on old wasmtime?" questions. **6. Three hard-coded `Target::Native` literals in `main.rs`** (build path's `compile_files`, run path's `compile_files`, script-mode's `compile_files`). Two of those (run and script-mode) genuinely can't be wasm. The build path is the only place `target` is computed from the CLI. The explicit literal isn't wrong, but a `compile_files_native(..)` helper for the two paths that can't change would document the intent and shrink the call sites. Stylistic. **7. The `parse_target` function** returns `Err` for `Some("native")` to map to Native — but the CLI flag accepts `None | Some("native")` as native. That's consistent with "no flag = native, explicit `--target native` = native, anything else = error". Worth confirming the docs list `native` as an accepted value (the current error message says "supported: native, wasm32-wasi", so it does). Good — the CLI surface, the error message, and the docs all agree. ### What's good - **`musttail` from PRs #20/#24 carries through to wasm with zero IR changes.** This is the architectural payoff. The discipline of "every cross-predicate transfer is musttail" looked native-specific at the time but is exactly what makes wasm-target work. The PR description names this; it deserves naming again. - **Strictly opt-in feature with byte-for-byte preservation** of the default install. The boring case (a user `cargo install plgc`s for native work) is unaffected. The cost lands only on contributors / users who actively want wasm. This is how to add capability without taxing the default user. - **One-source-of-truth pattern for target switching.** `Target` enum + two switches in codegen (triple, entry name) + a parallel `link_wasm` function. No hidden conditionals scattered through unrelated files. If a Tier 2 target needs another switch (e.g. no main at all, exported functions only), the seam is in two known places. - **Build.rs panic with named recovery command.** `--features wasm` without the runtime archive panics with `just build-runtime-wasm` as the recovery. This is correct cargo idiom (build script failures are how cargo communicates configuration problems) and the error message is honest about cause and cure. - **Bidirectional naming acceptance** for wasm targets (`wasm32-wasi` and `wasm32-wasip1`). Avoids one of the most common "but I typed the right thing!" failure modes. - **The wasm-smoke recipe is exactly the right scope for Tier 1.** Local-only, three queries, byte-identical comparison, runs against `examples/deps.pl`. Catches "did the toolchain regress" on a contributor's machine without CI infrastructure investment. CI wiring as a follow-up (ROADMAP M10) is the right scope discipline. - **Docs name what's NOT in scope.** The "Edge / serverless (not yet)" section in `wasm-target.md` is honest about V8 isolates / `wasm32-unknown-unknown` being Tier 2. Future users searching for Cloudflare Workers find the doc and know where to look without filing an issue. - **Built-in toolchain checks happen at link time, not at compile time.** A user who runs `plgc check` or `plgc compile-to-ir` (the IR-only path) doesn't pay for missing wasm tools. Only the linker step requires them. This keeps the lint/check loop fast even on a wasm-enabled plgc. ### Suggested order #1's two sub-items (`rustc --print host-tuple` and a one-line strategy note in the docs) are small durability improvements and worth doing before merge. #2 (a deep-recursion case in `wasm-smoke`) is the one substantive add — it earns the "constant stack on wasm" claim in the same way PR #24 earned it natively. #3+ are observations.
⏺ All green — default clippy clean, no test failures, 24
All checks were successful
CI - Linux / CI - Linux x86_64 (pull_request) Successful in 1m34s
06bb64c1fd
binaries, extended wasm-smoke passing. Here's how I handled
  the review.

  Review items addressed

  Before-merge items (the review's #1 and #2):

  - #1a — rustup-layout strategy  Added a STRATEGY
  paragraph to wasm_toolchain's doc comment: it tracks
  <sysroot>/lib/rustlib/<host>/bin, and if that ever moves
  the fallback is llvm-config or wasi-sdk. Documented where
  the next contributor will hit it.
  - #1b — rustc --print host-tuple  Verified it exists on
  this toolchain (matches -vV host) and replaced the
  -vV/host: string parsing.
  - #1c — partial-install detection  wasm_toolchain now
  checks self_contained.exists() first ("target not added →
  rustup target add") separately from the missing-libc.a case
  ("partially installed → remove and re-add").
  - #2 — earn the constant-stack claim  The substantive
  one. wasm-smoke now compiles count(N) :- ...,
  call(count(N1)) and runs it 1,000,000-deep under a 1 MB
  wasm stack (-W max-wasm-stack=1048576), asserting true. —
  the wasm analog of #24's ulimit -s 512. (5M also passes; 1M
  is the recipe's fast, unambiguous case.) This measures the
  musttail → return_call lowering at depth, not just
  structurally.

  Observation items folded in (cheap, prevent future
  confusion):
  - #3  wasm-target.md notes wasm builds top out at LLVM
  -O2.
  - #4  Comment in link_wasm explaining --allow-undefined
  defers imports to instantiation — which is why the smoke
  test's run is what verifies them.
  - #5  wasm-target.md notes the engine needs the tail-call
  proposal (and that an old engine rejects the module with a
  clear error).

  Bonus: while writing the deep-recursion test I hit a real
  WASI gotcha — WASI doesn't inherit host env, so
  PLG_MAX_STEPS must be passed via wasmtime --env. Documented
  in the Tuning section (it'd otherwise bite anyone raising
  the step limit on wasm).

  Skipped, with reasons:
  - #6 (compile_files_native helper for the run/script call
  sites) — the review marked it stylistic and "#3+ are
  observations." The explicit plgc::Target::Native literal is
  self-documenting and the indirection adds little; I left
  it.
  - #7 (parse_target/native) — no action needed; the review
  confirmed the CLI surface, error message, and docs already
  agree (native is listed as accepted).

  Verification after the changes: build/fmt/clippy clean on
  both feature sets, full suite (24 binaries) green, and the
  extended wasm-smoke passes all four checks including the
  constant-stack proof. Git and the PR are yours — nothing
  pushed.
navicore deleted branch wasm-t1 2026-06-20 14:24:51 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog!25
No description provided.