https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125856
--- Comment #13 from Paweł Bylica <chfast at gmail dot com> ---
GCC 16 also has a runtime side of this. When both pointers are known to be
16-byte aligned and the size is bounded below 64, -O2 expands memcpy as a byte
loop:
void f(char *d, const char *s, unsigned long n)
{
if (n <= 56)
__builtin_memcpy(__builtin_assume_aligned(d, 16),
__builtin_assume_aligned(s, 16), n);
}
GCC 16.2 -O2:
f:
cmpq $56, %rdx
jbe .L10
.L1:
ret
.L10:
andl $63, %edx
je .L1
xorl %eax, %eax
.L3:
movl %eax, %ecx
addl $1, %eax
movzbl (%rsi,%rcx), %r8d
movb %r8b, (%rdi,%rcx)
cmpl %edx, %eax
jb .L3
ret
GCC 15 calls memcpy. Calling f in a loop with n = 49 is about 6 times slower
than with GCC 15 (Ryzen 9 5950X).
Local arrays get the same byte loop, also when the size is a multiple of 8,
e.g. memcpy(r, x, (n - 1) * 8) with n in [1, 8] and uint64_t r[8], x[9], where
GCC 15 used an 8-byte loop. We hit this in intx (a multi-precision integer
library), where loop distribution creates such a memcpy. Current trunk expands
both with overlapping moves.