fix(opt): f32/f64 ops no longer panic the optimized lowering path (#120)#126
Merged
Conversation
…ct selection (#120) OptimizerBridge::wasm_to_ir had zero handlers for scalar f32/f64 ops — every float op fell through the catch-all `_ => Opcode::Nop`. A value-producing float op (e.g. f32.div) that emits Nop produces no vreg, so any downstream consumer references an unmapped vreg and trips the PR #101 defensive get_arm_reg panic in ir_to_arm. This is the #93 silent-drop class, for floats — the customer-reported crash on compiler_builtins float::div and gale_compute_ipi_mask. The IR Opcode enum (synth-opt) has no float opcodes at all, so the optimized path cannot lower floats without a large feature addition. Fix (Option A): optimize_full now detects scalar f32/f64 ops up front and returns a typed Err(Error::UnsupportedInstruction). The ARM backend's compile_wasm_to_arm catches that Err and falls back to the non-optimized InstructionSelector::select_with_stack path, which already handles f32 via VFP/FPU. Honest degradation: float-containing functions still compile correctly, just without IR-level optimization. The defensive panic at get_arm_reg is intentionally kept — the fix makes float ops never reach it, rather than removing the bug-finder. f64 remains unsupported in both paths; the optimized path now declines it cleanly instead of panicking. Tests: - crates/synth-synthesis/tests/regression_issue_120_f32_optimized.rs: exercises optimize_full + ir_to_arm (the path that panicked) for f32.div and assorted f32/f64 ops; asserts a clean Err, not a panic. - arm_backend.rs: end-to-end backend tests compiling f32 div/add/mul/sub on Cortex-M4F via the default (optimized) config, asserting non-empty machine code; plus a no-FPU rejection test. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…imized-path # Conflicts: # crates/synth-synthesis/src/optimizer_bridge.rs
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #120. The optimized lowering path (
wasm_to_ir→ir_to_arm) panicked with an unmapped-vreg error on any module containing scalar f32/f64 ops. Real customer bug — surfaced on thefalcon-rate-componentmodule and oncompiler_builtins float::div/gale_compute_ipi_mask, not a fuzz artifact.Root cause
OptimizerBridge::wasm_to_irhas zero match arms for f32/f64 ops — every float op falls through the_ => Opcode::Nopcatch-all. Independently confirmed: the IROpcodeenum (synth-opt) has no float opcodes at all. A value-producing float op emittingNopproduces no vreg, so a downstream consumer references an unmapped vreg and trips the PR #101 defensiveget_arm_regpanic inir_to_arm. Same #93 silent-drop class — for floats.The non-optimized selector (
select_with_stack) does handle f32 via VFP/FPU. Only the optimized path lacked it.Fix — Option A: decline + fall back
The IR has no float opcodes, so adding them to the optimized path (Option B) would be a large VFP-lowering feature. Instead:
optimizer_bridge.rs: newis_unsupported_float_opclassifier (all scalar f32/f64 arithmetic, comparison, math, conversion, load/store).optimize_fullscans its input and returnsErr(Error::UnsupportedInstruction(...))referencing 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 if any float op is present.arm_backend.rs:compile_wasm_to_armnow catches anErrfromoptimize_fulland falls back to direct selection (select_with_stack), which handles f32. The ISA feature gate still runs — f32 on a no-FPU target still fails cleanly.The optimized path becomes an opt-in fast path, not a correctness dependency: when it can't handle a module, the more-capable non-optimized selector takes over.
Tests
regression_issue_120_f32_optimized.rs(new, 5 tests):optimize_full+ir_to_armon f32.div+store, f32.div+return, assorted f32 ops, f64 ops — all assert cleanErr, never panic; plus an i32 guard that the optimized path still works.arm_backend.rs(3 new tests): end-to-endBackend::compile_functionfor f32 div/add/mul/sub on Cortex-M4F via the default optimized config (the exact config that panicked pre-fix) — asserts non-empty machine code; plus a no-FPU rejection test.Validation:
cargo test --workspace --exclude synth-verify0 failures; clippy clean; fmt clean.Notes for review
select_with_stackdoesn't implement f64 either — an f64 module now fails with a clean ISA/selection error instead of panicking, but still won't compile. Adding f64 is separate scope.optimizer_bridge.rspanic now take the typed-error fallback path.get_arm_regpanic is untouched — the fix routes float ops away from it, doesn't remove the guard.Test plan
🤖 Generated with Claude Code