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)
>