Merge branch 'rm_interleaving_simd' into 'develop'

SIMD optimizations for ldpc deinterleaving and rate-matching

See merge request oai/openairinterface5g!3825
This commit is contained in:
knopp
2026-07-04 23:00:36 +00:00
2 changed files with 881 additions and 36 deletions

View File

@@ -7,6 +7,7 @@
#include "common/utils/LOG/log.h"
// #define RM_DEBUG 1
#define USE128BIT
static const uint8_t index_k0[2][4] = {{0, 17, 33, 56}, {0, 13, 25, 43}};
@@ -48,7 +49,7 @@ void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e, uint8_t *f)
simde__m128i *f_128 = (simde__m128i *)f;
simde__m128i *e0_128 = (simde__m128i *)e0;
simde__m128i *e1_128 = (simde__m128i *)e1;
for (; i < (EQm & ~15); i += 64) {
for (; i < (EQm & ~15); i += 16) {
simde__m128i e0j = simde_mm_loadu_si128(e0_128++);
simde__m128i e1j = simde_mm_loadu_si128(e1_128++);
simde_mm_storeu_si128(f_128++, simde_mm_unpacklo_epi8(e0j, e1j));
@@ -342,14 +343,15 @@ void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e, uint8_t *f)
f = (uint8_t *)f_512;
#endif
#ifdef USE128BIT
e0_128 = (simde__m128i *)e0;
e1_128 = (simde__m128i *)e1;
e2_128 = (simde__m128i *)e2;
e3_128 = (simde__m128i *)e3;
e4_128 = (simde__m128i *)e4;
e5_128 = (simde__m128i *)e5;
e6_128 = (simde__m128i *)e6;
e7_128 = (simde__m128i *)e7;
simde__m128i *e0_128 = (simde__m128i *)e0;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *e2_128 = (simde__m128i *)e2;
simde__m128i *e3_128 = (simde__m128i *)e3;
simde__m128i *e4_128 = (simde__m128i *)e4;
simde__m128i *e5_128 = (simde__m128i *)e5;
simde__m128i *e6_128 = (simde__m128i *)e6;
simde__m128i *e7_128 = (simde__m128i *)e7;
simde__m128i *f_128 = (simde__m128i *)f;
for (; i < (EQm & ~15); i += 16) {
simde__m128i e0j = simde_mm_loadu_si128(e0_128++);
simde__m128i e1j = simde_mm_loadu_si128(e1_128++);
@@ -427,25 +429,452 @@ void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e, uint8_t *f)
}
}
void nr_interleaving_ldpc32(uint32_t E, uint8_t Qm, uint32_t *e, uint32_t *f)
{
const uint32_t EQm = E / Qm;
memset(f, 0, E * sizeof(uint32_t));
switch(Qm) {
case 2: {
uint32_t *e0 = e;
uint32_t *e1 = e + EQm;
int i = 0;
for (; i < EQm; i++) {
*f++ = *e0++;
*f++ = *e1++;
}
} break;
case 4: {
uint32_t *e0 = e;
uint32_t *e1 = e0 + EQm;
uint32_t *e2 = e1 + EQm;
uint32_t *e3 = e2 + EQm;
int i = 0;
for (; i < EQm; i++) {
*f++ = *e0++;
*f++ = *e1++;
*f++ = *e2++;
*f++ = *e3++;
}
} break;
case 6: {
uint32_t *e0 = e;
uint32_t *e1 = e0 + EQm;
uint32_t *e2 = e1 + EQm;
uint32_t *e3 = e2 + EQm;
uint32_t *e4 = e3 + EQm;
uint32_t *e5 = e4 + EQm;
int i = 0;
#if 0
simde__m128i *e0_128 = (simde__m128i *)e0;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *e2_128 = (simde__m128i *)e2;
simde__m128i *e3_128 = (simde__m128i *)e3;
simde__m128i *e4_128 = (simde__m128i *)e4;
simde__m128i *e5_128 = (simde__m128i *)e5;
simde__m128i *f128 = (simde__m128i *)f;
for (; i < (EQm & ~3); i += 4) {
simde__m128i e0j = simde_mm_loadu_si128(e0_128++);
simde__m128i e1j = simde_mm_loadu_si128(e1_128++);
simde__m128i e2j = simde_mm_loadu_si128(e2_128++);
simde__m128i e3j = simde_mm_loadu_si128(e3_128++);
simde__m128i e4j = simde_mm_loadu_si128(e4_128++);
simde__m128i e5j = simde_mm_loadu_si128(e5_128++);
simde__m128i tmp0 = simde_mm_unpacklo_epi32(e0j, e1j); // e0(i) e1(i) e0(i+1) e1(i+1)
simde__m128i tmp1 = simde_mm_unpacklo_epi32(e2j, e3j); // e2(i) e3(i) e2(i+1) e3(i+1)
simde__m128i tmp2 = simde_mm_unpacklo_epi32(e4j, e5j); // e4(i) e5(i) e4(i+1) e5(i+1)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp0, tmp1)); // e0(i) e1(i) e2(i) e3(i)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp2,simde_mm_unpackhi_epi64(tmp0,tmp0))); // e4(i) e5(i) e0(i+1) e1(i+1)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp1,tmp2)); // e2(i+1) e3(i+1) e4(i+1) e5(i+1)
tmp0 = simde_mm_unpackhi_epi32(e0j, e1j); // e0(i+2) e1(i+2) e0(i+3) e1(i+3)
tmp1 = simde_mm_unpackhi_epi32(e2j, e3j); // e2(i+2) e3(i+2) e2(i+3) e3(i+3)
tmp2 = simde_mm_unpackhi_epi32(e4j, e5j); // e4(i+2) e5(i+2) e4(i+3) e5(i+3)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp0, tmp1)); // e0(i+2) e1(i+2) e2(i+2) e3(i+2)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp2,simde_mm_unpackhi_epi64(tmp0,tmp0))); // e4(i+2) e5(i+2) e0(i+3) e1(i+3)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp1,tmp2)); // e2(i+3) e3(i+3) e4(i+3) e5(i+3)
}
e0 = (uint32_t *)e0_128;
e1 = (uint32_t *)e1_128;
e2 = (uint32_t *)e2_128;
e3 = (uint32_t *)e3_128;
e4 = (uint32_t *)e4_128;
e5 = (uint32_t *)e5_128;
f = (uint32_t *)f128;
#endif
for (; i < EQm; i++) {
*f++ = *e0++;
*f++ = *e1++;
*f++ = *e2++;
*f++ = *e3++;
*f++ = *e4++;
*f++ = *e5++;
}
} break;
case 8: {
uint32_t *e0 = e;
uint32_t *e1 = e0 + EQm;
uint32_t *e2 = e1 + EQm;
uint32_t *e3 = e2 + EQm;
uint32_t *e4 = e3 + EQm;
uint32_t *e5 = e4 + EQm;
uint32_t *e6 = e5 + EQm;
uint32_t *e7 = e6 + EQm;
int i = 0;
#if 0 /*def __AVX2__, this needs to be fixed, revert to 128-bit below*/
simde__m256i *e0_256 = (simde__m256i *)e0;
simde__m256i *e1_256 = (simde__m256i *)e1;
simde__m256i *e2_256 = (simde__m256i *)e2;
simde__m256i *e3_256 = (simde__m256i *)e3;
simde__m256i *e4_256 = (simde__m256i *)e4;
simde__m256i *e5_256 = (simde__m256i *)e5;
simde__m256i *e6_256 = (simde__m256i *)e6;
simde__m256i *e7_256 = (simde__m256i *)e7;
simde__m256i *f256 = (simde__m256i *)f;
for (; i < (EQm & ~7); i += 8) {
simde__m256i e0j = simde_mm256_loadu_si256(e0_256++);
simde__m256i e1j = simde_mm256_loadu_si256(e1_256++);
simde__m256i e2j = simde_mm256_loadu_si256(e2_256++);
simde__m256i e3j = simde_mm256_loadu_si256(e3_256++);
simde__m256i e4j = simde_mm256_loadu_si256(e4_256++);
simde__m256i e5j = simde_mm256_loadu_si256(e5_256++);
simde__m256i e6j = simde_mm256_loadu_si256(e6_256++);
simde__m256i e7j = simde_mm256_loadu_si256(e7_256++);
simde__m256i tmp0 = simde_mm256_unpacklo_epi32(e0j, e1j); // e0(i) e1(i) e0(i+1) e1(i+1) e0(i+4) e1(i+4) e0(i+5) e1(i+5)
simde__m256i tmp1 = simde_mm256_unpacklo_epi32(e2j, e3j); // e2(i) e3(i) e2(i+1) e3(i+1) e2(i+4) e3(i+4) e2(i+5) e3(i+5)
simde__m256i tmp2 = simde_mm256_unpacklo_epi32(e4j, e5j); // e4(i) e5(i) e4(i+1) e5(i+1) e4(i+4) e5(i+4) e4(i+5) e5(i+5)
simde__m256i tmp3 = simde_mm256_unpacklo_epi32(e6j, e7j); // e6(i) e7(i) e6(i+1) e7(i+1) e6(i+4) e7(i+4) e6(i+5) e7(i+5)
//
simde__m256i tmp4 = simde_mm256_unpacklo_epi64(tmp0, tmp1); // e0(i) e1(i) e2(i) e3(i) e0(i+4) e1(i+4) e2(i+4) e3(i+4)
simde__m256i tmp5 = simde_mm256_unpacklo_epi64(tmp2, tmp3); // e4(i) e5(i) e6(i) e7(i) e4(i+4) e5(i+4) e6(i+4) e7(i+4)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp4,tmp5,0x20)); // e0(i) e1(i) e2(i) e3(i) e4(i) e5(i) e6(i) e7(i)
simde__m256i tmp6 = simde_mm256_unpackhi_epi64(tmp0, tmp1); // e0(i+1) e1(i+1) e2(i+1) e3(i+1) e0(i+5) e1(i+5) e2(i+5) e3(i+5)
simde__m256i tmp7 = simde_mm256_unpackhi_epi64(tmp2, tmp3); // e4(i+1) e5(i+1) e6(i+1) e7(i+1) e4(i+5) e5(i+5) e6(i+5) e7(i+5)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp6,tmp7,0x20)); // e0(i+1) e1(i+1) e2(i+1) e3(i+1) e4(i+1) e5(i+1) e6(i+1) e7(i+1)
//
tmp0 = simde_mm256_unpackhi_epi32(e0j, e1j); // e0(i+2) e1(i+2) e0(i+3) e1(i+3) e0(i+6) e1(i+6) e0(i+7) e1(i+7)
tmp1 = simde_mm256_unpackhi_epi32(e2j, e3j); // e2(i+2) e3(i+2) e2(i+3) e3(i+3) e2(i+6) e3(i+6) e2(i+7) e3(i+7)
tmp2 = simde_mm256_unpackhi_epi32(e4j, e5j); // e4(i+2) e5(i+2) e4(i+3) e5(i+3) e4(i+6) e5(i+6) e4(i+7) e5(i+7)
tmp3 = simde_mm256_unpackhi_epi32(e6j, e7j); // e6(i+2) e7(i+2) e6(i+3) e7(i+3) e6(i+6) e7(i+6) e6(i+7) e7(i+7)
//
simde__m256i tmp8 = simde_mm256_unpacklo_epi64(tmp0, tmp1); // e0(i+2) e1(i+2) e2(i+2) e3(i+2) e0(i+6) e1(i+6) e2(i+6) e3(i+6)
simde__m256i tmp9 = simde_mm256_unpacklo_epi64(tmp2, tmp3); // e4(i+2) e5(i+2) e6(i+2) e7(i+2) e4(i+6) e5(i+6) e6(i+6) e7(i+6)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp8,tmp9,0x20)); // e0(i+2) e1(i+2) e2(i+2) e3(i+2) e4(i+2) e5(i+2) e6(i+2) e7(i+2)
simde__m256i tmp10 = simde_mm256_unpackhi_epi64(tmp0, tmp1); // e0(i+3) e1(i+3) e2(i+3) e3(i+3) e0(i+7) e1(i+7) e2(i+7) e3(i+7)
simde__m256i tmp11 = simde_mm256_unpackhi_epi64(tmp2, tmp3); // e4(i+3) e5(i+3) e6(i+3) e7(i+3) e4(i+7) e5(i+7) e6(i+7) e7(i+7)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp10,tmp11,0x20)); // e0(i+3) e1(i+3) e2(i+3) e3(i+3) e4(i+3) e5(i+3) e6(i+3) e7(i+3)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp4,tmp5,0x31)); // e0(i+4) e1(i+4) e2(i+4) e3(i+4) e4(i+4) e5(i+4) e6(i+4) e7(i+4)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp6,tmp7,0x31)); // e0(i+5) e1(i+5) e2(i+5) e3(i+5) e4(i+5) e5(i+5) e6(i+5) e7(i+5)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp8,tmp9,0x31)); // e0(i+6) e1(i+6) e2(i+6) e3(i+6) e4(i+6) e5(i+6) e6(i+6) e7(i+6)
simde_mm256_storeu_si256(f256++,_mm256_permute2x128_si256(tmp10,tmp11,0x31)); // e0(i+7) e1(i+7) e2(i+7) e3(i+7) e4(i+7) e5(i+7) e6(i+7) e7(i+7)
}
e0 = (uint32_t *)e0_256;
e1 = (uint32_t *)e1_256;
e2 = (uint32_t *)e2_256;
e3 = (uint32_t *)e3_256;
e4 = (uint32_t *)e4_256;
e5 = (uint32_t *)e5_256;
e6 = (uint32_t *)e6_256;
e7 = (uint32_t *)e7_256;
f = (uint32_t *)f256;
#endif
simde__m128i *e0_128 = (simde__m128i *)e0;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *e2_128 = (simde__m128i *)e2;
simde__m128i *e3_128 = (simde__m128i *)e3;
simde__m128i *e4_128 = (simde__m128i *)e4;
simde__m128i *e5_128 = (simde__m128i *)e5;
simde__m128i *e6_128 = (simde__m128i *)e6;
simde__m128i *e7_128 = (simde__m128i *)e7;
simde__m128i *f128 = (simde__m128i *)f;
for (; i < (EQm & ~3); i += 4) {
simde__m128i e0j = simde_mm_loadu_si128(e0_128++);
simde__m128i e1j = simde_mm_loadu_si128(e1_128++);
simde__m128i e2j = simde_mm_loadu_si128(e2_128++);
simde__m128i e3j = simde_mm_loadu_si128(e3_128++);
simde__m128i e4j = simde_mm_loadu_si128(e4_128++);
simde__m128i e5j = simde_mm_loadu_si128(e5_128++);
simde__m128i e6j = simde_mm_loadu_si128(e6_128++);
simde__m128i e7j = simde_mm_loadu_si128(e7_128++);
simde__m128i tmp0 = simde_mm_unpacklo_epi32(e0j, e1j); // e0(i) e1(i) e0(i+1) e1(i+1)
simde__m128i tmp1 = simde_mm_unpacklo_epi32(e2j, e3j); // e2(i) e3(i) e2(i+1) e3(i+1)
simde__m128i tmp2 = simde_mm_unpacklo_epi32(e4j, e5j); // e4(i) e5(i) e4(i+1) e5(i+1)
simde__m128i tmp3 = simde_mm_unpacklo_epi32(e6j, e7j); // e6(i) e7(i) e6(i+1) e7(i+1)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp0, tmp1)); // e0(i) e1(i) e2(i) e3(i)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp2, tmp3)); // e4(i) e5(i) e6(i) e7(i)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp0, tmp1)); // e0(i+1) e1(i+1) e2(i+1) e3(i+1)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp2, tmp3)); // e4(i+1) e5(i+1) e6(i+1) e7(i+1)
tmp0 = simde_mm_unpackhi_epi32(e0j, e1j); // e0(i+2) e1(i+2) e0(i+3) e1(i+3)
tmp1 = simde_mm_unpackhi_epi32(e2j, e3j); // e2(i+2) e3(i+2) e2(i+3) e3(i+3)
tmp2 = simde_mm_unpackhi_epi32(e4j, e5j); // e4(i+2) e5(i+2) e4(i+3) e5(i+3)
tmp3 = simde_mm_unpackhi_epi32(e6j, e7j); // e6(i+2) e7(i+2) e6(i+3) e7(i+3)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp0, tmp1)); // e0(i+2) e1(i+2) e2(i+2) e3(i+2)
simde_mm_storeu_si128(f128++,simde_mm_unpacklo_epi64(tmp2, tmp3)); // e4(i+2) e5(i+2) e6(i+2) e7(i+2)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp0, tmp1)); // e0(i+3) e1(i+3) e2(i+3) e3(i+3)
simde_mm_storeu_si128(f128++,simde_mm_unpackhi_epi64(tmp2, tmp3)); //e4(i+3) e5(i+3) e6(i+3) e7(i+3)
}
e0 = (uint32_t *)e0_128;
e1 = (uint32_t *)e1_128;
e2 = (uint32_t *)e2_128;
e3 = (uint32_t *)e3_128;
e4 = (uint32_t *)e4_128;
e5 = (uint32_t *)e5_128;
e6 = (uint32_t *)e6_128;
e7 = (uint32_t *)e7_128;
f = (uint32_t *)f128;
}
break;
default:
AssertFatal(false, "Should be here!\n");
}
}
#if defined(__aarch64__)
static inline uint8x16_t tbl96_u8(uint8x16_t b0, uint8x16_t b1, uint8x16_t b2,
uint8x16_t b3, uint8x16_t b4, uint8x16_t b5,
uint8x16_t idx /* 0..95 */)
{
// Table 0: bytes 0..63
uint8x16x4_t T0 = {{ b0, b1, b2, b3 }};
uint8x16_t r0 = vqtbl4q_u8(T0, idx);
// Table 1: bytes 64..95, presented as a 64-byte table:
// bytes 0..31 map to original 64..95, bytes 32..63 are dummy (return 0)
uint8x16_t z = vdupq_n_u8(0);
uint8x16x4_t T1 = {{ b4, b5, z, z }};
// idx1 = idx - 64 (wrap-safe via unsigned subtract); only valid when idx>=64
uint8x16_t idx1 = vsubq_u8(idx, vdupq_n_u8(64));
uint8x16_t r1 = vqtbl4q_u8(T1, idx1);
// Select r1 where idx >= 64, else r0
uint8x16_t sel = vcgeq_u8(idx, vdupq_n_u8(64)); // 0xFF where idx>=64
return vbslq_u8(sel, r1, r0);
}
static inline uint8x16_t tbl128_u8(uint8x16_t b0, uint8x16_t b1, uint8x16_t b2, uint8x16_t b3,
uint8x16_t b4, uint8x16_t b5, uint8x16_t b6, uint8x16_t b7,
uint8x16_t idx /* 0..127 */)
{
// Table low: bytes 0..63
uint8x16x4_t T0 = {{ b0, b1, b2, b3 }};
uint8x16_t r0 = vqtbl4q_u8(T0, idx);
// Table high: bytes 64..127, mapped to 0..63 by subtracting 64
uint8x16x4_t T1 = {{ b4, b5, b6, b7 }};
uint8x16_t idx1 = vsubq_u8(idx, vdupq_n_u8(64));
uint8x16_t r1 = vqtbl4q_u8(T1, idx1);
// Select high where idx >= 64 else low
uint8x16_t sel = vcgeq_u8(idx, vdupq_n_u8(64));
return vbslq_u8(sel, r1, r0);
}
#elif defined(__AVX512BW__)
static inline __m512i idx_stride_u16(int ways, int k)
{
uint16_t idx[32] __attribute__((aligned(64)));
for (int n = 0; n < 32; n++) idx[n] = (uint16_t)(k + ways*n);
return _mm512_load_si512((const void*)idx);
}
// Build indices for k = 0..5, lanes n=0..15: p = k + 6*n
static inline __m512i make_idx_ab(int k) {
uint16_t idx[32] __attribute__((aligned(64))) = { 0 };
for (int n = 0; n < 16; n++) {
idx[n] = (uint16_t)(k + 6*n); // p in [0..95]
}
return _mm512_load_si512((const void*)idx);
}
static inline __m512i make_idx_bc(int k) {
uint16_t idx[32] __attribute__((aligned(64))) = { 0 };
for (int n = 0; n < 16; n++) {
int p = k + 6*n;
// For (B,C) pair, global p maps to:
// indices 0..31 -> B lanes 0..31 (global 32..63)
// indices 32..63 -> C lanes 0..31 (global 64..95)
// so use (p - 32) as index into [B|C] when p >= 32.
idx[n] = (uint16_t)(p - 32);
}
return _mm512_load_si512((const void*)idx);
}
static inline __mmask32 make_mask_bc(int k) {
// lanes needing BC are those with p >= 64 (since AB covers global 0..63)
__mmask32 m = 0;
for (int n = 0; n < 16; n++) {
int p = k + 6*n;
if (p >= 64) m |= (1u << n);
}
return m;
}
#endif
void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e, int16_t *f)
{
const uint32_t EQm = E / Qm;
switch (Qm) {
case 2: {
AssertFatal(E % 2 == 0, "");
int16_t *e1 = e + (E / 2);
int16_t *end = f + E - 1;
while (f < end) {
int16_t *e1 = e + EQm;
int i=0;
#if defined(__aarch64__)
for (; i + 8 <= EQm; i += 8) {
int16x8x2_t v = vld2q_s16(f); // 8 groups
f += 16;
vst1q_s16(e, v.val[0]); e += 8;
vst1q_s16(e1, v.val[1]); e1 += 8;
}
#elif defined(__AVX512BW__)
const __m512i idx0 = idx_stride_u16(2, 0);
const __m512i idx1 = idx_stride_u16(2, 1);
for (; i + 32 <= EQm; i += 32) {
// 32 groups * 2 u16 = 64 u16 = 2x512b
__m512i A = _mm512_loadu_si512((const void*)(f + 0)); // u16[0..31]
__m512i B = _mm512_loadu_si512((const void*)(f + 32)); // u16[32..63]
f += 64;
__m512i o0 = _mm512_permutex2var_epi16(A, idx0, B);
__m512i o1 = _mm512_permutex2var_epi16(A, idx1, B);
_mm512_storeu_si512((void*)e, o0); e += 32;
_mm512_storeu_si512((void*)e1, o1); e1 += 32;
}
#else
simde__m128i *e0_128 = (simde__m128i *)e;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *f128 = (simde__m128i *)f;
const uint8_t shuf4[16] __attribute__((aligned(16))) = {0,1,4,5,8,9,12,13,2,3,6,7,10,11,14,15};
const simde__m128i *shuf4_128 = (const simde__m128i *)shuf4;
for (; i < (EQm & ~7); i += 8) {
simde__m128i f0j = simde_mm_loadu_si128(f128++); // f0(i) f0(i+1) f0(i+2) f0(i+3) f0(i+4) f0(i+5) f0(i+6) f0(i+7)
simde__m128i f1j = simde_mm_loadu_si128(f128++); // f1(i) f1(i+1) f1(i+2) f1(i+3) f1(i+4) f1(i+5) f1(i+6) f1(i+7)
simde__m128i tmp0 = simde_mm_shuffle_epi8(f0j,*shuf4_128); // f0(i) f0(i+2) f0(i+4) f0(i+6) f0(i+1) f0(i+3) f0(i+5) f0(i+7)
simde__m128i tmp1 = simde_mm_shuffle_epi8(f1j,*shuf4_128); // f1(i) f1(i+2) f1(i+4) f1(i+6) f1(i+1) f1(i+3) f1(i+5) f1(i+7)
simde_mm_storeu_si128(e0_128++,simde_mm_unpacklo_epi64(tmp0, tmp1)); // f0(i) f0(i+2) f0(i+4) f0(i+6) f1(i) f1(i+2) f1(i+4) f1(i+6)
simde_mm_storeu_si128(e1_128++,simde_mm_unpackhi_epi64(tmp0, tmp1)); // f0(i+1) f0(i+3) f0(i+5) f0(i+7) f1(i+1) f1(i+3) f1(i+5) f1(i+7)
}
e=(int16_t *)e0_128;
e1=(int16_t *)e1_128;
f=(int16_t *)f128;
#endif
for (; i < EQm; i++) {
*e++ = *f++;
*e1++ = *f++;
}
} break;
case 4: {
AssertFatal(E % 4 == 0, "");
int16_t *e1 = e + (E / 4);
int16_t *e2 = e1 + (E / 4);
int16_t *e3 = e2 + (E / 4);
int16_t *end = f + E - 3;
while (f < end) {
int i=0;
int16_t *e1 = e + EQm;
int16_t *e2 = e1 + EQm;
int16_t *e3 = e2 + EQm;
#if defined(__aarch64__)
for (; i + 8 <= EQm; i += 8) {
int16x8x4_t v = vld4q_s16(f); // 8 groups
f += 32;
vst1q_s16(e, v.val[0]); e += 8;
vst1q_s16(e1, v.val[1]); e1 += 8;
vst1q_s16(e2, v.val[2]); e2 += 8;
vst1q_s16(e3, v.val[3]); e3 += 8;
}
#elif defined(__AVX512BW__)
const __m512i idx0 = idx_stride_u16(4, 0);
const __m512i idx1 = idx_stride_u16(4, 1);
const __m512i idx2 = idx_stride_u16(4, 2);
const __m512i idx3 = idx_stride_u16(4, 3);
for (; i + 32 <= EQm; i += 32) {
// 32 groups * 4 u16 = 128 u16 = 4x512b
__m512i A = _mm512_loadu_si512((const void*)(f + 0));
__m512i B = _mm512_loadu_si512((const void*)(f + 32));
__m512i C = _mm512_loadu_si512((const void*)(f + 64));
__m512i D = _mm512_loadu_si512((const void*)(f + 96));
f += 128;
// Build results from (A,B) and (C,D) with masking (boundary at 64 u16)
__m512i o0_ab = _mm512_permutex2var_epi16(A, idx0, B);
__m512i o1_ab = _mm512_permutex2var_epi16(A, idx1, B);
__m512i o2_ab = _mm512_permutex2var_epi16(A, idx2, B);
__m512i o3_ab = _mm512_permutex2var_epi16(A, idx3, B);
__m512i idx0_cd = _mm512_sub_epi16(idx0, _mm512_set1_epi16(64));
__m512i idx1_cd = _mm512_sub_epi16(idx1, _mm512_set1_epi16(64));
__m512i idx2_cd = _mm512_sub_epi16(idx2, _mm512_set1_epi16(64));
__m512i idx3_cd = _mm512_sub_epi16(idx3, _mm512_set1_epi16(64));
__m512i o0_cd = _mm512_permutex2var_epi16(C, idx0_cd, D);
__m512i o1_cd = _mm512_permutex2var_epi16(C, idx1_cd, D);
__m512i o2_cd = _mm512_permutex2var_epi16(C, idx2_cd, D);
__m512i o3_cd = _mm512_permutex2var_epi16(C, idx3_cd, D);
// lanes needing CD are those where idx >= 64
__mmask32 m0 = _mm512_cmpge_epu16_mask(idx0, _mm512_set1_epi16(64));
__mmask32 m1 = _mm512_cmpge_epu16_mask(idx1, _mm512_set1_epi16(64));
__mmask32 m2 = _mm512_cmpge_epu16_mask(idx2, _mm512_set1_epi16(64));
__mmask32 m3 = _mm512_cmpge_epu16_mask(idx3, _mm512_set1_epi16(64));
__m512i o0 = _mm512_mask_mov_epi16(o0_ab, m0, o0_cd);
__m512i o1 = _mm512_mask_mov_epi16(o1_ab, m1, o1_cd);
__m512i o2 = _mm512_mask_mov_epi16(o2_ab, m2, o2_cd);
__m512i o3 = _mm512_mask_mov_epi16(o3_ab, m3, o3_cd);
_mm512_storeu_si512((void*)e, o0); e += 32;
_mm512_storeu_si512((void*)e1, o1); e1 += 32;
_mm512_storeu_si512((void*)e2, o2); e2 += 32;
_mm512_storeu_si512((void*)e3, o3); e3 += 32;
}
#else
simde__m128i *e0_128 = (simde__m128i *)e;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *e2_128 = (simde__m128i *)e2;
simde__m128i *e3_128 = (simde__m128i *)e3;
simde__m128i *f128 = (simde__m128i *)f;
const uint8_t shuf16[16] __attribute__((aligned(16))) = {0,1,8,9,2,3,10,11,4,5,12,13,6,7,14,15};
const simde__m128i *shuf16_128 = (const simde__m128i *)shuf16;
for (i=0; i < (EQm & ~7); i += 8) {
simde__m128i f0j = simde_mm_loadu_si128(f128++);
simde__m128i f1j = simde_mm_loadu_si128(f128++);
simde__m128i f2j = simde_mm_loadu_si128(f128++);
simde__m128i f3j = simde_mm_loadu_si128(f128++);
simde__m128i tmp0 = simde_mm_shuffle_epi8(f0j,*shuf16_128); // f0(i) f0(i+4) f0(i+1) f0(i+5) f0(i+2) f0(i+6) f0(i+3) f0(i+7)
simde__m128i tmp1 = simde_mm_shuffle_epi8(f1j,*shuf16_128); // f1(i) f1(i+4) f1(i+1) f1(i+5) f1(i+2) f1(i+6) f1(i+3) f1(i+7)
simde__m128i tmp2 = simde_mm_shuffle_epi8(f2j,*shuf16_128); // f2(i) f2(i+4) f2(i+1) f2(i+5) f2(i+2) f2(i+6) f2(i+3) f2(i+7)
simde__m128i tmp3 = simde_mm_shuffle_epi8(f3j,*shuf16_128); // f3(i) f3(i+2) f3(i+1) f3(i+5) f3(i+2) f3(i+6) f3(i+3) f3(i+7)
simde__m128i tmp4 = simde_mm_unpacklo_epi32(tmp0,tmp1); // f0(i) f0(i+4) f1(i) f1(i+4) f0(i+1) f0(i+5) f1(i+1) f1(i+5)
simde__m128i tmp5 = simde_mm_unpacklo_epi32(tmp2,tmp3); // f2(i) f2(i+4) f3(i) f3(i+4) f2(i+1) f2(i+5) f3(i+1) f3(i+5)
simde_mm_storeu_si128(e0_128++,simde_mm_unpacklo_epi64(tmp4, tmp5)); // f0(i) f0(i+4) f1(i) f1(i+4) f2(i) f2(i+4) f3(i) f3(i+4)
simde_mm_storeu_si128(e1_128++,simde_mm_unpackhi_epi64(tmp4, tmp5)); // f0(i+1) f0(i+5) f1(i+1) f1(i+5) f2(i+1) f2(i+5) f3(i+1) f3(i+5)
tmp4 = simde_mm_unpackhi_epi32(tmp0,tmp1); // f0(i+2) f0(i+6) f1(i+2) f1(i+6) f0(i+3) f0(i+7) f1(i+3) f1(i+7)
tmp5 = simde_mm_unpackhi_epi32(tmp2,tmp3); // f2(i+2) f2(i+6) f3(i+2) f3(i+6) f2(i+3) f2(i+7) f3(i+3) f3(i+7)
simde_mm_storeu_si128(e2_128++,simde_mm_unpacklo_epi64(tmp4, tmp5)); // f0(i+2) f0(i+6) f1(i+2) f1(i+6) f2(i+2) f2(i+6) f3(i+2) f3(i+6)
simde_mm_storeu_si128(e3_128++,simde_mm_unpackhi_epi64(tmp4, tmp5)); // f0(i+3) f0(i+7) f1(i+3) f1(i+7) f2(i+3) f2(i+7) f3(i+3) f3(i+7)
}
e=(int16_t *)e0_128;
e1=(int16_t *)e1_128;
e2=(int16_t *)e2_128;
e3=(int16_t *)e3_128;
f=(int16_t *)f128;
#endif
for (; i < EQm; i++) {
*e++ = *f++;
*e1++ = *f++;
*e2++ = *f++;
@@ -454,13 +883,108 @@ void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e, int16_t *f)
} break;
case 6: {
AssertFatal(E % 6 == 0, "");
int16_t *e1 = e + (E / 6);
int16_t *e2 = e1 + (E / 6);
int16_t *e3 = e2 + (E / 6);
int16_t *e4 = e3 + (E / 6);
int16_t *e5 = e4 + (E / 6);
int16_t *end = f + E - 5;
while (f < end) {
int16_t *e1 = e + EQm;
int16_t *e2 = e1 + EQm;
int16_t *e3 = e2 + EQm;
int16_t *e4 = e3 + EQm;
int16_t *e5 = e4 + EQm;
int i=0;
#if defined(__aarch64__)
// Byte indices for extracting each stream
// Each s16 occupies 2 bytes → indices are 2*(k + 6*n)
const uint8x16_t idx0 = { 0, 1, 12, 13, 24, 25, 36, 37,
48, 49, 60, 61, 72, 73, 84, 85 };
const uint8x16_t idx1 = { 2, 3, 14, 15, 26, 27, 38, 39,
50, 51, 62, 63, 74, 75, 86, 87 };
const uint8x16_t idx2 = { 4, 5, 16, 17, 28, 29, 40, 41,
52, 53, 64, 65, 76, 77, 88, 89 };
const uint8x16_t idx3 = { 6, 7, 18, 19, 30, 31, 42, 43,
54, 55, 66, 67, 78, 79, 90, 91 };
const uint8x16_t idx4 = { 8, 9, 20, 21, 32, 33, 44, 45,
56, 57, 68, 69, 80, 81, 92, 93 };
const uint8x16_t idx5 = { 10, 11, 22, 23, 34, 35, 46, 47,
58, 59, 70, 71, 82, 83, 94, 95 };
for (; i + 8 < EQm; i += 8) {
// Load 96 bytes (48 u16)
uint8x16_t b0 = vld1q_u8((const uint8_t*)(f + 0)); // bytes 0..15
uint8x16_t b1 = vld1q_u8((const uint8_t*)(f + 8)); // bytes 16..31
uint8x16_t b2 = vld1q_u8((const uint8_t*)(f + 16)); // bytes 32..47
uint8x16_t b3 = vld1q_u8((const uint8_t*)(f + 24)); // bytes 48..63
uint8x16_t b4 = vld1q_u8((const uint8_t*)(f + 32)); // bytes 64..79
uint8x16_t b5 = vld1q_u8((const uint8_t*)(f + 40)); // bytes 80..95
f += 48;
uint8x16_t o0b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx0);
uint8x16_t o1b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx1);
uint8x16_t o2b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx2);
uint8x16_t o3b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx3);
uint8x16_t o4b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx4);
uint8x16_t o5b = tbl96_u8(b0,b1,b2,b3,b4,b5, idx5);
vst1q_s16(e, vreinterpretq_s16_u8(o0b)); e += 8;
vst1q_s16(e1, vreinterpretq_s16_u8(o1b)); e1 += 8;
vst1q_s16(e2, vreinterpretq_s16_u8(o2b)); e2 += 8;
vst1q_s16(e3, vreinterpretq_s16_u8(o3b)); e3 += 8;
vst1q_s16(e4, vreinterpretq_s16_u8(o4b)); e4 += 8;
vst1q_s16(e5, vreinterpretq_s16_u8(o5b)); e5 += 8;
}
#elif defined(__AVX512BW__)
// Precompute permute control once (all compile-time constant patterns)
const __m512i idx0_ab = make_idx_ab(0), idx1_ab = make_idx_ab(1);
const __m512i idx2_ab = make_idx_ab(2), idx3_ab = make_idx_ab(3);
const __m512i idx4_ab = make_idx_ab(4), idx5_ab = make_idx_ab(5);
const __m512i idx0_bc = make_idx_bc(0), idx1_bc = make_idx_bc(1);
const __m512i idx2_bc = make_idx_bc(2), idx3_bc = make_idx_bc(3);
const __m512i idx4_bc = make_idx_bc(4), idx5_bc = make_idx_bc(5);
const __mmask32 m0 = make_mask_bc(0), m1 = make_mask_bc(1), m2 = make_mask_bc(2);
const __mmask32 m3 = make_mask_bc(3), m4 = make_mask_bc(4), m5 = make_mask_bc(5);
const __mmask32 store16 = 0xFFFFu; // store only first 16 lanes (16x u16)
for (; i + 16 <= EQm; i += 16) {
// 16 groups = 16*(6 u16) = 96 u16 = 192 bytes = 3 * 64B
__m512i A = _mm512_loadu_si512((const void*)(f + 0)); // u16[0..31]
__m512i B = _mm512_loadu_si512((const void*)(f + 32)); // u16[32..63]
__m512i C = _mm512_loadu_si512((const void*)(f + 64)); // u16[64..95]
f += 96;
// Gather from AB (global 0..63) and BC (global 32..95)
__m512i o0_ab = _mm512_permutex2var_epi16(A, idx0_ab, B);
__m512i o1_ab = _mm512_permutex2var_epi16(A, idx1_ab, B);
__m512i o2_ab = _mm512_permutex2var_epi16(A, idx2_ab, B);
__m512i o3_ab = _mm512_permutex2var_epi16(A, idx3_ab, B);
__m512i o4_ab = _mm512_permutex2var_epi16(A, idx4_ab, B);
__m512i o5_ab = _mm512_permutex2var_epi16(A, idx5_ab, B);
__m512i o0_bc = _mm512_permutex2var_epi16(B, idx0_bc, C);
__m512i o1_bc = _mm512_permutex2var_epi16(B, idx1_bc, C);
__m512i o2_bc = _mm512_permutex2var_epi16(B, idx2_bc, C);
__m512i o3_bc = _mm512_permutex2var_epi16(B, idx3_bc, C);
__m512i o4_bc = _mm512_permutex2var_epi16(B, idx4_bc, C);
__m512i o5_bc = _mm512_permutex2var_epi16(B, idx5_bc, C);
// Select lanes that crossed the 64-element boundary
__m512i o0 = _mm512_mask_mov_epi16(o0_ab, m0, o0_bc);
__m512i o1 = _mm512_mask_mov_epi16(o1_ab, m1, o1_bc);
__m512i o2 = _mm512_mask_mov_epi16(o2_ab, m2, o2_bc);
__m512i o3 = _mm512_mask_mov_epi16(o3_ab, m3, o3_bc);
__m512i o4 = _mm512_mask_mov_epi16(o4_ab, m4, o4_bc);
__m512i o5 = _mm512_mask_mov_epi16(o5_ab, m5, o5_bc);
// Store only the first 16 lanes (16 groups) to each plane
_mm512_mask_storeu_epi16((void*)e, store16, o0); e += 16;
_mm512_mask_storeu_epi16((void*)e1, store16, o1); e1 += 16;
_mm512_mask_storeu_epi16((void*)e2, store16, o2); e2 += 16;
_mm512_mask_storeu_epi16((void*)e3, store16, o3); e3 += 16;
_mm512_mask_storeu_epi16((void*)e4, store16, o4); e4 += 16;
_mm512_mask_storeu_epi16((void*)e5, store16, o5); e5 += 16;
}
//
#endif
for (; i < EQm; i++) {
*e++ = *f++;
*e1++ = *f++;
*e2++ = *f++;
@@ -471,15 +995,181 @@ void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e, int16_t *f)
} break;
case 8: {
AssertFatal(E % 8 == 0, "");
int16_t *e1 = e + (E / 8);
int16_t *e2 = e1 + (E / 8);
int16_t *e3 = e2 + (E / 8);
int16_t *e4 = e3 + (E / 8);
int16_t *e5 = e4 + (E / 8);
int16_t *e6 = e5 + (E / 8);
int16_t *e7 = e6 + (E / 8);
int16_t *end = f + E - 7;
while (f < end) {
int16_t *e1 = e + EQm;
int16_t *e2 = e1 + EQm;
int16_t *e3 = e2 + EQm;
int16_t *e4 = e3 + EQm;
int16_t *e5 = e4 + EQm;
int16_t *e6 = e5 + EQm;
int16_t *e7 = e6 + EQm;
int i=0;
#if 0 //defined(__aarch64__)
// SIMDE version below is more efficient than tbl128
// For 8 groups: byte indices for stream k are 2*(k + 8*n) for n=0..7.
// That is: 2k + 16n (and +1 for the high byte of the u16).
const uint8x16_t idx0 = { 0, 1, 16, 17, 32, 33, 48, 49, 64, 65, 80, 81, 96, 97,112,113 };
const uint8x16_t idx1 = { 2, 3, 18, 19, 34, 35, 50, 51, 66, 67, 82, 83, 98, 99,114,115 };
const uint8x16_t idx2 = { 4, 5, 20, 21, 36, 37, 52, 53, 68, 69, 84, 85,100,101,116,117 };
const uint8x16_t idx3 = { 6, 7, 22, 23, 38, 39, 54, 55, 70, 71, 86, 87,102,103,118,119 };
const uint8x16_t idx4 = { 8, 9, 24, 25, 40, 41, 56, 57, 72, 73, 88, 89,104,105,120,121 };
const uint8x16_t idx5 = { 10, 11, 26, 27, 42, 43, 58, 59, 74, 75, 90, 91,106,107,122,123 };
const uint8x16_t idx6 = { 12, 13, 28, 29, 44, 45, 60, 61, 76, 77, 92, 93,108,109,124,125 };
const uint8x16_t idx7 = { 14, 15, 30, 31, 46, 47, 62, 63, 78, 79, 94, 95,110,111,126,127 };
for (; i + 8 <= EQm; i += 8) {
// Load 128 bytes = 64 u16 = 8 groups (aligned-friendly, but vld1q_u8 is fine either way)
uint8x16_t b0 = vld1q_u8((const uint8_t*)(f + 0)); // bytes 0..15
uint8x16_t b1 = vld1q_u8((const uint8_t*)(f + 8)); // bytes 16..31
uint8x16_t b2 = vld1q_u8((const uint8_t*)(f + 16)); // bytes 32..47
uint8x16_t b3 = vld1q_u8((const uint8_t*)(f + 24)); // bytes 48..63
uint8x16_t b4 = vld1q_u8((const uint8_t*)(f + 32)); // bytes 64..79
uint8x16_t b5 = vld1q_u8((const uint8_t*)(f + 40)); // bytes 80..95
uint8x16_t b6 = vld1q_u8((const uint8_t*)(f + 48)); // bytes 96..111
uint8x16_t b7 = vld1q_u8((const uint8_t*)(f + 56)); // bytes 112..127
f += 64; // consumed 64 u16
uint8x16_t o0b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx0);
uint8x16_t o1b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx1);
uint8x16_t o2b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx2);
uint8x16_t o3b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx3);
uint8x16_t o4b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx4);
uint8x16_t o5b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx5);
uint8x16_t o6b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx6);
uint8x16_t o7b = tbl128_u8(b0,b1,b2,b3,b4,b5,b6,b7, idx7);
vst1q_s16(e, vreinterpretq_s16_u8(o0b)); e += 8;
vst1q_s16(e1, vreinterpretq_s16_u8(o1b)); e1 += 8;
vst1q_s16(e2, vreinterpretq_s16_u8(o2b)); e2 += 8;
vst1q_s16(e3, vreinterpretq_s16_u8(o3b)); e3 += 8;
vst1q_s16(e4, vreinterpretq_s16_u8(o4b)); e4 += 8;
vst1q_s16(e5, vreinterpretq_s16_u8(o5b)); e5 += 8;
vst1q_s16(e6, vreinterpretq_s16_u8(o6b)); e6 += 8;
vst1q_s16(e7, vreinterpretq_s16_u8(o7b)); e7 += 8;
}
#elif defined(__AVX512BW__)
// Precompute indices for each stream k=0..7
const __m512i idx0 = idx_stride_u16(8,0);
const __m512i idx1 = idx_stride_u16(8,1);
const __m512i idx2 = idx_stride_u16(8,2);
const __m512i idx3 = idx_stride_u16(8,3);
const __m512i idx4 = idx_stride_u16(8,4);
const __m512i idx5 = idx_stride_u16(8,5);
const __m512i idx6 = idx_stride_u16(8,6);
const __m512i idx7 = idx_stride_u16(8,7);
const __m512i c64 = _mm512_set1_epi16(64);
const __m512i c128 = _mm512_set1_epi16(128);
const __m512i c192 = _mm512_set1_epi16(192);
for (; i + 32 <= EQm; i += 32) {
// 32 groups * 8 u16 = 256 u16 = 8 * 32 u16 = 8 ZMM vectors
__m512i A = _mm512_loadu_si512((const void*)(f + 0)); // u16[ 0.. 31]
__m512i B = _mm512_loadu_si512((const void*)(f + 32)); // u16[ 32.. 63]
__m512i C = _mm512_loadu_si512((const void*)(f + 64)); // u16[ 64.. 95]
__m512i D = _mm512_loadu_si512((const void*)(f + 96)); // u16[ 96..127]
__m512i E = _mm512_loadu_si512((const void*)(f + 128)); // u16[128..159]
__m512i F = _mm512_loadu_si512((const void*)(f + 160)); // u16[160..191]
__m512i G = _mm512_loadu_si512((const void*)(f + 192)); // u16[192..223]
__m512i H = _mm512_loadu_si512((const void*)(f + 224)); // u16[224..255]
f += 256;
// Helper macro: compute one stream (k) by permuting from 4 segments and blending
#define DO_STREAM(IDX, OUTPTR) do { \
/* segment masks based on global index p = k+8*n */ \
__mmask32 m1 = _mm512_cmpge_epu16_mask((IDX), c64); \
__mmask32 m2 = _mm512_cmpge_epu16_mask((IDX), c128); \
__mmask32 m3 = _mm512_cmpge_epu16_mask((IDX), c192); \
/* indices relative to each segment base */ \
__m512i i0 = (IDX); \
__m512i i1 = _mm512_sub_epi16((IDX), c64); \
__m512i i2 = _mm512_sub_epi16((IDX), c128); \
__m512i i3 = _mm512_sub_epi16((IDX), c192); \
/* permute from each 64-u16 segment (two ZMMs per segment) */ \
__m512i r0 = _mm512_permutex2var_epi16(A, i0, B); \
__m512i r1 = _mm512_permutex2var_epi16(C, i1, D); \
__m512i r2 = _mm512_permutex2var_epi16(E, i2, F); \
__m512i r3 = _mm512_permutex2var_epi16(G, i3, H); \
/* blend: later segments overwrite earlier where applicable */ \
__m512i r = _mm512_mask_mov_epi16(r0, m1, r1); \
r = _mm512_mask_mov_epi16(r, m2, r2); \
r = _mm512_mask_mov_epi16(r, m3, r3); \
_mm512_storeu_si512((void*)(OUTPTR), r); \
(OUTPTR) += 32; \
} while(0)
DO_STREAM(idx0, e);
DO_STREAM(idx1, e1);
DO_STREAM(idx2, e2);
DO_STREAM(idx3, e3);
DO_STREAM(idx4, e4);
DO_STREAM(idx5, e5);
DO_STREAM(idx6, e6);
DO_STREAM(idx7, e7);
#undef DO_STREAM
}
#else
simde__m128i *e0_128 = (simde__m128i *)e;
simde__m128i *e1_128 = (simde__m128i *)e1;
simde__m128i *e2_128 = (simde__m128i *)e2;
simde__m128i *e3_128 = (simde__m128i *)e3;
simde__m128i *e4_128 = (simde__m128i *)e4;
simde__m128i *e5_128 = (simde__m128i *)e5;
simde__m128i *e6_128 = (simde__m128i *)e6;
simde__m128i *e7_128 = (simde__m128i *)e7;
simde__m128i *f128 = (simde__m128i *)f;
for (i=0; i < (EQm & ~7); i += 8) {
simde__m128i f0j = simde_mm_loadu_si128(f128++);
simde__m128i f1j = simde_mm_loadu_si128(f128++);
simde__m128i f2j = simde_mm_loadu_si128(f128++);
simde__m128i f3j = simde_mm_loadu_si128(f128++);
simde__m128i f4j = simde_mm_loadu_si128(f128++);
simde__m128i f5j = simde_mm_loadu_si128(f128++);
simde__m128i f6j = simde_mm_loadu_si128(f128++);
simde__m128i f7j = simde_mm_loadu_si128(f128++);
simde__m128i tmp0 = simde_mm_unpacklo_epi16(f0j, f1j); // f0(i) f1(i) f0(i+1) f1(i+1) f0(i+2) f1(i+2) f0(i+3) f1(i+3)
simde__m128i tmp1 = simde_mm_unpacklo_epi16(f2j, f3j); // f2(i) f3(i) f2(i+1) f3(i+1) f2(i+2) f3(i+2) f2(i+3) f3(i+3)
simde__m128i tmp2 = simde_mm_unpacklo_epi16(f4j, f5j); // f4(i) f5(i) f4(i+1) f5(i+1) f4(i+2) f5(i+2) f4(i+3) f5(i+3)
simde__m128i tmp3 = simde_mm_unpacklo_epi16(f6j, f7j); // f6(i) f7(i) f6(i+1) f7(i+1) f6(i+2) f7(i+2) f6(i+3) f7(i+3)
//
simde__m128i tmp4 = simde_mm_unpacklo_epi32(tmp0, tmp1); // f0(i) f1(i) f2(i) f3(i) f0(i+1) f1(i+1) f2(i+1) f3(i+1)
simde__m128i tmp5 = simde_mm_unpacklo_epi32(tmp2, tmp3); // f4(i) f5(i) f6(i) f7(i) f4(i+1) f5(i+1) f6(i+1) f7(i+1)
simde_mm_storeu_si128(e0_128++,simde_mm_unpacklo_epi64(tmp4, tmp5)); // f0(i) f1(i) f2(i) f3(i) f4(i) f5(i) f7(i) f7(i)
simde_mm_storeu_si128(e1_128++,simde_mm_unpackhi_epi64(tmp4, tmp5));// f0(i+1) f1(i+1) f2(i+1) f3(i+1) f4(i+1) f5(i+1) f6(i+1) f7(i+1)
//
tmp4 = simde_mm_unpackhi_epi32(tmp0, tmp1); // f0(i+2) f1(i+2) f2(i+2) f3(i+2) f0(i+3) f1(i+3) f2(i+3) f3(i+3)
tmp5 = simde_mm_unpackhi_epi32(tmp2, tmp3); // f4(i+2) f5(i+2) f6(i+2) f7(i+2) f4(i+3) f5(i+3) f6(i+3) f7(i+3)
simde_mm_storeu_si128(e2_128++,simde_mm_unpacklo_epi64(tmp4, tmp5));// f0(i+2) f1(i+2) f2(i+2) f3(i+2) f4(i+2) f5(i+2) f7(i+2) f7(i+2)
simde_mm_storeu_si128(e3_128++,simde_mm_unpackhi_epi64(tmp4, tmp5)); // f0(i+3) f1(i+3) f2(i+3) f3(i+3) f4(i+3) f5(i+3) f7(i+3) f7(i+3)
tmp0 = simde_mm_unpackhi_epi16(f0j, f1j); // f0(i+4) f1(i+4) f0(i+5) f1(i+5) f0(i+6) f1(i+6) f0(i+7) f1(i+7)
tmp1 = simde_mm_unpackhi_epi16(f2j, f3j); // f2(i+4) f3(i+4) f2(i+5) f3(i+5) f2(i+6) f3(i+6) f2(i+7) f3(i+7)
tmp2 = simde_mm_unpackhi_epi16(f4j, f5j); // f4(i+4) f5(i+4) f4(i+5) f5(i+5) f4(i+6) f5(i+6) f4(i+7) f5(i+7)
tmp3 = simde_mm_unpackhi_epi16(f6j, f7j); // f6(i+4) f7(i+4) f6(i+5) f7(i+5) f6(i+6) f7(i+6) f6(i+7) f7(i+7)
//
tmp4 = simde_mm_unpacklo_epi32(tmp0, tmp1); // f0(i+4) f1(i+4) f2(i+4) f3(i+4) f0(i+5) f1(i+5) f2(i+5) f3(i+5)
tmp5 = simde_mm_unpacklo_epi32(tmp2, tmp3); // f4(i+4) f5(i+4) f6(i+4) f7(i+4) f4(i+5) f5(i+5) f6(i+5) f7(i+5)
simde_mm_storeu_si128(e4_128++,simde_mm_unpacklo_epi64(tmp4, tmp5)); // f0(i+4) f1(i+4) f2(i+4) f3(i+4) f4(i+4) f5(i+4) f7(i+4) f7(i+4)
simde_mm_storeu_si128(e5_128++,simde_mm_unpackhi_epi64(tmp4, tmp5));// f0(i+5) f1(i+5) f2(i+5) f3(i+5) f4(i+5) f5(i+5) f6(i+5) f7(i+5)
tmp4 = simde_mm_unpackhi_epi32(tmp0, tmp1); // f0(i+6) f1(i+6) f2(i+6) f3(i+6) f0(i+6) f1(i+6) f2(i+6) f3(i+6)
tmp5 = simde_mm_unpackhi_epi32(tmp2, tmp3); // f4(i+6) f5(i+6) f6(i+6) f7(i+6) f4(i+6) f5(i+6) f6(i+6) f7(i+6)
simde_mm_storeu_si128(e6_128++,simde_mm_unpacklo_epi64(tmp4, tmp5));// f0(i+6) f1(i+6) f2(i+6) f3(i+6) f4(i+6) f5(i+6) f7(i+6) f7(i+6)
simde_mm_storeu_si128(e7_128++,simde_mm_unpackhi_epi64(tmp4, tmp5)); // f0(i+7) f1(i+7) f2(i+7) f3(i+7) f4(i+7) f5(i+7) f7(i+7) f7(i+7)
}
e = (int16_t *)e0_128;
e1 = (int16_t *)e1_128;
e2 = (int16_t *)e2_128;
e3 = (int16_t *)e3_128;
e4 = (int16_t *)e4_128;
e5 = (int16_t *)e5_128;
e6 = (int16_t *)e6_128;
e7 = (int16_t *)e7_128;
f = (int16_t *)f128;
#endif
for(;i < EQm ; i++) {
*e++ = *f++;
*e1++ = *f++;
*e2++ = *f++;
@@ -494,6 +1184,7 @@ void nr_deinterleaving_ldpc(uint32_t E, uint8_t Qm, int16_t *e, int16_t *f)
AssertFatal(1 == 0, "Should not get here : Qm %d\n", Qm);
break;
}
}
int nr_rate_matching_ldpc(uint32_t Tbslbrm,
@@ -597,6 +1288,108 @@ int nr_rate_matching_ldpc(uint32_t Tbslbrm,
return 0;
}
int nr_rate_matching_ldpc32(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
uint32_t *d,
uint32_t *e,
uint8_t C,
uint32_t F,
uint32_t Foffset,
uint8_t rvidx,
uint32_t E)
{
if (C == 0) {
LOG_E(PHY, "nr_rate_matching: invalid parameter C %d\n", C);
return -1;
}
//Bit selection
uint32_t N = (BG == 1) ? (66 * Z) : (50 * Z);
uint32_t Ncb;
if (Tbslbrm == 0)
Ncb = N;
else {
uint32_t Nref = 3 * Tbslbrm / (2 * C); //R_LBRM = 2/3
Ncb = min(N, Nref);
}
uint32_t ind = (index_k0[BG - 1][rvidx] * Ncb / N) * Z;
#ifdef RM_DEBUG
printf("nr_rate_matching_ldpc: E %u, F %u, Foffset %u, k0 %u, Ncb %u, rvidx %d, Tbslbrm %u\n",
E,
F,
Foffset,
ind,
Ncb,
rvidx,
Tbslbrm);
#endif
if (Foffset > E) {
LOG_E(PHY,
"nr_rate_matching: invalid parameters (Foffset %d > E %d) F %d, k0 %d, Ncb %d, rvidx %d, Tbslbrm %d\n",
Foffset,
E,
F,
ind,
Ncb,
rvidx,
Tbslbrm);
return -1;
}
if (Foffset > Ncb) {
LOG_E(PHY, "nr_rate_matching: invalid parameters (Foffset %d > Ncb %d)\n", Foffset, Ncb);
return -1;
}
if (ind >= Foffset && ind < (F + Foffset))
ind = F + Foffset;
uint32_t k = 0;
if (ind < Foffset) { // case where we have some bits before the filler and the rest after
memcpy((void *)e, (void *)(d + ind), (Foffset - ind)<<2);
if (E + F <= Ncb - ind) { // E+F doesn't contain all coded bits
memcpy((void *)(e + Foffset - ind), (void *)(d + Foffset + F), (E - Foffset + ind)<<2);
k = E;
} else {
memcpy((void *)(e + Foffset - ind), (void *)(d + Foffset + F), (Ncb - Foffset - F)<<2);
k = Ncb - F - ind;
}
} else {
if (E <= Ncb - ind) { // E+F doesn't contain all coded bits
memcpy((void *)(e), (void *)(d + ind), E<<2);
k = E;
} else {
memcpy((void *)(e), (void *)(d + ind), (Ncb - ind)<<2);
k = Ncb - ind;
}
}
while (k < E) { // case where we do repetitions (low mcs)
for (ind = 0; (ind < Ncb) && (k < E); ind++) {
#ifdef RM_DEBUG
printf("RM_TX k%u Ind: %u (%d)\n", k, ind, d[ind]);
#endif
if (ind == Foffset)
ind = F + Foffset; // skip filler bits
e[k++] = d[ind];
}
}
return 0;
}
//#define USE_SCALAR 1
#define RMLOOP for (;ind<(ind2&7);k+=8,ind+=8) \
simde_mm_storeu_si128(&d[ind],simde_mm_adds_epi16(simde_mm_loadu_si128(&soft_input[k]),simde_mm_loadu_si128(&d[ind])));\
for (; ind<ind2 ; ind++,k++) d[ind] += soft_input[k];
int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
@@ -649,7 +1442,8 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
memset(d, 0, Ncb * sizeof(int16_t));
uint32_t k = 0;
if (ind < Foffset)
if (ind < Foffset) {
#ifdef USE_SCALAR
for (; (ind < Foffset) && (k < E); ind++) {
#ifdef RM_DEBUG
printf("RM_RX k%u Ind %u(before filler): %d (%d)=>", k, ind, d[ind], soft_input[k]);
@@ -659,9 +1453,14 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
printf("%d\n", d[ind]);
#endif
}
#else
int ind2 = ind + min(Foffset-ind,E);
RMLOOP;
#endif
}
if (ind >= Foffset && ind < Foffset + F)
ind = Foffset + F;
#ifdef USE_SCALAR
for (; (ind < Ncb) && (k < E); ind++) {
#ifdef RM_DEBUG
printf("RM_RX k%u Ind %u(after filler) %d (%d)=>", k, ind, d[ind], soft_input[k]);
@@ -671,8 +1470,13 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
printf("%d\n", d[ind]);
#endif
}
#else
int ind2 = ind + min(Ncb-ind,E-k);
RMLOOP;
#endif
while (k < E) {
#ifdef USE_SCALAR
for (ind = 0; (ind < Foffset) && (k < E); ind++) {
#ifdef RM_DEBUG
printf("RM_RX k%u Ind %u(before filler) %d(%d)=>", k, ind, d[ind], soft_input[k]);
@@ -691,6 +1495,14 @@ int nr_rate_matching_ldpc_rx(uint32_t Tbslbrm,
printf("%d\n", d[ind]);
#endif
}
#else
ind=0;
ind2 = min(Foffset,E-k);
RMLOOP;
ind = Foffset+F;
ind2 = ind + min(Ncb-ind,E-k);
RMLOOP;
#endif
}
return 0;
}

View File

@@ -16,6 +16,15 @@
*/
void nr_interleaving_ldpc(uint32_t E, uint8_t Qm, uint8_t *e, uint8_t *f);
/**
* \brief interleave a code segment after encoding and rate matching (32bit)
* \param E size of the code segment in bits
* \param Qm modulation order
* \param e input rate matched segment
* \param f output interleaved segment
*/
void nr_interleaving_ldpc32(uint32_t E, uint8_t Qm, uint32_t *e, uint32_t *f);
/**
* \brief deinterleave a code segment before RX rate matching and decoding
* \param E size of the code segment in bits
@@ -49,6 +58,30 @@ int nr_rate_matching_ldpc(uint32_t Tbslbrm,
uint8_t rvidx,
uint32_t E);
/**
* \brief rate match a code segment after encoding (32bit)
* \Tbslbrm Transport Block size LBRM
* \param BG LDPC base graph number
* \param Z segment lifting size
* \param d input encoded segment
* \param e output rate matched segment
* \param C number of segments in the Transport Block
* \param F number of filler bits in the segment
* \param Foffset offset of the filler bits in the segment
* \param rvidx redundancy version index
* \param E size of the code segment in bits
*/
int nr_rate_matching_ldpc32(uint32_t Tbslbrm,
uint8_t BG,
uint16_t Z,
uint32_t *d,
uint32_t *e,
uint8_t C,
uint32_t F,
uint32_t Foffset,
uint8_t rvidx,
uint32_t E);
/**
* \brief rate match a code segment before decoding
* \Tbslbrm Transport Block size LBRM