[PATCH v2 4/6] RISC-V: memmove() speed optimized: Align source address

m fally <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
If misaligned accesses are slow or prohibited, either
source or destination address are unaligned and the number
of bytes to be copied is > SZREG*2, align the source address
to xlen. This speeds up the function in the case where at
least one address is unaligned, since now one word (or doubleword
for rv64) is loaded at a time, therefore reducing the amount of
memory accesses necessary.
We still need to store back individual bytes since the
destination address might (still) be unaligned after aligning
the source.
The threshold of SZREG*2 was chosen to keep the negative effect
on shorter copies caused by the additional overhead from aligning
the source low.

This change also affects the case where both adresses are xlen-
aligned, the memory areas overlap destructively, and length is not
a multiple of SZREG. In the destructive-overlap case, the copying
needs to be done in reversed order. Therefore the length is added
to the addresses first, which causes them to become unaligned.

Reviewed-by: Christian Herber <[email protected]>
Signed-off-by: m fally <[email protected]>
---
 newlib/libc/machine/riscv/memmove.c | 185 ++++++++++++++++++++--------
 1 file changed, 135 insertions(+), 50 deletions(-)

diff --git a/newlib/libc/machine/riscv/memmove.c b/newlib/libc/machine/riscv/memmove.c
index f8937c2a2..12010f20f 100644
--- a/newlib/libc/machine/riscv/memmove.c
+++ b/newlib/libc/machine/riscv/memmove.c
@@ -1,4 +1,5 @@
 /* Copyright (c) 2019  SiFive Inc. All rights reserved.
+   Copyright (c) 2025  Marlene Fally <[email protected]>
 
    This copyrighted material is made available to anyone wishing to use,
    modify, copy, or redistribute it subject to the terms and conditions
@@ -30,6 +31,26 @@ __libc_fast_xlen_aligned (void *dst, const void *src)
 #endif
 }
 
+#if !defined(__riscv_misaligned_fast)
+static inline void
+__libc_memmove_misaligned_copy (unsigned char *dst,
+                                const uintxlen_t *aligned_src)
+{
+  uintxlen_t src_xlen = *aligned_src;
+
+  *dst++ = (unsigned char)(src_xlen);
+  *dst++ = (unsigned char)(src_xlen >> 8);
+  *dst++ = (unsigned char)(src_xlen >> 16);
+  *dst++ = (unsigned char)(src_xlen >> 24);
+#if __riscv_xlen == 64
+  *dst++ = (unsigned char)(src_xlen >> 32);
+  *dst++ = (unsigned char)(src_xlen >> 40);
+  *dst++ = (unsigned char)(src_xlen >> 48);
+  *dst++ = (unsigned char)(src_xlen >> 56);
+#endif
+}
+#endif
+
 static inline void
 __libc_aligned_copy_unrolled (uintxlen_t *aligned_dst,
                               const uintxlen_t *aligned_src)
@@ -69,74 +90,138 @@ memmove (void *dst_void, const void *src_void, size_t length)
       src += length;
       dst += length;
 
-      if (length >= SZREG && __libc_fast_xlen_aligned (dst, src))
+      if (length >= SZREG)
         {
-          aligned_dst = (uintxlen_t *)dst;
-          aligned_src = (uintxlen_t *)src;
-
-          /* If possible, unroll the word-copy loop by a factor 9 to
-             match memcpy. This speeds up the copying process for longer
-             lengths while barely degrading performance for lengths < SZREG*9.
-             Since we are copying backwards, decrement the addresses
-             before copying.
-           */
-          while (length >= SZREG * 9)
+          if (__libc_fast_xlen_aligned (dst, src))
             {
-              aligned_dst -= 9;
-              aligned_src -= 9;
-              __libc_aligned_copy_unrolled (aligned_dst, aligned_src);
-              length -= (SZREG * 9);
+              aligned_dst = (uintxlen_t *)dst;
+              aligned_src = (uintxlen_t *)src;
+
+              /* If possible, unroll the word-copy loop by a factor 9 to
+                 match memcpy. This speeds up the copying process for longer
+                 lengths while barely degrading performance for lengths <
+                 SZREG*9. Since we are copying backwards, decrement the
+                 addresses before copying.
+               */
+              while (length >= SZREG * 9)
+                {
+                  aligned_dst -= 9;
+                  aligned_src -= 9;
+                  __libc_aligned_copy_unrolled (aligned_dst, aligned_src);
+                  length -= (SZREG * 9);
+                }
+
+              while (length >= SZREG)
+                {
+                  *--aligned_dst = *--aligned_src;
+                  length -= SZREG;
+                }
+
+              /* Pick up any residual with a byte copier.  */
+              dst = (unsigned char *)aligned_dst;
+              src = (unsigned char *)aligned_src;
             }
-
-          while (length >= SZREG)
+#if !defined(__riscv_misaligned_fast)
+          else if (length > (SZREG * 2))
             {
-              *--aligned_dst = *--aligned_src;
-              length -= SZREG;
+              /* At least one address is not xlen-aligned. If
+                 misaligned accesses are slow or  prohibited,
+                 align the src so we can load SZREG bytes at a time.
+                 This reduces the amount of memory accesses made
+                 and therefore improves performance.
+               */
+              while ((uintxlen_t)src & (SZREG - 1))
+                {
+                  *--dst = *--src;
+                  length--;
+                }
+
+              aligned_src = (uintxlen_t *)src;
+
+              /* Decrement the addresses before copying since
+                 we are copying backwards. */
+              do
+                {
+                  aligned_src--;
+                  dst -= SZREG;
+                  __libc_memmove_misaligned_copy (dst, aligned_src);
+                  length -= SZREG;
+                }
+              while (length >= SZREG);
+
+              /* Pick up any residual with a byte copier.  */
+              src = (unsigned char *)aligned_src;
             }
-
-          /* Pick up any residual with a byte copier.  */
-          dst = (unsigned char *)aligned_dst;
-          src = (unsigned char *)aligned_src;
+#endif
         }
-
       while (length--)
         {
           *--dst = *--src;
         }
     }
-  else
+  else /* Memory areas overlap non-destructively or not at all. */
     {
-      /* Use optimizing algorithm for a non-destructive copy to closely
-         match memcpy. If the size is small or either SRC or DST is unaligned,
-         then punt into the byte copy loop.  This should be rare.  */
-      if (length >= SZREG && __libc_fast_xlen_aligned (dst, src))
+      if (length >= SZREG)
         {
-          aligned_dst = (uintxlen_t *)dst;
-          aligned_src = (uintxlen_t *)src;
-
-          /* If possible, unroll the word-copy loop by a factor 9 to
-             match memcpy. This speeds up the copying process for longer
-             lengths while barely degrading performance for lengths < SZREG*9.
-           */
-          while (length >= SZREG * 9)
+          if (__libc_fast_xlen_aligned (dst, src))
             {
-              __libc_aligned_copy_unrolled (aligned_dst, aligned_src);
-              aligned_dst += 9;
-              aligned_src += 9;
-              length -= (SZREG * 9);
+              aligned_dst = (uintxlen_t *)dst;
+              aligned_src = (uintxlen_t *)src;
+
+              /* If possible, unroll the word-copy loop by a factor 9 to
+                 match memcpy. This speeds up the copying process for longer
+                 lengths while barely degrading performance for lengths <
+                 SZREG*9.
+               */
+              while (length >= SZREG * 9)
+                {
+                  __libc_aligned_copy_unrolled (aligned_dst, aligned_src);
+                  aligned_dst += 9;
+                  aligned_src += 9;
+                  length -= (SZREG * 9);
+                }
+
+              while (length >= SZREG)
+                {
+                  *aligned_dst++ = *aligned_src++;
+                  length -= SZREG;
+                }
+
+              /* Pick up any residual with a byte copier.  */
+              dst = (unsigned char *)aligned_dst;
+              src = (unsigned char *)aligned_src;
             }
-
-          while (length >= SZREG)
+#if !defined(__riscv_misaligned_fast)
+          else if (length > (SZREG * 2))
             {
-              *aligned_dst++ = *aligned_src++;
-              length -= SZREG;
+              /* At least one address is not xlen-aligned. If
+                 misaligned accesses are slow or prohibited,
+                 align the src so we can load SZREG bytes at a time.
+                 This reduces the amount of memory accesses made
+                 and therefore improves performance.
+               */
+              while ((uintxlen_t)src & (SZREG - 1))
+                {
+                  *dst++ = *src++;
+                  length--;
+                }
+
+              aligned_src = (uintxlen_t *)src;
+
+              do
+                {
+                  __libc_memmove_misaligned_copy (dst, aligned_src);
+                  aligned_src++;
+                  dst += SZREG;
+                  length -= SZREG;
+                }
+              while (length >= SZREG);
+
+              /* Pick up any residual with a byte copier.  */
+              src = (unsigned char *)aligned_src;
             }
-
-          /* Pick up any residual with a byte copier.  */
-          dst = (unsigned char *)aligned_dst;
-          src = (unsigned char *)aligned_src;
+#endif
         }
-
       while (length--)
         {
           *dst++ = *src++;
-- 
2.49.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.