RE: [PATCH 1/1] riscv: add vectorized memset, memcpy and memmove
"Christian Herber (OSS)" <[email protected]> Tue, 16 Dec 2025 09:36:06 +0000
| Newsgroups | gmane.comp.lib.newlib |
|---|---|
| Message-ID | <AS8PR04MB9509DBF560A1925AA5B5462C86AAA@AS8PR04MB9509.eurprd04.prod.outlook.com> |
Hi Pincheng, is there a clear advantage for using assembly over using intrinsics? Christian > -----Original Message----- > From: Pincheng Wang <[email protected]> > Sent: Tuesday, 16 December 2025 03:20 > To: Kito Cheng <[email protected]> > Cc: [email protected] > Subject: Re: [PATCH 1/1] riscv: add vectorized memset, memcpy and memmove > > 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