ChaCha20 matryoshka (server AVX-512 + browser wasm) + native NEON U32x16 + retire simd_crypto.rs#241
Conversation
…ompile Native NEON U32x16 (the ChaCha20/BLAKE ARX lane), mirroring the wasm `[U32x4; 4]` shape so one ChaCha20 backend source compiles unchanged on every tier: - U32x4 gains `bitxor` (veorq_u32) + `rotate_left` (shift-or via the variable-count vshlq_u32; n%32 early-return). Rotate amount is a public ARX constant. - New U32x16([U32x4;4]) with Add / BitXor / rotate_left / splat / from_array / to_array, fanned over the 4 native lanes. - aarch64_simd F32x16::to_bits/from_bits switch to U32x16::from_array / to_array (native is [U32x4;4], not a [u32;16] tuple). - simd.rs aarch64 arm re-exports the native u32x16/U32x16 from simd_neon instead of the scalar fallback (drops them from the scalar list). Also fixes the pre-existing aarch64 *stable* compile breakage (the target never built on stable — no aarch64 CI caught it), a prerequisite for the cross-parity CI job: - Missing `pub type u16x8 = U16x8;` alias (simd.rs imported a symbol that did not exist). - `dot_i8x16_neon` / `codebook_gather_i8_dotprod` used the nightly-only `vdotq_s32` intrinsic (issue #117224). Rewritten with stable widening NEON (vmull_s8 + vpaddlq_s16 reduce; vmovl_s8 + vaddw_s16 accumulate) — bit-identical result, runs on all aarch64, no `dotprod` feature needed. Also fixes a latent bug in the gather (acc1..3 were never written, v1 aliased v0). Host ARX parity test green; aarch64 lib now `cargo check`-clean on stable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
…retire simd_crypto.rs
The AdaWorldAPI ChaCha20 "matryoshka": RustCrypto owns the cipher; ndarray owns
only the U32x16 ARX lane. The standalone from-scratch `src/simd_crypto.rs` (raw
_mm512_*/u32x4_* intrinsics) is RETIRED and superseded by a one-backend fork.
vendor/chacha20/ — fork of RustCrypto chacha20 0.9.1 (name/version kept for
[patch]; own [workspace] table, excluded from the parent workspace). Upstream
cipher verbatim (state schedule, counter, XChaCha, RNG, soft/sse2/avx2/neon).
The ONE delta:
src/backends/ndarray_simd.rs — the transpose block16 (16 blocks in parallel,
working vector w holds word w of every block across its 16 lanes, counter word
carries lane-index 0..=15) expressed over ndarray::simd::U32x16: every
quarter-round is a pure U32x16 + / ^ / rotate_left, NO cross-lane shuffle, NO
raw intrinsics, NO unsafe (all of that lives once inside ndarray::simd). Wrapped
in RustCrypto's StreamBackend (ParBlocksSize = U16), generic over R.
Selected at compile time under cfg(all(target_arch="x86_64", target_feature=
"avx512f")) via four cfg branches in backends.rs + lib.rs (Tokens / new / dispatch).
The ndarray dep is target.'cfg(target_arch="x86_64")' + default-features=false,
features=["std"] so wasm/aarch64 fork builds never pull ndarray.
[patch.crates-io] chacha20 = { path = "vendor/chacha20" } folds it transitively
under chacha20poly1305 -> encryption with zero change to the AEAD.
Parity — triple-gated, GREEN:
- the fork's own RustCrypto RFC 8439 vectors (chacha20/xchacha20 encryption +
keystream, core, seek) pass THROUGH ndarray_simd under -Ctarget-cpu=x86-64-v4;
- cargo test -p encryption (23 AEAD tests incl. XChaCha20-Poly1305 round-trip,
bit-flip-fails, wrong-key-fails) passes on the default v3 build (RustCrypto
avx2 fallback) and the avx512 build (ndarray_simd).
Retired: deleted src/simd_crypto.rs + tests/chacha20_rustcrypto_parity.rs + the
chacha20="0.9" dev-dep; removed the ndarray::simd::{chacha20_block,
chacha20_keystream, chacha20_state} surface (zero downstream consumers). aead.rs
doc rewritten to the transitive-acceleration framing.
Note: the workspace default is target-cpu=x86-64-v3 (avx2), so ndarray_simd
activates only on an avx512 build; the wasm-fork backend + cross-repo [patch]
propagation (MedCare-rs) are documented follow-ups in
.claude/CHACHA20_MATRYOSHKA_PLAN.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
The x86 `cargo test` suite never compiles `simd_neon.rs` (cfg'd to aarch64), so the native NEON SIMD lanes — including the new `U32x16` ARX lane the ChaCha20 backend rides — had no automated runtime verification. This closes that blind spot, mirroring the `wasm_simd`/node gate. - crates/neon-simd-parity — excluded bin that calls the REAL `ndarray::simd` aarch64 types (no copy → no drift) and self-checks each lane against scalar: U32x16 ARX (Add / BitXor / rotate_left over 0,1,7,8,12,16,24,31), F32x16 (splat / roundtrip / add / reduce_sum), I8x16 (roundtrip / add). Exit code points at the exact lane+op on mismatch. - scripts/neon-parity.sh — cross-build for aarch64-unknown-linux-gnu (via aarch64-linux-gnu-gcc) then run under qemu-aarch64-static. Exercises both integration (ndarray compiles for aarch64 on stable) and parity. - .github/workflows/ci.yaml — new `neon_simd` (neon-simd/parity-qemu) job installing gcc-aarch64-linux-gnu + qemu-user-static; added to `conclusion`. Verified GREEN locally: cross-built + run under qemu-aarch64-static → "U32x16 / F32x16 / I8x16 lanes bit-identical to scalar". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
…16 source, browser lane
Completes the matryoshka's browser half. The fork's `ndarray_simd` backend cfg
gates widen from x86_64+avx512f to
`any(all(x86_64, avx512f), all(wasm32, simd128))` (backends.rs module decl +
lib.rs::process_with_backend dispatch), and the target-scoped ndarray dep to
`cfg(any(target_arch="x86_64", target_arch="wasm32"))`.
The SAME src/backends/ndarray_simd.rs source now drives BOTH hardware lanes —
the AVX-512 `__m512i` lane on a server build and the native `[U32x4; 4]` lane on
a wasm32+simd128 browser build — because the backend only ever uses `U32x16`
ops, and ndarray's polyfill lowers `U32x16` to the right lane per target. aarch64
and other fork builds still never pull ndarray (they use RustCrypto's backends).
Verified:
- `cargo build --manifest-path vendor/chacha20/Cargo.toml --target
wasm32-unknown-unknown --lib` with `-Ctarget-feature=+simd128` compiles
ndarray-for-wasm (std, minimal features) + the wasm backend clean;
- x86 avx512 RustCrypto vectors still 9/9 green through ndarray_simd after the
cfg widening.
Bit-exact by composition: the ndarray_simd source is RFC-8439-proven on x86, and
the wasm U32x16 [U32x4;4] lane is node-proven by the existing wasm_simd CI job.
CI: added a compile-guard step to the wasm_simd job (build the fork for
wasm32+simd128) so a future ndarray::simd change that broke the wasm backend
fails there.
A consumer building `encryption` as a wasm cdylib with +simd128 now gets the
accelerated XChaCha20-Poly1305 keystream transparently — the browser goal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
|
Warning Review limit reached
Next review available in: 35 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (33)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4b0430fd-0872-4621-8e08-d8ff4364fa4a) |
…arch64 under -D warnings) The new `neon_simd` CI job builds ndarray for aarch64 with the workflow-global `RUSTFLAGS: -D warnings`. `bytemuck_cast_u64_to_u8`'s only caller is `unpack_4bit_avx2` (`#[cfg(target_arch = "x86_64")]`), so on aarch64 the function is dead code → `-D dead-code` build error. Gate the function with the same `#[cfg(target_arch = "x86_64")]` as its caller. x86_64 is unchanged (both compile and the function is used); reproduced green locally with `RUSTFLAGS="-D warnings" cargo build --target aarch64-unknown-linux-gnu`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37306f2fb0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } else if #[cfg(any( | ||
| all(target_arch = "x86_64", target_feature = "avx512f"), | ||
| all(target_arch = "wasm32", target_feature = "simd128"), | ||
| ))] { | ||
| // AdaWorldAPI matryoshka: keystream via ndarray::simd::U32x16 | ||
| // (AVX-512 lane on x86_64, native [U32x4; 4] lane on wasm32). | ||
| f.call(&mut backends::ndarray_simd::Backend(self)); |
There was a problem hiding this comment.
Honor x86 force flags before SIMD auto-selection
When building an AVX-512 target with RUSTFLAGS='--cfg chacha20_force_avx2' or --cfg chacha20_force_sse2, this branch matches before the x86 force-flag branch below, so process_with_backend still calls the new ndarray_simd backend even though the crate docs advertise those cfgs as forcing AVX2/SSE2. That makes A/B benchmarks or a rollback from the new backend impossible on x86-64-v4 builds; check the force cfgs before the AVX-512/wasm auto-selection and mirror the ordering in backends.rs.
Useful? React with 👍 / 👎.
…md auto-selection Addresses codex P2. The ndarray_simd branch was ordered before the x86 force-flag subtree, so on an x86-64-v4 build with `--cfg chacha20_force_avx2` (or `_sse2`) it matched first and silently ignored the force cfg — making A/B benchmarks and a rollback off the new backend impossible. Gate the x86_64 disjunct of the ndarray_simd auto-selection on `not(chacha20_force_avx2)` + `not(chacha20_force_sse2)` at all four cfg sites (backends.rs module decl, lib.rs Tokens type / new() tokens / process_with_backend) so a force cfg falls through to the existing AVX2/SSE2 subtree. The wasm32 disjunct stays unguarded — those cfgs are documented as ignored on non-x86. Verified under -Ctarget-cpu=x86-64-v4: default (no force) → ndarray_simd, 9/9 RustCrypto vectors green; `--cfg chacha20_force_avx2` → avx2 backend, 9/9 green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6pT32kk6pnuAAqR3JiYqu
Context
PR #240 merged the earlier arc (U32x16
rotate_leftall tiers, native wasm[U32x4;4], wasm parity CI) but not the matryoshka finalization — those commits landed on this branch after #240's merge point. This PR carries that unmerged work, rebased onto the mergedmaster, plus the wasm follow-up.What this does
1. Native NEON
U32x16 = [U32x4; 4]ARX lane (a5032919)U32x4gainsbitxor(veorq_u32) +rotate_left(vshlq_u32 shift-or,n%32guard); composed nativeU32x16withAdd/BitXor/rotate_left. The ChaCha20/BLAKE ARX lane is now native on every tier (AVX-512 / AVX2-scalar / wasm / NEON).u16x8alias, and two nightly-onlyvdotq_s32sites rewritten with stable widening NEON (also fixing a latent bug in the codebook gather). aarch64 lib is nowcargo check-clean on stable.2. ChaCha20 matryoshka +
simd_crypto.rsRETIRED (83d2ca70,37306f2f)vendor/chacha20/— fork of RustCryptochacha200.9.1 (name/version kept for[patch]; own[workspace], excluded from the parent). Upstream cipher verbatim; the one delta issrc/backends/ndarray_simd.rs— the transpose block16 (16 blocks ∥, word→16-lane vector, counter lane-index) overndarray::simd::U32x16: pure+/^/rotate_left, no raw intrinsics, nounsafe(all of that lives once inndarray::simd).any(all(x86_64, avx512f), all(wasm32, simd128)): the AVX-512__m512ilane (server) and the native[U32x4;4]lane (browser). aarch64/other fork builds never pull ndarray (they use RustCrypto's own backends). Folded transitively underchacha20poly1305 → encryptionvia[patch.crates-io], with zero change to the AEAD.src/simd_crypto.rs+ its parity test + thechacha20dev-dep; removed thendarray::simd::{chacha20_block, chacha20_keystream, chacha20_state}surface (zero downstream consumers). RustCrypto owns the cipher; ndarray owns only theU32x16lane.3. NEON cross-parity CI + wasm compile-guard (
7781028a,37306f2f)crates/neon-simd-parity(excluded bin, realndarray::simdaarch64 types) +scripts/neon-parity.sh(cross-build aarch64 → run underqemu-aarch64-static) + CIneon_simdjob (neon-simd/parity-qemu), added to theconclusiongate — closes NEON's runtime blind spot, the twin ofwasm_simd.wasm_simdbuilds the fork forwasm32+simd128.Verification (all green)
ndarray_simdunder-Ctarget-cpu=x86-64-v4.cargo test -p encryption(23 AEAD tests, incl. XChaCha20-Poly1305 round-trip / bit-flip / wrong-key) green on both the default v3 build (RustCrypto avx2 fallback) and the avx512 build (ndarray_simd).neon-simd-parityruntime-verified under qemu-aarch64: U32x16 ARX (rotate 16/12/8/7 + edges) / F32x16 / I8x16 bit-identical to scalar.wasm32-unknown-unknown+simd128 (ndarray-for-wasm + the wasm backend); wasmU32x16per-op parity is node-proven bywasm_simd.Notes / follow-up
target-cpu=x86-64-v3(AVX2), sondarray_simdactivates only on an avx512 build; the default path uses RustCrypto's own (vetted) avx2 backend. The CLAUDE.md "v4 mandatory" line is stale.chacha20was not used in the crate graph" note appears on scoped jobs (e.g.-p ndarray) that don't pullencryption..claude/CHACHA20_MATRYOSHKA_PLAN.md): cross-repo[patch]for MedCare-rs so its avx512encryptionbuild inherits the acceleration ([patch]doesn't transit repos).🤖 Generated with Claude Code
Generated by Claude Code