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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions