aboutsummaryrefslogtreecommitdiff
path: root/newlib/libc
diff options
context:
space:
mode:
authorEric Salem <ericsalem@gmail.com>2025-03-20 10:28:17 -0500
committerCorinna Vinschen <corinna@vinschen.de>2025-03-24 12:00:18 +0100
commitabb595ab0aa4696d6e64f5133bdcad93def184b5 (patch)
tree68f971fafa8ba0ae043dda0bdb3f3b49f0235b90 /newlib/libc
parentc3b9bb173c8c48fb44135d512b39cc856c9acd9c (diff)
downloadnewlib-abb595ab0aa4696d6e64f5133bdcad93def184b5.zip
newlib-abb595ab0aa4696d6e64f5133bdcad93def184b5.tar.gz
newlib-abb595ab0aa4696d6e64f5133bdcad93def184b5.tar.bz2
newlib: riscv: Optimize strlen()
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>
Diffstat (limited to 'newlib/libc')
-rw-r--r--newlib/libc/machine/riscv/strlen.c33
-rw-r--r--newlib/libc/machine/riscv/sys/string.h36
2 files changed, 57 insertions, 12 deletions
diff --git a/newlib/libc/machine/riscv/strlen.c b/newlib/libc/machine/riscv/strlen.c
index 5d314ec..7e5d416 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 6a0c42a..88e5dea 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