Hi Interesting, I wonder if maybe this could be applied to gnulib's crc module? And then gzip use that instead of the current custom code? There are other arch-specific optimizations in gnulib's crc. I did not study gzip's use of CRC, more investigation is needed because ancient CRC usage seems to vary quite a bit.
/Simon <[email protected]> writes: > Hi, > > This patch adds RISC-V CRC32 acceleration for gzip on RV64 systems with > Zbc, and enables a Zvbc-based vector folding path when vector crypto > support is available. > > On a C2044 system, compared with the unmodified gzip baseline, this > patch improves compression time by 4.21% at -1, 7.35% at -6, and 7.50% > at -9. Decompression time improves by 11.39% at -1, 7.88% at -6, and > 10.18% at -9. > > The patch is attached below. Comments and feedback are welcome. > > Best regards, > Shangcheng Huang > > > > > > 黄尚诚10330306 > > 应用软件开发 > RCH六部/无线及算力硬件研发中心/无线产品经营部 > 中兴通讯股份有限公司 > 深圳市西丽中兴工业园 邮编: 518057 > T: +86 755 xxxxxxxx M: +86 13922373447 > E: [email protected] > www.zte.com.cn > > From 9e795e9733fa2a2b262ba7dd529a08049b012daa Mon Sep 17 00:00:00 2001 > From: Shangcheng Huang <[email protected]> > Date: Fri, 18 Sep 2026 16:43:05 +0800 > Subject: [PATCH] gzip: add RISC-V Zbc/Zvbc CRC32 acceleration > > Add RISC-V CRC32 update paths using Zbc carry-less multiply on RV64, > with a Zvbc vector folding path when vector crypto support is available. > > On a C2044 system, compared with the unmodified gzip baseline, this > improves compression time by 4.21% at -1, 7.35% at -6, and 7.50% at -9. > Decompression time improves by 11.39% at -1, 7.88% at -6, and 10.18% > at -9. > > Signed-off-by: Shangcheng Huang <[email protected]> > --- > diff --git a/util.c b/util.c > index > 80c92d11873a5e39050d3e78fe74972c66de900c..4eab54963d7793c4f5012256264892dd6e209247 > 100644 > --- a/util.c > +++ b/util.c > @@ -67,6 +67,269 @@ copy (int in, int out) > return OK; > } > > +#if defined(__riscv_zbc) && (__riscv_xlen == 64) > +#include <stdint.h> > +# if defined(__riscv_vector) && defined(__riscv_zvbc) > +# include <riscv_vector.h> > +# endif > + > +static inline uint64_t > +rv_clmul(uint64_t a, uint64_t b) > +{ > + uint64_t r; > + __asm__ volatile( > + ".option push\n\t" > + ".option arch, +zbc\n\t" > + "clmul %0, %1, %2\n\t" > + ".option pop\n\t" > + : "=r"(r) : "r"(a), "r"(b)); > + return r; > +} > + > +static inline uint64_t > +rv_clmulh(uint64_t a, uint64_t b) > +{ > + uint64_t r; > + __asm__ volatile( > + ".option push\n\t" > + ".option arch, +zbc\n\t" > + "clmulh %0, %1, %2\n\t" > + ".option pop\n\t" > + : "=r"(r) : "r"(a), "r"(b)); > + return r; > +} > + > +static inline uint32_t > +rv_crc32_bytewise(uint32_t c, const uint8_t *p, size_t len) > +{ > + while (len > 0) { > + c ^= *p++; > + for (int i = 0; i < 8; i++) > + c = (c >> 1) ^ ((c & 1) ? 0xEDB88320U : 0); > + len--; > + } > + > + return c; > +} > + > +static inline uint32_t > +rv_crc32_fold8_zbc(uint32_t c, uint64_t val) > +{ > + uint64_t const1 = 0xb4e5b025f7011641ULL; > + uint64_t const2 = 0x00000000edb88320ULL; > + uint64_t q; > + uint64_t res; > + > + val ^= c; > + q = rv_clmul(val, const1); > + res = val ^ rv_clmulh(q, const2); > + return (uint32_t) (res >> 32); > +} > + > +static uint32_t rv_crc32_zbc(uint32_t crc, const uint8_t *p, size_t len); > + > +# if defined(__riscv_vector) && defined(__riscv_zvbc) > +typedef struct > +{ > + uint64_t lo; > + uint64_t hi; > +} rv_u128_fold_t; > + > +static inline uint64_t > +rv_vclmul(uint64_t a, uint64_t b) > +{ > + uint64_t in[1] = { a }; > + uint64_t out[1]; > + size_t vl = __riscv_vsetvl_e64m1(1); > + vuint64m1_t va = __riscv_vle64_v_u64m1(in, vl); > + vuint64m1_t vr = __riscv_vclmul_vx_u64m1(va, b, vl); > + > + __riscv_vse64_v_u64m1(out, vr, vl); > + return out[0]; > +} > + > +static inline uint64_t > +rv_vclmulh(uint64_t a, uint64_t b) > +{ > + uint64_t in[1] = { a }; > + uint64_t out[1]; > + size_t vl = __riscv_vsetvl_e64m1(1); > + vuint64m1_t va = __riscv_vle64_v_u64m1(in, vl); > + vuint64m1_t vr = __riscv_vclmulh_vx_u64m1(va, b, vl); > + > + __riscv_vse64_v_u64m1(out, vr, vl); > + return out[0]; > +} > + > +static inline rv_u128_fold_t > +rv_u128_load(const uint8_t *p) > +{ > + rv_u128_fold_t v; > + > + memcpy(&v, p, sizeof(v)); > + return v; > +} > + > +static inline rv_u128_fold_t > +rv_u128_xor(rv_u128_fold_t a, rv_u128_fold_t b) > +{ > + a.lo ^= b.lo; > + a.hi ^= b.hi; > + return a; > +} > + > +static inline unsigned __int128 > +rv_u128_pack(rv_u128_fold_t v) > +{ > + return ((unsigned __int128) v.hi << 64) | v.lo; > +} > + > +static inline rv_u128_fold_t > +rv_u128_clmul_scalar_zvbc(uint64_t a, uint64_t b) > +{ > + rv_u128_fold_t r; > + > + r.lo = rv_vclmul(a, b); > + r.hi = rv_vclmulh(a, b); > + return r; > +} > + > +static inline rv_u128_fold_t > +rv_u128_clmul_fold_zvbc(rv_u128_fold_t x, uint64_t k_lo, uint64_t k_hi) > +{ > + uint64_t vals[2] = { x.lo, x.hi }; > + uint64_t keys[2] = { k_lo, k_hi }; > + uint64_t prod_lo[2]; > + uint64_t prod_hi[2]; > + size_t vl = __riscv_vsetvl_e64m1(2); > + vuint64m1_t vx = __riscv_vle64_v_u64m1(vals, vl); > + vuint64m1_t vk = __riscv_vle64_v_u64m1(keys, vl); > + vuint64m1_t vlo = __riscv_vclmul_vv_u64m1(vx, vk, vl); > + vuint64m1_t vhi = __riscv_vclmulh_vv_u64m1(vx, vk, vl); > + rv_u128_fold_t r; > + > + __riscv_vse64_v_u64m1(prod_lo, vlo, vl); > + __riscv_vse64_v_u64m1(prod_hi, vhi, vl); > + > + r.lo = prod_lo[0] ^ prod_lo[1]; > + r.hi = prod_hi[0] ^ prod_hi[1]; > + return r; > +} > + > +static inline rv_u128_fold_t > +rv_crc32_fold_128_zvbc(rv_u128_fold_t x, uint64_t k_lo, uint64_t k_hi, > + rv_u128_fold_t data) > +{ > + return rv_u128_xor(rv_u128_clmul_fold_zvbc(x, k_lo, k_hi), data); > +} > + > +static inline uint32_t > +rv_crc32_reduce_128_zvbc(rv_u128_fold_t x) > +{ > + const uint64_t k4 = 0x00ccaa009eULL; > + const uint64_t k5 = 0x0163cd6124ULL; > + const uint64_t poly0 = 0x01db710641ULL; > + const uint64_t poly1 = 0x01f7011641ULL; > + const unsigned __int128 mask = > + (unsigned __int128) 0xFFFFFFFFU > + | ((unsigned __int128) 0xFFFFFFFFU << 64); > + unsigned __int128 acc; > + unsigned __int128 tmp; > + > + /* Match the ISA-L / Chromium reflected-CRC pipeline: > + 128b folding, then Barrett reduction to 32 bits. */ > + acc = ((unsigned __int128) x.hi) > + ^ rv_u128_pack(rv_u128_clmul_scalar_zvbc(x.lo, k4)); > + > + tmp = acc >> 32; > + acc &= mask; > + acc = rv_u128_pack(rv_u128_clmul_scalar_zvbc((uint64_t) acc, k5)) ^ tmp; > + > + tmp = acc & mask; > + tmp = rv_u128_pack(rv_u128_clmul_scalar_zvbc((uint64_t) tmp, poly1)) & > mask; > + acc ^= rv_u128_pack(rv_u128_clmul_scalar_zvbc((uint64_t) tmp, poly0)); > + > + return (uint32_t) (((uint64_t) acc) >> 32); > +} > + > +static uint32_t > +rv_crc32_zvbc(uint32_t crc, const uint8_t *p, size_t len) > +{ > + const uint64_t k1 = 0x0154442bd4ULL; > + const uint64_t k2 = 0x01c6e41596ULL; > + const uint64_t k3 = 0x01751997d0ULL; > + const uint64_t k4 = 0x00ccaa009eULL; > + size_t vl = __riscv_vsetvl_e64m1(2); > + uint32_t c; > + rv_u128_fold_t x1; > + rv_u128_fold_t x2; > + rv_u128_fold_t x3; > + rv_u128_fold_t x4; > + > + if (vl < 2 || len < 64) > + return rv_crc32_zbc(crc, p, len); > + > + c = crc ^ 0xFFFFFFFFU; > + x1 = rv_u128_load(p); > + x1.lo ^= c; > + x2 = rv_u128_load(p + 16); > + x3 = rv_u128_load(p + 32); > + x4 = rv_u128_load(p + 48); > + p += 64; > + len -= 64; > + > + while (len >= 64) { > + x1 = rv_crc32_fold_128_zvbc(x1, k1, k2, rv_u128_load(p)); > + x2 = rv_crc32_fold_128_zvbc(x2, k1, k2, rv_u128_load(p + 16)); > + x3 = rv_crc32_fold_128_zvbc(x3, k1, k2, rv_u128_load(p + 32)); > + x4 = rv_crc32_fold_128_zvbc(x4, k1, k2, rv_u128_load(p + 48)); > + p += 64; > + len -= 64; > + } > + > + x1 = rv_crc32_fold_128_zvbc(x1, k3, k4, x2); > + x1 = rv_crc32_fold_128_zvbc(x1, k3, k4, x3); > + x1 = rv_crc32_fold_128_zvbc(x1, k3, k4, x4); > + > + while (len >= 16) { > + x1 = rv_crc32_fold_128_zvbc(x1, k3, k4, rv_u128_load(p)); > + p += 16; > + len -= 16; > + } > + > + c = rv_crc32_reduce_128_zvbc(x1) ^ 0xFFFFFFFFU; > + c = rv_crc32_bytewise(c, p, len); > + > + return c; > +} > +# endif > + > +static uint32_t > +rv_crc32_zbc(uint32_t crc, const uint8_t *p, size_t len) > +{ > + uint32_t c = crc ^ 0xFFFFFFFFU; > + > + while (len > 0 && ((uintptr_t)p & 7)) { > + c = rv_crc32_bytewise(c, p, 1); > + p++; > + len--; > + } > + > + while (len >= 8) { > + uint64_t val; > + > + memcpy(&val, p, sizeof(val)); > + c = rv_crc32_fold8_zbc(c, val); > + p += 8; > + len -= 8; > + } > + > + c = rv_crc32_bytewise(c, p, len); > + > + return c ^ 0xFFFFFFFFU; > +} > +#endif > + > /* > =========================================================================== > * Run a set of bytes through the crc shift register. If s is a NULL > * pointer, then initialize the crc shift register contents instead. > @@ -76,7 +339,18 @@ copy (int in, int out) > ulg > updcrc (uch const *s, unsigned n) > { > - crc = (s == NULL ? 0 : crc32_update (crc, (const char *) s, n)); > + if (s == NULL) { > + crc = 0; > + } else { > +#if defined(__riscv_vector) && defined(__riscv_zvbc) \ > + && defined(__riscv_zbc) && (__riscv_xlen == 64) > + crc = rv_crc32_zvbc(crc, (const uint8_t *)s, n); > +#elif defined(__riscv_zbc) && (__riscv_xlen == 64) > + crc = rv_crc32_zbc(crc, (const uint8_t *)s, n); > +#else > + crc = crc32_update (crc, (const char *) s, n); > +#endif > + } > return crc; > } > >
signature.asc
Description: PGP signature
