Re: [PATCH 1/2] AArch64: Add SVE2 strchr
Adhemerval Zanella Netto <[email protected]> Tue, 4 Aug 2026 10:13:43 -0300
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Organization | Linaro |
| Message-ID | <[email protected]> |
On 03/08/26 12:48, Wilco Dijkstra wrote:
>
> Add an SVE2 strchr implementation and ifunc support. Use MATCH to check for
> both the input char and NUL. Performance of bench-strchr improves ~28% on
> Neoverse V2.
Patch looks ok, some minor comments below.
We now track the shared files from AOR since c5a4d7ccb2dc9f4bb2ed61c7c3463be97d28cc68,
so I think we should add this one as well.
>
> ---
>
> diff --git a/sysdeps/aarch64/multiarch/Makefile b/sysdeps/aarch64/multiarch/Makefile
> index 6294a70c252af39964a8750749db34da413e6656..9616231c042c03088384b07ea8c5465d413048d1 100644
> --- a/sysdeps/aarch64/multiarch/Makefile
> +++ b/sysdeps/aarch64/multiarch/Makefile
> @@ -16,6 +16,8 @@ sysdep_routines += \
> memset_oryon1 \
> memset_sve_zva64 \
> memset_zva64 \
> + strchr_generic \
> + strchr_sve2 \
> strlen_asimd \
> strlen_generic \
> # sysdep_routines
> diff --git a/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h b/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
> index 0910e321d24d84db951cf498c3e883c2cfdd01f5..50b2581455c94dc2bd38f8e5ba9a357d1b79e89f 100644
> --- a/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
> +++ b/sysdeps/aarch64/multiarch/dl-symbol-redir-ifunc.h
> @@ -22,6 +22,7 @@
> asm ("memset = __memset_generic");
> asm ("strlen = __strlen_generic");
> #ifndef SHARED
> +asm ("strchr = __strchr_generic");
> asm ("memcpy = __memcpy_generic");
> asm ("memmove = __memmove_generic");
> asm ("memcmp = __memcmp_generic");
> diff --git a/sysdeps/aarch64/multiarch/ifunc-impl-list.c b/sysdeps/aarch64/multiarch/ifunc-impl-list.c
> index d43f6b58ee075c815b93ab2f42735e7e9f315d32..a0c93147c532d935d562a66ac67ebaa5326dd3bd 100644
> --- a/sysdeps/aarch64/multiarch/ifunc-impl-list.c
> +++ b/sysdeps/aarch64/multiarch/ifunc-impl-list.c
> @@ -33,7 +33,6 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
>
> INIT_ARCH ();
>
> - /* Support sysdeps/aarch64/multiarch/memcmp.c, memcpy.c, memmove.c and memset.c. */
I think this is an unrelated change.
> IFUNC_IMPL (i, name, memcmp,
> IFUNC_IMPL_ADD (array, i, memcmp, sve, __memcmp_kunpeng950)
> IFUNC_IMPL_ADD (array, i, memcmp, 1, __memcmp_generic))
> @@ -61,6 +60,8 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
> IFUNC_IMPL (i, name, strlen,
> IFUNC_IMPL_ADD (array, i, strlen, !mte, __strlen_asimd)
> IFUNC_IMPL_ADD (array, i, strlen, 1, __strlen_generic))
> -
> + IFUNC_IMPL (i, name, strchr,
> + IFUNC_IMPL_ADD (array, i, strchr, sve2 && !mte, __strchr_sve2)
> + IFUNC_IMPL_ADD (array, i, strchr, 1, __strchr_generic))
> return 0;
> }
> diff --git a/sysdeps/aarch64/multiarch/strchr.c b/sysdeps/aarch64/multiarch/strchr.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..12f4e10bbe034ab462a4e578c69fd1d9b4afd9ed
> --- /dev/null
> +++ b/sysdeps/aarch64/multiarch/strchr.c
> @@ -0,0 +1,38 @@
> +/* Multiple versions of strchr. AArch64 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
> + <https://www.gnu.org/licenses/>. */
> +
> +/* Define multiple versions only for the definition in libc. */
> +
> +#if IS_IN (libc)
> +/* Redefine strchr so that the compiler won't complain about the type
> + mismatch with the IFUNC selector in strong_alias, below. */
> +# undef strchr
> +# define strchr __redirect_strchr
> +# include <string.h>
> +# include <init-arch.h>
> +
> +extern __typeof (__redirect_strchr) __libc_strchr;
> +
> +extern __typeof (__redirect_strchr) __strchr_generic attribute_hidden;
> +extern __typeof (__redirect_strchr) __strchr_sve2 attribute_hidden;
> +
> +libc_ifunc (__libc_strchr, (sve2 && !mte ? __strchr_sve2 : __strchr_generic));
As side note, maybe it would be better to consolidate the selecion logic
to avoid replicate on ifunc-impl-list.c, but it would be a different patch.
> +
> +# undef strchr
> +strong_alias (__libc_strchr, strchr);
> +#endif
> diff --git a/sysdeps/aarch64/multiarch/strchr_generic.S b/sysdeps/aarch64/multiarch/strchr_generic.S
> new file mode 100644
> index 0000000000000000000000000000000000000000..d72b0be62a89f4e5b090f9d5c1973088bad89704
> --- /dev/null
> +++ b/sysdeps/aarch64/multiarch/strchr_generic.S
> @@ -0,0 +1,35 @@
> +/* A generic optimized strchr implementation for AArch64.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <sysdep.h>
> +
> +#if IS_IN (libc)
> +
> +# define STRCHR __strchr_generic
> +
> +/* Do not hide the generic version of strchr, we use it internally. */
> +# undef libc_hidden_builtin_def
> +# define libc_hidden_builtin_def(name)
> +
> +# ifdef SHARED
> +/* It doesn't make sense to send libc-internal strchr calls through a PLT. */
> + .globl __GI_strchr; __GI_strchr = __strchr_generic
> +# endif
> +#endif
> +
> +#include "../strchr.S"
> diff --git a/sysdeps/aarch64/multiarch/strchr_sve2.S b/sysdeps/aarch64/multiarch/strchr_sve2.S
> new file mode 100644
> index 0000000000000000000000000000000000000000..4f24c65e2915a14286a0ea922ae8325bde3561c1
> --- /dev/null
> +++ b/sysdeps/aarch64/multiarch/strchr_sve2.S
> @@ -0,0 +1,116 @@
> +/* Optimized strchr for SVE2.
> + 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
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <sysdep.h>
> +
> +/* Assumptions:
> + *
> + * SVE2, unaligned accesses.
> + *
> + */
> +
> +.arch armv9-a+sve2
> +
> +#if defined (BUILD_STRCHRNUL)
> +#define STRCHR __strchrnul_sve2
> +#else
> +#define STRCHR __strchr_sve2
> +#endif
> +
> +#define chrin w1 // int chr.
I think we avoid '//' comments even in assembly file, but not sure how to handle
this in SHARED-FILES. For CORE-MATH, I tried to adapt them, but I have slipped
some.
> +#define result x0 // Return.
> +
> +#define src x0 // Current data pointer.
> +#define vlalign x2 // Alignment mask.
> +#define tmp x3
> +
> +#define zdata z0 // Data.
> +#define zchr_nul z1 // chr and NUL data for search.
> +
> +#define pall p0 // All active predicate.
> +#define pchr_nul p1 // chr and NUL search result.
> +#define pchr p2 // chr search result.
> +#define psrc p3 // Mask of chars before srcin.
> +#define pfirst p4 // First found.
> +
> +ENTRY (STRCHR)
> + ptrue pall.b
> + and chrin, chrin, 0xff // Set zeros after first byte.
> + dup zchr_nul.h, chrin // Initialize chr and NUL for search.
> +
> + and tmp, src, 0xfff // Check for pagecross of 2VL reads.
> + incb tmp, all, mul #2
> + tbnz tmp, 12, L(pagecross)
> +
> + ld1b {zdata.b}, pall/z, [src]
> + match pchr_nul.b, pall/z, zdata.b, zchr_nul.b
> + b.any L(firstmatch)
> + ld1b {zdata.b}, pall/z, [src, 1, mul vl]
> + match pchr_nul.b, pall/z, zdata.b, zchr_nul.b
> + b.none L(loop_start)
> + incb src
> +L(firstmatch):
> + brkb pfirst.b, pall/z, pchr_nul.b // Find first element.
> + incp src, pfirst.b // Increment by element index.
> +#if !defined (BUILD_STRCHRNUL)
> + cbz chrin, 1f // Special case NUL chrin.
> + cmpne pchr.b, pchr_nul/z, zdata.b, 0 // Matched chrin?
> + csel result, src, xzr, first // If it is NUL, return null.
> +1: ret
> + nop
> +#else
> + ret
> +#endif
> +L(loop_start):
> + mov vlalign, 0
> + decb vlalign
> + and src, src, vlalign // Align src to VL.
> +L(loop):
> + ld1b {zdata.b}, pall/z, [src, 2, mul vl]
> + incb src, all, mul #2
> + match pchr_nul.b, pall/z, zdata.b, zchr_nul.b
> + b.any L(loop_end)
> +L(loop2):
> + ld1b {zdata.b}, pall/z, [src, 1, mul vl]
> + match pchr_nul.b, pall/z, zdata.b, zchr_nul.b
> + b.none L(loop)
> + incb src
> +L(loop_end):
> + brkb pfirst.b, pall/z, pchr_nul.b // Find first element.
> + incp src, pfirst.b // Increment by element index.
> +#if !defined (BUILD_STRCHRNUL)
> + cbz chrin, 2f // Special case NUL chrin.
> + cmpne pchr.b, pchr_nul/z, zdata.b, 0 // Matched chrin?
> + csel result, src, xzr, first // If it is NUL, return null.
> +#endif
> +2: ret
> +
> + .p2align 3
> +L(pagecross):
> + mov vlalign, 0
> + decb vlalign
> + orn tmp, src, vlalign // Find end of VL aligned src.
> + whilels psrc.b, src, tmp // Mask off elements.
> + ld1b {zdata.b}, psrc/z, [src]
> + match pchr_nul.b, psrc/z, zdata.b, zchr_nul.b
> + b.any L(firstmatch)
> + and src, src, vlalign // Align src to VL.
> + b L(loop2)
> +
> +END (STRCHR)
> diff --git a/sysdeps/aarch64/strchr.S b/sysdeps/aarch64/strchr.S
> index e793fa92f15b63495eab94c83534c5830258bfec..daa28acbfc8b278897d47ec403dc945eb3ba3555 100644
> --- a/sysdeps/aarch64/strchr.S
> +++ b/sysdeps/aarch64/strchr.S
> @@ -26,6 +26,10 @@
> * MTE compatible.
> */
>
> +#ifndef STRCHR
> +# define STRCHR strchr
> +#endif
> +
> #define srcin x0
> #define chrin w1
> #define result x0
> @@ -51,7 +55,7 @@
> zeroes gives the position of the matching byte if it is a multiple of 4.
> If it is not a multiple of 4, there was no match. */
>
> -ENTRY (strchr)
> +ENTRY (STRCHR)
> bic src, srcin, 15
> dup vrepchr.16b, chrin
> ld1 {vdata.16b}, [src]
> @@ -109,6 +113,6 @@ L(end):
> csel result, result, xzr, eq
> ret
>
> -END (strchr)
> -libc_hidden_builtin_def (strchr)
> -weak_alias (strchr, index)
> +END (STRCHR)
> +libc_hidden_builtin_def (STRCHR)
> +weak_alias (STRCHR, index)
>