Coverage Report

Created: 2026-07-16 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/arch/x86/adler32_avx2_vnni.c
Line
Count
Source
1
/* adler32_avx2_vnni.c -- compute the Adler-32 checksum of a data stream
2
 * Based on Brian Bockelman's AVX2 version
3
 * Copyright (C) 1995-2011 Mark Adler
4
 * Authors:
5
 *   Adam Stylinski <kungfujesus06@gmail.com>
6
 *   Brian Bockelman <bockelman@gmail.com>
7
 * For conditions of distribution and use, see copyright notice in zlib.h
8
 */
9
10
#ifdef X86_AVX2VNNI
11
12
#include "zbuild.h"
13
#include "adler32_p.h"
14
#include "arch_functions.h"
15
#include <immintrin.h>
16
#include "x86_intrins.h"
17
#include "adler32_avx2_p.h"
18
19
0
Z_INTERNAL uint32_t adler32_avx2_vnni(uint32_t adler, const uint8_t *src, size_t len) {
20
0
    uint32_t adler0, adler1;
21
0
    adler1 = (adler >> 16) & 0xffff;
22
0
    adler0 = adler & 0xffff;
23
24
0
rempeel:
25
0
    if (len < 16) {
26
0
        return adler32_copy_tail(adler0, NULL, src, len, adler1, 1, 15, 0);
27
0
    } else if (len < 32) {
28
0
        return adler32_ssse3(adler, src, len);
29
0
    }
30
31
0
    const __m256i dot2v = _mm256_setr_epi8(64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47,
32
0
                                           46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33);
33
0
    const __m256i dot2v_0 = _mm256_setr_epi8(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
34
0
                                             14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
35
36
0
    const __m256i zero = _mm256_setzero_si256();
37
    /* Just like the avx512 version but we need to split over 2 registers */
38
0
    __m256i vs1a, vs2a, vs2_0a;
39
0
    __m256i vs1b, vs2b, vs2_0b;
40
41
0
    while (len >= 32) {
42
        /* Set the bottommost bytes */
43
0
        vs1a = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler0));
44
0
        vs2a = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler1));
45
46
0
        vs1b = zero;
47
0
        vs2b = zero;
48
49
0
        __m256i vs1_0a = vs1a;
50
0
        __m256i vs1_0b = vs1b;
51
0
        __m256i vs3 = zero;
52
0
        __m256i vs3b = zero;
53
0
        vs2_0a = zero;
54
0
        vs2_0b = zero;
55
56
0
        size_t k = ALIGN_DOWN(MIN(len, NMAX), 32);
57
0
        len -= k;
58
59
        /* We might get a tad bit more ILP here if we sum to a second register in the loop */
60
0
        __m256i vbuf0, vbuf1, vbuf2, vbuf3;
61
62
        /* Manually unrolled this loop by 4 for a decent amount of ILP */
63
0
        while (k >= 128) {
64
            /*
65
               vs1 = adler + sum(c[i])
66
               vs2 = sum2 + 64 vs1 + sum( (64-i+1) c[i] )
67
            */
68
0
            vbuf0 = _mm256_loadu_si256((__m256i*)src);
69
0
            vbuf1 = _mm256_loadu_si256((__m256i*)(src + 32));
70
0
            vbuf2 = _mm256_loadu_si256((__m256i*)(src + 64));
71
0
            vbuf3 = _mm256_loadu_si256((__m256i*)(src + 96));
72
0
            src += 128;
73
0
            k -= 128;
74
75
0
            __m256i vs1_sada = _mm256_sad_epu8(vbuf0, zero);
76
0
            __m256i vs1_sadb = _mm256_sad_epu8(vbuf1, zero);
77
78
0
            vs1a = _mm256_add_epi32(vs1a, vs1_sada);
79
0
            vs1b = _mm256_add_epi32(vs1b, vs1_sadb);
80
81
0
            vs3 = _mm256_add_epi32(vs3, vs1_0a);
82
0
            vs3b = _mm256_add_epi32(vs3b, vs1_0b);
83
84
0
            vs2a = _mm256_dpbusd_epi32(vs2a, vbuf0, dot2v);
85
0
            vs2b = _mm256_dpbusd_epi32(vs2b, vbuf1, dot2v_0);
86
87
0
            vs3 = _mm256_add_epi32(vs3, vs1a);
88
0
            vs3b = _mm256_add_epi32(vs3b, vs1b);
89
90
0
            vs1_sada = _mm256_sad_epu8(vbuf2, zero);
91
0
            vs1_sadb = _mm256_sad_epu8(vbuf3, zero);
92
93
0
            vs1a = _mm256_add_epi32(vs1a, vs1_sada);
94
0
            vs1b = _mm256_add_epi32(vs1b, vs1_sadb);
95
96
0
            vs2_0a = _mm256_dpbusd_epi32(vs2_0a, vbuf2, dot2v);
97
0
            vs2_0b = _mm256_dpbusd_epi32(vs2_0b, vbuf3, dot2v_0);
98
99
0
            vs1_0a = vs1a;
100
0
            vs1_0b = vs1b;
101
0
        }
102
103
0
        vs3 = _mm256_add_epi32(vs3, vs3b);
104
0
        vs3 = _mm256_slli_epi32(vs3, 6);
105
0
        vs2_0a = _mm256_add_epi32(vs2_0a, vs2_0b);
106
0
        vs2a = _mm256_add_epi32(vs2a, vs3);
107
0
        vs2a = _mm256_add_epi32(vs2a, vs2_0a);
108
0
        vs2a = _mm256_add_epi32(vs2a, vs2b);
109
0
        vs1a = _mm256_add_epi32(vs1a, vs1b);
110
0
        vs1_0a = _mm256_add_epi32(vs1_0a, vs1_0b);
111
112
0
        vs3 = zero;
113
114
115
0
        while (k >= 32) {
116
0
            __m256i vbuf = _mm256_loadu_si256((__m256i*)src);
117
0
            src += 32;
118
0
            k -= 32;
119
120
0
            __m256i vs1_sad = _mm256_sad_epu8(vbuf, zero); // Sum of abs diff, resulting in 2 x int32's
121
122
0
            vs1a = _mm256_add_epi32(vs1a, vs1_sad);
123
0
            vs3 = _mm256_add_epi32(vs3, vs1_0a);
124
0
            vs2a = _mm256_dpbusd_epi32(vs2a, vbuf, dot2v_0);
125
0
            vs1_0a = vs1a;
126
0
        }
127
128
0
        vs3 = _mm256_slli_epi32(vs3, 5);
129
0
        vs2a = _mm256_add_epi32(vs2a, vs3);
130
131
0
        adler0 = partial_hsum256(vs1a) % BASE;
132
0
        adler1 = hsum256(vs2a) % BASE;
133
0
    }
134
135
0
    adler = adler0 | (adler1 << 16);
136
137
0
    if (len) {
138
0
        goto rempeel;
139
0
    }
140
141
0
    return adler;
142
0
}
143
144
/* There's a data dependency bubble to pop here to maximize throughput but I haven't been able to quite factor
145
 * out exactly what gets us there. I'm leaving this here as this is the closest I managed to get. For performance
146
 * cores on Raptor Lake, the avx2 copy function is faster. For E-cores, this function is faster. There's
147
 * likely some sophisticated fusion happening on Raptor Cove that is just not happening on Gracemont */
148
149
#if 0
150
Z_INTERNAL uint32_t adler32_copy_avx2_vnni(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len) {
151
    uint32_t adler0, adler1;
152
    adler1 = (adler >> 16) & 0xffff;
153
    adler0 = adler & 0xffff;
154
155
rem_peel_copy:
156
    if (len < 16) {
157
        return adler32_copy_tail(adler0, dst, src, len, adler1, 1, 15, 1);
158
    } else if (len < 32) {
159
        return adler32_copy_sse42(adler, dst, src, len);
160
    }
161
162
    const __m256i dot2v = _mm256_setr_epi8(64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47,
163
                                           46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33);
164
    const __m256i dot2v_0 = _mm256_setr_epi8(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
165
                                             14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
166
167
    const __m256i zero = _mm256_setzero_si256();
168
    __m256i vs1, vs2, vs2_0, vs3_0;
169
170
    while (len >= 32) {
171
        vs1 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler0));
172
        vs2 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler1));
173
        __m256i vs1_0 = vs1;
174
        __m256i vs1_1 = zero;
175
        __m256i vs3 = zero;
176
        vs2_0 = vs3;
177
        vs3_0 = vs3;
178
179
        size_t k = MIN(len, NMAX);
180
        k -= k % 32;
181
        len -= k;
182
183
        /* We might get a tad bit more ILP here if we sum to a second register in the loop */
184
        __m256i vbuf0, vbuf1;
185
186
        /* Manually unrolled this loop by 2 for a decent amount of ILP */
187
        while (k >= 64) {
188
            /*
189
               vs1 = adler + sum(c[i])
190
               vs2 = sum2 + 64 vs1 + sum( (64-i+1) c[i] )
191
            */
192
            vbuf0 = _mm256_loadu_si256((__m256i*)src);
193
            vbuf1 = _mm256_loadu_si256((__m256i*)(src + 32));
194
            _mm256_storeu_si256((__m256i*)dst, vbuf0);
195
            _mm256_storeu_si256((__m256i*)(dst + 32), vbuf1);
196
            src += 64;
197
            dst += 64;
198
            k -= 64;
199
200
            __m256i vs1_sad = _mm256_sad_epu8(vbuf0, zero);
201
            __m256i vs1_sad2 = _mm256_sad_epu8(vbuf1, zero);
202
203
            /* multiply-add, resulting in 16 ints. Fuse with sum stage from prior versions, as we now have the dp
204
             * instructions to eliminate them */
205
            vs2 = _mm256_dpbusd_epi32(vs2, vbuf0, dot2v);
206
            vs2_0 = _mm256_dpbusd_epi32(vs2_0, vbuf1, dot2v_0);
207
208
            vs1_0 = _mm256_add_epi32(vs1_0, vs1_sad);
209
            vs1_1 = _mm256_add_epi32(vs1_1, vs1_sad2);
210
211
            vs3_0 = _mm256_add_epi32(vs3_0, vs1_1);
212
            vs3 = _mm256_add_epi32(vs3, vs1_0);
213
            vs1_0 = vs1;
214
        }
215
216
        vs2 = _mm256_add_epi32(vs2_0, vs2);
217
        vs3 = _mm256_slli_epi32(vs3, 6);
218
        vs2 = _mm256_add_epi32(vs2, vs3);
219
        vs2 = _mm256_add_epi32(vs3_0, vs2);
220
        vs3 = _mm256_setzero_si256();
221
222
        while (k >= 32) {
223
            __m256i vbuf = _mm256_loadu_si256((__m256i*)src);
224
            _mm256_storeu_si256((__m256i*)dst, vbuf);
225
            src += 32;
226
            dst += 32;
227
            k -= 32;
228
229
            __m256i vs1_sad = _mm256_sad_epu8(vbuf, zero); // Sum of abs diff, resulting in 2 x int32's
230
231
            vs1 = _mm256_add_epi32(vs1, vs1_sad);
232
            vs3 = _mm256_add_epi32(vs3, vs1_0);
233
            vs2 = _mm256_dpbusd_epi32(vs2, vbuf, dot2v_0);
234
            vs1_0 = vs1;
235
        }
236
237
        vs3 = _mm256_slli_epi32(vs3, 5);
238
        vs2 = _mm256_add_epi32(vs2, vs3);
239
240
        adler0 = partial_hsum256(vs1) % BASE;
241
        adler1 = hsum256(vs2) % BASE;
242
    }
243
244
    adler = adler0 | (adler1 << 16);
245
246
    /* Process tail (len < 64). */
247
    if (len) {
248
        goto rem_peel_copy;
249
    }
250
251
    return adler;
252
}
253
#endif
254
255
#endif