#include <my_global.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
typedef unsigned (*my_crc32_t)(unsigned , const void *, size_t);
unsigned crc32_aarch64(unsigned , const void *, size_t);
#ifdef HAVE_ARMV8_CRC
# ifdef HAVE_ARMV8_CRYPTO
static unsigned crc32c_aarch64_pmull(unsigned , const void *, size_t);
# endif
# ifdef _WIN32
# include <windows.h>
# ifdef __clang__
# include <arm_acle.h>
# include <arm_neon.h>
# endif
int crc32_aarch64_available(void )
{
return IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE);
}
unsigned crc32c_aarch64(unsigned , const void *, size_t);
my_crc32_t crc32c_aarch64_available(void )
{
if (crc32_aarch64_available() == 0 )
return NULL;
if (IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
return crc32c_aarch64_pmull;
return crc32c_aarch64;
}
# else /* _WIN32 */
# ifdef __APPLE__
# include <sys/sysctl.h>
int crc32_aarch64_available(void )
{
int ret;
size_t len = sizeof (ret);
if (sysctlbyname("hw.optional.armv8_crc32" , &ret, &len, NULL, 0 ) == -1 )
return 0 ;
return ret;
}
my_crc32_t crc32c_aarch64_available(void )
{
# ifdef HAVE_ARMV8_CRYPTO
if (crc32_aarch64_available())
return crc32c_aarch64_pmull;
# endif
return NULL;
}
# else /* __APPLE__ */
# include <sys/auxv.h>
# if defined (__FreeBSD__) || defined (__OpenBSD__)
static unsigned long getauxval(unsigned int key)
{
unsigned long val;
if (elf_aux_info(key, (void *)&val, (int )sizeof (val) != 0 ))
return 0 ul;
return val;
}
# else
# include <asm /hwcap.h>
# endif
# ifndef HWCAP_CRC32
# define HWCAP_CRC32 (1 << 7 )
# endif
# ifndef HWCAP_PMULL
# define HWCAP_PMULL (1 << 4 )
# endif
/* ARM made crc32 default from ARMv8.1 but optional in ARMv8A
* Runtime check API .
*/
int crc32_aarch64_available(void )
{
unsigned long auxv= getauxval(AT_HWCAP);
return (auxv & HWCAP_CRC32) != 0 ;
}
# endif /* __APPLE__ */
# ifndef __APPLE__
static unsigned crc32c_aarch64(unsigned , const void *, size_t);
my_crc32_t crc32c_aarch64_available(void )
{
unsigned long auxv= getauxval(AT_HWCAP);
if (!(auxv & HWCAP_CRC32))
return NULL;
# ifdef HAVE_ARMV8_CRYPTO
/* Raspberry Pi 4 supports crc32 but doesn't support pmull (MDEV-23030). */
if (auxv & HWCAP_PMULL)
return crc32c_aarch64_pmull;
# endif
return crc32c_aarch64;
}
# endif /* __APPLE__ */
# endif /* _WIN32 */
const char *crc32c_aarch64_impl(my_crc32_t c)
{
# ifdef HAVE_ARMV8_CRYPTO
if (c == crc32c_aarch64_pmull)
return "Using ARMv8 crc32 + pmull instructions" ;
# endif
# ifndef __APPLE__
if (c == crc32c_aarch64)
return "Using ARMv8 crc32 instructions" ;
# endif
return NULL;
}
#endif /* HAVE_ARMV8_CRC */
#ifndef HAVE_ARMV8_CRC_CRYPTO_INTRINSICS
/* Request crc extension capabilities from the assembler */
asm (".arch_extension crc" );
# ifdef HAVE_ARMV8_CRYPTO
/* crypto extension */
asm (".arch_extension crypto" );
# endif
#define CRC32CX(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32CW(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32CH(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32CB(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32X(crc, value) __asm__("crc32x %w[c], %w[c], %x[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32W(crc, value) __asm__("crc32w %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32H(crc, value) __asm__("crc32h %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32B(crc, value) __asm__("crc32b %w[c], %w[c], %w[v]" :[c]"+r" (crc):[v]"r" (value))
#define CRC32C3X8(buffer, ITR) \
__asm__("crc32cx %w[c1], %w[c1], %x[v]" :[c1]"+r" (crc1):[v]"r" (uint8korr(buffer + (42 *1 + (ITR)) * sizeof (uint64_t))));\
__asm__("crc32cx %w[c2], %w[c2], %x[v]" :[c2]"+r" (crc2):[v]"r" (uint8korr(buffer + (42 *2 + (ITR)) * sizeof (uint64_t))));\
__asm__("crc32cx %w[c0], %w[c0], %x[v]" :[c0]"+r" (crc0):[v]"r" (uint8korr(buffer + (42 *0 + (ITR)) * sizeof (uint64_t))));
#else /* HAVE_ARMV8_CRC_CRYPTO_INTRINSICS */
/* Intrinsics header*/
#ifndef _WIN32
#include <arm_acle.h>
#endif
#include <arm_neon.h>
#define CRC32CX(crc, value) (crc) = __crc32cd((crc), (value))
#define CRC32CW(crc, value) (crc) = __crc32cw((crc), (value))
#define CRC32CH(crc, value) (crc) = __crc32ch((crc), (value))
#define CRC32CB(crc, value) (crc) = __crc32cb((crc), (value))
#define CRC32X(crc, value) (crc) = __crc32d((crc), (value))
#define CRC32W(crc, value) (crc) = __crc32w((crc), (value))
#define CRC32H(crc, value) (crc) = __crc32h((crc), (value))
#define CRC32B(crc, value) (crc) = __crc32b((crc), (value))
#define CRC32C3X8(buffer, ITR) \
crc1 = __crc32cd(crc1, uint8korr(buffer + (42 *1 + (ITR)) * sizeof (uint64_t)));\
crc2 = __crc32cd(crc2, uint8korr(buffer + (42 *2 + (ITR)) * sizeof (uint64_t)));\
crc0 = __crc32cd(crc0, uint8korr(buffer + (42 *0 + (ITR)) * sizeof (uint64_t)));
#endif /* HAVE_ARMV8_CRC_CRYPTO_INTRINSICS */
#define CRC32C7X3X8(buffer, ITR) do {\
CRC32C3X8(buffer, ((ITR) * 7 + 0 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 1 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 2 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 3 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 4 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 5 )) \
CRC32C3X8(buffer, ((ITR) * 7 + 6 )) \
} while (0 )
#if defined _MSC_VER && !defined __clang__
#define PREF4X64L1(buffer, offset, itr)\
__prefetch(buffer + (offset) + ((itr) + 0 )*64 );\
__prefetch(buffer + (offset) + ((itr) + 1 )*64 );\
__prefetch(buffer + (offset) + ((itr) + 2 )*64 );\
__prefetch(buffer + (offset) + ((itr) + 3 )*64 );
#else
#define PREF4X64L1(buffer, PREF_OFFSET, ITR) \
__asm__("PRFM PLDL1KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 0 )*64 ));\
__asm__("PRFM PLDL1KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 1 )*64 ));\
__asm__("PRFM PLDL1KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 2 )*64 ));\
__asm__("PRFM PLDL1KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 3 )*64 ));
#endif
#define PREF1KL1(buffer, PREF_OFFSET) \
PREF4X64L1(buffer,(PREF_OFFSET), 0 ) \
PREF4X64L1(buffer,(PREF_OFFSET), 4 ) \
PREF4X64L1(buffer,(PREF_OFFSET), 8 ) \
PREF4X64L1(buffer,(PREF_OFFSET), 12 )
#if defined _MSC_VER && !defined __clang__
#define MY_PLDL2KEEP 2 /* PLDL2KEEP is 2 in ARMv8 */
#define PREF4X64L2(buffer,offset,itr)\
__prefetch2(buffer + offset + ((itr) + 0 ) * 64 , MY_PLDL2KEEP);\
__prefetch2(buffer + offset + ((itr) + 1 ) * 64 , MY_PLDL2KEEP);\
__prefetch2(buffer + offset + ((itr) + 2 ) * 64 , MY_PLDL2KEEP);\
__prefetch2(buffer + offset + ((itr) + 3 ) * 64 , MY_PLDL2KEEP);
#else
#define PREF4X64L2(buffer, PREF_OFFSET, ITR) \
__asm__("PRFM PLDL2KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 0 )*64 ));\
__asm__("PRFM PLDL2KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 1 )*64 ));\
__asm__("PRFM PLDL2KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 2 )*64 ));\
__asm__("PRFM PLDL2KEEP, [%x[v],%[c]]" ::[v]"r" (buffer), [c]"I" ((PREF_OFFSET) + ((ITR) + 3 )*64 ));
#endif
#define PREF1KL2(buffer, PREF_OFFSET) \
PREF4X64L2(buffer,(PREF_OFFSET), 0 ) \
PREF4X64L2(buffer,(PREF_OFFSET), 4 ) \
PREF4X64L2(buffer,(PREF_OFFSET), 8 ) \
PREF4X64L2(buffer,(PREF_OFFSET), 12 )
#ifndef __APPLE__
static unsigned crc32c_aarch64(unsigned crc, const void *buf, size_t len)
{
int64_t length= (int64_t)len;
const unsigned char *buffer= buf;
crc^= 0 xffffffff;
while ((length-= sizeof (uint64_t)) >= 0 )
{
CRC32CX(crc, uint8korr(buffer));
buffer+= sizeof (uint64_t);
}
/* The following is more efficient than the straight loop */
if (length & sizeof (uint32_t))
{
CRC32CW(crc, uint4korr(buffer));
buffer+= sizeof (uint32_t);
}
if (length & sizeof (uint16_t))
{
CRC32CH(crc, uint2korr(buffer));
buffer+= sizeof (uint16_t);
}
if (length & sizeof (uint8_t))
CRC32CB(crc, *buffer);
return ~crc;
}
#endif
#ifdef HAVE_ARMV8_CRYPTO
static inline uint64_t poly_mul(uint64_t a, uint64_t b)
{
#if defined _MSC_VER && !defined __clang__
return vgetq_lane_u64(vreinterpretq_u64_p128(neon_pmull_64(vcreate_p64(a), vcreate_p64(b))),0 );
#else
return (uint64_t) vmull_p64(a, b);
#endif
}
static unsigned crc32c_aarch64_pmull(unsigned crc, const void *buf, size_t len)
{
int64_t length= (int64_t)len;
const unsigned char *buffer= buf;
crc^= 0 xffffffff;
/* Crypto extension Support
* Parallel computation with 1024 Bytes ( per block )
* Intrinsics Support
*/
# ifdef HAVE_ARMV8_CRC_CRYPTO_INTRINSICS
/* Process per block size of 1024 Bytes
* A block size = 8 + 42 * 3 * sizeof ( uint64_t ) + 8
*/
for (const poly64_t k1= 0 xe417f38a, k2= 0 x8f158014; (length-= 1024 ) >= 0 ; )
{
uint32_t crc0, crc1, crc2;
uint64_t t0, t1;
/* Prefetch 3*1024 data for avoiding L2 cache miss */
PREF1KL2(buffer, 1024 *3 );
/* Do first 8 bytes here for better pipelining */
crc0= __crc32cd(crc, uint8korr(buffer));
crc1= 0 ;
crc2= 0 ;
buffer+= sizeof (uint64_t);
/* Process block inline
* Process crc0 last to avoid dependency with above
*/
CRC32C7X3X8(buffer, 0 );
CRC32C7X3X8(buffer, 1 );
CRC32C7X3X8(buffer, 2 );
CRC32C7X3X8(buffer, 3 );
CRC32C7X3X8(buffer, 4 );
CRC32C7X3X8(buffer, 5 );
buffer+= 42 *3 *sizeof (uint64_t);
/* Prefetch data for following block to avoid L1 cache miss */
PREF1KL1(buffer, 1024 );
/* Last 8 bytes
* Merge crc0 and crc1 into crc2
* crc1 multiply by K2
* crc0 multiply by K1
*/
t1= poly_mul(crc1, k2);
t0= poly_mul(crc0, k1);
crc= __crc32cd(crc2, uint8korr(buffer));
crc1= __crc32cd(0 , t1);
crc^= crc1;
crc0= __crc32cd(0 , t0);
crc^= crc0;
buffer+= sizeof (uint64_t);
}
# else /* HAVE_ARMV8_CRC_CRYPTO_INTRINSICS */
/*No intrinsics*/
__asm__("mov x16, #0xf38a \n\t"
"movk x16, #0xe417, lsl 16 \n\t"
"mov v1.2d[0], x16 \n\t"
"mov x16, #0x8014 \n\t"
"movk x16, #0x8f15, lsl 16 \n\t"
"mov v0.2d[0], x16 \n\t"
:::"x16" );
while ((length-= 1024 ) >= 0 )
{
uint32_t crc0, crc1, crc2;
PREF1KL2(buffer, 1024 *3 );
__asm__("crc32cx %w[c0], %w[c], %x[v]\n\t"
:[c0]"=r" (crc0):[c]"r" (crc), [v]"r" (uint8korr(buffer)):);
crc1= 0 ;
crc2= 0 ;
buffer+= sizeof (uint64_t);
CRC32C7X3X8(buffer, 0 );
CRC32C7X3X8(buffer, 1 );
CRC32C7X3X8(buffer, 2 );
CRC32C7X3X8(buffer, 3 );
CRC32C7X3X8(buffer, 4 );
CRC32C7X3X8(buffer, 5 );
buffer+= 42 *3 *sizeof (uint64_t);
PREF1KL1(buffer, 1024 );
__asm__("mov v2.2d[0], %x[c1] \n\t"
"pmull v2.1q, v2.1d, v0.1d \n\t"
"mov v3.2d[0], %x[c0] \n\t"
"pmull v3.1q, v3.1d, v1.1d \n\t"
"crc32cx %w[c], %w[c2], %x[v] \n\t"
"mov %x[c1], v2.2d[0] \n\t"
"crc32cx %w[c1], wzr, %x[c1] \n\t"
"eor %w[c], %w[c], %w[c1] \n\t"
"mov %x[c0], v3.2d[0] \n\t"
"crc32cx %w[c0], wzr, %x[c0] \n\t"
"eor %w[c], %w[c], %w[c0] \n\t"
:[c1]"+r" (crc1), [c0]"+r" (crc0), [c2]"+r" (crc2), [c]"+r" (crc)
:[v]"r" (uint8korr(buffer)));
buffer+= sizeof (uint64_t);
}
# endif /* HAVE_ARMV8_CRC_CRYPTO_INTRINSICS */
/* Done if Input data size is aligned with 1024 */
length+= 1024 ;
if (length)
{
while ((length-= sizeof (uint64_t)) >= 0 )
{
CRC32CX(crc, uint8korr(buffer));
buffer+= sizeof (uint64_t);
}
/* The following is more efficient than the straight loop */
if (length & sizeof (uint32_t))
{
CRC32CW(crc, uint4korr(buffer));
buffer+= sizeof (uint32_t);
}
if (length & sizeof (uint16_t))
{
CRC32CH(crc, uint2korr(buffer));
buffer+= sizeof (uint16_t);
}
if (length & sizeof (uint8_t))
CRC32CB(crc, *buffer);
}
return ~crc;
}
#endif /* HAVE_ARMV8_CRYPTO */
/* There are multiple approaches to calculate crc.
Approach - 1 : Process 8 bytes then 4 bytes then 2 bytes and then 1 bytes
Approach - 2 : Process 8 bytes and remaining workload using 1 bytes
Approach - 3 : Process 64 bytes at once by issuing 8 crc call and remaining
using 8 / 1 combination .
Based on micro - benchmark testing we found that Approach - 2 works best especially
given small chunk of variable data. */
unsigned int crc32_aarch64(unsigned int crc, const void *buf, size_t len)
{
const uint8_t *buf1= buf;
const uint64_t *buf8= (const uint64_t *) (((uintptr_t) buf + 7 ) & ~7 );
crc= ~crc;
/* if start pointer is not 8 bytes aligned */
while ((buf1 != (const uint8_t *) buf8) && len)
{
CRC32B(crc, *buf1++);
len--;
}
for (; len >= 8 ; len-= 8 ) {
CRC32X(crc, uint8korr((uchar *)buf8));
buf8++;
}
buf1= (const uint8_t *) buf8;
while (len--)
CRC32B(crc, *buf1++);
return ~crc;
}
Messung V0.5 in Prozent C=94 H=86 G=89
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland