Skip to content

feat(aarch64): milestone 3 — scalar floating point (#538) - #787

Merged
avrabe merged 4 commits into
mainfrom
feat/46-aarch64-m3
Jul 16, 2026
Merged

avrabe merged 4 commits into
mainfrom
feat/46-aarch64-m3

Conversation

@avrabe

@avrabe avrabe commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

AArch64 backend milestone 3 — scalar floating point (#538)

v0.46 Wave-2 Lane 4. Extends the host-native A64 backend (crates/synth-backend-aarch64) from the integer-only m2 subset to scalar floating point — the separate V/D/S register file.

Float op coverage: 0 → 35

m2 loud-declined every float op. m3 lands the high-value, total (non-trapping) scalar-float surface:

Group Ops
f32 arith add sub mul div abs neg
f32 compare eq ne lt le gt ge
f32 const f32.const (materialize + FMOV)
f64 arith add sub mul div abs neg sqrt
f64 compare eq ne lt le gt ge
f64 const f64.const
precision f64.promote_f32 f32.demote_f64
int→float f32/f64.convert_i32_s f32/f64.convert_i32_u
reinterpret f32.reinterpret_i32 i32.reinterpret_f32

Register-file separation

A64 floats live in V0–V31, an ABI counter independent of the GP arg registers. The value stack now tags each entry with its file (Gp/Fp); select_typed threads the per-param f32/f64 masks so (param i32 f32 i32) correctly resolves to w0, s0, w1. A GP op fed an FP operand errors (type-confusion guard) — never reads the wrong file.

Soundness — honest decline matrix

Still loud-declined (no symbol emitted, honest error — never silent-wrong):

Verification

  • clang-cross-verified encodings: every FP base (FADD/FSUB/FMUL/FDIV, FABS/FNEG/FSQRT, FCMP + NaN-correct mi/ls/gt/ge conds, the FMOV GP↔FP bridges, FCVT S↔D, SCVTF/UCVTF) checked against clang -target aarch64-linux-gnu ground truth (fp_* encoder tests).
  • Native + unicorn differential (aarch64_m3_floats_538_differential.py): each function runs under unicorn (UC_ARCH_ARM64, FPEN on) AND natively on the arm64 host (MAP_JIT), diffed bit-exactly vs wasmtime — 513/513 cases, including NaN/±0/±inf inputs and mixed int/float param assignment.
  • 36 backend unit tests green; m2 differential still 182/182 (no regression from the value-stack refactor); workspace clippy/fmt clean.

Cover targets only. References #538 (milestone 3). Do not merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 4 commits July 16, 2026 20:10
Add the A64 scalar-float instruction encodings (V/D/S register file):
FADD/FSUB/FMUL/FDIV, FABS/FNEG/FSQRT (S+D), FCMP + the NaN-correct Mi
condition, the FMOV GP<->FP bridges, FCVT S<->D, and SCVTF/UCVTF int->float.
Every base is clang-cross-verified (fp_* tests). The trapping float->int
truncations (FCVTZS/FCVTZU) are deliberately absent — A64 saturates where
WASM traps (#709 class), so they stay loud-declined at the selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…APCS64 (#538)

Lower the high-value scalar-float ops: f32/f64 const, add/sub/mul/div,
abs/neg/sqrt, the full compare family (NaN-correct conds), f32<->f64
promote/demote, i32->float convert (SCVTF/UCVTF), and the f32<->i32
reinterprets (FMOV). The value stack now tags each entry with its register
FILE (GP vs FP); float params resolve to their AAPCS64 V registers via an
independent counter (select_typed threads the f32/f64 param masks). A GP op
fed an FP operand ERRORS (type-confusion guard), never reads the wrong file.

Trapping float->int truncations (FCVTZS/FCVTZU saturate, #709) and
min/max/copysign/rounding stay loud-declined. 36 backend tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
aarch64_m3_floats_538_differential.py runs each emitted float function two
ways — under unicorn (UC_ARCH_ARM64, FPEN enabled) AND natively on the arm64
host (MAP_JIT mmap + CFUNCTYPE) — and diffs BOTH bit-exactly against wasmtime,
including NaN / +-0 / +-inf inputs (compare NaN-ordering) and mixed int/float
AAPCS64 param assignment. 513/513 cases matched (native + unicorn).

The trapping float->int truncations are absent by design (loud-declined,
#709); their decline is asserted in the backend unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…upports

The m2 decline-matrix oracle + the #554 loudskip test asserted f32.add/f64.mul
LOUD-DECLINE, but m3 (#787) implemented them → CI RED (correctly). Move both gates
to floats that DELIBERATELY stay declined: i32.trunc_f32_s (A64 FCVTZS saturates
where WASM traps — #709 soundness) + f32.min (NaN-propagation differs). The honesty
gate stays meaningful; the now-supported floats are exercised by the m3 differential.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.82969% with 42 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend-aarch64/src/selector.rs 86.95% 42 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit 2d13c21 into main Jul 16, 2026
39 checks passed
@avrabe
avrabe deleted the feat/46-aarch64-m3 branch July 16, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant