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;
>  }
>  
>

Attachment: signature.asc
Description: PGP signature

Reply via email to