This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 0790b52e64907c1cdd37746736a0686274a0f9ce
Author: Justin Hammond <[email protected]>
AuthorDate: Sat Aug 15 15:51:58 2026 +0800

    libs/libc/machine/risc-v: Compare a register at a time on equal offsets.
    
    memcmp, strncmp and strcmp reach their word loops only when both pointers
    are already on a register boundary:
    
      or    t0, a0, a1
      andi  t0, t0, SZREG-1
    
    That asks more than the loops need.  They load from the two pointers at
    the same boundary, so what matters is that the two agree about where a
    boundary falls, not that either is already on one.  A pair offset by the
    same amount can be walked up to the boundary a byte at a time and
    compared a register at a time from there.
    
    The union also holds far less often than the difference.  For arbitrary
    pointers on RV64 it is true about one time in 64 against one in eight,
    and the case it rejects, two strings carved out of the same buffer, is
    the common one.
    
    Test the difference of the pointers, and walk to the boundary first.
    arch_strcpy.S and arch_memcpy.S already do this.  Keeping every access
    aligned is not only faster here: the base ISA does not require misaligned
    loads and stores to be supported at all, so a routine in a machine
    directory cannot assume one will work, whatever it costs.
    
    Measured on a 1.4 GHz rv64, source and destination misaligned by one:
    
                        before   after
      memcmp 32K          34.4   458.0 MB/s
      strncmp 32K         32.4   253.0 MB/s
      strcmp 32K          41.0   280.0 MB/s
    
    Each of those was the rate of the byte loop the word loop was meant to
    replace.  Pointers that genuinely disagree still take the byte loop, and
    the aligned rates are unchanged.
    
    The measurements come from the benchmark in apache/nuttx-apps#3706.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 libs/libc/machine/risc-v/arch_memcmp.S  | 25 ++++++++++++++++++++--
 libs/libc/machine/risc-v/arch_strcmp.S  | 37 ++++++++++++++++++++++++++++++++-
 libs/libc/machine/risc-v/arch_strncmp.S | 29 +++++++++++++++++++++++---
 3 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/libs/libc/machine/risc-v/arch_memcmp.S 
b/libs/libc/machine/risc-v/arch_memcmp.S
index 3af0a4602ca..2110c283a13 100644
--- a/libs/libc/machine/risc-v/arch_memcmp.S
+++ b/libs/libc/machine/risc-v/arch_memcmp.S
@@ -45,12 +45,33 @@ ARCH_LIBCFUN(memcmp):
 
        beqz    a2, .Lequal
 
-       /* Check if both pointers share alignment */
+       /* The loops below load from both pointers at a boundary, which asks
+        * that the two agree about where a boundary falls, not that either
+        * is already on one.  Test the difference of the pointers, not their
+        * union.
+        */
 
-       or      t0, a0, a1
+       xor     t0, a0, a1
        andi    t0, t0, SZREG-1
        bnez    t0, .Lbyte_cmp
 
+       /* Same offset: walk both up to the boundary a byte at a time,
+        * stopping early on a difference.
+        */
+
+.Lalign_head:
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Laligned
+       lbu     t1, 0(a0)
+       lbu     t2, 0(a1)
+       bne     t1, t2, .Ldiff
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       addi    a2, a2, -1
+       beqz    a2, .Lequal
+       j       .Lalign_head
+
+.Laligned:
        li      t0, SZREG
        bltu    a2, t0, .Lbyte_cmp
 
diff --git a/libs/libc/machine/risc-v/arch_strcmp.S 
b/libs/libc/machine/risc-v/arch_strcmp.S
index a0bad30f602..3ccbcfbc47b 100644
--- a/libs/libc/machine/risc-v/arch_strcmp.S
+++ b/libs/libc/machine/risc-v/arch_strcmp.S
@@ -26,11 +26,46 @@
 ARCH_LIBCFUN(strcmp):
        .cfi_sections .debug_frame
        .cfi_startproc
-       or    a4, a0, a1
        li    t2, -1
+
+       /* Two pointers the same distance past a boundary can be compared
+        * a register at a time once both are walked up to it.  Only
+        * pointers that disagree about where the boundary falls need the
+        * byte loop, since no single aligned load serves both.  Test the
+        * difference of the pointers, not their union.
+        */
+
+       xor   a4, a0, a1
        and   a4, a4, SZREG-1
        bnez  a4, .Lmisaligned
 
+       /* Same offset: walk both up to the boundary a byte at a time,
+        * stopping early on a difference or a terminator.
+        */
+
+       and   a4, a0, SZREG-1
+       beqz  a4, .Laligned
+.Lhead:
+       lbu   a2, 0(a0)
+       lbu   a3, 0(a1)
+       bne   a2, a3, .Lheaddiff
+       addi  a0, a0, 1
+       addi  a1, a1, 1
+       beqz  a2, .Lheadeq
+       and   a4, a0, SZREG-1
+       bnez  a4, .Lhead
+       j     .Laligned
+
+.Lheaddiff:
+       sub   a0, a2, a3
+       ret
+
+.Lheadeq:
+       li    a0, 0
+       ret
+
+.Laligned:
+
 #if SZREG == 4
        li a5, 0x7f7f7f7f
 #else
diff --git a/libs/libc/machine/risc-v/arch_strncmp.S 
b/libs/libc/machine/risc-v/arch_strncmp.S
index df03d5fe13f..52bc2688d03 100644
--- a/libs/libc/machine/risc-v/arch_strncmp.S
+++ b/libs/libc/machine/risc-v/arch_strncmp.S
@@ -44,13 +44,36 @@ ARCH_LIBCFUN(strncmp):
 
        beqz    a2, .Lequal
 
-       /* Check alignment consistency */
+       /* The word loop below loads from both pointers at a boundary, which
+        * asks that the two agree about where a boundary falls, not that
+        * either is already on one.  Test the difference of the pointers,
+        * not their union.
+        */
 
-       or      t0, a0, a1
+       xor     t0, a0, a1
        andi    t0, t0, SZREG-1
        bnez    t0, .Lbyte_loop
 
-       /* Both aligned - load masks */
+       /* Same offset: walk both up to the boundary a byte at a time,
+        * stopping early on a difference or a terminator.
+        */
+
+.Lsnc_align_head:
+       andi    t0, a0, SZREG-1
+       beqz    t0, .Lsnc_aligned
+       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
+       beqz    a2, .Lequal
+       j       .Lsnc_align_head
+
+.Lsnc_aligned:
+
+       /* Load masks */
 
 #if SZREG == 8
        lla     t2, .Lsnc_mask01

Reply via email to