// Copyright (C) 2016 Erik Hofman - erik@ehofman.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // #ifndef __SIMD_H__ #define __SIMD_H__ 1 #include #include #include #include #include #if defined(_MSC_VER) # include #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) # include #elif defined(__GNUC__) && defined(__ARM_NEON__) # include # include #endif #include #include template class simd4_t; namespace simd4 { template inline simd4_t min(simd4_t v1, const simd4_t& v2) { for (int i=0; i::min(v1[i], v2[i]); } return v1; } template inline simd4_t max(simd4_t v1, const simd4_t& v2) { for (int i=0; i::max(v1[i], v2[i]); } return v1; } template inline simd4_t abs(simd4_t v) { for (int i=0; i inline T magnitude2(const simd4_t& vi) { simd4_t v(vi); T mag2 = 0; v = v*v; for (int i=0; i inline simd4_t interpolate(T tau, const simd4_t& v1, const simd4_t& v2) { return (T(1)-tau)*v1 + tau*v2; } template inline T magnitude(const simd4_t& v) { return std::sqrt(magnitude2(v)); } template inline T normalize(simd4_t& v) { T mag = simd4::magnitude(v); if (mag) { v /= mag; } else { v = T(0); } return mag; } template inline T dot(const simd4_t& v1, const simd4_t& v2) { simd4_t v(v1*v2); T dp = T(0); for (int i=0; i inline simd4_t cross(const simd4_t& v1, const simd4_t& v2) { simd4_t d; d[0] = v1[1]*v2[2] - v1[2]*v2[1]; d[1] = v1[2]*v2[0] - v1[0]*v2[2]; d[2] = v1[0]*v2[1] - v1[1]*v2[0]; return d; } } /* namespace simd4 */ template class simd4_t { private: union { T _v4[4]; T vec[N]; }; public: simd4_t(void) { for (int i=0; i<4; ++i) _v4[i] = 0; } simd4_t(T s) { for (int i=0; i simd4_t(const simd4_t& v) { std::memcpy(_v4, v.ptr(), sizeof(T[M])); for (int i=(M inline simd4_t& operator=(const simd4_t& v) { *this = simd4_t(v); return *this; } inline simd4_t& operator+=(T s) { for (int i=0; i& operator+=(const T v[N]) { for (int i=0; i& operator+=(const simd4_t& v) { for (int i=0; i& operator-=(T s) { for (int i=0; i& operator-=(const T v[N]) { for (int i=0; i& operator-=(const simd4_t& v) { for (int i=0; i& operator*=(T s) { for (int i=0; i& operator*=(const T v[N]) { for (int i=0; i& operator*=(const simd4_t& v) { for (int i=0; i& operator/=(T s) { for (int i=0; i& operator/=(const T v[N]) { for (int i=0; i& operator/=(const simd4_t& v) { for (int i=0; i inline simd4_t operator-(const simd4_t& v) { simd4_t r(T(0)); r -= v; return r; } template inline simd4_t operator+(simd4_t v1, const simd4_t& v2) { v1 += v2; return v1; } template inline simd4_t operator-(simd4_t v1, const simd4_t& v2) { v1 -= v2; return v1; } template inline simd4_t operator*(simd4_t v1, const simd4_t& v2) { v1 *= v2; return v1; } template inline simd4_t operator/(simd4_t v1, const simd4_t& v2) { v1 /= v2; return v1; } template inline simd4_t operator*(T f, simd4_t v) { v *= f; return v; } template inline simd4_t operator*(simd4_t v, T f) { v *= f; return v; } # ifdef __SSE__ namespace simd4 { alignas(16) static const uint32_t m2a32[] = { 0xffffffff,0xffffffff,0,0 }; alignas(16) static const uint32_t m3a32[] = { 0xffffffff,0xffffffff,0xffffffff,0 }; alignas(32) static const uint64_t m2a64[] = { 0xffffffffffffffff,0xffffffffffffffff,0,0 }; alignas(32) static const uint64_t m3a64[] = { 0xffffffffffffffff,0xffffffffffffffff,0xffffffffffffffff,0 }; }; /* namespace simd4 */ template class alignas(16) simd4_t { private: typedef float __vec4f_t[N]; union { __m128 simd4; __vec4f_t vec; }; public: simd4_t(void) : simd4(_mm_setzero_ps()) {} simd4_t(float f) {} simd4_t(float x, float y) : simd4_t(x,y,0,0) {} simd4_t(float x, float y, float z) : simd4_t(x,y,z,0) {} simd4_t(float x, float y, float z, float w) {} simd4_t(const __vec4f_t v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const __m128& v) { simd4 = v; } inline const __m128 (&v4(void) const) { return simd4; } inline __m128 (&v4(void)) { return simd4; } inline const float (&ptr(void) const)[N] { return vec; } inline float (&ptr(void))[N] { return vec; } inline const float& operator[](unsigned n) const { assert(n inline simd4_t& operator=(const simd4_t& v) { simd4 = simd4_t(v).v4(); return *this; } inline simd4_t& operator=(const __m128& v) { simd4 = v; return *this; } inline simd4_t& operator+=(float f) { simd4 = _mm_add_ps(simd4, _mm_set1_ps(f)); return *this; } inline simd4_t& operator+=(const simd4_t& v) { simd4 = _mm_add_ps(simd4, v.v4()); return *this; } inline simd4_t& operator-=(float f) { simd4 = _mm_sub_ps(simd4, _mm_set1_ps(f)); return *this; } inline simd4_t& operator-=(const simd4_t& v) { simd4 = _mm_sub_ps(simd4, v.v4()); return *this; } inline simd4_t& operator*=(float f) { simd4 = _mm_mul_ps(simd4, _mm_set1_ps(f)); return *this; } inline simd4_t& operator*=(const simd4_t& v) { simd4 = _mm_mul_ps(simd4, v.v4()); return *this; } inline simd4_t& operator/=(float f) { simd4 = _mm_div_ps(simd4, _mm_set1_ps(f)); return *this; } inline simd4_t& operator/=(const simd4_t& v) { simd4 = _mm_div_ps(simd4, v.v4()); return *this; } }; static const __m128 fmask2 = _mm_load_ps((const float*)simd4::m2a32); static const __m128 fmask3 = _mm_load_ps((const float*)simd4::m3a32); template<> inline simd4_t::simd4_t(float x, float y, float z, float w) { simd4 = _mm_set_ps(w,z,y,x); } template<> inline simd4_t::simd4_t(float x, float y, float z, float w) { simd4 = _mm_and_ps(_mm_set_ps(w,z,y,x), fmask3); } template<> inline simd4_t::simd4_t(float x, float y, float z, float w) { simd4 = _mm_and_ps(_mm_set_ps(w,z,y,x), fmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_ps(v.v4(), fmask3); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_ps(v.v4(), fmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_ps(v.v4(), fmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const __vec4f_t v) { simd4 = _mm_loadu_ps(v); } template<> inline simd4_t::simd4_t(const __vec4f_t v) { simd4 = _mm_and_ps(_mm_loadu_ps(v), fmask3); } template<> inline simd4_t::simd4_t(const __vec4f_t v) { simd4 = _mm_and_ps(_mm_loadu_ps(v), fmask2); } template<> inline simd4_t::simd4_t(float f) { simd4 = _mm_set1_ps(f); } template<> inline simd4_t::simd4_t(float f) { simd4 = _mm_and_ps(_mm_set1_ps(f), fmask3); } template<> inline simd4_t::simd4_t(float f) { simd4 = _mm_and_ps(_mm_set1_ps(f), fmask2); } namespace simd4 { // http://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86 # ifdef __SSE3__ inline static float hsum_ps_sse(__m128 v) { __m128 shuf = _mm_movehdup_ps(v); __m128 sums = _mm_add_ps(v, shuf); shuf = _mm_movehl_ps(shuf, sums); sums = _mm_add_ss(sums, shuf); return _mm_cvtss_f32(sums); } # else /* SSE */ inline static float hsum_ps_sse(__m128 v) { __m128 shuf = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 3, 0, 1)); __m128 sums = _mm_add_ps(v, shuf); shuf = _mm_movehl_ps(shuf, sums); sums = _mm_add_ss(sums, shuf); return _mm_cvtss_f32(sums); } # endif template<> inline float magnitude2(const simd4_t& v) { return hsum_ps_sse(_mm_mul_ps(v.v4(),v.v4())); } template<> inline float dot(const simd4_t& v1, const simd4_t& v2) { return hsum_ps_sse(_mm_mul_ps(v1.v4(),v2.v4())); } template<> inline simd4_t cross(const simd4_t& v1, const simd4_t& v2) { // http://threadlocalmutex.com/?p=8 __m128 v41 = v1.v4(), v42 = v2.v4(); __m128 a = _mm_shuffle_ps(v41, v41, _MM_SHUFFLE(3, 0, 2, 1)); __m128 b = _mm_shuffle_ps(v42, v42, _MM_SHUFFLE(3, 0, 2, 1)); __m128 c = _mm_sub_ps(_mm_mul_ps(v41, b), _mm_mul_ps(a, v42)); return _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 0, 2, 1)); } template inline simd4_t min(simd4_t v1, const simd4_t& v2) { v1 = _mm_min_ps(v1.v4(), v2.v4()); return v1; } template inline simd4_t max(simd4_t v1, const simd4_t& v2) { v1 = _mm_max_ps(v1.v4(), v2.v4()); return v1; } template inline simd4_tabs(simd4_t v) { static const __m128 sign_mask = _mm_set1_ps(-0.f); // -0.f = 1 << 31 v = _mm_andnot_ps(sign_mask, v.v4()); return v; } } /* namsepace simd4 */ # endif # ifdef __AVX_unsupported__ template class alignas(32) simd4_t { private: typedef double __vec4d_t[N]; union { __m256d simd4; __vec4d_t vec; }; public: simd4_t(void) : simd4(_mm256_setzero_pd()) {} simd4_t(double d) {} simd4_t(double x, double y) : simd4_t(x,y,0,0) {} simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {} simd4_t(double x, double y, double z, double w) {} simd4_t(const __vec4d_t v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const __m256d& v) { simd4 = v; } inline const __m256d (&v4(void) const) { return simd4; } inline __m256d (&v4(void)) { return simd4; } inline const double (&ptr(void) const)[N] { return vec; } inline double (&ptr(void))[N] { return vec; } inline const double& operator[](unsigned n) const { assert(n inline simd4_t& operator=(const simd4_t& v) { simd4 = simd4_t(v).v4(); return *this; } inline simd4_t& operator=(const __m256d& v) { simd4 = v; return *this; } inline simd4_t& operator+=(double d) { simd4 = _mm256_add_pd(simd4, _mm256_set1_pd(d)); return *this; } inline simd4_t& operator+=(const simd4_t& v) { simd4 = _mm256_add_pd(simd4, v.v4()); return *this; } inline simd4_t& operator-=(double d) { simd4 = _mm256_sub_pd(simd4, _mm256_set1_pd(d)); return *this; } inline simd4_t& operator-=(const simd4_t& v) { simd4 = _mm256_sub_pd(simd4, v.v4()); return *this; } inline simd4_t& operator*=(double d) { simd4 = _mm256_mul_pd(simd4, _mm256_set1_pd(d)); return *this; } inline simd4_t& operator*=(const simd4_t& v) { simd4 = _mm256_mul_pd(simd4, v.v4()); return *this; } inline simd4_t& operator/=(double d) { simd4 = _mm256_div_pd(simd4, _mm256_set1_pd(d)); return *this; } inline simd4_t& operator/=(const simd4_t& v) { simd4 = _mm256_div_pd(simd4, v.v4()); return *this; } }; static const __m256d dmask2 = _mm256_load_pd((const double*)simd4::m2a64); static const __m256d dmask3 = _mm256_load_pd((const double*)simd4::m3a64); template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4 = _mm256_set_pd(w,z,y,x); } template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4 = _mm256_and_pd(_mm256_set_pd(w,z,y,x), dmask3); } template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4 = _mm256_and_pd(_mm256_set_pd(w,z,y,x), dmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm256_and_pd(v.v4(), dmask3); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm256_and_pd(v.v4(), dmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm256_and_pd(v.v4(), dmask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4 = _mm256_loadu_pd(v); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask3); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask2); } template<> inline simd4_t::simd4_t(double d) { simd4 = _mm256_set1_pd(d); } template<> inline simd4_t::simd4_t(double d) { simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask3); } template<> inline simd4_t::simd4_t(double d) { simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask2); } namespace simd4 { // http://berenger.eu/blog/sseavxsimd-horizontal-sum-sum-simd-vector-intrinsic/ inline static double hsum_pd_avx(__m256d v) { const __m128d valupper = _mm256_extractf128_pd(v, 1); const __m128d vallower = _mm256_castpd256_pd128(v); _mm256_zeroupper(); const __m128d valval = _mm_add_pd(valupper, vallower); const __m128d sums = _mm_add_pd(_mm_permute_pd(valval,1), valval); return _mm_cvtsd_f64(sums); } template<> inline double magnitude2(const simd4_t& v) { return hsum_pd_avx(_mm256_mul_pd(v.v4(),v.v4())); } template<> inline double dot(const simd4_t& v1, const simd4_t& v2) { return hsum_pd_avx(_mm256_mul_pd(v1.v4(),v2.v4())); } # ifdef __AVX2__ template<> inline simd4_t cross(const simd4_t& v1, const simd4_t& v2) { // https://gist.github.com/L2Program/219e07581e69110e7942 __m256d v41 = v1.v4(), v42 = v2.v4(); return _mm256_sub_pd( _mm256_mul_pd( _mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 0, 2, 1)), _mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 1, 0, 2))), _mm256_mul_pd( _mm256_permute4x64_pd(v41,_MM_SHUFFLE(3, 1, 0, 2)), _mm256_permute4x64_pd(v42,_MM_SHUFFLE(3, 0, 2, 1)))); } # endif template inline simd4_t min(simd4_t v1, const simd4_t& v2) { v1 = _mm256_min_pd(v1.v4(), v2.v4()); return v1; } template inline simd4_t max(simd4_t v1, const simd4_t& v2) { v1 = _mm256_max_pd(v1.v4(), v2.v4()); return v1; } template inline simd4_tabs(simd4_t v) { static const __m256d sign_mask = _mm256_set1_pd(-0.); // -0. = 1 << 63 v = _mm256_andnot_pd(sign_mask, v.v4()); return v; } } /* namespace simd4 */ # elif defined __SSE2__ template class alignas(16) simd4_t { private: typedef double __vec4d_t[N]; union { __m128d simd4[2]; __vec4d_t vec; }; public: simd4_t(void) { simd4[0] = simd4[1] = _mm_setzero_pd(); } simd4_t(double d) {} simd4_t(double x, double y) : simd4_t(x,y,0,0) {} simd4_t(double x, double y, double z) : simd4_t(x,y,z,0) {} simd4_t(double x, double y, double z, double w) {} simd4_t(const __vec4d_t v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const __m128d v[2]) { simd4[0] = v[0]; simd4[1] = v[1]; } inline const __m128d (&v4(void) const)[2] { return simd4; } inline __m128d (&v4(void))[2] { return simd4; } inline const double (&ptr(void) const)[N] { return vec; } inline double (&ptr(void))[N] { return vec; } inline const double& operator[](unsigned n) const { assert(n inline simd4_t& operator=(const simd4_t& v) { simd4_t n(v); simd4[0] = n.v4()[0]; simd4[1] = n.v4()[1]; return *this; } inline simd4_t& operator=(const __m128d v[2]) { simd4[0] = v[0]; simd4[1] = v[1]; return *this; } inline simd4_t& operator+=(double d) { __m128d d4 = _mm_set1_pd(d); simd4[0] = _mm_add_pd(simd4[0], d4); simd4[1] = _mm_add_pd(simd4[1], d4); return *this; } inline simd4_t& operator+=(const simd4_t& v) { simd4[0] = _mm_add_pd(simd4[0], v.v4()[0]); simd4[1] = _mm_add_pd(simd4[1], v.v4()[1]); return *this; } inline simd4_t& operator-=(double d) { __m128d d4 = _mm_set1_pd(d); simd4[0] = _mm_sub_pd(simd4[0], d4); simd4[1] = _mm_sub_pd(simd4[1], d4); } inline simd4_t& operator-=(const simd4_t& v) { simd4[0] = _mm_sub_pd(simd4[0], v.v4()[0]); simd4[1] = _mm_sub_pd(simd4[1], v.v4()[1]); return *this; } inline simd4_t& operator*=(double d) { __m128d d4 = _mm_set1_pd(d); simd4[0] = _mm_mul_pd(simd4[0], d4); simd4[1] = _mm_mul_pd(simd4[1], d4); return *this; } inline simd4_t& operator*=(const simd4_t& v) { simd4[0] = _mm_mul_pd(simd4[0], v.v4()[0]); simd4[1] = _mm_mul_pd(simd4[1], v.v4()[1]); return *this; } inline simd4_t& operator/=(double d) { __m128d d4 = _mm_set1_pd(d); simd4[0] = _mm_div_pd(simd4[0], d4); simd4[1] = _mm_div_pd(simd4[1], d4); } inline simd4_t& operator/=(const simd4_t& v) { simd4[0] = _mm_div_pd(simd4[0], v.v4()[0]); simd4[1] = _mm_div_pd(simd4[1], v.v4()[1]); return *this; } }; static const __m128d dmask3 = _mm_load_pd((const double*)(simd4::m3a64+2)); template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4[0] = _mm_set_pd(y,x); simd4[1] = _mm_set_pd(w,z); } template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4[0] = _mm_set_pd(y,x); simd4[1] = _mm_and_pd(_mm_set_pd(w,z), dmask3); } template<> inline simd4_t::simd4_t(double x, double y, double z, double w) { simd4[0] = _mm_set_pd(y,x); simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1]; } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1]; } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1]; } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = _mm_and_pd(v.v4()[1], dmask3); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = v.v4()[1]; } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4[0] = v.v4()[0]; simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4[0] = _mm_loadu_pd(v); simd4[1] = _mm_loadu_pd(v+2); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4[0] = _mm_loadu_pd(v); simd4[1] = _mm_and_pd(_mm_loadu_pd(v+2), dmask3); } template<> inline simd4_t::simd4_t(const __vec4d_t v) { simd4[0] = _mm_loadu_pd(v); simd4[1] = _mm_setzero_pd(); } template<> inline simd4_t::simd4_t(double d) { simd4[0] = simd4[1] = _mm_set1_pd(d); } template<> inline simd4_t::simd4_t(double d) { simd4[0] = _mm_set1_pd(d); simd4[1] = _mm_and_pd(simd4[0], dmask3); } template<> inline simd4_t::simd4_t(double d) { simd4[0] = _mm_set1_pd(d); simd4[1] = _mm_setzero_pd(); } namespace simd4 { // http://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86 inline static double hsum_pd_sse(const __m128d vd[2]) { __m128 undef = _mm_setzero_ps(); __m128 shuftmp1 = _mm_movehl_ps(undef, _mm_castpd_ps(vd[0])); __m128 shuftmp2 = _mm_movehl_ps(undef, _mm_castpd_ps(vd[1])); __m128d shuf1 = _mm_castps_pd(shuftmp1); __m128d shuf2 = _mm_castps_pd(shuftmp2); return _mm_cvtsd_f64(_mm_add_sd(vd[0], shuf1)) + _mm_cvtsd_f64(_mm_add_sd(vd[1], shuf2)); } template<> inline double magnitude2(const simd4_t& v) { __m128d v2[2]; v2[0] = _mm_mul_pd(v.v4()[0],v.v4()[0]); v2[1] = _mm_mul_pd(v.v4()[1],v.v4()[1]); return hsum_pd_sse(v2); } template<> inline double dot(const simd4_t& v1, const simd4_t& v2) { __m128d mv[2]; mv[0] = _mm_mul_pd(v1.v4()[0],v2.v4()[0]); mv[1] = _mm_mul_pd(v1.v4()[1],v2.v4()[1]); return hsum_pd_sse(mv); } template<> inline simd4_t cross(const simd4_t& v1, const simd4_t& v2) { __m128d a[2], b[2], c[2]; simd4_t r; a[0] = _mm_shuffle_pd(v1.v4()[0], v1.v4()[1], _MM_SHUFFLE2(0, 1)); a[1] = _mm_shuffle_pd(v1.v4()[0], v1.v4()[1], _MM_SHUFFLE2(1, 0)); b[0] = _mm_shuffle_pd(v2.v4()[0], v2.v4()[1], _MM_SHUFFLE2(0, 1)); b[1] = _mm_shuffle_pd(v2.v4()[0], v2.v4()[1], _MM_SHUFFLE2(1, 0)); c[0] = _mm_sub_pd(_mm_mul_pd(v1.v4()[0], b[0]), _mm_mul_pd(a[0], v2.v4()[0])); c[1] = _mm_sub_pd(_mm_mul_pd(v1.v4()[1], b[1]), _mm_mul_pd(a[1], v2.v4()[1])); r.v4()[0] = _mm_shuffle_pd(c[0], c[1], _MM_SHUFFLE2(0, 1)); r.v4()[1] = _mm_shuffle_pd(c[0], c[1], _MM_SHUFFLE2(1, 0)); return r; } template inline simd4_t min(simd4_t v1, const simd4_t& v2) { v1.v4()[0] = _mm_min_pd(v1.v4()[0], v2.v4()[0]); v1.v4()[1] = _mm_min_pd(v1.v4()[1], v2.v4()[1]); return v1; } template inline simd4_t max(simd4_t v1, const simd4_t& v2) { v1.v4()[0] = _mm_max_pd(v1.v4()[0], v2.v4()[0]); v1.v4()[1] = _mm_max_pd(v1.v4()[1], v2.v4()[1]); return v1; } template inline simd4_tabs(simd4_t v) { static const __m128d sign_mask = _mm_set1_pd(-0.); // -0. = 1 << 63 v.v4()[0] = _mm_andnot_pd(sign_mask, v.v4()[0]); v.v4()[1] = _mm_andnot_pd(sign_mask, v.v4()[1]); return v; } } /* namespace simd4 */ # endif # ifdef __SSE2__ template class alignas(16) simd4_t { private: typedef int __vec4i_t[N]; union { __m128i simd4; __vec4i_t vec; }; public: simd4_t(void) : simd4(_mm_setzero_si128()) {} simd4_t(int i) {} simd4_t(int x, int y) : simd4_t(x,y,0,0) {} simd4_t(int x, int y, int z) : simd4_t(x,y,z,0) {} simd4_t(int x, int y, int z, int w) {} simd4_t(const __vec4i_t v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const simd4_t& v) {} simd4_t(const __m128i& v) { simd4 = v; } inline __m128i (&v4(void)) { return simd4; } inline const __m128i (&v4(void) const) { return simd4; } inline const int (&ptr(void) const)[N] { return vec; } inline int (&ptr(void))[N] { return vec; } inline const int& operator[](unsigned n) const { assert(n inline simd4_t& operator=(const simd4_t& v) { simd4 = simd4_t(v).v4(); return *this; } inline simd4_t& operator=(const __m128& v) { simd4 = v; return *this; } inline simd4_t& operator+=(int i) { simd4 = _mm_add_epi32(simd4, _mm_set1_epi32(i)); return *this; } inline simd4_t& operator+=(const simd4_t& v) { simd4 = _mm_add_epi32(simd4, v.v4()); return *this; } inline simd4_t& operator-=(int i) { simd4 = _mm_sub_epi32(simd4, _mm_set1_epi32(i)); return *this; } inline simd4_t& operator-=(const simd4_t& v) { simd4 = _mm_sub_epi32(simd4, v.v4()); return *this; } inline simd4_t& operator*=(int i) { return operator*=(_mm_set1_epi32(i)); } inline simd4_t& operator*=(const simd4_t& v) { return operator*=(v.v4()); } // https://software.intel.com/en-us/forums/intel-c-compiler/topic/288768 inline simd4_t& operator*=(const __m128i& v) { #ifdef __SSE4_1__ simd4 = _mm_mullo_epi32(simd4, v); #else __m128i tmp1 = _mm_mul_epu32(simd4, v); __m128i tmp2 = _mm_mul_epu32(_mm_srli_si128(simd4,4), _mm_srli_si128(v,4)); simd4 =_mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1,_MM_SHUFFLE (0,0,2,0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE (0,0,2,0))); #endif return *this; } inline simd4_t& operator/=(int s) { return operator/=(simd4_t(s)); } inline simd4_t& operator/=(const simd4_t& v) { for (int i=0; i inline simd4_t::simd4_t(int x, int y, int z, int w) { simd4 = _mm_set_epi32(w,z,y,x); } template<> inline simd4_t::simd4_t(int x, int y, int z, int w) { simd4 = _mm_and_si128(_mm_set_epi32(w,z,y,x), imask3); } template<> inline simd4_t::simd4_t(int x, int y, int z, int w) { simd4 = _mm_and_si128(_mm_set_epi32(w,z,y,x), imask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_si128(v.v4(), imask3); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_si128(v.v4(), imask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = _mm_and_si128(v.v4(), imask2); } template<> inline simd4_t::simd4_t(const simd4_t& v) { simd4 = v.v4(); } template<> inline simd4_t::simd4_t(const __vec4i_t v) { simd4 = _mm_loadu_si128((__m128i*)v); } template<> inline simd4_t::simd4_t(const __vec4i_t v) { simd4 = _mm_and_si128(_mm_loadu_si128((__m128i*)v), imask3); } template<> inline simd4_t::simd4_t(const __vec4i_t v) { simd4 = _mm_and_si128(_mm_loadu_si128((__m128i*)v), imask2); } template<> inline simd4_t::simd4_t(int i) { simd4 = _mm_set1_epi32(i); } template<> inline simd4_t::simd4_t(int i) { simd4 = _mm_and_si128(_mm_set1_epi32(i), imask3); } template<> inline simd4_t::simd4_t(int i) { simd4 = _mm_and_si128(_mm_set1_epi32(i), imask2); } namespace simd4 { # ifdef __SSE4_1__ template inline simd4_t min(simd4_t v1, const simd4_t& v2) { v1 = _mm_min_epi32(v1.v4(), v2.v4()); return v1; } template inline simd4_t max(simd4_t v1, const simd4_t& v2) { v1 = _mm_max_epi32(v1.v4(), v2.v4()); return v1; } # endif /* __SSE4_1__ */ } /* namespace simd4 */ # endif #endif /* __SIMD_H__ */