diff --git a/crates/synth-synthesis/src/optimizer_bridge.rs b/crates/synth-synthesis/src/optimizer_bridge.rs index 44db78c..03311c0 100644 --- a/crates/synth-synthesis/src/optimizer_bridge.rs +++ b/crates/synth-synthesis/src/optimizer_bridge.rs @@ -1431,6 +1431,17 @@ impl OptimizerBridge { // Helper to get ARM reg from virtual reg. // Also checks spill slots — if a vreg was spilled, returns R12 (IP scratch). // Callers should also call `reload_spill` to emit the actual load instruction. + // + // PANICS if the vreg is neither mapped nor spilled. The previous behavior was + // a silent `Reg::R0` fallback, which produced miscompilation: a downstream + // instruction reading the "unknown" vreg would silently consume whatever + // R0 happens to hold (often a live caller param or memset's dest pointer). + // Issue #93 was exactly this — `wasm_to_ir` had no handler for + // `I64ExtendI32U`/`I64ExtendI32S`/`I32WrapI64`, so the IR they should have + // produced never got mapped to ARM regs, and downstream i64 shifts read R0 + // 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. let get_arm_reg = |vreg: &OptReg, map: &HashMap, spills: &HashMap| -> Reg { if let Some(&r) = map.get(&vreg.0) { @@ -1439,7 +1450,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 + ); } };