Skip to content

wasm_to_ir: unmapped vreg panic still trips on compiler_builtins (float::div) and gale_compute_ipi_mask after v0.2.1's #97 memset fix #120

@avrabe

Description

@avrabe

Context — synth's defensive panic from #101 is now catching new wasm_to_ir bugs

After v0.2.1 fixed the silicon-blocking memset miscompile (#97), the defensive panic on unmapped vreg instead of silent R0 fallback (#101) replaced silent miscompiles with explicit panics. Excellent change — it surfaces a class of bugs that would otherwise have produced broken binaries.

We're now hitting those panics on the same merged.wasm from the wasm-cross-LTO silicon-anchor experiment (pulseengine/gale@a4beaeb, branch feat/silicon-smart-data, PR #40, see benches/engine_control/silicon/boards/nucleo_g474re/NOTES-wasm-cross-lto-spike.md for full pipeline context).

What synth v0.3.0 does on the merged wasm

Reproducer (commands from /tmp/wasm-shim-poc, gale-ffi as wasm32 staticlib + a small shim, wasm-ld merged):

# rebuild gale-ffi as wasm32 staticlib (--no-default-features --features sem
# reduces the surface but doesn't avoid the panic — see below)
( cd /Volumes/Home/git/pulseengine/gale-smart-data/ffi && \
  cargo rustc --release --target wasm32-unknown-unknown \
    --crate-type=staticlib --no-default-features --features sem )

# wasm-ld merge with the shim (compiles its host of z_impl_k_sem_give)
clang --target=wasm32-unknown-unknown -O2 -nostdlib \
  -c wasm_host_shim.c -o shim.wasm.o
wasm-ld --no-entry --export-all --allow-undefined \
  --whole-archive .../libgale_ffi.a --no-whole-archive shim.wasm.o \
  -o merged-sem-only.wasm

synth compile merged-sem-only.wasm \
  --target cortex-m4f --all-exports --relocatable \
  -o merged.o

Observed panics, three different cases

Case 1 — gale-ffi with all features (full surface)

INFO Compiling function 'gale_compute_ipi_mask' via backend 'arm'...
thread 'main' panicked at crates/synth-synthesis/src/optimizer_bridge.rs:1617:21:
synth internal compiler error: vreg v47 has no assigned ARM register and no spill slot.
This is a wasm_to_ir bug — likely a wasm op whose result is unmapped (see issue #93).

Case 2 — gale-ffi with --features sem only (~30% fewer functions)

INFO Compiling function 'gale_sem_validate_v2' via backend 'arm'...   ← OK, 80 bytes
INFO Compiling function '_ZN17compiler_builtins5float3div3div17h3f008815e51f3f21E' via backend 'arm'...
thread 'main' panicked at crates/synth-synthesis/src/optimizer_bridge.rs:1617:21:
synth internal compiler error: vreg v721 has no assigned ARM register and no spill slot.

(I haven't isolated Case 3 yet but suspect there are more — the panic catches a class of bugs, not one specific function.)

What this is NOT

This is not the same as #93. #93 was about i64 codegen for memset's loop counter producing non-terminating code. #97 fixed that specific instance. The defensive panic added by #101 catches a broader class of "wasm op whose result is unmapped during ir lowering" — and there are evidently several different wasm-op patterns in compiler_builtins (float div, gale's cpu_mask helper, possibly others) that still trip it.

The message text in the panic references #93 because that's the class label, but each specific function panicking is a separate underlying lowering gap.

Recommended fix path

This connects directly to the existing strategy work in #74 (zero semantic tests verify compiled code) and #76 (validator-based i64/f32/f64 proof coverage). Concrete suggestions:

  1. Triage the panicking functions — collect the list of unique (panic location, wasm op pattern) tuples that the defensive panic from fix(opt): defensive panic on unmapped vreg instead of silent R0 fallback #101 has caught since landing. Each tuple is a distinct wasm_to_ir gap, even though they share an error message.

  2. Per-function regression tests — for each panicking function, capture the wasm bytes that trigger it and add a compiles_without_panic test. Trivial harness, catches future regressions cheaply.

  3. Validator coverage — the Adopt validator-based verification strategy (CompCert pattern) for i64/f32/f64 proof coverage #76 validator pattern would let synth emit a result-vreg-or-spill for every wasm op without needing per-op Rocq proofs, and verify the assignment post-codegen. That closes the underlying class without requiring synth-team to enumerate every wasm op.

Side note — affects safety claim chain

PulseEngine's positioning for safety-critical (ASIL-D / DO-178C) deployment leans on the meld→loom→synth chain being a verified-construction path. For gale silicon-anchor users today this means: rustc-direct compiles cleanly, LLVM-LTO compiles cleanly, but the verified-construction wasm chain panics. The defensive panic is the right behavior compared to silent miscompile, but it's a concrete blocker for "use synth instead of LLVM-LTO on real silicon."

Cross-references

Marking bug because the panicking input is valid wasm produced by rustc with default codegen — synth should compile it or, if it genuinely can't, refuse it with a more actionable diagnostic (e.g. listing the specific wasm opcode whose lowering is unimplemented).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions