[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