>From e372886f67bc5cb49f4bd8e299126b3958f2c1ab Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Sun, 4 Oct 2026 15:04:29 +0000 Subject: [PATCH v3] aarch64: workaround for syscalls with sve SVE registers z0..z31 overlap with the base isa registers d0..d31 (bottom 64bits) and q0..q31 (bottom 128bits). SVE also has an ffr and p0..p15 predicate registers. arm64 linux syscalls preserve q0..q31 but not the top bits of z0..z31 (if they are >128bits) nor ffr nor p0..p15. the c call abi requires preserving d8..d15. so the syscall inline asm requires sve z0..z31, p0..p15 and ffr clobbers, but then d8..d15 is spilled at every syscall call site defeating the purpose of inlining. unfortunately there is no inline asm syntax for specifying clobbers for z0..z31 top bits only. the lack of sve clobbers matters, clang uses p regs around syscalls. so use extern syscalls as a workaround. adding noinline is not enough due to inter-procedural optimizations and gives bigger code. using c code for the extern syscalls is not lto safe within libc.so. the workaround is only applied when musl is built with sve (not lto safe for libc.a). the extern call shuffles arguments so its overhead is minimized. with gcc-16.1.0 -march=armv8-a+sve, libc.so .text increases by 2529 and there are 486 new refs to __syscall[0-6]_asm across *.lo. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128010 https://www.kernel.org/doc/html/latest/arch/arm64/sve.html#system-call-behaviour https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#simd-and-floating-point-registers --- v3: - single __syscall_asm.S - reorder args for less register movs arch/aarch64/syscall_arch.h | 41 ++++++++++++++++++++++ src/internal/aarch64/__syscall_asm.S | 51 ++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/internal/aarch64/__syscall_asm.S diff --git a/arch/aarch64/syscall_arch.h b/arch/aarch64/syscall_arch.h index 504983aa..6c596eef 100644 --- a/arch/aarch64/syscall_arch.h +++ b/arch/aarch64/syscall_arch.h @@ -1,6 +1,46 @@ #define __SYSCALL_LL_E(x) (x) #define __SYSCALL_LL_O(x) (x) +#if __ARM_FEATURE_SVE +/* workaround for no clobber syntax for z reg top bits. */ +long __syscall0_asm(long n); +long __syscall1_asm(long n, long a); +long __syscall2_asm(long n, long a, long b); +long __syscall3_asm(long n, long a, long b, long c); +long __syscall4_asm(long n, long a, long b, long c, long d); +long __syscall5_asm(long n, long a, long b, long c, long d, long e); +long __syscall6_asm(long n, long a, long b, long c, long d, long e, long f); + +/* changed register order for extern calls to better fit the syscall abi. */ +static inline long __syscall0(long n) +{ + return __syscall0_asm(n); +} +static inline long __syscall1(long n, long a) +{ + return __syscall1_asm(a, n); +} +static inline long __syscall2(long n, long a, long b) +{ + return __syscall2_asm(a, b, n); +} +static inline long __syscall3(long n, long a, long b, long c) +{ + return __syscall3_asm(a, b, c, n); +} +static inline long __syscall4(long n, long a, long b, long c, long d) +{ + return __syscall4_asm(a, b, c, d, n); +} +static inline long __syscall5(long n, long a, long b, long c, long d, long e) +{ + return __syscall5_asm(a, b, c, d, e, n); +} +static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f) +{ + return __syscall6_asm(a, b, c, d, e, f, n); +} +#else #define __asm_syscall(...) do { \ __asm__ __volatile__ ( "svc 0" \ : "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \ @@ -70,6 +110,7 @@ static inline long __syscall6(long n, long a, long b, long c, long d, long e, lo register long x5 __asm__("x5") = f; __asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)); } +#endif #define VDSO_USEFUL #define VDSO_CGT_SYM "__kernel_clock_gettime" diff --git a/src/internal/aarch64/__syscall_asm.S b/src/internal/aarch64/__syscall_asm.S new file mode 100644 index 00000000..6e3ba150 --- /dev/null +++ b/src/internal/aarch64/__syscall_asm.S @@ -0,0 +1,51 @@ +#if __ARM_FEATURE_SVE + +.global __syscall0_asm +.type __syscall0_asm,%function +__syscall0_asm: + mov x8, x0 + svc 0 + ret + +.global __syscall1_asm +.type __syscall1_asm,%function +__syscall1_asm: + mov x8, x1 + svc 0 + ret + +.global __syscall2_asm +.type __syscall2_asm,%function +__syscall2_asm: + mov x8, x2 + svc 0 + ret + +.global __syscall3_asm +.type __syscall3_asm,%function +__syscall3_asm: + mov x8, x3 + svc 0 + ret + +.global __syscall4_asm +.type __syscall4_asm,%function +__syscall4_asm: + mov x8, x4 + svc 0 + ret + +.global __syscall5_asm +.type __syscall5_asm,%function +__syscall5_asm: + mov x8, x5 + svc 0 + ret + +.global __syscall6_asm +.type __syscall6_asm,%function +__syscall6_asm: + mov x8, x6 + svc 0 + ret +#endif -- 2.52.0