Skip to content

chore(pasta): add fused x86-64 assembly lazy-squaring chains - #239

Draft
TalDerei wants to merge 6 commits into
mainfrom
pasta-x86-64-asm-rebased
Draft

chore(pasta): add fused x86-64 assembly lazy-squaring chains#239
TalDerei wants to merge 6 commits into
mainfrom
pasta-x86-64-asm-rebased

Conversation

@TalDerei

@TalDerei TalDerei commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Stacks on #222 which already contains single-operation x86 assembly square; the new candidate adds a separate fused assembly loop for whole lazy-squaring chain (the portable rust version was implemented in #218).

Makes squaring chains 0.4–4.1% faster and hash-to-curve 0.4–0.5% faster than #222.


Screenshot 2026-08-27 at 3 13 03 PM Screenshot 2026-08-27 at 3 14 29 PM

ebfull and others added 5 commits August 27, 2026 14:36
A transcription of the aarch64-asm backend's five-limb CIOS rounds into
one inline asm! block using MULX with dual ADCX/ADOX carry chains,
exploiting the shared Pasta modulus shape (p[2] = 0, p[3] = 2^62) so a
single routine serves Fp and Fq. Operand limbs are addressed through
readonly pointers rather than pinned registers (twelve pinned limbs plus
staging temporaries exceed x86-64's allocatable set); squaring routes
through the multiplication (measured on Apple silicon and Skylake-X, a
dedicated squaring block does not beat it). The sqr_n_mul fallback now
routes through the runtime dispatchers so chains square and multiply in
assembly on backends without a fused chain.

Measured on i9-7960X (Skylake-X): Fp mul 25.96 -> 21.01 ns (-19.8%),
Fq mul 20.46 ns; square unchanged. Differential tests: 100k random
canonical pairs per field against the portable path, 100k unreduced-lhs
near-modulus stress per field, edge matrices, and debug canonicity
panics; full 200-test suite green with multicore,orbits,x86_64-asm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fp/fq benches' square cell called the inherent (always-portable)
Fp::square, which shadows Field::square, the runtime-dispatched
production path — so the cell measured the portable squaring even under
the assembly features. That artifact mis-sized an assembly squaring
decision twice: it made the AArch64 inline squaring look no better than
portable (it is in fact 5-6% faster than the assembly multiplication;
a square-through-mul routing proposed on the bad reading was correctly
rejected by measurement on M4 Max), and it made the x86-64 backend's
mul-routed squaring look like a wash (with the fixed cell it measures
21.94 -> 20.97 ns, ~1.05x over portable). The cell now calls the trait
method explicitly; the x86_64_asm module docs and changelog now state
the corrected picture, with a dedicated x86-64 assembly squaring noted
as measured headroom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Transcribes the AArch64 backend's squaring into the x86-64 backend: the
512-bit square as cross products, one doubling pass, and the diagonals
(ten MULX against the multiplication's sixteen), four two-sweep
Montgomery cancellations on a rotating window (the consumed input
pointer's register is reclaimed as the carried fifth limb), the high
half folded in under the below-2p bound, and a CMOV conditional
subtraction. Measured 2-5% ahead of squaring through the multiplication
on Skylake-X (20.0-20.7 vs 21.0 ns across runs), mirroring the AArch64
backend's own square-over-mul margin.

Two slower schedulings are pinned in the module docs so they are not
retried on this microarchitecture family: squaring routed through the
multiplication, and a merged interleaved-ADCX/ADOX Montgomery reduction
(staged q*p operands, TEST-cleared dual chains) that measured ~10%
slower than the two short sequential sweeps (22.3 vs 20.2 ns) despite
the shorter nominal dependency length.

Differential tests (100k random canonical squares per field against the
portable path, plus edge matrices) and the full suite pass in release
and debug under multicore,orbits,x86_64-asm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Correct the x86_64_asm module's canonicity contract: only the
  canonical-rhs/canonical-input preconditions are debug-asserted; the
  unreduced-lhs limb bound is not (and cannot be), no current caller
  passes an unreduced lhs, and the differential tests pin the allowance.
- Drop the stale "(squaring routes through it)" changelog parenthetical
  that contradicted the dedicated-squaring text in the same entry.
- Note in sqr_n_mul_runtime's doc that only the AArch64 backend fuses the
  chain, and note in CI that the pasta --all-features lanes execute the
  x86-64 assembly and therefore require ADX/BMI2 runners.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@TalDerei TalDerei self-assigned this Aug 27, 2026
pasta: add fused x86 assembly lazy-squaring chains
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.

2 participants