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.

Reply via email to