Coverage Report

Created: 2026-07-25 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/arch/x86/crc32_chorba_sse2.c
Line
Count
Source
1
#include "zbuild.h"
2
#include "arch_functions.h"
3
4
#if defined(X86_SSE2) && defined(CRC32_CHORBA_SSE_FALLBACK)
5
6
#include "crc32_chorba_p.h"
7
#include "crc32_braid_tbl.h"
8
#include <emmintrin.h>
9
#include "arch/x86/x86_intrins.h"
10
11
0
#define LSHIFT_QWORD(x)     _mm_unpacklo_epi64(_mm_setzero_si128(), (x))
12
0
#define RSHIFT_QWORD(x)     _mm_unpackhi_epi64((x), _mm_setzero_si128())
13
0
#define ALIGNR_QWORD(a, b)  _mm_castpd_si128(_mm_shuffle_pd(_mm_castsi128_pd(a), _mm_castsi128_pd(b), 1))
14
15
#define READ_NEXT(in, off, a, b) \
16
0
    do { \
17
0
        a = _mm_load_si128((__m128i*)(in + off / sizeof(uint64_t))); \
18
0
        b = _mm_load_si128((__m128i*)(in + off / sizeof(uint64_t) + 2)); \
19
0
    } while (0)
20
21
#define NEXT_ROUND(invec, a, b, c, d) \
22
0
    do { \
23
0
        a = _mm_xor_si128(_mm_slli_epi64(invec, 17), _mm_slli_epi64(invec, 55)); \
24
0
        b = _mm_xor_si128(_mm_xor_si128(_mm_srli_epi64(invec, 47), _mm_srli_epi64(invec, 9)), _mm_slli_epi64(invec, 19)); \
25
0
        c = _mm_xor_si128(_mm_srli_epi64(invec, 45), _mm_slli_epi64(invec, 44)); \
26
0
        d = _mm_srli_epi64(invec, 20); \
27
0
    } while (0)
28
29
#define ACCUM_ROUND() \
30
0
    do { \
31
0
        __m128i a4_ = _mm_unpacklo_epi64(next56, ab4); \
32
0
        __m128i b2c2 = ALIGNR_QWORD(ab2, cd2); \
33
0
        __m128i b4c4 = ALIGNR_QWORD(ab4, cd4); \
34
0
        __m128i d2_ = RSHIFT_QWORD(cd2); \
35
0
        next12 = _mm_xor_si128(_mm_xor_si128(a4_, ab3), cd1); \
36
0
        next12 = _mm_xor_si128(next12, b2c2); \
37
0
        next34 = _mm_xor_si128(b4c4, cd3); \
38
0
        next34 = _mm_xor_si128(next34, d2_); \
39
0
        next56 = RSHIFT_QWORD(cd4); \
40
0
    } while (0)
41
42
0
Z_INTERNAL uint32_t chorba_small_nondestructive_sse2(uint32_t crc, const uint8_t *buf, size_t len) {
43
    /* The calling function ensured that this is aligned correctly */
44
0
    const uint64_t* input = (const uint64_t*)buf;
45
0
    ALIGNED_(16) uint64_t final[9] = {0};
46
0
    uint64_t next1 = ~crc;
47
0
    crc = 0;
48
0
    uint64_t next2, next3, next4, next5;
49
50
0
    __m128i next12 = _mm_cvtsi64_si128(next1);
51
0
    __m128i next34 = _mm_setzero_si128();
52
0
    __m128i next56 = _mm_setzero_si128();
53
0
    __m128i ab1, ab2, ab3, ab4, cd1, cd2, cd3, cd4;
54
55
0
    size_t i = 0;
56
57
    /* This is weird, doing for vs while drops 10% off the exec time */
58
0
    for (; (i + 256 + 40 + 32 + 32) < len; i += 32) {
59
0
        __m128i in1in2, in3in4;
60
61
0
        const uint64_t *input_ptr = input + (i / sizeof(uint64_t));
62
0
        const __m128i *input_ptr_128 = (__m128i*)input_ptr;
63
0
        __m128i chorba12 = _mm_load_si128(input_ptr_128++);
64
0
        __m128i chorba34 = _mm_load_si128(input_ptr_128++);
65
0
        __m128i chorba56 = _mm_load_si128(input_ptr_128++);
66
0
        __m128i chorba78 = _mm_load_si128(input_ptr_128++);
67
68
0
        chorba12 = _mm_xor_si128(chorba12, next12);
69
0
        chorba34 = _mm_xor_si128(chorba34, next34);
70
0
        chorba56 = _mm_xor_si128(chorba56, next56);
71
0
        chorba78 = _mm_xor_si128(chorba78, chorba12);
72
0
        __m128i chorba45 = ALIGNR_QWORD(chorba34, chorba56);
73
0
        __m128i chorba23 = ALIGNR_QWORD(chorba12, chorba34);
74
75
0
        i += 8 * 8;
76
77
        /* 0-3 */
78
0
        READ_NEXT(input, i, in1in2, in3in4);
79
80
0
        __m128i chorba34xor = _mm_xor_si128(chorba34, LSHIFT_QWORD(chorba12));
81
0
        in1in2 = _mm_xor_si128(in1in2, chorba34xor);
82
83
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
84
85
0
        in3in4 = _mm_xor_si128(in3in4, ab1);
86
        /* _hopefully_ we don't get a huge domain switching penalty for this. This seems to be the best sequence */
87
0
        __m128i chorba56xor = _mm_xor_si128(chorba56, LSHIFT_QWORD(ab2));
88
89
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba56xor, chorba23));
90
0
        in3in4 = _mm_xor_si128(in3in4, chorba12);
91
92
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
93
94
        /* chorba56 already consumed next56, clear it so ACCUM_ROUND
95
           does not xor the stale value into next12 */
96
0
        next56 = _mm_setzero_si128();
97
0
        ACCUM_ROUND();
98
99
0
        i += 32;
100
101
        /* 4-7 */
102
0
        READ_NEXT(input, i, in1in2, in3in4);
103
104
0
        in1in2 = _mm_xor_si128(in1in2, next12);
105
0
        in1in2 = _mm_xor_si128(in1in2, chorba78);
106
0
        in1in2 = _mm_xor_si128(in1in2, chorba45);
107
0
        in1in2 = _mm_xor_si128(in1in2, chorba34);
108
109
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
110
111
0
        in3in4 = _mm_xor_si128(in3in4, next34);
112
0
        in3in4 = _mm_xor_si128(in3in4, ab1);
113
0
        in3in4 = _mm_xor_si128(in3in4, chorba56);
114
0
        __m128i chorba67 = ALIGNR_QWORD(chorba56, chorba78);
115
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba67, LSHIFT_QWORD(ab2)));
116
117
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
118
119
0
        ACCUM_ROUND();
120
121
0
        i += 32;
122
123
        /* 8-11 */
124
0
        READ_NEXT(input, i, in1in2, in3in4);
125
126
0
        __m128i chorba80 = RSHIFT_QWORD(chorba78);
127
0
        __m128i next12_chorba12 = _mm_xor_si128(next12, chorba12);
128
0
        in1in2 = _mm_xor_si128(in1in2, chorba80);
129
0
        in1in2 = _mm_xor_si128(in1in2, chorba78);
130
0
        in1in2 = _mm_xor_si128(in1in2, next12_chorba12);
131
132
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
133
134
0
        in3in4 = _mm_xor_si128(next34, in3in4);
135
0
        in3in4 = _mm_xor_si128(in3in4, ab1);
136
0
        __m128i a2_ = LSHIFT_QWORD(ab2);
137
0
        in3in4 = _mm_xor_si128(in3in4, chorba34);
138
0
        in3in4 = _mm_xor_si128(in3in4, a2_);
139
140
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
141
142
0
        ACCUM_ROUND();
143
144
0
        i += 32;
145
146
        /* 12-15 */
147
0
        READ_NEXT(input, i, in1in2, in3in4);
148
149
0
        in1in2 = _mm_xor_si128(in1in2, next12);
150
0
        __m128i chorb56xorchorb12 = _mm_xor_si128(chorba56, chorba12);
151
0
        in1in2 = _mm_xor_si128(in1in2, chorb56xorchorb12);
152
0
        __m128i chorb1_ = LSHIFT_QWORD(chorba12);
153
0
        in1in2 = _mm_xor_si128(in1in2, chorb1_);
154
155
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
156
157
0
        in3in4 = _mm_xor_si128(next34, in3in4);
158
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(ab1, chorba78));
159
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba34, chorba12));
160
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba23, LSHIFT_QWORD(ab2)));
161
162
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
163
164
0
        ACCUM_ROUND();
165
166
0
        i += 32;
167
168
        /* 16-19 */
169
0
        READ_NEXT(input, i, in1in2, in3in4);
170
171
0
        __m128i chorba1_ = LSHIFT_QWORD(chorba12);
172
0
        in1in2 = _mm_xor_si128(_mm_xor_si128(next12, in1in2), _mm_xor_si128(chorba56, chorba45));
173
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba12, chorba34));
174
0
        in1in2 = _mm_xor_si128(chorba1_, in1in2);
175
176
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
177
178
0
        a2_ = LSHIFT_QWORD(ab2);
179
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(ab1, chorba78));
180
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba56, chorba34));
181
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba23, chorba67));
182
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba1_, a2_));
183
0
        in3in4 = _mm_xor_si128(in3in4, next34);
184
185
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
186
187
0
        ACCUM_ROUND();
188
189
0
        i += 32;
190
191
        /* 20-23 */
192
0
        READ_NEXT(input, i, in1in2, in3in4);
193
194
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(next12, chorba78));
195
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba45, chorba56));
196
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba23, chorba12));
197
0
        in1in2 = _mm_xor_si128(in1in2, chorba80);
198
199
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
200
201
0
        a2_ = LSHIFT_QWORD(ab2);
202
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(next34, ab1));
203
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba78, chorba67));
204
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba45, chorba34));
205
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba1_, a2_));
206
0
        in3in4 = _mm_xor_si128(in3in4, chorba12);
207
208
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
209
210
0
        ACCUM_ROUND();
211
212
0
        i += 32;
213
214
        /* 24-27 */
215
0
        READ_NEXT(input, i, in1in2, in3in4);
216
217
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(next12, chorba67));
218
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba56, chorba34));
219
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba23, chorba12));
220
0
        in1in2 = _mm_xor_si128(in1in2, chorba80);
221
222
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
223
224
0
        a2_ = LSHIFT_QWORD(ab2);
225
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(next34, ab1));
226
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba78, chorba56));
227
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba45, chorba34));
228
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba80, a2_));
229
230
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
231
232
0
        ACCUM_ROUND();
233
234
0
        i += 32;
235
236
        /* 28-31 */
237
0
        READ_NEXT(input, i, in1in2, in3in4);
238
239
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(next12, chorba78));
240
0
        in1in2 = _mm_xor_si128(in1in2, _mm_xor_si128(chorba67, chorba56));
241
242
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
243
244
0
        a2_ = LSHIFT_QWORD(ab2);
245
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(next34, ab1));
246
0
        in3in4 = _mm_xor_si128(in3in4, _mm_xor_si128(chorba78, chorba80));
247
0
        in3in4 = _mm_xor_si128(a2_, in3in4);
248
249
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
250
251
0
        ACCUM_ROUND();
252
0
    }
253
254
0
    for (; (i + 40 + 32) < len; i += 32) {
255
0
        __m128i in1in2, in3in4;
256
257
0
        READ_NEXT(input, i, in1in2, in3in4);
258
259
0
        in1in2 = _mm_xor_si128(in1in2, next12);
260
261
0
        NEXT_ROUND(in1in2, ab1, ab2, ab3, ab4);
262
263
0
        __m128i a2_ = LSHIFT_QWORD(ab2);
264
0
        __m128i ab1_next34 = _mm_xor_si128(next34, ab1);
265
0
        in3in4 = _mm_xor_si128(in3in4, ab1_next34);
266
0
        in3in4 = _mm_xor_si128(a2_, in3in4);
267
268
0
        NEXT_ROUND(in3in4, cd1, cd2, cd3, cd4);
269
270
0
        ACCUM_ROUND();
271
0
    }
272
273
0
    next1 = _mm_cvtsi128_si64(next12);
274
0
    next2 = _mm_cvtsi128_si64(_mm_unpackhi_epi64(next12, next12));
275
0
    next3 = _mm_cvtsi128_si64(next34);
276
0
    next4 = _mm_cvtsi128_si64(_mm_unpackhi_epi64(next34, next34));
277
0
    next5 = _mm_cvtsi128_si64(next56);
278
279
    /* Skip the call to memcpy */
280
0
    size_t copy_len = len - i;
281
0
    __m128i *final128 = (__m128i*)final;
282
0
    __m128i *input128 = (__m128i*)(input + (i / sizeof(uint64_t)));
283
0
    while (copy_len >= 64) {
284
0
        _mm_store_si128(final128++, _mm_load_si128(input128++));
285
0
        _mm_store_si128(final128++, _mm_load_si128(input128++));
286
0
        _mm_store_si128(final128++, _mm_load_si128(input128++));
287
0
        _mm_store_si128(final128++, _mm_load_si128(input128++));
288
0
        copy_len -= 64;
289
0
    }
290
291
0
    while (copy_len >= 16) {
292
0
        _mm_store_si128(final128++, _mm_load_si128(input128++));
293
0
        copy_len -= 16;
294
0
    }
295
296
0
    uint8_t *src_bytes = (uint8_t*)input128;
297
0
    uint8_t *dst_bytes = (uint8_t*)final128;
298
0
    while (copy_len--) {
299
0
       *dst_bytes++ = *src_bytes++;
300
0
    }
301
302
0
    final[0] ^= next1;
303
0
    final[1] ^= next2;
304
0
    final[2] ^= next3;
305
0
    final[3] ^= next4;
306
0
    final[4] ^= next5;
307
308
    /* We perform the same loop that braid_internal is doing but we'll skip
309
     * the function call for this tiny tail */
310
0
    uint8_t *final_bytes = (uint8_t*)final;
311
0
    size_t rem = len - i;
312
313
0
    while (rem--) {
314
0
        crc = crc_table[(crc ^ *final_bytes++) & 0xff] ^ (crc >> 8);
315
0
    }
316
317
0
    return ~crc;
318
0
}
319
320
0
Z_INTERNAL uint32_t crc32_chorba_sse2(uint32_t crc, const uint8_t *buf, size_t len) {
321
0
    uintptr_t align_diff = ALIGN_DIFF(buf, 16);
322
0
    if (len <= align_diff + CHORBA_SMALL_THRESHOLD_64BIT)
323
0
        return crc32_braid(crc, buf, len);
324
325
0
    if (align_diff) {
326
0
        crc = crc32_braid(crc, buf, align_diff);
327
0
        len -= align_diff;
328
0
        buf += align_diff;
329
0
    }
330
0
#ifdef CRC32_CHORBA_FALLBACK
331
0
    if (len > CHORBA_LARGE_THRESHOLD)
332
0
        return crc32_chorba_118960_nondestructive(crc, buf, len);
333
0
#endif
334
0
    return chorba_small_nondestructive_sse2(crc, buf, len);
335
0
}
336
337
0
Z_INTERNAL uint32_t crc32_copy_chorba_sse2(uint32_t crc, uint8_t *dst, const uint8_t *src, size_t len) {
338
0
    crc = crc32_chorba_sse2(crc, src, len);
339
0
    memcpy(dst, src, len);
340
0
    return crc;
341
0
}
342
#endif