PR #22527 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22527
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22527.patch

The PR depends on https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22526


>From 9fe980e0089dd532ff7a8a177f3f0be904fc6cc3 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 17 Mar 2026 16:34:26 +0800
Subject: [PATCH 1/4] checkasm/aarch64: fix operator precedence bug in
 ARG_STACK

The expression
  ((8*(MAX_ARGS - 8) + 15) & ~15 + 16)
is equal to
  ((8*(MAX_ARGS - 8) + 15) & (~15 + 16))

which evaluated to zero.
---
 tests/checkasm/aarch64/checkasm.S | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/checkasm/aarch64/checkasm.S 
b/tests/checkasm/aarch64/checkasm.S
index 6d3c738801..4b2db86a9e 100644
--- a/tests/checkasm/aarch64/checkasm.S
+++ b/tests/checkasm/aarch64/checkasm.S
@@ -68,7 +68,7 @@ function checkasm_stack_clobber, export=1
 endfunc
 
 // + 16 for stack canary reference
-#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15 + 16)
+#define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16)
 
 function checkasm_checked_call, export=1
         stp             x29, x30, [sp, #-16]!
-- 
2.52.0


>From eff960998fdc5820f903ecf636dacc63ac17f76e Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 17 Mar 2026 15:59:33 +0800
Subject: [PATCH 2/4] checkasm/aarch64: eliminates serial data dependencies on
 sp

Signed-off-by: Zhao Zhili <[email protected]>
---
 tests/checkasm/aarch64/checkasm.S | 46 ++++++++++++++++---------------
 1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/tests/checkasm/aarch64/checkasm.S 
b/tests/checkasm/aarch64/checkasm.S
index 4b2db86a9e..2ec6d78240 100644
--- a/tests/checkasm/aarch64/checkasm.S
+++ b/tests/checkasm/aarch64/checkasm.S
@@ -71,17 +71,18 @@ endfunc
 #define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16)
 
 function checkasm_checked_call, export=1
-        stp             x29, x30, [sp, #-16]!
-        mov             x29, sp
-        stp             x19, x20, [sp, #-16]!
-        stp             x21, x22, [sp, #-16]!
-        stp             x23, x24, [sp, #-16]!
-        stp             x25, x26, [sp, #-16]!
-        stp             x27, x28, [sp, #-16]!
-        stp             d8,  d9,  [sp, #-16]!
-        stp             d10, d11, [sp, #-16]!
-        stp             d12, d13, [sp, #-16]!
-        stp             d14, d15, [sp, #-16]!
+        sub             sp,  sp,  #160
+        stp             x29, x30, [sp, #144]
+        add             x29, sp,  #144
+        stp             x19, x20, [sp, #128]
+        stp             x21, x22, [sp, #112]
+        stp             x23, x24, [sp, #96]
+        stp             x25, x26, [sp, #80]
+        stp             x27, x28, [sp, #64]
+        stp             d8,  d9,  [sp, #48]
+        stp             d10, d11, [sp, #32]
+        stp             d12, d13, [sp, #16]
+        stp             d14, d15, [sp]
 
         movrel          x9, register_init
         ldp             d8,  d9,  [x9], #16
@@ -183,16 +184,17 @@ function checkasm_checked_call, export=1
 1:
         bl              X(checkasm_fail_func)
 0:
-        ldp             x0,  x1,  [sp], #16
-        ldp             d14, d15, [sp], #16
-        ldp             d12, d13, [sp], #16
-        ldp             d10, d11, [sp], #16
-        ldp             d8,  d9,  [sp], #16
-        ldp             x27, x28, [sp], #16
-        ldp             x25, x26, [sp], #16
-        ldp             x23, x24, [sp], #16
-        ldp             x21, x22, [sp], #16
-        ldp             x19, x20, [sp], #16
-        ldp             x29, x30, [sp], #16
+        ldp             x0,  x1,  [sp]
+        ldp             d14, d15, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d10, d11, [sp, #48]
+        ldp             d8,  d9,  [sp, #64]
+        ldp             x27, x28, [sp, #80]
+        ldp             x25, x26, [sp, #96]
+        ldp             x23, x24, [sp, #112]
+        ldp             x21, x22, [sp, #128]
+        ldp             x19, x20, [sp, #144]
+        ldp             x29, x30, [sp, #160]
+        add             sp,  sp,  #176
         ret
 endfunc
-- 
2.52.0


>From 2852c9b9c5b90b9adb23e0af10c931132d32a278 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 17 Mar 2026 19:08:41 +0800
Subject: [PATCH 3/4] checkasm/aarch64: extract macros for code reuse

Prepare for adding an alternative code path for Apple platform.
No functional change.

Signed-off-by: Zhao Zhili <[email protected]>
---
 tests/checkasm/aarch64/checkasm.S | 189 +++++++++++++++++-------------
 1 file changed, 105 insertions(+), 84 deletions(-)

diff --git a/tests/checkasm/aarch64/checkasm.S 
b/tests/checkasm/aarch64/checkasm.S
index 2ec6d78240..24c52b2e2d 100644
--- a/tests/checkasm/aarch64/checkasm.S
+++ b/tests/checkasm/aarch64/checkasm.S
@@ -56,6 +56,107 @@ endconst
 
 #define CLOBBER_STACK ((8*MAX_ARGS + 15) & ~15)
 
+.macro save_callee_regs
+        sub             sp,  sp,  #160
+        stp             x29, x30, [sp, #144]
+        add             x29, sp,  #144
+        stp             x19, x20, [sp, #128]
+        stp             x21, x22, [sp, #112]
+        stp             x23, x24, [sp, #96]
+        stp             x25, x26, [sp, #80]
+        stp             x27, x28, [sp, #64]
+        stp             d8,  d9,  [sp, #48]
+        stp             d10, d11, [sp, #32]
+        stp             d12, d13, [sp, #16]
+        stp             d14, d15, [sp]
+.endm
+
+.macro load_callee_init
+        movrel          x9, register_init
+        ldp             d8,  d9,  [x9], #16
+        ldp             d10, d11, [x9], #16
+        ldp             d12, d13, [x9], #16
+        ldp             d14, d15, [x9], #16
+        ldp             x19, x20, [x9], #16
+        ldp             x21, x22, [x9], #16
+        ldp             x23, x24, [x9], #16
+        ldp             x25, x26, [x9], #16
+        ldp             x27, x28, [x9], #16
+.endm
+
+.macro fill_garbage_regs
+        movrel          x9, register_init
+        ldp             x10, x11, [x9], #32
+        ldp             x12, x13, [x9], #32
+        ldp             x14, x15, [x9], #32
+        ldp             x16, x17, [x9], #32
+        ldp             x8,  x9,  [x9]
+.endm
+
+.macro check_reg_neon reg1, reg2
+        ldr             q1,  [x9], #16
+        uzp1            v2.2d,  v\reg1\().2d, v\reg2\().2d
+        eor             v1.16b, v1.16b, v2.16b
+        orr             v3.16b, v3.16b, v1.16b
+.endm
+
+.macro check_reg reg1, reg2
+        ldp             x0,  x1,  [x9], #16
+        eor             x0,  x0,  \reg1
+        eor             x1,  x1,  \reg2
+        orr             x3,  x3,  x0
+        orr             x3,  x3,  x1
+.endm
+
+// verify_and_return: check callee-saved registers, report errors, restore and 
return.
+// Entry: x2 = canary current, x3 = canary reference (inverted)
+//        [sp] = saved x0/x1 (return values)
+//        [sp, #16..#160] = saved callee-saved regs
+.macro verify_and_return
+        mvn             x3,  x3
+        cmp             x2,  x3
+        b.ne            2f
+
+        movrel          x9, register_init
+        movi            v3.8h,  #0
+
+        check_reg_neon  8,  9
+        check_reg_neon  10, 11
+        check_reg_neon  12, 13
+        check_reg_neon  14, 15
+        uqxtn           v3.8b,  v3.8h
+        umov            x3,  v3.d[0]
+
+        check_reg       x19, x20
+        check_reg       x21, x22
+        check_reg       x23, x24
+        check_reg       x25, x26
+        check_reg       x27, x28
+
+        cbz             x3,  0f
+
+        movrel          x0, error_message_register
+        b               1f
+2:
+        movrel          x0, error_message_stack
+1:
+        bl              X(checkasm_fail_func)
+0:
+        ldp             x0,  x1,  [sp]
+        ldp             d14, d15, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d10, d11, [sp, #48]
+        ldp             d8,  d9,  [sp, #64]
+        ldp             x27, x28, [sp, #80]
+        ldp             x25, x26, [sp, #96]
+        ldp             x23, x24, [sp, #112]
+        ldp             x21, x22, [sp, #128]
+        ldp             x19, x20, [sp, #144]
+        ldp             x29, x30, [sp, #160]
+        add             sp,  sp,  #176
+        ret
+.endm
+
 function checkasm_stack_clobber, export=1
         mov             x3,  sp
         mov             x2,  #CLOBBER_STACK
@@ -71,29 +172,8 @@ endfunc
 #define ARG_STACK (((8*(MAX_ARGS - 8) + 15) & ~15) + 16)
 
 function checkasm_checked_call, export=1
-        sub             sp,  sp,  #160
-        stp             x29, x30, [sp, #144]
-        add             x29, sp,  #144
-        stp             x19, x20, [sp, #128]
-        stp             x21, x22, [sp, #112]
-        stp             x23, x24, [sp, #96]
-        stp             x25, x26, [sp, #80]
-        stp             x27, x28, [sp, #64]
-        stp             d8,  d9,  [sp, #48]
-        stp             d10, d11, [sp, #32]
-        stp             d12, d13, [sp, #16]
-        stp             d14, d15, [sp]
-
-        movrel          x9, register_init
-        ldp             d8,  d9,  [x9], #16
-        ldp             d10, d11, [x9], #16
-        ldp             d12, d13, [x9], #16
-        ldp             d14, d15, [x9], #16
-        ldp             x19, x20, [x9], #16
-        ldp             x21, x22, [x9], #16
-        ldp             x23, x24, [x9], #16
-        ldp             x25, x26, [x9], #16
-        ldp             x27, x28, [x9], #16
+        save_callee_regs
+        load_callee_init
 
         sub             sp,  sp,  #ARG_STACK
 .equ pos, 0
@@ -106,12 +186,7 @@ function checkasm_checked_call, export=1
 
         // Fill x8-x17 with garbage. This doesn't have to be preserved,
         // but avoids relying on them having any particular value.
-        movrel          x9, register_init
-        ldp             x10, x11, [x9], #32
-        ldp             x12, x13, [x9], #32
-        ldp             x14, x15, [x9], #32
-        ldp             x16, x17, [x9], #32
-        ldp             x8,  x9,  [x9]
+        fill_garbage_regs
 
         // For stack overflows, the callee is free to overwrite the parameters
         // that were passed on the stack (if any), so we can only check after
@@ -142,59 +217,5 @@ function checkasm_checked_call, export=1
         add             sp,  sp,  #ARG_STACK
         stp             x0,  x1,  [sp, #-16]!
 
-        mvn             x3,  x3
-        cmp             x2,  x3
-        b.ne            2f
-
-        movrel          x9, register_init
-        movi            v3.8h,  #0
-
-.macro check_reg_neon reg1, reg2
-        ldr             q1,  [x9], #16
-        uzp1            v2.2d,  v\reg1\().2d, v\reg2\().2d
-        eor             v1.16b, v1.16b, v2.16b
-        orr             v3.16b, v3.16b, v1.16b
-.endm
-        check_reg_neon  8,  9
-        check_reg_neon  10, 11
-        check_reg_neon  12, 13
-        check_reg_neon  14, 15
-        uqxtn           v3.8b,  v3.8h
-        umov            x3,  v3.d[0]
-
-.macro check_reg reg1, reg2
-        ldp             x0,  x1,  [x9], #16
-        eor             x0,  x0,  \reg1
-        eor             x1,  x1,  \reg2
-        orr             x3,  x3,  x0
-        orr             x3,  x3,  x1
-.endm
-        check_reg       x19, x20
-        check_reg       x21, x22
-        check_reg       x23, x24
-        check_reg       x25, x26
-        check_reg       x27, x28
-
-        cbz             x3,  0f
-
-        movrel          x0, error_message_register
-        b               1f
-2:
-        movrel          x0, error_message_stack
-1:
-        bl              X(checkasm_fail_func)
-0:
-        ldp             x0,  x1,  [sp]
-        ldp             d14, d15, [sp, #16]
-        ldp             d12, d13, [sp, #32]
-        ldp             d10, d11, [sp, #48]
-        ldp             d8,  d9,  [sp, #64]
-        ldp             x27, x28, [sp, #80]
-        ldp             x25, x26, [sp, #96]
-        ldp             x23, x24, [sp, #112]
-        ldp             x21, x22, [sp, #128]
-        ldp             x19, x20, [sp, #144]
-        ldp             x29, x30, [sp, #160]
-        add             sp,  sp,  #176
-        ret
+        verify_and_return
 endfunc
-- 
2.52.0


>From f0411fa023f82ace8fa0d6846f3dad4877aa39d0 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Tue, 17 Mar 2026 19:09:31 +0800
Subject: [PATCH 4/4] checkasm/aarch64: add Apple ARM64 support using wrapper
 pattern

Apple's ARM64 ABI passes variadic arguments on the stack rather
than in registers, so the existing checkasm_checked_call approach
(which relies on variadic register passing) doesn't work on macOS.

Use a wrapper pattern (similar to RISC-V): the C side calls
checkasm_set_function() to store the target function pointer, then
calls checkasm_checked_call_wrapper() with a normal (non-variadic)
prototype. The wrapper loads the stored function pointer and calls
it after the usual register init and stack clobber setup.

The stored function pointer uses a static global variable for now.
Thread-local storage can be considered in the
future if needed.
---
 tests/checkasm/aarch64/checkasm.S | 68 +++++++++++++++++++++++++++++++
 tests/checkasm/checkasm.h         | 13 +++++-
 2 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/tests/checkasm/aarch64/checkasm.S 
b/tests/checkasm/aarch64/checkasm.S
index 24c52b2e2d..cb927882b0 100644
--- a/tests/checkasm/aarch64/checkasm.S
+++ b/tests/checkasm/aarch64/checkasm.S
@@ -157,6 +157,8 @@ endconst
         ret
 .endm
 
+#if !defined(__APPLE__)
+
 function checkasm_stack_clobber, export=1
         mov             x3,  sp
         mov             x2,  #CLOBBER_STACK
@@ -219,3 +221,69 @@ function checkasm_checked_call, export=1
 
         verify_and_return
 endfunc
+
+#else /* __APPLE__ */
+
+        .pushsection __DATA,__bss
+        .align  3
+.Lstored_func:
+        .space  8
+        .popsection
+
+function checkasm_set_function, export=1
+        movrel          x9, .Lstored_func
+        str             x0, [x9]
+        ret
+endfunc
+
+function checkasm_get_wrapper, export=1
+        movrel          x0, checkasm_checked_call_wrapper
+        ret
+endfunc
+
+// clobber area + 8-byte canary reference, aligned to 16
+#define ARG_STACK ((CLOBBER_STACK + 8 + 15) & ~15)
+
+function checkasm_checked_call_wrapper
+        save_callee_regs
+        load_callee_init
+
+        sub             sp,  sp, #ARG_STACK
+        mov             x9,  #CLOBBER_STACK
+        mov             x10, sp
+        mov             x11, #0xbeef
+        movk            x11, #0xdead, lsl #16
+        movk            x11, #0xbeef, lsl #32
+        movk            x11, #0xdead, lsl #48
+1:
+        stp             x11, x11, [x10], #16
+        subs            x9,  x9,  #16
+        b.gt            1b
+
+.equ pos, 0
+.rept MAX_ARGS-8
+        ldr             x9,  [x29, #16 + pos]
+        str             x9,  [sp, #pos]
+.equ pos, pos + 8
+.endr
+
+        ldr             x9,  [sp, #(MAX_ARGS-8)*8]
+        mvn             x9,  x9
+        str             x9,  [sp, #(ARG_STACK-8)]
+
+        fill_garbage_regs
+
+        movrel          x16, .Lstored_func
+        ldr             x16, [x16]
+        blr             x16
+
+        ldr             x2,  [sp, #(MAX_ARGS-8)*8]
+        ldr             x3,  [sp, #(ARG_STACK-8)]
+
+        add             sp,  sp,  #(ARG_STACK)
+        stp             x0,  x1,  [sp, #-16]!
+
+        verify_and_return
+endfunc
+
+#endif /* __APPLE__ */
diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h
index 25654b20ba..e0b4be5273 100644
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -295,7 +295,18 @@ extern void (*checkasm_checked_call)(void *func, int 
dummy, ...);
     (checkasm_set_signal_handler_state(1),\
      checked_call((func), 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 
0, 0, 0, 0));\
     checkasm_set_signal_handler_state(0)
-#elif ARCH_AARCH64 && !defined(__APPLE__)
+#elif ARCH_AARCH64 && defined(__APPLE__)
+/* Apple ARM64: Use wrapper pattern (like RISC-V) to avoid variadic ABI issues.
+ * Apple's ABI passes variadic arguments on the stack, not in registers. */
+void checkasm_set_function(void *);
+void *checkasm_get_wrapper(void);
+#define declare_new(ret, ...) \
+    ret (*checked_call)(__VA_ARGS__) = checkasm_get_wrapper();
+#define checkasm_call_checked(func, ...) \
+    (checkasm_set_signal_handler_state(1),\
+     checkasm_set_function(func), checked_call(__VA_ARGS__));\
+    checkasm_set_signal_handler_state(0)
+#elif ARCH_AARCH64
 void checkasm_stack_clobber(uint64_t clobber, ...);
 void checkasm_checked_call(void *func, ...);
 #define declare_new(ret, ...) ret (*checked_call)(void *, int, int, int, int, 
int, int, int, __VA_ARGS__,\
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to