From 534735aeb7e5a86a5f95a1b177e716cfcc8599cd Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Mon, 11 May 2026 00:42:30 +0200 Subject: [PATCH] fix(opt): defensive panic on unmapped vreg instead of silent R0 fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to issue #93 (silicon-blocking memset bug). The root cause was optimizer_bridge::wasm_to_ir silently dropping three wasm ops (I64ExtendI32U/S, I32WrapI64) — they produced vregs that never got mapped to ARM registers. The downstream lookup in get_arm_reg returned Reg::R0 as a silent fallback, so subsequent i64 shifts read R0 as rm_lo/rm_hi, destroying memset's destination pointer on real silicon. Replace the silent fallback with a panic that names the vreg. Future "wasm op silently dropped" bugs of this class will surface at the boundary instead of producing miscompiled firmware. A compiler crash is strictly better than a hung MCU. Verified: * cargo test --workspace — all pass (no path legitimately hits the fallback — the post-#97 codebase produces mappings for every wasm op it emits IR for) * cargo clippy --workspace --all-targets -- -D warnings — clean * cargo fmt --check — clean Co-Authored-By: Claude Opus 4.7 (1M context) --- .../synth-synthesis/src/optimizer_bridge.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/synth-synthesis/src/optimizer_bridge.rs b/crates/synth-synthesis/src/optimizer_bridge.rs index d232a83..304923a 100644 --- a/crates/synth-synthesis/src/optimizer_bridge.rs +++ b/crates/synth-synthesis/src/optimizer_bridge.rs @@ -1600,12 +1600,12 @@ impl OptimizerBridge { // as their `rm_lo`/`rm_hi`, destroying the loop counter on real silicon. // A loud panic here is strictly better than a quiet miscompilation — // crash the compiler, not the firmware. - // Note: the silent R0 fallback is intentionally preserved here. PR #101 - // holds the defensive panic version of this helper; it will land once - // every wasm_to_ir gap is closed. The previously-known `v13` case - // during `fib` compilation was fixed in this PR by adding a `Call` - // opcode and handler — see `Opcode::Call` below and `WasmOp::Call` in - // `wasm_to_ir`. + // History: PR #108 (47-site AAPCS audit) deferred this panic, leaving a + // silent R0 fallback while one last v13 case in fib compilation was + // tracked down. PR #109 fixed that case (WasmOp::Call had no wasm_to_ir + // handler, falling through to Opcode::Nop). With #109 stacked under + // this PR, every known wasm_to_ir gap is closed and this panic is safe + // to ship as a permanent guard against the class. let get_arm_reg = |vreg: &OptReg, map: &HashMap, spills: &HashMap| -> Reg { if let Some(&r) = map.get(&vreg.0) { @@ -1614,7 +1614,12 @@ impl OptimizerBridge { // Will be reloaded into R12 by reload_spill Reg::R12 } else { - Reg::R0 + panic!( + "synth internal compiler error: vreg v{} 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).", + vreg.0 + ); } };