Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/libdeflate-src/lib/x86/crc32_pclmul_template.h
Line
Count
Source
1
/*
2
 * x86/crc32_pclmul_template.h - gzip CRC-32 with PCLMULQDQ instructions
3
 *
4
 * Copyright 2016 Eric Biggers
5
 *
6
 * Permission is hereby granted, free of charge, to any person
7
 * obtaining a copy of this software and associated documentation
8
 * files (the "Software"), to deal in the Software without
9
 * restriction, including without limitation the rights to use,
10
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the
12
 * Software is furnished to do so, subject to the following
13
 * conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be
16
 * included in all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
 * OTHER DEALINGS IN THE SOFTWARE.
26
 */
27
28
/*
29
 * This file is a "template" for instantiating PCLMULQDQ-based crc32_x86
30
 * functions.  The "parameters" are:
31
 *
32
 * SUFFIX:
33
 *  Name suffix to append to all instantiated functions.
34
 * ATTRIBUTES:
35
 *  Target function attributes to use.  Must satisfy the dependencies of the
36
 *  other parameters as follows:
37
 *     VL=16 && USE_AVX512=0: at least pclmul,sse4.1
38
 *     VL=32 && USE_AVX512=0: at least vpclmulqdq,pclmul,avx2
39
 *     VL=32 && USE_AVX512=1: at least vpclmulqdq,pclmul,avx512bw,avx512vl
40
 *     VL=64 && USE_AVX512=1: at least vpclmulqdq,pclmul,avx512bw,avx512vl
41
 *     (Other combinations are not useful and have not been tested.)
42
 * VL:
43
 *  Vector length in bytes.  Must be 16, 32, or 64.
44
 * USE_AVX512:
45
 *  If 1, take advantage of AVX-512 features such as masking and the
46
 *  vpternlog instruction.  This doesn't enable the use of 512-bit vectors;
47
 *  the vector length is controlled by VL.  If 0, assume that the CPU might
48
 *  not support AVX-512.
49
 *
50
 * The overall algorithm used is CRC folding with carryless multiplication
51
 * instructions.  Note that the x86 crc32 instruction cannot be used, as it is
52
 * for a different polynomial, not the gzip one.  For an explanation of CRC
53
 * folding with carryless multiplication instructions, see
54
 * scripts/gen-crc32-consts.py and the following blog posts and papers:
55
 *
56
 *  "An alternative exposition of crc32_4k_pclmulqdq"
57
 *  https://www.corsix.org/content/alternative-exposition-crc32_4k_pclmulqdq
58
 *
59
 *  "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction"
60
 *  https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
61
 *
62
 * The original pclmulqdq instruction does one 64x64 to 128-bit carryless
63
 * multiplication.  The VPCLMULQDQ feature added instructions that do two
64
 * parallel 64x64 to 128-bit carryless multiplications in combination with AVX
65
 * or AVX512VL, or four in combination with AVX512F.
66
 */
67
68
#if VL == 16
69
216
#  define vec_t     __m128i
70
1.44M
#  define fold_vec    fold_vec128
71
1.44M
#  define VLOADU(p)   _mm_loadu_si128((const void *)(p))
72
118
#  define VXOR(a, b)    _mm_xor_si128((a), (b))
73
0
#  define M128I_TO_VEC(a) a
74
216
#  define MULTS_8V    _mm_set_epi64x(CRC32_X991_MODG, CRC32_X1055_MODG)
75
216
#  define MULTS_4V    _mm_set_epi64x(CRC32_X479_MODG, CRC32_X543_MODG)
76
216
#  define MULTS_2V    _mm_set_epi64x(CRC32_X223_MODG, CRC32_X287_MODG)
77
216
#  define MULTS_1V    _mm_set_epi64x(CRC32_X95_MODG, CRC32_X159_MODG)
78
#elif VL == 32
79
0
#  define vec_t     __m256i
80
0
#  define fold_vec    fold_vec256
81
0
#  define VLOADU(p)   _mm256_loadu_si256((const void *)(p))
82
0
#  define VXOR(a, b)    _mm256_xor_si256((a), (b))
83
0
#  define M128I_TO_VEC(a) _mm256_zextsi128_si256(a)
84
0
#  define MULTS(a, b)   _mm256_set_epi64x(a, b, a, b)
85
0
#  define MULTS_8V    MULTS(CRC32_X2015_MODG, CRC32_X2079_MODG)
86
0
#  define MULTS_4V    MULTS(CRC32_X991_MODG, CRC32_X1055_MODG)
87
0
#  define MULTS_2V    MULTS(CRC32_X479_MODG, CRC32_X543_MODG)
88
0
#  define MULTS_1V    MULTS(CRC32_X223_MODG, CRC32_X287_MODG)
89
#elif VL == 64
90
0
#  define vec_t     __m512i
91
0
#  define fold_vec    fold_vec512
92
0
#  define VLOADU(p)   _mm512_loadu_si512((const void *)(p))
93
0
#  define VXOR(a, b)    _mm512_xor_si512((a), (b))
94
0
#  define M128I_TO_VEC(a) _mm512_zextsi128_si512(a)
95
0
#  define MULTS(a, b)   _mm512_set_epi64(a, b, a, b, a, b, a, b)
96
0
#  define MULTS_8V    MULTS(CRC32_X4063_MODG, CRC32_X4127_MODG)
97
0
#  define MULTS_4V    MULTS(CRC32_X2015_MODG, CRC32_X2079_MODG)
98
0
#  define MULTS_2V    MULTS(CRC32_X991_MODG, CRC32_X1055_MODG)
99
0
#  define MULTS_1V    MULTS(CRC32_X479_MODG, CRC32_X543_MODG)
100
#else
101
#  error "unsupported vector length"
102
#endif
103
104
#undef fold_vec128
105
static forceinline ATTRIBUTES __m128i
106
ADD_SUFFIX(fold_vec128)(__m128i src, __m128i dst, __m128i /* __v2du */ mults)
107
1.44M
{
108
1.44M
  dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x00));
109
1.44M
  dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x11));
110
1.44M
  return dst;
111
1.44M
}
Unexecuted instantiation: crc32.c:fold_vec128_vpclmulqdq_avx512_vl512
Unexecuted instantiation: crc32.c:fold_vec128_vpclmulqdq_avx512_vl256
Unexecuted instantiation: crc32.c:fold_vec128_vpclmulqdq_avx2
crc32.c:fold_vec128_pclmulqdq_avx
Line
Count
Source
107
1.44M
{
108
1.44M
  dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x00));
109
  dst = _mm_xor_si128(dst, _mm_clmulepi64_si128(src, mults, 0x11));
110
1.44M
  return dst;
111
1.44M
}
Unexecuted instantiation: crc32.c:fold_vec128_pclmulqdq
112
1.44M
#define fold_vec128 ADD_SUFFIX(fold_vec128)
113
114
#if VL >= 32
115
#undef fold_vec256
116
static forceinline ATTRIBUTES __m256i
117
ADD_SUFFIX(fold_vec256)(__m256i src, __m256i dst, __m256i /* __v4du */ mults)
118
0
{
119
#if USE_AVX512
120
  /* vpternlog with immediate 0x96 is a three-argument XOR. */
121
  return _mm256_ternarylogic_epi32(
122
      _mm256_clmulepi64_epi128(src, mults, 0x00),
123
      _mm256_clmulepi64_epi128(src, mults, 0x11),
124
      dst,
125
      0x96);
126
#else
127
  return _mm256_xor_si256(
128
      _mm256_xor_si256(dst,
129
           _mm256_clmulepi64_epi128(src, mults, 0x00)),
130
      _mm256_clmulepi64_epi128(src, mults, 0x11));
131
#endif
132
0
}
Unexecuted instantiation: crc32.c:fold_vec256_vpclmulqdq_avx512_vl512
Unexecuted instantiation: crc32.c:fold_vec256_vpclmulqdq_avx512_vl256
Unexecuted instantiation: crc32.c:fold_vec256_vpclmulqdq_avx2
133
0
#define fold_vec256 ADD_SUFFIX(fold_vec256)
134
#endif /* VL >= 32 */
135
136
#if VL >= 64
137
#undef fold_vec512
138
static forceinline ATTRIBUTES __m512i
139
ADD_SUFFIX(fold_vec512)(__m512i src, __m512i dst, __m512i /* __v8du */ mults)
140
0
{
141
  /* vpternlog with immediate 0x96 is a three-argument XOR. */
142
0
  return _mm512_ternarylogic_epi32(
143
0
      _mm512_clmulepi64_epi128(src, mults, 0x00),
144
0
      _mm512_clmulepi64_epi128(src, mults, 0x11),
145
0
      dst,
146
0
      0x96);
147
0
}
148
0
#define fold_vec512 ADD_SUFFIX(fold_vec512)
149
#endif /* VL >= 64 */
150
151
/*
152
 * Given 'x' containing a 16-byte polynomial, and a pointer 'p' that points to
153
 * the next '1 <= len <= 15' data bytes, rearrange the concatenation of 'x' and
154
 * the data into vectors x0 and x1 that contain 'len' bytes and 16 bytes,
155
 * respectively.  Then fold x0 into x1 and return the result.
156
 * Assumes that 'p + len - 16' is in-bounds.
157
 */
158
#undef fold_lessthan16bytes
159
static forceinline ATTRIBUTES __m128i
160
ADD_SUFFIX(fold_lessthan16bytes)(__m128i x, const u8 *p, size_t len,
161
         __m128i /* __v2du */ mults_128b)
162
103
{
163
103
  __m128i lshift = _mm_loadu_si128((const void *)&shift_tab[len]);
164
103
  __m128i rshift = _mm_loadu_si128((const void *)&shift_tab[len + 16]);
165
103
  __m128i x0, x1;
166
167
  /* x0 = x left-shifted by '16 - len' bytes */
168
103
  x0 = _mm_shuffle_epi8(x, lshift);
169
170
  /*
171
   * x1 = the last '16 - len' bytes from x (i.e. x right-shifted by 'len'
172
   * bytes) followed by the remaining data.
173
   */
174
103
  x1 = _mm_blendv_epi8(_mm_shuffle_epi8(x, rshift),
175
103
           _mm_loadu_si128((const void *)(p + len - 16)),
176
           /* msb 0/1 of each byte selects byte from arg1/2 */
177
103
           rshift);
178
179
103
  return fold_vec128(x0, x1, mults_128b);
180
103
}
Unexecuted instantiation: crc32.c:fold_lessthan16bytes_vpclmulqdq_avx512_vl512
Unexecuted instantiation: crc32.c:fold_lessthan16bytes_vpclmulqdq_avx512_vl256
Unexecuted instantiation: crc32.c:fold_lessthan16bytes_vpclmulqdq_avx2
crc32.c:fold_lessthan16bytes_pclmulqdq_avx
Line
Count
Source
162
103
{
163
103
  __m128i lshift = _mm_loadu_si128((const void *)&shift_tab[len]);
164
103
  __m128i rshift = _mm_loadu_si128((const void *)&shift_tab[len + 16]);
165
103
  __m128i x0, x1;
166
167
  /* x0 = x left-shifted by '16 - len' bytes */
168
103
  x0 = _mm_shuffle_epi8(x, lshift);
169
170
  /*
171
   * x1 = the last '16 - len' bytes from x (i.e. x right-shifted by 'len'
172
   * bytes) followed by the remaining data.
173
   */
174
103
  x1 = _mm_blendv_epi8(_mm_shuffle_epi8(x, rshift),
175
103
           _mm_loadu_si128((const void *)(p + len - 16)),
176
           /* msb 0/1 of each byte selects byte from arg1/2 */
177
103
           rshift);
178
179
103
  return fold_vec128(x0, x1, mults_128b);
180
103
}
Unexecuted instantiation: crc32.c:fold_lessthan16bytes_pclmulqdq
181
103
#define fold_lessthan16bytes  ADD_SUFFIX(fold_lessthan16bytes)
182
183
static ATTRIBUTES u32
184
ADD_SUFFIX(crc32_x86)(u32 crc, const u8 *p, size_t len)
185
216
{
186
  /*
187
   * mults_{N}v are the vectors of multipliers for folding across N vec_t
188
   * vectors, i.e. N*VL*8 bits.  mults_128b are the two multipliers for
189
   * folding across 128 bits.  mults_128b differs from mults_1v when
190
   * VL != 16.  All multipliers are 64-bit, to match what pclmulqdq needs,
191
   * but since this is for CRC-32 only their low 32 bits are nonzero.
192
   * For more details, see scripts/gen-crc32-consts.py.
193
   */
194
216
  const vec_t mults_8v = MULTS_8V;
195
216
  const vec_t mults_4v = MULTS_4V;
196
216
  const vec_t mults_2v = MULTS_2V;
197
216
  const vec_t mults_1v = MULTS_1V;
198
216
  const __m128i mults_128b = _mm_set_epi64x(CRC32_X95_MODG, CRC32_X159_MODG);
199
216
  const __m128i barrett_reduction_constants =
200
216
    _mm_set_epi64x(CRC32_BARRETT_CONSTANT_2, CRC32_BARRETT_CONSTANT_1);
201
216
  vec_t v0, v1, v2, v3, v4, v5, v6, v7;
202
216
  __m128i x0 = _mm_cvtsi32_si128(crc);
203
216
  __m128i x1;
204
205
216
  if (len < 8*VL) {
206
123
    if (len < VL) {
207
98
      STATIC_ASSERT(VL == 16 || VL == 32 || VL == 64);
208
98
      if (len < 16) {
209
      #if USE_AVX512
210
0
        if (len < 4)
211
0
          return crc32_slice1(crc, p, len);
212
        /*
213
         * Handle 4 <= len <= 15 bytes by doing a masked
214
         * load, XOR'ing the current CRC with the first
215
         * 4 bytes, left-shifting by '16 - len' bytes to
216
         * align the result to the end of x0 (so that it
217
         * becomes the low-order coefficients of a
218
         * 128-bit polynomial), and then doing the usual
219
         * reduction from 128 bits to 32 bits.
220
         */
221
0
        x0 = _mm_xor_si128(
222
0
          x0, _mm_maskz_loadu_epi8((1 << len) - 1, p));
223
0
        x0 = _mm_shuffle_epi8(
224
0
          x0, _mm_loadu_si128((const void *)&shift_tab[len]));
225
0
        goto reduce_x0;
226
      #else
227
        return crc32_slice1(crc, p, len);
228
      #endif
229
98
      }
230
      /*
231
       * Handle 16 <= len < VL bytes where VL is 32 or 64.
232
       * Use 128-bit instructions so that these lengths aren't
233
       * slower with VL > 16 than with VL=16.
234
       */
235
0
      x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0);
236
0
      if (len >= 32) {
237
0
        x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 16)),
238
0
             mults_128b);
239
0
        if (len >= 48)
240
0
          x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 32)),
241
0
               mults_128b);
242
0
      }
243
0
      p += len & ~15;
244
0
      goto less_than_16_remaining;
245
98
    }
246
25
    v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0));
247
25
    if (len < 2*VL) {
248
4
      p += VL;
249
4
      goto less_than_vl_remaining;
250
4
    }
251
21
    v1 = VLOADU(p + 1*VL);
252
21
    if (len < 4*VL) {
253
15
      p += 2*VL;
254
15
      goto less_than_2vl_remaining;
255
15
    }
256
6
    v2 = VLOADU(p + 2*VL);
257
6
    v3 = VLOADU(p + 3*VL);
258
6
    p += 4*VL;
259
93
  } else {
260
    /*
261
     * If the length is large and the pointer is misaligned, align
262
     * it.  For smaller lengths, just take the misaligned load
263
     * penalty.  Note that on recent x86 CPUs, vmovdqu with an
264
     * aligned address is just as fast as vmovdqa, so there's no
265
     * need to use vmovdqa in the main loop.
266
     */
267
93
    if (len > 65536 && ((uintptr_t)p & (VL-1))) {
268
0
      size_t align = -(uintptr_t)p & (VL-1);
269
270
0
      len -= align;
271
0
      x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0);
272
0
      p += 16;
273
0
      if (align & 15) {
274
0
        x0 = fold_lessthan16bytes(x0, p, align & 15,
275
0
                mults_128b);
276
0
        p += align & 15;
277
0
        align &= ~15;
278
0
      }
279
0
      while (align) {
280
0
        x0 = fold_vec128(x0, *(const __m128i *)p,
281
0
             mults_128b);
282
0
        p += 16;
283
0
        align -= 16;
284
0
      }
285
0
      v0 = M128I_TO_VEC(x0);
286
    #  if VL == 32
287
      v0 = _mm256_inserti128_si256(v0, *(const __m128i *)p, 1);
288
    #  elif VL == 64
289
      v0 = _mm512_inserti32x4(v0, *(const __m128i *)p, 1);
290
      v0 = _mm512_inserti64x4(v0, *(const __m256i *)(p + 16), 1);
291
    #  endif
292
0
      p -= 16;
293
93
    } else {
294
93
      v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0));
295
93
    }
296
93
    v1 = VLOADU(p + 1*VL);
297
93
    v2 = VLOADU(p + 2*VL);
298
93
    v3 = VLOADU(p + 3*VL);
299
93
    v4 = VLOADU(p + 4*VL);
300
93
    v5 = VLOADU(p + 5*VL);
301
93
    v6 = VLOADU(p + 6*VL);
302
93
    v7 = VLOADU(p + 7*VL);
303
93
    p += 8*VL;
304
305
    /*
306
     * This is the main loop, processing 8*VL bytes per iteration.
307
     * 4*VL is usually enough and would result in smaller code, but
308
     * Skylake and Cascade Lake need 8*VL to get full performance.
309
     */
310
181k
    while (len >= 16*VL) {
311
180k
      v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_8v);
312
180k
      v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_8v);
313
180k
      v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_8v);
314
180k
      v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_8v);
315
180k
      v4 = fold_vec(v4, VLOADU(p + 4*VL), mults_8v);
316
180k
      v5 = fold_vec(v5, VLOADU(p + 5*VL), mults_8v);
317
180k
      v6 = fold_vec(v6, VLOADU(p + 6*VL), mults_8v);
318
180k
      v7 = fold_vec(v7, VLOADU(p + 7*VL), mults_8v);
319
180k
      p += 8*VL;
320
180k
      len -= 8*VL;
321
180k
    }
322
323
    /* Fewer than 8*VL bytes remain. */
324
93
    v0 = fold_vec(v0, v4, mults_4v);
325
93
    v1 = fold_vec(v1, v5, mults_4v);
326
93
    v2 = fold_vec(v2, v6, mults_4v);
327
93
    v3 = fold_vec(v3, v7, mults_4v);
328
93
    if (len & (4*VL)) {
329
52
      v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_4v);
330
52
      v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_4v);
331
52
      v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_4v);
332
52
      v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_4v);
333
52
      p += 4*VL;
334
52
    }
335
93
  }
336
  /* Fewer than 4*VL bytes remain. */
337
99
  v0 = fold_vec(v0, v2, mults_2v);
338
99
  v1 = fold_vec(v1, v3, mults_2v);
339
99
  if (len & (2*VL)) {
340
59
    v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_2v);
341
59
    v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_2v);
342
59
    p += 2*VL;
343
59
  }
344
114
less_than_2vl_remaining:
345
  /* Fewer than 2*VL bytes remain. */
346
114
  v0 = fold_vec(v0, v1, mults_1v);
347
114
  if (len & VL) {
348
51
    v0 = fold_vec(v0, VLOADU(p), mults_1v);
349
51
    p += VL;
350
51
  }
351
118
less_than_vl_remaining:
352
  /*
353
   * Fewer than VL bytes remain.  Reduce v0 (length VL bytes) to x0
354
   * (length 16 bytes) and fold in any 16-byte data segments that remain.
355
   */
356
#if VL == 16
357
  x0 = v0;
358
#else
359
  {
360
  #if VL == 32
361
    __m256i y0 = v0;
362
  #else
363
    const __m256i mults_256b =
364
0
      _mm256_set_epi64x(CRC32_X223_MODG, CRC32_X287_MODG,
365
0
            CRC32_X223_MODG, CRC32_X287_MODG);
366
0
    __m256i y0 = fold_vec256(_mm512_extracti64x4_epi64(v0, 0),
367
           _mm512_extracti64x4_epi64(v0, 1),
368
           mults_256b);
369
0
    if (len & 32) {
370
0
      y0 = fold_vec256(y0, _mm256_loadu_si256((const void *)p),
371
0
           mults_256b);
372
0
      p += 32;
373
0
    }
374
  #endif
375
0
    x0 = fold_vec128(_mm256_extracti128_si256(y0, 0),
376
         _mm256_extracti128_si256(y0, 1), mults_128b);
377
  }
378
0
  if (len & 16) {
379
0
    x0 = fold_vec128(x0, _mm_loadu_si128((const void *)p),
380
0
         mults_128b);
381
0
    p += 16;
382
0
  }
383
#endif
384
118
less_than_16_remaining:
385
118
  len &= 15;
386
387
  /* Handle any remainder of 1 to 15 bytes. */
388
118
  if (len)
389
103
    x0 = fold_lessthan16bytes(x0, p, len, mults_128b);
390
#if USE_AVX512
391
0
reduce_x0:
392
0
#endif
393
  /*
394
   * Multiply the remaining 128-bit message polynomial 'x0' by x^32, then
395
   * reduce it modulo the generator polynomial G.  This gives the CRC.
396
   *
397
   * This implementation matches that used in crc-pclmul-template.S from
398
   * https://lore.kernel.org/r/20250210174540.161705-4-ebiggers@kernel.org/
399
   * with the parameters n=32 and LSB_CRC=1 (what the gzip CRC uses).  See
400
   * there for a detailed explanation of the math used here.
401
   */
402
0
  x0 = _mm_xor_si128(_mm_clmulepi64_si128(x0, mults_128b, 0x10),
403
0
         _mm_bsrli_si128(x0, 8));
404
0
  x1 = _mm_clmulepi64_si128(x0, barrett_reduction_constants, 0x00);
405
0
  x1 = _mm_clmulepi64_si128(x1, barrett_reduction_constants, 0x10);
406
0
  x0 = _mm_xor_si128(x0, x1);
407
  return _mm_extract_epi32(x0, 2);
408
0
}
Unexecuted instantiation: crc32.c:crc32_x86_vpclmulqdq_avx512_vl512
Unexecuted instantiation: crc32.c:crc32_x86_vpclmulqdq_avx512_vl256
Unexecuted instantiation: crc32.c:crc32_x86_vpclmulqdq_avx2
crc32.c:crc32_x86_pclmulqdq_avx
Line
Count
Source
185
216
{
186
  /*
187
   * mults_{N}v are the vectors of multipliers for folding across N vec_t
188
   * vectors, i.e. N*VL*8 bits.  mults_128b are the two multipliers for
189
   * folding across 128 bits.  mults_128b differs from mults_1v when
190
   * VL != 16.  All multipliers are 64-bit, to match what pclmulqdq needs,
191
   * but since this is for CRC-32 only their low 32 bits are nonzero.
192
   * For more details, see scripts/gen-crc32-consts.py.
193
   */
194
216
  const vec_t mults_8v = MULTS_8V;
195
216
  const vec_t mults_4v = MULTS_4V;
196
216
  const vec_t mults_2v = MULTS_2V;
197
216
  const vec_t mults_1v = MULTS_1V;
198
216
  const __m128i mults_128b = _mm_set_epi64x(CRC32_X95_MODG, CRC32_X159_MODG);
199
216
  const __m128i barrett_reduction_constants =
200
216
    _mm_set_epi64x(CRC32_BARRETT_CONSTANT_2, CRC32_BARRETT_CONSTANT_1);
201
216
  vec_t v0, v1, v2, v3, v4, v5, v6, v7;
202
216
  __m128i x0 = _mm_cvtsi32_si128(crc);
203
216
  __m128i x1;
204
205
216
  if (len < 8*VL) {
206
123
    if (len < VL) {
207
98
      STATIC_ASSERT(VL == 16 || VL == 32 || VL == 64);
208
98
      if (len < 16) {
209
      #if USE_AVX512
210
        if (len < 4)
211
          return crc32_slice1(crc, p, len);
212
        /*
213
         * Handle 4 <= len <= 15 bytes by doing a masked
214
         * load, XOR'ing the current CRC with the first
215
         * 4 bytes, left-shifting by '16 - len' bytes to
216
         * align the result to the end of x0 (so that it
217
         * becomes the low-order coefficients of a
218
         * 128-bit polynomial), and then doing the usual
219
         * reduction from 128 bits to 32 bits.
220
         */
221
        x0 = _mm_xor_si128(
222
          x0, _mm_maskz_loadu_epi8((1 << len) - 1, p));
223
        x0 = _mm_shuffle_epi8(
224
          x0, _mm_loadu_si128((const void *)&shift_tab[len]));
225
        goto reduce_x0;
226
      #else
227
98
        return crc32_slice1(crc, p, len);
228
98
      #endif
229
98
      }
230
      /*
231
       * Handle 16 <= len < VL bytes where VL is 32 or 64.
232
       * Use 128-bit instructions so that these lengths aren't
233
       * slower with VL > 16 than with VL=16.
234
       */
235
0
      x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0);
236
0
      if (len >= 32) {
237
0
        x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 16)),
238
0
             mults_128b);
239
0
        if (len >= 48)
240
0
          x0 = fold_vec128(x0, _mm_loadu_si128((const void *)(p + 32)),
241
0
               mults_128b);
242
0
      }
243
0
      p += len & ~15;
244
0
      goto less_than_16_remaining;
245
98
    }
246
25
    v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0));
247
25
    if (len < 2*VL) {
248
4
      p += VL;
249
4
      goto less_than_vl_remaining;
250
4
    }
251
21
    v1 = VLOADU(p + 1*VL);
252
21
    if (len < 4*VL) {
253
15
      p += 2*VL;
254
15
      goto less_than_2vl_remaining;
255
15
    }
256
6
    v2 = VLOADU(p + 2*VL);
257
6
    v3 = VLOADU(p + 3*VL);
258
6
    p += 4*VL;
259
93
  } else {
260
    /*
261
     * If the length is large and the pointer is misaligned, align
262
     * it.  For smaller lengths, just take the misaligned load
263
     * penalty.  Note that on recent x86 CPUs, vmovdqu with an
264
     * aligned address is just as fast as vmovdqa, so there's no
265
     * need to use vmovdqa in the main loop.
266
     */
267
93
    if (len > 65536 && ((uintptr_t)p & (VL-1))) {
268
0
      size_t align = -(uintptr_t)p & (VL-1);
269
270
0
      len -= align;
271
0
      x0 = _mm_xor_si128(_mm_loadu_si128((const void *)p), x0);
272
0
      p += 16;
273
0
      if (align & 15) {
274
0
        x0 = fold_lessthan16bytes(x0, p, align & 15,
275
0
                mults_128b);
276
0
        p += align & 15;
277
0
        align &= ~15;
278
0
      }
279
0
      while (align) {
280
0
        x0 = fold_vec128(x0, *(const __m128i *)p,
281
0
             mults_128b);
282
0
        p += 16;
283
0
        align -= 16;
284
0
      }
285
0
      v0 = M128I_TO_VEC(x0);
286
    #  if VL == 32
287
      v0 = _mm256_inserti128_si256(v0, *(const __m128i *)p, 1);
288
    #  elif VL == 64
289
      v0 = _mm512_inserti32x4(v0, *(const __m128i *)p, 1);
290
      v0 = _mm512_inserti64x4(v0, *(const __m256i *)(p + 16), 1);
291
    #  endif
292
0
      p -= 16;
293
93
    } else {
294
93
      v0 = VXOR(VLOADU(p), M128I_TO_VEC(x0));
295
93
    }
296
93
    v1 = VLOADU(p + 1*VL);
297
93
    v2 = VLOADU(p + 2*VL);
298
93
    v3 = VLOADU(p + 3*VL);
299
93
    v4 = VLOADU(p + 4*VL);
300
93
    v5 = VLOADU(p + 5*VL);
301
93
    v6 = VLOADU(p + 6*VL);
302
93
    v7 = VLOADU(p + 7*VL);
303
93
    p += 8*VL;
304
305
    /*
306
     * This is the main loop, processing 8*VL bytes per iteration.
307
     * 4*VL is usually enough and would result in smaller code, but
308
     * Skylake and Cascade Lake need 8*VL to get full performance.
309
     */
310
181k
    while (len >= 16*VL) {
311
180k
      v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_8v);
312
180k
      v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_8v);
313
180k
      v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_8v);
314
180k
      v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_8v);
315
180k
      v4 = fold_vec(v4, VLOADU(p + 4*VL), mults_8v);
316
180k
      v5 = fold_vec(v5, VLOADU(p + 5*VL), mults_8v);
317
180k
      v6 = fold_vec(v6, VLOADU(p + 6*VL), mults_8v);
318
180k
      v7 = fold_vec(v7, VLOADU(p + 7*VL), mults_8v);
319
180k
      p += 8*VL;
320
180k
      len -= 8*VL;
321
180k
    }
322
323
    /* Fewer than 8*VL bytes remain. */
324
93
    v0 = fold_vec(v0, v4, mults_4v);
325
93
    v1 = fold_vec(v1, v5, mults_4v);
326
93
    v2 = fold_vec(v2, v6, mults_4v);
327
93
    v3 = fold_vec(v3, v7, mults_4v);
328
93
    if (len & (4*VL)) {
329
52
      v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_4v);
330
52
      v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_4v);
331
52
      v2 = fold_vec(v2, VLOADU(p + 2*VL), mults_4v);
332
52
      v3 = fold_vec(v3, VLOADU(p + 3*VL), mults_4v);
333
52
      p += 4*VL;
334
52
    }
335
93
  }
336
  /* Fewer than 4*VL bytes remain. */
337
99
  v0 = fold_vec(v0, v2, mults_2v);
338
99
  v1 = fold_vec(v1, v3, mults_2v);
339
99
  if (len & (2*VL)) {
340
59
    v0 = fold_vec(v0, VLOADU(p + 0*VL), mults_2v);
341
59
    v1 = fold_vec(v1, VLOADU(p + 1*VL), mults_2v);
342
59
    p += 2*VL;
343
59
  }
344
114
less_than_2vl_remaining:
345
  /* Fewer than 2*VL bytes remain. */
346
114
  v0 = fold_vec(v0, v1, mults_1v);
347
114
  if (len & VL) {
348
51
    v0 = fold_vec(v0, VLOADU(p), mults_1v);
349
51
    p += VL;
350
51
  }
351
118
less_than_vl_remaining:
352
  /*
353
   * Fewer than VL bytes remain.  Reduce v0 (length VL bytes) to x0
354
   * (length 16 bytes) and fold in any 16-byte data segments that remain.
355
   */
356
118
#if VL == 16
357
118
  x0 = v0;
358
#else
359
  {
360
  #if VL == 32
361
    __m256i y0 = v0;
362
  #else
363
    const __m256i mults_256b =
364
      _mm256_set_epi64x(CRC32_X223_MODG, CRC32_X287_MODG,
365
            CRC32_X223_MODG, CRC32_X287_MODG);
366
    __m256i y0 = fold_vec256(_mm512_extracti64x4_epi64(v0, 0),
367
           _mm512_extracti64x4_epi64(v0, 1),
368
           mults_256b);
369
    if (len & 32) {
370
      y0 = fold_vec256(y0, _mm256_loadu_si256((const void *)p),
371
           mults_256b);
372
      p += 32;
373
    }
374
  #endif
375
    x0 = fold_vec128(_mm256_extracti128_si256(y0, 0),
376
         _mm256_extracti128_si256(y0, 1), mults_128b);
377
  }
378
  if (len & 16) {
379
    x0 = fold_vec128(x0, _mm_loadu_si128((const void *)p),
380
         mults_128b);
381
    p += 16;
382
  }
383
#endif
384
118
less_than_16_remaining:
385
118
  len &= 15;
386
387
  /* Handle any remainder of 1 to 15 bytes. */
388
118
  if (len)
389
103
    x0 = fold_lessthan16bytes(x0, p, len, mults_128b);
390
#if USE_AVX512
391
reduce_x0:
392
#endif
393
  /*
394
   * Multiply the remaining 128-bit message polynomial 'x0' by x^32, then
395
   * reduce it modulo the generator polynomial G.  This gives the CRC.
396
   *
397
   * This implementation matches that used in crc-pclmul-template.S from
398
   * https://lore.kernel.org/r/20250210174540.161705-4-ebiggers@kernel.org/
399
   * with the parameters n=32 and LSB_CRC=1 (what the gzip CRC uses).  See
400
   * there for a detailed explanation of the math used here.
401
   */
402
118
  x0 = _mm_xor_si128(_mm_clmulepi64_si128(x0, mults_128b, 0x10),
403
118
         _mm_bsrli_si128(x0, 8));
404
118
  x1 = _mm_clmulepi64_si128(x0, barrett_reduction_constants, 0x00);
405
118
  x1 = _mm_clmulepi64_si128(x1, barrett_reduction_constants, 0x10);
406
118
  x0 = _mm_xor_si128(x0, x1);
407
  return _mm_extract_epi32(x0, 2);
408
118
}
Unexecuted instantiation: crc32.c:crc32_x86_pclmulqdq
409
410
#undef vec_t
411
#undef fold_vec
412
#undef VLOADU
413
#undef VXOR
414
#undef M128I_TO_VEC
415
#undef MULTS
416
#undef MULTS_8V
417
#undef MULTS_4V
418
#undef MULTS_2V
419
#undef MULTS_1V
420
421
#undef SUFFIX
422
#undef ATTRIBUTES
423
#undef VL
424
#undef USE_AVX512