[PATCH 06/10] serpent: reduce CTR bulk counter carry handling to 16 bits
Jussi Kivilinna
jussi.kivilinna at iki.fi
Fri Jul 24 20:50:13 CEST 2026
* cipher/serpent.c (serpent_setkey): Assign ctr16be_enc bulk op.
(_gcry_serpent_ctr_enc): Use cipher_block_add_be16.
* cipher/serpent-avx2-amd64.S (inc_le128): Remove.
(_gcry_serpent_avx2_ctr_enc): Add to low 16 counter bits only, drop
full-width overflow path.
* cipher/serpent-sse2-amd64.S (_gcry_serpent_sse2_ctr_enc): Add to low
16 counter bits only, drop full-width overflow path.
* cipher/serpent-avx512-x86.c (ctr_generate): Do 16-bit big-endian
counter addition, add carry slow path with vpaddw/vpshufb.
* cipher/serpent-armv7-neon.S (_gcry_serpent_neon_ctr_enc): Drop 64-bit
counter overflow path.
* cipher/Makefile.am (avx512f_cflags): Add -mavx512bw.
* configure.ac: Add -mavx512bw and _mm512_shuffle_epi8 to AVX512
intrinsics check.
--
Serpent CTR keystream generators did full 128-bit counter increment
with separate 64-bit overflow path. Under ctr16be_enc contract generic
ctr code splits work at low 16-bit overflow, so bulk function adds only
to low 16 counter bits. Drop overflow paths, use 16-bit stepping.
AVX512 slow path now does 16-bit vector addition and byte shuffle
(vpaddw, vpshufb), which need AVX512BW. Add -mavx512bw to AVX512
intrinsics build flags and probe _mm512_shuffle_epi8 in configure.
Moves serpent off transitional ctr_enc alias.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
cipher/Makefile.am | 2 +-
cipher/serpent-armv7-neon.S | 39 -------------
cipher/serpent-avx2-amd64.S | 71 +++++-------------------
cipher/serpent-avx512-x86.c | 108 ++++++++++++++++--------------------
cipher/serpent-sse2-amd64.S | 67 +++++-----------------
cipher/serpent.c | 4 +-
configure.ac | 3 +-
7 files changed, 80 insertions(+), 214 deletions(-)
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 8f54ea47..021a7ab6 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -357,7 +357,7 @@ rijndael-vp-aarch64.lo: $(srcdir)/rijndael-vp-aarch64.c Makefile
`echo $(LTCOMPILE) $(aarch64_simd_cflags) -c $< | $(instrumentation_munging) `
if ENABLE_X86_AVX512_INTRINSICS_EXTRA_CFLAGS
-avx512f_cflags = -mavx512f
+avx512f_cflags = -mavx512f -mavx512bw
else
avx512f_cflags =
endif
diff --git a/cipher/serpent-armv7-neon.S b/cipher/serpent-armv7-neon.S
index 4179ba2c..c8b9660b 100644
--- a/cipher/serpent-armv7-neon.S
+++ b/cipher/serpent-armv7-neon.S
@@ -702,10 +702,6 @@ _gcry_serpent_neon_ctr_enc:
vmov RB3d0, RT0d0;
vmov RT2d0, RT0d0;
- /* check need for handling 64-bit overflow and carry */
- beq .Ldo_ctr_carry;
-
-.Lctr_carry_done:
/* le => be */
vrev64.u8 RA1, RA1;
vrev64.u8 RA2, RA2;
@@ -757,41 +753,6 @@ _gcry_serpent_neon_ctr_enc:
veor RB3, RB3;
pop {r4,pc};
-
-.Ldo_ctr_carry:
- cmp r4, #-8;
- blo .Lctr_carry_done;
- beq .Lcarry_RT2;
-
- cmp r4, #-6;
- blo .Lcarry_RB3;
- beq .Lcarry_RB2;
-
- cmp r4, #-4;
- blo .Lcarry_RB1;
- beq .Lcarry_RB0;
-
- cmp r4, #-2;
- blo .Lcarry_RA3;
- beq .Lcarry_RA2;
-
- vsub.u64 RA1d0, RT1d0;
-.Lcarry_RA2:
- vsub.u64 RA2d0, RT1d0;
-.Lcarry_RA3:
- vsub.u64 RA3d0, RT1d0;
-.Lcarry_RB0:
- vsub.u64 RB0d0, RT1d0;
-.Lcarry_RB1:
- vsub.u64 RB1d0, RT1d0;
-.Lcarry_RB2:
- vsub.u64 RB2d0, RT1d0;
-.Lcarry_RB3:
- vsub.u64 RB3d0, RT1d0;
-.Lcarry_RT2:
- vsub.u64 RT2d0, RT1d0;
-
- b .Lctr_carry_done;
.size _gcry_serpent_neon_ctr_enc,.-_gcry_serpent_neon_ctr_enc;
.align 3
diff --git a/cipher/serpent-avx2-amd64.S b/cipher/serpent-avx2-amd64.S
index 7aba235f..a419eb29 100644
--- a/cipher/serpent-avx2-amd64.S
+++ b/cipher/serpent-avx2-amd64.S
@@ -633,12 +633,6 @@ _gcry_serpent_avx2_blk16:
CFI_ENDPROC();
ELF(.size _gcry_serpent_avx2_blk16,.-_gcry_serpent_avx2_blk16;)
-#define inc_le128(x, minus_one, tmp) \
- vpcmpeqq minus_one, x, tmp; \
- vpsubq minus_one, x, x; \
- vpslldq $8, tmp, tmp; \
- vpsubq tmp, x, x;
-
.align 16
.globl _gcry_serpent_avx2_ctr_enc
ELF(.type _gcry_serpent_avx2_ctr_enc, at function;)
@@ -651,79 +645,40 @@ _gcry_serpent_avx2_ctr_enc:
*/
CFI_STARTPROC();
- movq 8(%rcx), %rax;
- bswapq %rax;
-
vzeroupper;
vbroadcasti128 .Lbswap128_mask rRIP, RTMP3;
vpcmpeqd RNOT, RNOT, RNOT;
- vpsrldq $8, RNOT, RNOT; /* ab: -1:0 ; cd: -1:0 */
- vpaddq RNOT, RNOT, RTMP2; /* ab: -2:0 ; cd: -2:0 */
+ vpsrldq $14, RNOT, RNOT; /* ab: -1:0 ; cd: -1:0 */
+ vpaddw RNOT, RNOT, RTMP2; /* ab: -2:0 ; cd: -2:0 */
/* load IV and byteswap */
vmovdqu (%rcx), RTMP4x;
vpshufb RTMP3x, RTMP4x, RTMP4x;
vmovdqa RTMP4x, RTMP0x;
- inc_le128(RTMP4x, RNOTx, RTMP1x);
+ vpsubw RNOTx, RTMP4x, RTMP4x;
vinserti128 $1, RTMP4x, RTMP0, RTMP0;
vpshufb RTMP3, RTMP0, RA0; /* +1 ; +0 */
- /* check need for handling 64-bit overflow and carry */
- cmpq $(0xffffffffffffffff - 16), %rax;
- ja .Lhandle_ctr_carry;
-
/* construct IVs */
- vpsubq RTMP2, RTMP0, RTMP0; /* +3 ; +2 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +3 ; +2 */
vpshufb RTMP3, RTMP0, RA1;
- vpsubq RTMP2, RTMP0, RTMP0; /* +5 ; +4 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +5 ; +4 */
vpshufb RTMP3, RTMP0, RA2;
- vpsubq RTMP2, RTMP0, RTMP0; /* +7 ; +6 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +7 ; +6 */
vpshufb RTMP3, RTMP0, RA3;
- vpsubq RTMP2, RTMP0, RTMP0; /* +9 ; +8 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +9 ; +8 */
vpshufb RTMP3, RTMP0, RB0;
- vpsubq RTMP2, RTMP0, RTMP0; /* +11 ; +10 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +11 ; +10 */
vpshufb RTMP3, RTMP0, RB1;
- vpsubq RTMP2, RTMP0, RTMP0; /* +13 ; +12 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +13 ; +12 */
vpshufb RTMP3, RTMP0, RB2;
- vpsubq RTMP2, RTMP0, RTMP0; /* +15 ; +14 */
+ vpsubw RTMP2, RTMP0, RTMP0; /* +15 ; +14 */
vpshufb RTMP3, RTMP0, RB3;
- vpsubq RTMP2, RTMP0, RTMP0; /* +16 */
- vpshufb RTMP3x, RTMP0x, RTMP0x;
-
- jmp .Lctr_carry_done;
-.Lhandle_ctr_carry:
- /* construct IVs */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RA1; /* +3 ; +2 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RA2; /* +5 ; +4 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RA3; /* +7 ; +6 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RB0; /* +9 ; +8 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RB1; /* +11 ; +10 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RB2; /* +13 ; +12 */
- inc_le128(RTMP0, RNOT, RTMP1);
- inc_le128(RTMP0, RNOT, RTMP1);
- vpshufb RTMP3, RTMP0, RB3; /* +15 ; +14 */
- inc_le128(RTMP0, RNOT, RTMP1);
- vextracti128 $1, RTMP0, RTMP0x;
- vpshufb RTMP3x, RTMP0x, RTMP0x; /* +16 */
-
-.align 4
-.Lctr_carry_done:
- /* store new IV */
- vmovdqu RTMP0x, (%rcx);
+ /* Update IV */
+ addb $16, 15(%rcx);
+ adcb $0, 14(%rcx);
call __serpent_enc_blk16;
diff --git a/cipher/serpent-avx512-x86.c b/cipher/serpent-avx512-x86.c
index 5b5c2483..dfbe2432 100644
--- a/cipher/serpent-avx512-x86.c
+++ b/cipher/serpent-avx512-x86.c
@@ -618,74 +618,62 @@ ctr_generate(unsigned char *ctr, __m512i vin[8])
4LL << 56, 0,
4LL << 56, 0,
4LL << 56, 0);
- const __m512i add4567 = _mm512_add_epi32(add0123, add4444);
- const __m512i add8888 = _mm512_add_epi32(add4444, add4444);
+ const __m512i add4567 = _mm512_add_epi8(add0123, add4444);
+ const __m512i add8888 = _mm512_add_epi8(add4444, add4444);
// Fast path without carry handling.
__m512i vctr =
_mm512_broadcast_i32x4(_mm_loadu_si128((const void *)ctr));
- cipher_block_add(ctr, 32, blocksize);
- vin[0] = _mm512_add_epi32(vctr, add0123);
- vin[1] = _mm512_add_epi32(vctr, add4567);
- vin[2] = _mm512_add_epi32(vin[0], add8888);
- vin[3] = _mm512_add_epi32(vin[1], add8888);
- vin[4] = _mm512_add_epi32(vin[2], add8888);
- vin[5] = _mm512_add_epi32(vin[3], add8888);
- vin[6] = _mm512_add_epi32(vin[4], add8888);
- vin[7] = _mm512_add_epi32(vin[5], add8888);
+ cipher_block_add_be16(ctr, 32, blocksize);
+ vin[0] = _mm512_add_epi8(vctr, add0123);
+ vin[1] = _mm512_add_epi8(vctr, add4567);
+ vin[2] = _mm512_add_epi8(vin[0], add8888);
+ vin[3] = _mm512_add_epi8(vin[1], add8888);
+ vin[4] = _mm512_add_epi8(vin[2], add8888);
+ vin[5] = _mm512_add_epi8(vin[3], add8888);
+ vin[6] = _mm512_add_epi8(vin[4], add8888);
+ vin[7] = _mm512_add_epi8(vin[5], add8888);
}
else
{
- // Slow path.
- u32 blocks[4][blocksize / sizeof(u32)];
-
- cipher_block_cpy(blocks[0], ctr, blocksize);
- cipher_block_cpy(blocks[1], ctr, blocksize);
- cipher_block_cpy(blocks[2], ctr, blocksize);
- cipher_block_cpy(blocks[3], ctr, blocksize);
- cipher_block_add(ctr, 32, blocksize);
- cipher_block_add(blocks[1], 1, blocksize);
- cipher_block_add(blocks[2], 2, blocksize);
- cipher_block_add(blocks[3], 3, blocksize);
- vin[0] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[1] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[2] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[3] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[4] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[5] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[6] = _mm512_loadu_epi32 (blocks);
- cipher_block_add(blocks[0], 4, blocksize);
- cipher_block_add(blocks[1], 4, blocksize);
- cipher_block_add(blocks[2], 4, blocksize);
- cipher_block_add(blocks[3], 4, blocksize);
- vin[7] = _mm512_loadu_epi32 (blocks);
-
- wipememory(blocks, sizeof(blocks));
+ const __m512i add0123 = _mm512_set_epi64(0, 3,
+ 0, 2,
+ 0, 1,
+ 0, 0);
+ const __m512i add4444 = _mm512_set_epi64(0, 4,
+ 0, 4,
+ 0, 4,
+ 0, 4);
+ const __m512i add4567 = _mm512_add_epi16(add0123, add4444);
+ const __m512i add8888 = _mm512_add_epi16(add4444, add4444);
+ static const unsigned char bswap_mask[16] __attribute__ ((aligned (16))) =
+ { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+ const __m512i vbswap =
+ _mm512_broadcast_i32x4(_mm_loadu_si128((const void *)bswap_mask));
+
+ // Slow path with 16-bit carry.
+ __m512i vctr =
+ _mm512_broadcast_i32x4(_mm_loadu_si128((const void *)ctr));
+
+ cipher_block_add_be16(ctr, 32, blocksize);
+ vctr = _mm512_shuffle_epi8(vctr, vbswap);
+ vin[0] = _mm512_add_epi16(vctr, add0123);
+ vin[1] = _mm512_add_epi16(vctr, add4567);
+ vin[2] = _mm512_add_epi16(vin[0], add8888);
+ vin[3] = _mm512_add_epi16(vin[1], add8888);
+ vin[4] = _mm512_add_epi16(vin[2], add8888);
+ vin[5] = _mm512_add_epi16(vin[3], add8888);
+ vin[6] = _mm512_add_epi16(vin[4], add8888);
+ vin[7] = _mm512_add_epi16(vin[5], add8888);
+ vin[0] = _mm512_shuffle_epi8(vin[0], vbswap);
+ vin[1] = _mm512_shuffle_epi8(vin[1], vbswap);
+ vin[2] = _mm512_shuffle_epi8(vin[2], vbswap);
+ vin[3] = _mm512_shuffle_epi8(vin[3], vbswap);
+ vin[4] = _mm512_shuffle_epi8(vin[4], vbswap);
+ vin[5] = _mm512_shuffle_epi8(vin[5], vbswap);
+ vin[6] = _mm512_shuffle_epi8(vin[6], vbswap);
+ vin[7] = _mm512_shuffle_epi8(vin[7], vbswap);
}
}
diff --git a/cipher/serpent-sse2-amd64.S b/cipher/serpent-sse2-amd64.S
index 885c2bf1..93912935 100644
--- a/cipher/serpent-sse2-amd64.S
+++ b/cipher/serpent-sse2-amd64.S
@@ -688,68 +688,27 @@ _gcry_serpent_sse2_ctr_enc:
pbswap(RTMP0, RTMP1); /* be => le */
pcmpeqd RNOT, RNOT;
- psrldq $8, RNOT; /* low: -1, high: 0 */
+ psrldq $14, RNOT; /* low: -1, high: 0 */
movdqa RNOT, RTMP2;
- paddq RTMP2, RTMP2; /* low: -2, high: 0 */
+ paddw RTMP2, RTMP2; /* low: -2, high: 0 */
/* construct IVs */
movdqa RTMP0, RTMP1;
- psubq RNOT, RTMP0; /* +1 */
+ psubw RNOT, RTMP0; /* +1 */
movdqa RTMP0, RA1;
- psubq RTMP2, RTMP1; /* +2 */
+ psubw RTMP2, RTMP1; /* +2 */
movdqa RTMP1, RA2;
- psubq RTMP2, RTMP0; /* +3 */
+ psubw RTMP2, RTMP0; /* +3 */
movdqa RTMP0, RA3;
- psubq RTMP2, RTMP1; /* +4 */
+ psubw RTMP2, RTMP1; /* +4 */
movdqa RTMP1, RB0;
- psubq RTMP2, RTMP0; /* +5 */
+ psubw RTMP2, RTMP0; /* +5 */
movdqa RTMP0, RB1;
- psubq RTMP2, RTMP1; /* +6 */
+ psubw RTMP2, RTMP1; /* +6 */
movdqa RTMP1, RB2;
- psubq RTMP2, RTMP0; /* +7 */
+ psubw RTMP2, RTMP0; /* +7 */
movdqa RTMP0, RB3;
- psubq RTMP2, RTMP1; /* +8 */
-
- /* check need for handling 64-bit overflow and carry */
- cmpl $0xffffffff, 8(%rcx);
- jne .Lno_ctr_carry;
-
- movl 12(%rcx), %eax;
- bswapl %eax;
- cmpl $-8, %eax;
- jb .Lno_ctr_carry;
- pslldq $8, RNOT; /* low: 0, high: -1 */
- je .Lcarry_RTMP0;
-
- cmpl $-6, %eax;
- jb .Lcarry_RB3;
- je .Lcarry_RB2;
-
- cmpl $-4, %eax;
- jb .Lcarry_RB1;
- je .Lcarry_RB0;
-
- cmpl $-2, %eax;
- jb .Lcarry_RA3;
- je .Lcarry_RA2;
-
- psubq RNOT, RA1;
-.Lcarry_RA2:
- psubq RNOT, RA2;
-.Lcarry_RA3:
- psubq RNOT, RA3;
-.Lcarry_RB0:
- psubq RNOT, RB0;
-.Lcarry_RB1:
- psubq RNOT, RB1;
-.Lcarry_RB2:
- psubq RNOT, RB2;
-.Lcarry_RB3:
- psubq RNOT, RB3;
-.Lcarry_RTMP0:
- psubq RNOT, RTMP1;
-
-.Lno_ctr_carry:
+
/* le => be */
pbswap(RA1, RTMP0);
pbswap(RA2, RTMP0);
@@ -759,8 +718,10 @@ _gcry_serpent_sse2_ctr_enc:
pbswap(RB2, RTMP0);
pbswap(RB3, RTMP0);
pbswap(RTMP1, RTMP0);
- /* store new IV */
- movdqu RTMP1, (%rcx);
+
+ /* Update IV */
+ addb $8, 15(%rcx);
+ adcb $0, 14(%rcx);
call __serpent_enc_blk8;
diff --git a/cipher/serpent.c b/cipher/serpent.c
index 72a2287d..37e0c63a 100644
--- a/cipher/serpent.c
+++ b/cipher/serpent.c
@@ -854,7 +854,7 @@ serpent_setkey (void *ctx,
memset (bulk_ops, 0, sizeof(*bulk_ops));
bulk_ops->cbc_dec = _gcry_serpent_cbc_dec;
bulk_ops->cfb_dec = _gcry_serpent_cfb_dec;
- bulk_ops->ctr_enc = _gcry_serpent_ctr_enc;
+ bulk_ops->ctr16be_enc = _gcry_serpent_ctr_enc;
bulk_ops->ocb_crypt = _gcry_serpent_ocb_crypt;
bulk_ops->ocb_auth = _gcry_serpent_ocb_auth;
bulk_ops->xts_crypt = _gcry_serpent_xts_crypt;
@@ -1126,7 +1126,7 @@ _gcry_serpent_ctr_enc(void *context, unsigned char *ctr,
outbuf += sizeof(serpent_block_t);
inbuf += sizeof(serpent_block_t);
/* Increment the counter. */
- cipher_block_add(ctr, 1, sizeof(serpent_block_t));
+ cipher_block_add_be16(ctr, 1, sizeof(serpent_block_t));
}
wipememory(tmpbuf, sizeof(tmpbuf));
diff --git a/configure.ac b/configure.ac
index 604f063e..631ed81e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1834,7 +1834,7 @@ fi
# Check whether compiler supports x86/AVX512 intrinsics
#
_gcc_cflags_save=$CFLAGS
-CFLAGS="$CFLAGS -mavx512f"
+CFLAGS="$CFLAGS -mavx512f -mavx512bw"
AC_CACHE_CHECK([whether compiler supports x86/AVX512 intrinsics],
[gcry_cv_cc_x86_avx512_intrinsics],
@@ -1851,6 +1851,7 @@ AC_CACHE_CHECK([whether compiler supports x86/AVX512 intrinsics],
x = _mm512_loadu_epi32 (in); /* check the GCC bug 90980. */
x = _mm512_maskz_loadu_epi32(_cvtu32_mask16(0xfff0), in)
^ _mm512_castsi128_si512(y);
+ x = _mm512_shuffle_epi8(x, x);
asm volatile ("vinserti32x4 \$3, %0, %%zmm6, %%zmm6;\n\t"
"vpxord %%zmm6, %%zmm6, %%zmm6"
::"x"(y),"r"(in):"memory","xmm6");
--
2.53.0
More information about the Gcrypt-devel
mailing list