https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128000
Bug ID: 128000
Summary: AArch64: 16-byte __builtin_memcpy uses ldp/stp of X
registers instead of ldr/str of a Q register
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Keywords: missed-optimization
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: tomasz.marzec at snowflake dot com
Target Milestone: ---
Host: aarch64-unknown-linux-gnu
Target: aarch64-unknown-linux-gnu
Build: aarch64-unknown-linux-gnu
Created attachment 65891
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65891&action=edit
preprocessed testcase
With -O2 -mcpu=neoverse-v2, a 16-byte __builtin_memcpy is expanded to an
ldp/stp pair of X registers. A hand-written single Q-register ldr/str is about
5-10% faster on Neoverse-V2 for independent 16-byte copies, so with
-mcpu=neoverse-v2 I'd expect GCC to choose it here.
In a separate test where the copied data is loaded back immediately, the
X-register pair was faster (about 5.5 vs 7.1 cycles per copy), so this may be a
trade-off. If the X-register pair is preferred for a reason I'm not aware of,
I'd be interested to know.
Testcase t.c (preprocessed t.i attached):
void f(void *d, const void *s)
{
__builtin_memcpy(d, s, 16);
}
gcc -O2 -mcpu=neoverse-v2 -Wall -Wextra -S t.c (no warnings):
f:
ldp x2, x3, [x1]
stp x2, x3, [x0]
ret
I'd expect GCC to emit a single Q-register copy, as Clang 23.1 does:
f:
ldr q0, [x1]
str q0, [x0]
ret
Compiler Explorer (GCC 16.1, GCC trunk, Clang 23.1):
https://godbolt.org/z/or5o43fc4
Setup: GCC 16.2.0 built from the unmodified releases/gcc-16.2.0 tag,
aarch64-unknown-linux-gnu, configured with --enable-languages=c,c++
--disable-bootstrap --disable-multilib --without-isl --disable-nls.
Host: Neoverse-V2.