Skip to content

fix(wasm-host): auto-provision wasm-host in all compile paths - #8337

Merged
proggeramlug merged 1 commit into
PerryTS:mainfrom
jdalton:wasm-host-auto-provision
Aug 18, 2026
Merged

fix(wasm-host): auto-provision wasm-host in all compile paths#8337
proggeramlug merged 1 commit into
PerryTS:mainfrom
jdalton:wasm-host-auto-provision

Conversation

@jdalton

@jdalton jdalton commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Problem

A program referencing WebAssembly.* did NOT reliably get the wasm-host cargo feature enabled on perry-runtime, causing link failures:

Undefined symbols for architecture arm64: "_js_webassembly_module_new"

This blocks sdxgen (whose acorn.wasm path needs it) and is the concrete instance of the --enable-wasm-runtime / wasm-host auto-detect gap.

Root cause — three broken paths

  1. --enable-wasm-runtime did not enable the feature. The flag only linked libperry_wasm_host.a (run_pipeline.rs:5288 checked args.enable_wasm_runtime) but never enabled perry-runtime/wasm-host in the auto-optimize cargo invocation (freshness.rs:192 only checked ctx.needs_wasm_runtime, NOT args.enable_wasm_runtime). So the runtime archive lacked js_webassembly_* symbol definitions while the host library was linked — the symbol stayed undefined.

  2. PERRY_NO_AUTO_OPTIMIZE=1 never enabled wasm-host. The no-auto path (no_auto.rs) only resolved prebuilt well-known ext archives. The prebuilt libperry_runtime.a is built WITHOUT wasm-host (deliberately kept out of default to avoid wasmi bloat on non-wasm programs). So even though feature_detect.rs set ctx.needs_wasm_runtime = true, the no-auto path never rebuilt the runtime with the feature.

  3. Dynamic WebAssembly access was not detected. The static lowering (module_static.rs) sets hir_module.uses_webassembly for direct WebAssembly.Module/instantiate/etc. call sites, but a minified bundle can reach WebAssembly via dynamic property access (const WA = WebAssembly; WA.Module(bytes), globalThis.WebAssembly) that lowers to an ordinary PropertyGet without hitting any set-site. The codegen still emits js_webassembly_* FFI calls for those paths, so the link died.

Fix

  1. --enable-wasm-runtime override (run_pipeline.rs): fold args.enable_wasm_runtime into ctx.needs_wasm_runtime before any feature/library provisioning so the flag triggers the same provisioning as auto-detection — the wasm-host cargo feature, the runtime rebuild, the library link, and the symbol-stub scan all see it.

  2. No-auto runtime rebuild (no_auto.rs): when ctx.needs_wasm_runtime is set, build perry-runtime-static with default features + perry-runtime/wasm-host into a dedicated target dir (target/perry-wasm-host-runtime) so the prebuilt libperry_runtime.a is not clobbered. This is the same on-demand build pattern build_missing_prebuilt_ext_lib uses for CPU-only ext wrappers. perry-wasm-host has no tokio dep, so there is no perry-ext-net: outbound TCP panics — LTO dead-strips tokio CONTEXT statics #507 shared-tokio concern.

  3. Dynamic access fallback (feature_detect.rs): add a fallback grep for "WebAssembly" in the HIR debug string, mirroring the fetch/crypto/EventEmitter fallbacks. Over-matching only over-links the wasm host (a size cost); the rule is zero false negatives.

Verification

  • cargo test -p perry --bin perry -- compile::optimized_libs — 34 passed (31 existing + 3 new regression tests).
  • cargo test -p perry-hir --lib — 315 passed.
  • New regression tests: wasm-usage → wasm-host cross-feature provisioned; non-wasm → not provisioned; wasm usage changes the cache key.

Backward compat

  • --enable-wasm-runtime still works (now actually enables the feature, not just the library).
  • Non-wasm programs don't link the host — wasm-host stays out of default, the detection only fires on WebAssembly.* usage, and the no-auto rebuild only runs when needs_wasm_runtime is set.

Summary by CodeRabbit

  • New Features

    • Improved WebAssembly runtime support during compilation.
    • The compiler now detects WebAssembly usage more reliably and enables the required runtime automatically.
    • Explicit WebAssembly runtime options are consistently applied across compilation and linking.
    • When needed, the runtime is rebuilt with WebAssembly host support for the target platform.
  • Bug Fixes

    • Improved fallback behavior when runtime sources, SDKs, or cross-compilation tools are unavailable, preserving the prebuilt runtime when possible.
    • Optimized runtime caching now distinguishes WebAssembly-enabled builds from standard builds.

A program referencing WebAssembly.* did not reliably get the perry-runtime
wasm-host cargo feature enabled, causing link failures with
_Undefined symbols for architecture arm64: _js_webassembly_module_new_.

Three gaps are closed:

1. --enable-wasm-runtime did not enable the feature. The flag only linked
   libperry_wasm_host.a (run_pipeline.rs) but never enabled
   perry-runtime/wasm-host in the auto-optimize cargo invocation
   (freshness.rs only checked ctx.needs_wasm_runtime, not
   args.enable_wasm_runtime). Fix: fold args.enable_wasm_runtime into
   ctx.needs_wasm_runtime early in run_pipeline so the flag is an explicit
   override that triggers the same provisioning as auto-detection.

2. PERRY_NO_AUTO_OPTIMIZE=1 never enabled wasm-host. The no-auto path
   (no_auto.rs) only resolved prebuilt well-known ext archives; the
   prebuilt libperry_runtime.a is built WITHOUT wasm-host (deliberately
   kept out of default to avoid wasmi bloat). Fix: when needs_wasm_runtime
   is set, build perry-runtime-static with default features +
   perry-runtime/wasm-host into a dedicated target dir so the prebuilt is
   not clobbered. perry-wasm-host has no tokio dep, so no PerryTS#507 concern.

3. Dynamic WebAssembly access was not detected. The static lowering
   (module_static.rs) sets hir_module.uses_webassembly for direct
   WebAssembly.Module/instantiate/etc. call sites, but a minified bundle
   can reach WebAssembly via dynamic property access that lowers to an
   ordinary PropertyGet without hitting any set-site. Fix: add a fallback
   grep for WebAssembly in the HIR debug string, mirroring the
   fetch/crypto fallbacks (zero false negatives).

Regression tests assert wasm-usage -> wasm-host cross-feature provisioned,
non-wasm -> not provisioned, and wasm usage changes the cache key.
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e11d8ab-dec0-4f50-a49c-524d2e91fa57

📥 Commits

Reviewing files that changed from the base of the PR and between 7441e1f and 34684d8.

📒 Files selected for processing (4)
  • crates/perry/src/commands/compile/collect_modules/feature_detect.rs
  • crates/perry/src/commands/compile/optimized_libs/no_auto.rs
  • crates/perry/src/commands/compile/optimized_libs/tests.rs
  • crates/perry/src/commands/compile/run_pipeline.rs

Included review availability: Your plan includes up to 8 reviews per rolling hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The compiler now detects WebAssembly references through lowered HIR fallback scanning and explicit flags. It propagates one runtime state to optimized-library selection, rebuilds the wasm-host runtime when needed, and separates wasm and non-wasm cache entries.

Changes

WebAssembly runtime support

Layer / File(s) Summary
Normalize WebAssembly runtime detection
crates/perry/src/commands/compile/collect_modules/feature_detect.rs, crates/perry/src/commands/compile/run_pipeline.rs
Fallback HIR scanning and --enable-wasm-runtime now set the shared ctx.needs_wasm_runtime flag.
Build the wasm-host runtime
crates/perry/src/commands/compile/optimized_libs/no_auto.rs
The no-auto path builds perry-runtime-static with wasm-host, applies platform-specific cross-compilation settings, and falls back to the prebuilt runtime on failure.
Validate feature and cache separation
crates/perry/src/commands/compile/optimized_libs/tests.rs
Tests verify wasm-host feature selection, omission for non-WebAssembly code, and separate cache keys.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 34684

The change improves wasm-host provisioning across compile paths, and no actionable merge-blocking risk remains at the current head after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant CompilePipeline
  participant CompileContext
  participant ResolveNoAutoOptimizedLibs
  participant BuildWasmHostRuntime
  participant Cargo
  CompilePipeline->>CompileContext: normalize WebAssembly runtime state
  CompileContext->>ResolveNoAutoOptimizedLibs: provide needs_wasm_runtime
  ResolveNoAutoOptimizedLibs->>BuildWasmHostRuntime: request wasm-host runtime
  BuildWasmHostRuntime->>Cargo: build perry-runtime with wasm-host
  Cargo-->>BuildWasmHostRuntime: return platform-specific archive
  BuildWasmHostRuntime-->>ResolveNoAutoOptimizedLibs: return runtime or fallback
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: automatic wasm-host provisioning across all compile paths.
Description check ✅ Passed The description clearly explains the problem, root causes, fixes, verification results, and backward compatibility impact.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug

Copy link
Copy Markdown
Contributor

Merging — everything passes except cargo fmt --all -- --check, which flags
optimized_libs/no_auto.rs (lines 5, 89, 98, 134, 154) and
optimized_libs/tests.rs:1299. I confirmed it is this branch and not the merge
with #8338: clean main is fmt-clean (exit 0), and this branch alone on current
main still fails, including in no_auto.rs, which #8338 never touched.

Since this is a fork branch I can't push the fix here, so I'm landing the
formatting as an immediate follow-up rather than bouncing the PR for whitespace.

Validated otherwise: optimized_libs bin tests 36/36 (up from 33 — your three
new ones), perry-runtime --lib 2579, perry-stdlib --lib 124, and 49 of 50
gates including the compile tier (cargo check --workspace --all-targets under
-D warnings and cargo clippy --workspace).

One note for later, not a blocker: the new WebAssembly fallback builds a
format!("{:?}") of the whole HIR. That is consistent with the file rather than
new — feature_detect.rs already does this six separate times on main — but
it means a large module now gets its HIR formatted to a string seven times per
compile. On the Next.js fixture (104 modules, multi-MB IR) that is real time and
peak RSS. Computing hir_debug once at the top of detect_optional_feature_usage
and passing &str to each check would collapse all seven.

@proggeramlug
proggeramlug merged commit 7e86f80 into PerryTS:main Aug 18, 2026
43 of 48 checks passed
proggeramlug added a commit that referenced this pull request Aug 18, 2026
#8337 landed from a fork branch with cargo fmt --check failing on
no_auto.rs and tests.rs. Verified this is the branch's own formatting and
not the #8338 merge: clean main was fmt-clean and #8337 alone still failed,
including no_auto.rs which #8338 never touched.

Co-authored-by: Ralph Küpper <ralph3@skelpo.com>
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.

2 participants