Skip to content

aarch64 backend completeness: execution-verified capability map + remaining gaps (memory, non-param locals, calls, if/loop/return, div/rem, popcnt) #851

Description

@avrabe

Summary

Following the milestone-4 float work (#790) and #842 void-block control flow, here is an execution-verified capability map of the aarch64 backend on synth 0.50.0 — what works, and the remaining gaps — to help scope the next milestones. Everything below was compiled with -b aarch64 and run natively on an arm64 host (MAP_JIT), diffed bit-exact against wasmtime: 36 ops, 95 checks, 0 miscompiles.

✅ Works (native-execution verified vs wasmtime)

  • i32 ALU: add, sub, mul, and, or, xor, shl, shr_s, shr_u, rotl, rotr, clz, ctz, eqz (shift masking, rotate edges, clz/ctz(0) all correct)
  • i32 compares: all 10 (eq/ne/lt_s/lt_u/gt_s/gt_u/le_s/le_u/ge_s/ge_u), incl. INT_MIN/INT_MAX boundary
  • i64 ALU: add, sub, mul, and, or, xor, shl, shr_s, rotl (64-bit results)
  • f32: add/sub/mul/div, min, max, copysign, sqrt, trunc/trunc_sat/convert — NaN propagation + signed zero correct (min(+0,-0)=-0, min(NaN,1)=NaN — scalar fmin/fmax, not fminnm)
  • f64: arith + min/max/sqrt work natively (scalar fmin/fmax, NaN-correct)
  • control: block / br / br_if (feat(aarch64): void-block control flow — block/br/br_if (#538) #842)

🚧 Not yet supported (the ask — roughly in blocker order for general code)

These currently honestly decline (no silent miscompile — good), but block real programs:

  1. Memory: i32.load/i32.store (and sub-word/i64 loads) — declined. Biggest blocker for any non-trivial module.
  2. Non-param locals: local declarations beyond params — declined (local.get {i}: non-param locals not yet supported). Blocks almost all real functions.
  3. Calls: call / call_indirect — declined.
  4. Control flow beyond block/br: if/else, loop, return — declined (only block/br/br_if today, so no early-exit or loops).
  5. Integer div/rem: i32/i64 div_s/div_u/rem_s/rem_u — declined.
  6. popcnt: i32/i64.popcnt — declined (aarch64 has CNT on the vector side; needs the fmov/cnt/addv sequence).
  7. f64↔i64 reinterpret: i64.reinterpret_f64 / f64.reinterpret_i64 — declined (GI-FPU-001), so f64 bit-round-tripping isn't available.

Suggested priority

For "run a real WASM module on aarch64," the gating four are memory + non-param locals + calls + full control flow (if/loop/return) — the compute/ALU/float core is already solid and verified. div/rem/popcnt are smaller, self-contained adds.

Verification harness

A standing execution matrix (synth_aarch64_matrix.sh in our campaign harness) re-runs all 95 native checks each pass and prints the frontier, so each newly-lowered op is execution-verified automatically and any regression is caught. Happy to share it if useful.

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