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).
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_instructionsgates 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:So
i64.mul,i64.shl,i64.shr_s,i64.shr_u,i64.rotl,i64.rotr, i64 comparisons (SetCond), andi64.eqz(SetCondZ) are dropped to NOP on A32. i64div_s/rem_salso NOP (they build on these). i64and/or/xor/add/subARE implemented on A32.Evidence (A32 disasm of
i64.mul,--no-optimize, cortex-r5)No
umull/mulanywhere. Same NOP body fori64.shl/shr_u/shr_s/div_s/eqz. Contrast:i64.andemitsand r0,r0,r2; and r1,r1,r3andi64.addemitsadds; adc(correct), andi64.mulon the Thumb path emits real multiplies — so this is A32-encoder-specific, not "i64 unimplemented."Repro
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).