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 Ssse3 {
22 mul128: &'static Mul128,
23 skew: &'static Skew,
24}
25
26impl Ssse3 {
27 pub fn new() -> Self {
35 cpufeatures::new!(has_ssse3_for_engine, "ssse3");
36 assert!(has_ssse3_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 Ssse3 {
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_ssse3(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_ssse3(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_ssse3(x, log_m);
78 }
79 }
80
81 fn eval_poly(erasures: &mut [GfElement; GF_ORDER], truncated_size: usize) {
82 unsafe { Self::eval_poly_ssse3(erasures, truncated_size) }
84 }
85}
86
87impl Default for Ssse3 {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl Ssse3 {
100 #[target_feature(enable = "ssse3")]
101 unsafe fn mul_ssse3(&self, x: &mut [[u8; SHARD_CHUNK_BYTES]], log_m: GfElement) {
102 let lut = &self.mul128[log_m as usize];
103
104 for chunk in x.iter_mut() {
105 let x_ptr = chunk.as_mut_ptr().cast::<__m128i>();
106 unsafe {
108 let x0_lo = _mm_loadu_si128(x_ptr);
109 let x1_lo = _mm_loadu_si128(x_ptr.add(1));
110 let x0_hi = _mm_loadu_si128(x_ptr.add(2));
111 let x1_hi = _mm_loadu_si128(x_ptr.add(3));
112 let (prod0_lo, prod0_hi) = Self::mul_128(x0_lo, x0_hi, lut);
113 let (prod1_lo, prod1_hi) = Self::mul_128(x1_lo, x1_hi, lut);
114 _mm_storeu_si128(x_ptr, prod0_lo);
115 _mm_storeu_si128(x_ptr.add(1), prod1_lo);
116 _mm_storeu_si128(x_ptr.add(2), prod0_hi);
117 _mm_storeu_si128(x_ptr.add(3), prod1_hi);
118 }
119 }
120 }
121
122 #[inline(always)]
124 fn mul_128(value_lo: __m128i, value_hi: __m128i, lut: &Multiply128lutT) -> (__m128i, __m128i) {
125 let mut prod_lo: __m128i;
126 let mut prod_hi: __m128i;
127
128 unsafe {
130 let t0_lo = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.lo[0]).cast::<__m128i>());
131 let t1_lo = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.lo[1]).cast::<__m128i>());
132 let t2_lo = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.lo[2]).cast::<__m128i>());
133 let t3_lo = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.lo[3]).cast::<__m128i>());
134
135 let t0_hi = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.hi[0]).cast::<__m128i>());
136 let t1_hi = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.hi[1]).cast::<__m128i>());
137 let t2_hi = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.hi[2]).cast::<__m128i>());
138 let t3_hi = _mm_loadu_si128(core::ptr::from_ref::<u128>(&lut.hi[3]).cast::<__m128i>());
139
140 let clr_mask = _mm_set1_epi8(0x0f);
141
142 let data_0 = _mm_and_si128(value_lo, clr_mask);
143 prod_lo = _mm_shuffle_epi8(t0_lo, data_0);
144 prod_hi = _mm_shuffle_epi8(t0_hi, data_0);
145
146 let data_1 = _mm_and_si128(_mm_srli_epi64(value_lo, 4), clr_mask);
147 prod_lo = _mm_xor_si128(prod_lo, _mm_shuffle_epi8(t1_lo, data_1));
148 prod_hi = _mm_xor_si128(prod_hi, _mm_shuffle_epi8(t1_hi, data_1));
149
150 let data_0 = _mm_and_si128(value_hi, clr_mask);
151 prod_lo = _mm_xor_si128(prod_lo, _mm_shuffle_epi8(t2_lo, data_0));
152 prod_hi = _mm_xor_si128(prod_hi, _mm_shuffle_epi8(t2_hi, data_0));
153
154 let data_1 = _mm_and_si128(_mm_srli_epi64(value_hi, 4), clr_mask);
155 prod_lo = _mm_xor_si128(prod_lo, _mm_shuffle_epi8(t3_lo, data_1));
156 prod_hi = _mm_xor_si128(prod_hi, _mm_shuffle_epi8(t3_hi, data_1));
157 }
158
159 (prod_lo, prod_hi)
160 }
161
162 #[inline(always)]
165 fn muladd_128(
166 mut x_lo: __m128i,
167 mut x_hi: __m128i,
168 y_lo: __m128i,
169 y_hi: __m128i,
170 lut: &Multiply128lutT,
171 ) -> (__m128i, __m128i) {
172 let (prod_lo, prod_hi) = Self::mul_128(y_lo, y_hi, lut);
173 unsafe {
175 x_lo = _mm_xor_si128(x_lo, prod_lo);
176 x_hi = _mm_xor_si128(x_hi, prod_hi);
177 }
178 (x_lo, x_hi)
179 }
180}
181
182impl Ssse3 {
186 #[inline(always)]
188 fn fftb_128(
189 &self,
190 x: &mut [u8; SHARD_CHUNK_BYTES],
191 y: &mut [u8; SHARD_CHUNK_BYTES],
192 log_m: GfElement,
193 ) {
194 let lut = &self.mul128[log_m as usize];
195 let x_ptr = x.as_mut_ptr().cast::<__m128i>();
196 let y_ptr = y.as_mut_ptr().cast::<__m128i>();
197 unsafe {
199 let mut x0_lo = _mm_loadu_si128(x_ptr);
200 let mut x1_lo = _mm_loadu_si128(x_ptr.add(1));
201 let mut x0_hi = _mm_loadu_si128(x_ptr.add(2));
202 let mut x1_hi = _mm_loadu_si128(x_ptr.add(3));
203
204 let mut y0_lo = _mm_loadu_si128(y_ptr);
205 let mut y1_lo = _mm_loadu_si128(y_ptr.add(1));
206 let mut y0_hi = _mm_loadu_si128(y_ptr.add(2));
207 let mut y1_hi = _mm_loadu_si128(y_ptr.add(3));
208
209 (x0_lo, x0_hi) = Self::muladd_128(x0_lo, x0_hi, y0_lo, y0_hi, lut);
210 (x1_lo, x1_hi) = Self::muladd_128(x1_lo, x1_hi, y1_lo, y1_hi, lut);
211
212 _mm_storeu_si128(x_ptr, x0_lo);
213 _mm_storeu_si128(x_ptr.add(1), x1_lo);
214 _mm_storeu_si128(x_ptr.add(2), x0_hi);
215 _mm_storeu_si128(x_ptr.add(3), x1_hi);
216
217 y0_lo = _mm_xor_si128(y0_lo, x0_lo);
218 y1_lo = _mm_xor_si128(y1_lo, x1_lo);
219 y0_hi = _mm_xor_si128(y0_hi, x0_hi);
220 y1_hi = _mm_xor_si128(y1_hi, x1_hi);
221
222 _mm_storeu_si128(y_ptr, y0_lo);
223 _mm_storeu_si128(y_ptr.add(1), y1_lo);
224 _mm_storeu_si128(y_ptr.add(2), y0_hi);
225 _mm_storeu_si128(y_ptr.add(3), y1_hi);
226 }
227 }
228
229 #[inline(always)]
231 fn fft_butterfly_partial(
232 &self,
233 x: &mut [[u8; SHARD_CHUNK_BYTES]],
234 y: &mut [[u8; SHARD_CHUNK_BYTES]],
235 log_m: GfElement,
236 ) {
237 for (x_chunk, y_chunk) in zip(x.iter_mut(), y.iter_mut()) {
238 self.fftb_128(x_chunk, y_chunk, log_m);
239 }
240 }
241
242 #[inline(always)]
243 fn fft_butterfly_two_layers(
244 &self,
245 data: &mut ShardsRefMut<'_>,
246 pos: usize,
247 dist: usize,
248 log_m01: GfElement,
249 log_m23: GfElement,
250 log_m02: GfElement,
251 ) {
252 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
253
254 if log_m02 == GF_MODULUS {
257 utils::xor(s2, s0);
258 utils::xor(s3, s1);
259 } else {
260 self.fft_butterfly_partial(s0, s2, log_m02);
261 self.fft_butterfly_partial(s1, s3, log_m02);
262 }
263
264 if log_m01 == GF_MODULUS {
267 utils::xor(s1, s0);
268 } else {
269 self.fft_butterfly_partial(s0, s1, log_m01);
270 }
271
272 if log_m23 == GF_MODULUS {
273 utils::xor(s3, s2);
274 } else {
275 self.fft_butterfly_partial(s2, s3, log_m23);
276 }
277 }
278
279 #[target_feature(enable = "ssse3")]
280 unsafe fn fft_private_ssse3(
281 &self,
282 data: &mut ShardsRefMut<'_>,
283 pos: usize,
284 size: usize,
285 truncated_size: usize,
286 skew_delta: usize,
287 ) {
288 self.fft_private(data, pos, size, truncated_size, skew_delta);
289 }
290
291 #[inline(always)]
292 fn fft_private(
293 &self,
294 data: &mut ShardsRefMut<'_>,
295 pos: usize,
296 size: usize,
297 truncated_size: usize,
298 skew_delta: usize,
299 ) {
300 let mut dist4 = size;
303 let mut dist = size >> 2;
304 while dist != 0 {
305 let mut r = 0;
306 while r < truncated_size {
307 let base = r + dist + skew_delta - 1;
308
309 let log_m01 = self.skew[base];
310 let log_m02 = self.skew[base + dist];
311 let log_m23 = self.skew[base + dist * 2];
312
313 for i in r..r + dist {
314 self.fft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
315 }
316
317 r += dist4;
318 }
319 dist4 = dist;
320 dist >>= 2;
321 }
322
323 if dist4 == 2 {
326 let mut r = 0;
327 while r < truncated_size {
328 let log_m = self.skew[r + skew_delta];
329
330 let (x, y) = data.dist2_mut(pos + r, 1);
331
332 if log_m == GF_MODULUS {
333 utils::xor(y, x);
334 } else {
335 self.fft_butterfly_partial(x, y, log_m);
336 }
337
338 r += 2;
339 }
340 }
341 }
342}
343
344impl Ssse3 {
348 #[inline(always)]
350 fn ifftb_128(
351 &self,
352 x: &mut [u8; SHARD_CHUNK_BYTES],
353 y: &mut [u8; SHARD_CHUNK_BYTES],
354 log_m: GfElement,
355 ) {
356 let lut = &self.mul128[log_m as usize];
357 let x_ptr = x.as_mut_ptr().cast::<__m128i>();
358 let y_ptr = y.as_mut_ptr().cast::<__m128i>();
359
360 unsafe {
362 let mut x0_lo = _mm_loadu_si128(x_ptr);
363 let mut x1_lo = _mm_loadu_si128(x_ptr.add(1));
364 let mut x0_hi = _mm_loadu_si128(x_ptr.add(2));
365 let mut x1_hi = _mm_loadu_si128(x_ptr.add(3));
366
367 let mut y0_lo = _mm_loadu_si128(y_ptr);
368 let mut y1_lo = _mm_loadu_si128(y_ptr.add(1));
369 let mut y0_hi = _mm_loadu_si128(y_ptr.add(2));
370 let mut y1_hi = _mm_loadu_si128(y_ptr.add(3));
371
372 y0_lo = _mm_xor_si128(y0_lo, x0_lo);
373 y1_lo = _mm_xor_si128(y1_lo, x1_lo);
374 y0_hi = _mm_xor_si128(y0_hi, x0_hi);
375 y1_hi = _mm_xor_si128(y1_hi, x1_hi);
376
377 _mm_storeu_si128(y_ptr, y0_lo);
378 _mm_storeu_si128(y_ptr.add(1), y1_lo);
379 _mm_storeu_si128(y_ptr.add(2), y0_hi);
380 _mm_storeu_si128(y_ptr.add(3), y1_hi);
381
382 (x0_lo, x0_hi) = Self::muladd_128(x0_lo, x0_hi, y0_lo, y0_hi, lut);
383 (x1_lo, x1_hi) = Self::muladd_128(x1_lo, x1_hi, y1_lo, y1_hi, lut);
384
385 _mm_storeu_si128(x_ptr, x0_lo);
386 _mm_storeu_si128(x_ptr.add(1), x1_lo);
387 _mm_storeu_si128(x_ptr.add(2), x0_hi);
388 _mm_storeu_si128(x_ptr.add(3), x1_hi);
389 }
390 }
391
392 #[inline(always)]
393 fn ifft_butterfly_partial(
394 &self,
395 x: &mut [[u8; SHARD_CHUNK_BYTES]],
396 y: &mut [[u8; SHARD_CHUNK_BYTES]],
397 log_m: GfElement,
398 ) {
399 for (x_chunk, y_chunk) in zip(x.iter_mut(), y.iter_mut()) {
400 self.ifftb_128(x_chunk, y_chunk, log_m);
401 }
402 }
403
404 #[inline(always)]
405 fn ifft_butterfly_two_layers(
406 &self,
407 data: &mut ShardsRefMut<'_>,
408 pos: usize,
409 dist: usize,
410 log_m01: GfElement,
411 log_m23: GfElement,
412 log_m02: GfElement,
413 ) {
414 let (s0, s1, s2, s3) = data.dist4_mut(pos, dist);
415
416 if log_m01 == GF_MODULUS {
419 utils::xor(s1, s0);
420 } else {
421 self.ifft_butterfly_partial(s0, s1, log_m01);
422 }
423
424 if log_m23 == GF_MODULUS {
425 utils::xor(s3, s2);
426 } else {
427 self.ifft_butterfly_partial(s2, s3, log_m23);
428 }
429
430 if log_m02 == GF_MODULUS {
433 utils::xor(s2, s0);
434 utils::xor(s3, s1);
435 } else {
436 self.ifft_butterfly_partial(s0, s2, log_m02);
437 self.ifft_butterfly_partial(s1, s3, log_m02);
438 }
439 }
440
441 #[target_feature(enable = "ssse3")]
442 unsafe fn ifft_private_ssse3(
443 &self,
444 data: &mut ShardsRefMut<'_>,
445 pos: usize,
446 size: usize,
447 truncated_size: usize,
448 skew_delta: usize,
449 ) {
450 self.ifft_private(data, pos, size, truncated_size, skew_delta);
451 }
452
453 #[inline(always)]
454 fn ifft_private(
455 &self,
456 data: &mut ShardsRefMut<'_>,
457 pos: usize,
458 size: usize,
459 truncated_size: usize,
460 skew_delta: usize,
461 ) {
462 let mut dist = 1;
465 let mut dist4 = 4;
466 while dist4 <= size {
467 let mut r = 0;
468 while r < truncated_size {
469 let base = r + dist + skew_delta - 1;
470
471 let log_m01 = self.skew[base];
472 let log_m02 = self.skew[base + dist];
473 let log_m23 = self.skew[base + dist * 2];
474
475 for i in r..r + dist {
476 self.ifft_butterfly_two_layers(data, pos + i, dist, log_m01, log_m23, log_m02);
477 }
478
479 r += dist4;
480 }
481 dist = dist4;
482 dist4 <<= 2;
483 }
484
485 if dist < size {
488 let log_m = self.skew[dist + skew_delta - 1];
489 if log_m == GF_MODULUS {
490 utils::xor_within(data, pos + dist, pos, dist);
491 } else {
492 let (mut a, mut b) = data.split_at_mut(pos + dist);
493 for i in 0..dist {
494 self.ifft_butterfly_partial(
495 &mut a[pos + i], &mut b[i], log_m,
498 );
499 }
500 }
501 }
502 }
503}
504
505impl Ssse3 {
509 #[target_feature(enable = "ssse3")]
510 unsafe fn eval_poly_ssse3(erasures: &mut [GfElement; GF_ORDER], truncated_size: usize) {
511 utils::eval_poly(erasures, truncated_size);
512 }
513}
514
515