Coverage Report

Created: 2026-09-04 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/arch/x86/adler32_avx512_vnni.c
Line
Count
Source
1
/* adler32_avx512_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_AVX512VNNI
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_avx512_p.h"
18
#include "adler32_avx2_p.h"
19
20
0
Z_INTERNAL uint32_t adler32_avx512_vnni(uint32_t adler, const uint8_t *src, size_t len) {
21
0
    uint32_t adler0, adler1;
22
0
    adler1 = (adler >> 16) & 0xffff;
23
0
    adler0 = adler & 0xffff;
24
25
0
rem_peel:
26
0
    if (len < 32)
27
0
        return adler32_ssse3(adler, src, len);
28
29
0
    if (len < 64)
30
0
        return adler32_avx2(adler, src, len);
31
32
0
    const __m512i dot2v = _mm512_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
33
0
                                          20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
34
0
                                          38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
35
0
                                          56, 57, 58, 59, 60, 61, 62, 63, 64);
36
37
0
    const __m512i zero = _mm512_setzero_si512();
38
0
    __m512i vs1, vs2_0;
39
40
0
    while (len >= 64) {
41
0
        vs1 = _mm512_zextsi128_si512(_mm_cvtsi32_si128(adler0));
42
0
        vs2_0 = _mm512_zextsi128_si512(_mm_cvtsi32_si128(adler1));
43
0
        size_t k = ALIGN_DOWN(MIN(len, NMAX), 64);
44
0
        len -= k;
45
0
        __m512i vs1_0 = vs1;
46
0
        __m512i vs3 = _mm512_setzero_si512();
47
48
0
        __m512i vs2_1 = _mm512_setzero_si512();
49
0
        __m512i vs2_2 = _mm512_setzero_si512();
50
0
        __m512i vs2_3 = _mm512_setzero_si512();
51
52
0
        __m512i vbuf0, vbuf1, vbuf2, vbuf3;
53
54
        /* Remainder peeling for 256-byte alignment boundaries */
55
0
        while (k % 256) {
56
0
            vbuf0 = _mm512_loadu_si512((__m512i*)src);
57
0
            src += 64;
58
0
            k -= 64;
59
60
0
            __m512i vs1_sad = _mm512_sad_epu8(vbuf0, zero);
61
0
            vs1 = _mm512_add_epi32(vs1, vs1_sad);
62
0
            vs3 = _mm512_add_epi32(vs3, vs1_0);
63
0
            vs2_0 = _mm512_dpbusd_epi32(vs2_0, vbuf0, dot2v);
64
0
            vs1_0 = vs1;
65
0
        }
66
67
        /* Manually unrolled by 4 to maximize ILP with separate accumulators */
68
0
        while (k >= 256) {
69
0
            vbuf0 = _mm512_loadu_si512((__m512i*)src);
70
0
            vbuf1 = _mm512_loadu_si512((__m512i*)(src + 64));
71
0
            vbuf2 = _mm512_loadu_si512((__m512i*)(src + 128));
72
0
            vbuf3 = _mm512_loadu_si512((__m512i*)(src + 192));
73
0
            src += 256;
74
0
            k -= 256;
75
76
            // Chunk 0
77
0
            __m512i vs1_sad0 = _mm512_sad_epu8(vbuf0, zero);
78
0
            vs1 = _mm512_add_epi32(vs1, vs1_sad0);
79
0
            vs3 = _mm512_add_epi32(vs3, vs1_0);
80
0
            vs2_0 = _mm512_dpbusd_epi32(vs2_0, vbuf0, dot2v);
81
82
            // Chunk 1
83
0
            vs3 = _mm512_add_epi32(vs3, vs1);
84
0
            __m512i vs1_sad1 = _mm512_sad_epu8(vbuf1, zero);
85
0
            vs1 = _mm512_add_epi32(vs1, vs1_sad1);
86
0
            vs2_1 = _mm512_dpbusd_epi32(vs2_1, vbuf1, dot2v);
87
88
            // Chunk 2
89
0
            vs3 = _mm512_add_epi32(vs3, vs1);
90
0
            __m512i vs1_sad2 = _mm512_sad_epu8(vbuf2, zero);
91
0
            vs1 = _mm512_add_epi32(vs1, vs1_sad2);
92
0
            vs2_2 = _mm512_dpbusd_epi32(vs2_2, vbuf2, dot2v);
93
94
            // Chunk 3
95
0
            vs3 = _mm512_add_epi32(vs3, vs1);
96
0
            __m512i vs1_sad3 = _mm512_sad_epu8(vbuf3, zero);
97
0
            vs1 = _mm512_add_epi32(vs1, vs1_sad3);
98
0
            vs2_3 = _mm512_dpbusd_epi32(vs2_3, vbuf3, dot2v);
99
100
0
            vs1_0 = vs1;
101
0
        }
102
103
        /* Scale baseline accumulators by 64 bytes. 128 rolls over the sign bit
104
         * and the dot product instruction forces the multiplicand to be interpreted
105
         * as signed prior to the accumulation */
106
0
        vs3 = _mm512_slli_epi32(vs3, 6);
107
0
        vs2_0 = _mm512_add_epi32(vs2_0, vs3);
108
109
0
        vs2_0 = _mm512_add_epi32(vs2_0, vs2_1);
110
0
        vs2_2 = _mm512_add_epi32(vs2_2, vs2_3);
111
0
        vs2_0 = _mm512_add_epi32(vs2_0, vs2_2);
112
113
0
        adler0 = partial_hsum(vs1) % BASE;
114
0
        adler1 = _mm512_reduce_add_epu32(vs2_0) % BASE;
115
0
    }
116
117
0
    adler = adler0 | (adler1 << 16);
118
119
    /* Process tail (len < 64). */
120
0
    if (len) {
121
0
        goto rem_peel;
122
0
    }
123
124
0
    return adler;
125
0
}
126
127
/* Use 256-bit vectors when copying because 512-bit variant is slower for numerous architectural reasons */
128
0
Z_INTERNAL uint32_t adler32_copy_avx512_vnni(uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len) {
129
0
    uint32_t adler0, adler1;
130
0
    adler1 = (adler >> 16) & 0xffff;
131
0
    adler0 = adler & 0xffff;
132
133
0
rem_peel_copy:
134
0
    if (len < 32) {
135
0
       __mmask32 storemask = _bzhi_u32(0xFFFFFFFF, (unsigned)len);
136
0
        __m256i copy_vec = _mm256_maskz_loadu_epi8(storemask, src);
137
0
        _mm256_mask_storeu_epi8(dst, storemask, copy_vec);
138
139
0
        return adler32_ssse3(adler, src, len);
140
0
    }
141
142
0
    const __m256i dot2v = _mm256_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
143
0
                                          20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32);
144
145
0
    const __m256i zero = _mm256_setzero_si256();
146
0
    __m256i vs1, vs2_0;
147
148
0
    while (len >= 32) {
149
0
        vs1 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler0));
150
0
        vs2_0 = _mm256_zextsi128_si256(_mm_cvtsi32_si128(adler1));
151
152
0
        size_t k = ALIGN_DOWN(MIN(len, NMAX), 32);
153
0
        len -= k;
154
155
0
        __m256i vs1_0 = vs1;
156
0
        __m256i vs3 = _mm256_setzero_si256();
157
158
0
        __m256i vs2_1 = _mm256_setzero_si256();
159
0
        __m256i vs2_2 = _mm256_setzero_si256();
160
0
        __m256i vs2_3 = _mm256_setzero_si256();
161
162
0
        __m256i vbuf0, vbuf1, vbuf2, vbuf3;
163
164
0
        while (k % 128) {
165
0
            vbuf0 = _mm256_loadu_si256((__m256i*)src);
166
0
            _mm256_storeu_si256((__m256i*)dst, vbuf0);
167
0
            dst += 32;
168
0
            src += 32;
169
0
            k -= 32;
170
171
0
            __m256i vs1_sad = _mm256_sad_epu8(vbuf0, zero);
172
0
            vs1 = _mm256_add_epi32(vs1, vs1_sad);
173
0
            vs3 = _mm256_add_epi32(vs3, vs1_0);
174
0
            vs2_0 = _mm256_dpbusd_epi32(vs2_0, vbuf0, dot2v);
175
0
            vs1_0 = vs1;
176
0
        }
177
178
        /* Manually unrolled by 4 blocks (128 bytes total per iteration) */
179
0
        while (k >= 128) {
180
0
            vbuf0 = _mm256_loadu_si256((__m256i*)src);
181
0
            vbuf1 = _mm256_loadu_si256((__m256i*)(src + 32));
182
0
            vbuf2 = _mm256_loadu_si256((__m256i*)(src + 64));
183
0
            vbuf3 = _mm256_loadu_si256((__m256i*)(src + 96));
184
0
            src += 128;
185
0
            k -= 128;
186
187
0
            _mm256_storeu_si256((__m256i*)dst, vbuf0);
188
0
            _mm256_storeu_si256((__m256i*)(dst + 32), vbuf1);
189
0
            _mm256_storeu_si256((__m256i*)(dst + 64), vbuf2);
190
0
            _mm256_storeu_si256((__m256i*)(dst + 96), vbuf3);
191
0
            dst += 128;
192
193
0
            __m256i vs1_sad0 = _mm256_sad_epu8(vbuf0, zero);
194
0
            vs1 = _mm256_add_epi32(vs1, vs1_sad0);
195
0
            vs3 = _mm256_add_epi32(vs3, vs1_0);
196
0
            vs2_0 = _mm256_dpbusd_epi32(vs2_0, vbuf0, dot2v);
197
198
0
            vs3 = _mm256_add_epi32(vs3, vs1);
199
0
            __m256i vs1_sad1 = _mm256_sad_epu8(vbuf1, zero);
200
0
            vs1 = _mm256_add_epi32(vs1, vs1_sad1);
201
0
            vs2_1 = _mm256_dpbusd_epi32(vs2_1, vbuf1, dot2v);
202
203
0
            vs3 = _mm256_add_epi32(vs3, vs1);
204
0
            __m256i vs1_sad2 = _mm256_sad_epu8(vbuf2, zero);
205
0
            vs1 = _mm256_add_epi32(vs1, vs1_sad2);
206
0
            vs2_2 = _mm256_dpbusd_epi32(vs2_2, vbuf2, dot2v);
207
208
0
            vs3 = _mm256_add_epi32(vs3, vs1);
209
0
            __m256i vs1_sad3 = _mm256_sad_epu8(vbuf3, zero);
210
0
            vs1 = _mm256_add_epi32(vs1, vs1_sad3);
211
0
            vs2_3 = _mm256_dpbusd_epi32(vs2_3, vbuf3, dot2v);
212
213
0
            vs1_0 = vs1;
214
0
        }
215
216
0
        vs3 = _mm256_slli_epi32(vs3, 5);
217
0
        vs2_0 = _mm256_add_epi32(vs2_0, vs3);
218
219
0
        vs2_0 = _mm256_add_epi32(vs2_0, vs2_1);
220
0
        vs2_2 = _mm256_add_epi32(vs2_2, vs2_3);
221
0
        vs2_0 = _mm256_add_epi32(vs2_0, vs2_2);
222
223
0
        adler0 = partial_hsum256(vs1) % BASE;
224
0
        adler1 = hsum256(vs2_0) % BASE;
225
0
    }
226
227
0
    adler = adler0 | (adler1 << 16);
228
229
    /* Process tail (len < 32). */
230
0
    if (len) {
231
0
        goto rem_peel_copy;
232
0
    }
233
234
0
    return adler;
235
0
}
236
237
#endif