Why (grounded, cross-repo chain)
The generated guest cabi_realloc (cabi_realloc_wit_bindgen_0_58_0 → crate::rt::cabi_realloc, crates/guest-rust/src/rt/) is backed by the global growing allocator, so any component built with these bindings emits memory.grow (the canonical-ABI realloc + Vec/String lifting/lowering paths). That single opcode blocks the embedded lowering pipeline downstream:
- meld#299 —
meld fuse --memory shared --address-rebase (single-address-space MCU mode) correctly fail-louds on memory.grow (SR-37: a frozen shared static layout can't survive a runtime grow). So a component with one memory.grow cannot be MCU-lowered.
- gale#89 —
gale-app-demo / gale-kiln each carry exactly one memory.grow "from the default Rust/wit-bindgen wasip2 allocator"; the multi-memory fallback isn't MCU-lowerable (2 × 1088 KB; synth rejects). This blocks the jess → Pixhawk 6X-RT drone pipeline.
So the cleanest place to kill the problem is at the source: let wit-bindgen-generated components link a bounded, no-grow allocator so they never emit memory.grow.
Proposal
A binding option (feature/flag, e.g. cabi_realloc = "static-arena" or a no_std+bounded mode) that backs cabi_realloc and the lifting/lowering temporaries with a fixed static arena sized at build time, failing loud (trap) on exhaustion instead of calling memory.grow. Generated components then satisfy meld's --memory shared --address-rebase precondition directly, no post-processing.
This complements #1 (per-item heap alloc in StreamReader::next/StreamWriter::write_one): both are about making the generated code's allocation behaviour deterministic and bounded for real-time / no_std / MCU targets — the PulseEngine embedded line of work on this fork.
Honest scope notes
- This is opt-in; it must not change default (host/cloud) codegen.
- "Bounded arena + trap-on-exhaustion" is the sound analogue of meld#299 Option 1 (bounded static heap) — doing it in the binding generator means the component is born MCU-lowerable rather than rewritten later.
- Pairs with the gale#89 "components must be no_std/no-grow" contract: this is the wit-bindgen capability that lets a component meet that contract without hand-rolling an allocator.
Refs: meld#299, gale#89, gale#63 (hosting), and #1 (real-time stream alloc) on this fork.
Why (grounded, cross-repo chain)
The generated guest
cabi_realloc(cabi_realloc_wit_bindgen_0_58_0→crate::rt::cabi_realloc,crates/guest-rust/src/rt/) is backed by the global growing allocator, so any component built with these bindings emitsmemory.grow(the canonical-ABI realloc +Vec/Stringlifting/lowering paths). That single opcode blocks the embedded lowering pipeline downstream:meld fuse --memory shared --address-rebase(single-address-space MCU mode) correctly fail-louds onmemory.grow(SR-37: a frozen shared static layout can't survive a runtime grow). So a component with onememory.growcannot be MCU-lowered.gale-app-demo/gale-kilneach carry exactly onememory.grow"from the default Rust/wit-bindgen wasip2 allocator"; the multi-memory fallback isn't MCU-lowerable (2 × 1088 KB; synth rejects). This blocks the jess → Pixhawk 6X-RT drone pipeline.So the cleanest place to kill the problem is at the source: let wit-bindgen-generated components link a bounded, no-grow allocator so they never emit
memory.grow.Proposal
A binding option (feature/flag, e.g.
cabi_realloc = "static-arena"or ano_std+bounded mode) that backscabi_reallocand the lifting/lowering temporaries with a fixed static arena sized at build time, failing loud (trap) on exhaustion instead of callingmemory.grow. Generated components then satisfy meld's--memory shared --address-rebaseprecondition directly, no post-processing.This complements #1 (per-item heap alloc in
StreamReader::next/StreamWriter::write_one): both are about making the generated code's allocation behaviour deterministic and bounded for real-time /no_std/ MCU targets — the PulseEngine embedded line of work on this fork.Honest scope notes
Refs: meld#299, gale#89, gale#63 (hosting), and #1 (real-time stream alloc) on this fork.