diff options
author | Eric Salem <ericsalem@gmail.com> | 2025-03-20 10:28:17 -0500 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2025-03-24 12:00:18 +0100 |
commit | abb595ab0aa4696d6e64f5133bdcad93def184b5 (patch) | |
tree | 68f971fafa8ba0ae043dda0bdb3f3b49f0235b90 /newlib/libc | |
parent | c3b9bb173c8c48fb44135d512b39cc856c9acd9c (diff) | |
download | newlib-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.c | 33 | ||||
-rw-r--r-- | newlib/libc/machine/riscv/sys/string.h | 36 |
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 |