[PATCH 15/17] riscv: add vectorized strstr

Pincheng Wang <[email protected]> Thu, 16 Jul 2026 23:00:46 +0800
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
The vector implementation uses a fault-only-first (vle8ff.v) scan to find
candidate positions of the needle's first byte, then verifies each
candidate with a byte loop that stops at the needle's NUL or at a
mismatch; both phases stop at a NUL, so no load ever crosses past the end
of either string into an unmapped page.  This provides significant
performance improvements on RVV-capable hardware.  Use conditional
compilation to fall back to the generic implementation when __riscv_vector
is not available, maintaining compatibility with non-vector RISC-V
systems.

Signed-off-by: Pincheng Wang <[email protected]>
---
 newlib/libc/machine/riscv/Makefile.inc |  4 +-
 newlib/libc/machine/riscv/strstr-asm.S | 55 ++++++++++++++++++++++++++
 newlib/libc/machine/riscv/strstr.c     |  5 +++
 3 files changed, 63 insertions(+), 1 deletion(-)
 create mode 100644 newlib/libc/machine/riscv/strstr-asm.S
 create mode 100644 newlib/libc/machine/riscv/strstr.c

diff --git a/newlib/libc/machine/riscv/Makefile.inc b/newlib/libc/machine/riscv/Makefile.inc
index d145a3024..c2d918d03 100644
--- a/newlib/libc/machine/riscv/Makefile.inc
+++ b/newlib/libc/machine/riscv/Makefile.inc
@@ -47,4 +47,6 @@ libc_a_SOURCES += \
 	%D%/strrchr-asm.S \
 	%D%/strrchr.c \
 	%D%/strspn-asm.S \
-	%D%/strspn.c
+	%D%/strspn.c \
+	%D%/strstr-asm.S \
+	%D%/strstr.c
diff --git a/newlib/libc/machine/riscv/strstr-asm.S b/newlib/libc/machine/riscv/strstr-asm.S
new file mode 100644
index 000000000..f3e969c68
--- /dev/null
+++ b/newlib/libc/machine/riscv/strstr-asm.S
@@ -0,0 +1,55 @@
+#if defined(__riscv_vector) && __riscv_xlen == 64 && !defined(__OPTIMIZE_SIZE__) && !defined(PREFER_SIZE_OVER_SPEED)
+.text
+.option push
+.option arch, +v
+/* char *strstr(const char *a0, const char *a1)
+   Locate substring a1 in a0.  A vectorized scan (vle8ff, page-boundary safe)
+   finds candidate positions of needle[0]; each candidate is verified with a
+   byte loop that stops at the needle's NUL (match) or at a mismatch/the
+   haystack NUL.  Both phases stop at a NUL, so no load ever crosses past the
+   end of either string into an unmapped page.  */
+.global strstr
+.type strstr, @function
+strstr:
+  lbu t0, 0(a1)            /* c0 = needle[0] */
+  beqz t0, .Lret_h         /* empty needle -> return haystack */
+.Lloop:
+  vsetvli t1, zero, e8, m1, ta, ma
+  vle8ff.v v8, (a0)
+  csrr t1, vl
+  vmseq.vx v1, v8, t0      /* lanes == c0 */
+  vmseq.vi v2, v8, 0       /* NUL lanes */
+  vfirst.m t2, v1
+  vfirst.m t3, v2
+  bltz t2, .Lno_c0         /* no c0 in this chunk */
+  bltz t3, .Lcandidate     /* c0 present, no NUL -> candidate */
+  bltu t2, t3, .Lcandidate /* c0 occurs before NUL */
+  /* NUL at or before c0 -> end of haystack reached */
+.Lnotfound:
+  li a0, 0
+  ret
+.Lno_c0:
+  bgez t3, .Lnotfound      /* hit NUL with no c0 -> not found */
+  add a0, a0, t1
+  j .Lloop
+.Lcandidate:
+  add a0, a0, t2           /* a0 -> candidate (h[*]==c0) */
+  mv t4, a0                /* haystack cursor */
+  mv t5, a1                /* needle cursor */
+.Lverify:
+  lbu t6, 0(t5)
+  beqz t6, .Lmatch         /* needle exhausted -> match at a0 */
+  lbu a2, 0(t4)
+  bne a2, t6, .Ladvance    /* mismatch (a2 may be NUL) */
+  addi t4, t4, 1
+  addi t5, t5, 1
+  j .Lverify
+.Ladvance:
+  addi a0, a0, 1           /* slide one byte and rescan */
+  j .Lloop
+.Lmatch:
+.Lret_h:
+  ret
+.size strstr, .-strstr
+.option pop
+#endif
diff --git a/newlib/libc/machine/riscv/strstr.c b/newlib/libc/machine/riscv/strstr.c
new file mode 100644
index 000000000..55c59d64a
--- /dev/null
+++ b/newlib/libc/machine/riscv/strstr.c
@@ -0,0 +1,5 @@
+#if defined(__OPTIMIZE_SIZE__) || defined(PREFER_SIZE_OVER_SPEED) || !defined(__riscv_vector) || __riscv_xlen != 64
+# include "../../string/strstr.c"
+#else
+/* strstr defined in strstr-asm.S */
+#endif
-- 
2.39.5