This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 931d5f50d446ae0f5c66ee783a232192af3bcfbc
Author: ganjing <[email protected]>
AuthorDate: Tue Aug 11 14:22:35 2026 +0800

    libs/libc/risc-v: Add optimized strlcpy.
    
    Add word-at-a-time strlcpy using DETECTNULL for both the copy phase
    and the strlen tail when truncated.  The copy loop aligns src and
    processes a register at a time, falling to bytewise for the last word
    containing the terminator.  When truncated, the remaining src length
    is measured with a second word-at-a-time loop.
    
    strlcpy has 46 call sites in a typical kernel image (more than strcpy)
    and is not covered by newlib OPTSPEED, making it a high-value target.
    
    Assisted-by: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: ganjing <[email protected]>
---
 libs/libc/machine/risc-v/CMakeLists.txt |   4 +
 libs/libc/machine/risc-v/Kconfig        |   9 ++
 libs/libc/machine/risc-v/Make.defs      |   4 +
 libs/libc/machine/risc-v/arch_strlcpy.S | 201 ++++++++++++++++++++++++++++++++
 4 files changed, 218 insertions(+)

diff --git a/libs/libc/machine/risc-v/CMakeLists.txt 
b/libs/libc/machine/risc-v/CMakeLists.txt
index ab4bd0c099f..f5f0501c5dc 100644
--- a/libs/libc/machine/risc-v/CMakeLists.txt
+++ b/libs/libc/machine/risc-v/CMakeLists.txt
@@ -84,6 +84,10 @@ if(CONFIG_RISCV_STRNCMP)
   list(APPEND SRCS arch_strncmp.S)
 endif()
 
+if(CONFIG_RISCV_STRLCPY)
+  list(APPEND SRCS arch_strlcpy.S)
+endif()
+
 if(CONFIG_RISCV_STRCAT)
   list(APPEND SRCS arch_strcat.S)
 endif()
diff --git a/libs/libc/machine/risc-v/Kconfig b/libs/libc/machine/risc-v/Kconfig
index 3392ec883b9..112d6582fa8 100644
--- a/libs/libc/machine/risc-v/Kconfig
+++ b/libs/libc/machine/risc-v/Kconfig
@@ -24,6 +24,7 @@ config RISCV_STRING_FUNCTION
        select RISCV_STRRCHR
        select RISCV_STRNCMP
        select RISCV_STRCAT
+       select RISCV_STRLCPY
 
 config RISCV_MEMCPY
        bool "Enable optimized memcpy() for RISC-V"
@@ -159,3 +160,11 @@ config RISCV_STRCAT
        depends on ARCH_TOOLCHAIN_GNU
        ---help---
                Enable optimized RISC-V specific strcat() library function
+
+config RISCV_STRLCPY
+       bool "Enable optimized strlcpy() for RISC-V"
+       default n
+       select LIBC_ARCH_STRLCPY
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strlcpy() library function
diff --git a/libs/libc/machine/risc-v/Make.defs 
b/libs/libc/machine/risc-v/Make.defs
index c7a1b125f2b..7159b372f37 100644
--- a/libs/libc/machine/risc-v/Make.defs
+++ b/libs/libc/machine/risc-v/Make.defs
@@ -84,6 +84,10 @@ ifeq ($(CONFIG_RISCV_STRNCMP),y)
 ASRCS += arch_strncmp.S
 endif
 
+ifeq ($(CONFIG_RISCV_STRLCPY),y)
+ASRCS += arch_strlcpy.S
+endif
+
 ifeq ($(CONFIG_RISCV_STRCAT),y)
 ASRCS += arch_strcat.S
 endif
diff --git a/libs/libc/machine/risc-v/arch_strlcpy.S 
b/libs/libc/machine/risc-v/arch_strlcpy.S
new file mode 100644
index 00000000000..6d18cf40e73
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strlcpy.S
@@ -0,0 +1,201 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strlcpy.S
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#include "libc.h"
+
+#ifdef LIBC_BUILD_STRLCPY
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strlcpy)
+.type  ARCH_LIBCFUN(strlcpy), @function
+.align 2
+
+/************************************************************************************
+ * Name: strlcpy
+ *
+ * size_t strlcpy(char *dst, const char *src, size_t size)
+ *
+ * Copy at most size-1 bytes from src to dst, null-terminate (if size > 0),
+ * and return strlen(src).  Word-at-a-time DETECTNULL for both the copy
+ * phase and the strlen tail when truncated.
+ 
************************************************************************************/
+
+/* DETECTNULL constants loaded from .srodata */
+
+       .section .srodata, "a"
+       .align  SZREG
+.Lmask_01:
+#if SZREG == 8
+       .dword  0x0101010101010101
+#else
+       .word   0x01010101
+#endif
+.Lmask_80:
+#if SZREG == 8
+       .dword  0x8080808080808080
+#else
+       .word   0x80808080
+#endif
+
+       .text
+
+ARCH_LIBCFUN(strlcpy):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       mv      t6, a1                  /* save src start for return value */
+
+       /* size == 0: skip copy, just measure src */
+
+       beqz    a2, .Lmeasure
+
+       addi    a2, a2, -1              /* reserve space for null terminator */
+
+       /* Bytewise copy head: align src to SZREG boundary */
+
+.Lcopy_head:
+       beqz    a2, .Ltruncated
+       andi    t0, a1, SZREG-1
+       beqz    t0, .Lcopy_word_setup
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Ldone
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       addi    a2, a2, -1
+       j       .Lcopy_head
+
+.Lcopy_word_setup:
+       /* Load DETECTNULL masks */
+
+       lla     t2, .Lmask_01
+       lla     t3, .Lmask_80
+       REG_L   t2, 0(t2)               /* t2 = 0x0101...01 */
+       REG_L   t3, 0(t3)               /* t3 = 0x8080...80 */
+
+       /* Word copy loop: need at least SZREG bytes remaining in size */
+
+       li      t4, SZREG
+
+.Lcopy_word:
+       bltu    a2, t4, .Lcopy_tail
+       REG_L   t0, 0(a1)
+
+       /* DETECTNULL: (word - 0x0101..01) & ~word & 0x8080..80 */
+
+       sub     t1, t0, t2
+       not     t5, t0
+       and     t1, t1, t5
+       and     t1, t1, t3
+       bnez    t1, .Lcopy_last_word
+
+       /* No null in this word, store it */
+
+       REG_S   t0, 0(a0)
+       addi    a0, a0, SZREG
+       addi    a1, a1, SZREG
+       sub     a2, a2, t4
+       j       .Lcopy_word
+
+.Lcopy_last_word:
+       /* Null found within word: copy bytes until null */
+
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Ldone
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       j       .Lcopy_last_word
+
+.Lcopy_tail:
+       /* Less than SZREG bytes of size left: bytewise copy */
+
+       beqz    a2, .Ltruncated
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Ldone
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       addi    a2, a2, -1
+       j       .Lcopy_tail
+
+.Ltruncated:
+       /* Null-terminate dst */
+
+       sb      zero, 0(a0)
+
+       /* Measure remaining src length with word-at-a-time */
+
+.Lmeasure:
+       /* Bytewise to align src */
+
+       andi    t0, a1, SZREG-1
+       beqz    t0, .Lmeasure_word_setup
+
+.Lmeasure_head:
+       lbu     t0, 0(a1)
+       beqz    t0, .Lreturn
+       addi    a1, a1, 1
+       andi    t0, a1, SZREG-1
+       bnez    t0, .Lmeasure_head
+
+.Lmeasure_word_setup:
+       /* Load masks if not already loaded (from truncated path) */
+
+       lla     t2, .Lmask_01
+       lla     t3, .Lmask_80
+       REG_L   t2, 0(t2)
+       REG_L   t3, 0(t3)
+
+.Lmeasure_word:
+       REG_L   t0, 0(a1)
+       sub     t1, t0, t2
+       not     t5, t0
+       and     t1, t1, t5
+       and     t1, t1, t3
+       bnez    t1, .Lmeasure_tail
+       addi    a1, a1, SZREG
+       j       .Lmeasure_word
+
+.Lmeasure_tail:
+       /* Find exact null position */
+
+       lbu     t0, 0(a1)
+       beqz    t0, .Lreturn
+       addi    a1, a1, 1
+       j       .Lmeasure_tail
+
+.Ldone:
+       /* Normal completion: null found during copy */
+
+.Lreturn:
+       /* Return strlen(src) = current src pos - original start */
+
+       sub     a0, a1, t6
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strlcpy), .-ARCH_LIBCFUN(strlcpy)
+
+#endif

Reply via email to