Repository navigation
PPC64LE AES-CTR: counters by vadduwm, one vperm per block, and the tail by 4/2/1 - #4750
Merged
Merged
Conversation
Each block's counter took mtvsrd, vperm and addi on a counter word in r8. The counter block is now kept in counterBase, and each block takes a copy of it and a vadduwm by counterOne (one in the last word), which adds one to the last 32 bits modulo 2^32, as inc32 does. The data is XORed in lxvd2x's order after the store's vperm, so a block takes one vperm, not one for the data and one for the result. The counter block is stored back whole at the end. Proofs: ctrAdd (Value.lean) replaces the counter word in the batch and loop invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKUFHGBeNadVff4mCVhkSH
The data loops took the blocks left after the batches of eight one at a time, each a serial chain of rounds. They now take four, two and one by the bits of their number (tailStep), so at most three chains: ECB's tail and CTR's (GCM's short messages) gain. vg_aes_ctr32_to, which nothing calls since #4721, is deleted with its contract and proofs, and ctrWith with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKUFHGBeNadVff4mCVhkSH
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKUFHGBeNadVff4mCVhkSH
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Nov 8, 2026
Member
Author
|
The merge queue dropped this PR in run 42528625150, but the failure isn't this PR's. All of the run's test, build and Lean jobs passed; it was tested on top of #4751. The only failure is Generated by Claude Code |
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.
This replaces #4737, which conflicted with
mainonce #4721 deletedvg_aes_ctr32_to's generated files. It adds the 4/2/1 remainder the AES-GCM session asked for, and deletesvg_aes_ctr32_to. Part of the ppc64le performance work from #4141; the audit found AES-CTR at 1.6× aws-lc-rs at 16 KiB.This is an implementation change only. It changes no TCB or
Spec/file, and it usesvadduwm, which is already in the model.Why
From the audit run's numbers, the AES rounds cost about 1 cycle per round per block. That is
vcipher's throughput, the same floor aws-lc-rs reaches. The gaps are elsewhere:What changes (
Impl/Aes/PPC64LE/Crypto.lean)mtvsrd,vpermandaddion a counter word kept inr8.counterBase(v10). Each block takes a copy of it (vor), thenvadduwm counterBase, counterBase, counterStep.counterStep(v11) holdscounterOne, which is one in the last word. So the add increments the last 32 bits modulo 2³², exactlyinc32.writeBlockloads the data withlxvd2xand puts the keystream intolxvd2x's order with the store'svperm. It XORs in that order and stores withstxvd2x. That is onevpermper block instead of two.r8.batchestakes the blocks left after the batches of eight (fewer than eight) by four, two and one, by the bits of their number (tailStep). That is at most three serial chains instead of up to seven. ECB sharesbatches, so its tail changes too.vg_aes_ctr32_to: deleted, withctrWith's out-of-place parameter, its contract (Proof/Aes/PPC64LE/Base.lean) and its proofs. Nothing has called it since PPC64LE AES-GCM: open_to decrypts and hashes in one pass (vg_aes_gcm_decrypt_blocks_to) #4721.Per block, the loop goes from 10 instructions outside the rounds to 7, and from 3
vperms to 1.Proofs (
Proof/Aes/PPC64LE/Crypto/)ctrAdd v i, the counter block withiadded to its last 32 bits, replaces ther8counter word. It appears inreadValue, inReadInv,BatchRead,BatchCipherandBatchDone, and inLoopInv, whosecounteris nowcounterBase = ctrAdd icb k.counter_stepshows thatvadduwmbycounterOneisctrAdd · 1, viapack4and the words ofcounterOne.ctrAdd_repeatshowsctrAdd icb j = inc32^j icb.writeBlock_ok's CTR case shows the stored block isrev128 (v ⊕ rev128 data), usingswapDwords_xor,flip_storeandrev128_xor.tail_step_okproves one step. Before the step forc, fewer than2cblocks are left, so bitcis set exactly when at leastcare (bit_test, bydecideover eight cases).batches_okruns the three steps after the batch loop.ctr32_wp: in place only. The counter block is stored back withstore_ok, andread_write_self16gives back the advanced block.taint_decideoverctr32. The tail branches only on bits ofn.There's no
sorry, no new axiom and no change of resource limits.Checks
Rust (Linux, PPC64LE, stable), which runs the AES-CTR, ECB, GCM and CCM vector tests on POWER.Emit.leanregenerated the crate, changing onlyvg_aes_ctr32and the ECB functions inaes.rsandaes_ecb.rs.check_lean_public_imports.pyreports nothing.Benchmark
Run 42527285490:
arches=powerpc64, basemainat846fbbae, on POWER9. Modulesaes_ctr aes_ecb aes_gcm, narrowed toaes-ctr-.*|aes-ecb-encrypt-.*|aes-128-gcm-(encrypt|decrypt), since the full modules exceed the job's budget. Each figure is the fastest of each side's copies.aes-ctr-128/64aes-ctr-128/1024aes-ctr-128/16384aes-ctr-192/16384aes-ctr-256/16384aes-128-gcm-decrypt/16384aes-128-gcm-decrypt/1048576aes-128-gcm-encrypt/64aes-128-gcm-encrypt/192aes-ecb-encrypt-128/16384vg_aes_ctr32's whole loop, are 6–8% faster at 16 KiB and up (AES-128 and AES-192). That is less than the 1.2× I estimated, so the work outside the rounds was not all that held CTR back.Base copies/Head copiesin the run's summary).aes-ctr-192/64reads 22% slower in this run, while 64 bytes of AES-128 and AES-256 read faster.AES-CTR-128 at 16 KiB is now about 1.5× aws-lc-rs's 4.48 µs from the audit run, down from 1.6×.
The two slower rows
I compared the generated code with
maininstead of rerunning.main, and it is at the same offset within the function.assembly/aes_ecb.sand the encoded words insrc/aes_ecb.rsfirst differ after the batch-of-eight loop (labels24/25), in the tail. Neither size reaches the tail, since both are whole multiples of eight blocks. The function grew by the tail's code, so its placement relative to other functions in the binary may have moved. Its instructions did not.vg_aes_ctr32code as AES-128 and AES-192, with 14 rounds instead of 10 or 12. Those read 8.4% and 7.5% faster in the same run.So neither row reflects a change to the code it runs. Each is runner noise, or for ECB possibly a layout effect of the longer function.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TKUFHGBeNadVff4mCVhkSH