riscv: Implement Zbb based strlen and prefer it over the RVV based strlen implementation when Zbb is available

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Fri Jun 12 12:33:45 GMT 2026



On 11/06/26 18:30, Jeffrey Law wrote:
> 
> 
> On 6/11/2026 1:51 PM, Adhemerval Zanella Netto wrote:
>> Did you check the generic implementation? Both 3d6fcf1bd7f462d333c36a14efc0e03f2fdd3f9e
>> and 4c966c078036abe0e36bd86c9eaeb4501e552977 added zbb handling, so building with
>> -march=rv64imafdc_zbb -mabi=lp64d with gcc-16 I see:
> It included the generic, but I didn't build with flags that would have flipped Zbb on.
> 
> [ ... ]
> 
>> The ASM version seems to have an extra cost of li/sub valid-bytes setup, longer
>> first-word path, and the alignment padding nops.  I would guess both would have
>> similar performance profiles.
> Yea, I would expect largely similar.
> 
>>
>> And there is another question if adding a zbb sysdep is really a good move here.
>> My understanding is chip produces and distros are moving to RVA23U64/RVA23S64,
>> which implies in zbb and thus you can select it without the need to add an
>> specific implementation.
> While I would *love* to see everyone settle on rva23, but I'm highly skeptical based on my conversations with distros and others.
> 
> Jeff

Sigh... then I think it would be better to have a way to build zbb optimized 
implementations using the generic code, instead of adding zbb assembly routines.

Unfortunately __attribute__ ((target ("arch=+zbb"))) was only added on gcc-14; 
and RISC-V lackk any option to add +zbb for the extensions for a translation unit. 
One option would be filter out the compiler used zbb, check if zbb is being used 
(by parsing the .attribute form the assembly), and compose a -march with the compiler 
default *plus* zbb. I think someone has already suggested it, I don't recall exactly.

Something like the below, and you can go even further and add a config.make to avoid
building and selecting zbb if compiler already default to it (as for building to rva23).
I will let this exercise for the reader ;)

diff --git a/sysdeps/unix/sysv/linux/riscv/configure.ac b/sysdeps/unix/sysv/linux/riscv/configure.ac
index 9c736415f72..f84a1669e4a 100644
--- a/sysdeps/unix/sysv/linux/riscv/configure.ac
+++ b/sysdeps/unix/sysv/linux/riscv/configure.ac
@@ -32,6 +32,28 @@ fi

 LIBC_CONFIG_VAR([default-abi], [$libc_cv_riscv_int_abi$libc_cv_riscv_float_abi])

+AC_CACHE_CHECK([for the compiler option to enable the Zbb extension],
+              [libc_cv_riscv_zbb_cflags], [dnl
+libc_cv_riscv_zbb_cflags=
+cat > conftest.c <<EOF
+int foo (void) { return 0; }
+EOF
+libc_cv_riscv_arch=`$CC $CFLAGS $CPPFLAGS -S -o - conftest.c 2>/dev/null \
+  | sed -n 's/.*\.attribute@<:@^,@:>@*,@<:@^"@:>@*"\(rv@<:@^"@:>@*\)".*/\1/p'`
+rm -f conftest*
+if test -n "$libc_cv_riscv_arch"; then
+  save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -march=${libc_cv_riscv_arch}_zbb"
+  AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+#ifndef __riscv_zbb
+# error __riscv_zbb is not defined
+#endif
+]])],
+    [libc_cv_riscv_zbb_cflags="-march=${libc_cv_riscv_arch}_zbb"])
+  CFLAGS="$save_CFLAGS"
+fi])
+LIBC_CONFIG_VAR([riscv-zbb-cflags], [$libc_cv_riscv_zbb_cflags])
+
 case $libc_cv_riscv_int_abi$libc_cv_riscv_float_abi-$machine in
 lp64-riscv/rv64/*)
   LIBC_SLIBDIR_RTLDDIR([lib64/lp64], [lib])
diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
index 30d92c1ba92..df7fde2fd8d 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/Makefile
@@ -31,6 +31,7 @@ sysdep_routines += \
   strlen \
   strlen-generic \
   strlen-vector \
+  strlen-zbb \
   strncmp \
   strncmp-generic \
   strncmp-vector \
@@ -40,6 +41,8 @@ sysdep_routines += \
   # sysdep_routines

 CFLAGS-memcpy_noalignment.c += -mno-strict-align
+CFLAGS-strlen-zbb.c += $(riscv-zbb-cflags)
+
 # Called during static initialization
 CFLAGS-memset-generic.c += $(no-stack-protector)
 CFLAGS-memcpy-generic.c += $(no-stack-protector)
diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c b/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
index 80275785293..e598f958487 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/ifunc-impl-list.c
@@ -28,6 +28,7 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,

   bool fast_unaligned = false;
   bool rvv_enabled = false;
+  bool zbb_enabled = false;

   struct riscv_hwprobe pairs[2] = {
     {.key = RISCV_HWPROBE_KEY_CPUPERF_0},
@@ -41,6 +42,9 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,

     if (pairs[1].value & RISCV_HWPROBE_IMA_V)
       rvv_enabled = true;
+
+    if (pairs[1].value & RISCV_HWPROBE_EXT_ZBB)
+      zbb_enabled = true;
   }

   IFUNC_IMPL (i, name, memcpy,
@@ -68,6 +72,8 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
   IFUNC_IMPL (i, name, strlen,
              IFUNC_IMPL_ADD (array, i, strlen, rvv_enabled,
                              __strlen_vector)
+             IFUNC_IMPL_ADD (array, i, strlen, zbb_enabled,
+                             __strlen_zbb)
              IFUNC_IMPL_ADD (array, i, strlen, 1, __strlen_generic))

   IFUNC_IMPL (i, name, strcmp,
diff --git a/sysdeps/unix/sysv/linux/riscv/multiarch/strlen.c b/sysdeps/unix/sysv/linux/riscv/multiarch/strlen.c
index 9975286b85a..5ed08ac37e1 100644
--- a/sysdeps/unix/sysv/linux/riscv/multiarch/strlen.c
+++ b/sysdeps/unix/sysv/linux/riscv/multiarch/strlen.c
@@ -32,14 +32,19 @@ extern __typeof (__redirect_strlen) __libc_strlen;

 extern __typeof (__redirect_strlen) __strlen_generic attribute_hidden;
 extern __typeof (__redirect_strlen) __strlen_vector attribute_hidden;
+extern __typeof (__redirect_strlen) __strlen_zbb attribute_hidden;

 static inline __typeof (__redirect_strlen) *
 select_strlen_ifunc (uint64_t dl_hwcap, __riscv_hwprobe_t hwprobe_func)
 {
   unsigned long long int v;
-  if (__riscv_hwprobe_one (hwprobe_func, RISCV_HWPROBE_KEY_IMA_EXT_0, &v) == 0
-      && (v & RISCV_HWPROBE_IMA_V) == RISCV_HWPROBE_IMA_V)
-    return __strlen_vector;
+  if (__riscv_hwprobe_one (hwprobe_func, RISCV_HWPROBE_KEY_IMA_EXT_0, &v) == 0)
+    {
+      if (v & RISCV_HWPROBE_IMA_V)
+       return __strlen_vector;
+      if (v & RISCV_HWPROBE_EXT_ZBB)
+       return __strlen_zbb;
+    }
   return __strlen_generic;
 }


More information about the Libc-alpha mailing list