This is an automated email from the ASF dual-hosted git repository.

GUIDINGLI pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new afc42d0aa24 libs/libc/string: Copy and compare by words when pointers 
agree on alignment.
afc42d0aa24 is described below

commit afc42d0aa24f6a26173c367dec35bff6d05473b5
Author: Justin Hammond <[email protected]>
AuthorDate: Mon Aug 17 18:55:45 2026 +0800

    libs/libc/string: Copy and compare by words when pointers agree on 
alignment.
    
    The BSD string functions take a word path only when both pointers are
    aligned, and a byte path otherwise.  A pair at the same offset from a
    boundary takes the byte path even though copying or comparing a few leading
    bytes aligns both at once, since aligning one aligns the other.
    
    Add MISALIGNED(), which asks whether two pointers disagree about where a
    boundary falls, and walk an agreeing pair up to the boundary before the
    existing path selection.  MISALIGNED4() does the same for the 4-byte path,
    so a pair that is 4-byte but not 8-byte aligned reaches the wide path
    instead of the middle one.  No existing line changes: the walk is a new step
    ahead of the current decisions.  A pair at differing offsets still takes the
    byte path, since no single boundary serves both.
    
    Measured on an EIC7700 EVB (EIC7700X, RV64GC, 1.4GHz) with the BSD string
    functions selected and the RISC-V assembly ones disabled, using the
    benchmark in apps#3706, medians of 3 runs in MB/s at its largest size:
    
                    equal offset            aligned
      memcpy     414 -> 4148  10.0x    4214 -> 4208
      memcmp      41 ->  361   8.8x     362 ->  360
      strncmp     28 ->  202   7.4x     207 ->  207
      strcmp      42 ->  273   6.5x     278 ->  276
      strncpy    377 -> 1676   4.5x    1824 -> 1748
      stpncpy    376 -> 1654   4.4x    1843 -> 1724
      stpcpy     551 -> 1833   3.3x    1970 -> 1939
      memccpy    650 -> 2012   3.1x    2478 -> 2016
      strcpy     636 -> 1837   2.9x    1678 -> 1965
    
    Cases the walk never runs for move in both directions by up to a third, the
    largest being memccpy at differing offsets, 648 -> 414.  Their code is
    unchanged, so that is code placement rather than an effect of the change.
    
    The change is architecture independent but has only been measured on
    RV64GC.  Word size, alignment cost and byte loop codegen all differ
    elsewhere, so the balance wants measuring on other architectures.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 libs/libc/libc.h                  | 10 ++++++++++
 libs/libc/string/lib_bsdmemccpy.c | 21 +++++++++++++++++++++
 libs/libc/string/lib_bsdmemcmp.c  | 22 ++++++++++++++++++++++
 libs/libc/string/lib_bsdmemcpy.c  | 16 ++++++++++++++++
 libs/libc/string/lib_bsdstpcpy.c  | 17 +++++++++++++++++
 libs/libc/string/lib_bsdstpncpy.c | 20 ++++++++++++++++++++
 libs/libc/string/lib_bsdstrcmp.c  | 19 +++++++++++++++++++
 libs/libc/string/lib_bsdstrcpy.c  | 17 +++++++++++++++++
 libs/libc/string/lib_bsdstrncmp.c | 25 +++++++++++++++++++++++++
 libs/libc/string/lib_bsdstrncpy.c | 20 ++++++++++++++++++++
 10 files changed, 187 insertions(+)

diff --git a/libs/libc/libc.h b/libs/libc/libc.h
index 2cc91f5069d..465c636589b 100644
--- a/libs/libc/libc.h
+++ b/libs/libc/libc.h
@@ -178,6 +178,16 @@
 
 #define UNALIGNED(x, y) ((UNALIGNED_X(x)) | (UNALIGNED_X(y)))
 
+/* Nonzero if x and y disagree about where a "libc_data_t" boundary falls.
+ * A pair that agrees can be walked up to the boundary a byte at a time and
+ * handled a word at a time from there, since aligning one aligns the
+ * other.  A pair that disagrees cannot, because no single boundary serves
+ * both.
+ */
+
+#define MISALIGNED(x, y) \
+  ((((uintptr_t)(x)) ^ ((uintptr_t)(y))) & (sizeof(libc_data_t) - 1))
+
 #define ALIGNED(x) \
   (((libc_data_t)(uintptr_t)(x) & (sizeof(libc_data_t) - 1)) == 0)
 
diff --git a/libs/libc/string/lib_bsdmemccpy.c 
b/libs/libc/string/lib_bsdmemccpy.c
index 610c482c400..59eee4246ca 100644
--- a/libs/libc/string/lib_bsdmemccpy.c
+++ b/libs/libc/string/lib_bsdmemccpy.c
@@ -60,6 +60,27 @@ FAR void *memccpy(FAR void *s1, FAR const void *s2, int c, 
size_t n)
   FAR const unsigned char *pin = (FAR const unsigned char *)s2;
   unsigned char endchar = c & 0xff;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  The end character is left for the byte loop, which copies it
+   * and reports where it landed.  Fewer than LITTLEBLOCKSIZE bytes are
+   * copied and the size was tested against that first, so this cannot run
+   * past the end.
+   */
+
+  if (!TOO_SMALL(n) && !MISALIGNED(pin, pout) && UNALIGNED_X(pin))
+    {
+      while (*pin != endchar)
+        {
+          *pout++ = *pin++;
+          n--;
+          if (!UNALIGNED_X(pin))
+            {
+              break;
+            }
+        }
+    }
+
   /* If the size is small, or either pin or pout is unaligned,
    * then punt into the byte copy loop.  This should be rare.
    */
diff --git a/libs/libc/string/lib_bsdmemcmp.c b/libs/libc/string/lib_bsdmemcmp.c
index 871b2275aa7..16ce6dea95d 100644
--- a/libs/libc/string/lib_bsdmemcmp.c
+++ b/libs/libc/string/lib_bsdmemcmp.c
@@ -45,6 +45,28 @@ int memcmp(FAR const void *s1, FAR const void *s2, size_t n)
   FAR unsigned char *p1 = (FAR unsigned char *)s1;
   FAR unsigned char *p2 = (FAR unsigned char *)s2;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  A difference found on the way stops the walk and is reported
+   * by the byte loop.  Fewer than LITTLEBLOCKSIZE bytes are compared and
+   * the size was tested against that first, so this cannot run past the
+   * end.
+   */
+
+  if (!TOO_SMALL(n) && !MISALIGNED(p1, p2) && UNALIGNED_X(p1))
+    {
+      while (*p1 == *p2)
+        {
+          p1++;
+          p2++;
+          n--;
+          if (!UNALIGNED_X(p1))
+            {
+              break;
+            }
+        }
+    }
+
   /* If the size is too small, or either pointer is unaligned,
    * then we punt to the byte compare loop.  Hopefully this will
    * not turn up in inner loops.
diff --git a/libs/libc/string/lib_bsdmemcpy.c b/libs/libc/string/lib_bsdmemcpy.c
index 3950a85f9c0..d3062118ccf 100644
--- a/libs/libc/string/lib_bsdmemcpy.c
+++ b/libs/libc/string/lib_bsdmemcpy.c
@@ -49,6 +49,22 @@ FAR void *memcpy(FAR void *dest, FAR const void *src, size_t 
n)
   FAR char *pout = dest;
   FAR const char *pin = src;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  Fewer than LITTLEBLOCKSIZE bytes are copied and the size was
+   * tested against that first, so this cannot run past the end.
+   */
+
+  if (!TOO_SMALL(n) && !MISALIGNED(pin, pout) && UNALIGNED_X(pin))
+    {
+      do
+        {
+          *pout++ = *pin++;
+          n--;
+        }
+      while (UNALIGNED_X(pin));
+    }
+
   /* If the size is small, or either pin or pout is unaligned,
    * then punt into the byte copy loop.  This should be rare.
    */
diff --git a/libs/libc/string/lib_bsdstpcpy.c b/libs/libc/string/lib_bsdstpcpy.c
index fbd9687adef..c386fc9cc7f 100644
--- a/libs/libc/string/lib_bsdstpcpy.c
+++ b/libs/libc/string/lib_bsdstpcpy.c
@@ -56,6 +56,23 @@ no_builtin("stpcpy")
 nosanitize_address
 FAR char *stpcpy(FAR char *dest, FAR const char *src)
 {
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  The terminator is left for the byte loop to copy.
+   */
+
+  if (!MISALIGNED(src, dest) && UNALIGNED_X(src))
+    {
+      while (*src != '\0')
+        {
+          *dest++ = *src++;
+          if (!UNALIGNED_X(src))
+            {
+              break;
+            }
+        }
+    }
+
   /* If src or dest is unaligned, then copy bytes. */
 
   if (!UNALIGNED(src, dest))
diff --git a/libs/libc/string/lib_bsdstpncpy.c 
b/libs/libc/string/lib_bsdstpncpy.c
index 1f8914551d4..0df9c108982 100644
--- a/libs/libc/string/lib_bsdstpncpy.c
+++ b/libs/libc/string/lib_bsdstpncpy.c
@@ -67,6 +67,26 @@ FAR char *stpncpy(FAR char *dest, FAR const char *src, 
size_t n)
 {
   FAR char *ret = NULL;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  The terminator is left for the byte loop, which also pads.
+   * Fewer than LITTLEBLOCKSIZE bytes are copied and n was tested against
+   * that first, so n cannot run out here.
+   */
+
+  if (!MISALIGNED(src, dest) && !TOO_SMALL(n) && UNALIGNED_X(src))
+    {
+      while (*src != '\0')
+        {
+          *dest++ = *src++;
+          n--;
+          if (!UNALIGNED_X(src))
+            {
+              break;
+            }
+        }
+    }
+
   /* If src and dest is aligned and n large enough, then copy words. */
 
   if (!UNALIGNED(src, dest) && !TOO_SMALL(n))
diff --git a/libs/libc/string/lib_bsdstrcmp.c b/libs/libc/string/lib_bsdstrcmp.c
index 2ec0fed57cb..f1fbc33e9a6 100644
--- a/libs/libc/string/lib_bsdstrcmp.c
+++ b/libs/libc/string/lib_bsdstrcmp.c
@@ -43,6 +43,25 @@ no_builtin("strcmp")
 nosanitize_address
 int strcmp(FAR const char *cs, FAR const char *ct)
 {
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  A difference or a terminator found on the way stops the walk
+   * and is reported by the byte loop.
+   */
+
+  if (!MISALIGNED(cs, ct) && UNALIGNED_X(cs))
+    {
+      while (*cs != '\0' && *cs == *ct)
+        {
+          cs++;
+          ct++;
+          if (!UNALIGNED_X(cs))
+            {
+              break;
+            }
+        }
+    }
+
   /* If cs or ct are unaligned, then compare bytes. */
 
   if (!UNALIGNED(cs, ct))
diff --git a/libs/libc/string/lib_bsdstrcpy.c b/libs/libc/string/lib_bsdstrcpy.c
index a19e44d4c94..b891c543d1c 100644
--- a/libs/libc/string/lib_bsdstrcpy.c
+++ b/libs/libc/string/lib_bsdstrcpy.c
@@ -58,6 +58,23 @@ FAR char *strcpy(FAR char *dest, FAR const char *src)
   FAR char *dst0 = dest;
   FAR const char *src0 = src;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  The terminator is left for the byte loop to copy.
+   */
+
+  if (!MISALIGNED(src0, dst0) && UNALIGNED_X(src0))
+    {
+      while (*src0 != '\0')
+        {
+          *dst0++ = *src0++;
+          if (!UNALIGNED_X(src0))
+            {
+              break;
+            }
+        }
+    }
+
   /* If SRC or DEST is unaligned, then copy bytes. */
 
   if (!UNALIGNED(src0, dst0))
diff --git a/libs/libc/string/lib_bsdstrncmp.c 
b/libs/libc/string/lib_bsdstrncmp.c
index 20f127e1716..f341f86f3ef 100644
--- a/libs/libc/string/lib_bsdstrncmp.c
+++ b/libs/libc/string/lib_bsdstrncmp.c
@@ -48,6 +48,31 @@ int strncmp(FAR const char *cs, FAR const char *ct, size_t 
nb)
       return 0;
     }
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  A difference found on the way stops the walk and is reported
+   * by the byte loop; the count running out or a terminator means the
+   * strings are equal over the whole comparison.
+   */
+
+  if (!MISALIGNED(cs, ct) && UNALIGNED_X(cs))
+    {
+      while (*cs == *ct)
+        {
+          if (--nb == 0 || *cs == '\0')
+            {
+              return 0;
+            }
+
+          cs++;
+          ct++;
+          if (!UNALIGNED_X(cs))
+            {
+              break;
+            }
+        }
+    }
+
   /* If cs or ct are unaligned, then compare bytes. */
 
   if (!UNALIGNED(cs, ct))
diff --git a/libs/libc/string/lib_bsdstrncpy.c 
b/libs/libc/string/lib_bsdstrncpy.c
index 79aeca0aba7..ce261b10f54 100644
--- a/libs/libc/string/lib_bsdstrncpy.c
+++ b/libs/libc/string/lib_bsdstrncpy.c
@@ -67,6 +67,26 @@ FAR char *strncpy(FAR char *dest, FAR const char *src, 
size_t n)
   FAR char *dst0 = dest;
   FAR const char *src0 = src;
 
+  /* Walk a pair that agrees about where a boundary falls up to it, so that
+   * the word path below is reached even when the caller aligned neither
+   * pointer.  The terminator is left for the byte loop, which also pads.
+   * Fewer than LITTLEBLOCKSIZE bytes are copied and n was tested against
+   * that first, so n cannot run out here.
+   */
+
+  if (!MISALIGNED(src0, dst0) && !TOO_SMALL(n) && UNALIGNED_X(src0))
+    {
+      while (*src0 != '\0')
+        {
+          *dst0++ = *src0++;
+          n--;
+          if (!UNALIGNED_X(src0))
+            {
+              break;
+            }
+        }
+    }
+
   /* If src and dest is aligned and n large enough, then copy words. */
 
   if (!UNALIGNED(src0, dst0) && !TOO_SMALL(n))

Reply via email to