<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>