PR #23761 opened by Shreesh Adiga (tantei3)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23761
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23761.patch
Ported the x86 AES-NI implementation to aarch64 NEON using AES extension.
Benchmarking on A720 core reports about 50x speedup:
```
checkasm: all 6 tests passed
Benchmark results:
name nsecs (vs ref)
aes_decrypt_128_c: 5671.2
aes_decrypt_128_neon_aes: 92.3 (61.40x)
aes_decrypt_192_c: 7031.2
aes_decrypt_192_neon_aes: 112.2 (62.65x)
aes_decrypt_256_c: 8107.5
aes_decrypt_256_neon_aes: 131.2 (61.74x)
aes_encrypt_128_c: 5838.7
aes_encrypt_128_neon_aes: 101.9 (57.24x)
aes_encrypt_192_c: 7058.7
aes_encrypt_192_neon_aes: 120.4 (58.61x)
aes_encrypt_256_c: 7952.2
aes_encrypt_256_neon_aes: 136.1 (54.44x)
```
>From 06d06f1ab3e403843cab1ccdff2fb156ee565bb6 Mon Sep 17 00:00:00 2001
From: Shreesh Adiga <[email protected]>
Date: Fri, 10 Jul 2026 20:05:15 +0530
Subject: [PATCH 1/2] avutil/cpu: add aarch64 CPU feature flag for ARM AES
---
configure | 7 ++++++-
libavutil/aarch64/asm.S | 9 +++++++--
libavutil/aarch64/cpu.c | 10 +++++++++-
libavutil/aarch64/cpu.h | 1 +
libavutil/cpu.c | 1 +
libavutil/cpu.h | 1 +
libavutil/tests/cpu.c | 1 +
tests/checkasm/checkasm.c | 1 +
8 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/configure b/configure
index 80954aa826..863148c7b7 100755
--- a/configure
+++ b/configure
@@ -478,6 +478,7 @@ Optimization options (experts only):
--disable-armv6t2 disable armv6t2 optimizations
--disable-vfp disable VFP optimizations
--disable-neon disable NEON optimizations
+ --disable-arm-aes disable ARM/AArch64 AES optimizations
--disable-arm-crc disable ARM/AArch64 CRC optimizations
--disable-dotprod disable DOTPROD optimizations
--disable-i8mm disable I8MM optimizations
@@ -2338,6 +2339,7 @@ ARCH_EXT_LIST_ARM="
armv6
armv6t2
armv8
+ arm_aes
arm_crc
dotprod
i8mm
@@ -2974,6 +2976,7 @@ intrinsics_sse2_deps="sse2"
vfp_deps="arm"
vfpv3_deps="vfp"
setend_deps="arm"
+arm_aes_deps="aarch64 neon"
arm_crc_deps="aarch64"
dotprod_deps="aarch64 neon"
i8mm_deps="aarch64 neon"
@@ -6649,7 +6652,8 @@ if enabled aarch64; then
# internal assembler in clang 3.3 does not support this instruction
enabled neon && check_insn neon 'ext v0.8B, v0.8B, v1.8B, #1'
- archext_list="arm_crc dotprod i8mm pmull eor3 sve sve2 sme sme_i16i64 sme2"
+ archext_list="arm_aes arm_crc dotprod i8mm pmull eor3 sve sve2 sme
sme_i16i64 sme2"
+ enabled arm_aes && check_archext_name_insn arm_aes aes 'aese v0.16b,
v1.16b'
enabled arm_crc && check_archext_name_insn arm_crc crc 'crc32x w0, w0, x0'
enabled pmull && check_archext_name_insn pmull aes 'pmull v0.1q, v0.1d,
v0.1d'
enabled eor3 && check_archext_name_insn eor3 sha3 'eor3 v0.16b, v1.16b,
v2.16b, v3.16b'
@@ -8506,6 +8510,7 @@ if enabled aarch64; then
echo "NEON enabled ${neon-no}"
echo "DOTPROD enabled ${dotprod-no}"
echo "I8MM enabled ${i8mm-no}"
+ echo "ARM_AES enabled ${arm_aes-no}"
echo "PMULL enabled ${pmull-no}"
echo "EOR3 enabled ${eor3-no}"
echo "SVE enabled ${sve-no}"
diff --git a/libavutil/aarch64/asm.S b/libavutil/aarch64/asm.S
index e0cb74a579..665202a808 100644
--- a/libavutil/aarch64/asm.S
+++ b/libavutil/aarch64/asm.S
@@ -65,10 +65,14 @@
#endif
#if HAVE_AS_ARCHEXT_AES_DIRECTIVE
-#define ENABLE_PMULL .arch_extension aes
-#define DISABLE_PMULL .arch_extension noaes
+#define ENABLE_ARM_AES .arch_extension aes
+#define ENABLE_PMULL .arch_extension aes
+#define DISABLE_ARM_AES .arch_extension noaes
+#define DISABLE_PMULL .arch_extension noaes
#else
+#define ENABLE_ARM_AES
#define ENABLE_PMULL
+#define DISABLE_ARM_AES
#define DISABLE_PMULL
#endif
@@ -121,6 +125,7 @@
#endif
DISABLE_ARM_CRC
+DISABLE_ARM_AES
DISABLE_PMULL
DISABLE_EOR3
DISABLE_DOTPROD
diff --git a/libavutil/aarch64/cpu.c b/libavutil/aarch64/cpu.c
index b8d2bd3280..81c972ab5a 100644
--- a/libavutil/aarch64/cpu.c
+++ b/libavutil/aarch64/cpu.c
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <sys/auxv.h>
+#define HWCAP_AARCH64_AES (1 << 3)
#define HWCAP_AARCH64_PMULL (1 << 4)
#define HWCAP_AARCH64_CRC32 (1 << 7)
#define HWCAP_AARCH64_SHA3 (1 << 17)
@@ -42,6 +43,8 @@ static int detect_flags(void)
unsigned long hwcap = ff_getauxval(AT_HWCAP);
unsigned long hwcap2 = ff_getauxval(AT_HWCAP2);
+ if (hwcap & HWCAP_AARCH64_AES)
+ flags |= AV_CPU_FLAG_ARM_AES;
if (hwcap & HWCAP_AARCH64_PMULL)
flags |= AV_CPU_FLAG_PMULL;
if (hwcap & HWCAP_AARCH64_SHA3)
@@ -91,6 +94,8 @@ static int detect_flags(void)
flags |= AV_CPU_FLAG_SME_I16I64;
if (have_feature("hw.optional.armv8_crc32"))
flags |= AV_CPU_FLAG_ARM_CRC;
+ if (have_feature("hw.optional.arm.FEAT_AES"))
+ flags |= AV_CPU_FLAG_ARM_AES;
if (have_feature("hw.optional.arm.FEAT_PMULL"))
flags |= AV_CPU_FLAG_PMULL;
if (have_feature("hw.optional.armv8_2_sha3"))
@@ -156,8 +161,10 @@ static int detect_flags(void)
flags |= AV_CPU_FLAG_ARM_CRC;
#endif
#ifdef PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE
- if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
+ if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)) {
+ flags |= AV_CPU_FLAG_ARM_AES;
flags |= AV_CPU_FLAG_PMULL;
+ }
#endif
#ifdef PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE
if (IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE))
@@ -226,6 +233,7 @@ int ff_get_cpu_flags_aarch64(void)
flags |= AV_CPU_FLAG_ARM_CRC;
#endif
#ifdef __ARM_FEATURE_AES
+ flags |= AV_CPU_FLAG_ARM_AES;
flags |= AV_CPU_FLAG_PMULL;
#endif
#ifdef __ARM_FEATURE_SHA3
diff --git a/libavutil/aarch64/cpu.h b/libavutil/aarch64/cpu.h
index 433ba60bca..91e5725f57 100644
--- a/libavutil/aarch64/cpu.h
+++ b/libavutil/aarch64/cpu.h
@@ -26,6 +26,7 @@
#define have_neon(flags) CPUEXT(flags, NEON)
#define have_vfp(flags) CPUEXT(flags, VFP)
#define have_arm_crc(flags) CPUEXT(flags, ARM_CRC)
+#define have_arm_aes(flags) CPUEXT(flags, ARM_AES)
#define have_pmull(flags) CPUEXT(flags, PMULL)
#define have_eor3(flags) CPUEXT(flags, EOR3)
#define have_dotprod(flags) CPUEXT(flags, DOTPROD)
diff --git a/libavutil/cpu.c b/libavutil/cpu.c
index 00029f81ca..6994166147 100644
--- a/libavutil/cpu.c
+++ b/libavutil/cpu.c
@@ -193,6 +193,7 @@ int av_parse_cpu_caps(unsigned *flags, const char *s)
{ "sme2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_SME2
}, .unit = "flags" },
{ "pmull", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_PMULL
}, .unit = "flags" },
{ "eor3", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_EOR3
}, .unit = "flags" },
+ { "aes", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_ARM_AES
}, .unit = "flags" },
#elif ARCH_MIPS
{ "mmi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_MMI
}, .unit = "flags" },
{ "msa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_MSA
}, .unit = "flags" },
diff --git a/libavutil/cpu.h b/libavutil/cpu.h
index c41686344f..5db34c2e2a 100644
--- a/libavutil/cpu.h
+++ b/libavutil/cpu.h
@@ -84,6 +84,7 @@
#define AV_CPU_FLAG_SETEND (1 <<16)
#define AV_CPU_FLAG_PMULL (1 <<17)
#define AV_CPU_FLAG_EOR3 (1 <<18)
+#define AV_CPU_FLAG_ARM_AES (1 <<19)
#define AV_CPU_FLAG_MMI (1 << 0)
#define AV_CPU_FLAG_MSA (1 << 1)
diff --git a/libavutil/tests/cpu.c b/libavutil/tests/cpu.c
index f74d02270e..248f46576f 100644
--- a/libavutil/tests/cpu.c
+++ b/libavutil/tests/cpu.c
@@ -50,6 +50,7 @@ static const struct {
{ AV_CPU_FLAG_SVE2, "sve2" },
{ AV_CPU_FLAG_SME, "sme" },
{ AV_CPU_FLAG_SME_I16I64, "sme_i16i64" },
+ { AV_CPU_FLAG_ARM_AES, "aes" },
{ AV_CPU_FLAG_ARM_CRC, "crc" },
{ AV_CPU_FLAG_SME2, "sme2" },
{ AV_CPU_FLAG_PMULL, "pmull" },
diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c
index 4f54db8e1d..288d675566 100644
--- a/tests/checkasm/checkasm.c
+++ b/tests/checkasm/checkasm.c
@@ -330,6 +330,7 @@ static const CheckasmCpuInfo cpuflags[] = {
{ "SVE2", "sve2", AV_CPU_FLAG_SVE2 },
{ "SME", "sme", AV_CPU_FLAG_SME },
{ "SME-I16I64", "sme_i16i64", AV_CPU_FLAG_SME_I16I64 },
+ { "NEON_AES", "neon_aes", AV_CPU_FLAG_ARM_AES },
{ "CRC", "crc", AV_CPU_FLAG_ARM_CRC },
{ "SME2", "sme2", AV_CPU_FLAG_SME2 },
{ "PMULL", "pmull_eor3", AV_CPU_FLAG_PMULL|AV_CPU_FLAG_EOR3 },
--
2.52.0
>From bdbe1d479e1b67e4a0f71c00e4802cadb8751c1e Mon Sep 17 00:00:00 2001
From: Shreesh Adiga <[email protected]>
Date: Fri, 10 Jul 2026 20:17:12 +0530
Subject: [PATCH 2/2] avutil/aes: add aarch64 NEON implementation
Ported the x86 AES-NI implementation to aarch64 NEON using AES extension.
Benchmarking on A720 core reports about 50x speedup:
checkasm: all 6 tests passed
Benchmark results:
name nsecs (vs ref)
aes_decrypt_128_c: 5671.2
aes_decrypt_128_neon_aes: 92.3 (61.40x)
aes_decrypt_192_c: 7031.2
aes_decrypt_192_neon_aes: 112.2 (62.65x)
aes_decrypt_256_c: 8107.5
aes_decrypt_256_neon_aes: 131.2 (61.74x)
aes_encrypt_128_c: 5838.7
aes_encrypt_128_neon_aes: 101.9 (57.24x)
aes_encrypt_192_c: 7058.7
aes_encrypt_192_neon_aes: 120.4 (58.61x)
aes_encrypt_256_c: 7952.2
aes_encrypt_256_neon_aes: 136.1 (54.44x)
---
libavutil/aarch64/Makefile | 6 +-
libavutil/aarch64/aes.S | 145 +++++++++++++++++++++++++++++++++++
libavutil/aarch64/aes_init.c | 50 ++++++++++++
libavutil/aes.c | 2 +
libavutil/aes_internal.h | 1 +
5 files changed, 202 insertions(+), 2 deletions(-)
create mode 100644 libavutil/aarch64/aes.S
create mode 100644 libavutil/aarch64/aes_init.c
diff --git a/libavutil/aarch64/Makefile b/libavutil/aarch64/Makefile
index 8a7e7ca057..4775fb7dc7 100644
--- a/libavutil/aarch64/Makefile
+++ b/libavutil/aarch64/Makefile
@@ -1,8 +1,10 @@
-OBJS += aarch64/cpu.o \
+OBJS += aarch64/aes_init.o \
+ aarch64/cpu.o \
aarch64/float_dsp_init.o \
aarch64/tx_float_init.o \
-ARMV8-OBJS += aarch64/crc.o
+ARMV8-OBJS += aarch64/aes.o \
+ aarch64/crc.o
NEON-OBJS += aarch64/float_dsp_neon.o \
aarch64/tx_float_neon.o \
diff --git a/libavutil/aarch64/aes.S b/libavutil/aarch64/aes.S
new file mode 100644
index 0000000000..b4993daa70
--- /dev/null
+++ b/libavutil/aarch64/aes.S
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2026 Shreesh Adiga <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "asm.S"
+
+#if HAVE_ARM_AES
+ENABLE_ARM_AES
+
+.macro aesenc x, y
+ aese \x, \y
+ aesmc \x, \x
+.endm
+
+.macro aesenclast x, y
+ aese \x, \y
+.endm
+
+.macro aesdec x, y
+ aesd \x, \y
+ aesimc \x, \x
+.endm
+
+.macro aesdeclast x, y
+ aesd \x, \y
+.endm
+
+.macro AES_CRYPT op, rounds
+ .ifc \op, enc
+ .macro aesop x, y
+ aesenc \x, \y
+ .endm
+ .macro aesoplast x, y
+ aesenclast \x, \y
+ .endm
+ .else
+ .macro aesop x, y
+ aesdec \x, \y
+ .endm
+ .macro aesoplast x, y
+ aesdeclast \x, \y
+ .endm
+ .endif
+
//-----------------------------------------------------------------------------
+ // void ff_aes_[enc|dec]rypt_N_neon(AVAES *a, uint8_t *dst, const
uint8_t *src,
+ // int count, uint8_t *iv, int rounds)
+
//-----------------------------------------------------------------------------
+ function ff_aes_\op\()rypt_\rounds\()_neon, export=1
+ cbz w3, 3f
+ lsl w3, w3, #4
+ add x0, x0, #0x70
+ add x2, x2, x3
+ add x1, x1, x3
+ neg x3, x3
+ movi v1.2d, #0
+ .if \rounds == 14
+ ldr q31, [x0, #(8*2*\rounds-0x70)]
+ ldp q29, q30, [x0, #0x50]
+ ldp q27, q28, [x0, #0x30]
+ .elseif \rounds == 12
+ ldr q29, [x0, #(8*2*\rounds-0x70)]
+ ldp q27, q28, [x0, #0x30]
+ .else
+ ldr q27, [x0, #(8*2*\rounds-0x70)]
+ .endif
+ ldp q25, q26, [x0, #0x10]
+ ldp q23, q24, [x0, #-0x10]
+ ldp q21, q22, [x0, #-0x30]
+ ldp q19, q20, [x0, #-0x50]
+ ldp q17, q18, [x0, #-0x70]
+ cbz x4, 1f
+ ldr q1, [x4]
+ 1:
+ ldr q0, [x2, x3]
+ .ifc \op, enc
+ eor v0.16b, v0.16b, v1.16b
+ .endif
+ .if \rounds > 10
+ .if \rounds > 12
+ aesop v0.16b, v31.16b
+ aesop v0.16b, v30.16b
+ .endif
+ aesop v0.16b, v29.16b
+ aesop v0.16b, v28.16b
+ .endif
+ aesop v0.16b, v27.16b
+ aesop v0.16b, v26.16b
+ aesop v0.16b, v25.16b
+ aesop v0.16b, v24.16b
+ aesop v0.16b, v23.16b
+ aesop v0.16b, v22.16b
+ aesop v0.16b, v21.16b
+ aesop v0.16b, v20.16b
+ aesop v0.16b, v19.16b
+ aesoplast v0.16b, v18.16b
+ eor v0.16b, v0.16b, v17.16b
+ cbz x4, 2f
+ .ifc \op, enc
+ mov v1.16b, v0.16b
+ .else
+ eor v0.16b, v0.16b, v1.16b
+ ldr q1, [x2, x3]
+ .endif
+ 2:
+ str q0, [x1, x3]
+ adds x3, x3, #16
+ b.lt 1b
+ cbz x4, 3f
+ .ifc \op, dec
+ str q1, [x4]
+ .else
+ str q0, [x4]
+ .endif
+ 3:
+ ret
+ endfunc
+ .purgem aesop
+ .purgem aesoplast
+.endm
+
+AES_CRYPT enc 10
+AES_CRYPT enc 12
+AES_CRYPT enc 14
+AES_CRYPT dec 10
+AES_CRYPT dec 12
+AES_CRYPT dec 14
+
+DISABLE_ARM_AES
+#endif
diff --git a/libavutil/aarch64/aes_init.c b/libavutil/aarch64/aes_init.c
new file mode 100644
index 0000000000..60bdc0574c
--- /dev/null
+++ b/libavutil/aarch64/aes_init.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2026 Shreesh Adiga <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stddef.h>
+#include "libavutil/aes_internal.h"
+#include "libavutil/aarch64/cpu.h"
+
+void ff_aes_decrypt_10_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+void ff_aes_decrypt_12_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+void ff_aes_decrypt_14_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+void ff_aes_encrypt_10_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+void ff_aes_encrypt_12_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+void ff_aes_encrypt_14_neon(AVAES *a, uint8_t *dst, const uint8_t *src,
+ int count, uint8_t *iv, int rounds);
+
+void ff_init_aes_aarch64(AVAES *a, int decrypt)
+{
+ int cpu_flags = av_get_cpu_flags();
+
+ if (have_arm_aes(cpu_flags)) {
+ if (a->rounds == 10)
+ a->crypt = decrypt ? ff_aes_decrypt_10_neon :
ff_aes_encrypt_10_neon;
+ else if (a->rounds == 12)
+ a->crypt = decrypt ? ff_aes_decrypt_12_neon :
ff_aes_encrypt_12_neon;
+ else if (a->rounds == 14)
+ a->crypt = decrypt ? ff_aes_decrypt_14_neon :
ff_aes_encrypt_14_neon;
+ }
+}
diff --git a/libavutil/aes.c b/libavutil/aes.c
index 13fadb4a47..c24da9f9f0 100644
--- a/libavutil/aes.c
+++ b/libavutil/aes.c
@@ -239,6 +239,8 @@ int av_aes_init(AVAES *a, const uint8_t *key, int key_bits,
int decrypt)
a->crypt = decrypt ? aes_decrypt : aes_encrypt;
#if ARCH_X86 && HAVE_X86ASM && HAVE_AESNI_EXTERNAL
ff_init_aes_x86(a, decrypt);
+#elif ARCH_AARCH64
+ ff_init_aes_aarch64(a, decrypt);
#endif
ff_thread_once(&aes_static_init, aes_init_static);
diff --git a/libavutil/aes_internal.h b/libavutil/aes_internal.h
index 17f79d3ce3..92b5a9802a 100644
--- a/libavutil/aes_internal.h
+++ b/libavutil/aes_internal.h
@@ -41,5 +41,6 @@ typedef struct AVAES {
} AVAES;
void ff_init_aes_x86(AVAES *a, int decrypt);
+void ff_init_aes_aarch64(AVAES *a, int decrypt);
#endif /* AVUTIL_AES_INTERNAL_H */
--
2.52.0
_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]