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