Re: [PATCH 1/1] riscv: add vectorized memset, memcpy and memmove

Pincheng Wang <[email protected]> Tue, 16 Dec 2025 10:20:02 +0800
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
Hi Kito,

Sorry for the late reply.

On 2025/12/12 16:33, Kito Cheng wrote:
>> diff --git a/newlib/libc/machine/riscv/memcpy-asm.S b/newlib/libc/machine/riscv/memcpy-asm.S
>> index 2771285f9..9d1d2d4bd 100644
>> --- a/newlib/libc/machine/riscv/memcpy-asm.S
>> +++ b/newlib/libc/machine/riscv/memcpy-asm.S
>> @@ -9,11 +9,11 @@
>>      http://www.opensource.org/licenses.
>>   */
>>
>> -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
>>   .text
>>   .global memcpy
>>   .type  memcpy, @function
>>   memcpy:
>> +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
> 
> This seems not right change to me, memcpy-asm.S is NOT conditional
> compile in the Makefile, so that mean if we didn't defined
> PREFER_SIZE_OVER_SPEED, __OPTIMIZE_SIZE__ or __riscv_v, we will have a
> empty memcpy in memcpy-asm.o and then this will included in libc.a
> 
> Same issue for memmove-asm.S
> 

My apologies for not implementing and testing the changes thoroughly 
enough. I'll move the guard back to its original position in the next 
revision.

>>     mv a3, a0
>>     beqz a2, 2f
>>
>> @@ -29,4 +29,25 @@ memcpy:
>>     ret
>>
>>     .size        memcpy, .-memcpy
>> +#elif defined(__riscv_v)
> 
> Suggest use __riscv_vector rather than __riscv_v, so that we can also
> use that logic for zve* extensions.
> 
>> +  .option push
>> +  .option arch, +v
> 
> and arch, +zve32x here rather than +v
> 

Will replace macro and arch,+v to support both full V and Zve* extensions.

>> +  mv      t0, a0                    /* running dst */
>> +  mv      t1, a1                    /* running src */
>> +  beqz    a2, .Ldone_copy           /* n == 0 then return */
>> +
>> +.Lbulk_copy:
>> +  vsetvli t2, a2, e8, m8, ta, ma    /* t2 = vl (bytes) */
>> +  vle8.v  v0, (t1)
>> +  vse8.v  v0, (t0)
>> +  add     t0, t0, t2
>> +  add     t1, t1, t2
>> +  sub     a2, a2, t2
> 
> This sub can be drop
> 
>> +  bnez    a2, .Lbulk_copy
> 
> You can use either src(a1)+len(a2) or dst(a0)+len(a2) for loop condition:
> 
> something like:
> 
> void *
> memcpy(unsigned char *dst, const unsigned char *src,
>                         const size_t sz)
> {
>   const unsigned char *end = dst + sz;
>   while (dst != end)
>     *dst++ = *src++;
>    return dst;
> }
> 
> This optimization could be applied on other function as well
> 

Thanks for the suggestion. Will restructure the loop conditions as you 
suggested.

>> +  /* fallthrough */
>> +
>> +.Ldone_copy:
>> +  ret
>> +.size memcpy, .-memcpy
>> +.option pop
>>   #endif
>> diff --git a/newlib/libc/machine/riscv/memcpy.c b/newlib/libc/machine/riscv/memcpy.c
>> index a27e0ecb1..cd58c30a5 100644
>> --- a/newlib/libc/machine/riscv/memcpy.c
>> +++ b/newlib/libc/machine/riscv/memcpy.c
>> @@ -10,7 +10,7 @@
>>      http://www.opensource.org/licenses.
>>   */
>>
>> -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
>> +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__) || defined(__riscv_v)
>>   // memcpy defined in memcpy-asm.S
>>   #else
>>
>> diff --git a/newlib/libc/machine/riscv/memmove-asm.S b/newlib/libc/machine/riscv/memmove-asm.S
>> index 061472ca2..5cc2e5143 100644
>> --- a/newlib/libc/machine/riscv/memmove-asm.S
>> +++ b/newlib/libc/machine/riscv/memmove-asm.S
>> @@ -9,11 +9,11 @@
>>      http://www.opensource.org/licenses.
>>   */
>>
>> -#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
>>   .text
>>   .global memmove
>>   .type  memmove, @function
>>   memmove:
>> +#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
>>     beqz a2, .Ldone              /* in case there are 0 bytes to be copied, return immediately */
>>
>>     mv a4, a0                    /* copy the destination address over to a4, since memmove should return that address in a0 at the end */
>> @@ -37,4 +37,49 @@ memmove:
>>     ret
>>
>>     .size        memmove, .-memmove
>> +#elif defined(__riscv_v)
>> +  .option push
>> +  .option arch, +v
>> +  beqz    a2, .Ldone_move           /* n == 0 */
>> +  beq     a0, a1, .Ldone_move       /* dst == src */
>> +
>> +  /* overlap check */
>> +  bgeu    a1, a0, .Lforward_move    /* src >= dst then forward move*/
>> +
>> +  sub     t2, a0, a1                /* t2 = dst - src */
>> +  bgeu    t2, a2, .Lforward_move    /* no overlap then forward move */
>> +
>> +  /* backward move */
>> +  add     t0, a0, a2                /* running dst_end */
>> +  add     t1, a1, a2                /* running src_end */
>> +
>> +.Lbackward_loop:
>> +  vsetvli t3, a2, e8, m8, ta, ma    /* t3 = vl (bytes) */
>> +  sub     t0, t0, t3
>> +  sub     t1, t1, t3
>> +  vle8.v  v0, (t1)
>> +  vse8.v  v0, (t0)
>> +  sub     a2, a2, t3
>> +  bnez    a2, .Lbackward_loop
>> +  j       .Ldone_move
> 
> `ret` rather than `j       .Ldone_move` here, ret and j are both one
> instruction, so let just return to save one more jump :)

Absolutely.I'll change in the next revision.

Thank you very much for the detailed and thoughtful feedback. I really 
appreciate your guidance. I'll post v2 of the patch shortly.

Best regards,
Pincheng Wang