https://gcc.gnu.org/g:209757c5407dee80404800eff67e9dffb3430505
commit r17-1952-g209757c5407dee80404800eff67e9dffb3430505 Author: H.J. Lu <[email protected]> Date: Wed Jun 24 18:48:42 2026 +0800 x86-64: Don't use overlapping unaligned moves to move 1 byte commit b41f96465190751561f6909e858604ceab00595b Author: H.J. Lu <[email protected]> Date: Mon Oct 20 16:14:34 2025 +0800 x86-64: Inline memmove with overlapping unaligned loads and stores. inlines memmove with overlapping unaligned and stores. For void foo (char *dest, char *src, size_t len) { if (len <= 1) __builtin_memmove (dest, src, len); } it uses overlapping unaligned and stores to move 1 byte: cmpl $1, %edx jb .L1 movl %edx, %edx movzbl (%rsi), %ecx movzbl -1(%rsi,%rdx), %eax movb %cl, (%rdi) movb %al, -1(%rdi,%rdx) ret Don't call ix86_expand_n_overlapping_move_movmem when max size == 1 to generate: cmpl $1, %edx jb .L1 movzbl (%rsi), %eax movb %al, (%rdi) ret gcc/ PR target/125968 * config/i386/i386-expand.cc (ix86_expand_movmem): Don't call ix86_expand_n_overlapping_move_movmem when max size == 1. gcc/testsuite/ PR target/125968 * gcc.target/i386/builtin-memmove-16.c: New test. * gcc.target/i386/builtin-memmove-17.c: Likewise. * gcc.target/i386/builtin-memmove-18a.c: Likewise. * gcc.target/i386/builtin-memmove-18b.c: Likewise. Signed-off-by: H.J. Lu <[email protected]> Diff: --- gcc/config/i386/i386-expand.cc | 11 ++++-- gcc/testsuite/gcc.target/i386/builtin-memmove-16.c | 42 ++++++++++++++++++++++ gcc/testsuite/gcc.target/i386/builtin-memmove-17.c | 31 ++++++++++++++++ .../gcc.target/i386/builtin-memmove-18a.c | 28 +++++++++++++++ .../gcc.target/i386/builtin-memmove-18b.c | 6 ++++ 5 files changed, 115 insertions(+), 3 deletions(-) diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc index 2303de120abf..69265c4e2287 100644 --- a/gcc/config/i386/i386-expand.cc +++ b/gcc/config/i386/i386-expand.cc @@ -10630,7 +10630,12 @@ ix86_expand_set_or_movmem (rtx operands[], bool iscpymem, bool issetmem) probable_max_size = INTVAL (probable_max_size_exp); if (CONST_INT_P (expected_size_exp)) expected_size = INTVAL (expected_size_exp); - } + + /* NB: This assert may fail without the fixes for + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125977 + */ + gcc_assert (min_size != max_size); + } /* Make sure we don't need to care about overflow later on. */ if (count > (HOST_WIDE_INT_1U << 30)) @@ -10759,9 +10764,9 @@ ix86_expand_set_or_movmem (rtx operands[], bool iscpymem, bool issetmem) nullptr, count_mode, 1, more_2x_vec_label); - if (min_size == 0 || min_size <= 2 * move_max) + if (max_size != 1 && (min_size == 0 || min_size <= 2 * move_max)) { - /* Size >= MOVE_MAX and size <= 2 * MOVE_MAX. */ + /* Max size != 1, size >= MOVE_MAX and size <= 2 * MOVE_MAX. */ ix86_expand_n_overlapping_move_set_or_movmem (dst, src, memset_vals_p, destreg, srcreg, diff --git a/gcc/testsuite/gcc.target/i386/builtin-memmove-16.c b/gcc/testsuite/gcc.target/i386/builtin-memmove-16.c new file mode 100644 index 000000000000..29fd84391c18 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/builtin-memmove-16.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-avx -msse2 -mtune=generic -minline-all-stringops" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target { lp64 } } {^\t?\.} } } */ + +/* +**gcc_memmove: +**.LFB0: +** .cfi_startproc +** cmpq \$2, %rdx +** jbe .L7 +**.L1: +** ret +** .p2align 4,,10 +** .p2align 3 +**.L7: +** cmpl \$2, %edx +** jnb .L8 +** cmpl \$1, %edx +** jb .L1 +** movzbl \(%rsi\), %eax +** movb %al, \(%rdi\) +** ret +** .p2align 4,,10 +** .p2align 3 +**.L8: +** movl %edx, %edx +** movzwl \(%rsi\), %ecx +** movzwl -2\(%rsi,%rdx\), %eax +** movw %cx, \(%rdi\) +** movw %ax, -2\(%rdi,%rdx\) +** ret +** .cfi_endproc +**... +*/ + +void +gcc_memmove (void *a, void *b, __SIZE_TYPE__ n) +{ + if (n < 3) + __builtin_memmove (a, b, n); +} diff --git a/gcc/testsuite/gcc.target/i386/builtin-memmove-17.c b/gcc/testsuite/gcc.target/i386/builtin-memmove-17.c new file mode 100644 index 000000000000..8d55c21b73da --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/builtin-memmove-17.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-avx -msse2 -mtune=generic -minline-all-stringops" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target { lp64 } } {^\t?\.} } } */ + +/* +**gcc_memmove: +**.LFB0: +** .cfi_startproc +** cmpq \$1, %rdx +** jbe .L7 +**.L1: +** ret +** .p2align 4,,10 +** .p2align 3 +**.L7: +** cmpl \$1, %edx +** jb .L1 +** movzbl \(%rsi\), %eax +** movb %al, \(%rdi\) +** ret +** .cfi_endproc +**... +*/ + +void +gcc_memmove (void *a, void *b, __SIZE_TYPE__ n) +{ + if (n < 2) + __builtin_memmove (a, b, n); +} diff --git a/gcc/testsuite/gcc.target/i386/builtin-memmove-18a.c b/gcc/testsuite/gcc.target/i386/builtin-memmove-18a.c new file mode 100644 index 000000000000..0eb92027a928 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/builtin-memmove-18a.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mno-avx -msse2 -mtune=generic" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target { lp64 } } {^\t?\.} } } */ + +/* +**gcc_memmove: +**.LFB0: +** .cfi_startproc +** cmpq \$1, %rdx +** je .L4 +** ret +** .p2align 4,,10 +** .p2align 3 +**.L4: +** movzbl \(%rsi\), %eax +** movb %al, \(%rdi\) +** ret +** .cfi_endproc +**... +*/ + +void +gcc_memmove (void *a, void *b, __SIZE_TYPE__ n) +{ + if (n >= 1 && n <= 1) + __builtin_memmove (a, b, n); +} diff --git a/gcc/testsuite/gcc.target/i386/builtin-memmove-18b.c b/gcc/testsuite/gcc.target/i386/builtin-memmove-18b.c new file mode 100644 index 000000000000..0a20955fc90c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/builtin-memmove-18b.c @@ -0,0 +1,6 @@ +/* { dg-do compile } */ +/* { dg-options "-O0 -mno-avx -msse2 -mtune=generic" } */ + +#include "builtin-memmove-18a.c" + +/* { dg-final { scan-assembler "call _?memmove" } } */
