[PATCH 09/17] riscv: add vectorized strncpy

Pincheng Wang <[email protected]> Thu, 16 Jul 2026 23:00:40 +0800
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
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 <[email protected]>
---
 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

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