Skip to content

libs/libc/risc-v: Add optimized string and memory functions. - #19782

Merged
xiaoxiang781216 merged 3 commits into
apache:masterfrom
Shanks0224:riscv-string-functions-v2
Aug 12, 2026
Merged

libs/libc/risc-v: Add optimized string and memory functions.#19782
xiaoxiang781216 merged 3 commits into
apache:masterfrom
Shanks0224:riscv-string-functions-v2

Conversation

@Shanks0224

Copy link
Copy Markdown
Contributor

libs/libc/risc-v: Add optimized string and memory functions.

Summary

Add assembly-optimized implementations for 14 string/memory functions
using word-at-a-time techniques and XLEN-adaptive macros for both RV32
and RV64. The generic C library processes these functions byte by byte;
these replacements work a register width at a time (4 bytes on RV32,
8 on RV64) after aligning the pointers.

Functions added:

  • memmove: direction check + tail to memcpy for forward, reverse path
    with 16xSZREG unroll and shift-merge for misaligned source.
  • memcmp: word-granularity compare when both pointers share alignment.
  • memchr: broadcast target byte, XOR with each word, DETECTNULL to
    find matches. Counter-based bounds to avoid pointer overflow.
  • strlen, strnlen: DETECTNULL word loop with constants from .srodata.
  • strcpy, strncpy: word loop with DETECTNULL, zero-fill 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 and null.
    strchrnul reuses strchr via #define USE_AS_STRCHRNUL.
  • strrchr: forward scan recording last match position.
  • strncmp: word-at-a-time compare with null detection and counter.
  • strcat: strlen(dst) then word-at-a-time copy from src.

Each function is independently selectable via CONFIG_RISCV_, or
all enabled together with CONFIG_RISCV_STRING_FUNCTION=y.

Impact

  • Is new feature added? YES. 14 new arch-optimized string functions behind Kconfig options (default n).
  • Impact on user? NO. Must opt-in via CONFIG_RISCV_STRING_FUNCTION=y.
  • Impact on build? NO. New source files compiled only when selected.
  • Impact on hardware? NO.
  • Impact on documentation? NO.
  • Impact on security? NO.
  • Impact on compatibility? NO. Existing behavior unchanged when options are off.

Testing

I confirm that changes are verified on local setup and works as intended:

  • Build Host: Linux x86_64, riscv-none-elf-gcc 13.2.1
  • Target(s): QEMU rv-virt RV32 (rv-virt:nsh), QEMU rv-virt RV64 (rv-virt:nsh64)

Correctness: arch_libctest reports PASSED for all 16 functions across
alignments 0-7 and boundary sizes 0-128, including overlap tests for
memmove.

Performance (QEMU RV32, rdcycle, 128 bytes unless noted, 100 iterations avg):

                     baseline    optimized    speedup
  memmove(128)          564          423       1.33x
  memcmp(128)           686          316       2.17x
  memchr(128)           305          264       1.16x
  strlen(128)           474          274       1.73x
  strnlen(128)          579          320       1.81x
  strcmp(128)           621          305       2.04x
  strcpy(128)           441          300       1.47x
  strncpy(128)          599          360       1.66x
  stpcpy(128)           479          315       1.52x
  strchr(128)           341          258       1.32x
  strchrnul(128)        270          241       1.12x
  strrchr(128)          557          523       1.07x
  strncmp(128)          675          367       1.84x
  strcat(64)            418          267       1.57x

Testing logs (optimized, all functions):

nsh> arch_libctest
Testing memcpy...
memcpy: PASSED
memcpy(128) avg cycles: 676
Testing memmove...
memmove: PASSED
memmove(128) avg cycles: 423
Testing memset...
memset: PASSED
memset(128) avg cycles: 237
Testing memcmp...
memcmp: PASSED
memcmp(128) avg cycles: 316
Testing memchr...
memchr: PASSED
memchr(128) avg cycles: 264
Testing strlen...
strlen: PASSED
strlen(128) avg cycles: 274
Testing strcmp...
strcmp: PASSED
strcmp(128) avg cycles: 305
Testing strcpy...
strcpy: PASSED
strcpy(128) avg cycles: 300
Testing strchr...
strchr: PASSED
strchr(128) avg cycles: 258
Testing strncmp...
strncmp: PASSED
strncmp(128) avg cycles: 367
Testing strnlen...
strnlen: PASSED
strnlen(128) avg cycles: 320
Testing strncpy...
strncpy: PASSED
strncpy(128) avg cycles: 360
Testing stpcpy...
stpcpy: PASSED
stpcpy(128) avg cycles: 315
Testing strcat...
strcat: PASSED
strcat(64) avg cycles: 267
Testing strrchr...
strrchr: PASSED
strrchr(128) avg cycles: 523
Testing strchrnul...
strchrnul: PASSED
strchrnul(128) avg cycles: 241
arch_libc_test Passed

@github-actions github-actions Bot added Area: OS Components OS Components issues Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces. labels Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@Shanks0224
Shanks0224 force-pushed the riscv-string-functions-v2 branch from aa5f207 to 1e0041d Compare August 11, 2026 05:27
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@Fishwaldo could you benchmark this patch on your hardware?

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) <noreply@anthropic.com>
Signed-off-by: ganjing <ganjing@xiaomi.com>
Reduce branch overhead in the memcmp main loop by comparing four
words per iteration: XOR each pair, OR the four differences together,
and branch once.  On a mismatch the single-word loop locates the
exact differing word within four words of the fault.

Add a beqz guard at .Lbyte_cmp entry to handle the case where the
4-word loop consumes all remaining bytes exactly.

Measured on QEMU RV32: memcmp(128) 313 -> 271 cycles (13% faster).

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: ganjing <ganjing@xiaomi.com>
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>
@Shanks0224
Shanks0224 force-pushed the riscv-string-functions-v2 branch from b56ea73 to b66728b Compare August 11, 2026 09:27
@xiaoxiang781216
xiaoxiang781216 merged commit 931d5f5 into apache:master Aug 12, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: OS Components OS Components issues Size: XL The size of the change in this PR is very large. Consider breaking down the PR into smaller pieces.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants