Commit 38519047ce for ffmpeg
commit 38519047cedcb2abc1143adfe162ff88cf57b0d3
Author: Kieran Kunhya <kierank@obe.tv>
Date: Sat Nov 14 01:40:17 2026 +0000
avutil/cpu: add AVX-VNNI flag
AVX-VNNI is the VEX-coded form of the VNNI dot products (vpdpbusd(s),
vpdpwssd(s)) on xmm and ymm registers. Intel Alder Lake and later and
AMD Zen 5 have it, and Intel client CPUs have it without AVX-512, so
AV_CPU_FLAG_AVX512ICL cannot stand in for it.
It is reported in CPUID.(EAX=7,ECX=1):EAX bit 4, so ff_cpu_cpuid() gets a
subleaf argument. Being VEX coded, it also needs the OS to save the YMM
state, which is checked as for AVX2. The flag reports exactly what CPUID
says: CPUs with AVX512-VNNI but without AVX-VNNI, such as Ice Lake, do
not set it.
configure gets an avxvnni extension, depending on avx2, and checks that
the assembler accepts {vex} vpdpwssd.
diff --git a/configure b/configure
index 48a506bc2f..78526ae9af 100755
--- a/configure
+++ b/configure
@@ -471,6 +471,7 @@ Optimization options (experts only):
--disable-avx2 disable AVX2 optimizations
--disable-avx512 disable AVX-512 optimizations
--disable-avx512icl disable AVX-512ICL optimizations
+ --disable-avxvnni disable AVX-VNNI optimizations
--disable-aesni disable AESNI optimizations
--disable-clmul disable CLMUL optimizations
--disable-armv5te disable armv5te optimizations
@@ -2359,6 +2360,7 @@ ARCH_EXT_LIST_X86_SIMD="
avx2
avx512
avx512icl
+ avxvnni
fma3
fma4
mmx
@@ -2985,6 +2987,7 @@ fma4_deps="avx"
avx2_deps="avx"
avx512_deps="avx2"
avx512icl_deps="avx512"
+avxvnni_deps="avx2"
mmx_external_deps="x86asm"
mmx_inline_deps="inline_asm x86"
@@ -6762,6 +6765,7 @@ elif enabled x86; then
enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0"
enabled avx512icl && check_x86asm avx512icl_external "vpdpwssds zmm31{k1}{z}, zmm29, zmm28"
enabled avx2 && check_x86asm avx2_external "vextracti128 xmm0, ymm0, 0"
+ enabled avxvnni && check_x86asm avxvnni_external "{vex} vpdpwssd ymm0, ymm1, ymm2"
enabled xop && check_x86asm xop_external "vpmacsdd xmm0, xmm1, xmm2, xmm3"
enabled fma4 && check_x86asm fma4_external "vfmaddps ymm0, ymm1, ymm2, ymm3"
enabled avx && check_x86asm avx_external "vpermilpd ymm0, ymm1, 0x72"
@@ -8347,6 +8351,7 @@ if enabled x86; then
echo "AVX2 enabled ${avx2-no}"
echo "AVX-512 enabled ${avx512-no}"
echo "AVX-512ICL enabled ${avx512icl-no}"
+ echo "AVX-VNNI enabled ${avxvnni-no}"
echo "XOP enabled ${xop-no}"
echo "FMA3 enabled ${fma3-no}"
echo "FMA4 enabled ${fma4-no}"
diff --git a/doc/APIchanges b/doc/APIchanges
index 1096fde9c9..3eac08a054 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
API changes, most recent first:
+2026-11-xx - xxxxxxxxxx - lavu 61.21.100 - cpu.h
+ Add AV_CPU_FLAG_AVXVNNI.
+
2026-11-xx - xxxxxxxxxx - lavf 63.20.101 - avformat.h
With AVSEEK_FLAG_PREROLL set, avformat_seek_file() and av_seek_frame()
move the target back by the audio pre-roll, and discard the pre-roll
diff --git a/libavutil/cpu.c b/libavutil/cpu.c
index 6994166147..cbefdddf30 100644
--- a/libavutil/cpu.c
+++ b/libavutil/cpu.c
@@ -152,6 +152,7 @@ int av_parse_cpu_caps(unsigned *flags, const char *s)
{ "clmul", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_CLMUL }, .unit = "flags" },
{ "avx512" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_AVX512 }, .unit = "flags" },
{ "avx512icl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_AVX512ICL }, .unit = "flags" },
+ { "avxvnni" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_AVXVNNI }, .unit = "flags" },
{ "slowgather", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_CPU_FLAG_SLOW_GATHER }, .unit = "flags" },
#define CPU_FLAG_P2 AV_CPU_FLAG_CMOV | AV_CPU_FLAG_MMX
diff --git a/libavutil/cpu.h b/libavutil/cpu.h
index 5db34c2e2a..49da37a4d9 100644
--- a/libavutil/cpu.h
+++ b/libavutil/cpu.h
@@ -59,6 +59,7 @@
#define AV_CPU_FLAG_BMI2 0x40000 ///< Bit Manipulation Instruction Set 2
#define AV_CPU_FLAG_AVX512 0x100000 ///< AVX-512 functions: requires OS support even if YMM/ZMM registers aren't used
#define AV_CPU_FLAG_AVX512ICL 0x200000 ///< F/CD/BW/DQ/VL/VNNI/IFMA/VBMI/VBMI2/VPOPCNTDQ/BITALG/GFNI/VAES/VPCLMULQDQ
+#define AV_CPU_FLAG_AVXVNNI 0x800000 ///< AVX-VNNI: VEX-coded VNNI, requires OS support for YMM registers
#define AV_CPU_FLAG_SLOW_GATHER 0x2000000 ///< CPU has slow gathers.
#define AV_CPU_FLAG_ALTIVEC 0x0001 ///< standard
diff --git a/libavutil/tests/cpu.c b/libavutil/tests/cpu.c
index 379feffe70..43ae7124b0 100644
--- a/libavutil/tests/cpu.c
+++ b/libavutil/tests/cpu.c
@@ -98,6 +98,7 @@ static const struct {
{ AV_CPU_FLAG_CLMUL, "clmul" },
{ AV_CPU_FLAG_AVX512, "avx512" },
{ AV_CPU_FLAG_AVX512ICL, "avx512icl" },
+ { AV_CPU_FLAG_AVXVNNI, "avxvnni" },
{ AV_CPU_FLAG_SLOW_GATHER, "slowgather" },
#elif ARCH_LOONGARCH
{ AV_CPU_FLAG_LSX, "lsx" },
diff --git a/libavutil/version.h b/libavutil/version.h
index cbdd0ad029..3a39a0b47c 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 61
-#define LIBAVUTIL_VERSION_MINOR 20
+#define LIBAVUTIL_VERSION_MINOR 21
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
diff --git a/libavutil/x86/cpu.c b/libavutil/x86/cpu.c
index 5563f6cc3b..5e1c3b4783 100644
--- a/libavutil/x86/cpu.c
+++ b/libavutil/x86/cpu.c
@@ -30,8 +30,8 @@
#if HAVE_X86ASM
-#define cpuid(index, eax, ebx, ecx, edx) \
- ff_cpu_cpuid(index, &eax, &ebx, &ecx, &edx)
+#define cpuid_count(index, subleaf, eax, ebx, ecx, edx) \
+ ff_cpu_cpuid(index, subleaf, &eax, &ebx, &ecx, &edx)
#define xgetbv(index, eax, edx) \
ff_cpu_xgetbv(index, &eax, &edx)
@@ -39,13 +39,13 @@
#elif HAVE_INLINE_ASM
/* ebx saving is necessary for PIC. gcc seems unable to see it alone */
-#define cpuid(index, eax, ebx, ecx, edx) \
+#define cpuid_count(index, subleaf, eax, ebx, ecx, edx) \
__asm__ volatile ( \
"mov %%"FF_REG_b", %%"FF_REG_S" \n\t" \
"cpuid \n\t" \
"xchg %%"FF_REG_b", %%"FF_REG_S \
: "=a" (eax), "=S" (ebx), "=c" (ecx), "=d" (edx) \
- : "0" (index), "2"(0))
+ : "0" (index), "2" (subleaf))
#define xgetbv(index, eax, edx) \
__asm__ (".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c" (index))
@@ -62,6 +62,11 @@
#endif /* HAVE_INLINE_ASM */
+#ifdef cpuid_count
+#define cpuid(index, eax, ebx, ecx, edx) \
+ cpuid_count(index, 0, eax, ebx, ecx, edx)
+#endif
+
#if ARCH_X86_64
#define cpuid_test() 1
@@ -168,6 +173,15 @@ int ff_get_cpu_flags_x86(void)
if (ebx & 0x00000100)
rval |= AV_CPU_FLAG_BMI2;
}
+#if HAVE_AVXVNNI
+ /* AVX-VNNI is VEX coded, so it needs the OS YMM state, like AVX2.
+ * eax of subleaf 0 is the highest subleaf. */
+ if ((rval & AV_CPU_FLAG_AVX) && eax >= 1) {
+ cpuid_count(7, 1, eax, ebx, ecx, edx);
+ if (eax & 0x00000010)
+ rval |= AV_CPU_FLAG_AVXVNNI;
+ }
+#endif /* HAVE_AVXVNNI */
}
cpuid(0x80000000, max_ext_level, ebx, ecx, edx);
diff --git a/libavutil/x86/cpu.h b/libavutil/x86/cpu.h
index af081b2ed8..67e208ecbc 100644
--- a/libavutil/x86/cpu.h
+++ b/libavutil/x86/cpu.h
@@ -46,6 +46,7 @@
#define X86_AESNI(flags) CPUEXT(flags, AESNI)
#define X86_CLMUL(flags) CPUEXT(flags, CLMUL)
#define X86_AVX512(flags) CPUEXT(flags, AVX512)
+#define X86_AVXVNNI(flags) CPUEXT(flags, AVXVNNI)
#define EXTERNAL_MMX(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, MMX)
#define EXTERNAL_MMXEXT(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, MMXEXT)
@@ -76,6 +77,7 @@
#define EXTERNAL_CLMUL(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, CLMUL)
#define EXTERNAL_AVX512(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, AVX512)
#define EXTERNAL_AVX512ICL(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, AVX512ICL)
+#define EXTERNAL_AVXVNNI(flags) CPUEXT_SUFFIX(flags, _EXTERNAL, AVXVNNI)
#define INLINE_MMX(flags) CPUEXT_SUFFIX(flags, _INLINE, MMX)
#define INLINE_MMXEXT(flags) CPUEXT_SUFFIX(flags, _INLINE, MMXEXT)
@@ -100,7 +102,7 @@
#define INLINE_AVX2(flags) CPUEXT_SUFFIX(flags, _INLINE, AVX2)
#define INLINE_AESNI(flags) CPUEXT_SUFFIX(flags, _INLINE, AESNI)
-void ff_cpu_cpuid(int index, int *eax, int *ebx, int *ecx, int *edx);
+void ff_cpu_cpuid(int index, int subleaf, int *eax, int *ebx, int *ecx, int *edx);
void ff_cpu_xgetbv(int op, int *eax, int *edx);
int ff_cpu_cpuid_test(void);
diff --git a/libavutil/x86/cpuid.asm b/libavutil/x86/cpuid.asm
index 766f77fcdf..dd7383b6ed 100644
--- a/libavutil/x86/cpuid.asm
+++ b/libavutil/x86/cpuid.asm
@@ -26,16 +26,16 @@
SECTION .text
;-----------------------------------------------------------------------------
-; void ff_cpu_cpuid(int index, int *eax, int *ebx, int *ecx, int *edx)
+; void ff_cpu_cpuid(int index, int subleaf, int *eax, int *ebx, int *ecx, int *edx)
;-----------------------------------------------------------------------------
-cglobal cpu_cpuid, 5,7
+cglobal cpu_cpuid, 6,7
push rbx
+ push r5
push r4
push r3
push r2
- push r1
mov eax, r0d
- xor ecx, ecx
+ mov ecx, r1d
cpuid
pop r4
mov [r4], eax
diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c
index e50d2885a3..4d15a7b7d1 100644
--- a/tests/checkasm/checkasm.c
+++ b/tests/checkasm/checkasm.c
@@ -386,6 +386,7 @@ static const CheckasmCpuInfo cpuflags[] = {
{ "FMA3", "fma3", AV_CPU_FLAG_FMA3 },
{ "FMA4", "fma4", AV_CPU_FLAG_FMA4 },
{ "AVX2", "avx2", AV_CPU_FLAG_AVX2 },
+ { "AVX-VNNI", "avxvnni", AV_CPU_FLAG_AVXVNNI },
{ "AVX-512", "avx512", AV_CPU_FLAG_AVX512 },
{ "AVX-512ICL", "avx512icl", AV_CPU_FLAG_AVX512ICL },
#elif ARCH_LOONGARCH