Skip to content

pasta: x86-64 MULX/ADX assembly field arithmetic behind x86_64-asm - #222

Open
ebfull wants to merge 4 commits into
mainfrom
pasta-x86-64-asm
Open

pasta: x86-64 MULX/ADX assembly field arithmetic behind x86_64-asm#222
ebfull wants to merge 4 commits into
mainfrom
pasta-x86-64-asm

Conversation

@ebfull

@ebfull ebfull commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Pulled out of #217 (per review there) so the orbits calibration can land without entangling the x86-64 field arithmetic with #218's in-flight squaring work. This is the x86_64-asm backend exactly as reviewed on that branch, rebased onto current main. It touches the same files as #218 (fields.rs, fields/fp.rs, fields/fq.rs, the pasta changelog), so whichever of the two lands second rebases — happy for that to be this one.

What this adds

MULX/ADCX/ADOX Montgomery multiplication plus a dedicated assembly squaring behind a new opt-in x86_64-asm feature: a transcription of the aarch64 backend's five-limb CIOS rounds with the same canonicity contract and bounds analysis. Requires BMI2+ADX (Broadwell / Zen or newer; documented to fault on older CPUs); no-op on other architectures. Measured on Skylake-X: mul 25.96 → 21.01 ns, square 21.94 → ~20.3 ns; end to end (within the #217 stack) +11–41% verifier and +2–21% prover. Two slower schedulings are pinned in the module docs so they are not retried: square-through-mul (2–5% behind the dedicated square) and interleaved-ADCX/ADOX reduction sweeps (~10% behind two short sequential sweeps).

Alongside it, a bench fix: the fp/fq square cell called the inherent always-portable square, which shadows Field::square — so it measured the portable path under every asm feature and had mis-sized one squaring decision per host before this series. It now calls the trait method, i.e. the cell measures whichever backend is compiled in (relevant to benchmarking #218's portable squaring too: portable is what it measures unless an asm feature is on).

Validation

  • Differential tests per field against the portable path: 100k random canonical pairs, 100k unreduced-lhs near-modulus stress, edge matrices, and debug canonicity panics.
  • Full pasta suite green after the rebase: 200/200 in release with multicore,orbits,x86_64-asm on an ADX/BMI2 host; rustfmt clean.
  • CI note included: the pasta --all-features lanes execute the assembly, so they require ADX/BMI2 runners (true of GitHub's current fleet).

🤖 Generated with Claude Code

ebfull and others added 4 commits August 26, 2026 16:53
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>
@ValarDragon

Copy link
Copy Markdown
Contributor

Failing to compile
image

@TalDerei

TalDerei commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

will review this and retro #217 for my own curiosity.

@TalDerei

TalDerei commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

retro reviewed #218 and #217. now that #218 has landed, we should be careful during rebase since it could replace the faster lazy chains on main with assembly squares. will benchmark, but hard to say what the factor improvement this assembly backend currently provides since this impl partially conflicts with #218.

@TalDerei

TalDerei commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

didn’t have access to my linux box, so installed linux/amd64 container in orbstack (docker env running rosetta-emulated x86 with BMI2 / ADX extensions, but learned that doesn’t work since rosetta path doesn’t expose/emulate the required BMI2/ADX instructions). so ran a hosted runner workflow (against rebased version of this branch) which has BMI2/ADX and found:

Screenshot 2026-08-27 at 1 24 23 PM

@TalDerei

TalDerei commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Low spec bug: the contract says no production caller passes an unreduced lhs, but PrimeField::from_repr multiplies arbitrary, possibly invalid 256-bit input by R2 before returning the validity bit; Fq does likewise. Current correctness survives because R2 satisfies the documented limb bound, but the caller census is false and that dependency is untested for x86.

posted with help from fable 5.

@TalDerei

Copy link
Copy Markdown
Contributor

@v12sec pls review

@v12-auditor

v12-auditor Bot commented Aug 28, 2026

Copy link
Copy Markdown

Note

Complete: Audit complete. V12 did not find any issues that need review.

Open the full results here.

Analyzed six files, diff 276a4bf...6512886.

@TalDerei TalDerei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left observations, optimistically after rebase and FF are resolved!

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.

3 participants