leopard/LeopardFF16.cpp

1341 lines
37 KiB
C++
Raw Normal View History

2017-05-18 03:06:13 +00:00
/*
2017-05-25 09:24:15 +00:00
Copyright (c) 2017 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
2017-05-27 02:51:30 +00:00
* Neither the name of Leopard-RS nor the names of its contributors may be
2017-05-25 09:24:15 +00:00
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
2017-05-18 03:06:13 +00:00
*/
2017-05-27 02:51:30 +00:00
#include "LeopardFF16.h"
2017-05-27 03:10:53 +00:00
#ifdef LEO_HAS_FF16
2017-05-18 03:06:13 +00:00
#include <string.h>
2017-05-27 02:51:30 +00:00
namespace leopard { namespace ff16 {
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// Datatypes and Constants
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// Basis used for generating logarithm tables
2017-05-28 01:44:06 +00:00
static const ffe_t kCantorBasis[kBits] = {
0x0001, 0xACCA, 0x3C0E, 0x163E,
2017-05-18 03:06:13 +00:00
0xC582, 0xED2E, 0x914C, 0x4012,
0x6C98, 0x10D8, 0x6A72, 0xB900,
0xFDB8, 0xFB34, 0xFF38, 0x991E
};
2017-05-27 08:15:24 +00:00
// Using the Cantor basis here enables us to avoid a lot of extra calculations
// when applying the formal derivative in decoding.
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// Field Operations
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// z = x + y (mod kModulus)
static inline ffe_t AddMod(const ffe_t a, const ffe_t b)
2017-05-18 03:06:13 +00:00
{
const unsigned sum = (unsigned)a + b;
2017-05-27 02:51:30 +00:00
// Partial reduction step, allowing for kModulus to be returned
return static_cast<ffe_t>(sum + (sum >> kBits));
2017-05-18 03:06:13 +00:00
}
2017-05-27 02:51:30 +00:00
// z = x - y (mod kModulus)
static inline ffe_t SubMod(const ffe_t a, const ffe_t b)
2017-05-18 03:06:13 +00:00
{
const unsigned dif = (unsigned)a - b;
2017-05-27 02:51:30 +00:00
// Partial reduction step, allowing for kModulus to be returned
return static_cast<ffe_t>(dif + (dif >> kBits));
2017-05-18 03:06:13 +00:00
}
2017-05-24 08:23:19 +00:00
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// Fast Walsh-Hadamard Transform (FWHT) (mod kModulus)
2017-05-18 03:06:13 +00:00
2017-05-28 08:23:03 +00:00
#if defined(LEO_FWHT_OPT)
2017-05-24 08:23:19 +00:00
// {a, b} = {a + b, a - b} (Mod Q)
2017-05-27 02:51:30 +00:00
static LEO_FORCE_INLINE void FWHT_2(ffe_t& LEO_RESTRICT a, ffe_t& LEO_RESTRICT b)
2017-05-24 08:23:19 +00:00
{
2017-05-27 02:51:30 +00:00
const ffe_t sum = AddMod(a, b);
const ffe_t dif = SubMod(a, b);
2017-05-24 08:23:19 +00:00
a = sum;
b = dif;
}
2017-05-27 02:51:30 +00:00
static LEO_FORCE_INLINE void FWHT_4(ffe_t* data)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
ffe_t t0 = data[0];
ffe_t t1 = data[1];
ffe_t t2 = data[2];
ffe_t t3 = data[3];
2017-05-18 03:06:13 +00:00
FWHT_2(t0, t1);
FWHT_2(t2, t3);
FWHT_2(t0, t2);
FWHT_2(t1, t3);
data[0] = t0;
data[1] = t1;
data[2] = t2;
data[3] = t3;
}
2017-05-27 02:51:30 +00:00
static LEO_FORCE_INLINE void FWHT_4(ffe_t* data, unsigned s)
2017-05-18 03:06:13 +00:00
{
unsigned x = 0;
2017-05-27 02:51:30 +00:00
ffe_t t0 = data[x]; x += s;
ffe_t t1 = data[x]; x += s;
ffe_t t2 = data[x]; x += s;
ffe_t t3 = data[x];
2017-05-18 03:06:13 +00:00
FWHT_2(t0, t1);
FWHT_2(t2, t3);
FWHT_2(t0, t2);
FWHT_2(t1, t3);
unsigned y = 0;
data[y] = t0; y += s;
data[y] = t1; y += s;
data[y] = t2; y += s;
data[y] = t3;
}
2017-05-27 02:51:30 +00:00
static inline void FWHT_8(ffe_t* data)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
ffe_t t0 = data[0];
ffe_t t1 = data[1];
ffe_t t2 = data[2];
ffe_t t3 = data[3];
ffe_t t4 = data[4];
ffe_t t5 = data[5];
ffe_t t6 = data[6];
ffe_t t7 = data[7];
2017-05-18 03:06:13 +00:00
FWHT_2(t0, t1);
FWHT_2(t2, t3);
FWHT_2(t4, t5);
FWHT_2(t6, t7);
FWHT_2(t0, t2);
FWHT_2(t1, t3);
FWHT_2(t4, t6);
FWHT_2(t5, t7);
FWHT_2(t0, t4);
FWHT_2(t1, t5);
FWHT_2(t2, t6);
FWHT_2(t3, t7);
data[0] = t0;
data[1] = t1;
data[2] = t2;
data[3] = t3;
data[4] = t4;
data[5] = t5;
data[6] = t6;
data[7] = t7;
}
2017-05-27 02:51:30 +00:00
static inline void FWHT_16(ffe_t* data)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
ffe_t t0 = data[0];
ffe_t t1 = data[1];
ffe_t t2 = data[2];
ffe_t t3 = data[3];
ffe_t t4 = data[4];
ffe_t t5 = data[5];
ffe_t t6 = data[6];
ffe_t t7 = data[7];
ffe_t t8 = data[8];
ffe_t t9 = data[9];
ffe_t t10 = data[10];
ffe_t t11 = data[11];
ffe_t t12 = data[12];
ffe_t t13 = data[13];
ffe_t t14 = data[14];
ffe_t t15 = data[15];
2017-05-18 03:06:13 +00:00
FWHT_2(t0, t1);
FWHT_2(t2, t3);
FWHT_2(t4, t5);
FWHT_2(t6, t7);
FWHT_2(t8, t9);
FWHT_2(t10, t11);
FWHT_2(t12, t13);
FWHT_2(t14, t15);
FWHT_2(t0, t2);
FWHT_2(t1, t3);
FWHT_2(t4, t6);
FWHT_2(t5, t7);
FWHT_2(t8, t10);
FWHT_2(t9, t11);
FWHT_2(t12, t14);
FWHT_2(t13, t15);
FWHT_2(t0, t4);
FWHT_2(t1, t5);
FWHT_2(t2, t6);
FWHT_2(t3, t7);
FWHT_2(t8, t12);
FWHT_2(t9, t13);
FWHT_2(t10, t14);
FWHT_2(t11, t15);
FWHT_2(t0, t8);
FWHT_2(t1, t9);
FWHT_2(t2, t10);
FWHT_2(t3, t11);
FWHT_2(t4, t12);
FWHT_2(t5, t13);
FWHT_2(t6, t14);
FWHT_2(t7, t15);
data[0] = t0;
data[1] = t1;
data[2] = t2;
data[3] = t3;
data[4] = t4;
data[5] = t5;
data[6] = t6;
data[7] = t7;
data[8] = t8;
data[9] = t9;
data[10] = t10;
data[11] = t11;
data[12] = t12;
data[13] = t13;
data[14] = t14;
data[15] = t15;
}
2017-05-29 22:01:01 +00:00
static void FWHT_SmallData(ffe_t* data, unsigned bits)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned n = (1UL << bits);
2017-05-18 03:06:13 +00:00
if (n <= 2)
{
if (n == 2)
FWHT_2(data[0], data[1]);
return;
}
2017-05-29 22:01:01 +00:00
for (unsigned i = bits; i > 3; i -= 2)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
unsigned m = (1UL << i);
2017-05-18 03:06:13 +00:00
unsigned m4 = (m >> 2);
for (unsigned r = 0; r < n; r += m)
for (unsigned j = 0; j < m4; j++)
FWHT_4(data + j + r, m4);
}
2017-05-29 22:01:01 +00:00
if (bits & 1)
2017-05-18 03:06:13 +00:00
{
for (unsigned i0 = 0; i0 < n; i0 += 8)
FWHT_8(data + i0);
}
else
{
for (unsigned i0 = 0; i0 < n; i0 += 4)
FWHT_4(data + i0);
}
}
2017-05-24 08:23:19 +00:00
// Decimation in time (DIT) version
2017-05-29 22:01:01 +00:00
static void FWHT(ffe_t* data, const unsigned bits)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
if (bits <= 13)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
FWHT_SmallData(data, bits);
2017-05-18 03:06:13 +00:00
return;
}
FWHT_2(data[2], data[3]);
FWHT_4(data + 4);
FWHT_8(data + 8);
FWHT_16(data + 16);
2017-05-29 22:01:01 +00:00
for (unsigned i = 5; i < bits; ++i)
FWHT(data + (unsigned)(1UL << i), i);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
for (unsigned i = 0; i < bits; ++i)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned mh = (1UL << i);
2017-05-18 03:06:13 +00:00
for (unsigned t1 = 0, t2 = mh; t1 < mh; ++t1, ++t2)
FWHT_2(data[t1], data[t2]);
}
}
2017-05-28 08:23:03 +00:00
#else // LEO_FWHT_OPT
2017-05-27 02:51:30 +00:00
// Reference implementation
void FWHT(ffe_t* data, const unsigned bits)
{
const unsigned size = (unsigned)(1UL << bits);
for (unsigned width = 1; width < size; width <<= 1)
for (unsigned i = 0; i < size; i += (width << 1))
for (unsigned j = i; j < (width + i); ++j)
FWHT_2(data[j], data[j + width]);
}
2017-05-28 08:23:03 +00:00
#endif // LEO_FWHT_OPT
2017-05-27 02:51:30 +00:00
// Transform specialized for the finite field order
void FWHT(ffe_t data[kOrder])
{
FWHT(data, kBits);
}
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// Logarithm Tables
static ffe_t LogLUT[kOrder];
static ffe_t ExpLUT[kOrder];
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// Initialize LogLUT[], ExpLUT[]
static void InitializeLogarithmTables()
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
// LFSR table generation:
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
unsigned state = 1;
for (unsigned i = 0; i < kModulus; ++i)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
ExpLUT[state] = static_cast<ffe_t>(i);
state <<= 1;
if (state >= kOrder)
state ^= kPolynomial;
}
ExpLUT[0] = kModulus;
2017-05-28 01:44:06 +00:00
// Conversion to Cantor basis:
2017-05-27 02:51:30 +00:00
LogLUT[0] = 0;
for (unsigned i = 0; i < kBits; ++i)
{
2017-05-28 01:44:06 +00:00
const ffe_t basis = kCantorBasis[i];
2017-05-27 02:51:30 +00:00
const unsigned width = static_cast<unsigned>(1UL << i);
for (unsigned j = 0; j < width; ++j)
LogLUT[j + width] = LogLUT[j] ^ basis;
}
for (unsigned i = 0; i < kOrder; ++i)
LogLUT[i] = ExpLUT[LogLUT[i]];
for (unsigned i = 0; i < kOrder; ++i)
ExpLUT[LogLUT[i]] = i;
ExpLUT[kModulus] = ExpLUT[0];
}
2017-05-29 22:01:01 +00:00
2017-05-27 02:51:30 +00:00
//------------------------------------------------------------------------------
// Multiplies
struct {
2017-05-29 22:01:01 +00:00
LEO_ALIGNED LEO_M128 Value[kBits / 4];
} static Multiply128LUT[kOrder];
2017-05-27 02:51:30 +00:00
#if defined(LEO_TRY_AVX2)
struct {
2017-05-29 22:01:01 +00:00
LEO_ALIGNED LEO_M256 Value[kBits / 4];
} static Multiply256LUT[kOrder];
2017-05-27 02:51:30 +00:00
#endif // LEO_TRY_AVX2
// Returns a * Log(b)
2017-05-29 22:01:01 +00:00
static ffe_t MultiplyLog(ffe_t a, ffe_t log_b)
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
/*
Note that this operation is not a normal multiplication in a finite
field because the right operand is already a logarithm. This is done
because it moves K table lookups from the Decode() method into the
initialization step that is less performance critical. The LogWalsh[]
table below contains precalculated logarithms so it is easier to do
all the other multiplies in that form as well.
*/
2017-05-27 02:51:30 +00:00
if (a == 0)
return 0;
2017-05-29 22:01:01 +00:00
return ExpLUT[AddMod(LogLUT[a], log_b)];
2017-05-27 02:51:30 +00:00
}
2017-05-29 22:01:01 +00:00
void InitializeMultiplyTables()
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
for (unsigned log_m = 0; log_m < kOrder; ++log_m)
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
uint8_t table0[16], table1[16], table2[16], table3[16];
for (uint8_t x = 0; x < 16; ++x)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
table0[x] = MultiplyLog(x, static_cast<ffe_t>(log_m));
table1[x] = MultiplyLog(x << 4, static_cast<ffe_t>(log_m));
table2[x] = MultiplyLog(x << 8, static_cast<ffe_t>(log_m));
table3[x] = MultiplyLog(x << 12, static_cast<ffe_t>(log_m));
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
const LEO_M128 value0 = _mm_loadu_si128((LEO_M128*)table0);
const LEO_M128 value1 = _mm_loadu_si128((LEO_M128*)table1);
const LEO_M128 value2 = _mm_loadu_si128((LEO_M128*)table2);
const LEO_M128 value3 = _mm_loadu_si128((LEO_M128*)table3);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
_mm_storeu_si128(&Multiply128LUT[log_m].Value[0], value0);
_mm_storeu_si128(&Multiply128LUT[log_m].Value[1], value1);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
#if defined(LEO_TRY_AVX2)
if (CpuHasAVX2)
{
2017-05-29 22:01:01 +00:00
_mm256_storeu_si256(&Multiply256LUT[log_m].Value[0],
2017-05-27 02:51:30 +00:00
_mm256_broadcastsi128_si256(table_lo));
2017-05-29 22:01:01 +00:00
_mm256_storeu_si256(&Multiply256LUT[log_m].Value[1],
2017-05-27 02:51:30 +00:00
_mm256_broadcastsi128_si256(table_hi));
2017-05-18 03:06:13 +00:00
}
2017-05-27 02:51:30 +00:00
#endif // LEO_TRY_AVX2
2017-05-18 03:06:13 +00:00
}
2017-05-27 02:51:30 +00:00
}
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
void mul_mem(
void * LEO_RESTRICT x, const void * LEO_RESTRICT y,
ffe_t log_m, uint64_t bytes)
{
2017-05-27 02:51:30 +00:00
#if defined(LEO_TRY_AVX2)
2017-05-18 03:06:13 +00:00
if (CpuHasAVX2)
{
2017-05-29 22:01:01 +00:00
const LEO_M256 table_lo_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[0]);
const LEO_M256 table_hi_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[1]);
2017-05-27 02:51:30 +00:00
const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f);
2017-05-29 22:01:01 +00:00
LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast<LEO_M256 *>(x);
const LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast<const LEO_M256 *>(y);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
do
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
#define LEO_MUL_256(x_ptr, y_ptr) { \
LEO_M256 data = _mm256_loadu_si256(y_ptr); \
LEO_M256 lo = _mm256_and_si256(data, clr_mask); \
lo = _mm256_shuffle_epi8(table_lo_y, lo); \
LEO_M256 hi = _mm256_srli_epi64(data, 4); \
hi = _mm256_and_si256(hi, clr_mask); \
hi = _mm256_shuffle_epi8(table_hi_y, hi); \
_mm256_storeu_si256(x_ptr, _mm256_xor_si256(lo, hi)); }
LEO_MUL_256(x32 + 1, y32 + 1);
LEO_MUL_256(x32, y32);
y32 += 2, x32 += 2;
bytes -= 64;
} while (bytes > 0);
2017-05-27 02:51:30 +00:00
return;
}
#endif // LEO_TRY_AVX2
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
const LEO_M128 table_lo_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[0]);
const LEO_M128 table_hi_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[1]);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
const LEO_M128 clr_mask = _mm_set1_epi8(0x0f);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast<LEO_M128 *>(x);
const LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast<const LEO_M128 *>(y);
2017-05-27 02:51:30 +00:00
do
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
#define LEO_MUL_128(x_ptr, y_ptr) { \
LEO_M128 data = _mm_loadu_si128(y_ptr); \
LEO_M128 lo = _mm_and_si128(data, clr_mask); \
lo = _mm_shuffle_epi8(table_lo_y, lo); \
LEO_M128 hi = _mm_srli_epi64(data, 4); \
hi = _mm_and_si128(hi, clr_mask); \
hi = _mm_shuffle_epi8(table_hi_y, hi); \
_mm_storeu_si128(x_ptr, _mm_xor_si128(lo, hi)); }
LEO_MUL_128(x16 + 3, y16 + 3);
LEO_MUL_128(x16 + 2, y16 + 2);
LEO_MUL_128(x16 + 1, y16 + 1);
LEO_MUL_128(x16, y16);
2017-05-27 02:51:30 +00:00
x16 += 4, y16 += 4;
2017-05-29 22:01:01 +00:00
2017-05-27 02:51:30 +00:00
bytes -= 64;
} while (bytes > 0);
}
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// FFT Operations
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
void fft_butterfly(
void * LEO_RESTRICT x, void * LEO_RESTRICT y,
2017-05-29 22:01:01 +00:00
ffe_t log_m, uint64_t bytes)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
#if defined(LEO_TRY_AVX2)
if (CpuHasAVX2)
{
const LEO_M256 table_lo_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[0]);
const LEO_M256 table_hi_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[1]);
const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f);
LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast<LEO_M256 *>(x);
LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast<LEO_M256 *>(y);
do
{
#define LEO_FFTB_256(x_ptr, y_ptr) { \
LEO_M256 y_data = _mm256_loadu_si256(y_ptr); \
LEO_M256 lo = _mm256_and_si256(y_data, clr_mask); \
lo = _mm256_shuffle_epi8(table_lo_y, lo); \
LEO_M256 hi = _mm256_srli_epi64(y_data, 4); \
hi = _mm256_and_si256(hi, clr_mask); \
hi = _mm256_shuffle_epi8(table_hi_y, hi); \
LEO_M256 x_data = _mm256_loadu_si256(x_ptr); \
x_data = _mm256_xor_si256(x_data, _mm256_xor_si256(lo, hi)); \
y_data = _mm256_xor_si256(y_data, x_data); \
_mm256_storeu_si256(x_ptr, x_data); \
_mm256_storeu_si256(y_ptr, y_data); }
LEO_FFTB_256(x32 + 1, y32 + 1);
LEO_FFTB_256(x32, y32);
y32 += 2, x32 += 2;
bytes -= 64;
} while (bytes > 0);
return;
}
#endif // LEO_TRY_AVX2
const LEO_M128 table_lo_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[0]);
const LEO_M128 table_hi_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[1]);
const LEO_M128 clr_mask = _mm_set1_epi8(0x0f);
LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast<LEO_M128 *>(x);
LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast<LEO_M128 *>(y);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
do
{
#define LEO_FFTB_128(x_ptr, y_ptr) { \
LEO_M128 y_data = _mm_loadu_si128(y_ptr); \
LEO_M128 lo = _mm_and_si128(y_data, clr_mask); \
lo = _mm_shuffle_epi8(table_lo_y, lo); \
LEO_M128 hi = _mm_srli_epi64(y_data, 4); \
hi = _mm_and_si128(hi, clr_mask); \
hi = _mm_shuffle_epi8(table_hi_y, hi); \
LEO_M128 x_data = _mm_loadu_si128(x_ptr); \
x_data = _mm_xor_si128(x_data, _mm_xor_si128(lo, hi)); \
y_data = _mm_xor_si128(y_data, x_data); \
_mm_storeu_si128(x_ptr, x_data); \
_mm_storeu_si128(y_ptr, y_data); }
LEO_FFTB_128(x16 + 3, y16 + 3);
LEO_FFTB_128(x16 + 2, y16 + 2);
LEO_FFTB_128(x16 + 1, y16 + 1);
LEO_FFTB_128(x16, y16);
x16 += 4, y16 += 4;
bytes -= 64;
} while (bytes > 0);
2017-05-27 02:51:30 +00:00
}
2017-05-29 22:01:01 +00:00
#ifdef LEO_USE_VECTOR4_OPT
2017-05-27 03:30:48 +00:00
void fft_butterfly4(
2017-05-27 02:51:30 +00:00
void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0,
void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1,
void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2,
2017-05-27 03:30:48 +00:00
void * LEO_RESTRICT x_3, void * LEO_RESTRICT y_3,
2017-05-29 22:01:01 +00:00
ffe_t log_m, uint64_t bytes)
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
#if defined(LEO_TRY_AVX2)
if (CpuHasAVX2)
{
const LEO_M256 table_lo_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[0]);
const LEO_M256 table_hi_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[1]);
const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast<LEO_M256 *>(x_0);
LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast<LEO_M256 *>(y_0);
LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast<LEO_M256 *>(x_1);
LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast<LEO_M256 *>(y_1);
LEO_M256 * LEO_RESTRICT x32_2 = reinterpret_cast<LEO_M256 *>(x_2);
LEO_M256 * LEO_RESTRICT y32_2 = reinterpret_cast<LEO_M256 *>(y_2);
LEO_M256 * LEO_RESTRICT x32_3 = reinterpret_cast<LEO_M256 *>(x_3);
LEO_M256 * LEO_RESTRICT y32_3 = reinterpret_cast<LEO_M256 *>(y_3);
do
{
LEO_FFTB_256(x32_0 + 1, y32_0 + 1);
LEO_FFTB_256(x32_0, y32_0);
y32_0 += 2, x32_0 += 2;
LEO_FFTB_256(x32_1 + 1, y32_1 + 1);
LEO_FFTB_256(x32_1, y32_1);
y32_1 += 2, x32_1 += 2;
LEO_FFTB_256(x32_2 + 1, y32_2 + 1);
LEO_FFTB_256(x32_2, y32_2);
y32_2 += 2, x32_2 += 2;
LEO_FFTB_256(x32_3 + 1, y32_3 + 1);
LEO_FFTB_256(x32_3, y32_3);
y32_3 += 2, x32_3 += 2;
bytes -= 64;
} while (bytes > 0);
return;
}
#endif // LEO_TRY_AVX2
const LEO_M128 table_lo_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[0]);
const LEO_M128 table_hi_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[1]);
const LEO_M128 clr_mask = _mm_set1_epi8(0x0f);
LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast<LEO_M128 *>(x_0);
LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast<LEO_M128 *>(y_0);
LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast<LEO_M128 *>(x_1);
LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast<LEO_M128 *>(y_1);
LEO_M128 * LEO_RESTRICT x16_2 = reinterpret_cast<LEO_M128 *>(x_2);
LEO_M128 * LEO_RESTRICT y16_2 = reinterpret_cast<LEO_M128 *>(y_2);
LEO_M128 * LEO_RESTRICT x16_3 = reinterpret_cast<LEO_M128 *>(x_3);
LEO_M128 * LEO_RESTRICT y16_3 = reinterpret_cast<LEO_M128 *>(y_3);
do
{
LEO_FFTB_128(x16_0 + 3, y16_0 + 3);
LEO_FFTB_128(x16_0 + 2, y16_0 + 2);
LEO_FFTB_128(x16_0 + 1, y16_0 + 1);
LEO_FFTB_128(x16_0, y16_0);
x16_0 += 4, y16_0 += 4;
LEO_FFTB_128(x16_1 + 3, y16_1 + 3);
LEO_FFTB_128(x16_1 + 2, y16_1 + 2);
LEO_FFTB_128(x16_1 + 1, y16_1 + 1);
LEO_FFTB_128(x16_1, y16_1);
x16_1 += 4, y16_1 += 4;
LEO_FFTB_128(x16_2 + 3, y16_2 + 3);
LEO_FFTB_128(x16_2 + 2, y16_2 + 2);
LEO_FFTB_128(x16_2 + 1, y16_2 + 1);
LEO_FFTB_128(x16_2, y16_2);
x16_2 += 4, y16_2 += 4;
LEO_FFTB_128(x16_3 + 3, y16_3 + 3);
LEO_FFTB_128(x16_3 + 2, y16_3 + 2);
LEO_FFTB_128(x16_3 + 1, y16_3 + 1);
LEO_FFTB_128(x16_3, y16_3);
x16_3 += 4, y16_3 += 4;
bytes -= 64;
} while (bytes > 0);
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
#endif // LEO_USE_VECTOR4_OPT
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// IFFT Operations
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
void ifft_butterfly(
void * LEO_RESTRICT x, void * LEO_RESTRICT y,
2017-05-29 22:01:01 +00:00
ffe_t log_m, uint64_t bytes)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
#if defined(LEO_TRY_AVX2)
if (CpuHasAVX2)
{
const LEO_M256 table_lo_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[0]);
const LEO_M256 table_hi_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[1]);
const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f);
LEO_M256 * LEO_RESTRICT x32 = reinterpret_cast<LEO_M256 *>(x);
LEO_M256 * LEO_RESTRICT y32 = reinterpret_cast<LEO_M256 *>(y);
do
{
#define LEO_IFFTB_256(x_ptr, y_ptr) { \
LEO_M256 x_data = _mm256_loadu_si256(x_ptr); \
LEO_M256 y_data = _mm256_loadu_si256(y_ptr); \
y_data = _mm256_xor_si256(y_data, x_data); \
_mm256_storeu_si256(y_ptr, y_data); \
LEO_M256 lo = _mm256_and_si256(y_data, clr_mask); \
lo = _mm256_shuffle_epi8(table_lo_y, lo); \
LEO_M256 hi = _mm256_srli_epi64(y_data, 4); \
hi = _mm256_and_si256(hi, clr_mask); \
hi = _mm256_shuffle_epi8(table_hi_y, hi); \
x_data = _mm256_xor_si256(x_data, _mm256_xor_si256(lo, hi)); \
_mm256_storeu_si256(x_ptr, x_data); }
LEO_IFFTB_256(x32 + 1, y32 + 1);
LEO_IFFTB_256(x32, y32);
y32 += 2, x32 += 2;
bytes -= 64;
} while (bytes > 0);
return;
}
#endif // LEO_TRY_AVX2
const LEO_M128 table_lo_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[0]);
const LEO_M128 table_hi_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[1]);
const LEO_M128 clr_mask = _mm_set1_epi8(0x0f);
LEO_M128 * LEO_RESTRICT x16 = reinterpret_cast<LEO_M128 *>(x);
LEO_M128 * LEO_RESTRICT y16 = reinterpret_cast<LEO_M128 *>(y);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
do
{
#define LEO_IFFTB_128(x_ptr, y_ptr) { \
LEO_M128 x_data = _mm_loadu_si128(x_ptr); \
LEO_M128 y_data = _mm_loadu_si128(y_ptr); \
y_data = _mm_xor_si128(y_data, x_data); \
_mm_storeu_si128(y_ptr, y_data); \
LEO_M128 lo = _mm_and_si128(y_data, clr_mask); \
lo = _mm_shuffle_epi8(table_lo_y, lo); \
LEO_M128 hi = _mm_srli_epi64(y_data, 4); \
hi = _mm_and_si128(hi, clr_mask); \
hi = _mm_shuffle_epi8(table_hi_y, hi); \
x_data = _mm_xor_si128(x_data, _mm_xor_si128(lo, hi)); \
_mm_storeu_si128(x_ptr, x_data); }
LEO_IFFTB_128(x16 + 3, y16 + 3);
LEO_IFFTB_128(x16 + 2, y16 + 2);
LEO_IFFTB_128(x16 + 1, y16 + 1);
LEO_IFFTB_128(x16, y16);
x16 += 4, y16 += 4;
bytes -= 64;
} while (bytes > 0);
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
#ifdef LEO_USE_VECTOR4_OPT
2017-05-27 03:30:48 +00:00
void ifft_butterfly4(
2017-05-27 02:51:30 +00:00
void * LEO_RESTRICT x_0, void * LEO_RESTRICT y_0,
void * LEO_RESTRICT x_1, void * LEO_RESTRICT y_1,
void * LEO_RESTRICT x_2, void * LEO_RESTRICT y_2,
2017-05-27 03:30:48 +00:00
void * LEO_RESTRICT x_3, void * LEO_RESTRICT y_3,
2017-05-29 22:01:01 +00:00
ffe_t log_m, uint64_t bytes)
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
#if defined(LEO_TRY_AVX2)
if (CpuHasAVX2)
{
const LEO_M256 table_lo_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[0]);
const LEO_M256 table_hi_y = _mm256_loadu_si256(&Multiply256LUT[log_m].Value[1]);
const LEO_M256 clr_mask = _mm256_set1_epi8(0x0f);
LEO_M256 * LEO_RESTRICT x32_0 = reinterpret_cast<LEO_M256 *>(x_0);
LEO_M256 * LEO_RESTRICT y32_0 = reinterpret_cast<LEO_M256 *>(y_0);
LEO_M256 * LEO_RESTRICT x32_1 = reinterpret_cast<LEO_M256 *>(x_1);
LEO_M256 * LEO_RESTRICT y32_1 = reinterpret_cast<LEO_M256 *>(y_1);
LEO_M256 * LEO_RESTRICT x32_2 = reinterpret_cast<LEO_M256 *>(x_2);
LEO_M256 * LEO_RESTRICT y32_2 = reinterpret_cast<LEO_M256 *>(y_2);
LEO_M256 * LEO_RESTRICT x32_3 = reinterpret_cast<LEO_M256 *>(x_3);
LEO_M256 * LEO_RESTRICT y32_3 = reinterpret_cast<LEO_M256 *>(y_3);
do
{
LEO_IFFTB_256(x32_0 + 1, y32_0 + 1);
LEO_IFFTB_256(x32_0, y32_0);
y32_0 += 2, x32_0 += 2;
LEO_IFFTB_256(x32_1 + 1, y32_1 + 1);
LEO_IFFTB_256(x32_1, y32_1);
y32_1 += 2, x32_1 += 2;
LEO_IFFTB_256(x32_2 + 1, y32_2 + 1);
LEO_IFFTB_256(x32_2, y32_2);
y32_2 += 2, x32_2 += 2;
LEO_IFFTB_256(x32_3 + 1, y32_3 + 1);
LEO_IFFTB_256(x32_3, y32_3);
y32_3 += 2, x32_3 += 2;
bytes -= 64;
} while (bytes > 0);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
return;
}
#endif // LEO_TRY_AVX2
const LEO_M128 table_lo_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[0]);
const LEO_M128 table_hi_y = _mm_loadu_si128(&Multiply128LUT[log_m].Value[1]);
const LEO_M128 clr_mask = _mm_set1_epi8(0x0f);
LEO_M128 * LEO_RESTRICT x16_0 = reinterpret_cast<LEO_M128 *>(x_0);
LEO_M128 * LEO_RESTRICT y16_0 = reinterpret_cast<LEO_M128 *>(y_0);
LEO_M128 * LEO_RESTRICT x16_1 = reinterpret_cast<LEO_M128 *>(x_1);
LEO_M128 * LEO_RESTRICT y16_1 = reinterpret_cast<LEO_M128 *>(y_1);
LEO_M128 * LEO_RESTRICT x16_2 = reinterpret_cast<LEO_M128 *>(x_2);
LEO_M128 * LEO_RESTRICT y16_2 = reinterpret_cast<LEO_M128 *>(y_2);
LEO_M128 * LEO_RESTRICT x16_3 = reinterpret_cast<LEO_M128 *>(x_3);
LEO_M128 * LEO_RESTRICT y16_3 = reinterpret_cast<LEO_M128 *>(y_3);
do
{
LEO_IFFTB_128(x16_0 + 3, y16_0 + 3);
LEO_IFFTB_128(x16_0 + 2, y16_0 + 2);
LEO_IFFTB_128(x16_0 + 1, y16_0 + 1);
LEO_IFFTB_128(x16_0, y16_0);
x16_0 += 4, y16_0 += 4;
LEO_IFFTB_128(x16_1 + 3, y16_1 + 3);
LEO_IFFTB_128(x16_1 + 2, y16_1 + 2);
LEO_IFFTB_128(x16_1 + 1, y16_1 + 1);
LEO_IFFTB_128(x16_1, y16_1);
x16_1 += 4, y16_1 += 4;
LEO_IFFTB_128(x16_2 + 3, y16_2 + 3);
LEO_IFFTB_128(x16_2 + 2, y16_2 + 2);
LEO_IFFTB_128(x16_2 + 1, y16_2 + 1);
LEO_IFFTB_128(x16_2, y16_2);
x16_2 += 4, y16_2 += 4;
LEO_IFFTB_128(x16_3 + 3, y16_3 + 3);
LEO_IFFTB_128(x16_3 + 2, y16_3 + 2);
LEO_IFFTB_128(x16_3 + 1, y16_3 + 1);
LEO_IFFTB_128(x16_3, y16_3);
x16_3 += 4, y16_3 += 4;
bytes -= 64;
} while (bytes > 0);
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
#endif // LEO_USE_VECTOR4_OPT
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// FFT
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
// Twisted factors used in FFT
static ffe_t FFTSkew[kModulus];
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
// Factors used in the evaluation of the error locator polynomial
static ffe_t LogWalsh[kOrder];
static void FFTInitialize()
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
ffe_t temp[kBits - 1];
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
// Generate FFT skew vector {1}:
2017-05-27 02:51:30 +00:00
for (unsigned i = 1; i < kBits; ++i)
2017-05-29 22:01:01 +00:00
temp[i - 1] = static_cast<ffe_t>(1UL << i);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned m = 0; m < (kBits - 1); ++m)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned step = 1UL << (m + 1);
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
FFTSkew[(1UL << m) - 1] = 0;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = m; i < (kBits - 1); ++i)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned s = (1UL << (i + 1));
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
for (unsigned j = (1UL << m) - 1; j < s; j += step)
2017-05-27 02:51:30 +00:00
FFTSkew[j + s] = FFTSkew[j] ^ temp[i];
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
temp[m] = kModulus - LogLUT[MultiplyLog(temp[m], LogLUT[temp[m] ^ 1])];
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = m + 1; i < (kBits - 1); ++i)
2017-05-29 22:01:01 +00:00
{
const ffe_t sum = AddMod(LogLUT[temp[i] ^ 1], temp[m]);
temp[i] = MultiplyLog(temp[i], sum);
}
2017-05-18 03:06:13 +00:00
}
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < kOrder; ++i)
FFTSkew[i] = LogLUT[FFTSkew[i]];
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
// Precalculate FWHT(Log[i]):
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < kOrder; ++i)
LogWalsh[i] = LogLUT[i];
LogWalsh[0] = 0;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
FWHT(LogWalsh, kBits);
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
void VectorFFTButterfly(
const uint64_t bytes,
unsigned count,
void** x,
void** y,
const ffe_t log_m)
{
if (log_m == kModulus)
{
VectorXOR(bytes, count, y, x);
return;
}
#ifdef LEO_USE_VECTOR4_OPT
while (count >= 4)
{
fft_butterfly4(
x[0], y[0],
x[1], y[1],
x[2], y[2],
x[3], y[3],
log_m, bytes);
x += 4, y += 4;
count -= 4;
}
#endif // LEO_USE_VECTOR4_OPT
for (unsigned i = 0; i < count; ++i)
fft_butterfly(x[i], y[i], log_m, bytes);
}
void VectorIFFTButterfly(
const uint64_t bytes,
unsigned count,
void** x,
void** y,
const ffe_t log_m)
{
if (log_m == kModulus)
{
VectorXOR(bytes, count, y, x);
return;
}
#ifdef LEO_USE_VECTOR4_OPT
while (count >= 4)
{
ifft_butterfly4(
x[0], y[0],
x[1], y[1],
x[2], y[2],
x[3], y[3],
log_m, bytes);
x += 4, y += 4;
count -= 4;
}
#endif // LEO_USE_VECTOR4_OPT
for (unsigned i = 0; i < count; ++i)
ifft_butterfly(x[i], y[i], log_m, bytes);
}
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-29 22:01:01 +00:00
// Reed-Solomon Encode
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
void ReedSolomonEncode(
2017-05-27 02:51:30 +00:00
uint64_t buffer_bytes,
unsigned original_count,
unsigned recovery_count,
unsigned m,
2017-05-29 22:01:01 +00:00
void* const * data,
2017-05-27 02:51:30 +00:00
void** work)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
// work <- data
2017-05-28 20:50:32 +00:00
// TBD: Unroll first loop to eliminate this
2017-05-29 22:01:01 +00:00
unsigned first_end = m;
if (original_count < m)
{
first_end = original_count;
for (unsigned i = original_count; i < m; ++i)
memset(work[i], 0, buffer_bytes);
}
for (unsigned i = 0; i < first_end; ++i)
2017-05-27 02:51:30 +00:00
memcpy(work[i], data[i], buffer_bytes);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- IFFT(data, m, m)
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned width = 1; width < m; width <<= 1)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned range = width << 1;
const ffe_t* skewLUT = FFTSkew + width + m - 1;
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
for (unsigned j = 0; j < first_end; j += range)
#else
for (unsigned j = 0; j < m; j += range)
#endif
{
VectorIFFTButterfly(
buffer_bytes,
width,
work + j,
work + j + width,
skewLUT[j]);
2017-05-27 02:51:30 +00:00
}
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
if (m >= original_count)
goto skip_body;
2017-05-27 02:51:30 +00:00
for (unsigned i = m; i + m <= original_count; i += m)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
// temp <- data + i
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
data += m;
2017-05-27 02:51:30 +00:00
void** temp = work + m;
2017-05-18 03:06:13 +00:00
2017-05-28 20:50:32 +00:00
// TBD: Unroll first loop to eliminate this
2017-05-27 02:51:30 +00:00
for (unsigned j = 0; j < m; ++j)
memcpy(temp[j], data[j], buffer_bytes);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// temp <- IFFT(temp, m, m + i)
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
const ffe_t* skewLUT = FFTSkew + m + i - 1;
2017-05-27 02:51:30 +00:00
for (unsigned width = 1; width < m; width <<= 1)
{
2017-05-29 22:01:01 +00:00
const unsigned range = width << 1;
for (unsigned j = width; j < m; j += range)
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
VectorIFFTButterfly(
buffer_bytes,
width,
temp + j - width,
temp + j,
skewLUT[j]);
2017-05-27 02:51:30 +00:00
}
}
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- work XOR temp
2017-05-18 03:06:13 +00:00
2017-05-28 20:50:32 +00:00
// TBD: Unroll last loop to eliminate this
2017-05-29 22:01:01 +00:00
VectorXOR(
buffer_bytes,
m,
work,
temp);
2017-05-27 02:51:30 +00:00
}
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
const unsigned last_count = original_count % m;
if (last_count != 0)
{
const unsigned i = original_count - last_count;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// temp <- data + i
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
data += m;
2017-05-27 02:51:30 +00:00
void** temp = work + m;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned j = 0; j < last_count; ++j)
memcpy(temp[j], data[j], buffer_bytes);
for (unsigned j = last_count; j < m; ++j)
memset(temp[j], 0, buffer_bytes);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// temp <- IFFT(temp, m, m + i)
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned width = 1, shift = 1; width < m; width <<= 1, ++shift)
2017-05-24 08:23:19 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned range = width << 1;
const ffe_t* skewLUT = FFTSkew + width + m + i - 1;
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
// Calculate stop considering that the right is all zeroes
const unsigned stop = ((last_count + range - 1) >> shift) << shift;
for (unsigned j = 0; j < stop; j += range)
#else
for (unsigned j = 0; j < m; j += range)
#endif
2017-05-27 02:51:30 +00:00
{
2017-05-29 22:01:01 +00:00
VectorIFFTButterfly(
buffer_bytes,
width,
temp + j,
temp + j + width,
skewLUT[j]);
2017-05-27 02:51:30 +00:00
}
2017-05-24 08:23:19 +00:00
}
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- work XOR temp
2017-05-18 03:06:13 +00:00
2017-05-28 20:50:32 +00:00
// TBD: Unroll last loop to eliminate this
2017-05-29 22:01:01 +00:00
VectorXOR(
buffer_bytes,
m,
work,
temp);
2017-05-24 08:23:19 +00:00
}
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
skip_body:
2017-05-27 02:51:30 +00:00
// work <- FFT(work, m, 0)
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned width = (m >> 1); width > 0; width >>= 1)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
const ffe_t* skewLUT = FFTSkew + width - 1;
const unsigned range = width << 1;
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
for (unsigned j = 0; j < recovery_count; j += range)
#else
2017-05-27 02:51:30 +00:00
for (unsigned j = 0; j < m; j += range)
2017-05-29 22:01:01 +00:00
#endif
2017-05-24 08:23:19 +00:00
{
2017-05-29 22:01:01 +00:00
VectorFFTButterfly(
buffer_bytes,
width,
work + j,
work + j + width,
skewLUT[j]);
}
}
}
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
//------------------------------------------------------------------------------
// ErrorBitfield
#ifdef LEO_SCHEDULE_OPT
// Used in decoding to decide which final FFT operations to perform
class ErrorBitfield
{
static const unsigned kWords = kOrder / 64;
uint64_t Words[7][kWords] = {};
public:
LEO_FORCE_INLINE void Set(unsigned i)
{
Words[0][i / 64] |= (uint64_t)1 << (i % 64);
}
void Prepare();
LEO_FORCE_INLINE bool IsNeeded(unsigned mip_level, unsigned bit) const
{
if (mip_level >= 8)
return true;
return 0 != (Words[mip_level - 1][bit / 64] & ((uint64_t)1 << (bit % 64)));
}
};
static const uint64_t kHiMasks[5] = {
0xAAAAAAAAAAAAAAAAULL,
0xCCCCCCCCCCCCCCCCULL,
0xF0F0F0F0F0F0F0F0ULL,
0xFF00FF00FF00FF00ULL,
0xFFFF0000FFFF0000ULL,
};
void ErrorBitfield::Prepare()
{
// First mip level is for final layer of FFT: pairs of data
for (unsigned i = 0; i < kWords; ++i)
{
const uint64_t w0 = Words[0][i];
const uint64_t hi2lo0 = w0 | ((w0 & kHiMasks[0]) >> 1);
const uint64_t lo2hi0 = ((w0 & (kHiMasks[0] >> 1)) << 1);
Words[0][i] = hi2lo0 | lo2hi0;
for (unsigned j = 1, bits = 2; j < 5; ++j, bits <<= 1)
{
const uint64_t w_j = Words[j - 1][i];
const uint64_t hi2lo_j = w_j | ((w_j & kHiMasks[j]) >> bits);
const uint64_t lo2hi_j = ((w_j & (kHiMasks[j] >> bits)) << bits);
Words[j][i] = hi2lo_j | lo2hi_j;
2017-05-24 08:23:19 +00:00
}
}
2017-05-29 22:01:01 +00:00
for (unsigned i = 0; i < kWords; ++i)
{
uint64_t w = Words[4][i];
w |= w >> 32;
w |= w << 32;
Words[5][i] = w;
}
for (unsigned i = 0; i < kWords; i += 2)
Words[6][i] = Words[6][i + 1] = Words[5][i] | Words[5][i + 1];
2017-05-18 03:06:13 +00:00
}
2017-05-29 22:01:01 +00:00
#endif // LEO_SCHEDULE_OPT
2017-05-18 03:06:13 +00:00
//------------------------------------------------------------------------------
2017-05-29 22:01:01 +00:00
// Reed-Solomon Decode
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
void ReedSolomonDecode(
2017-05-27 02:51:30 +00:00
uint64_t buffer_bytes,
unsigned original_count,
unsigned recovery_count,
unsigned m, // NextPow2(recovery_count)
unsigned n, // NextPow2(m + original_count) = work_count
void* const * const original, // original_count entries
void* const * const recovery, // recovery_count entries
void** work) // n entries
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
// Fill in error locations
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
ErrorBitfield ErrorBits;
#endif // LEO_SCHEDULE_OPT
ffe_t ErrorLocations[kOrder] = {};
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < recovery_count; ++i)
2017-05-29 22:01:01 +00:00
if (!recovery[i])
ErrorLocations[i] = 1;
2017-05-27 02:51:30 +00:00
for (unsigned i = recovery_count; i < m; ++i)
ErrorLocations[i] = 1;
for (unsigned i = 0; i < original_count; ++i)
2017-05-29 22:01:01 +00:00
{
if (!original[i])
{
ErrorLocations[i + m] = 1;
#ifdef LEO_SCHEDULE_OPT
ErrorBits.Set(i + m);
#endif // LEO_SCHEDULE_OPT
}
}
#ifdef LEO_SCHEDULE_OPT
ErrorBits.Prepare();
#endif // LEO_SCHEDULE_OPT
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// Evaluate error locator polynomial
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
FWHT(ErrorLocations, kBits);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < kOrder; ++i)
2017-05-27 03:30:48 +00:00
ErrorLocations[i] = ((unsigned)ErrorLocations[i] * (unsigned)LogWalsh[i]) % kModulus;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
FWHT(ErrorLocations, kBits);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- recovery data
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < recovery_count; ++i)
{
if (recovery[i])
2017-05-29 22:01:01 +00:00
mul_mem(work[i], recovery[i], ErrorLocations[i], buffer_bytes);
2017-05-27 02:51:30 +00:00
else
memset(work[i], 0, buffer_bytes);
}
for (unsigned i = recovery_count; i < m; ++i)
memset(work[i], 0, buffer_bytes);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- original data
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = 0; i < original_count; ++i)
{
if (original[i])
2017-05-29 22:01:01 +00:00
mul_mem(work[m + i], original[i], ErrorLocations[m + i], buffer_bytes);
2017-05-27 02:51:30 +00:00
else
memset(work[m + i], 0, buffer_bytes);
}
for (unsigned i = m + original_count; i < n; ++i)
memset(work[i], 0, buffer_bytes);
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
// work <- IFFT(work, n, 0)
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
const unsigned input_count = m + original_count;
unsigned mip_level = 0;
for (unsigned width = 1; width < n; width <<= 1, ++mip_level)
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
const unsigned range = width << 1;
2017-05-27 02:51:30 +00:00
2017-05-29 22:01:01 +00:00
for (unsigned j = width; j < n; j += range)
{
VectorIFFTButterfly(
buffer_bytes,
width,
work + j - width,
work + j,
FFTSkew[j - 1]);
2017-05-18 03:06:13 +00:00
}
}
2017-05-27 02:51:30 +00:00
// work <- FormalDerivative(work, n)
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
for (unsigned i = 1; i < n; ++i)
{
const unsigned width = ((i ^ (i - 1)) + 1) >> 1;
2017-05-18 03:06:13 +00:00
2017-05-29 22:01:01 +00:00
VectorXOR(
buffer_bytes,
width,
work + i - width,
work + i);
2017-05-27 02:51:30 +00:00
}
// work <- FFT(work, n, 0) truncated to m + original_count
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
const unsigned output_count = m + original_count;
2017-05-29 22:01:01 +00:00
for (unsigned width = (n >> 1); width > 0; width >>= 1, --mip_level)
2017-05-18 03:06:13 +00:00
{
2017-05-27 02:51:30 +00:00
const ffe_t* skewLUT = FFTSkew + width - 1;
const unsigned range = width << 1;
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
2017-05-27 02:51:30 +00:00
for (unsigned j = (m < range) ? 0 : m; j < output_count; j += range)
2017-05-29 22:01:01 +00:00
#else
for (unsigned j = 0; j < n; j += range)
#endif
2017-05-18 03:06:13 +00:00
{
2017-05-29 22:01:01 +00:00
#ifdef LEO_SCHEDULE_OPT
if (!ErrorBits.IsNeeded(mip_level, j))
continue;
#endif // LEO_SCHEDULE_OPT
VectorFFTButterfly(
buffer_bytes,
width,
work + j,
work + j + width,
skewLUT[j]);
2017-05-18 03:06:13 +00:00
}
}
2017-05-27 02:51:30 +00:00
// Reveal erasures
for (unsigned i = 0; i < original_count; ++i)
if (!original[i])
2017-05-29 22:01:01 +00:00
mul_mem(work[i], work[i + m], kModulus - ErrorLocations[i + m], buffer_bytes);
2017-05-18 03:06:13 +00:00
}
//------------------------------------------------------------------------------
2017-05-27 02:51:30 +00:00
// API
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
static bool IsInitialized = false;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
bool Initialize()
{
if (IsInitialized)
return true;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
if (!CpuHasSSSE3)
return false;
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
InitializeLogarithmTables();
2017-05-29 22:01:01 +00:00
InitializeMultiplyTables();
2017-05-27 02:51:30 +00:00
FFTInitialize();
2017-05-18 03:06:13 +00:00
2017-05-27 02:51:30 +00:00
IsInitialized = true;
return true;
2017-05-18 03:06:13 +00:00
}
2017-05-27 02:51:30 +00:00
}} // namespace leopard::ff16
2017-05-27 03:10:53 +00:00
#endif // LEO_HAS_FF16