Skip to content

ChaCha20 matryoshka (server AVX-512 + browser wasm) + native NEON U32x16 + retire simd_crypto.rs#241

Merged
AdaWorldAPI merged 6 commits into
masterfrom
claude/medcare-ruff-codebook-handover-5ulx0i
Jul 12, 2026
Merged

ChaCha20 matryoshka (server AVX-512 + browser wasm) + native NEON U32x16 + retire simd_crypto.rs#241
AdaWorldAPI merged 6 commits into
masterfrom
claude/medcare-ruff-codebook-handover-5ulx0i

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Context

PR #240 merged the earlier arc (U32x16 rotate_left all 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 merged master, plus the wasm follow-up.

What this does

1. Native NEON U32x16 = [U32x4; 4] ARX lane (a5032919)

  • U32x4 gains bitxor (veorq_u32) + rotate_left (vshlq_u32 shift-or, n%32 guard); composed native U32x16 with Add/BitXor/rotate_left. The ChaCha20/BLAKE ARX lane is now native on every tier (AVX-512 / AVX2-scalar / wasm / NEON).
  • Also fixes the pre-existing aarch64 stable-compile breakage (no aarch64 CI had caught it): the missing u16x8 alias, and two nightly-only vdotq_s32 sites rewritten with stable widening NEON (also fixing a latent bug in the codebook gather). aarch64 lib is now cargo check-clean on stable.

2. ChaCha20 matryoshka + simd_crypto.rs RETIRED (83d2ca70, 37306f2f)

  • vendor/chacha20/ — fork of RustCrypto chacha20 0.9.1 (name/version kept for [patch]; own [workspace], excluded from the parent). Upstream cipher verbatim; the one delta is src/backends/ndarray_simd.rs — the transpose block16 (16 blocks ∥, word→16-lane vector, counter lane-index) over ndarray::simd::U32x16: pure +/^/rotate_left, no raw intrinsics, no unsafe (all of that lives once in ndarray::simd).
  • The same source drives both hardware lanes, compile-time-selected under any(all(x86_64, avx512f), all(wasm32, simd128)): the AVX-512 __m512i lane (server) and the native [U32x4;4] lane (browser). aarch64/other fork builds never pull ndarray (they use RustCrypto's own backends). Folded transitively under chacha20poly1305 → encryption via [patch.crates-io], with zero change to the AEAD.
  • Retired: deleted src/simd_crypto.rs + its parity test + the chacha20 dev-dep; removed the ndarray::simd::{chacha20_block, chacha20_keystream, chacha20_state} surface (zero downstream consumers). RustCrypto owns the cipher; ndarray owns only the U32x16 lane.

3. NEON cross-parity CI + wasm compile-guard (7781028a, 37306f2f)

  • crates/neon-simd-parity (excluded bin, real ndarray::simd aarch64 types) + scripts/neon-parity.sh (cross-build aarch64 → run under qemu-aarch64-static) + CI neon_simd job (neon-simd/parity-qemu), added to the conclusion gate — closes NEON's runtime blind spot, the twin of wasm_simd.
  • A wasm compile-guard step added to wasm_simd builds the fork for wasm32+simd128.

Verification (all green)

  • Fork 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 / wrong-key) green on both the default v3 build (RustCrypto avx2 fallback) and the avx512 build (ndarray_simd).
  • neon-simd-parity runtime-verified under qemu-aarch64: U32x16 ARX (rotate 16/12/8/7 + edges) / F32x16 / I8x16 bit-identical to scalar.
  • Fork builds for wasm32-unknown-unknown +simd128 (ndarray-for-wasm + the wasm backend); wasm U32x16 per-op parity is node-proven by wasm_simd.

Notes / follow-up

  • The workspace default is target-cpu=x86-64-v3 (AVX2), so ndarray_simd activates only on an avx512 build; the default path uses RustCrypto's own (vetted) avx2 backend. The CLAUDE.md "v4 mandatory" line is stale.
  • A benign "Patch chacha20 was not used in the crate graph" note appears on scoped jobs (e.g. -p ndarray) that don't pull encryption.
  • Remaining follow-up (documented in .claude/CHACHA20_MATRYOSHKA_PLAN.md): cross-repo [patch] for MedCare-rs so its avx512 encryption build inherits the acceleration ([patch] doesn't transit repos).

🤖 Generated with Claude Code


Generated by Claude Code

claude added 4 commits July 12, 2026 05:55
…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
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@AdaWorldAPI, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 876f8b80-2e2b-47f1-9b32-f5255aae8d0e

📥 Commits

Reviewing files that changed from the base of the PR and between 729599e and 2539cad.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • .claude/CHACHA20_MATRYOSHKA_PLAN.md
  • .claude/blackboard.md
  • .github/workflows/ci.yaml
  • Cargo.toml
  • crates/encryption/src/aead.rs
  • crates/neon-simd-parity/.gitignore
  • crates/neon-simd-parity/Cargo.toml
  • crates/neon-simd-parity/src/main.rs
  • scripts/neon-parity.sh
  • src/lib.rs
  • src/palette_codec.rs
  • src/simd.rs
  • src/simd_crypto.rs
  • src/simd_neon.rs
  • tests/chacha20_rustcrypto_parity.rs
  • vendor/chacha20/.gitignore
  • vendor/chacha20/CHANGELOG.md
  • vendor/chacha20/Cargo.toml
  • vendor/chacha20/LICENSE-APACHE
  • vendor/chacha20/LICENSE-MIT
  • vendor/chacha20/README.md
  • vendor/chacha20/src/backends.rs
  • vendor/chacha20/src/backends/avx2.rs
  • vendor/chacha20/src/backends/ndarray_simd.rs
  • vendor/chacha20/src/backends/neon.rs
  • vendor/chacha20/src/backends/soft.rs
  • vendor/chacha20/src/backends/sse2.rs
  • vendor/chacha20/src/legacy.rs
  • vendor/chacha20/src/lib.rs
  • vendor/chacha20/src/xchacha.rs
  • vendor/chacha20/tests/data/chacha20-legacy.blb
  • vendor/chacha20/tests/data/chacha20.blb
  • vendor/chacha20/tests/mod.rs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Jul 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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".

Comment on lines +266 to +272
} 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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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
@AdaWorldAPI AdaWorldAPI merged commit 8611186 into master Jul 12, 2026
19 checks passed
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