9ed784e3d333bcf525eae370662b01e129f35b79
[openwrt/staging/dedeckeh.git] / package / libs / zlib / patches / 001-neon-implementation-of-adler32.patch
1 From d2f06cd65d7ac39c6dd6761eef162abc946b155b Mon Sep 17 00:00:00 2001
2 From: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
3 Date: Tue, 11 Apr 2017 17:13:02 -0700
4 Subject: [PATCH] NEON implementation for Adler32
5
6 The checksum is calculated in the uncompressed PNG data
7 and can be made much faster by using SIMD.
8
9 Tests in ARMv8 yielded an improvement of about 3x
10 (e.g. walltime was 350ms x 125ms for a 4096x4096 bytes
11 executed 30 times). That results in at least 18% improvement
12 in image decoding in Chromium.
13
14 Further details at:
15 https://bugs.chromium.org/p/chromium/issues/detail?id=688601
16 ---
17 CMakeLists.txt | 29 +++++++---
18 adler32.c | 5 ++
19 contrib/README.contrib | 3 +
20 contrib/arm/neon_adler32.c | 137 +++++++++++++++++++++++++++++++++++++++++++++
21 4 files changed, 166 insertions(+), 8 deletions(-)
22 create mode 100644 contrib/arm/neon_adler32.c
23
24 --- a/CMakeLists.txt
25 +++ b/CMakeLists.txt
26 @@ -7,6 +7,7 @@ set(VERSION "1.2.12")
27
28 option(ASM686 "Enable building i686 assembly implementation")
29 option(AMD64 "Enable building amd64 assembly implementation")
30 +option(ARMv8 "Enable building ARM NEON intrinsics implementation")
31
32 set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables")
33 set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries")
34 @@ -132,14 +133,26 @@ endif()
35 if(CMAKE_COMPILER_IS_GNUCC)
36 if(ASM686)
37 set(ZLIB_ASMS contrib/asm686/match.S)
38 - elseif (AMD64)
39 + elseif(AMD64)
40 set(ZLIB_ASMS contrib/amd64/amd64-match.S)
41 - endif ()
42 + elseif(ARMv8)
43 + set(ZLIB_ARMv8 contrib/arm/neon_adler32.c)
44 + endif()
45
46 - if(ZLIB_ASMS)
47 - add_definitions(-DASMV)
48 - set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
49 - endif()
50 + if(ZLIB_ASMS)
51 + add_definitions(-DASMV)
52 + set_source_files_properties(${ZLIB_ASMS} PROPERTIES LANGUAGE C COMPILE_FLAGS -DNO_UNDERLINE)
53 + elseif(ZLIB_ARMv8)
54 + add_definitions(-DARMv8)
55 + set(COMPILER ${CMAKE_C_COMPILER})
56 + # NEON is mandatory in ARMv8.
57 + if(${COMPILER} MATCHES "aarch64")
58 + set_source_files_properties(${ZLIB_ARMv8} PROPERTIES LANGUAGE C COMPILE_FLAGS -march=armv8-a)
59 + # But it was optional for ARMv7.
60 + elseif(${COMPILER} MATCHES "arm")
61 + set_source_files_properties(${ZLIB_ARMv8} PROPERTIES LANGUAGE C COMPILE_FLAGS -mfpu=neon)
62 + endif()
63 + endif()
64 endif()
65
66 if(MSVC)
67 @@ -183,8 +196,8 @@ if(MINGW)
68 set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
69 endif(MINGW)
70
71 -add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
72 -add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
73 +add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_ARMv8} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
74 +add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_ASMS} ${ZLIB_ARMv8} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
75 set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
76 set_target_properties(zlib PROPERTIES SOVERSION 1)
77
78 diff --git a/adler32.c b/adler32.c
79 index d0be4380..45ebaa4b 100644
80 --- a/adler32.c
81 +++ b/adler32.c
82 @@ -136,7 +136,12 @@ uLong ZEXPORT adler32(adler, buf, len)
83 const Bytef *buf;
84 uInt len;
85 {
86 +#ifdef ARMv8
87 +# pragma message("Using NEON-ized Adler32.")
88 + return NEON_adler32(adler, buf, len);
89 +#else
90 return adler32_z(adler, buf, len);
91 +#endif
92 }
93
94 /* ========================================================================= */
95 --- a/contrib/README.contrib
96 +++ b/contrib/README.contrib
97 @@ -8,6 +8,9 @@ ada/ by Dmitriy Anisimkov <anisim
98 Support for Ada
99 See http://zlib-ada.sourceforge.net/
100
101 +arm/ by Adenilson Cavalcanti <cavalcantii@chromium.org>
102 + ARM optimizations (NEON and ARMv8 code).
103 +
104 blast/ by Mark Adler <madler@alumni.caltech.edu>
105 Decompressor for output of PKWare Data Compression Library (DCL)
106
107 --- /dev/null
108 +++ b/contrib/arm/neon_adler32.c
109 @@ -0,0 +1,137 @@
110 +/* Copyright (C) 1995-2011, 2016 Mark Adler
111 + * Copyright (C) 2017 ARM Holdings Inc.
112 + * Authors: Adenilson Cavalcanti <adenilson.cavalcanti@arm.com>
113 + * Simon Hosie <simon.hosie@arm.com>
114 + * This software is provided 'as-is', without any express or implied
115 + * warranty. In no event will the authors be held liable for any damages
116 + * arising from the use of this software.
117 + * Permission is granted to anyone to use this software for any purpose,
118 + * including commercial applications, and to alter it and redistribute it
119 + * freely, subject to the following restrictions:
120 + * 1. The origin of this software must not be misrepresented; you must not
121 + * claim that you wrote the original software. If you use this software
122 + * in a product, an acknowledgment in the product documentation would be
123 + * appreciated but is not required.
124 + * 2. Altered source versions must be plainly marked as such, and must not be
125 + * misrepresented as being the original software.
126 + * 3. This notice may not be removed or altered from any source distribution.
127 + */
128 +
129 +#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
130 +#include <arm_neon.h>
131 +
132 +static void NEON_accum32(uint32_t *s, const unsigned char *buf,
133 + unsigned int len)
134 +{
135 + static const uint8_t taps[32] = {
136 + 32, 31, 30, 29, 28, 27, 26, 25,
137 + 24, 23, 22, 21, 20, 19, 18, 17,
138 + 16, 15, 14, 13, 12, 11, 10, 9,
139 + 8, 7, 6, 5, 4, 3, 2, 1 };
140 +
141 + uint32x2_t adacc2, s2acc2, as;
142 + uint8x16_t t0 = vld1q_u8(taps), t1 = vld1q_u8(taps + 16);
143 +
144 + uint32x4_t adacc = vdupq_n_u32(0), s2acc = vdupq_n_u32(0);
145 + adacc = vsetq_lane_u32(s[0], adacc, 0);
146 + s2acc = vsetq_lane_u32(s[1], s2acc, 0);
147 +
148 + while (len >= 2) {
149 + uint8x16_t d0 = vld1q_u8(buf), d1 = vld1q_u8(buf + 16);
150 + uint16x8_t adler, sum2;
151 + s2acc = vaddq_u32(s2acc, vshlq_n_u32(adacc, 5));
152 + adler = vpaddlq_u8( d0);
153 + adler = vpadalq_u8(adler, d1);
154 + sum2 = vmull_u8( vget_low_u8(t0), vget_low_u8(d0));
155 + sum2 = vmlal_u8(sum2, vget_high_u8(t0), vget_high_u8(d0));
156 + sum2 = vmlal_u8(sum2, vget_low_u8(t1), vget_low_u8(d1));
157 + sum2 = vmlal_u8(sum2, vget_high_u8(t1), vget_high_u8(d1));
158 + adacc = vpadalq_u16(adacc, adler);
159 + s2acc = vpadalq_u16(s2acc, sum2);
160 + len -= 2;
161 + buf += 32;
162 + }
163 +
164 + while (len > 0) {
165 + uint8x16_t d0 = vld1q_u8(buf);
166 + uint16x8_t adler, sum2;
167 + s2acc = vaddq_u32(s2acc, vshlq_n_u32(adacc, 4));
168 + adler = vpaddlq_u8(d0);
169 + sum2 = vmull_u8( vget_low_u8(t1), vget_low_u8(d0));
170 + sum2 = vmlal_u8(sum2, vget_high_u8(t1), vget_high_u8(d0));
171 + adacc = vpadalq_u16(adacc, adler);
172 + s2acc = vpadalq_u16(s2acc, sum2);
173 + buf += 16;
174 + len--;
175 + }
176 +
177 + adacc2 = vpadd_u32(vget_low_u32(adacc), vget_high_u32(adacc));
178 + s2acc2 = vpadd_u32(vget_low_u32(s2acc), vget_high_u32(s2acc));
179 + as = vpadd_u32(adacc2, s2acc2);
180 + s[0] = vget_lane_u32(as, 0);
181 + s[1] = vget_lane_u32(as, 1);
182 +}
183 +
184 +static void NEON_handle_tail(uint32_t *pair, const unsigned char *buf,
185 + unsigned int len)
186 +{
187 + /* Oldie K&R code integration. */
188 + unsigned int i;
189 + for (i = 0; i < len; ++i) {
190 + pair[0] += buf[i];
191 + pair[1] += pair[0];
192 + }
193 +}
194 +
195 +extern unsigned long NEON_adler32(unsigned long adler, const unsigned char *buf,
196 + const unsigned int len)
197 +{
198 + /* initial Adler-32 value (deferred check for len == 1 speed) */
199 + if (!buf)
200 + return 1L;
201 +
202 + /* The largest prime smaller than 65536. */
203 + const uint32_t M_BASE = 65521;
204 + /* This is the threshold where doing accumulation may overflow. */
205 + const int M_NMAX = 5552;
206 +
207 + unsigned long sum2;
208 + uint32_t pair[2];
209 + int n = M_NMAX;
210 + unsigned int done = 0;
211 + /* Oldie K&R code integration. */
212 + unsigned int i;
213 +
214 + /* Split Adler-32 into component sums, it can be supplied by
215 + * the caller sites (e.g. in a PNG file).
216 + */
217 + sum2 = (adler >> 16) & 0xffff;
218 + adler &= 0xffff;
219 + pair[0] = adler;
220 + pair[1] = sum2;
221 +
222 + for (i = 0; i < len; i += n) {
223 + if ((i + n) > len)
224 + n = len - i;
225 +
226 + if (n < 16)
227 + break;
228 +
229 + NEON_accum32(pair, buf + i, n / 16);
230 + pair[0] %= M_BASE;
231 + pair[1] %= M_BASE;
232 +
233 + done += (n / 16) * 16;
234 + }
235 +
236 + /* Handle the tail elements. */
237 + if (done < len) {
238 + NEON_handle_tail(pair, (buf + done), len - done);
239 + pair[0] %= M_BASE;
240 + pair[1] %= M_BASE;
241 + }
242 +
243 + /* D = B * 65536 + A, see: https://en.wikipedia.org/wiki/Adler-32. */
244 + return (pair[1] << 16) | pair[0];
245 +}
246 +#endif