Skip to content

fix(decoder): SIMD (v128) ops loud-skip — category-level catch, no silent no-op drop (#680) - #691

Merged
avrabe merged 1 commit into
mainfrom
fix/680-v128-loud-skip
Jul 10, 2026
Merged

fix(decoder): SIMD (v128) ops loud-skip — category-level catch, no silent no-op drop (#680)#691
avrabe merged 1 commit into
mainfrom
fix/680-v128-loud-skip

Conversation

@avrabe

@avrabe avrabe commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #680.

Problem

On -t cortex-m3 (no SIMD hardware) WASM SIMD (v128) ops silently compiled to zero machine code: i32x4.add returned an operand passthrough (mov r0,r1) and v128.store left memory unwritten — wrong result shipped with rc=0. The #554 class (aarch64 f32 drop), here for v128 on the thumb backend.

Root cause: the decoder decodes a large v128 subset into WasmOp variants, but the only selector arms for them are gated on a has_helium flag nothing in the production pipeline sets (test-only MVE prototype) — so selection dropped them without a diagnostic. The GI-FPU-001 loud-skip (#371) only fires for ops that decode to None; the decoded-but-unselectable v128 ops sailed past it. Per-op-incomplete honesty is exactly how this slipped through (v128.any_true was loud, i32x4.add silent).

Fix — category-level, no wildcard drop survives

The diagnostic says "no SIMD lowering for this target" (not "SIMD invalid") — cortex-m55/MVE can legitimately lift this once the Helium lowering is actually wired to a target.

Oracles (red → green)

  • Red on main (7a9222d): both issue reproducers compile rc=0 silently (wrong result under QEMU vs wasmtime per thumb-2: SIMD (v128) ops silently dropped to no-ops on cortex-m3 (no SIMD hw) — wrong result / unwritten memory instead of honest rejection (#554 class) #680).
  • Green here: rc=1 with skipping function 'f': ... I32x4Splat: no SIMD lowering for this target — the op would be silently dropped to a no-op (WASM SIMD proposal, #680).
  • Non-vacuity: scalar functions in the same module still compile (mixed-module verified: vf skipped, sf emitted, ELF produced).
  • New decoder tests (red on main): SIMD-op flag on both issue reproducers, v128 local + op-free v128 signature flag, v128 global access flag with imported-index shift — scalar/i32 siblings asserted unflagged in every test.
  • Frozen anchors 10/10 byte-identical (frozen_codegen_bytes); workspace 109 test suites green; cargo fmt --check + chunked clippy --all-targets -- -D warnings clean across all crates.

Lineage: #554 (honest rejection over silent miscompile), #665 (intentionally-ignored class), #648 (typed-global/local precedent), #371/#556 (loud-skip / honest-bail machinery), #369 (GI-FPU-001).

🤖 Generated with Claude Code

…lent no-op drop on non-SIMD targets (#680)

On cortex-m3 (no SIMD hardware) WASM SIMD ops silently compiled to zero
machine code: i32x4.add returned an operand passthrough (mov r0,r1) and
v128.store left memory unwritten — the #554 class, for v128. Root cause:
the decoder DOES decode a large v128 subset into WasmOp variants, but the
only selector arms for them are gated on a `has_helium` flag that nothing
in the production pipeline ever sets (test-only prototype), so selection
dropped them without a diagnostic. The GI-FPU-001 (#371) loud-skip only
fires for ops that decode to None — the decoded-but-unselectable v128 ops
sailed past it (per-op-incomplete honesty, exactly how #680 slipped in).

Fix — category-level, no wildcard drop survives:

- `is_simd_operator`: macro-generated from wasmparser's own
  `for_each_operator!` proposal markers (`@simd` / `@relaxed_simd`), so it
  covers the ENTIRE SIMD operator space of the pinned wasmparser with no
  hand-kept op list. Any SIMD-proposal operator flags the function into
  the established loud-skip/honest-bail lane (#371 all-exports skip with a
  named reason, #556 single-function bail).
- v128-typed LOCALS and v128 params/results flag too — expressible with
  zero SIMD operators (`local.get 0` passthrough), lowered as 4-byte moves
  that silently truncate the 16-byte value (both decode entry points, like
  the #648 float-global fix).
- v128-typed GLOBAL accesses (imported + defined, full index space) flag —
  same lane as #648's f32/f64 globals: the `v128.const` initializer is
  never captured and an access moves 4 of the 16 bytes.

The reason string says "no SIMD lowering for this target" (not "SIMD
invalid") — cortex-m55/MVE can legitimately lift this once the Helium
lowering is actually wired to a target.

Oracles: red on main (issue reproducers compile rc=0, wrong result under
QEMU vs wasmtime) → green (rc=1, diagnostic naming the op + #680); scalar
functions in the same module still compile (non-vacuity, mixed-module
verified). New decoder tests red on main: SIMD-op flag (both issue
reproducers), v128 local + signature flag, v128 global access flag
(imported index shift), i32/scalar siblings unflagged. Frozen anchors
10/10 byte-identical; workspace 109 suites green; fmt + chunked clippy
-D warnings clean.

Refs #554, #665, #648, #371, #556, #369.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.26168% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-core/src/wasm_decoder.rs 96.26% 8 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 36c617a into main Jul 10, 2026
34 checks passed
@avrabe
avrabe deleted the fix/680-v128-loud-skip branch July 10, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant