[riscv] Speed up memmove() when copying in forwards direction

Use the word-at-a-time variable-length memcpy() implementation when
performing an overlapping copy in the forwards direction, since this
is guaranteed to be safe and likely to be substantially faster than
the existing bytewise copy.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-05-21 16:12:56 +01:00
parent 20d2c0f787
commit a534563345
2 changed files with 14 additions and 60 deletions

View File

@@ -195,42 +195,13 @@ void riscv_memset ( void *dest, size_t len, int character ) {
}
/**
* Copy (possibly overlapping) memory region forwards
* Copy (possibly overlapping) memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void riscv_memmove_forwards ( void *dest, const void *src, size_t len ) {
unsigned long discard_data;
/* Do nothing if length is zero */
if ( ! len )
return;
/* Assume memmove() is not performance-critical, and perform a
* bytewise copy for simplicity.
*/
__asm__ __volatile__ ( "\n1:\n\t"
"lb %2, (%1)\n\t"
"sb %2, (%0)\n\t"
"addi %1, %1, 1\n\t"
"addi %0, %0, 1\n\t"
"bne %0, %3, 1b\n\t"
: "+r" ( dest ), "+r" ( src ),
"=&r" ( discard_data )
: "r" ( dest + len )
: "memory" );
}
/**
* Copy (possibly overlapping) memory region backwards
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void riscv_memmove_backwards ( void *dest, const void *src, size_t len ) {
void riscv_memmove ( void *dest, const void *src, size_t len ) {
void *orig_dest = dest;
unsigned long discard_data;
@@ -238,8 +209,14 @@ void riscv_memmove_backwards ( void *dest, const void *src, size_t len ) {
if ( ! len )
return;
/* Use memcpy() if copy direction is forwards */
if ( dest <= src ) {
memcpy ( dest, src, len );
return;
}
/* Assume memmove() is not performance-critical, and perform a
* bytewise copy for simplicity.
* bytewise copy backwards for simplicity.
*/
dest += len;
src += len;
@@ -254,19 +231,3 @@ void riscv_memmove_backwards ( void *dest, const void *src, size_t len ) {
: "r" ( orig_dest )
: "memory" );
}
/**
* Copy (possibly overlapping) memory region
*
* @v dest Destination region
* @v src Source region
* @v len Length
*/
void riscv_memmove ( void *dest, const void *src, size_t len ) {
if ( dest <= src ) {
riscv_memmove_forwards ( dest, src, len );
} else {
riscv_memmove_backwards ( dest, src, len );
}
}

View File

@@ -12,8 +12,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern void riscv_bzero ( void *dest, size_t len );
extern void riscv_memset ( void *dest, size_t len, int character );
extern void riscv_memcpy ( void *dest, const void *src, size_t len );
extern void riscv_memmove_forwards ( void *dest, const void *src, size_t len );
extern void riscv_memmove_backwards ( void *dest, const void *src, size_t len );
extern void riscv_memmove ( void *dest, const void *src, size_t len );
/**
@@ -68,17 +66,12 @@ static inline __attribute__ (( always_inline )) void *
memmove ( void *dest, const void *src, size_t len ) {
ssize_t offset = ( dest - src );
/* If required direction of copy is known at build time, then
* use the appropriate forwards/backwards copy directly.
/* If direction of copy is known to be forwards at build time,
* then use variable-length memcpy().
*/
if ( __builtin_constant_p ( offset ) ) {
if ( offset <= 0 ) {
riscv_memmove_forwards ( dest, src, len );
return dest;
} else {
riscv_memmove_backwards ( dest, src, len );
return dest;
}
if ( __builtin_constant_p ( offset ) && ( offset <= 0 ) ) {
riscv_memcpy ( dest, src, len );
return dest;
}
/* Otherwise, use ambidirectional copy */