new file mode 100644
@@ -0,0 +1,17 @@
+/* Re-include the default wcschr implementation.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Licensed under the GNU Lesser General Public License v2.1+; see COPYING.LESSER. */
+
+#include <wchar.h>
+
+#if IS_IN(libc)
+# define WCSCHR __wcschr_generic
+# undef libc_hidden_def
+# define libc_hidden_def(name)
+# undef libc_hidden_weak
+# define libc_hidden_weak(name)
+# undef static_weak_alias
+# define static_weak_alias(name, alias)
+# include <wcsmbs/wcschr.c>
+#endif
new file mode 100644
@@ -0,0 +1,13 @@
+/* RISC-V RVV based wcschr.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Licensed under the GNU Lesser General Public License v2.1+; see COPYING.LESSER. */
+
+#if IS_IN(libc)
+# define WCSCHR __wcschr_vector
+# undef libc_hidden_builtin_def
+# define libc_hidden_builtin_def(name)
+# undef weak_alias
+# define weak_alias(name, alias)
+# include <sysdeps/riscv/rvv/wcschr.S>
+#endif
new file mode 100644
@@ -0,0 +1,200 @@
+/* RISC-V RVV based wcschr.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Licensed under the GNU Lesser General Public License v2.1+; see COPYING.LESSER. */
+
+#include <sysdep.h>
+#include <sys/asm.h>
+
+#ifndef WCSCHR
+# define WCSCHR __wcschr
+#endif
+
+#define wcs a0
+#define wc a1
+#define vl_reg a2
+#define step a3
+#define first a4
+#define post_vl a5
+#define page_size a6
+#define page_end a7
+#define scalar t0
+#define page_mask t1
+
+#define vchunk v16
+#define vhit v0
+#define vtmp v1
+
+ENTRY (WCSCHR)
+.option push
+.option arch, +v
+ /* Check the first element before selecting the target-specific path. It
+ keeps the most latency-sensitive match and NUL cases out of both loop
+ setup and vector startup. */
+ lw scalar, 0(wcs)
+ beq scalar, wc, L(ret)
+ beqz scalar, L(not_found)
+ addi wcs, wcs, 4
+ beqz wc, L(zero_scalar)
+
+ /* Scan 32 elements in total before paying vector startup. Process four
+ elements per iteration to reduce pointer updates and code footprint,
+ with only one loop backedge per group. */
+ lw scalar, 0(wcs)
+ beq scalar, wc, L(ret)
+ beqz scalar, L(not_found)
+ lw scalar, 4(wcs)
+ beq scalar, wc, L(ret4)
+ beqz scalar, L(not_found)
+ lw scalar, 8(wcs)
+ beq scalar, wc, L(ret8)
+ beqz scalar, L(not_found)
+ addi wcs, wcs, 12
+ li step, 7
+L(scalar_loop):
+ lw scalar, 0(wcs)
+ beq scalar, wc, L(ret)
+ beqz scalar, L(not_found)
+ lw scalar, 4(wcs)
+ beq scalar, wc, L(ret4)
+ beqz scalar, L(not_found)
+ lw scalar, 8(wcs)
+ beq scalar, wc, L(ret8)
+ beqz scalar, L(not_found)
+ lw scalar, 12(wcs)
+ beq scalar, wc, L(ret12)
+ beqz scalar, L(not_found)
+ addi wcs, wcs, 16
+ addi step, step, -1
+ bnez step, L(scalar_loop)
+
+ vsetvli vl_reg, zero, e32, m1, ta, ma
+ vle32ff.v vchunk, (wcs)
+ vmseq.vx vtmp, vchunk, wc
+ vmseq.vi vhit, vchunk, 0
+ vmor.mm vhit, vhit, vtmp
+ vfirst.m first, vhit
+ bgez first, L(found_nonzero)
+ /* A fault-only-first load may shorten vl. Read it only on the miss
+ path, where it is needed to advance the input pointer. */
+ csrr post_vl, vl
+ slli post_vl, post_vl, 2
+ add wcs, wcs, post_vl
+ vsetvli vl_reg, zero, e32, m8, ta, ma
+ slli step, vl_reg, 2
+ li page_size, 4096
+ addi page_mask, page_size, -1
+
+L(long_loop):
+ and page_end, wcs, page_mask
+ add page_end, page_end, step
+ bgtu page_end, page_size, L(long_page_cross)
+ vle32.v vchunk, (wcs)
+ vmseq.vx vtmp, vchunk, wc
+ vmseq.vi vhit, vchunk, 0
+ vmor.mm vhit, vhit, vtmp
+ vfirst.m first, vhit
+ bgez first, L(found_nonzero)
+ add wcs, wcs, step
+ j L(long_loop)
+
+L(long_page_cross):
+ vle32ff.v vchunk, (wcs)
+ vmseq.vx vtmp, vchunk, wc
+ vmseq.vi vhit, vchunk, 0
+ vmor.mm vhit, vhit, vtmp
+ vfirst.m first, vhit
+ bgez first, L(found_nonzero)
+ csrr post_vl, vl
+ slli post_vl, post_vl, 2
+ add wcs, wcs, post_vl
+ vsetvli zero, zero, e32, m8, ta, ma
+ j L(long_loop)
+
+L(found_nonzero):
+ slli first, first, 2
+ add wcs, wcs, first
+ lw scalar, 0(wcs)
+ beq scalar, wc, L(ret)
+L(not_found):
+ li wcs, 0
+L(ret):
+ ret
+L(ret4):
+ addi wcs, wcs, 4
+ ret
+L(ret8):
+ addi wcs, wcs, 8
+ ret
+L(ret12):
+ addi wcs, wcs, 12
+ ret
+
+ /* Searching for L'\0' needs only the NUL mask and does not require a
+ scalar reload after vfirst.m. */
+L(zero_scalar):
+ /* The first element was already checked. Scan elements 1..39 with the
+ same four-way unrolling before entering the NUL-only vector path. */
+ lw scalar, 0(wcs)
+ beqz scalar, L(ret)
+ lw scalar, 4(wcs)
+ beqz scalar, L(ret4)
+ lw scalar, 8(wcs)
+ beqz scalar, L(ret8)
+ addi wcs, wcs, 12
+ li step, 9
+L(zero_scalar_loop):
+ lw scalar, 0(wcs)
+ beqz scalar, L(ret)
+ lw scalar, 4(wcs)
+ beqz scalar, L(ret4)
+ lw scalar, 8(wcs)
+ beqz scalar, L(ret8)
+ lw scalar, 12(wcs)
+ beqz scalar, L(ret12)
+ addi wcs, wcs, 16
+ addi step, step, -1
+ bnez step, L(zero_scalar_loop)
+
+L(zero_first_vector):
+ vsetvli vl_reg, zero, e32, m1, ta, ma
+ vle32ff.v vchunk, (wcs)
+ vmseq.vi vhit, vchunk, 0
+ vfirst.m first, vhit
+ bgez first, L(found_zero)
+ csrr post_vl, vl
+ slli post_vl, post_vl, 2
+ add wcs, wcs, post_vl
+ vsetvli vl_reg, zero, e32, m8, ta, ma
+ slli step, vl_reg, 2
+ li page_size, 4096
+ addi page_mask, page_size, -1
+
+L(zero_long_loop):
+ and page_end, wcs, page_mask
+ add page_end, page_end, step
+ bgtu page_end, page_size, L(zero_long_page_cross)
+ vle32.v vchunk, (wcs)
+ vmseq.vi vhit, vchunk, 0
+ vfirst.m first, vhit
+ bgez first, L(found_zero)
+ add wcs, wcs, step
+ j L(zero_long_loop)
+
+L(zero_long_page_cross):
+ vle32ff.v vchunk, (wcs)
+ vmseq.vi vhit, vchunk, 0
+ vfirst.m first, vhit
+ bgez first, L(found_zero)
+ csrr post_vl, vl
+ slli post_vl, post_vl, 2
+ add wcs, wcs, post_vl
+ vsetvli zero, zero, e32, m8, ta, ma
+ j L(zero_long_loop)
+
+L(found_zero):
+ slli first, first, 2
+ add wcs, wcs, first
+ ret
+.option pop
+END (WCSCHR)
@@ -59,3 +59,10 @@ CFLAGS-strlen-generic.op = $(no-stack-protector)
CFLAGS-memcpy_noalignment.c += -mno-strict-align
endif
+
+ifeq ($(subdir),wcsmbs)
+sysdep_routines += \
+ wcschr-generic \
+ wcschr-vector \
+ # sysdep_routines
+endif
@@ -18,6 +18,7 @@
#include <ifunc-impl-list.h>
#include <string.h>
+#include <wchar.h>
#include <sys/hwprobe.h>
size_t
@@ -120,5 +121,10 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
__strncpy_vector)
IFUNC_IMPL_ADD (array, i, strncpy, 1, __strncpy_generic))
+ IFUNC_IMPL (i, name, wcschr,
+ IFUNC_IMPL_ADD (array, i, wcschr, rvv_enabled,
+ __wcschr_vector)
+ IFUNC_IMPL_ADD (array, i, wcschr, 1, __wcschr_generic))
+
return 0;
}
new file mode 100644
@@ -0,0 +1,44 @@
+/* Multiple versions of wcschr.
+ All versions must be listed in ifunc-impl-list.c.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Licensed under the GNU Lesser General Public License v2.1+; see COPYING.LESSER. */
+
+#if IS_IN (libc)
+# undef wcschr
+# define wcschr __redirect_wcschr
+# include <stdint.h>
+# include <wchar.h>
+# include <ifunc-init.h>
+# include <riscv-ifunc.h>
+# include <sys/hwprobe.h>
+
+extern __typeof (__redirect_wcschr) __libc_wcschr;
+
+extern __typeof (__redirect_wcschr) __wcschr_generic attribute_hidden;
+extern __typeof (__redirect_wcschr) __wcschr_vector attribute_hidden;
+
+static inline __typeof (__redirect_wcschr) *
+select_wcschr_ifunc (uint64_t dl_hwcap, __riscv_hwprobe_t hwprobe_func)
+{
+ unsigned long long 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 __wcschr_vector;
+
+ return __wcschr_generic;
+}
+
+riscv_libc_ifunc (__libc_wcschr, select_wcschr_ifunc);
+
+# undef wcschr
+strong_alias (__libc_wcschr, wcschr);
+strong_alias (wcschr, __wcschr)
+# ifdef SHARED
+__hidden_ver1 (wcschr, __GI_wcschr, __redirect_wcschr)
+ __attribute__ ((visibility ("hidden"))) __attribute_copy__ (wcschr);
+# endif
+#else
+# include <wcsmbs/wcschr.c>
+#endif