[09/17] riscv: add vectorized strncpy

Message ID 20260716150048.216052-10-pincheng.plct@isrc.iscas.ac.cn
State New
Headers
Series riscv: vectorized str* routines |

Commit Message

Pincheng Wang July 16, 2026, 3 p.m. UTC
  The vector implementation copies the source in vector-length chunks using
fault-only-first loads (vle8ff.v); vl is capped by the remaining count so
the source is never read past byte n or across an unmapped page, and the
tail after a NUL is zero-filled with wide m8 stores.  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 <pincheng.plct@isrc.iscas.ac.cn>
---
 newlib/libc/machine/riscv/Makefile.inc  |  2 ++
 newlib/libc/machine/riscv/strncpy-asm.S | 46 +++++++++++++++++++++++++
 newlib/libc/machine/riscv/strncpy.c     |  5 +++
 3 files changed, 53 insertions(+)
 create mode 100644 newlib/libc/machine/riscv/strncpy-asm.S
 create mode 100644 newlib/libc/machine/riscv/strncpy.c
  

Comments

Kito Cheng Aug. 5, 2026, 1:51 p.m. UTC | #1
> +strncpy:
> +  mv a3, a0                 /* save dest for return */

This comment seems misleading, the comments seems like trying to say
we save the a0 to a3 for return,
but actually we just didn't change a0 at all, so called it save dest
for return is little bit weird.
  

Patch

diff --git a/newlib/libc/machine/riscv/Makefile.inc b/newlib/libc/machine/riscv/Makefile.inc
index 334d4351b..db572be82 100644
--- a/newlib/libc/machine/riscv/Makefile.inc
+++ b/newlib/libc/machine/riscv/Makefile.inc
@@ -32,6 +32,8 @@  libc_a_SOURCES += \
 	%D%/strlen.c \
 	%D%/strncmp-asm.S \
 	%D%/strncmp.c \
+	%D%/strncpy-asm.S \
+	%D%/strncpy.c \
 	%D%/strnlen-asm.S \
 	%D%/strnlen.c \
 	%D%/strrchr-asm.S \
diff --git a/newlib/libc/machine/riscv/strncpy-asm.S b/newlib/libc/machine/riscv/strncpy-asm.S
new file mode 100644
index 000000000..b220fde44
--- /dev/null
+++ b/newlib/libc/machine/riscv/strncpy-asm.S
@@ -0,0 +1,46 @@ 
+#if defined(__riscv_vector) && __riscv_xlen == 64 && !defined(__OPTIMIZE_SIZE__) && !defined(PREFER_SIZE_OVER_SPEED)
+.text
+.option push
+.option arch, +v
+/* char *strncpy(char *a0, const char *a1, size_t a2)
+   Copy at most a2 bytes; if a NUL is met in src, the rest of dest is
+   zero-filled.  Source is read with vle8ff (page-boundary safe) and vl is
+   also capped by the remaining count so we never read past byte a2.  */
+.global strncpy
+.type strncpy, @function
+strncpy:
+  mv a3, a0                 /* save dest for return */
+  beqz a2, .Ldone
+.Lloop:
+  vsetvli zero, a2, e8, m1, ta, ma
+  vle8ff.v v8, (a1)
+  vmseq.vi v1, v8, 0        /* NUL mask */
+  csrr a4, vl               /* actual count (may be truncated by fault) */
+  vfirst.m a5, v1
+  bgez a5, .Lnul
+  /* no NUL: copy whole chunk */
+  vse8.v v8, (a3)
+  add a1, a1, a4
+  add a3, a3, a4
+  sub a2, a2, a4
+  bnez a2, .Lloop
+  j .Ldone
+.Lnul:
+  /* NUL at index a5: copy the [0,a5) prefix, then zero-fill the rest */
+  vmsbf.m v0, v1            /* mask = elements before first NUL */
+  vse8.v v8, (a3), v0.t
+  add a3, a3, a5
+  sub a2, a2, a5            /* remaining bytes to zero (>=1) */
+.Lfill:
+  beqz a2, .Ldone
+  vsetvli a4, a2, e8, m8, ta, ma
+  vmv.v.i v8, 0
+  vse8.v v8, (a3)
+  add a3, a3, a4
+  sub a2, a2, a4
+  bnez a2, .Lfill
+.Ldone:
+  ret
+.size strncpy, .-strncpy
+.option pop
+#endif
diff --git a/newlib/libc/machine/riscv/strncpy.c b/newlib/libc/machine/riscv/strncpy.c
new file mode 100644
index 000000000..19f8f47db
--- /dev/null
+++ b/newlib/libc/machine/riscv/strncpy.c
@@ -0,0 +1,5 @@ 
+#if defined(__OPTIMIZE_SIZE__) || defined(PREFER_SIZE_OVER_SPEED) || !defined(__riscv_vector) || __riscv_xlen != 64
+# include "../../string/strncpy.c"
+#else
+/* strncpy defined in strncpy-asm.S */
+#endif