Measured while turning gale's thin-seam drivers into real components (gale REQ-DRV-COMPONENT-001). Reporting because it changes whether that migration is affordable at all on a 32 KB part.
The numbers — same driver, same toolchain (loom 1.2.0 + synth 0.52.0, cortex-m3, --all-exports --relocatable)
| shape |
.text |
raw #[no_mangle] exports, env.mmio_* imports (today) |
502 B |
component: import gust:hal/mmio, export gust:hal/gpio, canonical exports only |
1032 B |
| component, dual-exporting raw + canonical (transitional) |
1196 B |
dual-export after loom optimize --passes inline |
1202 B |
So componentizing roughly doubles a tiny driver, and loom does not reduce it.
What is in the extra bytes
$ arm-none-eabi-nm gpio.o | grep ' T '
T cabi_realloc
T cabi_realloc_wit_bindgen_0_52_0
T gust:hal/gpio@0.1.0#clear
T gust:hal/gpio@0.1.0#configure
T gust:hal/gpio@0.1.0#read
T gust:hal/gpio@0.1.0#set
T gust:hal/gpio@0.1.0#toggle
Two observations:
cabi_realloc is emitted twice and is unreachable. The whole interface is scalar — every function is func(u32, u32[, u32]) -> [u32]. Nothing is lifted or lowered that could allocate. Neither realloc can ever be called on this component, yet both survive --passes inline.
- The canonical wrappers roughly double the function bodies. Discounting the ~104 B of realloc, five canonical wrappers cost ~928 B against ~502 B for the same five functions exported raw.
Why this matters for the dissolve specifically
In a hosted runtime the canonical ABI is load-bearing. In this pipeline it is not: the component is fused and lowered ahead of time, and the native consumer calls the functions directly — nothing ever performs a canonical lift/lower at runtime. The glue is build-time scaffolding that survives into the shipped image.
The interface here is the cheapest possible case (all scalars). If it costs 2x, the cost on richer interfaces is worth knowing before eight drivers each pay it.
The ask
Is there a pass — or could there be — that recognises canonical-ABI export glue as dead once the component has been fused for ahead-of-time lowering, and drops it along with an unreachable cabi_realloc? Even just DCE'ing a provably-uncallable cabi_realloc on scalar-only interfaces would be a clear win.
Happy to provide the fixture: gpio-thin is a single public crate, five scalar functions, and both shapes are one cargo build apart. Same offer as the synth#882 fixtures, which turned out to catch more than was reported.
Repro
git clone https://github.com/pulseengine/gale
cd gale/benches/gust/drivers/gpio-thin # branch: feat/gpio-thin-component
cargo build --release --target wasm32-unknown-unknown
loom optimize target/wasm32-unknown-unknown/release/gust_gpio_thin.wasm --passes inline -o /tmp/g.wasm
synth compile /tmp/g.wasm --target cortex-m3 --all-exports --relocatable -o /tmp/g.o
arm-none-eabi-size /tmp/g.o
Measured while turning gale's thin-seam drivers into real components (gale REQ-DRV-COMPONENT-001). Reporting because it changes whether that migration is affordable at all on a 32 KB part.
The numbers — same driver, same toolchain (loom 1.2.0 + synth 0.52.0, cortex-m3,
--all-exports --relocatable).text#[no_mangle]exports,env.mmio_*imports (today)import gust:hal/mmio,export gust:hal/gpio, canonical exports onlyloom optimize --passes inlineSo componentizing roughly doubles a tiny driver, and loom does not reduce it.
What is in the extra bytes
Two observations:
cabi_reallocis emitted twice and is unreachable. The whole interface is scalar — every function isfunc(u32, u32[, u32]) -> [u32]. Nothing is lifted or lowered that could allocate. Neither realloc can ever be called on this component, yet both survive--passes inline.Why this matters for the dissolve specifically
In a hosted runtime the canonical ABI is load-bearing. In this pipeline it is not: the component is fused and lowered ahead of time, and the native consumer calls the functions directly — nothing ever performs a canonical lift/lower at runtime. The glue is build-time scaffolding that survives into the shipped image.
The interface here is the cheapest possible case (all scalars). If it costs 2x, the cost on richer interfaces is worth knowing before eight drivers each pay it.
The ask
Is there a pass — or could there be — that recognises canonical-ABI export glue as dead once the component has been fused for ahead-of-time lowering, and drops it along with an unreachable
cabi_realloc? Even just DCE'ing a provably-uncallablecabi_reallocon scalar-only interfaces would be a clear win.Happy to provide the fixture:
gpio-thinis a single public crate, five scalar functions, and both shapes are onecargo buildapart. Same offer as the synth#882 fixtures, which turned out to catch more than was reported.Repro