https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128212
Bug ID: 128212
Summary: __builtin_crc64_data64 loop performs unnecessary moves
between XMM/GPR and can be optimized further
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: 16567adigashreesh at gmail dot com
Target Milestone: ---
For a loop which computes __builtin_rev_crc64_data64 (and similar CRC
builtins), the builtin expansion loads the 64 bit data from memory into GPR and
then performs clmul and moves it back to GPR every iteration.
Godbolt link: https://godbolt.org/z/dx8o798TY
```
#include <stdio.h>
#include <stdint.h>
#define POLY 0x9A6C9329AC4BC9B5
uint64_t crc_loop(uint64_t *arr, size_t len) {
uint64_t crc = 0;
for (size_t i = 0; i < len; i++) {
crc = __builtin_crc64_data64(crc, arr[i], POLY);
}
return crc;
}
```
Observed the below assembly:
```
"crc_loop(unsigned long*, unsigned long)":
test rsi, rsi
je .L4
movdqa xmm2, XMMWORD PTR .LC0[rip]
lea rdx, [rdi+rsi*8]
xor eax, eax
.L3:
xor rax, QWORD PTR [rdi]
add rdi, 8
movq xmm0, rax
punpcklqdq xmm0, xmm0
movdqa xmm1, xmm0
pclmulqdq xmm1, xmm2, 0
pxor xmm0, xmm1
pclmulqdq xmm0, xmm2, 17
movq rax, xmm0
cmp rdi, rdx
jne .L3
ret
.L4:
xor eax, eax
ret
.LC0:
.quad -2071874871052589118
.quad -7319313487190308427
```
It would be more optimal to keep the result in lane 0 instead of lane 1, and
have the data loaded directly into new XMM reg using movq and perform the xor
inside XMM so that we can minimize the GPR<-->XMM transfers and have it done
only at the loop start and end once.
Additionally GCC could implement 128-bit folding algorithm as described in the
Intel whitepaper "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ
Instruction" when the loop is going to include 64 bytes and more. This would
involve computing the constants for 128-bit fold and performing single 128-bit
folds and reducing it at the loop end and processing the tail separately.