<div dir="ltr"><div>Hi Wathsala,</div><div><br></div><div>I tested this on QEMU for correctness. I don't have access to any ARM environment for precise benchmarking.</div><div>The only device I have is an Android phone where I can't compile DPDK either.</div><div>I can isolate this code and use libbenchmark via Termux if that is needed, but I don't expect to see large measurable benefits.</div><div><br></div><div>This patch is based on my previous work on FFmpeg's av_crc optimization for aarch64 which you can</div><div>refer here: <a href="https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21651">https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21651</a></div><div><br></div><div>Thanks,</div><div>Shreesh</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Fri, Oct 9, 2026 at 8:54 AM Wathsala Vithanage <<a href="mailto:wathsala.vithanage@arm.com">wathsala.vithanage@arm.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Shreesh,<br>
<br>
Looks correct. Can you share before and after performance data in the<br>
commit message covering short inputs below 16 bytes, lengths exercising<br>
the different nonzero remainders modulo 16, and larger buffers to show<br>
the effect on the folding loop?<br>
<br>
Thanks<br>
<br>
--wathsala<br>
<br>
On 10/6/26 10:18, Shreesh Adiga wrote:<br>
> This patch includes the following minor changes:<br>
> 1) Use TBL based shift left for variable len shift instead of using<br>
>     vshift_bytes_left which adds a switch case for 16 len cases.<br>
> 2) Align the fold constants to use PMULL and PMULL2 instructions<br>
>     instead of using cross lane operands which requires additional shift.<br>
> 3) Remove unnecessary static arrays used in crc32_reduce_64_to_32 by<br>
>     adapting x86 SSE logic.<br>
> 4) Use x86 SSE logic for processing partial bytes sequence to avoid<br>
>     using multiple variable len shifts which inserts switch case.<br>
><br>
> Signed-off-by: Shreesh Adiga <<a href="mailto:16567adigashreesh@gmail.com" target="_blank">16567adigashreesh@gmail.com</a>><br>
> ---<br>
>   lib/net/net_crc_neon.c | 71 +++++++++++++++++++++++++++---------------<br>
>   1 file changed, 46 insertions(+), 25 deletions(-)<br>
><br>
> diff --git a/lib/net/net_crc_neon.c b/lib/net/net_crc_neon.c<br>
> index d924aed25d..3833fcba6e 100644<br>
> --- a/lib/net/net_crc_neon.c<br>
> +++ b/lib/net/net_crc_neon.c<br>
> @@ -24,6 +24,31 @@ struct crc_pmull_ctx {<br>
>   alignas(16) struct crc_pmull_ctx crc32_eth_pmull;<br>
>   alignas(16) struct crc_pmull_ctx crc16_ccitt_pmull;<br>
>   <br>
> +static const alignas(16) uint8_t crc_neon_shift_tab[32] = {<br>
> +     0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,<br>
> +     0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,<br>
> +     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,<br>
> +     0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f<br>
> +};<br>
> +<br>
> +/**<br>
> + * Shifts left 128 bit register by specified number of bytes<br>
> + *<br>
> + * @param reg<br>
> + *   128 bit value<br>
> + * @param num<br>
> + *   number of bytes to shift left reg by (0-16)<br>
> + *<br>
> + * @return<br>
> + *   reg << (num * 8)<br>
> + */<br>
> +static inline uint64x2_t<br>
> +neon_shift_left(uint64x2_t reg, const unsigned int num)<br>
> +{<br>
> +     uint8x16_t tbl = vld1q_u8(crc_neon_shift_tab + 16 - num);<br>
> +     return vreinterpretq_u64_u8(vqtbl1q_u8(vreinterpretq_u8_u64(reg), tbl));<br>
> +}<br>
> +<br>
>   /**<br>
>    * @brief Performs one folding round<br>
>    *<br>
> @@ -46,12 +71,12 @@ crcr32_folding_round(uint64x2_t data_block, uint64x2_t precomp,<br>
>       uint64x2_t fold)<br>
>   {<br>
>       uint64x2_t tmp0 = vreinterpretq_u64_p128(vmull_p64(<br>
> -                     vgetq_lane_p64(vreinterpretq_p64_u64(fold), 1),<br>
> +                     vgetq_lane_p64(vreinterpretq_p64_u64(fold), 0),<br>
>                       vgetq_lane_p64(vreinterpretq_p64_u64(precomp), 0)));<br>
>   <br>
> -     uint64x2_t tmp1 = vreinterpretq_u64_p128(vmull_p64(<br>
> -                     vgetq_lane_p64(vreinterpretq_p64_u64(fold), 0),<br>
> -                     vgetq_lane_p64(vreinterpretq_p64_u64(precomp), 1)));<br>
> +     uint64x2_t tmp1 = vreinterpretq_u64_p128(vmull_high_p64(<br>
> +                     vreinterpretq_p64_u64(fold),<br>
> +                     vreinterpretq_p64_u64(precomp)));<br>
>   <br>
>       return veorq_u64(tmp1, veorq_u64(data_block, tmp0));<br>
>   }<br>
> @@ -98,26 +123,19 @@ static inline uint32_t<br>
>   crcr32_reduce_64_to_32(uint64x2_t data64,<br>
>       uint64x2_t precomp)<br>
>   {<br>
> -     static alignas(16) uint32_t mask1[4] = {<br>
> -             0xffffffff, 0xffffffff, 0x00000000, 0x00000000<br>
> -     };<br>
> -     static alignas(16) uint32_t mask2[4] = {<br>
> -             0x00000000, 0xffffffff, 0xffffffff, 0xffffffff<br>
> -     };<br>
>       uint64x2_t tmp0, tmp1, tmp2;<br>
>   <br>
> -     tmp0 = vandq_u64(data64, vld1q_u64((uint64_t *)mask2));<br>
> +     tmp0 = vreinterpretq_u64_u32(<br>
> +             vsetq_lane_u32(0, vreinterpretq_u32_u64(data64), 0));<br>
>   <br>
>       tmp1 = vreinterpretq_u64_p128(vmull_p64(<br>
>               vgetq_lane_p64(vreinterpretq_p64_u64(tmp0), 0),<br>
>               vgetq_lane_p64(vreinterpretq_p64_u64(precomp), 0)));<br>
>       tmp1 = veorq_u64(tmp1, tmp0);<br>
> -     tmp1 = vandq_u64(tmp1, vld1q_u64((uint64_t *)mask1));<br>
>   <br>
>       tmp2 = vreinterpretq_u64_p128(vmull_p64(<br>
>               vgetq_lane_p64(vreinterpretq_p64_u64(tmp1), 0),<br>
>               vgetq_lane_p64(vreinterpretq_p64_u64(precomp), 1)));<br>
> -     tmp2 = veorq_u64(tmp2, tmp1);<br>
>       tmp2 = veorq_u64(tmp2, tmp0);<br>
>   <br>
>       return vgetq_lane_u32(vreinterpretq_u32_u64(tmp2), 2);<br>
> @@ -177,10 +195,10 @@ crc32_eth_calc_pmull(<br>
>               fold = vld1q_u64((uint64_t *)buffer);<br>
>               fold = veorq_u64(fold, temp);<br>
>               if (unlikely(data_len < 4)) {<br>
> -                     fold = vshift_bytes_left(fold, 8 - data_len);<br>
> +                     fold = neon_shift_left(fold, 8 - data_len);<br>
>                       goto barret_reduction;<br>
>               }<br>
> -             fold = vshift_bytes_left(fold, 16 - data_len);<br>
> +             fold = neon_shift_left(fold, 16 - data_len);<br>
>               goto reduction_128_64;<br>
>       }<br>
>   <br>
> @@ -201,14 +219,17 @@ crc32_eth_calc_pmull(<br>
>   <br>
>       /** Partial bytes - process last <16 bytes */<br>
>       if (likely(n < data_len)) {<br>
> -             uint64x2_t last16, a, b, mask;<br>
> +             uint8x16_t last16, t1, t2;<br>
> +             uint64x2_t a, b;<br>
>               uint32_t rem = data_len & 15;<br>
>   <br>
> -             last16 = vld1q_u64((const uint64_t *)&data[data_len - 16]);<br>
> -             a = vshift_bytes_left(fold, 16 - rem);<br>
> -             b = vshift_bytes_right(fold, rem);<br>
> -             mask = vshift_bytes_left(vdupq_n_u64(-1), 16 - rem);<br>
> -             b = vorrq_u64(b, vandq_u64(mask, last16));<br>
> +             last16 = vld1q_u8((const uint8_t *)&data[data_len - 16]);<br>
> +             t1 = vld1q_u8(crc_neon_shift_tab + rem);<br>
> +             a  = vreinterpretq_u64_u8(vqtbl1q_u8(vreinterpretq_u8_u64(fold), t1));<br>
> +             t2 = vmvnq_u8(t1);<br>
> +             t2 = vqtbl1q_u8(vreinterpretq_u8_u64(fold), t2);<br>
> +             t1 = vcgezq_s8(vreinterpretq_s8_u8(t1));<br>
> +             b  = vreinterpretq_u64_u8(vbslq_u8(t1, last16, t2));<br>
>   <br>
>               /* k = rk3 & rk4 */<br>
>               fold = crcr32_folding_round(b, k, a);<br>
> @@ -230,14 +251,14 @@ void<br>
>   rte_net_crc_neon_init(void)<br>
>   {<br>
>       /* Initialize CRC16 data */<br>
> -     uint64_t ccitt_k1_k2[2] = {0x14ff2LLU, 0x19a3cLLU};<br>
> -     uint64_t ccitt_k3_k4[2] = {0x189aeLLU, 0x8e10LLU};<br>
> +     uint64_t ccitt_k1_k2[2] = {0x19a3cLLU, 0x14ff2LLU};<br>
> +     uint64_t ccitt_k3_k4[2] = {0x8e10LLU, 0x189aeLLU};<br>
>       uint64_t ccitt_k5_k6[2] = {0x189aeLLU, 0x114aaLLU};<br>
>       uint64_t ccitt_k7_k8[2] = {0x11c581910LLU, 0x10811LLU};<br>
>   <br>
>       /* Initialize CRC32 data */<br>
> -     uint64_t eth_k1_k2[2] = {0x1c6e41596LLU, 0x154442bd4LLU};<br>
> -     uint64_t eth_k3_k4[2] = {0xccaa009eLLU, 0x1751997d0LLU};<br>
> +     uint64_t eth_k1_k2[2] = {0x154442bd4LLU, 0x1c6e41596LLU};<br>
> +     uint64_t eth_k3_k4[2] = {0x1751997d0LLU, 0xccaa009eLLU};<br>
>       uint64_t eth_k5_k6[2] = {0xccaa009eLLU, 0x163cd6124LLU};<br>
>       uint64_t eth_k7_k8[2] = {0x1f7011640LLU, 0x1db710641LLU};<br>
>   <br>
</blockquote></div>