Skip to content

arm backend silently skips functions on a wasm-tools-valid module — component validator reports false value-stack underflow (Select/LocalSet) #329

@avrabe

Description

@avrabe

Summary

synth compile --cortex-m --all-exports silently skips two functions of the falcon flight core because synth's component validator reports a wasm value-stack underflow — on a module that wasm-tools validate accepts as fully valid. The skipped functions are absent from the ELF (an on-target completeness gap, distinct from the regalloc skips tracked in #242 and the call_indirect dispatch gap in #275).

Reproduction (deterministic, from public assets)

# falcon-flight-v1.56.wasm — relay release asset, sha256 3fb275db…a41341
loom optimize falcon-flight-v1.56.wasm -o f.loom.wasm        # loom v1.1.13
meld fuse f.loom.wasm -o f.fused.wasm                        # meld v0.30.0 (also repro's on v0.29.0)
wasm-tools validate f.fused.wasm                             # => VALID
synth compile f.fused.wasm --cortex-m --all-exports -o out.elf

Observed on both synth v0.11.39 and v0.11.40 (so not version-introduced):

warning: skipping function 'func_30': ... Component validation failed:
  wasm value-stack underflow at op 21 (LocalSet(2)): would pop 1 from depth 0
warning: skipping function 'func_39': ... Component validation failed:
  wasm value-stack underflow at op 2 (Select): would pop 3 from depth 2

Committed artifact for byte-exact repro: repro/synth-underflow/falcon-v1.56.fused.wasm in pulseengine/jess (sha256 bd946fb5…29713).

Analysis

The module is spec-valid (wasm-tools agrees), so this is synth's internal abstract-stack model under-counting, not a malformed input:

  • func_39, op 2 = Select: a select pops 3 (two operands + i32 condition), but synth's model believes depth is only 2 at op 2 — i.e. the two operands feeding the select aren't both on synth's tracked stack that early. Suggests block/control-flow results (or function params) not seeded onto the abstract stack.
  • func_30, op 21 = LocalSet(2): pops 1, synth thinks depth 0 — same family of under-count, here after ~20 ops.

Both failures are very early in the function, which points at stack initialization / block-result accounting rather than a deep-nesting edge. Possibly related to the (closed) #72 explicit-value-stack work, but that landed and this still reproduces.

Impact for jess

2 functions of the verified flight core are silently dropped from the Cortex-M ELF. Combined with #275 (dispatch) and the #242 regalloc skips, it's part of the on-target completeness picture jess tracks (recorded as AFD-017). Non-blocking for the wasm/kiln path (whole module present there); blocking for a complete self-contained firmware.

Suggested fix

Seed synth's abstract value stack from the function signature (params) and block/loop result types before instruction walk, and verify the Select lowering accounts for all three pops. A targeted fixture from func_39 (the Select case) would lock it. Happy to extract a minimal reproducer from the committed core if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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