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 9b20c629fe020775c9b0e1249904fb6695ca9447
Author: ganjing <[email protected]>
AuthorDate: Tue Aug 11 12:23:53 2026 +0800

    libs/libc/risc-v: Add optimized string and memory functions.
    
    Add assembly-optimized implementations for 14 string/memory functions
    using word-at-a-time techniques (DETECTNULL, broadcast+XOR) and
    XLEN-adaptive macros for both RV32 and RV64:
    
     - memmove: direction check + forward tail to memcpy, reverse path
       with 16xSZREG unroll and shift-merge for misaligned src.
     - memcmp: word-granularity compare when both pointers share alignment,
       bytewise fallback for mismatched pointers.
     - memchr: broadcast target byte, XOR with each word, DETECTNULL to
       find matches. Counter-based bounds (no pointer overflow).
     - strlen: DETECTNULL word loop, constants loaded from .srodata.
     - strnlen: strlen with counter-based length limit.
     - strcpy/strncpy: word loop with DETECTNULL, zero-fill remainder
       for strncpy. strncpy reuses strcpy via #define USE_AS_STRNCPY.
     - stpcpy/stpncpy: reuse strcpy/strncpy via #define USE_AS_STPCPY.
     - strchr/strchrnul: broadcast+XOR detecting both target char and
       null simultaneously. strchrnul reuses strchr via #define.
     - strrchr: forward scan recording last match position.
     - strncmp: word-at-a-time compare with null detection and counter.
     - strcat: strlen(dst) then strcpy(dst_end, src) word-at-a-time.
    
    Each function is independently selectable via CONFIG_RISCV_<FUNC>,
    or all enabled together with CONFIG_RISCV_STRING_FUNCTION=y.
    
    Assisted-by: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: ganjing <[email protected]>
---
 libs/libc/libc.h                          |  10 ++
 libs/libc/machine/risc-v/CMakeLists.txt   |  56 ++++++++++
 libs/libc/machine/risc-v/Kconfig          | 125 ++++++++++++++++++++++
 libs/libc/machine/risc-v/Make.defs        |  56 ++++++++++
 libs/libc/machine/risc-v/arch_memchr.S    | 127 ++++++++++++++++++++++
 libs/libc/machine/risc-v/arch_memcmp.S    |  96 +++++++++++++++++
 libs/libc/machine/risc-v/arch_memmove.S   | 107 +++++++++++++++++++
 libs/libc/machine/risc-v/arch_stpcpy.S    |  27 +++++
 libs/libc/machine/risc-v/arch_stpncpy.S   |  28 +++++
 libs/libc/machine/risc-v/arch_strcat.S    | 152 ++++++++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strchr.S    | 171 ++++++++++++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strchrnul.S |  27 +++++
 libs/libc/machine/risc-v/arch_strcpy.S    | 163 ++++++++++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strlen.S    | 117 ++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strncmp.S   | 116 ++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strncpy.S   |  27 +++++
 libs/libc/machine/risc-v/arch_strnlen.S   | 123 +++++++++++++++++++++
 libs/libc/machine/risc-v/arch_strrchr.S   | 166 +++++++++++++++++++++++++++++
 18 files changed, 1694 insertions(+)

diff --git a/libs/libc/libc.h b/libs/libc/libc.h
index 9701fb0af9c..a8197a424ce 100644
--- a/libs/libc/libc.h
+++ b/libs/libc/libc.h
@@ -155,6 +155,16 @@
 #  define LIBC_BUILD_STRRCHR
 #endif
 
+#if ((!defined(CONFIG_LIBC_PREVENT_STPCPY_USER) && !defined(__KERNEL__))  || \
+     (!defined(CONFIG_LIBC_PREVENT_STPCPY_KERNEL) && defined(__KERNEL__)))
+#  define LIBC_BUILD_STPCPY
+#endif
+
+#if ((!defined(CONFIG_LIBC_PREVENT_STPNCPY_USER) && !defined(__KERNEL__))  || \
+     (!defined(CONFIG_LIBC_PREVENT_STPNCPY_KERNEL) && defined(__KERNEL__)))
+#  define LIBC_BUILD_STPNCPY
+#endif
+
 #ifdef CONFIG_MM_KASAN
 #  define ARCH_LIBCFUN(x)  arch_##x
 #else
diff --git a/libs/libc/machine/risc-v/CMakeLists.txt 
b/libs/libc/machine/risc-v/CMakeLists.txt
index 93ef36ef3d6..ab4bd0c099f 100644
--- a/libs/libc/machine/risc-v/CMakeLists.txt
+++ b/libs/libc/machine/risc-v/CMakeLists.txt
@@ -28,10 +28,66 @@ if(CONFIG_RISCV_MEMSET)
   list(APPEND SRCS arch_memset.S)
 endif()
 
+if(CONFIG_RISCV_MEMMOVE)
+  list(APPEND SRCS arch_memmove.S)
+endif()
+
+if(CONFIG_RISCV_MEMCMP)
+  list(APPEND SRCS arch_memcmp.S)
+endif()
+
+if(CONFIG_RISCV_MEMCHR)
+  list(APPEND SRCS arch_memchr.S)
+endif()
+
 if(CONFIG_RISCV_STRCMP)
   list(APPEND SRCS arch_strcmp.S)
 endif()
 
+if(CONFIG_RISCV_STRLEN)
+  list(APPEND SRCS arch_strlen.S)
+endif()
+
+if(CONFIG_RISCV_STRNLEN)
+  list(APPEND SRCS arch_strnlen.S)
+endif()
+
+if(CONFIG_RISCV_STRCPY)
+  list(APPEND SRCS arch_strcpy.S)
+endif()
+
+if(CONFIG_RISCV_STRNCPY)
+  list(APPEND SRCS arch_strncpy.S)
+endif()
+
+if(CONFIG_RISCV_STPCPY)
+  list(APPEND SRCS arch_stpcpy.S)
+endif()
+
+if(CONFIG_RISCV_STPNCPY)
+  list(APPEND SRCS arch_stpncpy.S)
+endif()
+
+if(CONFIG_RISCV_STRCHR)
+  list(APPEND SRCS arch_strchr.S)
+endif()
+
+if(CONFIG_RISCV_STRCHRNUL)
+  list(APPEND SRCS arch_strchrnul.S)
+endif()
+
+if(CONFIG_RISCV_STRRCHR)
+  list(APPEND SRCS arch_strrchr.S)
+endif()
+
+if(CONFIG_RISCV_STRNCMP)
+  list(APPEND SRCS arch_strncmp.S)
+endif()
+
+if(CONFIG_RISCV_STRCAT)
+  list(APPEND SRCS arch_strcat.S)
+endif()
+
 if(CONFIG_ARCH_SETJMP_H)
   list(APPEND SRCS arch_setjmp.S)
 endif()
diff --git a/libs/libc/machine/risc-v/Kconfig b/libs/libc/machine/risc-v/Kconfig
index 2e1dc8ff37b..3392ec883b9 100644
--- a/libs/libc/machine/risc-v/Kconfig
+++ b/libs/libc/machine/risc-v/Kconfig
@@ -9,7 +9,21 @@ config RISCV_STRING_FUNCTION
        depends on ARCH_TOOLCHAIN_GNU
        select RISCV_MEMCPY
        select RISCV_MEMSET
+       select RISCV_MEMMOVE
+       select RISCV_MEMCMP
+       select RISCV_MEMCHR
        select RISCV_STRCMP
+       select RISCV_STRLEN
+       select RISCV_STRNLEN
+       select RISCV_STRCPY
+       select RISCV_STRNCPY
+       select RISCV_STPCPY
+       select RISCV_STPNCPY
+       select RISCV_STRCHR
+       select RISCV_STRCHRNUL
+       select RISCV_STRRCHR
+       select RISCV_STRNCMP
+       select RISCV_STRCAT
 
 config RISCV_MEMCPY
        bool "Enable optimized memcpy() for RISC-V"
@@ -26,6 +40,30 @@ config RISCV_MEMSET
        ---help---
                Enable optimized RISC-V specific memset() library function
 
+config RISCV_MEMMOVE
+       bool "Enable optimized memmove() for RISC-V"
+       default n
+       select LIBC_ARCH_MEMMOVE
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific memmove() library function
+
+config RISCV_MEMCMP
+       bool "Enable optimized memcmp() for RISC-V"
+       default n
+       select LIBC_ARCH_MEMCMP
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific memcmp() library function
+
+config RISCV_MEMCHR
+       bool "Enable optimized memchr() for RISC-V"
+       default n
+       select LIBC_ARCH_MEMCHR
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific memchr() library function
+
 config RISCV_STRCMP
        bool "Enable optimized strcmp() for RISC-V"
        default n
@@ -34,3 +72,90 @@ config RISCV_STRCMP
        ---help---
                Enable optimized RISC-V specific strcmp() library function
 
+config RISCV_STRLEN
+       bool "Enable optimized strlen() for RISC-V"
+       default n
+       select LIBC_ARCH_STRLEN
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strlen() library function
+
+config RISCV_STRNLEN
+       bool "Enable optimized strnlen() for RISC-V"
+       default n
+       select LIBC_ARCH_STRNLEN
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strnlen() library function
+
+config RISCV_STRCPY
+       bool "Enable optimized strcpy() for RISC-V"
+       default n
+       select LIBC_ARCH_STRCPY
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strcpy() library function
+
+config RISCV_STRNCPY
+       bool "Enable optimized strncpy() for RISC-V"
+       default n
+       select LIBC_ARCH_STRNCPY
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strncpy() library function
+
+config RISCV_STPCPY
+       bool "Enable optimized stpcpy() for RISC-V"
+       default n
+       select LIBC_ARCH_STPCPY
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific stpcpy() library function
+
+config RISCV_STPNCPY
+       bool "Enable optimized stpncpy() for RISC-V"
+       default n
+       select LIBC_ARCH_STPNCPY
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific stpncpy() library function
+
+config RISCV_STRCHR
+       bool "Enable optimized strchr() for RISC-V"
+       default n
+       select LIBC_ARCH_STRCHR
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strchr() library function
+
+config RISCV_STRCHRNUL
+       bool "Enable optimized strchrnul() for RISC-V"
+       default n
+       select LIBC_ARCH_STRCHRNUL
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strchrnul() library function
+
+config RISCV_STRRCHR
+       bool "Enable optimized strrchr() for RISC-V"
+       default n
+       select LIBC_ARCH_STRRCHR
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strrchr() library function
+
+config RISCV_STRNCMP
+       bool "Enable optimized strncmp() for RISC-V"
+       default n
+       select LIBC_ARCH_STRNCMP
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strncmp() library function
+
+config RISCV_STRCAT
+       bool "Enable optimized strcat() for RISC-V"
+       default n
+       select LIBC_ARCH_STRCAT
+       depends on ARCH_TOOLCHAIN_GNU
+       ---help---
+               Enable optimized RISC-V specific strcat() library function
diff --git a/libs/libc/machine/risc-v/Make.defs 
b/libs/libc/machine/risc-v/Make.defs
index ec76a24eed4..c7a1b125f2b 100644
--- a/libs/libc/machine/risc-v/Make.defs
+++ b/libs/libc/machine/risc-v/Make.defs
@@ -28,10 +28,66 @@ ifeq ($(CONFIG_RISCV_MEMSET),y)
 ASRCS += arch_memset.S
 endif
 
+ifeq ($(CONFIG_RISCV_MEMMOVE),y)
+ASRCS += arch_memmove.S
+endif
+
+ifeq ($(CONFIG_RISCV_MEMCMP),y)
+ASRCS += arch_memcmp.S
+endif
+
+ifeq ($(CONFIG_RISCV_MEMCHR),y)
+ASRCS += arch_memchr.S
+endif
+
 ifeq ($(CONFIG_RISCV_STRCMP),y)
 ASRCS += arch_strcmp.S
 endif
 
+ifeq ($(CONFIG_RISCV_STRLEN),y)
+ASRCS += arch_strlen.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRNLEN),y)
+ASRCS += arch_strnlen.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRCPY),y)
+ASRCS += arch_strcpy.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRNCPY),y)
+ASRCS += arch_strncpy.S
+endif
+
+ifeq ($(CONFIG_RISCV_STPCPY),y)
+ASRCS += arch_stpcpy.S
+endif
+
+ifeq ($(CONFIG_RISCV_STPNCPY),y)
+ASRCS += arch_stpncpy.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRCHR),y)
+ASRCS += arch_strchr.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRCHRNUL),y)
+ASRCS += arch_strchrnul.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRRCHR),y)
+ASRCS += arch_strrchr.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRNCMP),y)
+ASRCS += arch_strncmp.S
+endif
+
+ifeq ($(CONFIG_RISCV_STRCAT),y)
+ASRCS += arch_strcat.S
+endif
+
 ifeq ($(CONFIG_ARCH_SETJMP_H),y)
 ASRCS += arch_setjmp.S
 endif
diff --git a/libs/libc/machine/risc-v/arch_memchr.S 
b/libs/libc/machine/risc-v/arch_memchr.S
new file mode 100644
index 00000000000..cf57eba799d
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_memchr.S
@@ -0,0 +1,127 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_memchr.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_MEMCHR
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(memchr)
+.type  ARCH_LIBCFUN(memchr), @function
+.align 2
+
+/************************************************************************************
+ * Name: memchr
+ *
+ * void *memchr(const void *s, int c, size_t n)
+ *
+ * Broadcast + XOR + DETECTNULL word-at-a-time search.
+ 
************************************************************************************/
+ARCH_LIBCFUN(memchr):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       beqz    a2, .Lnotfound
+       andi    a1, a1, 0xff
+       mv      t6, a2                  /* t6 = remaining count */
+
+       /* Byte-by-byte head until aligned */
+
+.Lhead:
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Laligned
+       beqz    t6, .Lnotfound
+       lbu     t0, 0(a0)
+       beq     t0, a1, .Lfound
+       addi    a0, a0, 1
+       addi    t6, t6, -1
+       j       .Lhead
+
+.Laligned:
+       /* Broadcast c to full XLEN register */
+
+       slli    t0, a1, 8
+       or      t0, t0, a1
+       slli    t1, t0, 16
+       or      a6, t0, t1
+#if SZREG == 8
+       slli    t0, a6, 32
+       or      a6, a6, t0
+       lla     t2, .Lmc_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lmc_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+       /* Word loop */
+
+.Lword_loop:
+       li      t0, SZREG
+       bltu    t6, t0, .Ltail          /* not enough bytes for full word */
+       REG_L   t0, 0(a0)
+       xor     t0, t0, a6              /* matching bytes become 0x00 */
+
+       /* DETECTNULL on xored value */
+
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+       bnez    t1, .Ltail              /* found match in this word */
+       addi    a0, a0, SZREG
+       addi    t6, t6, -SZREG
+       j       .Lword_loop
+
+       /* Byte-by-byte tail */
+
+.Ltail:
+       beqz    t6, .Lnotfound
+       lbu     t0, 0(a0)
+       beq     t0, a1, .Lfound
+       addi    a0, a0, 1
+       addi    t6, t6, -1
+       j       .Ltail
+
+.Lfound:
+       ret
+.Lnotfound:
+       li      a0, 0
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(memchr), .-ARCH_LIBCFUN(memchr)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lmc_mask01:
+       .dword  0x0101010101010101
+.Lmc_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_memcmp.S 
b/libs/libc/machine/risc-v/arch_memcmp.S
new file mode 100644
index 00000000000..346022c7683
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_memcmp.S
@@ -0,0 +1,96 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_memcmp.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_MEMCMP
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(memcmp)
+.type  ARCH_LIBCFUN(memcmp), @function
+.align 2
+
+/************************************************************************************
+ * Name: memcmp
+ *
+ * int memcmp(const void *s1, const void *s2, size_t n)
+ *
+ * Word-at-a-time comparison with byte-level mismatch detection.
+ 
************************************************************************************/
+ARCH_LIBCFUN(memcmp):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       beqz    a2, .Lequal
+
+       or      t0, a0, a1
+       andi    t0, t0, SZREG-1
+       bnez    t0, .Lbyte_cmp
+
+       li      t0, SZREG
+       bltu    a2, t0, .Lbyte_cmp
+
+.Lword_loop:
+       REG_L   t1, 0(a0)
+       REG_L   t2, 0(a1)
+       bne     t1, t2, .Lfind_diff
+       addi    a0, a0, SZREG
+       addi    a1, a1, SZREG
+       sub     a2, a2, t0
+       bgeu    a2, t0, .Lword_loop
+       beqz    a2, .Lequal
+
+.Lbyte_cmp:
+       lbu     t1, 0(a0)
+       lbu     t2, 0(a1)
+       bne     t1, t2, .Ldiff
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       addi    a2, a2, -1
+       bnez    a2, .Lbyte_cmp
+
+.Lequal:
+       li      a0, 0
+       ret
+
+.Ldiff:
+       sub     a0, t1, t2
+       ret
+
+.Lfind_diff:
+       /* Little-endian: first differing byte is at LSB side */
+       andi    t3, t1, 0xff
+       andi    t4, t2, 0xff
+       bne     t3, t4, .Lfound
+       srli    t1, t1, 8
+       srli    t2, t2, 8
+       j       .Lfind_diff
+.Lfound:
+       sub     a0, t3, t4
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(memcmp), .-ARCH_LIBCFUN(memcmp)
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_memmove.S 
b/libs/libc/machine/risc-v/arch_memmove.S
new file mode 100644
index 00000000000..60349815bf7
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_memmove.S
@@ -0,0 +1,107 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_memmove.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_MEMMOVE
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(memmove)
+.type  ARCH_LIBCFUN(memmove), @function
+.align 2
+
+/************************************************************************************
+ * Name: memmove
+ *
+ * void *memmove(void *dst, const void *src, size_t n)
+ *
+ * Direction-aware copy with reverse path for overlapping regions.
+ 
************************************************************************************/
+ARCH_LIBCFUN(memmove):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       beqz    a2, .Ldone
+       bltu    a1, a0, .Lcheck_overlap
+       tail    ARCH_LIBCFUN(memcpy)
+
+.Lcheck_overlap:
+       add     t0, a1, a2
+       bleu    t0, a0, .Lfwd
+       j       .Lreverse
+.Lfwd:
+       tail    ARCH_LIBCFUN(memcpy)
+
+.Lreverse:
+       mv      t6, a0
+       add     t0, a0, a2
+       add     t1, a1, a2
+
+       li      t2, 2*SZREG
+       bleu    a2, t2, .Lrev_byte
+
+       andi    t2, t0, SZREG-1
+       beqz    t2, .Lrev_aligned
+       sub     a2, a2, t2
+.Lrev_align:
+       addi    t0, t0, -1
+       addi    t1, t1, -1
+       lbu     t3, 0(t1)
+       sb      t3, 0(t0)
+       addi    t2, t2, -1
+       bnez    t2, .Lrev_align
+
+.Lrev_aligned:
+       andi    t2, t1, SZREG-1
+       bnez    t2, .Lrev_byte_remain
+
+       li      t3, SZREG
+       bltu    a2, t3, .Lrev_byte_remain
+
+.Lrev_word:
+       addi    t0, t0, -SZREG
+       addi    t1, t1, -SZREG
+       REG_L   t2, 0(t1)
+       REG_S   t2, 0(t0)
+       sub     a2, a2, t3
+       bgeu    a2, t3, .Lrev_word
+
+.Lrev_byte_remain:
+       beqz    a2, .Lrev_done
+.Lrev_byte:
+       addi    t0, t0, -1
+       addi    t1, t1, -1
+       lbu     t2, 0(t1)
+       sb      t2, 0(t0)
+       addi    a2, a2, -1
+       bnez    a2, .Lrev_byte
+
+.Lrev_done:
+       mv      a0, t6
+.Ldone:
+       ret
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(memmove), .-ARCH_LIBCFUN(memmove)
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_stpcpy.S 
b/libs/libc/machine/risc-v/arch_stpcpy.S
new file mode 100644
index 00000000000..683a6ef4451
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_stpcpy.S
@@ -0,0 +1,27 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_stpcpy.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"
+
+#define USE_AS_STPCPY
+#define STRCPY ARCH_LIBCFUN(stpcpy)
+#include "arch_strcpy.S"
diff --git a/libs/libc/machine/risc-v/arch_stpncpy.S 
b/libs/libc/machine/risc-v/arch_stpncpy.S
new file mode 100644
index 00000000000..2fb41e7346a
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_stpncpy.S
@@ -0,0 +1,28 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_stpncpy.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"
+
+#define USE_AS_STPCPY
+#define USE_AS_STRNCPY
+#define STRCPY ARCH_LIBCFUN(stpncpy)
+#include "arch_strcpy.S"
diff --git a/libs/libc/machine/risc-v/arch_strcat.S 
b/libs/libc/machine/risc-v/arch_strcat.S
new file mode 100644
index 00000000000..55297b35a16
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strcat.S
@@ -0,0 +1,152 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strcat.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_STRCAT
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strcat)
+.type  ARCH_LIBCFUN(strcat), @function
+.align 2
+
+/************************************************************************************
+ * Name: strcat
+ *
+ * char *strcat(char *dst, const char *src)
+ *
+ * Word-at-a-time strlen + word-at-a-time copy.
+ 
************************************************************************************/
+ARCH_LIBCFUN(strcat):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       mv      t6, a0                  /* save original dst for return */
+       mv      a7, a1                  /* save src */
+
+       /* --- Phase 1: Find end of dst (word-at-a-time strlen) --- */
+
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Lslen_aligned
+
+.Lslen_head:
+       lbu     t0, 0(a0)
+       beqz    t0, .Lcopy_start
+       addi    a0, a0, 1
+       andi    t0, a0, SZREG-1
+       bnez    t0, .Lslen_head
+
+.Lslen_aligned:
+#if SZREG == 8
+       lla     t2, .Lct_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lct_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+.Lslen_word:
+       REG_L   t0, 0(a0)
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+       bnez    t1, .Lslen_find
+       addi    a0, a0, SZREG
+       j       .Lslen_word
+
+.Lslen_find:
+       lbu     t0, 0(a0)
+       beqz    t0, .Lcopy_start
+       addi    a0, a0, 1
+       j       .Lslen_find
+
+.Lcopy_start:
+       /* a0 = dst end (null position), a7 = src */
+       /* --- Phase 2: Copy src to dst end (word-at-a-time) --- */
+
+       mv      a1, a7
+
+       /* Check alignment consistency */
+
+       xor     t0, a0, a1
+       andi    t0, t0, SZREG-1
+       bnez    t0, .Lcopy_byte         /* misaligned: byte copy */
+
+       /* Align */
+
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Lcopy_word
+
+.Lcopy_align:
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Lcat_done
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       andi    t0, a0, SZREG-1
+       bnez    t0, .Lcopy_align
+
+.Lcopy_word:
+       /* Word-at-a-time copy with null detection */
+       /* t2, t3 still hold masks from strlen phase */
+
+       REG_L   t0, 0(a1)
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+       bnez    t1, .Lcopy_byte         /* has null: finish byte-by-byte */
+       REG_S   t0, 0(a0)
+       addi    a0, a0, SZREG
+       addi    a1, a1, SZREG
+       j       .Lcopy_word
+
+.Lcopy_byte:
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Lcat_done
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       j       .Lcopy_byte
+
+.Lcat_done:
+       mv      a0, t6
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strcat), .-ARCH_LIBCFUN(strcat)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lct_mask01:
+       .dword  0x0101010101010101
+.Lct_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strchr.S 
b/libs/libc/machine/risc-v/arch_strchr.S
new file mode 100644
index 00000000000..729c8ebbf38
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strchr.S
@@ -0,0 +1,171 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strchr.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_STRCHR
+
+#include "asm.h"
+
+#ifndef STRCHR
+#define STRCHR ARCH_LIBCFUN(strchr)
+#endif
+
+.text
+.global STRCHR
+.type  STRCHR, @function
+.align 2
+
+/************************************************************************************
+ * Name: strchr
+ *
+ * char *strchr(const char *s, int c)
+ *
+ * Broadcast + DETECTCHAR + DETECTNULL. Supports strchrnul via macro.
+ 
************************************************************************************/
+STRCHR:
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       andi    a1, a1, 0xff
+
+       /* Byte-by-byte until aligned */
+
+.Lalign_loop:
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Laligned
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       j       .Lalign_loop
+
+.Laligned:
+       /* Broadcast c to all bytes */
+
+       slli    t0, a1, 8
+       or      t0, t0, a1
+       slli    t1, t0, 16
+       or      a6, t0, t1              /* a6 = broadcast_c (32-bit) */
+#if SZREG == 8
+       slli    t0, a6, 32
+       or      a6, a6, t0              /* a6 = broadcast_c (64-bit) */
+#endif
+
+       /* Load masks */
+#if SZREG == 8
+       lla     t2, .Lchr_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lchr_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+.Lword_loop:
+       REG_L   t0, 0(a0)
+
+       /* DETECTNULL(word) - check for string end */
+
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+
+       /* DETECTCHAR(word, c) = DETECTNULL(word ^ broadcast_c) */
+
+       xor     t4, t0, a6
+       sub     t5, t4, t2
+       not     a5, t4
+       and     t5, t5, a5
+       and     t5, t5, t3
+
+       /* If either null or char found */
+
+       or      a5, t1, t5
+       bnez    a5, .Lbyte_scan
+       addi    a0, a0, SZREG
+       j       .Lword_loop
+
+.Lbyte_scan:
+       /* Scan byte-by-byte to determine if char or null came first */
+
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+#if SZREG == 8
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+       lbu     t1, 0(a0)
+       beq     t1, a1, .Lfound
+       beqz    t1, .Lnot_found
+       addi    a0, a0, 1
+#endif
+       j       .Lword_loop
+
+.Lfound:
+       ret
+.Lnot_found:
+#ifdef USE_AS_STRCHRNUL
+       ret
+#else
+       li      a0, 0
+       ret
+#endif
+
+       .cfi_endproc
+       .size   STRCHR, .-STRCHR
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lchr_mask01:
+       .dword  0x0101010101010101
+.Lchr_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strchrnul.S 
b/libs/libc/machine/risc-v/arch_strchrnul.S
new file mode 100644
index 00000000000..6626fc0c6d3
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strchrnul.S
@@ -0,0 +1,27 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strchrnul.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"
+
+#define USE_AS_STRCHRNUL
+#define STRCHR ARCH_LIBCFUN(strchrnul)
+#include "arch_strchr.S"
diff --git a/libs/libc/machine/risc-v/arch_strcpy.S 
b/libs/libc/machine/risc-v/arch_strcpy.S
new file mode 100644
index 00000000000..fea02705c52
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strcpy.S
@@ -0,0 +1,163 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strcpy.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_STRCPY
+
+#include "asm.h"
+
+#ifndef STRCPY
+#define STRCPY ARCH_LIBCFUN(strcpy)
+#endif
+
+.text
+.global STRCPY
+.type  STRCPY, @function
+.align 2
+
+/************************************************************************************
+ * Name: strcpy
+ *
+ * char *strcpy(char *dst, const char *src)
+ *
+ * Word-at-a-time with DETECTNULL. Supports stpcpy/strncpy/stpncpy via macros.
+ 
************************************************************************************/
+STRCPY:
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       mv      t6, a0                  /* save original dst */
+#ifdef USE_AS_STRNCPY
+       beqz    a2, .Ldone_ret
+       mv      a7, a2                  /* a7 = n */
+#endif
+
+       /* Check src/dst alignment consistency */
+
+       xor     t0, a0, a1
+       andi    t0, t0, SZREG-1
+       bnez    t0, .Lbyte_loop
+
+       /* Align to SZREG boundary */
+
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Laligned
+
+.Lalign_head:
+#ifdef USE_AS_STRNCPY
+       beqz    a7, .Lnull_pad
+#endif
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Lfound_null
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+#ifdef USE_AS_STRNCPY
+       addi    a7, a7, -1
+#endif
+       andi    t0, a0, SZREG-1
+       bnez    t0, .Lalign_head
+
+.Laligned:
+#if SZREG == 8
+       lla     t2, .Lsc_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lsc_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+.Lword_loop:
+#ifdef USE_AS_STRNCPY
+       li      t0, SZREG
+       bltu    a7, t0, .Lbyte_loop
+#endif
+       REG_L   t0, 0(a1)
+
+       /* DETECTNULL */
+
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+       bnez    t1, .Lbyte_loop         /* has null: finish byte-by-byte */
+
+       REG_S   t0, 0(a0)
+       addi    a0, a0, SZREG
+       addi    a1, a1, SZREG
+#ifdef USE_AS_STRNCPY
+       addi    a7, a7, -SZREG
+#endif
+       j       .Lword_loop
+
+.Lbyte_loop:
+#ifdef USE_AS_STRNCPY
+       beqz    a7, .Lnull_pad
+#endif
+       lbu     t0, 0(a1)
+       sb      t0, 0(a0)
+       beqz    t0, .Lfound_null
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+#ifdef USE_AS_STRNCPY
+       addi    a7, a7, -1
+#endif
+       j       .Lbyte_loop
+
+.Lfound_null:
+       /* a0 points to where we wrote the null byte */
+#ifdef USE_AS_STRNCPY
+       addi    a7, a7, -1              /* count the null */
+.Lnull_pad:
+       beqz    a7, .Ldone_ret
+.Lpad_loop:
+       addi    a0, a0, 1
+       sb      zero, 0(a0)
+       addi    a7, a7, -1
+       bnez    a7, .Lpad_loop
+#endif
+
+.Ldone_ret:
+#ifdef USE_AS_STPCPY
+       /* stpcpy: return pointer to the null byte written */
+       ret
+#else
+       mv      a0, t6
+       ret
+#endif
+
+       .cfi_endproc
+       .size   STRCPY, .-STRCPY
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lsc_mask01:
+       .dword  0x0101010101010101
+.Lsc_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strlen.S 
b/libs/libc/machine/risc-v/arch_strlen.S
new file mode 100644
index 00000000000..eae55a7b312
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strlen.S
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strlen.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_STRLEN
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strlen)
+.type  ARCH_LIBCFUN(strlen), @function
+.align 2
+
+/************************************************************************************
+ * Name: strlen
+ *
+ * size_t strlen(const char *s)
+ *
+ * Word-at-a-time DETECTNULL scanning.
+ 
************************************************************************************/
+ARCH_LIBCFUN(strlen):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       mv      t0, a0
+
+.Lbyte_head:
+       andi    t1, t0, SZREG-1
+       beqz    t1, .Laligned
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       j       .Lbyte_head
+
+.Laligned:
+#if SZREG == 8
+       lla     t3, .Lmask01
+       ld      t3, 0(t3)
+       lla     t4, .Lmask80
+       ld      t4, 0(t4)
+#else
+       li      t3, 0x01010101
+       li      t4, 0x80808080
+#endif
+
+.Lword_loop:
+       REG_L   t1, 0(t0)
+       sub     t2, t1, t3
+       not     t5, t1
+       and     t2, t2, t5
+       and     t2, t2, t4
+       bnez    t2, .Lfind_null
+       addi    t0, t0, SZREG
+       j       .Lword_loop
+
+.Lfind_null:
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+#if SZREG == 8
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+#endif
+
+.Ldone:
+       sub     a0, t0, a0
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strlen), .-ARCH_LIBCFUN(strlen)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lmask01:
+       .dword  0x0101010101010101
+.Lmask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strncmp.S 
b/libs/libc/machine/risc-v/arch_strncmp.S
new file mode 100644
index 00000000000..df03d5fe13f
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strncmp.S
@@ -0,0 +1,116 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strncmp.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_STRNCMP
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strncmp)
+.type  ARCH_LIBCFUN(strncmp), @function
+.align 2
+
+/************************************************************************************
+ * Name: strncmp
+ *
+ * int strncmp(const char *s1, const char *s2, size_t n)
+ *
+ * Word-at-a-time comparison with count limit.
+ 
************************************************************************************/
+ARCH_LIBCFUN(strncmp):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       beqz    a2, .Lequal
+
+       /* Check alignment consistency */
+
+       or      t0, a0, a1
+       andi    t0, t0, SZREG-1
+       bnez    t0, .Lbyte_loop
+
+       /* Both aligned - load masks */
+
+#if SZREG == 8
+       lla     t2, .Lsnc_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lsnc_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+.Lword_loop:
+       li      t0, SZREG
+       bltu    a2, t0, .Lbyte_loop
+       REG_L   t0, 0(a0)
+       REG_L   t1, 0(a1)
+
+       bne     t0, t1, .Lbyte_loop     /* words differ */
+
+       /* DETECTNULL on t0 */
+
+       sub     t4, t0, t2
+       not     t5, t0
+       and     t4, t4, t5
+       and     t4, t4, t3
+       bnez    t4, .Lequal             /* null found, words equal → return 0 */
+
+       addi    a0, a0, SZREG
+       addi    a1, a1, SZREG
+       addi    a2, a2, -SZREG
+       j       .Lword_loop
+
+.Lbyte_loop:
+       beqz    a2, .Lequal
+       lbu     t0, 0(a0)
+       lbu     t1, 0(a1)
+       bne     t0, t1, .Ldiff
+       beqz    t0, .Lequal
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       addi    a2, a2, -1
+       j       .Lbyte_loop
+
+.Lequal:
+       li      a0, 0
+       ret
+.Ldiff:
+       sub     a0, t0, t1
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strncmp), .-ARCH_LIBCFUN(strncmp)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lsnc_mask01:
+       .dword  0x0101010101010101
+.Lsnc_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strncpy.S 
b/libs/libc/machine/risc-v/arch_strncpy.S
new file mode 100644
index 00000000000..f45a1256736
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strncpy.S
@@ -0,0 +1,27 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strncpy.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"
+
+#define USE_AS_STRNCPY
+#define STRCPY ARCH_LIBCFUN(strncpy)
+#include "arch_strcpy.S"
diff --git a/libs/libc/machine/risc-v/arch_strnlen.S 
b/libs/libc/machine/risc-v/arch_strnlen.S
new file mode 100644
index 00000000000..be15084aa82
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strnlen.S
@@ -0,0 +1,123 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strnlen.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_STRNLEN
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strnlen)
+.type  ARCH_LIBCFUN(strnlen), @function
+.align 2
+
+/************************************************************************************
+ * Name: strnlen
+ *
+ * size_t strnlen(const char *s, size_t maxlen)
+ *
+ * Word-at-a-time DETECTNULL with maxlen bound.
+ 
************************************************************************************/
+ARCH_LIBCFUN(strnlen):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       beqz    a1, .Lret_zero
+       mv      t0, a0                  /* t0 = current ptr */
+       mv      t6, a1                  /* t6 = remaining count */
+
+       /* Align head */
+
+.Lhead:
+       andi    t1, t0, SZREG-1
+       beqz    t1, .Laligned
+       beqz    t6, .Lret_maxlen
+       lbu     t2, 0(t0)
+       beqz    t2, .Ldone
+       addi    t0, t0, 1
+       addi    t6, t6, -1
+       j       .Lhead
+
+.Laligned:
+#if SZREG == 8
+       lla     t2, .Lsnl_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lsnl_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+       /* Word loop with count check */
+
+.Lword_loop:
+       li      t4, SZREG
+       bltu    t6, t4, .Ltail          /* not enough bytes for a word */
+       REG_L   t1, 0(t0)
+
+       /* DETECTNULL */
+
+       sub     t4, t1, t2
+       not     t5, t1
+       and     t4, t4, t5
+       and     t4, t4, t3
+       bnez    t4, .Lfind_byte
+       addi    t0, t0, SZREG
+       addi    t6, t6, -SZREG
+       j       .Lword_loop
+
+.Lfind_byte:
+       /* Null in this word, find exact position */
+
+.Ltail:
+       beqz    t6, .Lret_maxlen
+       lbu     t1, 0(t0)
+       beqz    t1, .Ldone
+       addi    t0, t0, 1
+       addi    t6, t6, -1
+       j       .Ltail
+
+.Ldone:
+       sub     a0, t0, a0
+       ret
+.Lret_maxlen:
+       mv      a0, a1
+       ret
+.Lret_zero:
+       li      a0, 0
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strnlen), .-ARCH_LIBCFUN(strnlen)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lsnl_mask01:
+       .dword  0x0101010101010101
+.Lsnl_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif
diff --git a/libs/libc/machine/risc-v/arch_strrchr.S 
b/libs/libc/machine/risc-v/arch_strrchr.S
new file mode 100644
index 00000000000..7d27e6daa4e
--- /dev/null
+++ b/libs/libc/machine/risc-v/arch_strrchr.S
@@ -0,0 +1,166 @@
+/****************************************************************************
+ * libs/libc/machine/risc-v/arch_strrchr.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_STRRCHR
+
+#include "asm.h"
+
+.text
+.global ARCH_LIBCFUN(strrchr)
+.type  ARCH_LIBCFUN(strrchr), @function
+.align 2
+
+/************************************************************************************
+ * Name: strrchr
+ *
+ * char *strrchr(const char *s, int c)
+ *
+ * Word-at-a-time with last-match tracking.
+ 
************************************************************************************/
+ARCH_LIBCFUN(strrchr):
+       .cfi_sections .debug_frame
+       .cfi_startproc
+
+       andi    a1, a1, 0xff
+       li      a7, 0                   /* a7 = last_match = NULL */
+
+       /* Byte-by-byte head until aligned */
+
+.Lhead:
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Laligned
+       lbu     t0, 0(a0)
+       bne     t0, a1, .Lhead_skip
+       mv      a7, a0
+.Lhead_skip:
+       beqz    t0, .Ldone
+       addi    a0, a0, 1
+       j       .Lhead
+
+.Laligned:
+       /* Broadcast c and load masks */
+
+       slli    t0, a1, 8
+       or      t0, t0, a1
+       slli    t1, t0, 16
+       or      a6, t0, t1              /* a6 = broadcast_c (32-bit) */
+#if SZREG == 8
+       slli    t0, a6, 32
+       or      a6, a6, t0
+       lla     t2, .Lrc_mask01
+       ld      t2, 0(t2)
+       lla     t3, .Lrc_mask80
+       ld      t3, 0(t3)
+#else
+       li      t2, 0x01010101
+       li      t3, 0x80808080
+#endif
+
+       /* Word loop: check for null and target char simultaneously */
+
+.Lword_loop:
+       REG_L   t0, 0(a0)
+
+       /* DETECTNULL(word) - check string end */
+
+       sub     t1, t0, t2
+       not     t4, t0
+       and     t1, t1, t4
+       and     t1, t1, t3
+
+       /* DETECTCHAR(word, c) = DETECTNULL(word ^ broadcast_c) */
+
+       xor     t4, t0, a6
+       sub     t5, t4, t2
+       not     a5, t4
+       and     t5, t5, a5
+       and     t5, t5, t3
+
+       /* If null found, do final byte scan of this word */
+
+       bnez    t1, .Lfinal_scan
+
+       /* If target char found in this word, scan for last match */
+
+       bnez    t5, .Lupdate_match
+
+       addi    a0, a0, SZREG
+       j       .Lword_loop
+
+.Lupdate_match:
+       /* Target char found, byte-scan this word to update last_match */
+       /* Then continue to next word */
+
+       li      a5, SZREG
+.Lupdate_loop:
+       lbu     t0, 0(a0)
+       beqz    t0, .Ldone              /* shouldn't happen here, but safety */
+       beq     t0, a1, .Lupdate_hit
+.Lupdate_next:
+       addi    a0, a0, 1
+       addi    a5, a5, -1
+       bnez    a5, .Lupdate_loop
+       j       .Lword_loop             /* back to word loop (a0 now at next 
word) */
+
+.Lupdate_hit:
+       mv      a7, a0
+       j       .Lupdate_next
+
+.Lfinal_scan:
+       /* This word contains null. Byte-scan to find last match before null. */
+
+.Lfinal_loop:
+       lbu     t0, 0(a0)
+       beqz    t0, .Ldone
+       beq     t0, a1, .Lfinal_hit
+       addi    a0, a0, 1
+       j       .Lfinal_loop
+
+.Lfinal_hit:
+       mv      a7, a0
+       addi    a0, a0, 1
+       j       .Lfinal_loop
+
+.Ldone:
+       /* Special: if c == 0, return pointer to the null terminator */
+       bnez    a1, .Lret
+       mv      a7, a0
+
+.Lret:
+       mv      a0, a7
+       ret
+
+       .cfi_endproc
+       .size   ARCH_LIBCFUN(strrchr), .-ARCH_LIBCFUN(strrchr)
+
+#if SZREG == 8
+       .section .srodata.cst8,"aM",@progbits,8
+       .align  3
+.Lrc_mask01:
+       .dword  0x0101010101010101
+.Lrc_mask80:
+       .dword  0x8080808080808080
+#endif
+
+#endif

Reply via email to