Commit f924a86518 for ffmpeg

commit f924a86518aabac74db01f733c0023c216dfffff
Author: Martin Storsjö <martin@martin.st>
Date:   Fri Mar 22 10:58:36 2024 +0200

    arm/aarch64: Add helpers for debug printing from assembly

    These currently allow printing GPRs, SIMD vectors and memory.

    GPRs are printed as signed decimal and unsigned hexadecimal.

    SIMD vectors are printed as signed or unsigned decimal, as either
    8, 16 or 32 bit elements.

    Memory can be dumped, interpreted as a stored SIMD vector, by
    specifying a base register and how many vectors to read from that
    address.

    Usage example:

        DUMP_SIMD_U8("input", 4, 5) /* Printing v4-v5 as 8 bit unsigned elments */
        DUMP_SIMD_S16("sum", 6, 6) /* Printing v6 as 16 bit signed elments */
        DUMP_GPR("w,h", 4, 5) /* Printing x4-x5 */

    Which prints:

        3450: input:
        v04.16b = { 201, 1, 245, 0, 139, 0, 225, 1, 33, 1, 254, 0, 41, 2, 212, 1 }
        3451: sum:
        v06.8h = { 20394, -23720, -30625, 32619, -28321, 11312, 30270, 32051 }

        3468: w,h:
        x04 = 16 (0x10)
        x05 = 64 (0x40)

        (or)

        3468: w,h:
        x04 = -2401053092612145136 (0xdeadbeef00000010)
        x05 = -2401053092612145148 (0xdeadbeef00000004)

    For inspecting memory:

        DUMP_MEM_S16("out", x0, 2) /* Print 2 vectors worth of data from x0, as s16 */

    Which can print:

        3523: out:
        v00.8h = { 12340, 18058, 21605, -10848, -32395, -26577, -15192, 26296 }
        v01.8h = { 1998, 1722, 24014, -17100, 2150, -26619, -23289, -4052 }

    Using these macros requires linking in the debug printing helper
    functions, which can be enabled by passing --enable-asm-debug to
    the configure command.

diff --git a/configure b/configure
index ccc46895de..c37cedbbe0 100755
--- a/configure
+++ b/configure
@@ -541,6 +541,7 @@ Developer options (useful when working on FFmpeg itself):
   --disable-shader-compression don't compress shader code even when possible
   --disable-resource-compression don't compress resources even when possible
   --disable-version-tracking don't include the git/release version in the build
+  --enable-asm-debug       include functions for assembly debugging

 NOTE: Object files are built at the place where configure is launched.
 EOF
@@ -2249,6 +2250,7 @@ CONFIG_LIST="
     $PROGRAM_LIST
     $SUBSYSTEM_LIST
     autodetect
+    asm_debug
     fontconfig
     large_tests
     linux_perf
diff --git a/libavutil/aarch64/Makefile b/libavutil/aarch64/Makefile
index b286cfadb0..5508561f3d 100644
--- a/libavutil/aarch64/Makefile
+++ b/libavutil/aarch64/Makefile
@@ -3,6 +3,8 @@ OBJS += aarch64/aes_init.o                                            \
         aarch64/float_dsp_init.o                                      \
         aarch64/tx_float_init.o                                       \

+OBJS-$(CONFIG_ASM_DEBUG) += aarch64/asm_debug.o
+
 ARMV8-OBJS += aarch64/crc.o

 NEON-OBJS += aarch64/aes.o                                            \
diff --git a/libavutil/aarch64/asm.S b/libavutil/aarch64/asm.S
index 665202a808..2cda2ca8bc 100644
--- a/libavutil/aarch64/asm.S
+++ b/libavutil/aarch64/asm.S
@@ -370,3 +370,123 @@ ELF     .size   \name, . - \name
         AARCH64_VALIDATE_LINK_REGISTER
 .endm
 #endif
+
+
+#if CONFIG_ASM_DEBUG
+#undef x18
+.macro call_c func, line, msg, first, last, ptr, offset
+        stp             x29, x30, [sp, #-16]!
+        stp             x27, x28, [sp, #-16]!
+        stp             x25, x26, [sp, #-16]!
+        stp             x23, x24, [sp, #-16]!
+        stp             x21, x22, [sp, #-16]!
+        stp             x19, x20, [sp, #-16]!
+        stp             x17, x18, [sp, #-16]!
+        stp             x15, x16, [sp, #-16]!
+        stp             x13, x14, [sp, #-16]!
+        stp             x11, x12, [sp, #-16]!
+        stp             x9,  x10, [sp, #-16]!
+        stp             x7,  x8,  [sp, #-16]!
+        stp             x5,  x6,  [sp, #-16]!
+        stp             x3,  x4,  [sp, #-16]!
+        stp             x1,  x2,  [sp, #-16]!
+        mrs             x30, nzcv
+        stp             x30, x0,  [sp, #-16]!
+
+        stp             q30, q31, [sp, #-32]!
+        stp             q28, q29, [sp, #-32]!
+        stp             q26, q27, [sp, #-32]!
+        stp             q24, q25, [sp, #-32]!
+        stp             q22, q23, [sp, #-32]!
+        stp             q20, q21, [sp, #-32]!
+        stp             q18, q19, [sp, #-32]!
+        stp             q16, q17, [sp, #-32]!
+        stp             q14, q15, [sp, #-32]!
+        stp             q12, q13, [sp, #-32]!
+        stp             q10, q11, [sp, #-32]!
+        stp             q8,  q9,  [sp, #-32]!
+        stp             q6,  q7,  [sp, #-32]!
+        stp             q4,  q5,  [sp, #-32]!
+        stp             q2,  q3,  [sp, #-32]!
+        stp             q0,  q1,  [sp, #-32]!
+
+        add             x4,  \ptr, #\offset
+        ldr             x0,  =\line
+        adr             x1,  777f
+        mov             x2,  \first
+        mov             x3,  \last
+        bl              X(avpriv_\func)
+
+        ldp             q0,  q1,  [sp], #32
+        ldp             q2,  q3,  [sp], #32
+        ldp             q4,  q5,  [sp], #32
+        ldp             q6,  q7,  [sp], #32
+        ldp             q8,  q9,  [sp], #32
+        ldp             q10, q11, [sp], #32
+        ldp             q12, q13, [sp], #32
+        ldp             q14, q15, [sp], #32
+        ldp             q16, q17, [sp], #32
+        ldp             q18, q19, [sp], #32
+        ldp             q20, q21, [sp], #32
+        ldp             q22, q23, [sp], #32
+        ldp             q24, q25, [sp], #32
+        ldp             q26, q27, [sp], #32
+        ldp             q28, q29, [sp], #32
+        ldp             q30, q31, [sp], #32
+
+        ldp             x30, x0,  [sp], #16
+        msr             nzcv, x30
+        ldp             x1,  x2,  [sp], #16
+        ldp             x3,  x4,  [sp], #16
+        ldp             x5,  x6,  [sp], #16
+        ldp             x7,  x8,  [sp], #16
+        ldp             x9,  x10, [sp], #16
+        ldp             x11, x12, [sp], #16
+        ldp             x13, x14, [sp], #16
+        ldp             x15, x16, [sp], #16
+        ldp             x17, x18, [sp], #16
+        ldp             x19, x20, [sp], #16
+        ldp             x21, x22, [sp], #16
+        ldp             x23, x24, [sp], #16
+        ldp             x25, x26, [sp], #16
+        ldp             x27, x28, [sp], #16
+        ldp             x29, x30, [sp], #16
+.endm
+
+#ifdef __APPLE__
+#define SEP %%
+#else
+#define SEP ;
+#endif
+
+#define CALL_C(func, msg, first, last, ptr, offset) \
+        call_c          func, __LINE__, msg, first, last, ptr, offset SEP \
+        b               778f SEP \
+    777: \
+        .asciz msg SEP \
+        .align 2 SEP \
+    778:
+
+#define DUMP_SIMD_S8(msg, first, last)  CALL_C(dump_simd_s8,  msg, first, last, sp, 0)
+#define DUMP_SIMD_U8(msg, first, last)  CALL_C(dump_simd_u8,  msg, first, last, sp, 0)
+#define DUMP_SIMD_S16(msg, first, last) CALL_C(dump_simd_s16, msg, first, last, sp, 0)
+#define DUMP_SIMD_U16(msg, first, last) CALL_C(dump_simd_u16, msg, first, last, sp, 0)
+#define DUMP_SIMD_S32(msg, first, last) CALL_C(dump_simd_s32, msg, first, last, sp, 0)
+#define DUMP_SIMD_U32(msg, first, last) CALL_C(dump_simd_u32, msg, first, last, sp, 0)
+#define DUMP_SIMD_S64(msg, first, last) CALL_C(dump_simd_s64, msg, first, last, sp, 0)
+#define DUMP_SIMD_U64(msg, first, last) CALL_C(dump_simd_u64, msg, first, last, sp, 0)
+#define DUMP_SIMD_F32(msg, first, last) CALL_C(dump_simd_f32, msg, first, last, sp, 0)
+#define DUMP_SIMD_F64(msg, first, last) CALL_C(dump_simd_f64, msg, first, last, sp, 0)
+#define DUMP_MEM_S8(msg, reg, n)        CALL_C(dump_simd_s8,  msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U8(msg, reg, n)        CALL_C(dump_simd_u8,  msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S16(msg, reg, n)       CALL_C(dump_simd_s16, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U16(msg, reg, n)       CALL_C(dump_simd_u16, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S32(msg, reg, n)       CALL_C(dump_simd_s32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U32(msg, reg, n)       CALL_C(dump_simd_u32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S64(msg, reg, n)       CALL_C(dump_simd_s64, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U64(msg, reg, n)       CALL_C(dump_simd_u64, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_F32(msg, reg, n)       CALL_C(dump_simd_f32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_F64(msg, reg, n)       CALL_C(dump_simd_f64, msg, 0, n-1, reg, 0)
+#define DUMP_GPR(msg, first, last)      CALL_C(dump_gpr,      msg, first, last, sp, 520)
+
+#endif /* CONFIG_ASM_DEBUG */
diff --git a/libavutil/aarch64/asm_debug.c b/libavutil/aarch64/asm_debug.c
new file mode 100644
index 0000000000..105342fc13
--- /dev/null
+++ b/libavutil/aarch64/asm_debug.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright © 2019-2026, Martin Storsjo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <inttypes.h>
+
+void avpriv_dump_simd_s8(int line, const char *msg, int first, int last, const int8_t *ptr);
+void avpriv_dump_simd_s8(int line, const char *msg, int first, int last, const int8_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int8_t *vec = &ptr[16*v];
+        fprintf(stderr, "v%02d.16b = {", v);
+        for (int i = 0; i < 16; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u8(int line, const char *msg, int first, int last, const uint8_t *ptr);
+void avpriv_dump_simd_u8(int line, const char *msg, int first, int last, const uint8_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint8_t *vec = &ptr[16*v];
+        fprintf(stderr, "v%02d.16b = {", v);
+        for (int i = 0; i < 16; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s16(int line, const char *msg, int first, int last, const int16_t *ptr);
+void avpriv_dump_simd_s16(int line, const char *msg, int first, int last, const int16_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int16_t *vec = &ptr[8*v];
+        fprintf(stderr, "v%02d.8h = {", v);
+        for (int i = 0; i < 8; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u16(int line, const char *msg, int first, int last, const uint16_t *ptr);
+void avpriv_dump_simd_u16(int line, const char *msg, int first, int last, const uint16_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint16_t *vec = &ptr[8*v];
+        fprintf(stderr, "v%02d.8h = {", v);
+        for (int i = 0; i < 8; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s32(int line, const char *msg, int first, int last, const int32_t *ptr);
+void avpriv_dump_simd_s32(int line, const char *msg, int first, int last, const int32_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int32_t *vec = &ptr[4*v];
+        fprintf(stderr, "v%02d.4s = {", v);
+        for (int i = 0; i < 4; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u32(int line, const char *msg, int first, int last, const uint32_t *ptr);
+void avpriv_dump_simd_u32(int line, const char *msg, int first, int last, const uint32_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint32_t *vec = &ptr[4*v];
+        fprintf(stderr, "v%02d.4s = {", v);
+        for (int i = 0; i < 4; i++)
+            fprintf(stderr, "%s %u", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s64(int line, const char *msg, int first, int last, const int64_t *ptr);
+void avpriv_dump_simd_s64(int line, const char *msg, int first, int last, const int64_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int64_t *vec = &ptr[2*v];
+        fprintf(stderr, "v%02d.2d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %"PRId64, i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u64(int line, const char *msg, int first, int last, const uint64_t *ptr);
+void avpriv_dump_simd_u64(int line, const char *msg, int first, int last, const uint64_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint64_t *vec = &ptr[2*v];
+        fprintf(stderr, "v%02d.2d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %"PRIu64, i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_f32(int line, const char *msg, int first, int last, const float *ptr);
+void avpriv_dump_simd_f32(int line, const char *msg, int first, int last, const float *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const float *vec = &ptr[4*v];
+        fprintf(stderr, "v%02d.4s = {", v);
+        for (int i = 0; i < 4; i++)
+            fprintf(stderr, "%s %f", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_f64(int line, const char *msg, int first, int last, const double *ptr);
+void avpriv_dump_simd_f64(int line, const char *msg, int first, int last, const double *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const double *vec = &ptr[2*v];
+        fprintf(stderr, "v%02d.2d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %f", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_gpr(int line, const char *msg, int first, int last, const size_t *ptr);
+void avpriv_dump_gpr(int line, const char *msg, int first, int last, const size_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int r = first; r <= last; r++)
+        fprintf(stderr, "x%02d = %"PRId64" (0x%"PRIx64")\n", r, (int64_t) ptr[r], (uint64_t) ptr[r]);
+    fflush(stderr);
+}
diff --git a/libavutil/arm/Makefile b/libavutil/arm/Makefile
index 2988df08ca..cc0d648276 100644
--- a/libavutil/arm/Makefile
+++ b/libavutil/arm/Makefile
@@ -1,6 +1,8 @@
 OBJS += arm/cpu.o                                                       \
         arm/float_dsp_init_arm.o                                        \

+OBJS-$(CONFIG_ASM_DEBUG) += arm/asm_debug.o
+
 ARMV6-OBJS-$(CONFIG_PIXELUTILS) += arm/pixelutils_armv6.o

 VFP-OBJS += arm/float_dsp_init_vfp.o                                    \
diff --git a/libavutil/arm/asm.S b/libavutil/arm/asm.S
index e8a9dabbca..e61b7cdf9e 100644
--- a/libavutil/arm/asm.S
+++ b/libavutil/arm/asm.S
@@ -365,3 +365,66 @@ ELF     .eabi_attribute 28, 1
 #define GLUE(a, b) a ## b
 #define JOIN(a, b) GLUE(a, b)
 #define X(s) JOIN(EXTERN_ASM, s)
+
+
+#if CONFIG_ASM_DEBUG
+.macro call_c func, line, msg, first, last, ptr, offset
+        push            {lr}
+        add             lr,  sp,  #4
+        push            {lr}
+        push            {r0-r12}
+        mrs             lr,  cpsr
+        push            {lr}
+        vpush           {d16-d31}
+        vpush           {d0-d15}
+        add             r0,  \ptr, #\offset
+        // r0 is parameter 5 to the function, pushed on the stack.
+        // r1 is unused, but pushed to keep the stack aligned to 8 bytes.
+        push            {r0-r1}
+        ldr             r0,  =\line
+        adr             r1,  777f
+        mov             r2,  \first
+        mov             r3,  \last
+        bl              X(avpriv_\func)
+        pop             {r0-r1}
+        vpop            {d0-d15}
+        vpop            {d16-d31}
+        pop             {lr}
+        msr             cpsr, lr
+        pop             {r0-r12}
+        add             sp,  sp,  #4
+        pop             {lr}
+.endm
+
+#define CALL_C(func, msg, first, last, ptr, offset) \
+        call_c          func, __LINE__, msg, first, last, ptr, offset; \
+        b               778f; \
+    777: \
+        .asciz msg; \
+        .align 2; \
+        .ltorg; \
+    778:
+
+#define DUMP_SIMD_S8(msg, first, last)  CALL_C(dump_simd_s8,  msg, first, last, sp, 0)
+#define DUMP_SIMD_U8(msg, first, last)  CALL_C(dump_simd_u8,  msg, first, last, sp, 0)
+#define DUMP_SIMD_S16(msg, first, last) CALL_C(dump_simd_s16, msg, first, last, sp, 0)
+#define DUMP_SIMD_U16(msg, first, last) CALL_C(dump_simd_u16, msg, first, last, sp, 0)
+#define DUMP_SIMD_S32(msg, first, last) CALL_C(dump_simd_s32, msg, first, last, sp, 0)
+#define DUMP_SIMD_U32(msg, first, last) CALL_C(dump_simd_u32, msg, first, last, sp, 0)
+#define DUMP_SIMD_S64(msg, first, last) CALL_C(dump_simd_s64, msg, first, last, sp, 0)
+#define DUMP_SIMD_U64(msg, first, last) CALL_C(dump_simd_u64, msg, first, last, sp, 0)
+#define DUMP_SIMD_F32(msg, first, last) CALL_C(dump_simd_f32, msg, first, last, sp, 0)
+#define DUMP_SIMD_F64(msg, first, last) CALL_C(dump_simd_f64, msg, first, last, sp, 0)
+#define DUMP_MEM_S8(msg, reg, n)        CALL_C(dump_simd_s8,  msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U8(msg, reg, n)        CALL_C(dump_simd_u8,  msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S16(msg, reg, n)       CALL_C(dump_simd_s16, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U16(msg, reg, n)       CALL_C(dump_simd_u16, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S32(msg, reg, n)       CALL_C(dump_simd_s32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U32(msg, reg, n)       CALL_C(dump_simd_u32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_S64(msg, reg, n)       CALL_C(dump_simd_s64, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_U64(msg, reg, n)       CALL_C(dump_simd_u64, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_F32(msg, reg, n)       CALL_C(dump_simd_f32, msg, 0, n-1, reg, 0)
+#define DUMP_MEM_F64(msg, reg, n)       CALL_C(dump_simd_f64, msg, 0, n-1, reg, 0)
+#define DUMP_GPR(msg, first, last)      CALL_C(dump_gpr,      msg, first, last, sp, 260)
+
+#endif /* CONFIG_ASM_DEBUG */
diff --git a/libavutil/arm/asm_debug.c b/libavutil/arm/asm_debug.c
new file mode 100644
index 0000000000..f9fd3d2700
--- /dev/null
+++ b/libavutil/arm/asm_debug.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright © 2019-2026, Martin Storsjo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <inttypes.h>
+
+void avpriv_dump_simd_s8(int line, const char *msg, int first, int last, const int8_t *ptr);
+void avpriv_dump_simd_s8(int line, const char *msg, int first, int last, const int8_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int8_t *vec = &ptr[8*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 8; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u8(int line, const char *msg, int first, int last, const uint8_t *ptr);
+void avpriv_dump_simd_u8(int line, const char *msg, int first, int last, const uint8_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint8_t *vec = &ptr[8*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 8; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s16(int line, const char *msg, int first, int last, const int16_t *ptr);
+void avpriv_dump_simd_s16(int line, const char *msg, int first, int last, const int16_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int16_t *vec = &ptr[4*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 4; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u16(int line, const char *msg, int first, int last, const uint16_t *ptr);
+void avpriv_dump_simd_u16(int line, const char *msg, int first, int last, const uint16_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint16_t *vec = &ptr[4*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 4; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s32(int line, const char *msg, int first, int last, const int32_t *ptr);
+void avpriv_dump_simd_s32(int line, const char *msg, int first, int last, const int32_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int32_t *vec = &ptr[2*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %d", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u32(int line, const char *msg, int first, int last, const uint32_t *ptr);
+void avpriv_dump_simd_u32(int line, const char *msg, int first, int last, const uint32_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint32_t *vec = &ptr[2*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %u", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_s64(int line, const char *msg, int first, int last, const int64_t *ptr);
+void avpriv_dump_simd_s64(int line, const char *msg, int first, int last, const int64_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const int64_t *vec = &ptr[1*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 1; i++)
+            fprintf(stderr, "%s %"PRId64, i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_u64(int line, const char *msg, int first, int last, const uint64_t *ptr);
+void avpriv_dump_simd_u64(int line, const char *msg, int first, int last, const uint64_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const uint64_t *vec = &ptr[1*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 1; i++)
+            fprintf(stderr, "%s %"PRIu64, i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_f32(int line, const char *msg, int first, int last, const float *ptr);
+void avpriv_dump_simd_f32(int line, const char *msg, int first, int last, const float *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const float *vec = &ptr[2*v];
+        fprintf(stderr, "d%02d = {", v);
+        for (int i = 0; i < 2; i++)
+            fprintf(stderr, "%s %f", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_simd_f64(int line, const char *msg, int first, int last, const double *ptr);
+void avpriv_dump_simd_f64(int line, const char *msg, int first, int last, const double *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int v = first; v <= last; v++) {
+        const double *vec = &ptr[1*v];
+        fprintf(stderr, "v%02d = {", v);
+        for (int i = 0; i < 1; i++)
+            fprintf(stderr, "%s %f", i > 0 ? "," : "", vec[i]);
+        fprintf(stderr, " }\n");
+    }
+    fflush(stderr);
+}
+
+void avpriv_dump_gpr(int line, const char *msg, int first, int last, const size_t *ptr);
+void avpriv_dump_gpr(int line, const char *msg, int first, int last, const size_t *ptr)
+{
+    fprintf(stderr, "%d: %s:\n", line, msg);
+    for (int r = first; r <= last; r++)
+        if (r < 13)
+            fprintf(stderr, "r%02d = %"PRId32" (0x%"PRIx32")\n", r, (int32_t) ptr[r], (uint32_t) ptr[r]);
+        else
+            fprintf(stderr, "%s  = %"PRId32" (0x%"PRIx32")\n", r == 13 ? "sp" : "lr", (int32_t) ptr[r], (uint32_t) ptr[r]);
+    fflush(stderr);
+}