Commit 611cd6b9 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-33817 preparation: Restructuring and unit tests

In our unit test, let us rely on our own reference
implementation using the reflected
CRC-32 ISO 3309 and CRC-32C polynomials. Let us also
test with various lengths.

Let us refactor the CRC-32 and CRC-32C implementations
so that no special compilation flags will be needed and
that some function call indirection will be avoided.

pmull_supported: Remove. We will have pointers to two separate
functions crc32c_aarch64_pmull() and crc32c_aarch64().
parent b84d335d
......@@ -60,29 +60,21 @@ IF (WIN32)
ENDIF()
IF(MSVC)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32_x86.c)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32_x86.c crc32/crc32c_x86.cc)
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET (MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32c_amd64.cc)
ENDIF()
ADD_DEFINITIONS(-DHAVE_SSE42 -DHAVE_PCLMUL)
IF(CLANG_CL)
SET_SOURCE_FILES_PROPERTIES(crc32/crc32_x86.c PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
ENDIF()
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|i386|i686")
MY_CHECK_CXX_COMPILER_FLAG(-msse4.2)
MY_CHECK_CXX_COMPILER_FLAG(-mpclmul)
CHECK_INCLUDE_FILE(cpuid.h HAVE_CPUID_H)
CHECK_INCLUDE_FILE(x86intrin.h HAVE_X86INTRIN_H)
IF(have_CXX__msse4.2 AND HAVE_CPUID_H)
ADD_DEFINITIONS(-DHAVE_SSE42)
IF (have_CXX__mpclmul AND HAVE_X86INTRIN_H)
ADD_DEFINITIONS(-DHAVE_PCLMUL)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32_x86.c)
SET_SOURCE_FILES_PROPERTIES(crc32/crc32_x86.c PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32c_amd64.cc)
SET_SOURCE_FILES_PROPERTIES(crc32/crc32c_amd64.cc PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
ENDIF()
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32_x86.c crc32/crc32c_x86.cc)
IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS "5")
SET_SOURCE_FILES_PROPERTIES(crc32/crc32_x86.c PROPERTIES
COMPILE_FLAGS "-msse4.2 -mpclmul")
ENDIF()
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(MYSYS_SOURCES ${MYSYS_SOURCES} crc32/crc32c_amd64.cc)
IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS "5")
SET_SOURCE_FILES_PROPERTIES(crc32/crc32c_amd64.cc PROPERTIES
COMPILE_FLAGS "-msse4.2 -mpclmul")
ENDIF()
ENDIF()
ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
......
This diff is collapsed.
......@@ -56,11 +56,16 @@
#include <stddef.h>
#ifdef __GNUC__
#include <x86intrin.h>
# include <emmintrin.h>
# include <smmintrin.h>
# include <tmmintrin.h>
# include <wmmintrin.h>
# define USE_PCLMUL __attribute__((target("sse4.2,pclmul")))
#elif defined(_MSC_VER)
#include <intrin.h>
# include <intrin.h>
# define USE_PCLMUL /* nothing */
#else
#error "unknown compiler"
# error "unknown compiler"
#endif
/**
......@@ -71,6 +76,7 @@
*
* @return \a reg << (\a num * 8)
*/
USE_PCLMUL
static inline __m128i xmm_shift_left(__m128i reg, const unsigned int num)
{
static const MY_ALIGNED(16) uint8_t crc_xmm_shift_tab[48]= {
......@@ -111,6 +117,7 @@ struct crcr_pclmulqdq_ctx
*
* @return New 16 byte folded data
*/
USE_PCLMUL
static inline __m128i crcr32_folding_round(const __m128i data_block,
const __m128i precomp, const __m128i fold)
{
......@@ -128,6 +135,7 @@ static inline __m128i crcr32_folding_round(const __m128i data_block,
*
* @return data reduced to 64 bits
*/
USE_PCLMUL
static inline __m128i crcr32_reduce_128_to_64(__m128i data128, const __m128i precomp)
{
__m128i tmp0, tmp1, tmp2;
......@@ -152,6 +160,7 @@ static inline __m128i crcr32_reduce_128_to_64(__m128i data128, const __m128i pre
*
* @return data reduced to 32 bits
*/
USE_PCLMUL
static inline uint32_t crcr32_reduce_64_to_32(__m128i data64, const __m128i precomp)
{
static const MY_ALIGNED(16) uint32_t mask1[4]= {
......@@ -188,6 +197,7 @@ static inline uint32_t crcr32_reduce_64_to_32(__m128i data64, const __m128i prec
*
* @return CRC for given \a data block (32 bits wide).
*/
USE_PCLMUL
static inline uint32_t crcr32_calc_pclmulqdq(const uint8_t *data, uint32_t data_len,
uint32_t crc,
const struct crcr_pclmulqdq_ctx *params)
......
This diff is collapsed.
......@@ -47,6 +47,11 @@
#include <nmmintrin.h>
#include <wmmintrin.h>
#ifdef _MSC_VER
# define USE_PCLMUL /* nothing */
#else
# define USE_PCLMUL __attribute__((target("sse4.2,pclmul")))
#endif
#define CRCtriplet(crc, buf, offset) \
crc##0 = _mm_crc32_u64(crc##0, *(buf##0 + offset)); \
......@@ -131,6 +136,7 @@ static const uint64_t clmul_constants alignas(16) [] = {
};
// Compute the crc32c value for buffer smaller than 8
USE_PCLMUL
static inline void align_to_8(
size_t len,
uint64_t& crc0, // crc so far, updated on return
......@@ -155,6 +161,7 @@ static inline void align_to_8(
// CombineCRC performs pclmulqdq multiplication of 2 partial CRC's and a well
// chosen constant and xor's these with the remaining CRC.
//
USE_PCLMUL
static inline uint64_t CombineCRC(
size_t block_size,
uint64_t crc0,
......@@ -176,6 +183,7 @@ static inline uint64_t CombineCRC(
// Compute CRC-32C using the Intel hardware instruction.
extern "C"
USE_PCLMUL
uint32_t crc32c_3way(uint32_t crc, const char *buf, size_t len)
{
const unsigned char* next = (const unsigned char*)buf;
......
......@@ -11,8 +11,7 @@
extern "C" {
#endif
extern uint32_t crc32c_ppc(uint32_t crc, unsigned char const *buffer,
unsigned len);
extern unsigned crc32c_ppc(unsigned crc, const void *buffer, size_t len);
#ifdef __cplusplus
}
......
#include <my_global.h>
#include <cstddef>
#include <cstdint>
#ifdef _MSC_VER
# include <intrin.h>
#else
# include <cpuid.h>
#endif
extern "C" unsigned crc32c_sse42(unsigned crc, const void* buf, size_t size);
constexpr uint32_t cpuid_ecx_SSE42= 1U << 20;
constexpr uint32_t cpuid_ecx_SSE42_AND_PCLMUL= cpuid_ecx_SSE42 | 1U << 1;
static uint32_t cpuid_ecx()
{
#ifdef __GNUC__
uint32_t reax= 0, rebx= 0, recx= 0, redx= 0;
__cpuid(1, reax, rebx, recx, redx);
return recx;
#elif defined _MSC_VER
int regs[4];
__cpuid(regs, 1);
return regs[2];
#else
# error "unknown compiler"
#endif
}
typedef unsigned (*my_crc32_t)(unsigned, const void *, size_t);
extern "C" unsigned int crc32_pclmul(unsigned int, const void *, size_t);
extern "C" unsigned int crc32c_3way(unsigned int, const void *, size_t);
extern "C" my_crc32_t crc32_pclmul_enabled(void)
{
if (~cpuid_ecx() & cpuid_ecx_SSE42_AND_PCLMUL)
return nullptr;
return crc32_pclmul;
}
extern "C" my_crc32_t crc32c_x86_available(void)
{
#if SIZEOF_SIZE_T == 8
switch (cpuid_ecx() & cpuid_ecx_SSE42_AND_PCLMUL) {
case cpuid_ecx_SSE42_AND_PCLMUL:
return crc32c_3way;
case cpuid_ecx_SSE42:
return crc32c_sse42;
}
#else
if (cpuid_ecx() & cpuid_ecx_SSE42)
return crc32c_sse42;
#endif
return nullptr;
}
extern "C" const char *crc32c_x86_impl(my_crc32_t c)
{
#if SIZEOF_SIZE_T == 8
if (c == crc32c_3way)
return "Using crc32 + pclmulqdq instructions";
#endif
if (c == crc32c_sse42)
return "Using SSE4.2 crc32 instructions";
return nullptr;
}
......@@ -28,7 +28,7 @@
* any later version, or
* b) the Apache License, Version 2.0
*/
#include <stddef.h>
#include <altivec.h>
......@@ -57,12 +57,13 @@ static unsigned int __attribute__ ((aligned (32)))
__crc32_vpmsum(unsigned int crc, const void* p, unsigned long len);
unsigned int CRC32_FUNCTION(unsigned int crc, const unsigned char *p,
unsigned long len)
unsigned CRC32_FUNCTION(unsigned crc, const void *buffer, size_t len)
{
unsigned int prealign;
unsigned int tail;
const unsigned char *p = buffer;
#ifdef CRC_XOR
crc ^= 0xffffffff;
#endif
......
......@@ -26,23 +26,22 @@ static unsigned int my_crc32_zlib(unsigned int crc, const void *data,
return (unsigned int) crc32(crc, (const Bytef *)data, (unsigned int) len);
}
#ifdef HAVE_PCLMUL
extern "C" int crc32_pclmul_enabled();
extern "C" unsigned int crc32_pclmul(unsigned int, const void *, size_t);
#elif defined(__GNUC__) && defined(HAVE_ARMV8_CRC)
typedef unsigned int (*my_crc32_t)(unsigned int, const void *, size_t);
#if defined _M_IX86 || defined _M_X64 || defined __i386__ || defined __x86_64__
extern "C" my_crc32_t crc32_pclmul_enabled();
#elif defined HAVE_ARMV8_CRC
extern "C" int crc32_aarch64_available();
extern "C" unsigned int crc32_aarch64(unsigned int, const void *, size_t);
#endif
typedef unsigned int (*my_crc32_t)(unsigned int, const void *, size_t);
static my_crc32_t init_crc32()
{
#ifdef HAVE_PCLMUL
if (crc32_pclmul_enabled())
return crc32_pclmul;
#elif defined(__GNUC__) && defined(HAVE_ARMV8_CRC)
#if defined _M_IX86 || defined _M_X64 || defined __i386__ || defined __x86_64__
if (my_crc32_t crc= crc32_pclmul_enabled())
return crc;
#elif defined HAVE_ARMV8_CRC
if (crc32_aarch64_available())
return crc32_aarch64;
#endif
......
/* Copyright (c) MariaDB 2020
/* Copyright (c) MariaDB 2020, 2024
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
......@@ -19,51 +19,127 @@
#include <tap.h>
#include <string.h>
#include <ctype.h>
#include <zlib.h>
/*
Check that optimized crc32 (ieee, or ethernet polynomical) returns the same
result as zlib (not so well optimized, yet, but trustworthy)
The following lookup table oriented computation of CRC-32
is based on the Public Domain / Creative Commons CC0 Perl code from
http://billauer.co.il/blog/2011/05/perl-crc32-crc-xs-module/
*/
#define DO_TEST_CRC32(crc,str) \
ok(crc32(crc,(const Bytef *)str,(uint)(sizeof(str)-1)) == my_checksum(crc, str, sizeof(str)-1), "crc32 '%s'",str)
/* Check that CRC32-C calculation returns correct result*/
#define DO_TEST_CRC32C(crc,str,expected) \
do { \
unsigned int v = my_crc32c(crc, str, sizeof(str)-1); \
printf("crc32(%u,'%s',%zu)=%u\n",crc,str,sizeof(str)-1,v); \
ok(expected == my_crc32c(crc, str, sizeof(str)-1),"crc32c '%s'",str); \
}while(0)
/** Lookup tables */
static uint32 tab_3309[256], tab_castagnoli[256];
/** Initialize a lookup table for a CRC-32 polynomial */
static void init_lookup(uint32 *tab, uint32 polynomial)
{
unsigned i;
for (i= 0; i < 256; i++)
{
uint32 x= i;
unsigned j;
for (j= 0; j < 8; j++)
if (x & 1)
x= (x >> 1) ^ polynomial;
else
x>>= 1;
tab[i]= x;
}
}
/** Compute a CRC-32 one octet at a time based on a lookup table */
static uint crc_(uint32 crc, const void *buf, size_t len, const uint32 *tab)
{
const unsigned char *b= buf;
const unsigned char *const end = b + len;
crc^= 0xffffffff;
while (b != end)
crc= ((crc >> 8) & 0xffffff) ^ tab[(crc ^ *b++) & 0xff];
crc^= 0xffffffff;
return crc;
}
static uint crc32(uint32 crc, const void *buf, size_t len)
{ return crc_(crc, buf, len, tab_3309); }
static uint crc32c(uint32 crc, const void *buf, size_t len)
{ return crc_(crc, buf, len, tab_castagnoli); }
#define LONG_STR "1234567890234568900212345678901231213123321212123123123123123"\
"............................................................................." \
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" \
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
static char buf[16384];
typedef uint (*check)(uint32, const void*, size_t);
static size_t test_buf(check c1, check c2)
{
size_t s;
for (s= sizeof buf; s; s--)
if (c1(0, buf, s) != c2(0, buf, s))
break;
return s;
}
#define DO_TEST_CRC32(crc,str,len) \
ok(crc32(crc,str,len) == my_checksum(crc, str, len), \
"crc32(%u,'%.*s')", crc, (int) len, str)
/* Check that CRC-32C calculation returns correct result*/
#define DO_TEST_CRC32C(crc,str,len) \
ok(crc32c(crc,str,len) == my_crc32c(crc, str, len), \
"crc32c(%u,'%.*s')", crc, (int) len, str)
static const char STR[]=
"123456789012345678900212345678901231213123321212123123123123123"
"..........................................................................."
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
int main(int argc __attribute__((unused)),char *argv[])
{
MY_INIT(argv[0]);
plan(14);
init_lookup(tab_3309, 0xedb88320);
init_lookup(tab_castagnoli, 0x82f63b78);
plan(36);
printf("%s\n",my_crc32c_implementation());
DO_TEST_CRC32(0,"");
DO_TEST_CRC32(1,"");
DO_TEST_CRC32(0,"12345");
DO_TEST_CRC32(1,"12345");
DO_TEST_CRC32(0,"1234567890123456789");
DO_TEST_CRC32(0, LONG_STR);
DO_TEST_CRC32(0,STR,0);
DO_TEST_CRC32(1,STR,0);
DO_TEST_CRC32(0,STR,3);
DO_TEST_CRC32(0,STR,5);
DO_TEST_CRC32(1,STR,5);
DO_TEST_CRC32(0,STR,15);
DO_TEST_CRC32(0,STR,16);
DO_TEST_CRC32(0,STR,19);
DO_TEST_CRC32(0,STR,32);
DO_TEST_CRC32(0,STR,63);
DO_TEST_CRC32(0,STR,64);
DO_TEST_CRC32(0,STR,65);
DO_TEST_CRC32(0,STR,255);
DO_TEST_CRC32(0,STR,256);
DO_TEST_CRC32(0,STR,257);
DO_TEST_CRC32(0,STR,(sizeof(STR)-1));
ok(0 == my_checksum(0, NULL, 0) , "crc32 data = NULL, length = 0");
DO_TEST_CRC32C(0,"", 0);
DO_TEST_CRC32C(1,"", 1);
DO_TEST_CRC32C(0, "12345", 416359221);
DO_TEST_CRC32C(1, "12345", 549473433);
DO_TEST_CRC32C(0, "1234567890123456789", 2366987449U);
DO_TEST_CRC32C(0, LONG_STR, 3009234172U);
DO_TEST_CRC32C(0,STR,0);
DO_TEST_CRC32C(1,STR,0);
DO_TEST_CRC32C(0,STR,3);
DO_TEST_CRC32C(0,STR,5);
DO_TEST_CRC32C(1,STR,5);
DO_TEST_CRC32C(0,STR,15);
DO_TEST_CRC32C(0,STR,16);
DO_TEST_CRC32C(0,STR,19);
DO_TEST_CRC32C(0,STR,32);
DO_TEST_CRC32C(0,STR,63);
DO_TEST_CRC32C(0,STR,64);
DO_TEST_CRC32C(0,STR,65);
DO_TEST_CRC32C(0,STR,255);
DO_TEST_CRC32C(0,STR,256);
DO_TEST_CRC32C(0,STR,257);
DO_TEST_CRC32C(0,STR,(sizeof(STR)-1));
ok(0 == my_crc32c(0, NULL, 0), "crc32c data = NULL, length = 0");
memset(buf, 0x5a, sizeof buf);
ok(0 == test_buf(my_checksum, crc32), "crc32 with various lengths");
ok(0 == test_buf(my_crc32c, crc32c), "crc32c with various lengths");
my_end(0);
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment