Skip to content

feat: PIC package composition (level 2) — N packages, one memory, CompositionBuilder API#38

Merged
colinrozzi merged 1 commit into
mainfrom
pic-composition-proof
Jul 12, 2026
Merged

feat: PIC package composition (level 2) — N packages, one memory, CompositionBuilder API#38
colinrozzi merged 1 commit into
mainfrom
pic-composition-proof

Conversation

@colinrozzi

@colinrozzi colinrozzi commented Jul 12, 2026

Copy link
Copy Markdown
Owner

PIC package composition (level 2) — N packages, one memory, CompositionBuilder API

Package A imports and calls package B while sharing one linear memory (the level-2 spec, #37), on the 0.8.x PIC loader. Unlike the level-1 CompositionBuilder (separate memories, a cross-memory byte-copy bridge), a consumer's import is wired to a provider's export in the same memory — no copy.

The API

let mut comp = PicCompositionBuilder::new(&engine)
    .add_package("doubler", doubler_wasm)
    .add_package("adder", adder_wasm)
    .wire("adder", "math", "double", "doubler", "double")
    .build()?;
comp.call("adder", "process", &Value::S64(5))?; // → 11, adder called doubler in shared memory

How

  • src/runtime/pic_composition.rs: build() topologically instantiates each PIC package at a disjoint __memory_base + stack + table_base in one shared Memory, on one shared in-wasm allocator. Each cross-package import is wired via a thin shim that calls the provider's export in-memory and re-labels the ABI status (0→1) so the consumer's __import_impl frees the provider's result buffer (same shared allocator). call() frees its own buffers — no per-call leak.
  • packages/{doubler-pic,adder-pic}: PIC builds (originals stay non-PIC for the level-1 tests).

Tests

  • correctnessadder.process(5)=11 (and more) via the real PicCompositionBuilder.
  • no leak — 40 large (~256 KiB) cross-package returns through the 4 MiB shared heap; a leak would exhaust it in ~15 calls. Freed → all pass.
  • mechanism — the hand-rolled loader proof.

pic_composition 16/16; full workspace green; clippy + fmt clean; CI builds the two new fixtures.

Follow-up (level 3): static-merge the (now proven) PIC side modules into one passable .wasm.

🤖 Generated with Claude Code

…positionBuilder API

Package A imports and calls package B while SHARING one linear memory (per
docs/pic-composition.md level 2), on the 0.8.x PIC loader. Unlike the level-1
CompositionBuilder (separate memories, cross-memory byte-copy bridge), a
consumer's import is wired to a provider's export in the SAME memory — no copy.

- src/runtime/pic_composition.rs: PicCompositionBuilder / PicComposition. build()
  topologically instantiates each PIC package at a disjoint __memory_base + stack
  + table_base in one shared Memory on one shared in-wasm allocator; wires each
  cross-package import via a thin shim that calls the provider's export in-memory
  and re-labels the ABI status (0→1) so the consumer's __import_impl FREES the
  provider's result buffer. call() also frees its own buffers — no per-call leak.
- packages/{doubler-pic,adder-pic}: PIC builds (originals stay non-PIC for the
  level-1 tests); adder imports math.double + math.big from doubler.
- tests/pic_composition.rs:
  - pic_composition_builder_adder_calls_doubler — adder.process(5)=11 etc via the
    real PicCompositionBuilder API.
  - pic_composition_cross_package_returns_do_not_leak — 40 large (~256KiB) cross-
    package returns through the 4MiB shared heap; a leak would exhaust it in ~15.
  - pic_two_package_composition_adder_calls_doubler — the hand-rolled loader proof.

Follow-up (level 3): static-merge the proven PIC side modules into one .wasm.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@colinrozzi colinrozzi force-pushed the pic-composition-proof branch from 56b7917 to edfbcba Compare July 12, 2026 20:56
@colinrozzi colinrozzi changed the title feat: PIC shared-memory package composition — two packages, one memory (level-2 proof) feat: PIC package composition (level 2) — N packages, one memory, CompositionBuilder API Jul 12, 2026
@colinrozzi colinrozzi enabled auto-merge (squash) July 12, 2026 21:27
@colinrozzi colinrozzi merged commit 06d89c5 into main Jul 12, 2026
2 checks passed
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