Skip to content

A32 encoder silently NOPs i64 mul/shl/shr/rotl/rotr/compare/eqz (--target cortex-r5) — returns garbage instead of computing or rejecting #615

Description

@avrabe

Filed by the PulseEngine challenge harness (synth v0.30.1). Source-confirmed + A32 disassembly + Thumb contrast.

Defect: A32 encoder silently emits NOP for i64 mul / shifts / rotates / comparisons (--target cortex-r5)

On the ARM A32 path (IsaVariant::Arm32, reached via --target cortex-r5 / armv7r), a family of i64 ops encode to a literal NOP and return uninitialized garbage. validate_instructions gates only FPU/MVE, so there is no honest error — the wrong code just ships. (Distinct from #594, which was A32 call_indirect.)

Source — crates/synth-backend/src/arm_encoder.rs:

ArmOp::I64SetCond { .. } | ArmOp::I64SetCondZ { .. }
| ArmOp::I64Mul { .. } | ArmOp::I64Shl { .. }
| ArmOp::I64ShrS { .. } | ArmOp::I64ShrU { .. }
| ArmOp::I64Rotl { .. } | ArmOp::I64Rotr { .. } => 0xE1A00000, // NOP (Thumb-2 only)

So i64.mul, i64.shl, i64.shr_s, i64.shr_u, i64.rotl, i64.rotr, i64 comparisons (SetCond), and i64.eqz (SetCondZ) are dropped to NOP on A32. i64 div_s/rem_s also NOP (they build on these). i64 and/or/xor/add/sub ARE implemented on A32.

Evidence (A32 disasm of i64.mul, --no-optimize, cortex-r5)

<f>:  push {r4,r5,r6,r7,r8,lr}
      sub  sp, sp, #64
      mov  r0, r0          ; 0xE1A00000  <-- the multiply, as a NOP
      mov  r0, r4          ; return r4:r5 (never written -> uninitialized garbage)
      mov  r1, r5
      add  sp, sp, #64
      pop  {r4,...,pc}

No umull/mul anywhere. Same NOP body for i64.shl/shr_u/shr_s/div_s/eqz. Contrast: i64.and emits and r0,r0,r2; and r1,r1,r3 and i64.add emits adds; adc (correct), and i64.mul on the Thumb path emits real multiplies — so this is A32-encoder-specific, not "i64 unimplemented."

Repro

(module (func (export "m") (param i64 i64) (result i64) (i64.mul (local.get 0)(local.get 1))))
synth compile m.wat --target cortex-r5 -n m --relocatable --no-optimize -o m.o
# disasm shows mov r0,r0 (NOP) where the multiply belongs; qemu-A32 returns garbage; wasmtime m(3,5)=15.

Expected

Implement these i64 ops on the A32 encoder, or — per the #554/#594/#610 precedent — honestly reject them (the "Thumb-2 only" comment shows the limitation is known; it must not be a silent NOP). Silently NOPping i64 multiply/shift/compare on a shipping target produces wrong results with no diagnostic.

Note

Regression probe added: harness/synth_a32_i64_probe.sh (asserts these ops emit a real instruction or are rejected — never a NOP).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions