diff -Nru old/config/c-compiler.m4 new/config/c-compiler.m4 --- a/config/c-compiler.m4 2026-11-02 16:38:41.810155094 +0800 +++ b/config/c-compiler.m4 2026-11-02 16:38:41.814155136 +0800 @@ -662,6 +662,39 @@ undefine([Ac_cachevar])dnl ])# PGAC_ARMV8_CRC32C_INTRINSICS +# PGAC_RISCV_ZBC_CRC32C_INTRINSICS +# ---------------------------- +# Check if the compiler supports the CRC32C computation using the +# __builtin_rev_crc32_data8, __builtin_rev_crc32_data16 and +# __builtin_rev_crc32_data32 intrinsic functions. These expand to +# clmul-based code sequences on targets with the RISC-V Zbc +# carry-less multiplication extension. +# +# An optional compiler flag can be passed as argument (e.g. +# -march=rv64gc_zbc). If the intrinsics are supported, sets +# pgac_riscv_zbc_crc32c_intrinsics, and CFLAGS_CRC. +AC_DEFUN([PGAC_RISCV_ZBC_CRC32C_INTRINSICS], +[define([Ac_cachevar], [AS_TR_SH([pgac_cv_riscv_zbc_crc32c_intrinsics_$1])])dnl +AC_CACHE_CHECK([for __builtin_rev_crc32_data8, __builtin_rev_crc32_data16, and __builtin_rev_crc32_data32 with CFLAGS=$1], [Ac_cachevar], +[pgac_save_CFLAGS=$CFLAGS +CFLAGS="$pgac_save_CFLAGS $1" +AC_LINK_IFELSE([AC_LANG_PROGRAM([], + [unsigned int crc = 0; + crc = __builtin_rev_crc32_data8(crc, 0, 0x1EDC6F41); + crc = __builtin_rev_crc32_data16(crc, 0, 0x1EDC6F41); + crc = __builtin_rev_crc32_data32(crc, 0, 0x1EDC6F41); + /* return computed value, to prevent the above being optimized away */ + return crc == 0;])], + [Ac_cachevar=yes], + [Ac_cachevar=no]) +CFLAGS="$pgac_save_CFLAGS"]) +if test x"$Ac_cachevar" = x"yes"; then + CFLAGS_CRC="$1" + pgac_riscv_zbc_crc32c_intrinsics=yes +fi +undefine([Ac_cachevar])dnl +])# PGAC_RISCV_ZBC_CRC32C_INTRINSICS + # PGAC_LOONGARCH_CRC32C_INTRINSICS # --------------------------- # Check if the compiler supports the LoongArch CRCC instructions, using diff -Nru old/configure.ac new/configure.ac --- a/configure.ac 2026-11-02 16:38:41.814155136 +0800 +++ b/configure.ac 2026-11-02 16:38:41.814155136 +0800 @@ -2154,6 +2154,16 @@ # with the default compiler flags. PGAC_LOONGARCH_CRC32C_INTRINSICS() +# Check for RISC-V Zbc intrinsics to do CRC calculations. +# +# First check if __builtin_rev_crc32_data* intrinsics can be used with the +# default compiler flags. If not, check if adding "-march=rv64gc_zbc" flag +# helps. CFLAGS_CRC is set if the extra flag is required. +PGAC_RISCV_ZBC_CRC32C_INTRINSICS([]) +if test x"$pgac_riscv_zbc_crc32c_intrinsics" != x"yes"; then + PGAC_RISCV_ZBC_CRC32C_INTRINSICS([-march=rv64gc_zbc]) +fi + AC_SUBST(CFLAGS_CRC) # Select CRC-32C implementation. @@ -2175,7 +2185,7 @@ # # If we are targeting a LoongArch processor, CRC instructions are # always available (at least on 64 bit), so no runtime check is needed. -if test x"$USE_SLICING_BY_8_CRC32C" = x"" && test x"$USE_SSE42_CRC32C" = x"" && test x"$USE_SSE42_CRC32C_WITH_RUNTIME_CHECK" = x"" && test x"$USE_ARMV8_CRC32C" = x"" && test x"$USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK" = x"" && test x"$USE_LOONGARCH_CRC32C" = x""; then +if test x"$USE_SLICING_BY_8_CRC32C" = x"" && test x"$USE_SSE42_CRC32C" = x"" && test x"$USE_SSE42_CRC32C_WITH_RUNTIME_CHECK" = x"" && test x"$USE_ARMV8_CRC32C" = x"" && test x"$USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK" = x"" && test x"$USE_LOONGARCH_CRC32C" = x"" && test x"$USE_ZBC_CRC32C_WITH_RUNTIME_CHECK" = x""; then # Use Intel SSE 4.2 if available. if test x"$pgac_sse42_crc32_intrinsics" = x"yes" && test x"$SSE4_2_TARGETED" = x"1" ; then USE_SSE42_CRC32C=1 @@ -2197,9 +2207,14 @@ if test x"$pgac_loongarch_crc32c_intrinsics" = x"yes"; then USE_LOONGARCH_CRC32C=1 else - # fall back to slicing-by-8 algorithm, which doesn't require any - # special CPU support. - USE_SLICING_BY_8_CRC32C=1 + # RISC-V Zbc, with runtime check? + if test x"$pgac_riscv_zbc_crc32c_intrinsics" = x"yes"; then + USE_ZBC_CRC32C_WITH_RUNTIME_CHECK=1 + else + # fall back to slicing-by-8 algorithm, which doesn't require any + # special CPU support. + USE_SLICING_BY_8_CRC32C=1 + fi fi fi fi @@ -2234,9 +2249,15 @@ PG_CRC32C_OBJS="pg_crc32c_loongarch.o" AC_MSG_RESULT(LoongArch CRCC instructions) else - AC_DEFINE(USE_SLICING_BY_8_CRC32C, 1, [Define to 1 to use software CRC-32C implementation (slicing-by-8).]) - PG_CRC32C_OBJS="pg_crc32c_sb8.o" - AC_MSG_RESULT(slicing-by-8) + if test x"$USE_ZBC_CRC32C_WITH_RUNTIME_CHECK" = x"1"; then + AC_DEFINE(USE_ZBC_CRC32C_WITH_RUNTIME_CHECK, 1, [Define to 1 to use RISC-V Zbc clmul instructions with a runtime check.]) + PG_CRC32C_OBJS="pg_crc32c_zbc.o pg_crc32c_sb8.o pg_crc32c_zbc_choose.o" + AC_MSG_RESULT(RISC-V Zbc clmul instructions with runtime check) + else + AC_DEFINE(USE_SLICING_BY_8_CRC32C, 1, [Define to 1 to use software CRC-32C implementation (slicing-by-8).]) + PG_CRC32C_OBJS="pg_crc32c_sb8.o" + AC_MSG_RESULT(slicing-by-8) + fi fi fi fi diff -Nru old/src/include/pg_config.h.in new/src/include/pg_config.h.in --- a/src/include/pg_config.h.in 2026-11-02 16:38:41.814155136 +0800 +++ b/src/include/pg_config.h.in 2026-11-02 16:38:41.818155178 +0800 @@ -756,6 +756,9 @@ /* Define to select Win32-style shared memory. */ #undef USE_WIN32_SHARED_MEMORY +/* Define to 1 to use RISC-V Zbc clmul instructions with a runtime check. */ +#undef USE_ZBC_CRC32C_WITH_RUNTIME_CHECK + /* Define to 1 to build with ZSTD support. (--with-zstd) */ #undef USE_ZSTD diff -Nru old/src/include/port/pg_crc32c.h new/src/include/port/pg_crc32c.h --- a/src/include/port/pg_crc32c.h 2026-11-02 16:38:41.818155178 +0800 +++ b/src/include/port/pg_crc32c.h 2026-11-02 16:38:41.818155178 +0800 @@ -67,7 +67,7 @@ extern pg_crc32c pg_comp_crc32c_loongarch(pg_crc32c crc, const void *data, size_t len); -#elif defined(USE_SSE42_CRC32C_WITH_RUNTIME_CHECK) || defined(USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK) +#elif defined(USE_SSE42_CRC32C_WITH_RUNTIME_CHECK) || defined(USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK) || defined(USE_ZBC_CRC32C_WITH_RUNTIME_CHECK) /* * Use Intel SSE 4.2 or ARMv8 instructions, but perform a runtime check first @@ -86,6 +86,9 @@ #ifdef USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK extern pg_crc32c pg_comp_crc32c_armv8(pg_crc32c crc, const void *data, size_t len); #endif +#ifdef USE_ZBC_CRC32C_WITH_RUNTIME_CHECK +extern pg_crc32c pg_comp_crc32c_zbc(pg_crc32c crc, const void *data, size_t len); +#endif #else /* diff -Nru old/src/port/Makefile new/src/port/Makefile --- a/src/port/Makefile 2026-11-02 16:40:43.675421867 +0800 +++ b/src/port/Makefile 2026-11-02 16:40:43.675421867 +0800 @@ -93,6 +93,11 @@ pg_crc32c_armv8_shlib.o: CFLAGS+=$(CFLAGS_CRC) pg_crc32c_armv8_srv.o: CFLAGS+=$(CFLAGS_CRC) +# all versions of pg_crc32c_zbc.o need CFLAGS_CRC +pg_crc32c_zbc.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_zbc_shlib.o: CFLAGS+=$(CFLAGS_CRC) +pg_crc32c_zbc_srv.o: CFLAGS+=$(CFLAGS_CRC) + # all versions of pg_popcount_avx512_choose.o need CFLAGS_XSAVE pg_popcount_avx512_choose.o: CFLAGS+=$(CFLAGS_XSAVE) pg_popcount_avx512_choose_shlib.o: CFLAGS+=$(CFLAGS_XSAVE) diff -Nru old/src/port/pg_crc32c_zbc.c new/src/port/pg_crc32c_zbc.c --- a/src/port/pg_crc32c_zbc.c 1970-01-01 08:00:00.000000000 +0800 +++ b/src/port/pg_crc32c_zbc.c 2026-11-02 16:40:43.679421909 +0800 @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_zbc.c + * Compute CRC-32C checksum using RISC-V Zbc carry-less multiplication + * instructions + * + * Uses the compiler builtins __builtin_rev_crc32_data{8,16,32}(), which + * expand to clmul/clmulh sequences on targets with the Zbc extension. + * The polynomial argument is the normal (non-reflected) representation of + * the Castagnoli polynomial, 0x1EDC6F41. + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_zbc.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "port/pg_crc32c.h" + +#define CRC32C_POLY 0x1EDC6F41 + +pg_crc32c +pg_comp_crc32c_zbc(pg_crc32c crc, const void *data, size_t len) +{ + const unsigned char *p = data; + const unsigned char *pend = p + len; + + /* + * Process leading bytes one at a time, so that the loop below starts + * with a pointer aligned to eight bytes. + */ + while (p < pend && ((uintptr_t) p & 7)) + { + crc = __builtin_rev_crc32_data8(crc, *p++, CRC32C_POLY); + } + + /* Process eight bytes at a time. */ + while (p + 8 <= pend) + { + crc = __builtin_rev_crc32_data32(crc, ((const uint32_t *) p)[0], + CRC32C_POLY); + crc = __builtin_rev_crc32_data32(crc, ((const uint32_t *) p)[1], + CRC32C_POLY); + p += 8; + } + + /* Process remaining 0-7 bytes. */ + if (p + 4 <= pend) + { + crc = __builtin_rev_crc32_data32(crc, *(const uint32_t *) p, + CRC32C_POLY); + p += 4; + } + if (p + 2 <= pend) + { + crc = __builtin_rev_crc32_data16(crc, *(const uint16 *) p, + CRC32C_POLY); + p += 2; + } + if (p < pend) + { + crc = __builtin_rev_crc32_data8(crc, *p, CRC32C_POLY); + } + + return crc; +} diff -Nru old/src/port/pg_crc32c_zbc_choose.c new/src/port/pg_crc32c_zbc_choose.c --- a/src/port/pg_crc32c_zbc_choose.c 1970-01-01 08:00:00.000000000 +0800 +++ b/src/port/pg_crc32c_zbc_choose.c 2026-11-02 16:40:43.679421909 +0800 @@ -0,0 +1,100 @@ +/*------------------------------------------------------------------------- + * + * pg_crc32c_zbc_choose.c + * Choose between Zbc and software CRC-32C implementation. + * + * On first call, checks if the CPU we're running on supports the Zbc + * carry-less multiplication extension. If it does, use the clmul-based + * implementation for CRC-32C computation. Otherwise, fall back to the pure + * software implementation (slicing-by-8). + * + * The Zbc extension has no bit in the auxiliary vector, so like the ARMv8 + * implementation, we probe by trying to execute the clmul-based code. If + * the instructions are not available, we expect to get SIGILL, which we can + * trap. + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pg_crc32c_zbc_choose.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include + +#include "port/pg_crc32c.h" + + +static sigjmp_buf illegal_instruction_jump; + +/* + * Probe by trying to execute pg_comp_crc32c_zbc(). If the instructions + * aren't available, we expect to get SIGILL, which we can trap. + */ +static void +illegal_instruction_handler(SIGNAL_ARGS) +{ + siglongjmp(illegal_instruction_jump, 1); +} + +static bool +pg_crc32c_zbc_available(void) +{ + uint64 data = 42; + int result; + + /* + * Be careful not to do anything that might throw an error while we have + * the SIGILL handler set to a nonstandard value. + */ + pqsignal(SIGILL, illegal_instruction_handler); + if (sigsetjmp(illegal_instruction_jump, 1) == 0) + { + /* Rather than hard-wiring an expected result, compare to SB8 code */ + result = (pg_comp_crc32c_zbc(0, &data, sizeof(data)) == + pg_comp_crc32c_sb8(0, &data, sizeof(data))); + } + else + { + /* We got the SIGILL trap */ + result = -1; + } + pqsignal(SIGILL, SIG_DFL); + +#ifndef FRONTEND + /* We don't expect this case, so complain loudly */ + if (result == 0) + elog(ERROR, "crc32 hardware and software results disagree"); + + elog(DEBUG1, "using zbc crc32 clmul = %d", (result > 0)); +#endif + + return (result > 0); +} + +/* + * This gets called on the first call. It replaces the function pointer + * so that subsequent calls are routed directly to the chosen implementation. + */ +static pg_crc32c +pg_comp_crc32c_choose(pg_crc32c crc, const void *data, size_t len) +{ + if (pg_crc32c_zbc_available()) + pg_comp_crc32c = pg_comp_crc32c_zbc; + else + pg_comp_crc32c = pg_comp_crc32c_sb8; + + return pg_comp_crc32c(crc, data, len); +} + +pg_crc32c (*pg_comp_crc32c) (pg_crc32c crc, const void *data, size_t len) = pg_comp_crc32c_choose;