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
The following commit(s) were added to refs/heads/master by this push:
new a0fcbb7957e libs/libc/risc-v: Refresh memcpy and memset with
XLEN-adaptive loops.
a0fcbb7957e is described below
commit a0fcbb7957e916d03e346de9bdf5d1be2dd4ccd0
Author: ganjing <[email protected]>
AuthorDate: Tue Aug 11 11:59:21 2026 +0800
libs/libc/risc-v: Refresh memcpy and memset with XLEN-adaptive loops.
Rewrite arch_memcpy.S and arch_memset.S to be register-width aware on
both RV32 and RV64 using REG_L/REG_S/SZREG macros from asm.h.
memcpy gains:
- 16xSZREG unrolled main loop (128B/iter on RV64, 64B on RV32).
- Shift-merge path for misaligned src: reads two aligned words
straddling each output word and shifts them together, so no load
or store is ever misaligned.
- Single SZREG and byte loops for remainder and small copies.
memset gains:
- 32xSZREG unrolled main loop (256B/iter on RV64, 128B on RV32)
using Duff's device for non-power-of-two remainders.
- .option norvc ensures fixed 4-byte instruction width for correct
jump offset calculation in the Duff's device entry.
- Zero-length input handled correctly (branch to guarded tail).
The old memcpy always used lw/sw even on RV64, wasting half the
memory bandwidth. The old memset unrolled only 16 bytes per iteration.
Signed-off-by: ganjing <[email protected]>
---
libs/libc/machine/risc-v/arch_memcpy.S | 280 ++++++++++++++++++++++-----------
libs/libc/machine/risc-v/arch_memset.S | 170 +++++++++++---------
2 files changed, 279 insertions(+), 171 deletions(-)
diff --git a/libs/libc/machine/risc-v/arch_memcpy.S
b/libs/libc/machine/risc-v/arch_memcpy.S
index ee4cbeaae23..1db2cce930d 100644
--- a/libs/libc/machine/risc-v/arch_memcpy.S
+++ b/libs/libc/machine/risc-v/arch_memcpy.S
@@ -28,6 +28,20 @@
#ifdef LIBC_BUILD_MEMCPY
+#include "asm.h"
+
+/************************************************************************************
+ * Pre-processor Definitions
+
************************************************************************************/
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define SHL_H srl
+# define SHL_L sll
+#else
+# define SHL_H sll
+# define SHL_L srl
+#endif
+
/************************************************************************************
* Public Symbols
************************************************************************************/
@@ -38,109 +52,187 @@
/************************************************************************************
* Name: memcpy
+ *
+ * void *memcpy(void *dst, const void *src, size_t n)
+ *
+ * Optimized for RISC-V using XLEN-sized load/store with 16×SZREG unrolling.
+ * Handles unaligned src via shift-merge technique.
************************************************************************************/
.text
+ .align 2
ARCH_LIBCFUN(memcpy):
.cfi_sections .debug_frame
.cfi_startproc
- move t6, a0 /* Preserve return value */
-
- /* Defer to byte-oriented copy for small sizes */
- sltiu a3, a2, 128
- bnez a3, 4f
- /* Use word-oriented copy only if low-order bits match */
- andi a3, t6, 3
- andi a4, a1, 3
- bne a3, a4, 4f
-
- beqz a3, 2f /* Skip if already aligned */
- /*
- * Round to nearest double word-aligned address
- * greater than or equal to start address
- */
- andi a3, a1, ~3
- addi a3, a3, 4
- /* Handle initial misalignment */
- sub a4, a3, a1
-1:
- lb a5, 0(a1)
- addi a1, a1, 1
- sb a5, 0(t6)
- addi t6, t6, 1
- bltu a1, a3, 1b
- sub a2, a2, a4 /* Update count */
-
-2:
- andi a4, a2, ~63
- beqz a4, 4f
- add a3, a1, a4
-3:
- lw a4, 0(a1)
- lw a5, 4(a1)
- lw a6, 2*4(a1)
- lw a7, 3*4(a1)
- lw t0, 4*4(a1)
- lw t1, 5*4(a1)
- lw t2, 6*4(a1)
- lw t3, 7*4(a1)
- lw t4, 8*4(a1)
- lw t5, 9*4(a1)
- sw a4, 0(t6)
- sw a5, 4(t6)
- sw a6, 2*4(t6)
- sw a7, 3*4(t6)
- sw t0, 4*4(t6)
- sw t1, 5*4(t6)
- sw t2, 6*4(t6)
- sw t3, 7*4(t6)
- sw t4, 8*4(t6)
- sw t5, 9*4(t6)
- lw a4, 10*4(a1)
- lw a5, 11*4(a1)
- lw a6, 12*4(a1)
- lw a7, 13*4(a1)
- lw t0, 14*4(a1)
- lw t1, 15*4(a1)
- addi a1, a1, 16*4
- sw a4, 10*4(t6)
- sw a5, 11*4(t6)
- sw a6, 12*4(t6)
- sw a7, 13*4(t6)
- sw t0, 14*4(t6)
- sw t1, 15*4(t6)
- addi t6, t6, 16*4
- bltu a1, a3, 3b
- andi a2, a2, 63 /* Update count */
-
-4:
- /* Handle trailing misalignment */
- beqz a2, 6f
- add a3, a1, a2
-
- /* Use word-oriented copy if co-aligned to word boundary */
- or a5, a1, t6
- or a5, a5, a3
- andi a5, a5, 3
- bnez a5, 5f
-7:
- lw a4, 0(a1)
- addi a1, a1, 4
- sw a4, 0(t6)
- addi t6, t6, 4
- bltu a1, a3, 7b
+ move t6, a0 /* Preserve return value (dst) */
+
+ /* Small copy: size < 3*SZREG → byte-by-byte */
+
+ li a3, 3*SZREG
+ bltu a2, a3, .Lbyte_copy
+
+ /* Align dst to SZREG boundary */
+
+ andi a3, a0, SZREG-1
+ beqz a3, .Ldst_aligned
+
+ /* Copy head bytes to align dst */
+
+ sub a3, zero, a3
+ addi a3, a3, SZREG /* a3 = bytes to copy = SZREG -
misalignment */
+ sub a2, a2, a3 /* Update remaining count */
+.Lalign_head:
+ lbu a4, 0(a1)
+ addi a1, a1, 1
+ sb a4, 0(t6)
+ addi t6, t6, 1
+ addi a3, a3, -1
+ bnez a3, .Lalign_head
+
+.Ldst_aligned:
+ /* Now dst (t6) is SZREG-aligned. Check if src is also aligned */
+
+ andi a3, a1, SZREG-1
+ bnez a3, .Lunaligned
+
+ /* === Aligned path: both src and dst are SZREG-aligned === */
+
+ /* Main loop: 16×SZREG per iteration */
+
+ andi a4, a2, ~(16*SZREG-1)
+ beqz a4, .Laligned_tail
+ add a3, a1, a4
+
+ .align 3
+.Laligned_loop:
+ REG_L a4, 0*SZREG(a1)
+ REG_L a5, 1*SZREG(a1)
+ REG_L a6, 2*SZREG(a1)
+ REG_L a7, 3*SZREG(a1)
+ REG_L t0, 4*SZREG(a1)
+ REG_L t1, 5*SZREG(a1)
+ REG_L t2, 6*SZREG(a1)
+ REG_L t3, 7*SZREG(a1)
+ REG_L t4, 8*SZREG(a1)
+ REG_L t5, 9*SZREG(a1)
+ REG_S a4, 0*SZREG(t6)
+ REG_S a5, 1*SZREG(t6)
+ REG_S a6, 2*SZREG(t6)
+ REG_S a7, 3*SZREG(t6)
+ REG_S t0, 4*SZREG(t6)
+ REG_S t1, 5*SZREG(t6)
+ REG_S t2, 6*SZREG(t6)
+ REG_S t3, 7*SZREG(t6)
+ REG_S t4, 8*SZREG(t6)
+ REG_S t5, 9*SZREG(t6)
+ REG_L a4, 10*SZREG(a1)
+ REG_L a5, 11*SZREG(a1)
+ REG_L a6, 12*SZREG(a1)
+ REG_L a7, 13*SZREG(a1)
+ REG_L t0, 14*SZREG(a1)
+ REG_L t1, 15*SZREG(a1)
+ addi a1, a1, 16*SZREG
+ REG_S a4, 10*SZREG(t6)
+ REG_S a5, 11*SZREG(t6)
+ REG_S a6, 12*SZREG(t6)
+ REG_S a7, 13*SZREG(t6)
+ REG_S t0, 14*SZREG(t6)
+ REG_S t1, 15*SZREG(t6)
+ addi t6, t6, 16*SZREG
+ bltu a1, a3, .Laligned_loop
+
+ andi a2, a2, 16*SZREG-1 /* Update remaining count */
+
+.Laligned_tail:
+ /* Single-word copy for remainder */
+
+ andi a4, a2, ~(SZREG-1)
+ beqz a4, .Lbyte_copy_update
+ add a3, a1, a4
+.Lword_loop:
+ REG_L a4, 0(a1)
+ addi a1, a1, SZREG
+ REG_S a4, 0(t6)
+ addi t6, t6, SZREG
+ bltu a1, a3, .Lword_loop
+
+ andi a2, a2, SZREG-1 /* Update remaining count */
+
+.Lbyte_copy_update:
+ /* Fall through to byte copy with updated a2 */
+
+.Lbyte_copy:
+ /* Byte-by-byte copy for small/tail */
+
+ beqz a2, .Ldone
+ add a3, a1, a2
+.Lbyte_loop:
+ lbu a4, 0(a1)
+ addi a1, a1, 1
+ sb a4, 0(t6)
+ addi t6, t6, 1
+ bltu a1, a3, .Lbyte_loop
+.Ldone:
ret
-5:
- lb a4, 0(a1)
- addi a1, a1, 1
- sb a4, 0(t6)
- addi t6, t6, 1
- bltu a1, a3, 5b
-6:
- ret
+ /* === Unaligned path: dst aligned, src not aligned === */
+ /* Uses shift-merge to combine two aligned loads into one store */
+
+.Lunaligned:
+ /* a3 = src misalignment (already computed above) */
+
+ slli a6, a3, 3 /* a6 = shift_h = misalign * 8 bits */
+ sub a7, zero, a6
+ addi a7, a7, SZREG*8 /* a7 = shift_l = XLEN - shift_h */
+
+ /* Save src misalignment for later restore */
+
+ mv t4, a3 /* t4 = original misalignment bytes */
+
+ /* Align src down to SZREG boundary */
+
+ andi a1, a1, ~(SZREG-1)
+
+ /* Preload first aligned word from src */
+
+ REG_L a5, 0(a1)
+
+ /* Calculate loop count: process 2×SZREG per iteration */
+
+ andi a4, a2, ~(2*SZREG-1)
+ beqz a4, .Lunaligned_tail
+ add a3, t6, a4 /* a3 = end address for dst */
+
+ .align 3
+.Lunaligned_loop:
+ REG_L a4, SZREG(a1) /* Load next aligned word */
+ SHL_H t0, a5, a6 /* High part from previous word */
+ SHL_L t1, a4, a7 /* Low part from current word */
+ or t0, t0, t1 /* Combine */
+ REG_S t0, 0(t6) /* Store to dst */
+
+ REG_L a5, 2*SZREG(a1) /* Load next aligned word */
+ SHL_H t0, a4, a6 /* High part */
+ SHL_L t1, a5, a7 /* Low part */
+ or t0, t0, t1 /* Combine */
+ REG_S t0, SZREG(t6) /* Store to dst */
+
+ addi a1, a1, 2*SZREG
+ addi t6, t6, 2*SZREG
+ bltu t6, a3, .Lunaligned_loop
+
+ andi a2, a2, 2*SZREG-1 /* Update remaining */
+
+.Lunaligned_tail:
+ /* Restore real src pointer: aligned_src + misalignment */
+
+ add a1, a1, t4
+
+ j .Lbyte_copy
.cfi_endproc
-#endif
+ .size ARCH_LIBCFUN(memcpy), .-ARCH_LIBCFUN(memcpy)
+
+#endif /* LIBC_BUILD_MEMCPY */
diff --git a/libs/libc/machine/risc-v/arch_memset.S
b/libs/libc/machine/risc-v/arch_memset.S
index 76bfc930e57..4bcbbe2fcc8 100644
--- a/libs/libc/machine/risc-v/arch_memset.S
+++ b/libs/libc/machine/risc-v/arch_memset.S
@@ -18,98 +18,114 @@
#ifdef LIBC_BUILD_MEMSET
+#include "asm.h"
+
.text
.global ARCH_LIBCFUN(memset)
.type ARCH_LIBCFUN(memset), @function
+.align 2
+
+/************************************************************************************
+ * Name: memset
+ *
+ * void *memset(void *s, int c, size_t n)
+ *
+ * Optimized with 32xSZREG unrolled loop and .option norvc.
+
************************************************************************************/
ARCH_LIBCFUN(memset):
.cfi_sections .debug_frame
.cfi_startproc
- li t1, 15
- move a4, a0
- bleu a2, t1, .Ltiny
- and a5, a4, 15
- bnez a5, .Lmisaligned
-.Laligned:
- bnez a1, .Lwordify
+ mv t0, a0
-.Lwordified:
- and a3, a2, ~15
- and a2, a2, 15
- add a3, a3, a4
+ li t1, 2*SZREG
+ bleu a2, t1, .Lbyte_tail
-#if __riscv_xlen == 64
-1:
- sd a1, 0(a4)
- sd a1, 8(a4)
-#else
-1:
- sw a1, 0(a4)
- sw a1, 4(a4)
- sw a1, 8(a4)
- sw a1, 12(a4)
+ andi a1, a1, 0xff
+ slli t1, a1, 8
+ or a1, a1, t1
+ slli t1, a1, 16
+ or a1, a1, t1
+#ifdef CONFIG_ARCH_RV64
+ slli t1, a1, 32
+ or a1, a1, t1
#endif
- add a4, a4, 16
- bltu a4, a3, 1b
- bnez a2, .Ltiny
- ret
+ andi t1, t0, SZREG-1
+ beqz t1, .Laligned
+ sub t2, zero, t1
+ addi t2, t2, SZREG
+ sub a2, a2, t2
+.Lalign_head:
+ sb a1, 0(t0)
+ addi t0, t0, 1
+ addi t2, t2, -1
+ bnez t2, .Lalign_head
-.Ltiny:
- sub a3, t1, a2
- sll a3, a3, 2
-1:
- auipc t0, %pcrel_hi(.Ltable)
- add a3, a3, t0
-.option push
-.option norvc
-.Ltable_misaligned:
- jr a3, %pcrel_lo(1b)
-.Ltable:
- sb a1,14(a4)
- sb a1,13(a4)
- sb a1,12(a4)
- sb a1,11(a4)
- sb a1,10(a4)
- sb a1, 9(a4)
- sb a1, 8(a4)
- sb a1, 7(a4)
- sb a1, 6(a4)
- sb a1, 5(a4)
- sb a1, 4(a4)
- sb a1, 3(a4)
- sb a1, 2(a4)
- sb a1, 1(a4)
- sb a1, 0(a4)
-.option pop
- ret
+.Laligned:
+ li t3, 32*SZREG
+ bltu a2, t3, .Lword_tail
-.Lwordify:
- and a1, a1, 0xFF
- sll a3, a1, 8
- or a1, a1, a3
- sll a3, a1, 16
- or a1, a1, a3
-#if __riscv_xlen == 64
- sll a3, a1, 32
- or a1, a1, a3
-#endif
- j .Lwordified
+ .align 3
+.Lblock_loop:
+ .option push
+ .option norvc
+ REG_S a1, 0*SZREG(t0)
+ REG_S a1, 1*SZREG(t0)
+ REG_S a1, 2*SZREG(t0)
+ REG_S a1, 3*SZREG(t0)
+ REG_S a1, 4*SZREG(t0)
+ REG_S a1, 5*SZREG(t0)
+ REG_S a1, 6*SZREG(t0)
+ REG_S a1, 7*SZREG(t0)
+ REG_S a1, 8*SZREG(t0)
+ REG_S a1, 9*SZREG(t0)
+ REG_S a1, 10*SZREG(t0)
+ REG_S a1, 11*SZREG(t0)
+ REG_S a1, 12*SZREG(t0)
+ REG_S a1, 13*SZREG(t0)
+ REG_S a1, 14*SZREG(t0)
+ REG_S a1, 15*SZREG(t0)
+ REG_S a1, 16*SZREG(t0)
+ REG_S a1, 17*SZREG(t0)
+ REG_S a1, 18*SZREG(t0)
+ REG_S a1, 19*SZREG(t0)
+ REG_S a1, 20*SZREG(t0)
+ REG_S a1, 21*SZREG(t0)
+ REG_S a1, 22*SZREG(t0)
+ REG_S a1, 23*SZREG(t0)
+ REG_S a1, 24*SZREG(t0)
+ REG_S a1, 25*SZREG(t0)
+ REG_S a1, 26*SZREG(t0)
+ REG_S a1, 27*SZREG(t0)
+ REG_S a1, 28*SZREG(t0)
+ REG_S a1, 29*SZREG(t0)
+ REG_S a1, 30*SZREG(t0)
+ REG_S a1, 31*SZREG(t0)
+ .option pop
+ addi t0, t0, 32*SZREG
+ sub a2, a2, t3
+ bgeu a2, t3, .Lblock_loop
-.Lmisaligned:
- sll a3, a5, 2
-1:
- auipc t0, %pcrel_hi(.Ltable_misaligned)
- add a3, a3, t0
- mv t0, ra
- jalr a3, %pcrel_lo(1b)
- mv ra, t0
+.Lword_tail:
+ andi t1, a2, ~(SZREG-1)
+ beqz t1, .Lbyte_tail
+ add t2, t0, t1
+.Lword_loop:
+ REG_S a1, 0(t0)
+ addi t0, t0, SZREG
+ bltu t0, t2, .Lword_loop
+ andi a2, a2, SZREG-1
- add a5, a5, -16
- sub a4, a4, a5
- add a2, a2, a5
- bleu a2, t1, .Ltiny
- j .Laligned
+.Lbyte_tail:
+ beqz a2, .Ldone
+.Lbyte_fill:
+ sb a1, 0(t0)
+ addi t0, t0, 1
+ addi a2, a2, -1
+ bnez a2, .Lbyte_fill
+.Ldone:
+ ret
.cfi_endproc
.size ARCH_LIBCFUN(memset), .-ARCH_LIBCFUN(memset)