template <bool align> SIMD_INLINE void SquaredDifferenceKahanSum32f(const float * a, const float * b, size_t offset, float32x4_t & sum, float32x4_t & correction) { float32x4_t _a = Load<align>(a + offset); float32x4_t _b = Load<align>(b + offset); float32x4_t _d = vsubq_f32(_a, _b); float32x4_t term = vmlaq_f32(correction, _d, _d); float32x4_t temp = vaddq_f32(sum, term); correction = vsubq_f32(vmulq_f32(temp, sum), term); sum = temp; }
static void cft1st_128_neon(float* a) { const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); int j, k2; for (k2 = 0, j = 0; j < 128; j += 16, k2 += 4) { float32x4_t a00v = vld1q_f32(&a[j + 0]); float32x4_t a04v = vld1q_f32(&a[j + 4]); float32x4_t a08v = vld1q_f32(&a[j + 8]); float32x4_t a12v = vld1q_f32(&a[j + 12]); float32x4_t a01v = vcombine_f32(vget_low_f32(a00v), vget_low_f32(a08v)); float32x4_t a23v = vcombine_f32(vget_high_f32(a00v), vget_high_f32(a08v)); float32x4_t a45v = vcombine_f32(vget_low_f32(a04v), vget_low_f32(a12v)); float32x4_t a67v = vcombine_f32(vget_high_f32(a04v), vget_high_f32(a12v)); const float32x4_t wk1rv = vld1q_f32(&rdft_wk1r[k2]); const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2]); const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2]); const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2]); const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2]); const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2]); float32x4_t x0v = vaddq_f32(a01v, a23v); const float32x4_t x1v = vsubq_f32(a01v, a23v); const float32x4_t x2v = vaddq_f32(a45v, a67v); const float32x4_t x3v = vsubq_f32(a45v, a67v); const float32x4_t x3w = vrev64q_f32(x3v); float32x4_t x0w; a01v = vaddq_f32(x0v, x2v); x0v = vsubq_f32(x0v, x2v); x0w = vrev64q_f32(x0v); a45v = vmulq_f32(wk2rv, x0v); a45v = vmlaq_f32(a45v, wk2iv, x0w); x0v = vmlaq_f32(x1v, x3w, vec_swap_sign); x0w = vrev64q_f32(x0v); a23v = vmulq_f32(wk1rv, x0v); a23v = vmlaq_f32(a23v, wk1iv, x0w); x0v = vmlsq_f32(x1v, x3w, vec_swap_sign); x0w = vrev64q_f32(x0v); a67v = vmulq_f32(wk3rv, x0v); a67v = vmlaq_f32(a67v, wk3iv, x0w); a00v = vcombine_f32(vget_low_f32(a01v), vget_low_f32(a23v)); a04v = vcombine_f32(vget_low_f32(a45v), vget_low_f32(a67v)); a08v = vcombine_f32(vget_high_f32(a01v), vget_high_f32(a23v)); a12v = vcombine_f32(vget_high_f32(a45v), vget_high_f32(a67v)); vst1q_f32(&a[j + 0], a00v); vst1q_f32(&a[j + 4], a04v); vst1q_f32(&a[j + 8], a08v); vst1q_f32(&a[j + 12], a12v); } }
//----------------------------------------------------------------------------------- void MathlibNEON::SinCos4( ArrayReal x, ArrayReal &outSin, ArrayReal &outCos ) { // TODO: Improve accuracy by mapping to the range [-pi/4, pi/4] and swap // between cos & sin depending on which quadrant it fell: // Quadrant | sin | cos // n = 0 -> sin( x ), cos( x ) // n = 1 -> cos( x ), -sin( x ) // n = 2 -> -sin( x ), -cos( x ) // n = 3 -> -sin( x ), sin( x ) // See ARGUMENT REDUCTION FOR HUGE ARGUMENTS: // Good to the Last Bit // K. C. Ng and themembers of the FP group of SunPro // http://www.derekroconnor.net/Software/Ng--ArgReduction.pdf // -- Perhaps we can leave this to GSoC students? -- // Map arbitrary angle x to the range [-pi; +pi] without using division. // Code taken from MSDN's HLSL trick. Architectures with fused mad (i.e. NEON) // can replace the add, the sub, & the two muls for two mad ArrayReal integralPart; x = vaddq_f32( vmulq_f32( x, ONE_DIV_2PI ), HALF ); x = Modf4( x, integralPart ); x = vsubq_f32( vmulq_f32( x, TWO_PI ), PI ); sincos_ps( x, &outSin, &outCos ); }
template <bool align> SIMD_INLINE void SquaredDifferenceSum16f(const uint16_t * a, const uint16_t * b, size_t size, float * sum) { assert(size >= F); if (align) assert(Aligned(a) && Aligned(b)); size_t partialAlignedSize = AlignLo(size, F); size_t fullAlignedSize = AlignLo(size, DF); size_t i = 0; float32x4_t sums[2] = { vdupq_n_f32(0), vdupq_n_f32(0) }; if (fullAlignedSize) { for (; i < fullAlignedSize; i += DF) { SquaredDifferenceSum16f<align>(a, b, i + F * 0, sums[0]); SquaredDifferenceSum16f<align>(a, b, i + F * 1, sums[1]); } sums[0] = vaddq_f32(sums[0], sums[1]); } for (; i < partialAlignedSize; i += F) SquaredDifferenceSum16f<align>(a, b, i, sums[0]); if (partialAlignedSize != size) { float32x4_t tailMask = RightNotZero(size - partialAlignedSize); float32x4_t _a = vcvt_f32_f16((float16x4_t)LoadHalf<align>(a + size - F)); float32x4_t _b = vcvt_f32_f16((float16x4_t)LoadHalf<align>(a + size - F)); float32x4_t _d = And(vsubq_f32(_a, _b), tailMask); sums[0] = vaddq_f32(sums[0], vmulq_f32(_d, _d)); } *sum = ExtractSum32f(sums[0]); }
static inline float32x4_t floor_neon(float32x4_t a) { #if __ARM_ARCH >= 8 return vrndqm_f32(a); #else const float32x4_t round32 = vdupq_n_f32(12582912.0f); const float32x4_t vhalf = vdupq_n_f32(0.5f); float32x4_t rounded = vsubq_f32(vaddq_f32(a, round32), round32); uint32x4_t mask = vceqq_f32(a, rounded); float32x4_t floored = vsubq_f32(vaddq_f32(vsubq_f32(a, vhalf), round32), round32); return vreinterpretq_f32_u32(vorrq_u32(vandq_u32(vreinterpretq_u32_f32(a), mask), vbicq_u32(vreinterpretq_u32_f32(floored), mask))); #endif }
template <bool align> SIMD_INLINE void SquaredDifferenceSum32f(const float * a, const float * b, size_t offset, float32x4_t & sum) { float32x4_t _a = Load<align>(a + offset); float32x4_t _b = Load<align>(b + offset); float32x4_t _d = vsubq_f32(_a, _b); sum = vmlaq_f32(sum, _d, _d); }
template <bool align> SIMD_INLINE void SquaredDifferenceSum16f(const uint16_t * a, const uint16_t * b, size_t offset, float32x4_t & sum) { float32x4_t _a = vcvt_f32_f16((float16x4_t)LoadHalf<align>(a + offset)); float32x4_t _b = vcvt_f32_f16((float16x4_t)LoadHalf<align>(b + offset)); float32x4_t _d = vsubq_f32(_a, _b); sum = vmlaq_f32(sum, _d, _d); }
//----------------------------------------------------------------------------------- ArrayReal MathlibNEON::Cos4( ArrayReal x ) { // Map arbitrary angle x to the range [-pi; +pi] without using division. // Code taken from MSDN's HLSL trick. Architectures with fused mad (i.e. NEON) // can replace the add, the sub, & the two muls for two mad ArrayReal integralPart; x = vaddq_f32( vmulq_f32( x, ONE_DIV_2PI ), HALF ); x = Modf4( x, integralPart ); x = vsubq_f32( vmulq_f32( x, TWO_PI ), PI ); return cos_ps( x ); }
/* f32x4 sub */ void mw_neon_mm_sub_f32x4(float * A, int Row, int Col, float * B, float * C) { float32x4_t neon_a, neon_b, neon_c; int size = Row * Col; int i = 0; int k = 0; for (i = 4; i <= size ; i+=4) { k = i - 4; neon_a = vld1q_f32(A + k); neon_b = vld1q_f32(B + k); neon_c = vsubq_f32(neon_a, neon_b); vst1q_f32(C + k, neon_c); } k = i - 4; for (i = 0; i < size % 4; i++) { C[k + i] = A[k + i] - B[k + i]; } }
inline float32x4_t vsubq(const float32x4_t & v0, const float32x4_t & v1) { return vsubq_f32(v0, v1); }
static void cftmdl_128_neon(float* a) { int j; const int l = 8; const float32x4_t vec_swap_sign = vld1q_f32((float32_t*)k_swap_sign); float32x4_t wk1rv = vld1q_f32(cftmdl_wk1r); for (j = 0; j < l; j += 2) { const float32x2_t a_00 = vld1_f32(&a[j + 0]); const float32x2_t a_08 = vld1_f32(&a[j + 8]); const float32x2_t a_32 = vld1_f32(&a[j + 32]); const float32x2_t a_40 = vld1_f32(&a[j + 40]); const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); const float32x2_t a_16 = vld1_f32(&a[j + 16]); const float32x2_t a_24 = vld1_f32(&a[j + 24]); const float32x2_t a_48 = vld1_f32(&a[j + 48]); const float32x2_t a_56 = vld1_f32(&a[j + 56]); const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); const float32x4_t xx0 = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); const float32x4_t x1_x3_add = vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); const float32x4_t x1_x3_sub = vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); const float32x2_t yy0_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 0); const float32x2_t yy0_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 0); const float32x4_t yy0_as = vcombine_f32(yy0_a, yy0_s); const float32x2_t yy1_a = vdup_lane_f32(vget_high_f32(x1_x3_add), 1); const float32x2_t yy1_s = vdup_lane_f32(vget_high_f32(x1_x3_sub), 1); const float32x4_t yy1_as = vcombine_f32(yy1_a, yy1_s); const float32x4_t yy0 = vmlaq_f32(yy0_as, vec_swap_sign, yy1_as); const float32x4_t yy4 = vmulq_f32(wk1rv, yy0); const float32x4_t xx1_rev = vrev64q_f32(xx1); const float32x4_t yy4_rev = vrev64q_f32(yy4); vst1_f32(&a[j + 0], vget_low_f32(xx0)); vst1_f32(&a[j + 32], vget_high_f32(xx0)); vst1_f32(&a[j + 16], vget_low_f32(xx1)); vst1_f32(&a[j + 48], vget_high_f32(xx1_rev)); a[j + 48] = -a[j + 48]; vst1_f32(&a[j + 8], vget_low_f32(x1_x3_add)); vst1_f32(&a[j + 24], vget_low_f32(x1_x3_sub)); vst1_f32(&a[j + 40], vget_low_f32(yy4)); vst1_f32(&a[j + 56], vget_high_f32(yy4_rev)); } { const int k = 64; const int k1 = 2; const int k2 = 2 * k1; const float32x4_t wk2rv = vld1q_f32(&rdft_wk2r[k2 + 0]); const float32x4_t wk2iv = vld1q_f32(&rdft_wk2i[k2 + 0]); const float32x4_t wk1iv = vld1q_f32(&rdft_wk1i[k2 + 0]); const float32x4_t wk3rv = vld1q_f32(&rdft_wk3r[k2 + 0]); const float32x4_t wk3iv = vld1q_f32(&rdft_wk3i[k2 + 0]); wk1rv = vld1q_f32(&rdft_wk1r[k2 + 0]); for (j = k; j < l + k; j += 2) { const float32x2_t a_00 = vld1_f32(&a[j + 0]); const float32x2_t a_08 = vld1_f32(&a[j + 8]); const float32x2_t a_32 = vld1_f32(&a[j + 32]); const float32x2_t a_40 = vld1_f32(&a[j + 40]); const float32x4_t a_00_32 = vcombine_f32(a_00, a_32); const float32x4_t a_08_40 = vcombine_f32(a_08, a_40); const float32x4_t x0r0_0i0_0r1_x0i1 = vaddq_f32(a_00_32, a_08_40); const float32x4_t x1r0_1i0_1r1_x1i1 = vsubq_f32(a_00_32, a_08_40); const float32x2_t a_16 = vld1_f32(&a[j + 16]); const float32x2_t a_24 = vld1_f32(&a[j + 24]); const float32x2_t a_48 = vld1_f32(&a[j + 48]); const float32x2_t a_56 = vld1_f32(&a[j + 56]); const float32x4_t a_16_48 = vcombine_f32(a_16, a_48); const float32x4_t a_24_56 = vcombine_f32(a_24, a_56); const float32x4_t x2r0_2i0_2r1_x2i1 = vaddq_f32(a_16_48, a_24_56); const float32x4_t x3r0_3i0_3r1_x3i1 = vsubq_f32(a_16_48, a_24_56); const float32x4_t xx = vaddq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const float32x4_t xx1 = vsubq_f32(x0r0_0i0_0r1_x0i1, x2r0_2i0_2r1_x2i1); const float32x4_t x3i0_3r0_3i1_x3r1 = vrev64q_f32(x3r0_3i0_3r1_x3i1); const float32x4_t x1_x3_add = vmlaq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); const float32x4_t x1_x3_sub = vmlsq_f32(x1r0_1i0_1r1_x1i1, vec_swap_sign, x3i0_3r0_3i1_x3r1); float32x4_t xx4 = vmulq_f32(wk2rv, xx1); float32x4_t xx12 = vmulq_f32(wk1rv, x1_x3_add); float32x4_t xx22 = vmulq_f32(wk3rv, x1_x3_sub); xx4 = vmlaq_f32(xx4, wk2iv, vrev64q_f32(xx1)); xx12 = vmlaq_f32(xx12, wk1iv, vrev64q_f32(x1_x3_add)); xx22 = vmlaq_f32(xx22, wk3iv, vrev64q_f32(x1_x3_sub)); vst1_f32(&a[j + 0], vget_low_f32(xx)); vst1_f32(&a[j + 32], vget_high_f32(xx)); vst1_f32(&a[j + 16], vget_low_f32(xx4)); vst1_f32(&a[j + 48], vget_high_f32(xx4)); vst1_f32(&a[j + 8], vget_low_f32(xx12)); vst1_f32(&a[j + 40], vget_high_f32(xx12)); vst1_f32(&a[j + 24], vget_low_f32(xx22)); vst1_f32(&a[j + 56], vget_high_f32(xx22)); } } }
static void rftbsub_128_neon(float* a) { const float* c = rdft_w + 32; int j1, j2; const float32x4_t mm_half = vdupq_n_f32(0.5f); a[1] = -a[1]; // Vectorized code (four at once). // Note: commented number are indexes for the first iteration of the loop. for (j1 = 1, j2 = 2; j2 + 7 < 64; j1 += 4, j2 += 8) { // Load 'wk'. const float32x4_t c_j1 = vld1q_f32(&c[j1]); // 1, 2, 3, 4, const float32x4_t c_k1 = vld1q_f32(&c[29 - j1]); // 28, 29, 30, 31, const float32x4_t wkrt = vsubq_f32(mm_half, c_k1); // 28, 29, 30, 31, const float32x4_t wkr_ = reverse_order_f32x4(wkrt); // 31, 30, 29, 28, const float32x4_t wki_ = c_j1; // 1, 2, 3, 4, // Load and shuffle 'a'. // 2, 4, 6, 8, 3, 5, 7, 9 float32x4x2_t a_j2_p = vld2q_f32(&a[0 + j2]); // 120, 122, 124, 126, 121, 123, 125, 127, const float32x4x2_t k2_0_4 = vld2q_f32(&a[122 - j2]); // 126, 124, 122, 120 const float32x4_t a_k2_p0 = reverse_order_f32x4(k2_0_4.val[0]); // 127, 125, 123, 121 const float32x4_t a_k2_p1 = reverse_order_f32x4(k2_0_4.val[1]); // Calculate 'x'. const float32x4_t xr_ = vsubq_f32(a_j2_p.val[0], a_k2_p0); // 2-126, 4-124, 6-122, 8-120, const float32x4_t xi_ = vaddq_f32(a_j2_p.val[1], a_k2_p1); // 3-127, 5-125, 7-123, 9-121, // Calculate product into 'y'. // yr = wkr * xr - wki * xi; // yi = wkr * xi + wki * xr; const float32x4_t a_ = vmulq_f32(wkr_, xr_); const float32x4_t b_ = vmulq_f32(wki_, xi_); const float32x4_t c_ = vmulq_f32(wkr_, xi_); const float32x4_t d_ = vmulq_f32(wki_, xr_); const float32x4_t yr_ = vaddq_f32(a_, b_); // 2-126, 4-124, 6-122, 8-120, const float32x4_t yi_ = vsubq_f32(c_, d_); // 3-127, 5-125, 7-123, 9-121, // Update 'a'. // a[j2 + 0] -= yr; // a[j2 + 1] -= yi; // a[k2 + 0] += yr; // a[k2 + 1] -= yi; // 126, 124, 122, 120, const float32x4_t a_k2_p0n = vaddq_f32(a_k2_p0, yr_); // 127, 125, 123, 121, const float32x4_t a_k2_p1n = vsubq_f32(yi_, a_k2_p1); // Shuffle in right order and store. // 2, 3, 4, 5, 6, 7, 8, 9, const float32x4_t a_k2_p0nr = vrev64q_f32(a_k2_p0n); const float32x4_t a_k2_p1nr = vrev64q_f32(a_k2_p1n); // 124, 125, 126, 127, 120, 121, 122, 123 const float32x4x2_t a_k2_n = vzipq_f32(a_k2_p0nr, a_k2_p1nr); // 2, 4, 6, 8, a_j2_p.val[0] = vsubq_f32(a_j2_p.val[0], yr_); // 3, 5, 7, 9, a_j2_p.val[1] = vsubq_f32(yi_, a_j2_p.val[1]); // 2, 3, 4, 5, 6, 7, 8, 9, vst2q_f32(&a[0 + j2], a_j2_p); vst1q_f32(&a[122 - j2], a_k2_n.val[1]); vst1q_f32(&a[126 - j2], a_k2_n.val[0]); } // Scalar code for the remaining items. for (; j2 < 64; j1 += 1, j2 += 2) { const int k2 = 128 - j2; const int k1 = 32 - j1; const float wkr = 0.5f - c[k1]; const float wki = c[j1]; const float xr = a[j2 + 0] - a[k2 + 0]; const float xi = a[j2 + 1] + a[k2 + 1]; const float yr = wkr * xr + wki * xi; const float yi = wkr * xi - wki * xr; a[j2 + 0] = a[j2 + 0] - yr; a[j2 + 1] = yi - a[j2 + 1]; a[k2 + 0] = yr + a[k2 + 0]; a[k2 + 1] = yi - a[k2 + 1]; } a[65] = -a[65]; }
static void ne10_fft16_backward_float32_neon (ne10_fft_cpx_float32_t * Fout, ne10_fft_cpx_float32_t * Fin, ne10_fft_cpx_float32_t * twiddles) { ne10_fft_cpx_float32_t *tw1, *tw2, *tw3; // the first stage float32_t *p_src0, *p_src4, *p_src8, *p_src12; float32x4x2_t q2_in_0123, q2_in_4567, q2_in_89ab, q2_in_cdef; float32x4_t q_t0_r, q_t0_i, q_t1_r, q_t1_i, q_t2_r, q_t2_i, q_t3_r, q_t3_i; float32x4_t q_out_r048c, q_out_i048c, q_out_r159d, q_out_i159d; float32x4_t q_out_r26ae, q_out_i26ae, q_out_r37bf, q_out_i37bf; p_src0 = (float32_t*) (& (Fin[0])); p_src4 = (float32_t*) (& (Fin[4])); p_src8 = (float32_t*) (& (Fin[8])); p_src12 = (float32_t*) (& (Fin[12])); q2_in_0123 = vld2q_f32 (p_src0); q2_in_4567 = vld2q_f32 (p_src4); q2_in_89ab = vld2q_f32 (p_src8); q2_in_cdef = vld2q_f32 (p_src12); q_t2_r = vsubq_f32 (q2_in_0123.val[0], q2_in_89ab.val[0]); q_t2_i = vsubq_f32 (q2_in_0123.val[1], q2_in_89ab.val[1]); q_t3_r = vaddq_f32 (q2_in_0123.val[0], q2_in_89ab.val[0]); q_t3_i = vaddq_f32 (q2_in_0123.val[1], q2_in_89ab.val[1]); q_t0_r = vaddq_f32 (q2_in_4567.val[0], q2_in_cdef.val[0]); q_t0_i = vaddq_f32 (q2_in_4567.val[1], q2_in_cdef.val[1]); q_t1_r = vsubq_f32 (q2_in_4567.val[0], q2_in_cdef.val[0]); q_t1_i = vsubq_f32 (q2_in_4567.val[1], q2_in_cdef.val[1]); q_out_r26ae = vsubq_f32 (q_t3_r, q_t0_r); q_out_i26ae = vsubq_f32 (q_t3_i, q_t0_i); q_out_r048c = vaddq_f32 (q_t3_r, q_t0_r); q_out_i048c = vaddq_f32 (q_t3_i, q_t0_i); q_out_r159d = vsubq_f32 (q_t2_r, q_t1_i); q_out_i159d = vaddq_f32 (q_t2_i, q_t1_r); q_out_r37bf = vaddq_f32 (q_t2_r, q_t1_i); q_out_i37bf = vsubq_f32 (q_t2_i, q_t1_r); // second stages float32_t *p_dst0, *p_dst1, *p_dst2, *p_dst3; float32_t *p_tw1, *p_tw2, *p_tw3; float32x4_t q_s0_r, q_s0_i, q_s1_r, q_s1_i, q_s2_r, q_s2_i; float32x4_t q_s3_r, q_s3_i, q_s4_r, q_s4_i, q_s5_r, q_s5_i; float32x4x2_t q2_tmp_0, q2_tmp_1, q2_tmp_2, q2_tmp_3; float32x4_t q_in_r0123, q_in_r4567, q_in_r89ab, q_in_rcdef; float32x4_t q_in_i0123, q_in_i4567, q_in_i89ab, q_in_icdef; float32x4x2_t q2_tw1, q2_tw2, q2_tw3; float32x4x2_t q2_out_0123, q2_out_4567, q2_out_89ab, q2_out_cdef; float32x4_t q_one_by_nfft; tw1 = twiddles; tw2 = twiddles + 4; tw3 = twiddles + 8; p_dst0 = (float32_t*) (&Fout[0]); p_dst1 = (float32_t*) (&Fout[4]); p_dst2 = (float32_t*) (&Fout[8]); p_dst3 = (float32_t*) (&Fout[12]); p_tw1 = (float32_t*) tw1; p_tw2 = (float32_t*) tw2; p_tw3 = (float32_t*) tw3; q2_tmp_0 = vzipq_f32 (q_out_r048c, q_out_r159d); q2_tmp_1 = vzipq_f32 (q_out_i048c, q_out_i159d); q2_tmp_2 = vzipq_f32 (q_out_r26ae, q_out_r37bf); q2_tmp_3 = vzipq_f32 (q_out_i26ae, q_out_i37bf); q_in_r0123 = vcombine_f32 (vget_low_f32 (q2_tmp_0.val[0]), vget_low_f32 (q2_tmp_2.val[0])); q_in_i0123 = vcombine_f32 (vget_low_f32 (q2_tmp_1.val[0]), vget_low_f32 (q2_tmp_3.val[0])); q_in_r4567 = vcombine_f32 (vget_high_f32 (q2_tmp_0.val[0]), vget_high_f32 (q2_tmp_2.val[0])); q_in_i4567 = vcombine_f32 (vget_high_f32 (q2_tmp_1.val[0]), vget_high_f32 (q2_tmp_3.val[0])); q_in_r89ab = vcombine_f32 (vget_low_f32 (q2_tmp_0.val[1]), vget_low_f32 (q2_tmp_2.val[1])); q_in_i89ab = vcombine_f32 (vget_low_f32 (q2_tmp_1.val[1]), vget_low_f32 (q2_tmp_3.val[1])); q_in_rcdef = vcombine_f32 (vget_high_f32 (q2_tmp_0.val[1]), vget_high_f32 (q2_tmp_2.val[1])); q_in_icdef = vcombine_f32 (vget_high_f32 (q2_tmp_1.val[1]), vget_high_f32 (q2_tmp_3.val[1])); q2_tw1 = vld2q_f32 (p_tw1); q2_tw2 = vld2q_f32 (p_tw2); q2_tw3 = vld2q_f32 (p_tw3); q_s0_r = vmulq_f32 (q_in_r4567, q2_tw1.val[0]); q_s0_i = vmulq_f32 (q_in_i4567, q2_tw1.val[0]); q_s1_r = vmulq_f32 (q_in_r89ab, q2_tw2.val[0]); q_s1_i = vmulq_f32 (q_in_i89ab, q2_tw2.val[0]); q_s2_r = vmulq_f32 (q_in_rcdef, q2_tw3.val[0]); q_s2_i = vmulq_f32 (q_in_icdef, q2_tw3.val[0]); q_s0_r = vmlaq_f32 (q_s0_r, q_in_i4567, q2_tw1.val[1]); q_s0_i = vmlsq_f32 (q_s0_i, q_in_r4567, q2_tw1.val[1]); q_s1_r = vmlaq_f32 (q_s1_r, q_in_i89ab, q2_tw2.val[1]); q_s1_i = vmlsq_f32 (q_s1_i, q_in_r89ab, q2_tw2.val[1]); q_s2_r = vmlaq_f32 (q_s2_r, q_in_icdef, q2_tw3.val[1]); q_s2_i = vmlsq_f32 (q_s2_i, q_in_rcdef, q2_tw3.val[1]); q_s5_r = vsubq_f32 (q_in_r0123, q_s1_r); q_s5_i = vsubq_f32 (q_in_i0123, q_s1_i); q2_out_0123.val[0] = vaddq_f32 (q_in_r0123, q_s1_r); q2_out_0123.val[1] = vaddq_f32 (q_in_i0123, q_s1_i); q_s3_r = vaddq_f32 (q_s0_r, q_s2_r); q_s3_i = vaddq_f32 (q_s0_i, q_s2_i); q_s4_r = vsubq_f32 (q_s0_r, q_s2_r); q_s4_i = vsubq_f32 (q_s0_i, q_s2_i); q_one_by_nfft = vdupq_n_f32 (0.0625f); q2_out_89ab.val[0] = vsubq_f32 (q2_out_0123.val[0], q_s3_r); q2_out_89ab.val[1] = vsubq_f32 (q2_out_0123.val[1], q_s3_i); q2_out_0123.val[0] = vaddq_f32 (q2_out_0123.val[0], q_s3_r); q2_out_0123.val[1] = vaddq_f32 (q2_out_0123.val[1], q_s3_i); q2_out_4567.val[0] = vsubq_f32 (q_s5_r, q_s4_i); q2_out_4567.val[1] = vaddq_f32 (q_s5_i, q_s4_r); q2_out_cdef.val[0] = vaddq_f32 (q_s5_r, q_s4_i); q2_out_cdef.val[1] = vsubq_f32 (q_s5_i, q_s4_r); q2_out_89ab.val[0] = vmulq_f32 (q2_out_89ab.val[0], q_one_by_nfft); q2_out_89ab.val[1] = vmulq_f32 (q2_out_89ab.val[1], q_one_by_nfft); q2_out_0123.val[0] = vmulq_f32 (q2_out_0123.val[0], q_one_by_nfft); q2_out_0123.val[1] = vmulq_f32 (q2_out_0123.val[1], q_one_by_nfft); q2_out_4567.val[0] = vmulq_f32 (q2_out_4567.val[0], q_one_by_nfft); q2_out_4567.val[1] = vmulq_f32 (q2_out_4567.val[1], q_one_by_nfft); q2_out_cdef.val[0] = vmulq_f32 (q2_out_cdef.val[0], q_one_by_nfft); q2_out_cdef.val[1] = vmulq_f32 (q2_out_cdef.val[1], q_one_by_nfft); vst2q_f32 (p_dst0, q2_out_0123); vst2q_f32 (p_dst1, q2_out_4567); vst2q_f32 (p_dst2, q2_out_89ab); vst2q_f32 (p_dst3, q2_out_cdef); }
/* * NE10 Library : math/NE10_rsbc.neon.c */ #include "NE10_types.h" #include "macros.h" #include <assert.h> #include <arm_neon.h> ne10_result_t ne10_rsbc_float_neon (ne10_float32_t * dst, ne10_float32_t * src, const ne10_float32_t cst, ne10_uint32_t count) { NE10_DstSrcCst_DO_COUNT_TIMES_FLOAT_NEON ( n_dst = vsubq_f32 (n_cst, n_src); , n_rest = vsub_f32 (n_rest_cst, n_rest); ); } ne10_result_t ne10_rsbc_vec2f_neon (ne10_vec2f_t * dst, ne10_vec2f_t * src, const ne10_vec2f_t * cst, ne10_uint32_t count) { NE10_DstSrcCst_DO_COUNT_TIMES_VEC2F_NEON ( n_dst = vsubq_f32 (n_cst, n_src); , n_rest = vsub_f32 (n_rest_cst, n_rest); ); }
static void ne10_fft_split_r2c_1d_float32_neon (ne10_fft_cpx_float32_t *dst, const ne10_fft_cpx_float32_t *src, ne10_fft_cpx_float32_t *twiddles, ne10_int32_t ncfft) { ne10_int32_t k; ne10_int32_t count = ncfft / 2; ne10_fft_cpx_float32_t fpnk, fpk, f1k, f2k, tw, tdc; float32x4x2_t q2_fpk, q2_fpnk, q2_tw, q2_dst, q2_dst2; float32x4_t q_fpnk_r, q_fpnk_i; float32x4_t q_f1k_r, q_f1k_i, q_f2k_r, q_f2k_i; float32x4_t q_tw_r, q_tw_i; float32x4_t q_tmp0, q_tmp1, q_tmp2, q_tmp3, q_val; float32x4_t q_dst_r, q_dst_i, q_dst2_r, q_dst2_i; float32_t *p_src, *p_src2, *p_dst, *p_dst2, *p_twiddles; tdc.r = src[0].r; tdc.i = src[0].i; dst[0].r = tdc.r + tdc.i; dst[ncfft].r = tdc.r - tdc.i; dst[ncfft].i = dst[0].i = 0; if (count >= 4) { for (k = 1; k <= count ; k += 4) { p_src = (float32_t*) (& (src[k])); p_src2 = (float32_t*) (& (src[ncfft - k - 3])); p_twiddles = (float32_t*) (& (twiddles[k - 1])); p_dst = (float32_t*) (& (dst[k])); p_dst2 = (float32_t*) (& (dst[ncfft - k - 3])); q2_fpk = vld2q_f32 (p_src); q2_fpnk = vld2q_f32 (p_src2); q2_tw = vld2q_f32 (p_twiddles); q2_fpnk.val[0] = vrev64q_f32 (q2_fpnk.val[0]); q2_fpnk.val[1] = vrev64q_f32 (q2_fpnk.val[1]); q_fpnk_r = vcombine_f32 (vget_high_f32 (q2_fpnk.val[0]), vget_low_f32 (q2_fpnk.val[0])); q_fpnk_i = vcombine_f32 (vget_high_f32 (q2_fpnk.val[1]), vget_low_f32 (q2_fpnk.val[1])); q_fpnk_i = vnegq_f32 (q_fpnk_i); q_f1k_r = vaddq_f32 (q2_fpk.val[0], q_fpnk_r); q_f1k_i = vaddq_f32 (q2_fpk.val[1], q_fpnk_i); q_f2k_r = vsubq_f32 (q2_fpk.val[0], q_fpnk_r); q_f2k_i = vsubq_f32 (q2_fpk.val[1], q_fpnk_i); q_tmp0 = vmulq_f32 (q_f2k_r, q2_tw.val[0]); q_tmp1 = vmulq_f32 (q_f2k_i, q2_tw.val[1]); q_tmp2 = vmulq_f32 (q_f2k_r, q2_tw.val[1]); q_tmp3 = vmulq_f32 (q_f2k_i, q2_tw.val[0]); q_tw_r = vsubq_f32 (q_tmp0, q_tmp1); q_tw_i = vaddq_f32 (q_tmp2, q_tmp3); q_val = vdupq_n_f32 (0.5f); q_dst2_r = vsubq_f32 (q_f1k_r, q_tw_r); q_dst2_i = vsubq_f32 (q_tw_i, q_f1k_i); q_dst_r = vaddq_f32 (q_f1k_r, q_tw_r); q_dst_i = vaddq_f32 (q_f1k_i, q_tw_i); q_dst2_r = vmulq_f32 (q_dst2_r, q_val); q_dst2_i = vmulq_f32 (q_dst2_i, q_val); q2_dst.val[0] = vmulq_f32 (q_dst_r, q_val); q2_dst.val[1] = vmulq_f32 (q_dst_i, q_val); q_dst2_r = vrev64q_f32 (q_dst2_r); q_dst2_i = vrev64q_f32 (q_dst2_i); q2_dst2.val[0] = vcombine_f32 (vget_high_f32 (q_dst2_r), vget_low_f32 (q_dst2_r)); q2_dst2.val[1] = vcombine_f32 (vget_high_f32 (q_dst2_i), vget_low_f32 (q_dst2_i)); vst2q_f32 (p_dst, q2_dst); vst2q_f32 (p_dst2, q2_dst2); } } else { for (k = 1; k <= count ; k++) { fpk = src[k]; fpnk.r = src[ncfft - k].r; fpnk.i = - src[ncfft - k].i; f1k.r = fpk.r + fpnk.r; f1k.i = fpk.i + fpnk.i; f2k.r = fpk.r - fpnk.r; f2k.i = fpk.i - fpnk.i; tw.r = f2k.r * (twiddles[k - 1]).r - f2k.i * (twiddles[k - 1]).i; tw.i = f2k.r * (twiddles[k - 1]).i + f2k.i * (twiddles[k - 1]).r; dst[k].r = (f1k.r + tw.r) * 0.5f; dst[k].i = (f1k.i + tw.i) * 0.5f; dst[ncfft - k].r = (f1k.r - tw.r) * 0.5f; dst[ncfft - k].i = (tw.i - f1k.i) * 0.5f; } } }
void fft_real_neon( CkFftContext* context, const float* input, CkFftComplex* output, int count) { int countDiv2 = count/2; fft_neon(context, (const CkFftComplex*) input, output, countDiv2, false, 1, context->fwdExpTable, context->maxCount / countDiv2); output[countDiv2] = output[0]; int expTableStride = context->maxCount/count; const CkFftComplex* exp0 = context->fwdExpTable; const CkFftComplex* exp1 = context->fwdExpTable + countDiv2 * expTableStride; CkFftComplex* p0 = output; CkFftComplex* p1 = output + countDiv2 - 3; const CkFftComplex* pEnd = p0 + count/4; while (p0 < pEnd) { float32x4x2_t z0_v = vld2q_f32((const float32_t*) p0); float32x4x2_t z1_v = vld2q_f32((const float32_t*) p1); float32x2_t hi, lo; // reverse z1 real z1_v.val[0] = vrev64q_f32(z1_v.val[0]); hi = vget_high_f32(z1_v.val[0]); lo = vget_low_f32(z1_v.val[0]); z1_v.val[0] = vcombine_f32(hi, lo); // reverse z1 imaginary z1_v.val[1] = vrev64q_f32(z1_v.val[1]); hi = vget_high_f32(z1_v.val[1]); lo = vget_low_f32(z1_v.val[1]); z1_v.val[1] = vcombine_f32(hi, lo); float32x4x2_t sum_v; sum_v.val[0] = vaddq_f32(z0_v.val[0], z1_v.val[0]); sum_v.val[1] = vsubq_f32(z0_v.val[1], z1_v.val[1]); float32x4x2_t diff_v; diff_v.val[0] = vsubq_f32(z0_v.val[0], z1_v.val[0]); diff_v.val[1] = vaddq_f32(z0_v.val[1], z1_v.val[1]); float32x4x2_t exp_v; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 0); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 1); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 2); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 3); exp0 += expTableStride; float32x4x2_t f_v; f_v.val[0] = vnegq_f32(exp_v.val[1]); f_v.val[1] = exp_v.val[0]; float32x4x2_t c_v; multiply(f_v, diff_v, c_v); subtract(sum_v, c_v, z0_v); vst2q_f32((float32_t*) p0, z0_v); diff_v.val[0] = vnegq_f32(diff_v.val[0]); sum_v.val[1] = vnegq_f32(sum_v.val[1]); exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 0); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 1); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 2); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 3); exp1 -= expTableStride; f_v.val[0] = vnegq_f32(exp_v.val[1]); f_v.val[1] = exp_v.val[0]; multiply(f_v, diff_v, c_v); subtract(sum_v, c_v, z1_v); // reverse z1 real z1_v.val[0] = vrev64q_f32(z1_v.val[0]); hi = vget_high_f32(z1_v.val[0]); lo = vget_low_f32(z1_v.val[0]); z1_v.val[0] = vcombine_f32(hi, lo); // reverse z1 imaginary z1_v.val[1] = vrev64q_f32(z1_v.val[1]); hi = vget_high_f32(z1_v.val[1]); lo = vget_low_f32(z1_v.val[1]); z1_v.val[1] = vcombine_f32(hi, lo); vst2q_f32((float32_t*) p1, z1_v); p0 += 4; p1 -= 4; } if (count > 8) { // middle: p0->real = p0->real * 2.0f; p0->imag = -p0->imag * 2.0f; } }
void fft_real_inverse_neon( CkFftContext* context, const CkFftComplex* input, float* output, int count, CkFftComplex* tmpBuf) { int countDiv2 = count/2; int expTableStride = context->maxCount/count; const CkFftComplex* exp0 = context->invExpTable; const CkFftComplex* exp1 = context->invExpTable + countDiv2 * expTableStride; const CkFftComplex* p0 = input; const CkFftComplex* p1 = input + countDiv2 - 3; CkFftComplex* tmp0 = tmpBuf; CkFftComplex* tmp1 = tmpBuf + countDiv2 - 3; const CkFftComplex* pEnd = p0 + count/4; while (p0 < pEnd) { float32x4x2_t z0_v = vld2q_f32((const float32_t*) p0); float32x4x2_t z1_v = vld2q_f32((const float32_t*) p1); float32x2_t hi, lo; // reverse z1 real z1_v.val[0] = vrev64q_f32(z1_v.val[0]); hi = vget_high_f32(z1_v.val[0]); lo = vget_low_f32(z1_v.val[0]); z1_v.val[0] = vcombine_f32(hi, lo); // reverse z1 imaginary z1_v.val[1] = vrev64q_f32(z1_v.val[1]); hi = vget_high_f32(z1_v.val[1]); lo = vget_low_f32(z1_v.val[1]); z1_v.val[1] = vcombine_f32(hi, lo); float32x4x2_t sum_v; sum_v.val[0] = vaddq_f32(z0_v.val[0], z1_v.val[0]); sum_v.val[1] = vsubq_f32(z0_v.val[1], z1_v.val[1]); float32x4x2_t diff_v; diff_v.val[0] = vsubq_f32(z0_v.val[0], z1_v.val[0]); diff_v.val[1] = vaddq_f32(z0_v.val[1], z1_v.val[1]); float32x4x2_t exp_v; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 0); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 1); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 2); exp0 += expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp0, exp_v, 3); exp0 += expTableStride; float32x4x2_t f_v; f_v.val[0] = vnegq_f32(exp_v.val[1]); f_v.val[1] = exp_v.val[0]; float32x4x2_t c_v; multiply(f_v, diff_v, c_v); add(sum_v, c_v, z0_v); vst2q_f32((float32_t*) tmp0, z0_v); diff_v.val[0] = vnegq_f32(diff_v.val[0]); sum_v.val[1] = vnegq_f32(sum_v.val[1]); exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 0); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 1); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 2); exp1 -= expTableStride; exp_v = vld2q_lane_f32((const float32_t*) exp1, exp_v, 3); exp1 -= expTableStride; f_v.val[0] = vnegq_f32(exp_v.val[1]); f_v.val[1] = exp_v.val[0]; multiply(f_v, diff_v, c_v); add(sum_v, c_v, z1_v); // reverse z1 real z1_v.val[0] = vrev64q_f32(z1_v.val[0]); hi = vget_high_f32(z1_v.val[0]); lo = vget_low_f32(z1_v.val[0]); z1_v.val[0] = vcombine_f32(hi, lo); // reverse z1 imaginary z1_v.val[1] = vrev64q_f32(z1_v.val[1]); hi = vget_high_f32(z1_v.val[1]); lo = vget_low_f32(z1_v.val[1]); z1_v.val[1] = vcombine_f32(hi, lo); vst2q_f32((float32_t*) tmp1, z1_v); p0 += 4; tmp0 += 4; p1 -= 4; tmp1 -= 4; } // middle: tmp0->real = p0->real * 2.0f; tmp0->imag = -p0->imag * 2.0f; fft_neon(context, tmpBuf, (CkFftComplex*) output, countDiv2, true, 1, context->invExpTable, context->maxCount / countDiv2); }
static void ne10_fft_split_c2r_1d_float32_neon (ne10_fft_cpx_float32_t *dst, const ne10_fft_cpx_float32_t *src, ne10_fft_cpx_float32_t *twiddles, ne10_int32_t ncfft) { ne10_int32_t k; ne10_int32_t count = ncfft / 2; ne10_fft_cpx_float32_t fk, fnkc, fek, fok, tmp; float32x4x2_t q2_fk, q2_fnkc, q2_tw, q2_dst, q2_dst2; float32x4_t q_fnkc_r, q_fnkc_i; float32x4_t q_fek_r, q_fek_i, q_fok_r, q_fok_i; float32x4_t q_tmp0, q_tmp1, q_tmp2, q_tmp3, q_val; float32x4_t q_dst2_r, q_dst2_i; float32_t *p_src, *p_src2, *p_dst, *p_dst2, *p_twiddles; dst[0].r = (src[0].r + src[ncfft].r) * 0.5f; dst[0].i = (src[0].r - src[ncfft].r) * 0.5f; if (count >= 4) { for (k = 1; k <= count ; k += 4) { p_src = (float32_t*) (& (src[k])); p_src2 = (float32_t*) (& (src[ncfft - k - 3])); p_twiddles = (float32_t*) (& (twiddles[k - 1])); p_dst = (float32_t*) (& (dst[k])); p_dst2 = (float32_t*) (& (dst[ncfft - k - 3])); q2_fk = vld2q_f32 (p_src); q2_fnkc = vld2q_f32 (p_src2); q2_tw = vld2q_f32 (p_twiddles); q2_fnkc.val[0] = vrev64q_f32 (q2_fnkc.val[0]); q2_fnkc.val[1] = vrev64q_f32 (q2_fnkc.val[1]); q_fnkc_r = vcombine_f32 (vget_high_f32 (q2_fnkc.val[0]), vget_low_f32 (q2_fnkc.val[0])); q_fnkc_i = vcombine_f32 (vget_high_f32 (q2_fnkc.val[1]), vget_low_f32 (q2_fnkc.val[1])); q_fnkc_i = vnegq_f32 (q_fnkc_i); q_fek_r = vaddq_f32 (q2_fk.val[0], q_fnkc_r); q_fek_i = vaddq_f32 (q2_fk.val[1], q_fnkc_i); q_tmp0 = vsubq_f32 (q2_fk.val[0], q_fnkc_r); q_tmp1 = vsubq_f32 (q2_fk.val[1], q_fnkc_i); q_fok_r = vmulq_f32 (q_tmp0, q2_tw.val[0]); q_fok_i = vmulq_f32 (q_tmp1, q2_tw.val[0]); q_tmp2 = vmulq_f32 (q_tmp1, q2_tw.val[1]); q_tmp3 = vmulq_f32 (q_tmp0, q2_tw.val[1]); q_fok_r = vaddq_f32 (q_fok_r, q_tmp2); q_fok_i = vsubq_f32 (q_fok_i, q_tmp3); q_val = vdupq_n_f32 (0.5f); q_dst2_r = vsubq_f32 (q_fek_r, q_fok_r); q_dst2_i = vsubq_f32 (q_fok_i, q_fek_i); q2_dst.val[0] = vaddq_f32 (q_fek_r, q_fok_r); q2_dst.val[1] = vaddq_f32 (q_fek_i, q_fok_i); q_dst2_r = vmulq_f32 (q_dst2_r, q_val); q_dst2_i = vmulq_f32 (q_dst2_i, q_val); q2_dst.val[0] = vmulq_f32 (q2_dst.val[0], q_val); q2_dst.val[1] = vmulq_f32 (q2_dst.val[1], q_val); q_dst2_r = vrev64q_f32 (q_dst2_r); q_dst2_i = vrev64q_f32 (q_dst2_i); q2_dst2.val[0] = vcombine_f32 (vget_high_f32 (q_dst2_r), vget_low_f32 (q_dst2_r)); q2_dst2.val[1] = vcombine_f32 (vget_high_f32 (q_dst2_i), vget_low_f32 (q_dst2_i)); vst2q_f32 (p_dst, q2_dst); vst2q_f32 (p_dst2, q2_dst2); } } else { for (k = 1; k <= count ; k++) { fk = src[k]; fnkc.r = src[ncfft - k].r; fnkc.i = -src[ncfft - k].i; fek.r = fk.r + fnkc.r; fek.i = fk.i + fnkc.i; tmp.r = fk.r - fnkc.r; tmp.i = fk.i - fnkc.i; fok.r = tmp.r * twiddles[k - 1].r + tmp.i * twiddles[k - 1].i; fok.i = tmp.i * twiddles[k - 1].r - tmp.r * twiddles[k - 1].i; dst[k].r = (fek.r + fok.r) * 0.5f; dst[k].i = (fek.i + fok.i) * 0.5f; dst[ncfft - k].r = (fek.r - fok.r) * 0.5f; dst[ncfft - k].i = (fok.i - fek.i) * 0.5f; } } }
/* * NE10 Library : math/NE10_subc.neon.c */ #include "NE10_types.h" #include "macros.h" #include <assert.h> #include <arm_neon.h> ne10_result_t ne10_subc_float_neon (ne10_float32_t * dst, ne10_float32_t * src, const ne10_float32_t cst, ne10_uint32_t count) { NE10_XC_OPERATION_FLOAT_NEON ( n_dst = vsubq_f32 (n_src , n_cst); , n_tmp_src = vsub_f32 (n_tmp_src, n_tmp_cst); ); } ne10_result_t ne10_subc_vec2f_neon (ne10_vec2f_t * dst, ne10_vec2f_t * src, const ne10_vec2f_t * cst, ne10_uint32_t count) { NE10_XC_OPERATION_VEC2F_NEON ( n_dst = vsubq_f32 (n_src , n_cst); , n_tmp_src = vsub_f32 (n_tmp_src, n_tmp_cst); ); }
static float32x4_t vpowq_f32(float32x4_t a, float32x4_t b) { // a^b = exp2(b * log2(a)) // exp2(x) and log2(x) are calculated using polynomial approximations. float32x4_t log2_a, b_log2_a, a_exp_b; // Calculate log2(x), x = a. { // To calculate log2(x), we decompose x like this: // x = y * 2^n // n is an integer // y is in the [1.0, 2.0) range // // log2(x) = log2(y) + n // n can be evaluated by playing with float representation. // log2(y) in a small range can be approximated, this code uses an order // five polynomial approximation. The coefficients have been // estimated with the Remez algorithm and the resulting // polynomial has a maximum relative error of 0.00086%. // Compute n. // This is done by masking the exponent, shifting it into the top bit of // the mantissa, putting eight into the biased exponent (to shift/ // compensate the fact that the exponent has been shifted in the top/ // fractional part and finally getting rid of the implicit leading one // from the mantissa by substracting it out. const uint32x4_t vec_float_exponent_mask = vdupq_n_u32(0x7F800000); const uint32x4_t vec_eight_biased_exponent = vdupq_n_u32(0x43800000); const uint32x4_t vec_implicit_leading_one = vdupq_n_u32(0x43BF8000); const uint32x4_t two_n = vandq_u32(vreinterpretq_u32_f32(a), vec_float_exponent_mask); const uint32x4_t n_1 = vshrq_n_u32(two_n, kShiftExponentIntoTopMantissa); const uint32x4_t n_0 = vorrq_u32(n_1, vec_eight_biased_exponent); const float32x4_t n = vsubq_f32(vreinterpretq_f32_u32(n_0), vreinterpretq_f32_u32(vec_implicit_leading_one)); // Compute y. const uint32x4_t vec_mantissa_mask = vdupq_n_u32(0x007FFFFF); const uint32x4_t vec_zero_biased_exponent_is_one = vdupq_n_u32(0x3F800000); const uint32x4_t mantissa = vandq_u32(vreinterpretq_u32_f32(a), vec_mantissa_mask); const float32x4_t y = vreinterpretq_f32_u32(vorrq_u32(mantissa, vec_zero_biased_exponent_is_one)); // Approximate log2(y) ~= (y - 1) * pol5(y). // pol5(y) = C5 * y^5 + C4 * y^4 + C3 * y^3 + C2 * y^2 + C1 * y + C0 const float32x4_t C5 = vdupq_n_f32(-3.4436006e-2f); const float32x4_t C4 = vdupq_n_f32(3.1821337e-1f); const float32x4_t C3 = vdupq_n_f32(-1.2315303f); const float32x4_t C2 = vdupq_n_f32(2.5988452f); const float32x4_t C1 = vdupq_n_f32(-3.3241990f); const float32x4_t C0 = vdupq_n_f32(3.1157899f); float32x4_t pol5_y = C5; pol5_y = vmlaq_f32(C4, y, pol5_y); pol5_y = vmlaq_f32(C3, y, pol5_y); pol5_y = vmlaq_f32(C2, y, pol5_y); pol5_y = vmlaq_f32(C1, y, pol5_y); pol5_y = vmlaq_f32(C0, y, pol5_y); const float32x4_t y_minus_one = vsubq_f32(y, vreinterpretq_f32_u32(vec_zero_biased_exponent_is_one)); const float32x4_t log2_y = vmulq_f32(y_minus_one, pol5_y); // Combine parts. log2_a = vaddq_f32(n, log2_y); } // b * log2(a) b_log2_a = vmulq_f32(b, log2_a); // Calculate exp2(x), x = b * log2(a). { // To calculate 2^x, we decompose x like this: // x = n + y // n is an integer, the value of x - 0.5 rounded down, therefore // y is in the [0.5, 1.5) range // // 2^x = 2^n * 2^y // 2^n can be evaluated by playing with float representation. // 2^y in a small range can be approximated, this code uses an order two // polynomial approximation. The coefficients have been estimated // with the Remez algorithm and the resulting polynomial has a // maximum relative error of 0.17%. // To avoid over/underflow, we reduce the range of input to ]-127, 129]. const float32x4_t max_input = vdupq_n_f32(129.f); const float32x4_t min_input = vdupq_n_f32(-126.99999f); const float32x4_t x_min = vminq_f32(b_log2_a, max_input); const float32x4_t x_max = vmaxq_f32(x_min, min_input); // Compute n. const float32x4_t half = vdupq_n_f32(0.5f); const float32x4_t x_minus_half = vsubq_f32(x_max, half); const int32x4_t x_minus_half_floor = vcvtq_s32_f32(x_minus_half); // Compute 2^n. const int32x4_t float_exponent_bias = vdupq_n_s32(127); const int32x4_t two_n_exponent = vaddq_s32(x_minus_half_floor, float_exponent_bias); const float32x4_t two_n = vreinterpretq_f32_s32(vshlq_n_s32(two_n_exponent, kFloatExponentShift)); // Compute y. const float32x4_t y = vsubq_f32(x_max, vcvtq_f32_s32(x_minus_half_floor)); // Approximate 2^y ~= C2 * y^2 + C1 * y + C0. const float32x4_t C2 = vdupq_n_f32(3.3718944e-1f); const float32x4_t C1 = vdupq_n_f32(6.5763628e-1f); const float32x4_t C0 = vdupq_n_f32(1.0017247f); float32x4_t exp2_y = C2; exp2_y = vmlaq_f32(C1, y, exp2_y); exp2_y = vmlaq_f32(C0, y, exp2_y); // Combine parts. a_exp_b = vmulq_f32(exp2_y, two_n); } return a_exp_b; }
static void OverdriveAndSuppressNEON(AecCore* aec, float hNl[PART_LEN1], const float hNlFb, float efw[2][PART_LEN1]) { int i; const float32x4_t vec_hNlFb = vmovq_n_f32(hNlFb); const float32x4_t vec_one = vdupq_n_f32(1.0f); const float32x4_t vec_minus_one = vdupq_n_f32(-1.0f); const float32x4_t vec_overDriveSm = vmovq_n_f32(aec->overDriveSm); // vectorized code (four at once) for (i = 0; i + 3 < PART_LEN1; i += 4) { // Weight subbands float32x4_t vec_hNl = vld1q_f32(&hNl[i]); const float32x4_t vec_weightCurve = vld1q_f32(&WebRtcAec_weightCurve[i]); const uint32x4_t bigger = vcgtq_f32(vec_hNl, vec_hNlFb); const float32x4_t vec_weightCurve_hNlFb = vmulq_f32(vec_weightCurve, vec_hNlFb); const float32x4_t vec_one_weightCurve = vsubq_f32(vec_one, vec_weightCurve); const float32x4_t vec_one_weightCurve_hNl = vmulq_f32(vec_one_weightCurve, vec_hNl); const uint32x4_t vec_if0 = vandq_u32(vmvnq_u32(bigger), vreinterpretq_u32_f32(vec_hNl)); const float32x4_t vec_one_weightCurve_add = vaddq_f32(vec_weightCurve_hNlFb, vec_one_weightCurve_hNl); const uint32x4_t vec_if1 = vandq_u32(bigger, vreinterpretq_u32_f32(vec_one_weightCurve_add)); vec_hNl = vreinterpretq_f32_u32(vorrq_u32(vec_if0, vec_if1)); { const float32x4_t vec_overDriveCurve = vld1q_f32(&WebRtcAec_overDriveCurve[i]); const float32x4_t vec_overDriveSm_overDriveCurve = vmulq_f32(vec_overDriveSm, vec_overDriveCurve); vec_hNl = vpowq_f32(vec_hNl, vec_overDriveSm_overDriveCurve); vst1q_f32(&hNl[i], vec_hNl); } // Suppress error signal { float32x4_t vec_efw_re = vld1q_f32(&efw[0][i]); float32x4_t vec_efw_im = vld1q_f32(&efw[1][i]); vec_efw_re = vmulq_f32(vec_efw_re, vec_hNl); vec_efw_im = vmulq_f32(vec_efw_im, vec_hNl); // Ooura fft returns incorrect sign on imaginary component. It matters // here because we are making an additive change with comfort noise. vec_efw_im = vmulq_f32(vec_efw_im, vec_minus_one); vst1q_f32(&efw[0][i], vec_efw_re); vst1q_f32(&efw[1][i], vec_efw_im); } } // scalar code for the remaining items. for (; i < PART_LEN1; i++) { // Weight subbands if (hNl[i] > hNlFb) { hNl[i] = WebRtcAec_weightCurve[i] * hNlFb + (1 - WebRtcAec_weightCurve[i]) * hNl[i]; } hNl[i] = powf(hNl[i], aec->overDriveSm * WebRtcAec_overDriveCurve[i]); // Suppress error signal efw[0][i] *= hNl[i]; efw[1][i] *= hNl[i]; // Ooura fft returns incorrect sign on imaginary component. It matters // here because we are making an additive change with comfort noise. efw[1][i] *= -1; } }
// __INLINE void arm_cmplx_mult_cmplx_f32_dot( float32_t * pSrcA, float32_t * pSrcB, float32_t * pDst, uint32_t numSamples) { float32_t a, b, c, d; /* Temporary variables to store real and imaginary values */ float32x4_t A1, A2; /* Temporary variables to store real and imaginary values of source buffer A */ float32x4_t B1, B2; /* Temporary variables to store real and imaginary values of source buffer B */ float32x4_t C1, C2, C3, C4; /* Temporary variables to store multiplication output */ float32x4x2_t out1, out2, out3, out4; /* Temporary variables to stroe output result */ float32x4x2_t acc1, acc2, acc3, acc4; /* Accumulators */ float sum_real, sum_img; /* */ uint32_t blkCnt; /* loop counters */ /* Clear accumulators VDUP.32 q0,r0 Vector Duplicate duplicates a scalar into every element of the destination vector. */ acc1.val[0] = vdupq_n_f32(0.0f); acc1.val[1] = vdupq_n_f32(0.0f); acc2.val[0] = vdupq_n_f32(0.0f); acc2.val[1] = vdupq_n_f32(0.0f); acc3.val[0] = vdupq_n_f32(0.0f); acc3.val[1] = vdupq_n_f32(0.0f); acc4.val[0] = vdupq_n_f32(0.0f); acc4.val[1] = vdupq_n_f32(0.0f); /* Loop over blockSize number of values */ blkCnt = numSamples >> 4u; while(blkCnt > 0u) { /* A1, A2, B1, B2 each has two complex data. */ /******************************************************/ /* Step 1: Load data A1, A2, B1, B2 for group a:*/ /* read 2 complex values at a time from source A buffer float32x4_t vld1q_f32(__transfersize(4) float32_t const * ptr); VLD1.32 {d0, d1}, [r0] */ A1 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /* read 2 complex values at a time from source A buffer */ A2 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /* read 2 complex values at a time from source B buffer */ B1 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /* read 2 complex values at a time from source B buffer */ B2 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /******************************************************/ /* Step 2: Unzip data Out1, Out2 for group a:*/ /* unzip real and imag values A1: reala0, imga0, reala1, imga1 A2: realb0, imgb0, realb1, imgb1 out1.val0: reala0, reala1, realb0, realb1; out1.val1: imga0, imga1, imgb0, imgb1 vuzpq_f32: float32x4x2_t vuzpq_f32 (float32x4_t, float32x4_t) Form of expected instruction(s): vuzp.32 q0, q1 Vector Unzip de-interleaves the elements of two vectors. */ out1 = vuzpq_f32(A1, A2); out2 = vuzpq_f32(B1, B2); /******************************************************/ /* Step 1: Load data A1, A2, B1, B2 for group b:*/ /* read 2 complex values at a time from source A buffer */ A1 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /* read 2 complex values at a time from source A buffer */ A2 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /* read 2 complex values at a time from source B buffer */ B1 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /* read 2 complex values at a time from source B buffer */ B2 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /******************************************************/ /* Step 3: Compute data C1,C2,C3,C4 for group a:*/ /* C[2 * i] = A[2 * i] * B[2 * i] - A[2 * i + 1] * B[2 * i + 1]. */ /* C[2 * i + 1] = A[2 * i] * B[2 * i + 1] + A[2 * i + 1] * B[2 * i]. */ /* vmulq_f32: VMUL.F32 q0,q0,q0 val[0]: real val[1]: img C1 = a.real*b.real; C2 = a.img*b.img C3 = a.img*b.real; C4 = a.real*b.img */ /* multiply 4 samples at a time from A1 real input with B1 real input */ C1 = vmulq_f32(out1.val[0], out2.val[0]); /* multiply 4 samples at a time from A1 imaginary input with B1 imaginary input */ C2 = vmulq_f32(out1.val[1], out2.val[1]); /* multiply 4 samples at a time from A1 imaginary input with B1 real input */ C3 = vmulq_f32(out1.val[1], out2.val[0]); /* multiply 4 samples at a time from A1 real input with B1 imaginary input */ C4 = vmulq_f32(out1.val[0], out2.val[1]); /* real: c1-c2; img: c3+c4 */ /******************************************************/ /* Step 2: Unzip data Out2, Out3 for group b:*/ out2 = vuzpq_f32(A1, A2); out3 = vuzpq_f32(B1, B2); /******************************************************/ /* Step 1: Load data A1, A2 for group c:*/ /* read 2 complex values at a time from source A buffer */ A1 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /* read 2 complex values at a time from source A buffer */ A2 = vld1q_f32(pSrcA); /* increment source A buffer by 4 */ pSrcA += 4u; /******************************************************/ /* Step 4: Output or accumlate data for group a:*/ /* (a+bi)*(c+di) = (ac-bd)+(ad+bc)i*/ /* real: c1-c2; img: c3+c4 */ /* subtract 4 samples at time from real result to imaginary result, got four real part */ /* C1 = a.real*b.real; C2 = a.img*b.img C3 = a.img*b.real; C4 = a.real*b.img vaddq_f32: VADD.F32 q0,q0,q0 */ out1.val[0] = vsubq_f32(C1, C2); acc1.val[0] = vaddq_f32(out1.val[0], acc1.val[0]); /* add by Hank */ /* add real*imaginary result with imaginary*real result 4 at a time */ out1.val[1] = vaddq_f32(C3, C4); acc1.val[1] = vaddq_f32(out1.val[1], acc1.val[1]); /* add by Hank */ /* out1 is four complex product. */ /******************************************************/ /* Step 1: Load data B1, B2 for group c:*/ /* read 2 complex values at a time from source B buffer */ B1 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /* read 2 complex values at a time from source B buffer */ B2 = vld1q_f32(pSrcB); /* increment source B buffer by 4 */ pSrcB += 4u; /******************************************************/ /* Step 3: Compute data C1,C2 for group b:*/ /* multiply 4 samples at a time from A1 real input with B1 real input */ C1 = vmulq_f32(out2.val[0], out3.val[0]); /* multiply 4 samples at a time from A1 imaginary input with B1 imaginary input */ C2 = vmulq_f32(out2.val[1], out3.val[1]); /******************************************************/ /* Step 5: Store data for group a:*/ /* Store 4 complex samples to destination buffer VST2.32 {d0, d2}, [r0] */ //vst2q_f32(pDst, out1); /* increment destination buffer by 8 */ //pDst += 8u; /******************************************************/ /* Step 3: Compute data C3,C4 for group b:*/ /* multiply 4 samples at a time from A1 imaginary input with B1 real input */ C3 = vmulq_f32(out2.val[1], out3.val[0]); /* multiply 4 samples at a time from A1 real input with B1 imaginary input */ C4 = vmulq_f32(out2.val[0], out3.val[1]); /******************************************************/ /* Step 2: Unzip data Out1, Out2 for group C:*/ out3 = vuzpq_f32(A1, A2); out4 = vuzpq_f32(B1, B2); /******************************************************/ /* Step 1: Load data A1, A2, B1, B2 for group d:*/ /* read 4 complex values from source A buffer */ A1 = vld1q_f32(pSrcA); pSrcA += 4u; A2 = vld1q_f32(pSrcA); pSrcA += 4u; /* read 4 complex values from source B buffer */ B1 = vld1q_f32(pSrcB); pSrcB += 4u; B2 = vld1q_f32(pSrcB); pSrcB += 4u; /******************************************************/ /* Step 4: Output or accumlate data for group b:*/ /* subtract 4 samples at time from real result to imaginary result */ out2.val[0] = vsubq_f32(C1, C2); /* add real*imaginary result with imaginary*real result 4 at a time */ out2.val[1] = vaddq_f32(C3, C4); acc2.val[0] = vaddq_f32(out2.val[0], acc2.val[0]); /* add by Hank */ acc2.val[1] = vaddq_f32(out2.val[1], acc2.val[1]); /* add by Hank */ /******************************************************/ /* Step 3: Compute data C1,C2,C3,C4 for group c:*/ /* multiply 4 samples at a time from A3 real input with B3 real input */ C1 = vmulq_f32(out3.val[0], out4.val[0]); /* multiply 4 samples at a time from A3 imaginary input with B3 imaginary input */ C2 = vmulq_f32(out3.val[1], out4.val[1]); /* multiply 4 samples at a time from A3 imaginary input with B3 real input */ C3 = vmulq_f32(out3.val[1], out4.val[0]); /* multiply 4 samples at a time from A3 real input with B3 imaginary input */ C4 = vmulq_f32(out3.val[0], out4.val[1]); /******************************************************/ /* Step 2: Unzip data Out1, Out2 for group D:*/ out1 = vuzpq_f32(A1, A2); out4 = vuzpq_f32(B1, B2); /******************************************************/ /* Step 5: Store data for group b:*/ /* Store 4 complex samples to destination buffer */ //vst2q_f32(pDst, out2); /* increment destination buffer by 8 */ //pDst += 8u; /******************************************************/ /* Step 4: Output or accumlate data for group c:*/ /* subtract 4 samples at time from real result to imaginary result */ out3.val[0] = vsubq_f32(C1, C2); /* add real*imaginary result with imaginary*real result 4 at a time */ out3.val[1] = vaddq_f32(C3, C4); acc3.val[0] = vaddq_f32(out3.val[0], acc3.val[0]); /* add by Hank */ acc3.val[1] = vaddq_f32(out3.val[1], acc3.val[1]); /* add by Hank */ /******************************************************/ /* Step 3: Compute data C1,C2,C3,C4 for group d:*/ /* multiply 4 samples at a time from A1 real input with B1 real input */ C1 = vmulq_f32(out1.val[0], out4.val[0]); /* multiply 4 samples at a time from A1 imaginary input with B1 imaginary input */ C2 = vmulq_f32(out1.val[1], out4.val[1]); /* multiply 4 samples at a time from A1 imaginary input with B1 real input */ C3 = vmulq_f32(out1.val[1], out4.val[0]); /* multiply 4 samples at a time from A1 real input with B1 imaginary input */ C4 = vmulq_f32(out1.val[0], out4.val[1]); /******************************************************/ /* Step 5: Store data for group c:*/ /* Store 4 complex samples to destination buffer */ //vst2q_f32(pDst, out3); /******************************************************/ /* Step 4: Output or accumlate data for group d:*/ /* subtract 4 samples at time from real result to imaginary result */ out4.val[0] = vsubq_f32(C1, C2); /* increment destination buffer by 8 */ //pDst += 8u; /******************************************************/ /* Step 4: Output or accumlate data for group d:*/ /* add real*imaginary result with imaginary*real result 4 at a time */ out4.val[1] = vaddq_f32(C3, C4); acc4.val[0] = vaddq_f32(out4.val[0], acc4.val[0]); /* add by Hank */ acc4.val[1] = vaddq_f32(out4.val[1], acc4.val[1]); /* add by Hank */ /* zip real and imag values */ //out4 = vzipq_f32(out4.val[0], out4.val[1]); /******************************************************/ /* Step 5: Store data for group d:*/ /* Store 4 complex samples to destination buffer */ //vst1q_f32(pDst, out4.val[0]); //pDst += 4u; //vst1q_f32(pDst, out4.val[1]); //pDst += 4u; /* Decrement the numSamples loop counter */ blkCnt--; } blkCnt = numSamples & 15u; blkCnt = blkCnt >> 2u; /* If the blockSize is not a multiple of 16, compute remaining output samples. ** Compute multiple of 4 samples at a time in second loop. ** and remaining 1 to 3 samples in third loop. */ while(blkCnt > 0u) { /* Step 1: Load data A1, A2, B1, B2 */ /* read 4 complex values at a time from source A buffer */ A1 = vld1q_f32(pSrcA); /* increment source A buffer by 8 */ pSrcA += 4u; A2 = vld1q_f32(pSrcA); pSrcA += 4u; /* read 4 complex values at a time from source B buffer */ B1 = vld1q_f32(pSrcB); /* increment source B buffer by 8 */ pSrcB += 4u; B2 = vld1q_f32(pSrcB); pSrcB += 4u; /* Step 2: Unzip data Out1, Out2 */ /* Unzip data */ out1 = vuzpq_f32(A1, A2); out2 = vuzpq_f32(B1, B2); /* Step 3: Compute data C1,C2,C3,C4 */ /* C[2 * i] = A[2 * i] * B[2 * i] - A[2 * i + 1] * B[2 * i + 1]. */ /* C[2 * i + 1] = A[2 * i] * B[2 * i + 1] + A[2 * i + 1] * B[2 * i]. */ /* multiply 4 samples at a time from A1 real input with B1 real input */ C1 = vmulq_f32(out1.val[0], out2.val[0]); /* multiply 4 samples at a time from A1 imaginary input with B1 imaginary input */ C2 = vmulq_f32(out1.val[1], out2.val[1]); /* multiply 4 samples at a time from A1 imaginary input with B1 real input */ C3 = vmulq_f32(out1.val[1], out2.val[0]); /* multiply 4 samples at a time from A1 real input with B1 imaginary input */ C4 = vmulq_f32(out1.val[0], out2.val[1]); /* Step 4: Output or accumlate data for group d:*/ /* subtract 4 samples at time from real result to imaginary result */ out1.val[0] = vsubq_f32(C1, C2); /* add real*imaginary result with imaginary*real result 4 at a time */ out1.val[1] = vaddq_f32(C3, C4); acc1.val[0] = vaddq_f32(out1.val[0], acc1.val[0]); /* add by Hank */ acc1.val[1] = vaddq_f32(out1.val[1], acc1.val[1]); /* add by Hank */ //out1 = vzipq_f32(out1.val[0], out1.val[1]); /* Step 5: Store data */ /* Store 4 complex samples to destination buffer */ //vst1q_f32(pDst, out1.val[0]); //pDst += 4u; //vst1q_f32(pDst, out1.val[1]); //pDst += 4u; /* Decrement the numSamples loop counter */ blkCnt--; } blkCnt = numSamples & 3u; /* If the blockSize is not a multiple of 4, compute any remaining output samples here. ** No intrinsics is used. */ sum_real =0; sum_img =0; while(blkCnt > 0u) { /* C[2 * i] = A[2 * i] * B[2 * i] - A[2 * i + 1] * B[2 * i + 1]. */ /* C[2 * i + 1] = A[2 * i] * B[2 * i + 1] + A[2 * i + 1] * B[2 * i]. */ a = *pSrcA++; b = *pSrcA++; c = *pSrcB++; d = *pSrcB++; /* store the result in the destination buffer. */ sum_real += ((a * c) - (b * d)); sum_img += ((a * d) + (b * c)); /* Decrement the numSamples loop counter */ blkCnt--; } /* add 4 accumulators */ acc1.val[0] = vaddq_f32(acc1.val[0], acc2.val[0]); acc1.val[1] = vaddq_f32(acc1.val[1], acc2.val[1]); acc2.val[0] = vaddq_f32(acc3.val[0], acc4.val[0]); acc2.val[1] = vaddq_f32(acc3.val[1], acc4.val[1]); acc1.val[0] = vaddq_f32(acc1.val[0], acc2.val[0]); acc1.val[1] = vaddq_f32(acc1.val[1], acc2.val[1]); sum_real += vgetq_lane_f32(acc1.val[0], 0) + vgetq_lane_f32(acc1.val[0], 1) + vgetq_lane_f32(acc1.val[0], 2) + vgetq_lane_f32(acc1.val[0], 3); sum_img += vgetq_lane_f32(acc1.val[1], 0) + vgetq_lane_f32(acc1.val[1], 1) + vgetq_lane_f32(acc1.val[1], 2) + vgetq_lane_f32(acc1.val[1], 3); *pDst++=sum_real; *pDst++=sum_img;
static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); }