// -*-C++-*- #ifndef VEC_VSX_DOUBLE2_H #define VEC_VSX_DOUBLE2_H #include "floatprops.h" #include "mathfuncs.h" #include "vec_base.h" #include // VSX intrinsics #include #if defined __clang__ #define __vector vector #define __pixel pixel #define __bool bool #elif defined __gcc__ #undef vector #undef pixel #undef bool #elif defined __xlC__ #define __bool bool #else #error "Unknown compiler" #endif namespace vecmathlib { #define VECMATHLIB_HAVE_VEC_DOUBLE_2 template <> struct boolvec; template <> struct intvec; template <> struct realvec; template <> struct boolvec : floatprops { static int const size = 2; typedef bool scalar_t; typedef __vector __bool long long bvector_t; static int const alignment = sizeof(bvector_t); static_assert(size * sizeof(real_t) == sizeof(bvector_t), "vector size is wrong"); private: // true values are -1, false values are 0 // truth values are interpreted bit-wise static uint_t from_bool(bool a) { return -int_t(a); } static bool to_bool(uint_t a) { return a; } public: typedef boolvec boolvec_t; typedef intvec intvec_t; typedef realvec realvec_t; // Short names for type casts typedef real_t R; typedef int_t I; typedef uint_t U; typedef realvec_t RV; typedef intvec_t IV; typedef boolvec_t BV; typedef floatprops FP; typedef mathfuncs MF; bvector_t v; boolvec() {} // Can't have a non-trivial copy constructor; if so, objects won't // be passed in registers // boolvec(boolvec const& x): v(x.v) {} // boolvec& operator=(boolvec const& x) { return v=x.v, *this; } boolvec(bvector_t x) : v(x) {} boolvec(bool a) : v((bvector_t)vec_splats((unsigned long long)from_bool(a))) {} boolvec(bool const *as) { for (int d = 0; d < size; ++d) set_elt(d, as[d]); } operator bvector_t() const { return v; } bool operator[](int n) const { return to_bool(vecmathlib::get_elt(v, n)); } boolvec &set_elt(int n, bool a) { return vecmathlib::set_elt(v, n, from_bool(a)), *this; } intvec_t as_int() const; // defined after intvec intvec_t convert_int() const; // defined after intvec boolvec operator!() const { return vec_nor(v, v); } boolvec operator&&(boolvec x) const { return vec_and(v, x.v); } boolvec operator||(boolvec x) const { return vec_or(v, x.v); } boolvec operator==(boolvec x) const { return !(*this != x); } boolvec operator!=(boolvec x) const { return vec_xor(v, x.v); } bool all() const { return vec_all_ne(v, BV(false)); } bool any() const { return vec_any_ne(v, BV(false)); } // ifthen(condition, then-value, else-value) boolvec_t ifthen(boolvec_t x, boolvec_t y) const; intvec_t ifthen(intvec_t x, intvec_t y) const; // defined after intvec realvec_t ifthen(realvec_t x, realvec_t y) const; // defined after realvec }; template <> struct intvec : floatprops { static int const size = 2; typedef int_t scalar_t; typedef __vector signed long long ivector_t; static int const alignment = sizeof(ivector_t); static_assert(size * sizeof(real_t) == sizeof(ivector_t), "vector size is wrong"); typedef boolvec boolvec_t; typedef intvec intvec_t; typedef realvec realvec_t; // Short names for type casts typedef real_t R; typedef int_t I; typedef uint_t U; typedef realvec_t RV; typedef intvec_t IV; typedef boolvec_t BV; typedef floatprops FP; typedef mathfuncs MF; ivector_t v; intvec() {} // Can't have a non-trivial copy constructor; if so, objects won't // be passed in registers // intvec(intvec const& x): v(x.v) {} // intvec& operator=(intvec const& x) { return v=x.v, *this; } intvec(ivector_t x) : v(x) {} intvec(int_t a) : v(vec_splats((long long)a)) {} intvec(int_t const *as) { for (int d = 0; d < size; ++d) set_elt(d, as[d]); } static intvec iota() { return (__vector signed long long){0, 1}; } operator ivector_t() const { return v; } int_t operator[](int n) const { return vecmathlib::get_elt(v, n); } intvec_t &set_elt(int n, int_t a) { return vecmathlib::set_elt(v, n, a), *this; } // Vector casts do not change the bit battern boolvec_t as_bool() const { return (__vector __bool long long)v; } boolvec_t convert_bool() const { return *this != IV(I(0)); } realvec_t as_float() const; // defined after realvec realvec_t convert_float() const; // defined after realvec // Permutation control words private: // 0123 4567 -> 1436 // exchange pairs static __vector unsigned char perm_int_swap() { return (__vector unsigned char){4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15, 24, 25, 26, 27}; } // 0123 4567 -> 0426 // broadcast high elements of pairs static __vector unsigned char perm_int_bchi() { return (__vector unsigned char){0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27}; } public: intvec operator+() const { return *this; } intvec operator-() const { return vec_neg(v); } intvec operator+(intvec x) const { return vec_add(v, x.v); } intvec operator-(intvec x) const { return vec_sub(v, x.v); } intvec operator*(intvec x) const { return vec_mul(v, x.v); } intvec operator/(intvec x) const { return vec_div(v, x.v); } intvec operator%(intvec x) const { return *this - *this / x * x; } intvec &operator+=(intvec const &x) { return *this = *this + x; } intvec &operator-=(intvec const &x) { return *this = *this - x; } intvec &operator*=(intvec const &x) { return *this = *this * x; } intvec &operator/=(intvec const &x) { return *this = *this / x; } intvec &operator%=(intvec const &x) { return *this = *this % x; } intvec operator~() const { return (__vector signed long long)vec_nor((__vector signed int)v, (__vector signed int)v); } intvec operator&(intvec x) const { return (__vector signed long long)vec_and((__vector signed int)v, (__vector signed int)x.v); } intvec operator|(intvec x) const { return (__vector signed long long)vec_or((__vector signed int)v, (__vector signed int)x.v); } intvec operator^(intvec x) const { return (__vector signed long long)vec_xor((__vector signed int)v, (__vector signed int)x.v); } intvec &operator&=(intvec const &x) { return *this = *this & x; } intvec &operator|=(intvec const &x) { return *this = *this | x; } intvec &operator^=(intvec const &x) { return *this = *this ^ x; } intvec_t bitifthen(intvec_t x, intvec_t y) const; intvec lsr(int_t n) const { return lsr(IV(n)); } intvec_t rotate(int_t n) const; intvec operator>>(int_t n) const { return *this >> IV(n); } intvec operator<<(int_t n) const { return *this << IV(n); } intvec &operator>>=(int_t n) { return *this = *this >> n; } intvec &operator<<=(int_t n) { return *this = *this << n; } intvec lsr(intvec n) const { // return vec_sr(v, (__vector unsigned long long)n.v); intvec r; for (int i = 0; i < size; ++i) { r.set_elt(i, U((*this)[i]) >> U(n[i])); } return r; } intvec_t rotate(intvec_t n) const; intvec operator>>(intvec n) const { // return vec_sra(v, (__vector unsigned long long)n.v); intvec r; for (int i = 0; i < size; ++i) { r.set_elt(i, (*this)[i] >> n[i]); } return r; } intvec operator<<(intvec n) const { // return vec_sl(v, (__vector unsigned long long)n.v); intvec r; for (int i = 0; i < size; ++i) { r.set_elt(i, (*this)[i] << n[i]); } return r; } intvec &operator>>=(intvec n) { return *this = *this >> n; } intvec &operator<<=(intvec n) { return *this = *this << n; } intvec_t clz() const; intvec_t popcount() const; boolvec_t operator==(intvec const &x) const { // return vec_cmpeq(v, x.v); __vector signed int a = (__vector signed int)v; __vector signed int b = (__vector signed int)x.v; __vector __bool int c = vec_cmpeq(a, b); __vector __bool int cx = vec_perm(c, c, perm_int_swap()); __vector __bool int r = vec_and(c, cx); return (__vector __bool long long)r; } boolvec_t operator!=(intvec const &x) const { return !(*this == x); } boolvec_t operator<(intvec const &x) const { __vector signed int a = (__vector signed int)v; __vector signed int b = (__vector signed int)x.v; __vector __bool int lt = vec_cmplt(a, b); __vector __bool int eq = vec_cmpeq(a, b); __vector unsigned int ua = (__vector unsigned int)v; __vector unsigned int ub = (__vector unsigned int)x.v; __vector __bool int ult = vec_cmplt(ua, ub); __vector __bool int ultx = vec_perm(ult, ult, perm_int_swap()); __vector __bool int r = vec_or(lt, vec_and(eq, ultx)); r = vec_perm(r, r, perm_int_bchi()); return (__vector __bool long long)r; } boolvec_t operator<=(intvec const &x) const { return !(*this > x); } boolvec_t operator>(intvec const &x) const { return x < *this; } boolvec_t operator>=(intvec const &x) const { return !(*this < x); } intvec_t abs() const; boolvec_t isignbit() const { return (*this >> (bits - 1)).as_bool(); } intvec_t max(intvec_t x) const; intvec_t min(intvec_t x) const; }; template <> struct realvec : floatprops { static int const size = 2; typedef real_t scalar_t; typedef __vector double vector_t; static int const alignment = sizeof(vector_t); static char const *name() { return ""; } void barrier() { __asm__("" : "+v"(v)); } static_assert(size * sizeof(real_t) == sizeof(vector_t), "vector size is wrong"); typedef boolvec boolvec_t; typedef intvec intvec_t; typedef realvec realvec_t; // Short names for type casts typedef real_t R; typedef int_t I; typedef uint_t U; typedef realvec_t RV; typedef intvec_t IV; typedef boolvec_t BV; typedef floatprops FP; typedef mathfuncs MF; vector_t v; realvec() {} // Can't have a non-trivial copy constructor; if so, objects won't // be passed in registers // realvec(realvec const& x): v(x.v) {} // realvec& operator=(realvec const& x) { return v=x.v, *this; } realvec(vector_t x) : v(x) {} realvec(real_t a) : v(vec_splats(a)) {} realvec(real_t const *as) { for (int d = 0; d < size; ++d) set_elt(d, as[d]); } operator vector_t() const { return v; } real_t operator[](int n) const { return vecmathlib::get_elt(v, n); } realvec_t &set_elt(int n, real_t a) { return vecmathlib::set_elt(v, n, a), *this; } typedef vecmathlib::mask_t mask_t; static realvec_t loada(real_t const *p) { VML_ASSERT(intptr_t(p) % alignment == 0); return vec_xld2(0, (real_t *)p); } static realvec_t loadu(real_t const *p) { // TODO: Can this handle unaligned access? return vec_xld2(0, (real_t *)p); } static realvec_t loadu(real_t const *p, std::ptrdiff_t ioff) { VML_ASSERT(intptr_t(p) % alignment == 0); if (ioff % realvec::size == 0) return loada(p + ioff); return loadu(p + ioff); } realvec_t loada(real_t const *p, mask_t const &m) const { VML_ASSERT(intptr_t(p) % alignment == 0); if (__builtin_expect(all(m.m), true)) { return loada(p); } else { return m.m.ifthen(loada(p), *this); } } realvec_t loadu(real_t const *p, mask_t const &m) const { if (__builtin_expect(m.all_m, true)) { return loadu(p); } else { return m.m.ifthen(loadu(p), *this); } } realvec_t loadu(real_t const *p, std::ptrdiff_t ioff, mask_t const &m) const { VML_ASSERT(intptr_t(p) % alignment == 0); if (ioff % realvec::size == 0) return loada(p + ioff, m); return loadu(p + ioff, m); } void storea(real_t *p) const { VML_ASSERT(intptr_t(p) % alignment == 0); vec_xstd2(v, 0, p); } void storeu(real_t *p) const { // Vector stores would require vector loads, which would need to // be atomic // TODO: see // for good ideas p[0] = (*this)[0]; p[1] = (*this)[1]; } void storeu(real_t *p, std::ptrdiff_t ioff) const { VML_ASSERT(intptr_t(p) % alignment == 0); if (ioff % realvec::size == 0) return storea(p + ioff); storeu(p + ioff); } void storea(real_t *p, mask_t const &m) const { VML_ASSERT(intptr_t(p) % alignment == 0); if (__builtin_expect(m.all_m, true)) { storea(p); } else { // Use vec_ste? if (m.m[0]) p[0] = (*this)[0]; if (m.m[1]) p[1] = (*this)[1]; } } void storeu(real_t *p, mask_t const &m) const { if (__builtin_expect(m.all_m, true)) { storeu(p); } else { // Use vec_ste? if (m.m[0]) p[0] = (*this)[0]; if (m.m[1]) p[1] = (*this)[1]; } } void storeu(real_t *p, std::ptrdiff_t ioff, mask_t const &m) const { VML_ASSERT(intptr_t(p) % alignment == 0); if (ioff % realvec::size == 0) return storea(p + ioff, m); storeu(p + ioff, m); } intvec_t as_int() const { return (__vector signed long long)v; } intvec_t convert_int() const { return MF::vml_convert_int(*this); } realvec operator+() const { return *this; } realvec operator-() const { return RV(0.0) - *this; } realvec operator+(realvec x) const { return vec_add(v, x.v); } realvec operator-(realvec x) const { return vec_sub(v, x.v); } realvec operator*(realvec x) const { return vec_mul(v, x.v); } realvec operator/(realvec x) const { return vec_div(v, x.v); } realvec &operator+=(realvec const &x) { return *this = *this + x; } realvec &operator-=(realvec const &x) { return *this = *this - x; } realvec &operator*=(realvec const &x) { return *this = *this * x; } realvec &operator/=(realvec const &x) { return *this = *this / x; } real_t maxval() const { return vml_std::fmax((*this)[0], (*this)[1]); } real_t minval() const { return vml_std::fmin((*this)[0], (*this)[1]); } real_t prod() const { return (*this)[0] * (*this)[1]; } real_t sum() const { return (*this)[0] + (*this)[1]; } boolvec_t operator==(realvec const &x) const { return vec_cmpeq(v, x.v); } boolvec_t operator!=(realvec const &x) const { return !(*this == x); } boolvec_t operator<(realvec const &x) const { return vec_cmplt(v, x.v); } boolvec_t operator<=(realvec const &x) const { return vec_cmple(v, x.v); } boolvec_t operator>(realvec const &x) const { return vec_cmpgt(v, x.v); } boolvec_t operator>=(realvec const &x) const { return vec_cmpge(v, x.v); } realvec acos() const { return MF::vml_acos(*this); } realvec acosh() const { return MF::vml_acosh(*this); } realvec asin() const { return MF::vml_asin(*this); } realvec asinh() const { return MF::vml_asinh(*this); } realvec atan() const { return MF::vml_atan(*this); } realvec atan2(realvec y) const { return MF::vml_atan2(*this, y); } realvec atanh() const { return MF::vml_atanh(*this); } realvec cbrt() const { return MF::vml_cbrt(*this); } realvec ceil() const { return vec_ceil(v); } realvec copysign(realvec y) const { return MF::vml_copysign(*this, y); } realvec cos() const { return MF::vml_cos(*this); } realvec cosh() const { return MF::vml_cosh(*this); } realvec exp() const { return MF::vml_exp(*this); } realvec exp10() const { return MF::vml_exp10(*this); } realvec exp2() const { return MF::vml_exp2(*this); } realvec expm1() const { return MF::vml_expm1(*this); } realvec fabs() const { return vec_abs(v); } realvec fdim(realvec y) const { return MF::vml_fdim(*this, y); } realvec floor() const { return vec_floor(v); } realvec fma(realvec y, realvec z) const { return vec_madd(v, y.v, z.v); } realvec fmax(realvec y) const { return vec_max(v, y.v); } realvec fmin(realvec y) const { return vec_min(v, y.v); } realvec fmod(realvec y) const { return MF::vml_fmod(*this, y); } realvec frexp(intvec_t *r) const { return MF::vml_frexp(*this, r); } realvec hypot(realvec y) const { return MF::vml_hypot(*this, y); } intvec_t ilogb() const { return MF::vml_ilogb(*this); } boolvec_t isfinite() const { return MF::vml_isfinite(*this); } boolvec_t isinf() const { return MF::vml_isinf(*this); } boolvec_t isnan() const { return MF::vml_isnan(*this); } boolvec_t isnormal() const { return MF::vml_isnormal(*this); } realvec ldexp(int_t n) const { return MF::vml_ldexp(*this, n); } realvec ldexp(intvec_t n) const { return MF::vml_ldexp(*this, n); } realvec log() const { return MF::vml_log(*this); } realvec log10() const { return MF::vml_log10(*this); } realvec log1p() const { return MF::vml_log1p(*this); } realvec log2() const { return MF::vml_log2(*this); } realvec_t mad(realvec_t y, realvec_t z) const { return MF::vml_mad(*this, y, z); } realvec nextafter(realvec y) const { return MF::vml_nextafter(*this, y); } realvec pow(realvec y) const { return MF::vml_pow(*this, y); } realvec rcp() const { realvec x = *this; realvec r = vec_re(v); // this is only an approximation // TODO: use fma // Note: don't rewrite this expression, this may introduce // cancellation errors r += r * (RV(1.0) - x * r); // two Newton iterations (see vml_rcp) r += r * (RV(1.0) - x * r); return r; } realvec remainder(realvec y) const { return MF::vml_remainder(*this, y); } realvec rint() const { return vec_round(v); /* sic! */ } realvec round() const { return MF::vml_round(*this); } realvec rsqrt() const { return RV(1.0) / sqrt(); } boolvec_t signbit() const { return MF::vml_signbit(*this); } realvec sin() const { return MF::vml_sin(*this); } realvec sinh() const { return MF::vml_sinh(*this); } realvec sqrt() const { return vec_sqrt(v); } realvec tan() const { return MF::vml_tan(*this); } realvec tanh() const { return MF::vml_tanh(*this); } realvec trunc() const { return vec_trunc(v); } }; // boolvec definitions inline intvec boolvec::as_int() const { return (__vector signed long long)v; } inline intvec boolvec::convert_int() const { return -(__vector signed long long)v; } inline boolvec boolvec::ifthen(boolvec_t x, boolvec_t y) const { return vec_sel(y.v, x.v, v); } inline intvec boolvec::ifthen(intvec_t x, intvec_t y) const { return vec_sel(y.v, x.v, v); } inline realvec boolvec::ifthen(realvec_t x, realvec_t y) const { return vec_sel(y.v, x.v, v); } // intvec definitions inline intvec intvec::abs() const { return MF::vml_abs(*this); } inline realvec intvec::as_float() const { return (__vector double)v; } inline intvec intvec::bitifthen(intvec_t x, intvec_t y) const { return MF::vml_bitifthen(*this, x, y); } inline intvec intvec::clz() const { return MF::vml_clz(*this); } inline realvec intvec::convert_float() const { // return vec_ctd(v, 0); return MF::vml_convert_float(*this); } inline intvec intvec::max(intvec_t x) const { return MF::vml_max(*this, x); } inline intvec intvec::min(intvec_t x) const { return MF::vml_min(*this, x); } inline intvec intvec::popcount() const { return MF::vml_popcount(*this); } inline intvec intvec::rotate(int_t n) const { return MF::vml_rotate(*this, n); } inline intvec intvec::rotate(intvec_t n) const { return MF::vml_rotate(*this, n); } } // namespace vecmathlib #endif // #ifndef VEC_VSX_DOUBLE2_H