1087 lines
27 KiB
C++
1087 lines
27 KiB
C++
// 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 <cstdint>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <new>
|
|
|
|
#include <simgear/math/SGLimits.hxx>
|
|
#include <simgear/math/SGMisc.hxx>
|
|
|
|
template<typename T, int N> class simd4_t;
|
|
|
|
namespace simd4
|
|
{
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> min(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
|
|
for (int i=0; i<N; ++i) {
|
|
v1[i] = SGMisc<T>::min(v1[i], v2[i]);
|
|
}
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> max(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
|
|
for (int i=0; i<N; ++i) {
|
|
v1[i] = SGMisc<T>::max(v1[i], v2[i]);
|
|
}
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> abs(simd4_t<T,N> v) {
|
|
for (int i=0; i<N; ++i) {
|
|
v[i] = std::abs(v[i]);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline T magnitude2(simd4_t<T,N> v) {
|
|
T mag2 = 0;
|
|
v = v*v;
|
|
for (int i=0; i<N; ++i) {
|
|
mag2 += v[i];
|
|
}
|
|
return mag2;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline T magnitude(const simd4_t<T,N>& v) {
|
|
return std::sqrt(magnitude2(v));
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline T normalize(simd4_t<T,N>& v) {
|
|
T mag = simd4::magnitude(v);
|
|
if (mag) {
|
|
v /= mag;
|
|
} else {
|
|
v = T(0);
|
|
}
|
|
return mag;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline T dot(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
|
|
T dp = 0;
|
|
v1 *= v2;
|
|
for (int i=0; i<N; ++i) {
|
|
dp += v1[i];
|
|
}
|
|
return dp;
|
|
}
|
|
|
|
template<typename T>
|
|
inline simd4_t<T,3> cross(const simd4_t<T,3>& v1, const simd4_t<T,3>& v2)
|
|
{
|
|
simd4_t<T,3> 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];
|
|
d[3] = T(0);
|
|
return d;
|
|
}
|
|
|
|
} /* namespace simd4 */
|
|
|
|
|
|
template<typename T, int N>
|
|
class simd4_t
|
|
{
|
|
private:
|
|
union {
|
|
T _v4[4];
|
|
T vec[N];
|
|
};
|
|
|
|
public:
|
|
simd4_t(void) {}
|
|
simd4_t(T s) {
|
|
for (int i=0; i<N; ++i) vec[i] = s;
|
|
for (int i=N; i<4; ++i) _v4[i] = 0;
|
|
}
|
|
simd4_t(T x, T y) : simd4_t(x,y,0,0) {}
|
|
simd4_t(T x, T y, T z) : simd4_t(x,y,z,0) {}
|
|
simd4_t(T x, T y, T z, T w) {
|
|
_v4[0] = x; _v4[1] = y; _v4[2] = z; _v4[3] = w;
|
|
}
|
|
explicit simd4_t(const T v[N]) {
|
|
std::memcpy(vec, v, sizeof(T[N]));
|
|
for (int i=N; i<4; ++i) _v4[i] = 0;
|
|
}
|
|
template<int M>
|
|
simd4_t(const simd4_t<T,M>& v) {
|
|
std::memcpy(_v4, v.ptr(), sizeof(T[M]));
|
|
for (int i=M; i<4; ++i) _v4[i] = 0;
|
|
}
|
|
~simd4_t(void) {}
|
|
|
|
inline T (&v4(void))[N] {
|
|
return vec;
|
|
}
|
|
|
|
inline const T (&v4(void) const)[N] {
|
|
return vec;
|
|
}
|
|
|
|
inline const T (&ptr(void) const)[N] {
|
|
return vec;
|
|
}
|
|
|
|
inline T (&ptr(void))[N] {
|
|
return vec;
|
|
}
|
|
|
|
inline operator const T*(void) const {
|
|
return vec;
|
|
}
|
|
|
|
inline operator T*(void) {
|
|
return vec;
|
|
}
|
|
|
|
inline simd4_t<T,N>& operator=(T s) {
|
|
for (int i=0; i<N; ++i) vec[i] = s;
|
|
for (int i=N; i<4; ++i) _v4[i] = 0;
|
|
return *this;
|
|
}
|
|
inline simd4_t<T,N>& operator=(const T v[N]) {
|
|
std::memcpy(vec, v, sizeof(T[N]));
|
|
for (int i=N; i<4; ++i) _v4[i] = 0;
|
|
return *this;
|
|
}
|
|
template<int M>
|
|
inline simd4_t<T,N>& operator=(const simd4_t<T,M>& v) {
|
|
std::memcpy(_v4, v.ptr(), sizeof(T[M]));
|
|
for (int i=M; i<4; ++i) _v4[i] = 0;
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<T,N>& operator+=(T s) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] += s;
|
|
}
|
|
return *this;
|
|
}
|
|
inline simd4_t<T,N>& operator+=(const T v[N]) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] += v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
template<int M>
|
|
inline simd4_t<T,N>& operator+=(const simd4_t<T,M>& v) {
|
|
for (int i=0; i<M; ++i) {
|
|
_v4[i] += v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<T,N>& operator-=(T s) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] -= s;
|
|
}
|
|
return *this;
|
|
}
|
|
inline simd4_t<T,N>& operator-=(const T v[N]) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] -= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
template<int M>
|
|
inline simd4_t<T,N>& operator-=(const simd4_t<T,M>& v) {
|
|
for (int i=0; i<N; ++i) {
|
|
_v4[i] -= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
inline simd4_t<T,N>& operator*=(T s) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] *= s;
|
|
}
|
|
return *this;
|
|
}
|
|
inline simd4_t<T,N>& operator*=(const T v[N]) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] *= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
template<int M>
|
|
inline simd4_t<T,N>& operator*=(const simd4_t<T,M>& v) {
|
|
for (int i=0; i<N; ++i) {
|
|
_v4[i] *= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<T,N>& operator/=(T s) {
|
|
return operator*=(1/s);
|
|
}
|
|
inline simd4_t<T,N>& operator/=(const T v[N]) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] /= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
template<int M>
|
|
inline simd4_t<T,N>& operator/=(const simd4_t<T,M>& v) {
|
|
for (int i=0; i<N; ++i) {
|
|
_v4[i] /= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> operator-(const simd4_t<T,N>& v) {
|
|
simd4_t<T,N> r = T(0);
|
|
r -= v;
|
|
return r;
|
|
}
|
|
|
|
template<typename T, int N, int M>
|
|
inline simd4_t<T,N> operator+(simd4_t<T,N> v1, const simd4_t<T,M>& v2) {
|
|
v1 += v2;
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N, int M>
|
|
inline simd4_t<T,N> operator-(simd4_t<T,N> v1, const simd4_t<T,M>& v2) {
|
|
v1 -= v2;
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N, int M>
|
|
inline simd4_t<T,N> operator*(simd4_t<T,N> v1, const simd4_t<T,M>& v2) {
|
|
v1 *= v2;
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N, int M>
|
|
inline simd4_t<T,N> operator/(simd4_t<T,N> v1, const simd4_t<T,M>& v2) {
|
|
v1 /= v2;
|
|
return v1;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> operator*(T f, simd4_t<T,N> v) {
|
|
v *= f;
|
|
return v;
|
|
}
|
|
|
|
template<typename T, int N>
|
|
inline simd4_t<T,N> operator*(simd4_t<T,N> v, T f) {
|
|
v *= f;
|
|
return v;
|
|
}
|
|
|
|
|
|
# ifdef __MMX__
|
|
# include <mmintrin.h>
|
|
# if defined(_MSC_VER)
|
|
# define ALIGN16 __declspec(align(16))
|
|
# define ALIGN32 __declspec(align(32))
|
|
# define ALIGN16C
|
|
# define ALIGN32C
|
|
# elif defined(__GNUC__)
|
|
# define ALIGN16
|
|
# define ALIGN32
|
|
# define ALIGN16C __attribute__((aligned(16)))
|
|
# define ALIGN32C __attribute__((aligned(32)))
|
|
# endif
|
|
|
|
static const uint32_t m2a32[] = { 0xffffffff,0xffffffff,0,0 };
|
|
static const uint32_t m3a32[] = { 0xffffffff,0xffffffff,0xffffffff,0 };
|
|
static const uint64_t m2a64[] = { 0xffffffffffffffff,0xffffffffffffffff,0,0 };
|
|
static const uint64_t m3a64[] = { 0xffffffffffffffff,0xffffffffffffffff,0xffffffffffffffff,0 };
|
|
# endif
|
|
|
|
# ifdef __SSE__
|
|
# include <xmmintrin.h>
|
|
# ifdef __SSE3__
|
|
# include <pmmintrin.h>
|
|
# endif
|
|
|
|
ALIGN16
|
|
class simd_aligned16
|
|
{
|
|
public:
|
|
simd_aligned16() {}
|
|
~simd_aligned16() {}
|
|
|
|
static void *operator new (size_t size) throw (std::bad_alloc) {
|
|
void *p = _mm_malloc(size, 16);
|
|
if (!p) throw std::bad_alloc();
|
|
return p;
|
|
}
|
|
static void operator delete (void *p) {
|
|
_mm_free(p);
|
|
}
|
|
} ALIGN16C;
|
|
|
|
template<int N>
|
|
class simd4_t<float,N> : public simd_aligned16
|
|
{
|
|
private:
|
|
typedef float __vec4f_t[N];
|
|
|
|
union ALIGN16 {
|
|
__m128 simd4;
|
|
__vec4f_t vec;
|
|
} ALIGN16C;
|
|
|
|
public:
|
|
simd4_t(void) {}
|
|
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 = _mm_set_ps(w,z,y,x);
|
|
}
|
|
simd4_t(const __vec4f_t v) {}
|
|
template<int M>
|
|
simd4_t(const simd4_t<float,M>& v) {
|
|
simd4 = v.v4();
|
|
}
|
|
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 operator const float*(void) const {
|
|
return vec;
|
|
}
|
|
inline operator float*(void) {
|
|
return vec;
|
|
}
|
|
|
|
inline simd4_t<float,N>& operator+=(float f) {
|
|
return operator+=(simd4_t<float,N>(f));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<float,N>& operator+=(const simd4_t<float,M>& v) {
|
|
simd4 = _mm_add_ps(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<float,N>& operator-=(float f) {
|
|
return operator-=(simd4_t<float,N>(f));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<float,N>& operator-=(const simd4_t<float,M>& v) {
|
|
simd4 = _mm_sub_ps(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<float,N>& operator*=(float f) {
|
|
return operator*=(simd4_t<float,N>(f));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<float,N>& operator*=(const simd4_t<float,M>& v) {
|
|
simd4 = _mm_mul_ps(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<float,N>& operator/=(float f) {
|
|
return operator/=(simd4_t<float,4>(f));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<float,N>& operator/=(const simd4_t<float,M>& v) {
|
|
simd4 = _mm_div_ps(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
static const __m128 fmask2 = _mm_load_ps((const float*)m2a32);
|
|
static const __m128 fmask3 = _mm_load_ps((const float*)m3a32);
|
|
|
|
template<>
|
|
inline simd4_t<float,4>::simd4_t(const __vec4f_t v) {
|
|
simd4 = _mm_loadu_ps(v);
|
|
}
|
|
template<>
|
|
inline simd4_t<float,3>::simd4_t(const __vec4f_t v) {
|
|
simd4 = _mm_and_ps(_mm_loadu_ps(v), fmask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<float,2>::simd4_t(const __vec4f_t v) {
|
|
simd4 = _mm_and_ps(_mm_loadu_ps(v), fmask2);
|
|
}
|
|
template<>
|
|
inline simd4_t<float,4>::simd4_t(float f) {
|
|
simd4 = _mm_set1_ps(f);
|
|
}
|
|
template<>
|
|
inline simd4_t<float,3>::simd4_t(float f) {
|
|
simd4 = _mm_and_ps(_mm_set1_ps(f), fmask3);
|
|
}
|
|
|
|
template<>
|
|
inline simd4_t<float,2>::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(simd4_t<float,4> v) {
|
|
return hsum_ps_sse(v.v4()*v.v4());
|
|
}
|
|
|
|
template<>
|
|
inline float dot(simd4_t<float,4> v1, const simd4_t<float,4>& v2) {
|
|
return hsum_ps_sse(v1.v4()*v2.v4());
|
|
}
|
|
|
|
template<>
|
|
inline simd4_t<float,3> cross(const simd4_t<float,3>& v1, const simd4_t<float,3>& 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<int N>
|
|
inline simd4_t<float,N> min(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
|
|
v1 = _mm_min_ps(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
|
|
template<int N>
|
|
inline simd4_t<float,N> max(simd4_t<float,N> v1, const simd4_t<float,N>& v2) {
|
|
v1 = _mm_max_ps(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
|
|
template<int N>
|
|
inline simd4_t<float,N>abs(simd4_t<float,N> 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__
|
|
# include <immintrin.h>
|
|
|
|
ALIGN32
|
|
class simd_aligned32
|
|
{
|
|
public:
|
|
simd_aligned32() {}
|
|
~simd_aligned32() {}
|
|
|
|
static void *operator new (size_t size) throw (std::bad_alloc) {
|
|
void *p = _mm_malloc(size, 32);
|
|
if (!p) throw std::bad_alloc();
|
|
return p;
|
|
}
|
|
static void operator delete (void *p) {
|
|
_mm_free(p);
|
|
}
|
|
} ALIGN32C;
|
|
|
|
template<int N>
|
|
class simd4_t<double,N> : public simd_aligned32
|
|
{
|
|
private:
|
|
typedef double __vec4d_t[N];
|
|
|
|
union ALIGN32 {
|
|
__m256d simd4;
|
|
__vec4d_t vec;
|
|
} ALIGN32C;
|
|
|
|
public:
|
|
simd4_t(void) {}
|
|
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 = _mm256_set_pd(w,z,y,x);
|
|
}
|
|
simd4_t(const __vec4d_t v) {}
|
|
template<int M>
|
|
simd4_t(const simd4_t<double,M>& v) {
|
|
simd4 = v.v4();
|
|
}
|
|
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 operator const double*(void) const {
|
|
return vec;
|
|
}
|
|
inline operator double*(void) {
|
|
return vec;
|
|
}
|
|
|
|
inline simd4_t<double,N>& operator+=(double d) {
|
|
return operator+=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator+=(const simd4_t<double,M>& v) {
|
|
simd4 = _mm256_add_pd(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<double,N>& operator-=(double d) {
|
|
return operator-=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator-=(const simd4_t<double,M>& v) {
|
|
simd4 = _mm256_sub_pd(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<double,N>& operator*=(double d) {
|
|
return operator*=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator*=(const simd4_t<double,M>& v) {
|
|
simd4 = _mm256_mul_pd(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<double,N>& operator/=(double d) {
|
|
return operator/=(simd4_t<double,4>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator/=(const simd4_t<double,M>& v) {
|
|
simd4 = _mm256_div_pd(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
static const __m256d dmask2 = _mm256_load_pd((const double*)m2a64);
|
|
static const __m256d dmask3 = _mm256_load_pd((const double*)m3a64);
|
|
|
|
template<>
|
|
inline simd4_t<double,4>::simd4_t(const __vec4d_t v) {
|
|
simd4 = _mm256_loadu_pd(v);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,3>::simd4_t(const __vec4d_t v) {
|
|
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,2>::simd4_t(const __vec4d_t v) {
|
|
simd4 = _mm256_and_pd(_mm256_loadu_pd(v), dmask2);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,4>::simd4_t(double d) {
|
|
simd4 = _mm256_set1_pd(d);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,3>::simd4_t(double d) {
|
|
simd4 = _mm256_and_pd(_mm256_set1_pd(d), dmask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,2>::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(simd4_t<double,4> v) {
|
|
return hsum_pd_avx(_mm256_mul_pd(v.v4(),v.v4()));
|
|
}
|
|
|
|
template<>
|
|
inline double dot(simd4_t<double,4> v1, const simd4_t<double,4>& v2) {
|
|
return hsum_pd_avx(_mm256_mul_pd(v1.v4(),v2.v4()));
|
|
}
|
|
|
|
# ifdef __AVX2__
|
|
# include <pmmintrin.h>
|
|
template<>
|
|
inline simd4_t<double,3> cross(const simd4_t<double,3>& v1, const simd4_t<double,3>& 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<int N>
|
|
inline simd4_t<double,N> min(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
|
|
v1 = _mm256_min_pd(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
|
|
template<int N>
|
|
inline simd4_t<double,N> max(simd4_t<double,N> v1, const simd4_t<double,N>& v2) {
|
|
v1 = _mm256_max_pd(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
|
|
template<int N>
|
|
inline simd4_t<double,N>abs(simd4_t<double,N> 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__
|
|
# include <emmintrin.h>
|
|
|
|
template<int N>
|
|
class simd4_t<double,N> : public simd_aligned16
|
|
{
|
|
private:
|
|
typedef double __vec4d_t[N];
|
|
|
|
union ALIGN16 {
|
|
__m128d simd4[2];
|
|
__vec4d_t vec;
|
|
} ALIGN16C;
|
|
|
|
public:
|
|
simd4_t(void) {}
|
|
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[0] = _mm_set_pd(y,x);
|
|
simd4[1] = _mm_set_pd(w,z);
|
|
}
|
|
simd4_t(const __vec4d_t v) {}
|
|
template<int M>
|
|
simd4_t(const simd4_t<double,M>& v) {
|
|
simd4[0] = v.v4()[0];
|
|
simd4[1] = v.v4()[1];
|
|
}
|
|
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 operator const double*(void) const {
|
|
return vec;
|
|
}
|
|
inline operator double*(void) {
|
|
return vec;
|
|
}
|
|
|
|
inline simd4_t<double,N>& operator+=(double d) {
|
|
return operator+=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator+=(const simd4_t<double,M>& 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<double,N>& operator-=(double d) {
|
|
return operator-=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator-=(const simd4_t<double,M>& 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<double,N>& operator*=(double d) {
|
|
return operator*=(simd4_t<double,N>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator*=(const simd4_t<double,M>& 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<double,N>& operator/=(double d) {
|
|
return operator/=(simd4_t<double,4>(d));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<double,N>& operator/=(const simd4_t<double,M>& 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*)(m3a64+2));
|
|
|
|
template<>
|
|
inline simd4_t<double,4>::simd4_t(const __vec4d_t v) {
|
|
simd4[0] = _mm_loadu_pd(v);
|
|
simd4[1] = _mm_loadu_pd(v+2);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,3>::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<double,2>::simd4_t(const __vec4d_t v) {
|
|
simd4[0] = _mm_loadu_pd(v);
|
|
simd4[1] = _mm_setzero_pd();
|
|
}
|
|
template<>
|
|
inline simd4_t<double,4>::simd4_t(double d) {
|
|
simd4[0] = simd4[1] = _mm_set1_pd(d);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,3>::simd4_t(double d) {
|
|
simd4[0] = _mm_set1_pd(d);
|
|
simd4[1] = _mm_and_pd(simd4[0], dmask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<double,2>::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(simd4_t<double,4> v) {
|
|
v *= v;
|
|
return hsum_pd_sse(v.v4());
|
|
}
|
|
|
|
template<>
|
|
inline double dot(simd4_t<double,4> v1, const simd4_t<double,4>& v2) {
|
|
v1 *= v2;
|
|
return hsum_pd_sse(v1.v4());
|
|
}
|
|
|
|
#if 1
|
|
template<>
|
|
inline simd4_t<double,3> cross(const simd4_t<double,3>& v1, const simd4_t<double,3>& v2)
|
|
{
|
|
__m128d a[2], b[2], c[2];
|
|
simd4_t<double,3> 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;
|
|
}
|
|
#endif
|
|
|
|
template<int N>
|
|
inline simd4_t<double,N> min(simd4_t<double,N> v1, const simd4_t<double,N>& 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<int N>
|
|
inline simd4_t<double,N> max(simd4_t<double,N> v1, const simd4_t<double,N>& 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<int N>
|
|
inline simd4_t<double,N>abs(simd4_t<double,N> 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__
|
|
# include <emmintrin.h>
|
|
# ifdef __SSE4_1__
|
|
# include <smmintrin.h>
|
|
# endif
|
|
|
|
template<int N>
|
|
class simd4_t<int,N> : public simd_aligned16
|
|
{
|
|
private:
|
|
typedef int __vec4i_t[N];
|
|
|
|
union ALIGN16 {
|
|
__m128i simd4;
|
|
__vec4i_t vec;
|
|
} ALIGN16C;
|
|
|
|
public:
|
|
simd4_t(void) {}
|
|
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 = _mm_set_epi32(w,z,y,x);
|
|
}
|
|
simd4_t(const __vec4i_t v) {}
|
|
template<int M>
|
|
simd4_t(const simd4_t<int,M>& v) {
|
|
simd4 = v.v4();
|
|
}
|
|
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 operator const int*(void) const {
|
|
return vec;
|
|
}
|
|
|
|
inline operator int*(void) {
|
|
return vec;
|
|
}
|
|
|
|
inline simd4_t<int,N>& operator+=(int i) {
|
|
return operator+=(simd4_t<int,N>(i));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<int,N>& operator+=(const simd4_t<int,M>& v) {
|
|
simd4 = _mm_add_epi32(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<int,N>& operator-=(int i) {
|
|
return operator-=(simd4_t<int,N>(i));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<int,N>& operator-=(const simd4_t<int,M>& v) {
|
|
simd4 = _mm_sub_epi32(simd4, v.v4());
|
|
return *this;
|
|
}
|
|
|
|
inline simd4_t<int,N>& operator*=(int i) {
|
|
return operator*=(simd4_t<int,N>(i));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<int,N>& operator*=(const simd4_t<int,M>& v) {
|
|
return operator*=(v.v4());
|
|
}
|
|
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/288768
|
|
inline simd4_t<int,N>& 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<int,N>& operator/=(int s) {
|
|
return operator/=(simd4_t<int,4>(s));
|
|
}
|
|
template<int M>
|
|
inline simd4_t<int,N>& operator/=(const simd4_t<int,M>& v) {
|
|
for (int i=0; i<N; ++i) {
|
|
vec[i] /= v[i];
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
static const __m128i imask2 = _mm_loadu_si128((__m128i*)m2a32);
|
|
static const __m128i imask3 = _mm_loadu_si128((__m128i*)m3a32);
|
|
|
|
template<>
|
|
inline simd4_t<int,4>::simd4_t(const __vec4i_t v) {
|
|
simd4 = _mm_loadu_si128((__m128i*)v);
|
|
}
|
|
template<>
|
|
inline simd4_t<int,3>::simd4_t(const __vec4i_t v) {
|
|
simd4 = _mm_and_si128(_mm_loadu_si128((__m128i*)v), imask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<int,2>::simd4_t(const __vec4i_t v) {
|
|
simd4 = _mm_and_si128(_mm_loadu_si128((__m128i*)v), imask2);
|
|
}
|
|
template<>
|
|
inline simd4_t<int,4>::simd4_t(int i) {
|
|
simd4 = _mm_set1_epi32(i);
|
|
}
|
|
template<>
|
|
inline simd4_t<int,3>::simd4_t(int i) {
|
|
simd4 = _mm_and_si128(_mm_set1_epi32(i), imask3);
|
|
}
|
|
template<>
|
|
inline simd4_t<int,2>::simd4_t(int i) {
|
|
simd4 = _mm_and_si128(_mm_set1_epi32(i), imask2);
|
|
}
|
|
|
|
|
|
namespace simd4
|
|
{
|
|
|
|
# ifdef __SSE4_1__
|
|
template<int N>
|
|
inline simd4_t<int,N> min(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
|
|
v1 = _mm_min_epi32(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
|
|
template<int N>
|
|
inline simd4_t<int,N> max(simd4_t<int,N> v1, const simd4_t<int,N>& v2) {
|
|
v1 = _mm_max_epi32(v1.v4(), v2.v4());
|
|
return v1;
|
|
}
|
|
# endif /* __SSE4_1__ */
|
|
|
|
} /* namespace simd4 */
|
|
|
|
# endif
|
|
|
|
# ifdef __ARM_NEON__
|
|
# include <simgear/math/simd_neon.hxx>
|
|
# endif
|
|
|
|
#endif /* __SIMD_H__ */
|
|
|