mirror of
https://github.com/apache/nuttx.git
synced 2026-09-13 14:10:18 +00:00
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) <noreply@anthropic.com> Signed-off-by: ganjing <ganjing@xiaomi.com>
This commit is contained in:
parent
ad9c9d41b0
commit
931d5f50d4
4 changed files with 218 additions and 0 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
201
libs/libc/machine/risc-v/arch_strlcpy.S
Normal file
201
libs/libc/machine/risc-v/arch_strlcpy.S
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue