]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Add LoongArch64 (LSX) adler32, adler32_fold_copy implementation
authorVladislav Shchapov <vladislav@shchapov.ru>
Sat, 14 Jun 2025 12:04:23 +0000 (17:04 +0500)
committerHans Kristian Rosbach <hk-github@circlestorm.org>
Fri, 11 Jul 2025 14:12:18 +0000 (16:12 +0200)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
CMakeLists.txt
arch/loongarch/Makefile.in
arch/loongarch/adler32_lsx.c [new file with mode: 0644]
arch/loongarch/loongarch_functions.h
arch/loongarch/lsxintrin_ext.h
configure
functable.c
test/benchmarks/benchmark_adler32.cc
test/benchmarks/benchmark_adler32_copy.cc
test/test_adler32.cc

index 2ed57cd625f51fb71c15e8d0a1e45fae3e550549..c83cf42d7d801de8c7323ca411505e5e7496cc41 100644 (file)
@@ -1037,7 +1037,7 @@ if(WITH_OPTIM)
             check_lsx_intrinsics()
             if(HAVE_LSX_INTRIN)
                 add_definitions(-DLOONGARCH_LSX)
-                set(LSX_SRCS ${ARCHDIR}/chunkset_lsx.c ${ARCHDIR}/compare256_lsx.c ${ARCHDIR}/slide_hash_lsx.c)
+                set(LSX_SRCS ${ARCHDIR}/adler32_lsx.c ${ARCHDIR}/chunkset_lsx.c ${ARCHDIR}/compare256_lsx.c ${ARCHDIR}/slide_hash_lsx.c)
                 list(APPEND ZLIB_ARCH_SRCS ${LSX_SRCS})
                 set_property(SOURCE ${LSX_SRCS} PROPERTY COMPILE_FLAGS "${LSXFLAG} ${NOLTOFLAG}")
             else()
index 36988f6055fcef78d24ba76d4d3082b28ea8b427..424340f5e25b62476201770056c125c529622959 100644 (file)
@@ -20,6 +20,7 @@ TOPDIR=$(SRCTOP)
 all: \
        loongarch_features.o loongarch_features.lo \
        crc32_la.o crc32_la.lo \
+       adler32_lsx.o adler32_lsx.lo \
        chunkset_lasx.o chunkset_lasx.lo \
        chunkset_lsx.o chunkset_lsx.lo \
        compare256_lasx.o compare256_lasx.lo \
@@ -39,6 +40,12 @@ crc32_la.o: $(SRCDIR)/crc32_la.c
 crc32_la.lo: $(SRCDIR)/crc32_la.c
        $(CC) $(SFLAGS) -DPIC $(INCLUDES) -c -o $@ $(SRCDIR)/crc32_la.c
 
+adler32_lsx.o:
+       $(CC) $(CFLAGS) $(LSXFLAG) $(NOLTOFLAG) $(INCLUDES) -c -o $@ $(SRCDIR)/adler32_lsx.c
+
+adler32_lsx.lo:
+       $(CC) $(SFLAGS) $(LSXFLAG) $(NOLTOFLAG) -DPIC $(INCLUDES) -c -o $@ $(SRCDIR)/adler32_lsx.c
+
 chunkset_lasx.o:
        $(CC) $(CFLAGS) $(LASXFLAG) $(NOLTOFLAG) $(INCLUDES) -c -o $@ $(SRCDIR)/chunkset_lasx.c
 
diff --git a/arch/loongarch/adler32_lsx.c b/arch/loongarch/adler32_lsx.c
new file mode 100644 (file)
index 0000000..7f43262
--- /dev/null
@@ -0,0 +1,156 @@
+/* adler32_lsx.c -- compute the Adler-32 checksum of a data stream, based on Intel SSE4.2 implementation
+ * Copyright (C) 1995-2011 Mark Adler
+ * Copyright (C) 2025 Vladislav Shchapov <vladislav@shchapov.ru>
+ * Authors:
+ *   Adam Stylinski <kungfujesus06@gmail.com>
+ *   Brian Bockelman <bockelman@gmail.com>
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zbuild.h"
+#include "adler32_p.h"
+
+#ifdef LOONGARCH_LSX
+
+#include <lsxintrin.h>
+#include "lsxintrin_ext.h"
+
+static inline uint32_t partial_hsum(__m128i x) {
+    __m128i second_int = __lsx_vbsrl_v(x, 8);
+    __m128i sum = __lsx_vadd_w(x, second_int);
+    return __lsx_vpickve2gr_w(sum, 0);
+}
+
+static inline uint32_t hsum(__m128i x) {
+    __m128i sum1 = __lsx_vilvh_d(x, x);
+    __m128i sum2 = __lsx_vadd_w(x, sum1);
+    __m128i sum3 = __lsx_vshuf4i_w(sum2, 0x01);
+    __m128i sum4 = __lsx_vadd_w(sum2, sum3);
+    return __lsx_vpickve2gr_w(sum4, 0);
+}
+
+static inline uint32_t adler32_fold_copy_impl(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len, const int COPY) {
+    if (src == NULL) return 1L;
+    if (len == 0) return adler;
+
+    uint32_t adler0, adler1;
+    adler1 = (adler >> 16) & 0xffff;
+    adler0 = adler & 0xffff;
+
+rem_peel:
+    if (len < 16) {
+        if (COPY) {
+            return adler32_copy_len_16(adler0, src, dst, len, adler1);
+        } else {
+            return adler32_len_16(adler0, src, len, adler1);
+        }
+    }
+
+    __m128i vbuf, vbuf_0;
+    __m128i vs1_0, vs3, vs1, vs2, vs2_0, v_sad_sum1, v_short_sum2, v_short_sum2_0,
+            v_sad_sum2, vsum2, vsum2_0;
+    __m128i zero = __lsx_vldi(0);
+    const __m128i dot2v = (__m128i)((v16i8){ 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17 });
+    const __m128i dot2v_0 = (__m128i)((v16i8){ 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 });
+    const __m128i dot3v = __lsx_vreplgr2vr_h(1);
+    size_t k;
+
+    while (len >= 16) {
+
+        k = MIN(len, NMAX);
+        k -= k % 16;
+        len -= k;
+
+        vs1 = __lsx_vinsgr2vr_w(zero, adler0, 0);
+        vs2 = __lsx_vinsgr2vr_w(zero, adler1, 0);
+
+        vs3 = __lsx_vldi(0);
+        vs2_0 = __lsx_vldi(0);
+        vs1_0 = vs1;
+
+        while (k >= 32) {
+            /*
+               vs1 = adler + sum(c[i])
+               vs2 = sum2 + 16 vs1 + sum( (16-i+1) c[i] )
+            */
+            vbuf = __lsx_vld(src, 0);
+            vbuf_0 = __lsx_vld(src, 16);
+            src += 32;
+            k -= 32;
+
+            v_sad_sum1 = lsx_sad_bu(vbuf, zero);
+            v_sad_sum2 = lsx_sad_bu(vbuf_0, zero);
+
+            if (COPY) {
+                __lsx_vst(vbuf, dst, 0);
+                __lsx_vst(vbuf_0, dst, 16);
+                dst += 32;
+            }
+
+            v_short_sum2 = __lsx_vsadd_h(__lsx_vmulwev_h_bu_b(vbuf, dot2v), __lsx_vmulwod_h_bu_b(vbuf, dot2v));
+            v_short_sum2_0 = __lsx_vsadd_h(__lsx_vmulwev_h_bu_b(vbuf_0, dot2v_0), __lsx_vmulwod_h_bu_b(vbuf_0, dot2v_0));
+
+            vs1 = __lsx_vadd_w(v_sad_sum1, vs1);
+            vs3 = __lsx_vadd_w(vs1_0, vs3);
+
+            vsum2 = __lsx_vmaddwod_w_h(__lsx_vmulwev_w_h(v_short_sum2, dot3v), v_short_sum2, dot3v);
+            vsum2_0 = __lsx_vmaddwod_w_h(__lsx_vmulwev_w_h(v_short_sum2_0, dot3v), v_short_sum2_0, dot3v);
+            vs1 = __lsx_vadd_w(v_sad_sum2, vs1);
+            vs2 = __lsx_vadd_w(vsum2, vs2);
+            vs2_0 = __lsx_vadd_w(vsum2_0, vs2_0);
+            vs1_0 = vs1;
+        }
+
+        vs2 = __lsx_vadd_w(vs2_0, vs2);
+        vs3 = __lsx_vslli_w(vs3, 5);
+        vs2 = __lsx_vadd_w(vs3, vs2);
+        vs3 = __lsx_vldi(0);
+
+        while (k >= 16) {
+            /*
+               vs1 = adler + sum(c[i])
+               vs2 = sum2 + 16 vs1 + sum( (16-i+1) c[i] )
+            */
+            vbuf = __lsx_vld(src, 0);
+            src += 16;
+            k -= 16;
+
+            v_sad_sum1 = lsx_sad_bu(vbuf, zero);
+            v_short_sum2 = __lsx_vsadd_h(__lsx_vmulwev_h_bu_b(vbuf, dot2v_0), __lsx_vmulwod_h_bu_b(vbuf, dot2v_0));
+
+            vs1 = __lsx_vadd_w(v_sad_sum1, vs1);
+            vs3 = __lsx_vadd_w(vs1_0, vs3);
+            vsum2 = __lsx_vmaddwod_w_h(__lsx_vmulwev_w_h(v_short_sum2, dot3v), v_short_sum2, dot3v);
+            vs2 = __lsx_vadd_w(vsum2, vs2);
+            vs1_0 = vs1;
+
+            if (COPY) {
+                __lsx_vst(vbuf, dst, 0);
+                dst += 16;
+            }
+        }
+
+        vs3 = __lsx_vslli_w(vs3, 4);
+        vs2 = __lsx_vadd_w(vs2, vs3);
+
+        adler0 = partial_hsum(vs1) % BASE;
+        adler1 = hsum(vs2) % BASE;
+    }
+
+    /* If this is true, there's fewer than 16 elements remaining */
+    if (len) {
+        goto rem_peel;
+    }
+
+    return adler0 | (adler1 << 16);
+}
+
+Z_INTERNAL uint32_t adler32_lsx(uint32_t adler, const uint8_t *src, size_t len) {
+    return adler32_fold_copy_impl(adler, NULL, src, len, 0);
+}
+
+Z_INTERNAL uint32_t adler32_fold_copy_lsx(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len) {
+    return adler32_fold_copy_impl(adler, dst, src, len, 1);
+}
+
+#endif
index c70d6c137398e3a651ab42a4807ac64427027039..fa1886196c72782479a4e4d85963f960e78d0de8 100644 (file)
@@ -15,6 +15,8 @@ void     crc32_fold_loongarch64(crc32_fold *crc, const uint8_t *src, size_t len,
 #endif
 
 #ifdef LOONGARCH_LSX
+uint32_t adler32_lsx(uint32_t adler, const uint8_t *src, size_t len);
+uint32_t adler32_fold_copy_lsx(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len);
 void slide_hash_lsx(deflate_state *s);
 #  ifdef HAVE_BUILTIN_CTZ
     uint32_t compare256_lsx(const uint8_t *src0, const uint8_t *src1);
@@ -49,6 +51,10 @@ void inflate_fast_lasx(PREFIX3(stream) *strm, uint32_t start);
 #    define native_crc32_fold_copy crc32_fold_copy_loongarch64
 #  endif
 #  if defined(LOONGARCH_LSX) && defined(__loongarch_sx)
+#    undef native_adler32
+#    define native_adler32 adler32_lsx
+#    undef native_adler32_fold_copy
+#    define native_adler32_fold_copy adler32_fold_copy_lsx
 #    undef native_slide_hash
 #    define native_slide_hash slide_hash_lsx
 #    undef native_chunksize
index c89105e0507a9233a84300348abda3d2aff66f8b..0a0503b9f988493e36ffe2986a1a8ccaf1e42e8a 100644 (file)
@@ -8,6 +8,13 @@
 #include <lsxintrin.h>
 
 
+static inline __m128i lsx_sad_bu(__m128i a, __m128i b) {
+    __m128i tmp = __lsx_vabsd_bu(a, b);
+    tmp = __lsx_vhaddw_hu_bu(tmp, tmp);
+    tmp = __lsx_vhaddw_wu_hu(tmp, tmp);
+    return __lsx_vhaddw_du_wu(tmp, tmp);
+}
+
 static inline int lsx_movemask_b(__m128i v) {
     return __lsx_vpickve2gr_w(__lsx_vmskltz_b(v), 0);
 }
index 80fd5538319637ddd6f8f7c064778864b7da2df8..e37859bb838c9500bcf658a9906fe32ca4db68c0 100755 (executable)
--- a/configure
+++ b/configure
@@ -2316,8 +2316,8 @@ EOF
                 CFLAGS="${CFLAGS} -DLOONGARCH_LSX"
                 SFLAGS="${SFLAGS} -DLOONGARCH_LSX"
 
-                ARCH_STATIC_OBJS="${ARCH_STATIC_OBJS} chunkset_lsx.o compare256_lsx.o slide_hash_lsx.o"
-                ARCH_SHARED_OBJS="${ARCH_SHARED_OBJS} chunkset_lsx.lo compare256_lsx.lo slide_hash_lsx.lo"
+                ARCH_STATIC_OBJS="${ARCH_STATIC_OBJS} adler32_lsx.o chunkset_lsx.o compare256_lsx.o slide_hash_lsx.o"
+                ARCH_SHARED_OBJS="${ARCH_SHARED_OBJS} adler32_lsx.lo chunkset_lsx.lo compare256_lsx.lo slide_hash_lsx.lo"
             fi
 
             check_lasx_intrinsics
index 02bd7d3fe094b63bab574672663bcf2270c28fae..f31138b11fc24eb6ed09fea8a2a10fe0e10df68e 100644 (file)
@@ -279,6 +279,8 @@ static void init_functable(void) {
 #endif
 #ifdef LOONGARCH_LSX
     if (cf.loongarch.has_lsx) {
+        ft.adler32 = &adler32_lsx;
+        ft.adler32_fold_copy = &adler32_fold_copy_lsx;
         ft.slide_hash = slide_hash_lsx;
 #  ifdef HAVE_BUILTIN_CTZ
         ft.compare256 = &compare256_lsx;
index b1278950d0766bcf48cefb65ff5078c2701296ca..ee36a8096b8a98ecc617507548511bd0e2770fa3 100644 (file)
@@ -97,4 +97,8 @@ BENCHMARK_ADLER32(avx512, adler32_avx512, test_cpu_features.x86.has_avx512_commo
 BENCHMARK_ADLER32(avx512_vnni, adler32_avx512_vnni, test_cpu_features.x86.has_avx512vnni);
 #endif
 
+#ifdef LOONGARCH_LSX
+BENCHMARK_ADLER32(lsx, adler32_lsx, test_cpu_features.loongarch.has_lsx);
+#endif
+
 #endif
index bca8df18a891b9de90722078f159a6794c6b28bf..505bc252a6f53d957767fac0b54e2afcf1f0193a 100644 (file)
@@ -127,4 +127,9 @@ BENCHMARK_ADLER32_BASELINE_COPY(avx512_vnni_baseline, adler32_avx512_vnni, test_
 BENCHMARK_ADLER32_COPY(avx512_vnni, adler32_fold_copy_avx512_vnni, test_cpu_features.x86.has_avx512vnni);
 #endif
 
+#ifdef LOONGARCH_LSX
+BENCHMARK_ADLER32_BASELINE_COPY(lsx_baseline, adler32_lsx, test_cpu_features.loongarch.has_lsx);
+BENCHMARK_ADLER32_COPY(lsx, adler32_fold_copy_lsx, test_cpu_features.loongarch.has_lsx);
+#endif
+
 #endif
index b3d03021e4be5a096a726ea79dc1ca05cfaf78cd..eb8bccdf5e4876798bc2bc88953cd9e79d9d91a8 100644 (file)
@@ -392,4 +392,8 @@ TEST_ADLER32(avx512, adler32_avx512, test_cpu_features.x86.has_avx512_common)
 TEST_ADLER32(avx512_vnni, adler32_avx512_vnni, test_cpu_features.x86.has_avx512vnni)
 #endif
 
+#ifdef LOONGARCH_LSX
+TEST_ADLER32(lsx, adler32_lsx, test_cpu_features.loongarch.has_lsx)
+#endif
+
 #endif