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
