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.


-- 
H.J.
From 756d90affbea88cff3b1f4e34e90a2898286aa6b Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Wed, 24 Jun 2026 18:48:42 +0800
Subject: [PATCH] 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]>
---
 gcc/config/i386/i386-expand.cc                | 11 +++--
 .../gcc.target/i386/builtin-memmove-16.c      | 42 +++++++++++++++++++
 .../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(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/builtin-memmove-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/builtin-memmove-17.c
 create mode 100644 gcc/testsuite/gcc.target/i386/builtin-memmove-18a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/builtin-memmove-18b.c

diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc
index 2303de120ab..69265c4e228 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 00000000000..29fd84391c1
--- /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 00000000000..8d55c21b73d
--- /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 00000000000..0eb92027a92
--- /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 00000000000..0a20955fc90
--- /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" } } */
-- 
2.54.0

Reply via email to