Skip to content

Commit 578fe78

Browse files
pkdogYunquan Cheng
authored andcommitted
Add PCLMULQDQ-accelerated CRC32C path with large block folding
1 parent 2bbb3be commit 578fe78

6 files changed

Lines changed: 421 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,33 @@ int main() {
136136
" HAVE_SSE42)
137137
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS})
138138

139+
# Check for PCLMULQDQ support in the compiler (requires SSE4.2).
140+
set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS})
141+
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
142+
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} /arch:AVX")
143+
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
144+
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -msse4.2 -mpclmul")
145+
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
146+
check_cxx_source_compiles("
147+
#if defined(_MSC_VER)
148+
#include <intrin.h>
149+
#include <wmmintrin.h>
150+
#else // !defined(_MSC_VER)
151+
#include <cpuid.h>
152+
#include <nmmintrin.h>
153+
#include <wmmintrin.h>
154+
#endif // defined(_MSC_VER)
155+
156+
int main() {
157+
__m128i a = _mm_set_epi64x(1, 2);
158+
__m128i b = _mm_set_epi64x(3, 4);
159+
__m128i c = _mm_clmulepi64_si128(a, b, 0x00);
160+
(void)c;
161+
return 0;
162+
}
163+
" HAVE_PCLMUL)
164+
set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQURED_FLAGS})
165+
139166
# Check for ARMv8 w/ CRC and CRYPTO extensions support in the compiler.
140167
set(OLD_CMAKE_REQURED_FLAGS ${CMAKE_REQUIRED_FLAGS})
141168
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
@@ -262,6 +289,28 @@ if(BUILD_SHARED_LIBS)
262289
set_property(TARGET crc32c_sse42 PROPERTY POSITION_INDEPENDENT_CODE TRUE)
263290
endif(BUILD_SHARED_LIBS)
264291

292+
# PCLMULQDQ-accelerated CRC32C code is built separately with -mpclmul, so
293+
# unsupported instructions don't leak into code that runs without PCLMUL.
294+
add_library(crc32c_sse42_clmul OBJECT "")
295+
target_sources(crc32c_sse42_clmul
296+
PRIVATE
297+
"${PROJECT_BINARY_DIR}/include/crc32c/crc32c_config.h"
298+
"src/crc32c_sse42_clmul.cc"
299+
"src/crc32c_sse42_clmul.h"
300+
)
301+
if(HAVE_PCLMUL)
302+
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
303+
target_compile_options(crc32c_sse42_clmul PRIVATE "/arch:AVX")
304+
else(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
305+
target_compile_options(crc32c_sse42_clmul PRIVATE "-msse4.2" "-mpclmul")
306+
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
307+
endif(HAVE_PCLMUL)
308+
309+
# CMake only enables PIC by default in SHARED and MODULE targets.
310+
if(BUILD_SHARED_LIBS)
311+
set_property(TARGET crc32c_sse42_clmul PROPERTY POSITION_INDEPENDENT_CODE TRUE)
312+
endif(BUILD_SHARED_LIBS)
313+
265314
# Must be included before CMAKE_INSTALL_INCLUDEDIR is used.
266315
include(GNUInstallDirs)
267316

@@ -270,6 +319,7 @@ add_library(crc32c ""
270319
# section of target_sources when cmake_minimum_required becomes 3.9 or above.
271320
$<TARGET_OBJECTS:crc32c_arm64>
272321
$<TARGET_OBJECTS:crc32c_sse42>
322+
$<TARGET_OBJECTS:crc32c_sse42_clmul>
273323
)
274324
target_sources(crc32c
275325
PRIVATE
@@ -283,6 +333,7 @@ target_sources(crc32c
283333
"src/crc32c_round_up.h"
284334
"src/crc32c_sse42.h"
285335
"src/crc32c_sse42_check.h"
336+
"src/crc32c_sse42_clmul.h"
286337
"src/crc32c.cc"
287338

288339
# Only CMake 3.3+ supports PUBLIC sources in targets exported by "install".
@@ -304,6 +355,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
304355
set_property(TARGET crc32c APPEND PROPERTY COMPILE_OPTIONS "/WX")
305356
set_property(TARGET crc32c_arm64 APPEND PROPERTY COMPILE_OPTIONS "/WX")
306357
set_property(TARGET crc32c_sse42 APPEND PROPERTY COMPILE_OPTIONS "/WX")
358+
set_property(TARGET crc32c_sse42_clmul APPEND PROPERTY COMPILE_OPTIONS "/WX")
307359
endif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
308360

309361
if(CRC32C_BUILD_TESTS)

src/crc32c.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@
1212
#include "./crc32c_internal.h"
1313
#include "./crc32c_sse42.h"
1414
#include "./crc32c_sse42_check.h"
15+
#include "./crc32c_sse42_clmul.h"
1516

1617
namespace crc32c {
1718

1819
uint32_t Extend(uint32_t crc, const uint8_t* data, size_t count) {
19-
#if HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
20+
#if HAVE_SSE42 && HAVE_PCLMUL && (defined(_M_X64) || defined(__x86_64__))
21+
static bool can_use_clmul = CanUseClmul();
22+
if (can_use_clmul) return ExtendSse42Clmul(crc, data, count);
23+
static bool can_use_sse42 = CanUseSse42();
24+
if (can_use_sse42) return ExtendSse42(crc, data, count);
25+
#elif HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
2026
static bool can_use_sse42 = CanUseSse42();
2127
if (can_use_sse42) return ExtendSse42(crc, data, count);
2228
#elif HAVE_ARM64_CRC32C
2329
static bool can_use_arm64_crc32 = CanUseArm64Crc32();
2430
if (can_use_arm64_crc32) return ExtendArm64(crc, data, count);
25-
#endif // HAVE_SSE42 && (defined(_M_X64) || defined(__x86_64__))
31+
#endif // HAVE_SSE42 && HAVE_PCLMUL && (defined(_M_X64) || defined(__x86_64__))
2632

2733
return ExtendPortable(crc, data, count);
2834
}

src/crc32c_config.h.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
// intrinsics.
1919
#cmakedefine01 HAVE_SSE42
2020

21+
// Define to 1 if targeting X86 and the compiler has the _mm_clmulepi64_si128
22+
// intrinsic (PCLMULQDQ instruction).
23+
#cmakedefine01 HAVE_PCLMUL
24+
2125
// Define to 1 if targeting ARM and the compiler has the __crc32c{b,h,w,d} and
2226
// the vmull_p64 intrinsics.
2327
#cmakedefine01 HAVE_ARM64_CRC32C

src/crc32c_sse42_check.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ inline bool CanUseSse42() {
2727
return (cpu_info[2] & (1 << 20)) != 0;
2828
}
2929

30+
inline bool CanUseClmul() {
31+
int cpu_info[4];
32+
__cpuid(cpu_info, 1);
33+
return (cpu_info[2] & (1 << 1)) != 0;
34+
}
35+
3036
} // namespace crc32c
3137

3238
#else // !defined(_MSC_VER)
@@ -39,6 +45,11 @@ inline bool CanUseSse42() {
3945
return __get_cpuid(1, &eax, &ebx, &ecx, &edx) && ((ecx & (1 << 20)) != 0);
4046
}
4147

48+
inline bool CanUseClmul() {
49+
unsigned int eax, ebx, ecx, edx;
50+
return __get_cpuid(1, &eax, &ebx, &ecx, &edx) && ((ecx & (1 << 1)) != 0);
51+
}
52+
4253
} // namespace crc32c
4354

4455
#endif // defined(_MSC_VER)

0 commit comments

Comments
 (0)