Repository navigation
AES-GCM x86-64: AES-NI+AVX encryption builds its first counters in registers - #1750
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
vg_aes_gcm_encrypt_blocks_aesni_pclmul_avx(the AES-NI+AVX interleaved loop,Impl.Gcm.X86_64.StitchAvx8.encFor) stored its first eight counter blocks and then read them straight back.initCounterwrites the templates atscratch + 640with 16-byte stores followed by 4-byte stores of each counter word, and the first batch loaded them with 16-byte loads. A load that overlaps two stores of different sizes cannot be forwarded from the store buffer. It waits until both stores reach the cache, which happens only after every older instruction retires. Those older instructions include the serial chain that computesH'²…H'⁸and, inseal, the multiplication byHof the additional data. So the first AES rounds could not start until all of that had finished.The first batch now builds its counters in registers (
regCounters). It byte-reverses the counter block thatinitCounterloaded intoxmm7, adds 1 to doubleword 0 withvpadddseven times, and byte-reverses each result into the batch's registers. This is the same scheme asvg_aes_ctr32_aesni'sctrs. The templates are still written as before, for the later batches, whose loads come long after their stores. The rest of the loop is unchanged.This is part of #1228, following the rustls analysis in #991, and targets the AES-NI+AVX backend's remaining gap in the 256 B–1 KiB range. Every
sealandencrypt_in_placeof 256 bytes or more on this backend goes through this loop. So doesAesGcm::encrypt, which copies the text and seals it in place.Measurements
Intel (local Xeon @ 2.10 GHz, pinned to one core). The AES-NI+AVX backend was selected with
VG_CPU_FEATURES=avx,avx2,avx512f,avx512bw,avx512vl,bmi1,bmi2,adx,aes,pclmulqdq,ssse3, and aws-lc-rs was restricted to the same features withOPENSSL_ia32cap='~0x0:~0x574220200000'. Each run used a 13-byte AAD and took the best of 11; there were two alternating runs per side,main→ this PR. "+1" isAesGcm::encryptofn + 1bytes from two slices.At 16 KiB the change is within noise (one fixed cost per call).
AMD (CI's
ubuntu-latest, AMD EPYC 9V45). Two Benchmarks runs (workflow_dispatch,VG_CPU_FEATURES=aes,avx,pclmulqdq,ssse3, moduleaes_gcm) compared againstmain(run 1, run 2). Both runs landed on AMD runners:aes-128-gcm-encrypt/384aes-128-gcm-encrypt/1024aes-128-gcm-encrypt-out-of-place/384aes-128-gcm-encrypt-out-of-place/1024aes-128-gcm-encrypt-two-pieces/384aes-128-gcm-encrypt-reused-key/600Unchanged code (decryption, AES-GCM-SIV) moved by up to ±3% in run 1 and up to 6% in run 2, so the second run is noisier.
Proofs
Assembly comes only from the Lean code. Spec, TCB and contracts are unchanged.
batchis nowbatchL (loads n).batchL ldtakes any codeldthat leaves the batch's counter blocks inaregs(CountersIn), changing onlyrax,xmm11,xmm12andaregs(LdFrame).cipherBatch_okandbatchL_okare stated for any suchld.batch_okandCoreInv.bareBatchare theloads_okinstances.regCounters_ok(AesHash.lean) proves the new code's counters from the counter block inxmm7, usingpaddd_oneandpshufb_rev_rev.Readygains the two facts it needs after the setup: the mask inxmm0and the counter block inxmm7, whichsetupPrefix_okalready established.Ct.lean) covers the new code unchanged: it touches only registers, and its one constant is public.block_counts_agree(from AES-GCM x86-64: AES-NI+AVX encryption loop takes every block from 16 on #1684) already compares every block count from 16 to 41 against the scalar baseline on every detected backend.🤖 Generated with Claude Code
https://claude.ai/code/session_01FoYWDDEDFq1DFev5X7n2aq
Generated by Claude Code