1use crate::reed_solomon::engine::{
2 tables::{self, Mul128, Multiply128lutT, Skew},
3 utils, Engine, GfElement, ShardsRefMut, GF_MODULUS, GF_ORDER, SHARD_CHUNK_BYTES,
4};
5#[cfg(target_arch = "x86")]
6use core::arch::x86::*;
7#[cfg(target_arch = "x86_64")]
8use core::arch::x86_64::*;
9use core::iter::zip;
10
11#[derive(Clone, Copy)]
21pub struct Avx2 {
22 mul128: &'static Mul128,
23 skew: &'static Skew,
24}
25
26impl Avx2 {
27 pub fn new() -> Self {
35 cpufeatures::new!(has_avx2_for_engine, "avx2");
36 assert!(has_avx2_for_engine::get());
37
38 let mul128 = tables::get_mul128();
39 let skew = tables::get_skew();
40
41 Self { mul128, skew }
42 }
43}
44
45impl Engine for Avx2 {
46 fn fft(
47 &self,
48 data: &mut ShardsRefMut<'_>,
49 pos: usize,
50 size: usize,
51 truncated_size: usize,
52 skew_delta: usize,
53 ) {
54 unsafe {
56 self.fft_private_avx2(data, pos, size, truncated_size, skew_delta);
57 }
58 }
59
60 fn ifft(
61 &self,
62 data: &mut ShardsRefMut<'_>,
63 pos: usize,
64 size: usize,
65 truncated_size: usize,
66 skew_delta: usize,
67 ) {
68 unsafe {
70 self.ifft_private_avx2(data, pos, size, truncated_size, skew_delta);
71 }
72 }
73
74 fn mul(&self, x: &mut [[u8; SHARD_CHUNK_BYTES]], log_m: GfElement) {
75 unsafe {
77 self.mul_avx2(x, log_m);
78 }
79 }
80
81 fn eval_poly(erasures: &mut [GfElement; GF_ORDER], truncated_size: usize) {
82 unsafe { Self::eval_poly_avx2(erasures, truncated_size) }
84 }
85}
86
87impl Default for Avx2 {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96#[derive(Copy, Clone)]
100struct LutAvx2 {
101 t0_lo: __m256i,
102 t1_lo: __m256i,
103 t2_lo: __m256i,
104 t3_lo: __m256i,
105 t0_hi: __m256i,
106 t1_hi: __m256i,
107 t2_hi: __m256i,
108 t3_hi: __m256i,
109}
110
111impl From<&Multiply128lutT> for LutAvx2 {
112 #[inline(always)]
113 fn from(lut: &Multiply128lutT) -> Self {
114 unsafe {
116 Self {
117 t0_lo: _mm256_broadcastsi128_si256(_mm_loadu_si128(
118 core::ptr::from_ref::<u128>(&lut.lo[0]).cast::<__m128i>(),
119 )),
120 t1_lo: _mm256_broadcastsi128_si256(_mm_loadu_si128(
121 core::ptr::from_ref::<u128>(&lut.lo[1]).cast::<__m128i>(),
122 )),
123 t2_lo: _mm256_broadcastsi128_si256(_mm_loadu_si128(
124 core::ptr::from_ref::<u128>(&lut.lo[2]).cast::<__m128i>(),
125 )),
126 t3_lo: _mm256_broadcastsi128_si256(_mm_loadu_si128(
127 core::ptr::from_ref::<u128>(&lut.lo[3]).cast::<__m128i>(),
128 )),
129 t0_hi: _mm256_broadcastsi128_si256(_mm_loadu_si128(
130 core::ptr::from_ref::<u128>(&lut.hi[0]).cast::<__m128i>(),
131 )),
132 t1_hi: _mm256_broadcastsi128_si256(_mm_loadu_si128(
133 core::ptr::from_ref::<u128>(&lut.hi[1]).cast::<__m128i>(),
134 )),
135 t2_hi: _mm256_broadcastsi128_si256(_mm_loadu_si128(
136 core::ptr::from_ref::<u128>(&lut.hi[2]).cast::<__m128i>(),
137 )),
138 t3_hi: _mm256_broadcastsi128_si256(_mm_loadu_si128(
139 core::ptr::from_ref::<u128>(&lut.hi[3]).cast::<__m128i>(),
140 )),
141 }
142 }
143 }
144}
145
146impl Avx2 {
147 #[target_feature(enable = "avx2")]
148 unsafe fn mul_avx2(&self, x: &mut [[u8; SHARD_CHUNK_BYTES]], log_m: GfElement) {
149 let lut = &self.mul128[log_m as usize];
150 let lut_avx2 = LutAvx2::from(lut);
151
152 for chunk in x.iter_mut() {
153 let x_ptr = chunk.as_mut_ptr().cast::<__m256i>();
154 unsafe {
156 let x_lo = _mm256_loadu_si256(x_ptr);
157 let x_hi = _mm256_loadu_si256(x_ptr.add(1));
158 let (prod_lo, prod_hi) = Self::mul_256(x_lo, x_hi, lut_avx2);
159 _mm256_storeu_si256(x_ptr, prod_lo);
160 _mm256_storeu_si256(x_ptr.add(1), prod_hi);
161 }
162 }
163 }
164
165 #[inline(always)]
167 fn mul_256(value_lo: __m256i, value_hi: __m256i, lut_avx2: LutAvx2) -> (__m256i, __m256i) {
168 let mut prod_lo: __m256i;
169 let mut prod_hi: __m256i;
170
171 unsafe {
173 let clr_mask = _mm256_set1_epi8(0x0f);
174
175 let data_0 = _mm256_and_si256(value_lo, clr_mask);
176 prod_lo = _mm256_shuffle_epi8(lut_avx2.t0_lo, data_0);
177 prod_hi = _mm256_shuffle_epi8(lut_avx2.t0_hi, data_0);
178
179 let data_1 = _mm256_and_si256(_mm256_srli_epi64(value_lo, 4), clr_mask);
180 prod_lo = _mm256_xor_si256(prod_lo, _mm256_shuffle_epi8(lut_avx2.t1_lo, data_1));
181 prod_hi = _mm256_xor_si256(prod_hi, _mm256_shuffle_epi8(lut_avx2.t1_hi, data_1));
182
183 let data_0 = _mm256_and_si256(value_hi, clr_mask);
184 prod_lo = _mm256_xor_si256(prod_lo, _mm256_shuffle_epi8(lut_avx2.t2_lo, data_0));
185 prod_hi = _mm256_xor_si256(prod_hi, _mm256_shuffle_epi8(lut_avx2.t2_hi, data_0));
186
187 let data_1 = _mm256_and_si256(_mm256_srli_epi64(value_hi, 4), clr_mask);
188 prod_lo = _mm256_xor_si256(prod_lo, _mm256_shuffle_epi8(lut_avx2.t3_lo, data_1));
189 prod_hi = _mm256_xor_si256(prod_hi, _mm256_shuffle_epi8(lut_avx2.t3_hi, data_1));
190 }
191
192 (prod_lo, prod_hi)
193 }
194
195 #[inline(always)]
198 fn muladd_256(
199 mut x_lo: __m256i,
200 mut x_hi: __m256i,
201 y_lo: __m256i,
202 y_hi: __m256i,
203 lut_avx2: LutAvx2,
204 ) -> (__m256i, __m256i) {
205 let (prod_lo, prod_hi) = Self::mul_256(y_lo, y_hi, lut_avx2);
206 unsafe {
208 x_lo = _mm256_xor_si256(x_lo, prod_lo);
209 x_hi = _mm256_xor_si256(x_hi, prod_hi);
210 }
211 (x_lo, x_hi)
212 }
213}
214
215impl Avx2 {
219 #[inline(always)]
221 fn fftb_256(
222 x: &mut [u8; SHARD_CHUNK_BYTES],
223 y: &mut [u8; SHARD_CHUNK_BYTES],
224 lut_avx2: LutAvx2,
225 ) {
226 let x_ptr = x.as_mut_ptr().cast::<__m256i>();
227 let y_ptr = y.as_mut_ptr().cast::<__m256i>();
228
229 unsafe {
231 let mut x_lo = _mm256_loadu_si256(x_ptr);
232 let mut x_hi = _mm256_loadu_si256(x_ptr.add(1));
233
234 let mut y_lo = _mm256_loadu_si256(y_ptr);
235 let mut y_hi = _mm256_loadu_si256(y_ptr.add(1));
236
237 (x_lo, x_hi) = Self::muladd_256(x_lo, x_hi, y_lo, y_hi, lut_avx2);
238
239 _mm256_storeu_si256(x_ptr, x_lo);
240 _mm256_storeu_si256(x_ptr.add(1), x_hi);
241
242 y_lo = _mm256_xor_si256(y_lo, x_lo);
243 y_hi = _mm256_xor_si256(y_hi, x_hi);
244
245 _mm256_storeu_si256(y_ptr, y_lo);
246 _mm256_storeu_si256(y_ptr.add(1), y_hi);
247 }
248 }
249
250 #[inline(always)]
252 fn fft_butterfly_partial(
253 &self,
254 x: &mut [[u8; SHARD_CHUNK_BYTES]],
255 y: &mut [[u8; SHARD_CHUNK_BYTES]],
256 log_m: GfElement,
257 ) {
258 let lut = &self.mul128[log_m as usize];
259 let lut_avx2 = LutAvx2::from(lut);
260
261 for (x_chunk, y_chunk) in zip(x.iter_mut(), y.iter_mut()) {
262 Self::fftb_256(x_chunk, y_chunk, lut_avx2);
263 }
264 }
265
266 #[inline(always)]
267 fn fft_butterfly_two_layers(
268 &self,
269 data: &mut ShardsRefMut<'_>,
270 pos: usize,
271 dist: usize,
272 log_m01: GfElement,
273 log_m23: GfElement,
274 log_m02: GfElement,
275 ) {
276 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
277
278 if log_m02 == GF_MODULUS {
281 utils::xor(s2, s0);
282 utils::xor(s3, s1);
283 } else {
284 self.fft_butterfly_partial(s0, s2, log_m02);
285 self.fft_butterfly_partial(s1, s3, log_m02);
286 }
287
288 if log_m01 == GF_MODULUS {
291 utils::xor(s1, s0);
292 } else {
293 self.fft_butterfly_partial(s0, s1, log_m01);
294 }
295
296 if log_m23 == GF_MODULUS {
297 utils::xor(s3, s2);
298 } else {
299 self.fft_butterfly_partial(s2, s3, log_m23);
300 }
301 }
302
303 #[target_feature(enable = "avx2")]
304 unsafe fn fft_private_avx2(
305 &self,
306 data: &mut ShardsRefMut<'_>,
307 pos: usize,
308 size: usize,
309 truncated_size: usize,
310 skew_delta: usize,
311 ) {
312 self.fft_private(data, pos, size, truncated_size, skew_delta);
313 }
314
315 #[inline(always)]
316 fn fft_private(
317 &self,
318 data: &mut ShardsRefMut<'_>,
319 pos: usize,
320 size: usize,
321 truncated_size: usize,
322 skew_delta: usize,
323 ) {
324 let mut dist4 = size;
327 let mut dist = size >> 2;
328 while dist != 0 {
329 let mut r = 0;
330 while r < truncated_size {
331 let base = r + dist + skew_delta - 1;
332
333 let log_m01 = self.skew[base];
334 let log_m02 = self.skew[base + dist];
335 let log_m23 = self.skew[base + dist * 2];
336
337 for i in r..r + dist {
338 self.fft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
339 }
340
341 r += dist4;
342 }
343 dist4 = dist;
344 dist >>= 2;
345 }
346
347 if dist4 == 2 {
350 let mut r = 0;
351 while r < truncated_size {
352 let log_m = self.skew[r + skew_delta];
353
354 let (x, y) = data.dist2_mut(pos + r, 1);
355
356 if log_m == GF_MODULUS {
357 utils::xor(y, x);
358 } else {
359 self.fft_butterfly_partial(x, y, log_m);
360 }
361
362 r += 2;
363 }
364 }
365 }
366}
367
368impl Avx2 {
372 #[inline(always)]
374 fn ifftb_256(
375 x: &mut [u8; SHARD_CHUNK_BYTES],
376 y: &mut [u8; SHARD_CHUNK_BYTES],
377 lut_avx2: LutAvx2,
378 ) {
379 let x_ptr = x.as_mut_ptr().cast::<__m256i>();
380 let y_ptr = y.as_mut_ptr().cast::<__m256i>();
381
382 unsafe {
384 let mut x_lo = _mm256_loadu_si256(x_ptr);
385 let mut x_hi = _mm256_loadu_si256(x_ptr.add(1));
386
387 let mut y_lo = _mm256_loadu_si256(y_ptr);
388 let mut y_hi = _mm256_loadu_si256(y_ptr.add(1));
389
390 y_lo = _mm256_xor_si256(y_lo, x_lo);
391 y_hi = _mm256_xor_si256(y_hi, x_hi);
392
393 _mm256_storeu_si256(y_ptr, y_lo);
394 _mm256_storeu_si256(y_ptr.add(1), y_hi);
395
396 (x_lo, x_hi) = Self::muladd_256(x_lo, x_hi, y_lo, y_hi, lut_avx2);
397
398 _mm256_storeu_si256(x_ptr, x_lo);
399 _mm256_storeu_si256(x_ptr.add(1), x_hi);
400 }
401 }
402
403 #[inline(always)]
404 fn ifft_butterfly_partial(
405 &self,
406 x: &mut [[u8; SHARD_CHUNK_BYTES]],
407 y: &mut [[u8; SHARD_CHUNK_BYTES]],
408 log_m: GfElement,
409 ) {
410 let lut = &self.mul128[log_m as usize];
411 let lut_avx2 = LutAvx2::from(lut);
412
413 for (x_chunk, y_chunk) in zip(x.iter_mut(), y.iter_mut()) {
414 Self::ifftb_256(x_chunk, y_chunk, lut_avx2);
415 }
416 }
417
418 #[inline(always)]
419 fn ifft_butterfly_two_layers(
420 &self,
421 data: &mut ShardsRefMut<'_>,
422 pos: usize,
423 dist: usize,
424 log_m01: GfElement,
425 log_m23: GfElement,
426 log_m02: GfElement,
427 ) {
428 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
429
430 if log_m01 == GF_MODULUS {
433 utils::xor(s1, s0);
434 } else {
435 self.ifft_butterfly_partial(s0, s1, log_m01);
436 }
437
438 if log_m23 == GF_MODULUS {
439 utils::xor(s3, s2);
440 } else {
441 self.ifft_butterfly_partial(s2, s3, log_m23);
442 }
443
444 if log_m02 == GF_MODULUS {
447 utils::xor(s2, s0);
448 utils::xor(s3, s1);
449 } else {
450 self.ifft_butterfly_partial(s0, s2, log_m02);
451 self.ifft_butterfly_partial(s1, s3, log_m02);
452 }
453 }
454
455 #[target_feature(enable = "avx2")]
456 unsafe fn ifft_private_avx2(
457 &self,
458 data: &mut ShardsRefMut<'_>,
459 pos: usize,
460 size: usize,
461 truncated_size: usize,
462 skew_delta: usize,
463 ) {
464 self.ifft_private(data, pos, size, truncated_size, skew_delta);
465 }
466
467 #[inline(always)]
468 fn ifft_private(
469 &self,
470 data: &mut ShardsRefMut<'_>,
471 pos: usize,
472 size: usize,
473 truncated_size: usize,
474 skew_delta: usize,
475 ) {
476 let mut dist = 1;
479 let mut dist4 = 4;
480 while dist4 <= size {
481 let mut r = 0;
482 while r < truncated_size {
483 let base = r + dist + skew_delta - 1;
484
485 let log_m01 = self.skew[base];
486 let log_m02 = self.skew[base + dist];
487 let log_m23 = self.skew[base + dist * 2];
488
489 for i in r..r + dist {
490 self.ifft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
491 }
492
493 r += dist4;
494 }
495 dist = dist4;
496 dist4 <<= 2;
497 }
498
499 if dist < size {
502 let log_m = self.skew[dist + skew_delta - 1];
503 if log_m == GF_MODULUS {
504 utils::xor_within(data, pos + dist, pos, dist);
505 } else {
506 let (mut a, mut b) = data.split_at_mut(pos + dist);
507 for i in 0..dist {
508 self.ifft_butterfly_partial(
509 &mut a[pos + i], &mut b[i], log_m,
512 );
513 }
514 }
515 }
516 }
517}
518
519impl Avx2 {
523 #[target_feature(enable = "avx2")]
524 unsafe fn eval_poly_avx2(erasures: &mut [GfElement; GF_ORDER], truncated_size: usize) {
525 utils::eval_poly(erasures, truncated_size);
526 }
527}
528
529