Skip to content

Commit 050e077

Browse files
avrabeclaude
andauthored
fix(#369): unsupported decoder ops loud-skip, never silently drop (GI-FPU-001) (#371)
* fix(#369): unsupported decoder ops loud-skip the function, never silently drop (GI-FPU-001) The decoder's `convert_operator` returns None for any op it can't lower, and the caller silently dropped it — leaving the operand stack wrong and the function a SILENT miscompile (scalar f32.add -> `mov r0,r1`, returning an operand instead of the sum; same `_ => None` line drops memory.copy). Per the #180/#185 contract, an unsupported op must surface loudly, never silently continue. - wasm_decoder.rs: FunctionOps gains `unsupported: Option<String>`; decode_function_body records the first value-affecting op that decodes to None (everything except the intentional Nop/Unreachable, via is_intentionally_ignored). - main.rs compile loop: a flagged function is LOUD-SKIPPED (warning naming the op + symbol absent -> caller gets a link error), reusing the #168 skipped_funcs path, before selection. An all-skipped module errors loudly rather than emitting nothing. General fix: covers scalar f32/f64 (#369), bulk-memory (memory.copy/fill), and any other decoder-dropped value-affecting op. Honest interim until real VFP (GI-FPU-002) and bulk-memory lowering land. Verified: f32/f64 module now loud-skips (was silent `mov r0,r1`); all three frozen oracles byte-identical (control_step 0x00210A55 13/13, flight_seam 0x07FDF307, div_const 338/338 — no fixture uses floats); unit test + fmt + clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(release): v0.11.46 — #369 GI-FPU-001 unsupported-op loud-skip Pin sweep 0.11.45 -> 0.11.46 (workspace.package + 10 crate path-deps + MODULE.bazel + Cargo.lock). CHANGELOG v0.11.46 with falsification statement. rivet: GI-FPU-001 -> implemented + GI-FPU-VER-001 (verifies, sw-verification: unit test + frozen byte-identity). Frozen oracles re-confirmed on the bumped tree (control_step ORACLE PASS). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c6beaf7 commit 050e077

16 files changed

Lines changed: 191 additions & 50 deletions

File tree

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.11.46] - 2026-06-17
11+
12+
**CORRECTNESS — #369: an op the backend cannot lower now LOUD-SKIPS its
13+
function instead of being silently dropped into a wrong-value miscompile.**
14+
15+
- **#369 / GI-FPU-001 — unsupported decoder ops no longer silently drop**
16+
(#371): `convert_operator` returns `None` for any operator it can't lower, and
17+
the caller previously **dropped it silently** — leaving the operand stack wrong
18+
and the function a silent miscompile (`f32.add(a,b)` lowered to `mov r0,r1`,
19+
returning an operand instead of the sum; the same `_ => None` line drops
20+
`memory.copy`). Per the #180/#185 contract, an unsupported op must surface
21+
loudly. `FunctionOps` now carries `unsupported: Option<String>`;
22+
`decode_function_body` records the first value-affecting op that decodes to
23+
`None` (everything except the intentional `Nop`/`Unreachable`); and the
24+
compile loop **loud-skips** a flagged function (warning naming the op + symbol
25+
absent → a caller gets a link error), reusing the #168 `skipped_funcs` path. An
26+
all-skipped module errors loudly rather than emitting an empty object. Covers
27+
scalar f32/f64 (#369, jess AFD-024/AFD-008), bulk-memory (`memory.copy`/
28+
`fill`), and any other decoder-dropped value-affecting op — the honest interim
29+
until real VFP (GI-FPU-002) and bulk-memory lowering land.
30+
31+
**Falsification:** a module whose only function uses a scalar `f32`/`f64` op
32+
(or `memory.copy`) no longer produces an object containing that function — it
33+
is reported skipped and absent. A pure-integer module is byte-identical to
34+
before: the three frozen oracles stay bit-identical (control_step `0x00210A55`
35+
13/13, flight_seam `0x07FDF307`, div_const 338/338); no fixture uses floats.
36+
1037
## [0.11.45] - 2026-06-14
1138

1239
**ON-TARGET SHIPPABILITY — gale #354: a high-offset init segment no longer

Cargo.lock

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ resolver = "2"
2727
# semver to publish, so the convention now catches up: workspace
2828
# version follows the release tag, bumped pre-tag in the release
2929
# checklist. See docs/release-process.md.
30-
version = "0.11.45"
30+
version = "0.11.46"
3131
edition = "2024"
3232
rust-version = "1.88"
3333
authors = ["PulseEngine Team"]

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module(
77
name = "synth",
88
# Kept in lockstep with [workspace.package] version in Cargo.toml.
99
# Both are bumped pre-tag — see docs/release-process.md.
10-
version = "0.11.45",
10+
version = "0.11.46",
1111
)
1212

1313
# Bazel dependencies

artifacts/gale-integration.yaml

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,9 +607,12 @@ artifacts:
607607
fixture uses floats (zero `f32.`/`f64.` in scripts/repro/*.wat), so
608608
every frozen differential stays byte-identical. Shippable independently
609609
of GI-FPU-002 (the hard-float feature); bundles naturally with the
610-
memory.copy-drop fix (same decoder catch-all).
611-
status: proposed
612-
tags: [gale, fpu, float, correctness, silent-miscompile, ok-or-err, release-pending]
610+
memory.copy-drop fix (same decoder catch-all). IMPLEMENTED in v0.11.46
611+
(#371): FunctionOps.unsupported flag + decode_function_body recording +
612+
compile-loop loud-skip; f32/f64 module loud-skips (was silent mov r0,r1);
613+
3 frozen oracles byte-identical; unit test; covers memory.copy too.
614+
status: implemented
615+
tags: [gale, fpu, float, correctness, silent-miscompile, ok-or-err, release-v0.11.46]
613616
links:
614617
- type: derives-from
615618
target: GI-002
@@ -668,3 +671,27 @@ artifacts:
668671
re-test; cortex-m7 (sp) and cortex-m7dp (dp) outputs DIFFER (FPU mode
669672
consulted); all frozen fixtures stay byte-identical (no float content,
670673
unaffected); soft-float retained and unchanged for cortex-m3.
674+
675+
- id: GI-FPU-VER-001
676+
type: sw-verification
677+
title: "Unsupported-op loud-skip (#369) — unit test + frozen byte-identity"
678+
description: >
679+
Verifies GI-FPU-001 (v0.11.46, #371). Unit test
680+
`test_369_scalar_float_op_flags_function_unsupported_not_dropped`
681+
(crates/synth-core/src/wasm_decoder.rs) asserts a scalar `f32.add` flags
682+
its function `unsupported` (naming `F32Add`) while a pure-integer function
683+
stays clean. Manual: an f32/f64 module compiled for cortex-m7dp loud-skips
684+
both functions (symbol absent; warning names the op) where it previously
685+
emitted the silent `mov r0,r1` stub. Frozen byte-identity confirmed: the
686+
three differential oracles (control_step 0x00210A55 13/13, flight_seam
687+
0x07FDF307, div_const 338/338) PASS unchanged (no fixture uses floats).
688+
status: implemented
689+
tags: [gale, fpu, verification, unit-test, frozen, release-v0.11.46]
690+
links:
691+
- type: verifies
692+
target: GI-FPU-001
693+
fields:
694+
method: test
695+
pass-criteria: >
696+
cargo test test_369 green; f32 module emits no fadd/dmul symbol;
697+
control_step / flight_seam / div_const differentials byte-identical.

crates/synth-backend-awsm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ categories.workspace = true
1111
description = "aWsm backend integration for the Synth compiler"
1212

1313
[dependencies]
14-
synth-core = { path = "../synth-core", version = "0.11.45" }
14+
synth-core = { path = "../synth-core", version = "0.11.46" }
1515
anyhow.workspace = true
1616
thiserror.workspace = true

crates/synth-backend-riscv/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ categories.workspace = true
1111
description = "RISC-V encoder, ELF builder, PMP allocator, and bare-metal startup for synth"
1212

1313
[dependencies]
14-
synth-core = { path = "../synth-core", version = "0.11.45" }
15-
synth-synthesis = { path = "../synth-synthesis", version = "0.11.45" }
14+
synth-core = { path = "../synth-core", version = "0.11.46" }
15+
synth-synthesis = { path = "../synth-synthesis", version = "0.11.46" }
1616
anyhow.workspace = true
1717
thiserror.workspace = true
1818
tracing.workspace = true

crates/synth-backend-wasker/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ categories.workspace = true
1111
description = "Wasker backend integration for the Synth compiler"
1212

1313
[dependencies]
14-
synth-core = { path = "../synth-core", version = "0.11.45" }
14+
synth-core = { path = "../synth-core", version = "0.11.46" }
1515
anyhow.workspace = true
1616
thiserror.workspace = true

crates/synth-backend/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ default = ["arm-cortex-m"]
1515
arm-cortex-m = ["synth-synthesis"]
1616

1717
[dependencies]
18-
synth-core = { path = "../synth-core", version = "0.11.45" }
19-
synth-synthesis = { path = "../synth-synthesis", version = "0.11.45", optional = true }
18+
synth-core = { path = "../synth-core", version = "0.11.46" }
19+
synth-synthesis = { path = "../synth-synthesis", version = "0.11.46", optional = true }
2020
anyhow.workspace = true
2121
thiserror.workspace = true

crates/synth-cli/Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ verify = ["synth-verify"]
2727
# Path deps carry `version` so `cargo publish` rewrites them to the
2828
# crates.io coordinate. Bumping the workspace version requires
2929
# updating these in lockstep — see docs/release-process.md.
30-
synth-core = { path = "../synth-core", version = "0.11.45" }
31-
synth-frontend = { path = "../synth-frontend", version = "0.11.45" }
32-
synth-synthesis = { path = "../synth-synthesis", version = "0.11.45" }
33-
synth-backend = { path = "../synth-backend", version = "0.11.45" }
30+
synth-core = { path = "../synth-core", version = "0.11.46" }
31+
synth-frontend = { path = "../synth-frontend", version = "0.11.46" }
32+
synth-synthesis = { path = "../synth-synthesis", version = "0.11.46" }
33+
synth-backend = { path = "../synth-backend", version = "0.11.46" }
3434

3535
# Optional external backends
36-
synth-backend-awsm = { path = "../synth-backend-awsm", version = "0.11.45", optional = true }
37-
synth-backend-wasker = { path = "../synth-backend-wasker", version = "0.11.45", optional = true }
38-
synth-backend-riscv = { path = "../synth-backend-riscv", version = "0.11.45", optional = true }
36+
synth-backend-awsm = { path = "../synth-backend-awsm", version = "0.11.46", optional = true }
37+
synth-backend-wasker = { path = "../synth-backend-wasker", version = "0.11.46", optional = true }
38+
synth-backend-riscv = { path = "../synth-backend-riscv", version = "0.11.46", optional = true }
3939

4040
# Optional verification (requires z3)
41-
synth-verify = { path = "../synth-verify", version = "0.11.45", optional = true, features = ["z3-solver", "arm"] }
41+
synth-verify = { path = "../synth-verify", version = "0.11.46", optional = true, features = ["z3-solver", "arm"] }
4242

4343
# Optional PulseEngine WASM optimizer
4444
# Uncomment when loom crate is available:

0 commit comments

Comments
 (0)