[PATCH v2 1/3] string: add find_ne_all to string-fza.h

Matt Turner mattst88@gmail.com
Fri Aug 14 03:54:43 GMT 2026


find_zero_ne_all () searches for a zero byte in X1 or a byte that differs
between X1 and X2.  A caller that knows X2 contains no NUL byte does not
need the zero test, since a NUL byte in X1 already differs from every
byte of X2.

Add find_ne_all (), which searches for inequality alone, to the generic
implementation and to each target that provides its own string-fza.h.
Dropping the zero test makes it cheaper than find_zero_ne_all () on every
target.

The generic version returns the difference unreduced when
HAVE_BITOPTS_WORKING, since index_first () and index_last () then use
stdc_trailing_zeros () and stdc_leading_zeros (), which only need to know
which byte holds the first or the last set bit.  The fallback ctzb () and
clzb () multiply a mask that must hold exactly one bit per byte, so they
keep the reduced form.  armv6t2 and powerpc use the generic index_first ()
and index_last (), so they follow the same rule.

riscv is the one target that mixes the two: without the bitmap extensions
its string-fza.h falls back to the generic one while its string-fzi.h
defines an index_first () that tests bit 7 of each byte, which the
unreduced difference would defeat.  Give it a string-bitops.h that clears
HAVE_BITOPTS_WORKING in that configuration, where the stdbit.h routines
are unused anyway.  alpha needs no such treatment; its find_t is a cmpbge
mask of one bit per byte throughout.
---
 sysdeps/alpha/string-fza.h       |  7 +++++++
 sysdeps/arm/armv6t2/string-fza.h | 14 ++++++++++++++
 sysdeps/generic/string-fza.h     | 20 ++++++++++++++++++++
 sysdeps/powerpc/string-fza.h     | 14 ++++++++++++++
 sysdeps/riscv/string-bitops.h    | 28 ++++++++++++++++++++++++++++
 sysdeps/riscv/string-fza.h       |  7 +++++++
 6 files changed, 90 insertions(+)
 create mode 100644 sysdeps/riscv/string-bitops.h

diff --git ./sysdeps/alpha/string-fza.h ./sysdeps/alpha/string-fza.h
index ae5d890a7e..8178e75178 100644
--- ./sysdeps/alpha/string-fza.h
+++ ./sysdeps/alpha/string-fza.h
@@ -52,6 +52,13 @@ find_zero_ne_all (op_t x1, op_t x2)
   return find_zero_all (x1) | (find_zero_all (x1 ^ x2) ^ 0xff);
 }
 
+/* Identify bytes that are not equal between X1 and X2.  */
+static __always_inline find_t
+find_ne_all (op_t x1, op_t x2)
+{
+  return find_zero_all (x1 ^ x2) ^ 0xff;
+}
+
 /* Define the "inexact" versions in terms of the exact versions.  */
 #define find_zero_low		find_zero_all
 #define find_eq_low		find_eq_all
diff --git ./sysdeps/arm/armv6t2/string-fza.h ./sysdeps/arm/armv6t2/string-fza.h
index 1eab43fdc3..0ae4c79f7d 100644
--- ./sysdeps/arm/armv6t2/string-fza.h
+++ ./sysdeps/arm/armv6t2/string-fza.h
@@ -19,6 +19,7 @@
 #ifndef _STRING_FZA_H
 #define _STRING_FZA_H 1
 
+#include <string-bitops.h>
 #include <string-misc.h>
 #include <string-optype.h>
 
@@ -61,6 +62,19 @@ find_zero_ne_all (op_t x1, op_t x2)
   return find_zero_all (x1) | (find_zero_all (x1 ^ x2) ^ ones);
 }
 
+/* Identify bytes that are not equal between X1 and X2.  */
+static __always_inline find_t
+find_ne_all (op_t x1, op_t x2)
+{
+#if HAVE_BITOPTS_WORKING
+  /* The difference need not be reduced; see the generic string-fza.h.  */
+  return x1 ^ x2;
+#else
+  op_t ones = repeat_bytes (0x01);
+  return find_zero_all (x1 ^ x2) ^ ones;
+#endif
+}
+
 /* Define the "inexact" versions in terms of the exact versions.  */
 #define find_zero_low		find_zero_all
 #define find_eq_low		find_eq_all
diff --git ./sysdeps/generic/string-fza.h ./sysdeps/generic/string-fza.h
index 362c7a8fe6..3296b09748 100644
--- ./sysdeps/generic/string-fza.h
+++ ./sysdeps/generic/string-fza.h
@@ -19,6 +19,7 @@
 #ifndef _STRING_FZA_H
 #define _STRING_FZA_H 1
 
+#include <string-bitops.h>
 #include <string-misc.h>
 #include <string-optype.h>
 
@@ -95,4 +96,23 @@ find_zero_ne_all (op_t x1, op_t x2)
   return (ne2 | ~nz1) & ~m;
 }
 
+/* With similar caveats, identify bytes that are not equal between X1
+   and X2.  */
+static __always_inline find_t
+find_ne_all (op_t x1, op_t x2)
+{
+#if HAVE_BITOPTS_WORKING
+  /* index_first and index_last only need to know which byte holds the first
+     or the last set bit, so the difference does not have to be reduced to
+     one bit per byte.  The fallback ctzb and clzb do require the reduced
+     form, as does any target whose own index_first tests a fixed bit of
+     each byte; both clear HAVE_BITOPTS_WORKING.  */
+  return x1 ^ x2;
+#else
+  op_t m = repeat_bytes (0x7f);
+  op_t ne = x1 ^ x2;
+  return (((ne & m) + m) | ne) & ~m;
+#endif
+}
+
 #endif /* _STRING_FZA_H */
diff --git ./sysdeps/powerpc/string-fza.h ./sysdeps/powerpc/string-fza.h
index 43aa0db952..b09852ef94 100644
--- ./sysdeps/powerpc/string-fza.h
+++ ./sysdeps/powerpc/string-fza.h
@@ -21,6 +21,7 @@
 
 /* PowerISA 2.05 (POWER6) provides cmpb instruction.  */
 #ifdef _ARCH_PWR6
+# include <string-bitops.h>
 # include <string-misc.h>
 # include <string-optype.h>
 
@@ -60,6 +61,19 @@ find_zero_ne_all (op_t x1, op_t x2)
   return find_zero_all (x1) | ~find_eq_all (x1, x2);
 }
 
+/* Identify bytes that are not equal between X1 and X2.  */
+
+static __always_inline find_t
+find_ne_all (op_t x1, op_t x2)
+{
+#if HAVE_BITOPTS_WORKING
+  /* The difference need not be reduced; see the generic string-fza.h.  */
+  return x1 ^ x2;
+#else
+  return ~find_eq_all (x1, x2);
+#endif
+}
+
 /* Define the "inexact" versions in terms of the exact versions.  */
 # define find_zero_low		find_zero_all
 # define find_eq_low		find_eq_all
diff --git ./sysdeps/riscv/string-bitops.h ./sysdeps/riscv/string-bitops.h
new file mode 100644
index 0000000000..b941d69288
--- /dev/null
+++ ./sysdeps/riscv/string-bitops.h
@@ -0,0 +1,28 @@
+/* Zero byte detection, define whether to use stdbit.h.  RISC-V version.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Without the bitmap extensions string-fzi.h defines its own index_first
+   and index_last, which test bit 7 of each byte, and the stdbit.h routines
+   are not used at all.  Clear this so that the generic string-fza.h, which
+   string-fza.h falls back to in that case, reduces its masks to one bit per
+   byte.  */
+#if defined __riscv_zbb || defined __riscv_xtheadbb
+# define HAVE_BITOPTS_WORKING 1
+#else
+# define HAVE_BITOPTS_WORKING 0
+#endif
diff --git ./sysdeps/riscv/string-fza.h ./sysdeps/riscv/string-fza.h
index 7df4dfe415..fa2ddc3c46 100644
--- ./sysdeps/riscv/string-fza.h
+++ ./sysdeps/riscv/string-fza.h
@@ -63,6 +63,13 @@ find_zero_ne_all (op_t x1, op_t x2)
   return find_zero_all (x1) | ~find_eq_all (x1, x2);
 }
 
+/* Identify bytes that are not equal between X1 and X2.  */
+static __always_inline find_t
+find_ne_all (op_t x1, op_t x2)
+{
+  return ~find_eq_all (x1, x2);
+}
+
 /* Define the "inexact" versions in terms of the exact versions.  */
 # define find_zero_low		find_zero_all
 # define find_eq_low		find_eq_all
-- 
2.54.0



More information about the Libc-alpha mailing list