mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	This adds two optimizations for ARM:
NEON optimized Adler(-)32 checksum algorithm (ARMv7 and newer NEON CPUs)
ARM(v7+) specific optimization for inflate
I've also connected inflate optimization to the build using the following
source as template.
0397489124 (diff-a62ad2db6c83dbc205d34bb9a8884f16)
Additional info:
https://codereview.chromium.org/2676493007/
https://codereview.chromium.org/2722063002/
Sources:
https://github.com/madler/zlib/pull/251 (only the first commit)
https://github.com/madler/zlib/pull/256
Signed-off-by: Daniel Engberg <daniel.engberg.lists@pyret.net>
		
	
			
		
			
				
	
	
		
			254 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From d2f06cd65d7ac39c6dd6761eef162abc946b155b Mon Sep 17 00:00:00 2001
 | 
						|
From: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
 | 
						|
Date: Tue, 11 Apr 2017 17:13:02 -0700
 | 
						|
Subject: [PATCH] NEON implementation for Adler32
 | 
						|
 | 
						|
The checksum is calculated in the uncompressed PNG data
 | 
						|
and can be made much faster by using SIMD.
 | 
						|
 | 
						|
Tests in ARMv8 yielded an improvement of about 3x
 | 
						|
(e.g. walltime was 350ms x 125ms for a 4096x4096 bytes
 | 
						|
executed 30 times). That results in at least 18% improvement
 | 
						|
in image decoding in Chromium.
 | 
						|
 | 
						|
Further details at:
 | 
						|
https://bugs.chromium.org/p/chromium/issues/detail?id=688601
 | 
						|
---
 | 
						|
 CMakeLists.txt             |  29 +++++++---
 | 
						|
 adler32.c                  |   5 ++
 | 
						|
 contrib/README.contrib     |   3 +
 | 
						|
 contrib/arm/neon_adler32.c | 137 +++++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 4 files changed, 166 insertions(+), 8 deletions(-)
 | 
						|
 create mode 100644 contrib/arm/neon_adler32.c
 | 
						|
 | 
						|
diff --git a/CMakeLists.txt b/CMakeLists.txt
 | 
						|
index 0fe939df..8e75f664 100644
 | 
						|
--- a/CMakeLists.txt
 | 
						|
+++ b/CMakeLists.txt
 | 
						|
@@ -7,6 +7,7 @@ set(VERSION "1.2.11")
 | 
						|
 
 | 
						|
 option(ASM686 "Enable building i686 assembly implementation")
 | 
						|
 option(AMD64 "Enable building amd64 assembly implementation")
 | 
						|
+option(ARMv8 "Enable building ARM NEON intrinsics implementation")
 | 
						|
 
 | 
						|
 set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables")
 | 
						|
 set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries")
 | 
						|
@@ -132,14 +133,26 @@ endif()
 | 
						|
 if(CMAKE_COMPILER_IS_GNUCC)
 | 
						|
     if(ASM686)
 | 
						|
         set(ZLIB_ASMS contrib/asm686/match.S)
 | 
						|
-    elseif (AMD64)
 | 
						|
+    elseif(AMD64)
 | 
						|
         set(ZLIB_ASMS contrib/amd64/amd64-match.S)
 | 
						|
-    endif ()
 | 
						|
+    elseif(ARMv8)
 | 
						|
+        set(ZLIB_ARMv8 contrib/arm/neon_adler32.c)
 | 
						|
+    endif()
 | 
						|
 
 | 
						|
-	if(ZLIB_ASMS)
 | 
						|
-		add_definitions(-DASMV)
 | 
						|
-		set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
 | 
						|
-	endif()
 | 
						|
+    if(ZLIB_ASMS)
 | 
						|
+        add_definitions(-DASMV)
 | 
						|
+	    set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
 | 
						|
+    elseif(ZLIB_ARMv8)
 | 
						|
+        add_definitions(-DARMv8)
 | 
						|
+        set(COMPILER ${CMAKE_C_COMPILER})
 | 
						|
+        # NEON is mandatory in ARMv8.
 | 
						|
+        if(${COMPILER} MATCHES "aarch64")
 | 
						|
+            set_source_files_properties(${ZLIB_ARMv8} PROPERTIES LANGUAGE C COMPILE_FLAGS -march=armv8-a)
 | 
						|
+        # But it was optional for ARMv7.
 | 
						|
+        elseif(${COMPILER} MATCHES "arm")
 | 
						|
+            set_source_files_properties(${ZLIB_ARMv8} PROPERTIES LANGUAGE C COMPILE_FLAGS -mfpu=neon)
 | 
						|
+        endif()
 | 
						|
+    endif()
 | 
						|
 endif()
 | 
						|
 
 | 
						|
 if(MSVC)
 | 
						|
@@ -183,8 +196,8 @@ if(MINGW)
 | 
						|
     set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
 | 
						|
 endif(MINGW)
 | 
						|
 
 | 
						|
-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
 | 
						|
-add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
 | 
						|
+add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_ARMv8} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
 | 
						|
+add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_ARMv8} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
 | 
						|
 set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
 | 
						|
 set_target_properties(zlib PROPERTIES SOVERSION 1)
 | 
						|
 
 | 
						|
diff --git a/adler32.c b/adler32.c
 | 
						|
index d0be4380..45ebaa4b 100644
 | 
						|
--- a/adler32.c
 | 
						|
+++ b/adler32.c
 | 
						|
@@ -136,7 +136,12 @@ uLong ZEXPORT adler32(adler, buf, len)
 | 
						|
     const Bytef *buf;
 | 
						|
     uInt len;
 | 
						|
 {
 | 
						|
+#ifdef ARMv8
 | 
						|
+#  pragma message("Using NEON-ized Adler32.")
 | 
						|
+    return NEON_adler32(adler, buf, len);
 | 
						|
+#else
 | 
						|
     return adler32_z(adler, buf, len);
 | 
						|
+#endif
 | 
						|
 }
 | 
						|
 
 | 
						|
 /* ========================================================================= */
 | 
						|
diff --git a/contrib/README.contrib b/contrib/README.contrib
 | 
						|
index a411d5c3..3fd1d202 100644
 | 
						|
--- a/contrib/README.contrib
 | 
						|
+++ b/contrib/README.contrib
 | 
						|
@@ -12,6 +12,9 @@ amd64/      by Mikhail Teterin <mi@ALDAN.algebra.com>
 | 
						|
         asm code for AMD64
 | 
						|
         See patch at http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/96393
 | 
						|
 
 | 
						|
+arm/        by Adenilson Cavalcanti <cavalcantii@chromium.org>
 | 
						|
+        ARM optimizations (NEON and ARMv8 code).
 | 
						|
+
 | 
						|
 asm686/     by Brian Raiter <breadbox@muppetlabs.com>
 | 
						|
         asm code for Pentium and PPro/PII, using the AT&T (GNU as) syntax
 | 
						|
         See http://www.muppetlabs.com/~breadbox/software/assembly.html
 | 
						|
diff --git a/contrib/arm/neon_adler32.c b/contrib/arm/neon_adler32.c
 | 
						|
new file mode 100644
 | 
						|
index 00000000..f173a74f
 | 
						|
--- /dev/null
 | 
						|
+++ b/contrib/arm/neon_adler32.c
 | 
						|
@@ -0,0 +1,137 @@
 | 
						|
+/* Copyright (C) 1995-2011, 2016 Mark Adler
 | 
						|
+ * Copyright (C) 2017 ARM Holdings Inc.
 | 
						|
+ * Authors: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
 | 
						|
+ *          Simon Hosie <simon.hosie@arm.com>
 | 
						|
+ * This software is provided 'as-is', without any express or implied
 | 
						|
+ * warranty.  In no event will the authors be held liable for any damages
 | 
						|
+ * arising from the use of this software.
 | 
						|
+ * Permission is granted to anyone to use this software for any purpose,
 | 
						|
+ * including commercial applications, and to alter it and redistribute it
 | 
						|
+ * freely, subject to the following restrictions:
 | 
						|
+ * 1. The origin of this software must not be misrepresented; you must not
 | 
						|
+ *  claim that you wrote the original software. If you use this software
 | 
						|
+ *    in a product, an acknowledgment in the product documentation would be
 | 
						|
+ *    appreciated but is not required.
 | 
						|
+ * 2. Altered source versions must be plainly marked as such, and must not be
 | 
						|
+ *    misrepresented as being the original software.
 | 
						|
+ * 3. This notice may not be removed or altered from any source distribution.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
 | 
						|
+#include <arm_neon.h>
 | 
						|
+
 | 
						|
+static void NEON_accum32(uint32_t *s, const unsigned char *buf,
 | 
						|
+                         unsigned int len)
 | 
						|
+{
 | 
						|
+    static const uint8_t taps[32] = {
 | 
						|
+        32, 31, 30, 29, 28, 27, 26, 25,
 | 
						|
+        24, 23, 22, 21, 20, 19, 18, 17,
 | 
						|
+        16, 15, 14, 13, 12, 11, 10, 9,
 | 
						|
+        8, 7, 6, 5, 4, 3, 2, 1 };
 | 
						|
+
 | 
						|
+    uint32x2_t adacc2, s2acc2, as;
 | 
						|
+    uint8x16_t t0 = vld1q_u8(taps), t1 = vld1q_u8(taps + 16);
 | 
						|
+
 | 
						|
+    uint32x4_t adacc = vdupq_n_u32(0), s2acc = vdupq_n_u32(0);
 | 
						|
+    adacc = vsetq_lane_u32(s[0], adacc, 0);
 | 
						|
+    s2acc = vsetq_lane_u32(s[1], s2acc, 0);
 | 
						|
+
 | 
						|
+    while (len >= 2) {
 | 
						|
+        uint8x16_t d0 = vld1q_u8(buf), d1 = vld1q_u8(buf + 16);
 | 
						|
+        uint16x8_t adler, sum2;
 | 
						|
+        s2acc = vaddq_u32(s2acc, vshlq_n_u32(adacc, 5));
 | 
						|
+        adler = vpaddlq_u8(       d0);
 | 
						|
+        adler = vpadalq_u8(adler, d1);
 | 
						|
+        sum2 = vmull_u8(      vget_low_u8(t0), vget_low_u8(d0));
 | 
						|
+        sum2 = vmlal_u8(sum2, vget_high_u8(t0), vget_high_u8(d0));
 | 
						|
+        sum2 = vmlal_u8(sum2, vget_low_u8(t1), vget_low_u8(d1));
 | 
						|
+        sum2 = vmlal_u8(sum2, vget_high_u8(t1), vget_high_u8(d1));
 | 
						|
+        adacc = vpadalq_u16(adacc, adler);
 | 
						|
+        s2acc = vpadalq_u16(s2acc, sum2);
 | 
						|
+        len -= 2;
 | 
						|
+        buf += 32;
 | 
						|
+    }
 | 
						|
+
 | 
						|
+    while (len > 0) {
 | 
						|
+        uint8x16_t d0 = vld1q_u8(buf);
 | 
						|
+        uint16x8_t adler, sum2;
 | 
						|
+        s2acc = vaddq_u32(s2acc, vshlq_n_u32(adacc, 4));
 | 
						|
+        adler = vpaddlq_u8(d0);
 | 
						|
+        sum2 = vmull_u8(      vget_low_u8(t1), vget_low_u8(d0));
 | 
						|
+        sum2 = vmlal_u8(sum2, vget_high_u8(t1), vget_high_u8(d0));
 | 
						|
+        adacc = vpadalq_u16(adacc, adler);
 | 
						|
+        s2acc = vpadalq_u16(s2acc, sum2);
 | 
						|
+        buf += 16;
 | 
						|
+        len--;
 | 
						|
+    }
 | 
						|
+
 | 
						|
+    adacc2 = vpadd_u32(vget_low_u32(adacc), vget_high_u32(adacc));
 | 
						|
+    s2acc2 = vpadd_u32(vget_low_u32(s2acc), vget_high_u32(s2acc));
 | 
						|
+    as = vpadd_u32(adacc2, s2acc2);
 | 
						|
+    s[0] = vget_lane_u32(as, 0);
 | 
						|
+    s[1] = vget_lane_u32(as, 1);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void NEON_handle_tail(uint32_t *pair, const unsigned char *buf,
 | 
						|
+                             unsigned int len)
 | 
						|
+{
 | 
						|
+    /* Oldie K&R code integration. */
 | 
						|
+    unsigned int i;
 | 
						|
+    for (i = 0; i < len; ++i) {
 | 
						|
+        pair[0] += buf[i];
 | 
						|
+        pair[1] += pair[0];
 | 
						|
+    }
 | 
						|
+}
 | 
						|
+
 | 
						|
+extern unsigned long NEON_adler32(unsigned long adler, const unsigned char *buf,
 | 
						|
+                                  const unsigned int len)
 | 
						|
+{
 | 
						|
+    /* initial Adler-32 value (deferred check for len == 1 speed) */
 | 
						|
+    if (!buf)
 | 
						|
+        return 1L;
 | 
						|
+
 | 
						|
+    /* The largest prime smaller than 65536. */
 | 
						|
+    const uint32_t M_BASE = 65521;
 | 
						|
+    /* This is the threshold where doing accumulation may overflow. */
 | 
						|
+    const int M_NMAX = 5552;
 | 
						|
+
 | 
						|
+    unsigned long sum2;
 | 
						|
+    uint32_t pair[2];
 | 
						|
+    int n = M_NMAX;
 | 
						|
+    unsigned int done = 0;
 | 
						|
+    /* Oldie K&R code integration. */
 | 
						|
+    unsigned int i;
 | 
						|
+
 | 
						|
+    /* Split Adler-32 into component sums, it can be supplied by
 | 
						|
+     * the caller sites (e.g. in a PNG file).
 | 
						|
+     */
 | 
						|
+    sum2 = (adler >> 16) & 0xffff;
 | 
						|
+    adler &= 0xffff;
 | 
						|
+    pair[0] = adler;
 | 
						|
+    pair[1] = sum2;
 | 
						|
+
 | 
						|
+    for (i = 0; i < len; i += n) {
 | 
						|
+        if ((i + n) > len)
 | 
						|
+            n = len - i;
 | 
						|
+
 | 
						|
+        if (n < 16)
 | 
						|
+            break;
 | 
						|
+
 | 
						|
+        NEON_accum32(pair, buf + i, n / 16);
 | 
						|
+        pair[0] %= M_BASE;
 | 
						|
+        pair[1] %= M_BASE;
 | 
						|
+
 | 
						|
+        done += (n / 16) * 16;
 | 
						|
+    }
 | 
						|
+
 | 
						|
+    /* Handle the tail elements. */
 | 
						|
+    if (done < len) {
 | 
						|
+        NEON_handle_tail(pair, (buf + done), len - done);
 | 
						|
+        pair[0] %= M_BASE;
 | 
						|
+        pair[1] %= M_BASE;
 | 
						|
+    }
 | 
						|
+
 | 
						|
+    /* D = B * 65536 + A, see: https://en.wikipedia.org/wiki/Adler-32. */
 | 
						|
+    return (pair[1] << 16) | pair[0];
 | 
						|
+}
 | 
						|
+#endif
 |