[2/4] newlib: riscv: Optimize strlen()

Message ID 8d889f88a619a098e9beb38d805149d58c6538cb.1742483743.git.ericsalem@gmail.com
State New
Headers
Series newlib: riscv: Optimize string functions |

Commit Message

Eric Salem March 20, 2025, 3:28 p.m. UTC
  The RISC-V Zbb extension provides instructions optimized for bit
operations. Use them when available for the RISC-V port.

Reviewed-by: Christian Herber <christian.herber@oss.nxp.com>
Signed-off-by: Eric Salem <ericsalem@gmail.com>
---
 newlib/libc/machine/riscv/strlen.c     | 33 +++++++++++++++--------
 newlib/libc/machine/riscv/sys/string.h | 36 +++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 12 deletions(-)
  

Patch

diff --git a/newlib/libc/machine/riscv/strlen.c b/newlib/libc/machine/riscv/strlen.c
index 5d314ec51a72..7e5d41617eac 100644
--- a/newlib/libc/machine/riscv/strlen.c
+++ b/newlib/libc/machine/riscv/strlen.c
@@ -31,24 +31,35 @@  size_t strlen(const char *str)
     } while ((uintxlen_t)str & (sizeof (uintxlen_t) - 1));
 
   uintxlen_t *ps = (uintxlen_t *)str;
-  while (!__libc_detect_null (*ps++))
+  uintxlen_t psval;
+
+  while (!__libc_detect_null ((psval = *ps++)))
     ;
   asm volatile ("" : "+r"(ps)); /* prevent "optimization" */
 
   str = (const char *)ps;
   size_t ret = str - start, sp = sizeof (*ps);
 
-  char c0 = str[0 - sp], c1 = str[1 - sp], c2 = str[2 - sp], c3 = str[3 - sp];
-  if (c0 == 0)             return ret + 0 - sp;
-  if (c1 == 0)             return ret + 1 - sp;
-  if (c2 == 0)             return ret + 2 - sp;
-  if (sp == 4 || c3 == 0)  return ret + 3 - sp;
+  #if __riscv_zbb
+    psval = ~__LIBC_RISCV_ZBB_ORC_B(psval);
+    psval = __LIBC_RISCV_ZBB_CNT_Z(psval);
+
+    return ret + (psval >> 3) - sp;
+  #else
+    char c0 = str[0 - sp], c1 = str[1 - sp], c2 = str[2 - sp], c3 = str[3 - sp];
+    if (c0 == 0)    return ret + 0 - sp;
+    if (c1 == 0)    return ret + 1 - sp;
+    if (c2 == 0)    return ret + 2 - sp;
+    if (c3 == 0)    return ret + 3 - sp;
 
-  c0 = str[4 - sp], c1 = str[5 - sp], c2 = str[6 - sp];
-  if (c0 == 0)             return ret + 4 - sp;
-  if (c1 == 0)             return ret + 5 - sp;
-  if (c2 == 0)             return ret + 6 - sp;
+    #if __riscv_xlen == 64
+      c0 = str[4 - sp], c1 = str[5 - sp], c2 = str[6 - sp];
+      if (c0 == 0)  return ret + 4 - sp;
+      if (c1 == 0)  return ret + 5 - sp;
+      if (c2 == 0)  return ret + 6 - sp;
+    #endif
 
-  return ret + 7 - sp;
+    return ret + 7 - sp;
+  #endif
 #endif /* not PREFER_SIZE_OVER_SPEED */
 }
diff --git a/newlib/libc/machine/riscv/sys/string.h b/newlib/libc/machine/riscv/sys/string.h
index 6a0c42a49c12..88e5dea20c6a 100644
--- a/newlib/libc/machine/riscv/sys/string.h
+++ b/newlib/libc/machine/riscv/sys/string.h
@@ -14,12 +14,46 @@ 
 
 #include "asm.h"
 
+#if __riscv_zbb
+  #include <riscv_bitmanip.h>
+
+  // Determine which intrinsics to use based on XLEN and endianness
+  #if __riscv_xlen == 64
+    #define __LIBC_RISCV_ZBB_ORC_B(x)       __riscv_orc_b_64(x)
+
+    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+      #define __LIBC_RISCV_ZBB_CNT_Z(x)     __riscv_ctz_64(x)
+    #else
+      #define __LIBC_RISCV_ZBB_CNT_Z(x)     __riscv_clz_64(x)
+    #endif
+  #else
+    #define __LIBC_RISCV_ZBB_ORC_B(x)       __riscv_orc_b_32(x)
+
+    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+      #define __LIBC_RISCV_ZBB_CNT_Z(x)     __riscv_ctz_32(x)
+    #else
+      #define __LIBC_RISCV_ZBB_CNT_Z(x)     __riscv_clz_32(x)
+    #endif
+  #endif
+#endif
+
+
 static __inline uintxlen_t __libc_detect_null(uintxlen_t w)
 {
+#if __riscv_zbb
+  /*
+    If there are any zeros in each byte of the register, all bits will
+    be unset for that byte value, otherwise, all bits will be set.
+    If the value is -1, all bits are set, meaning no null byte was found.
+  */
+  return __LIBC_RISCV_ZBB_ORC_B(w) != -1;
+#else
   uintxlen_t mask = 0x7f7f7f7f;
-  if (sizeof (w) == 8)
+  #if __riscv_xlen == 64
     mask = ((mask << 16) << 16) | mask;
+  #endif
   return ~(((w & mask) + mask) | w | mask);
+#endif
 }
 
 #endif