/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * * Martin Renou * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XSIMD_BASE_HPP #define XSIMD_BASE_HPP #include #include #include #include #include #include #ifdef XSIMD_ENABLE_XTL_COMPLEX #include "xtl/xcomplex.hpp" #endif #include "../memory/xsimd_alignment.hpp" #include "xsimd_utils.hpp" #include "xsimd_base_bool.hpp" #include "xsimd_base_constant.hpp" namespace xsimd { template class simd_base; template class batch; namespace detail { template struct batch_kernel; } template struct simd_batch_inner_types { using batch_reference = X&; using const_batch_reference = const X&; }; template using batch_type_t = typename T::batch_type; namespace detail { template struct get_real_batch_type { using batch_type = batch_type_t; }; template struct get_real_batch_type, N>> { using batch_type = typename batch, N>::real_batch; }; #ifdef XSIMD_ENABLE_XTL_COMPLEX template struct get_real_batch_type, N>> { using batch_type = typename batch, N>::real_batch; }; #endif } template using real_batch_type_t = typename detail::get_real_batch_type::batch_type; namespace detail { template struct is_simd_type : std::is_base_of, X> { }; } template using enable_if_simd_t = typename std::enable_if::value, batch_type_t>::type; /************* * simd_base * *************/ /** * @class simd_base * @brief Base class for batches and batch proxies. * * The simd_base class is the base class for all classes * representing a batch or a batch proxy. It provides very few * methods, so concrete batches usually inherit from intermediate * classes. * * @tparam X The most derived type */ template class simd_base { public: using derived_class = X; using batch_reference = typename simd_batch_inner_types::batch_reference; using const_batch_reference = typename simd_batch_inner_types::const_batch_reference; batch_reference operator()(); const_batch_reference operator()() const; X& derived_cast(); const X& derived_cast() const; }; /************** * simd_batch * **************/ /** * @class simd_batch * @brief Base class for batch of integer or floating point values. * * The simd_batch class is the base class for all classes representing * a batch of integer or floating point values. Each type of batch (i.e. * a class inheriting from simd_batch) has its dedicated type of boolean * batch (i.e. a class inheriting from simd_batch_bool) for logical operations. * * @tparam X The derived type * @sa simd_batch_bool */ template class simd_batch : public simd_base { public: using base_type = simd_base; using batch_reference = typename base_type::batch_reference; using const_batch_reference = typename base_type::const_batch_reference; using batch_type = X; using value_type = typename simd_batch_traits::value_type; static constexpr std::size_t size = simd_batch_traits::size; using storage_type = typename simd_batch_traits::storage_type; using batch_bool_type = typename simd_batch_traits::batch_bool_type; using iterator = value_type*; using const_iterator = const value_type*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; static X broadcast(value_type v); template static X from_unaligned(T* src); template static X from_aligned(T* src); X& operator+=(const X& rhs); X& operator+=(const value_type& rhs); X& operator-=(const X& rhs); X& operator-=(const value_type& rhs); X& operator*=(const X& rhs); X& operator*=(const value_type& rhs); X& operator/=(const X& rhs); X& operator/=(const value_type& rhs); X& operator&=(const X& rhs); X& operator|=(const X& rhs); X& operator^=(const X& rhs); X& operator++(); X& operator++(int); X& operator--(); X& operator--(int); X& load_aligned(const char* src); X& load_unaligned(const char* src); void store_aligned(char* dst) const; void store_unaligned(char* dst) const; batch_reference get(); const_batch_reference get() const; value_type& operator[](std::size_t index); const value_type& operator[](std::size_t index) const; iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; const_iterator cbegin() const; const_iterator cend() const; reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rbegin() const; const_reverse_iterator rend() const; const_reverse_iterator crbegin() const; const_reverse_iterator crend() const; protected: simd_batch() = default; ~simd_batch() = default; simd_batch(const simd_batch&) = default; simd_batch& operator=(const simd_batch&) = default; simd_batch(simd_batch&&) = default; simd_batch& operator=(simd_batch&&) = default; constexpr simd_batch(storage_type value); using char_itype = typename std::conditional::value, int8_t, uint8_t>::type; union { storage_type m_value; value_type m_array[size]; }; }; template typename simd_batch_traits::batch_bool_type operator!(const simd_base& rhs); template batch_type_t min(const simd_base& lhs, const simd_base& rhs); template batch_type_t max(const simd_base& lhs, const simd_base& rhs); template batch_type_t fmin(const simd_base& lhs, const simd_base& rhs); template batch_type_t fmax(const simd_base& lhs, const simd_base& rhs); template real_batch_type_t abs(const simd_base& rhs); template batch_type_t fabs(const simd_base& rhs); template batch_type_t sqrt(const simd_base& rhs); template batch_type_t fma(const simd_base& x, const simd_base& y, const simd_base& z); template batch_type_t fms(const simd_base& x, const simd_base& y, const simd_base& z); template batch_type_t fnma(const simd_base& x, const simd_base& y, const simd_base& z); template batch_type_t fnms(const simd_base& x, const simd_base& y, const simd_base& z); template typename simd_batch_traits::value_type hadd(const simd_base& rhs); template enable_if_simd_t haddp(const X* row); template batch_type_t select(const typename simd_batch_traits::batch_bool_type& cond, const simd_base& a, const simd_base& b); template batch_type_t zip_lo(const simd_base& lhs, const simd_base& rhs); template batch_type_t zip_hi(const simd_base& lhs, const simd_base& rhs); template batch_type_t extract_pair(const simd_base& lhs, const simd_base& rhs, const int n); template typename simd_batch_traits::batch_bool_type isnan(const simd_base& x); template std::ostream& operator<<(std::ostream& out, const simd_batch& rhs); /*************************** * generic batch operators * ***************************/ template batch operator&&(const batch& lhs, const batch& rhs); template batch operator||(const batch& lhs, const batch& rhs); template batch operator<<(const batch& lhs, const batch& rhs); template batch operator>>(const batch& lhs, const batch& rhs); /************************** * batch cast functions * **************************/ // Provides a static_cast from batch to batch template struct batch_cast_impl { template static inline batch run_impl(const batch& x, detail::index_sequence) { return batch(static_cast(x[I])...); } public: static inline batch run(const batch& x) { return run_impl(x, detail::make_index_sequence{}); } }; template struct batch_cast_impl { static inline batch run(const batch& x) { return x; } }; // Shorthand for defining an intrinsic-based batch_cast implementation #define XSIMD_BATCH_CAST_INTRINSIC(T_IN, T_OUT, N, INTRINSIC) \ template <> \ struct batch_cast_impl \ { \ static inline batch run(const batch& x) \ { \ return INTRINSIC(x); \ } \ }; // Shorthand for defining an intrinsic-based batch_cast implementation that requires 2 intrinsics #define XSIMD_BATCH_CAST_INTRINSIC2(T_IN, T_OUT, N, INTRINSIC1, INTRINSIC2) \ template <> \ struct batch_cast_impl \ { \ static inline batch run(const batch& x) \ { \ return INTRINSIC2(INTRINSIC1(x)); \ } \ }; // Shorthand for defining an implicit batch_cast implementation #define XSIMD_BATCH_CAST_IMPLICIT(T_IN, T_OUT, N) \ template <> \ struct batch_cast_impl \ { \ static inline batch run(const batch& x) \ { \ return batch(x); \ } \ }; /************************** * bitwise cast functions * **************************/ // Provides a reinterpret_cast from batch to batch template struct bitwise_cast_impl; // Shorthand for defining an intrinsic-based bitwise_cast implementation #define XSIMD_BITWISE_CAST_INTRINSIC(T_IN, N_IN, T_OUT, N_OUT, INTRINSIC) \ template <> \ struct bitwise_cast_impl, batch> \ { \ static inline batch run(const batch& x) \ { \ return INTRINSIC(x); \ } \ }; // Backwards-compatible interface to bitwise_cast_impl template ::size> B bitwise_cast(const batch& x); template ::size> B bitwise_cast(const batch& x); template ::size> B bitwise_cast(const batch& x); template ::size> B bitwise_cast(const batch& x); template batch bitwise_cast(const batch_bool& src); /**************** * helper macro * ****************/ #define XSIMD_DECLARE_LOAD_STORE(TYPE, N, CVT_TYPE) \ batch& load_aligned(const CVT_TYPE*); \ batch& load_unaligned(const CVT_TYPE*); \ void store_aligned(CVT_TYPE* dst) const; \ void store_unaligned(CVT_TYPE* dst) const; #define XSIMD_DEFINE_LOAD_STORE(TYPE, N, CVT_TYPE, ALIGNMENT) \ inline batch& batch::load_aligned(const CVT_TYPE* src) \ { \ alignas(ALIGNMENT) TYPE tmp[N]; \ unroller([&](std::size_t i) { \ tmp[i] = static_cast(src[i]); \ }); \ return load_aligned(tmp); \ } \ inline batch& batch::load_unaligned(const CVT_TYPE* src) \ { \ return load_aligned(src); \ } \ inline void batch::store_aligned(CVT_TYPE* dst) const \ { \ alignas(ALIGNMENT) TYPE tmp[N]; \ store_aligned(tmp); \ unroller([&](std::size_t i) { \ dst[i] = static_cast(tmp[i]); \ }); \ } \ inline void batch::store_unaligned(CVT_TYPE* dst) const \ { \ return store_aligned(dst); \ } #ifdef XSIMD_32_BIT_ABI #define XSIMD_DECLARE_LOAD_STORE_LONG(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, long) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, unsigned long) \ namespace detail { template struct get_int_type; template <> struct get_int_type { using type = int32_t; }; template <> struct get_int_type { using type = uint32_t; }; template using get_int_type_t = typename get_int_type::type; } #define XSIMD_DEFINE_LOAD_STORE_LONG_IMPL(TYPE, N, CVT_TYPE, ALIGNMENT) \ inline batch& batch::load_aligned(const CVT_TYPE* src) \ { \ using int_type = detail::get_int_type_t; \ return this->load_aligned(reinterpret_cast(src)); \ } \ inline batch& batch::load_unaligned(const CVT_TYPE* src) \ { \ using int_type = detail::get_int_type_t; \ return this->load_unaligned(reinterpret_cast(src)); \ } \ inline void batch::store_aligned(CVT_TYPE* dst) const \ { \ using int_type = detail::get_int_type_t; \ this->store_aligned(reinterpret_cast(dst)); \ } \ inline void batch::store_unaligned(CVT_TYPE* dst) const \ { \ using int_type = detail::get_int_type_t; \ this->store_unaligned(reinterpret_cast(dst)); \ } \ #define XSIMD_DEFINE_LOAD_STORE_LONG(TYPE, N, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE_LONG_IMPL(TYPE, N, long, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE_LONG_IMPL(TYPE, N, unsigned long, ALIGNMENT) \ #else #define XSIMD_DECLARE_LOAD_STORE_LONG(TYPE, N) #define XSIMD_DEFINE_LOAD_STORE_LONG(TYPE, N, ALIGNMENT) #endif // XSIMD_32_BIT_ABI #define XSIMD_DECLARE_LOAD_STORE_INT8(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, bool) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, float) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, double) #define XSIMD_DEFINE_LOAD_STORE_INT8(TYPE, N, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, bool, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, float, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, double, ALIGNMENT) #define XSIMD_DECLARE_LOAD_STORE_INT16(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, bool) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, float) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, double) #define XSIMD_DEFINE_LOAD_STORE_INT16(TYPE, N, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, bool, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, float, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, double, ALIGNMENT) #define XSIMD_DECLARE_LOAD_STORE_INT32(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, bool) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, float) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, double) #define XSIMD_DEFINE_LOAD_STORE_INT32(TYPE, N, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, bool, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint64_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, float, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, double, ALIGNMENT) #define XSIMD_DECLARE_LOAD_STORE_INT64(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, bool) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, float) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, double) #define XSIMD_DEFINE_LOAD_STORE_INT64(TYPE, N, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, bool, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint8_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint16_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, int32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, uint32_t, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, float, ALIGNMENT) \ XSIMD_DEFINE_LOAD_STORE(TYPE, N, double, ALIGNMENT) #define XSIMD_DECLARE_LOAD_STORE_ALL(TYPE, N) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, bool) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint8_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint16_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint32_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, int64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, uint64_t) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, float) \ XSIMD_DECLARE_LOAD_STORE(TYPE, N, double) #define XSIMD_DEFINE_BITWISE_CAST(TYPE, N) \ inline batch bitwise_cast(const batch_bool& src) \ { \ TYPE z(0); \ return select(src, batch(TYPE(~z)), batch(z)); \ } #define XSIMD_DEFINE_BITWISE_CAST_FLOAT(TYPE, N) \ inline batch bitwise_cast(const batch_bool& src) \ { \ TYPE z0(0), z1(0); \ using int_type = as_unsigned_integer_t; \ int_type value(~int_type(0)); \ std::memcpy(&z1, &value, sizeof(int_type)); \ return select(src, batch(z1), batch(z0)); \ } #define XSIMD_DEFINE_BITWISE_CAST_ALL(NMIN) \ XSIMD_DEFINE_BITWISE_CAST_FLOAT(double, NMIN) \ XSIMD_DEFINE_BITWISE_CAST_FLOAT(float, NMIN * 2) \ XSIMD_DEFINE_BITWISE_CAST(int64_t, NMIN) \ XSIMD_DEFINE_BITWISE_CAST(uint64_t, NMIN) \ XSIMD_DEFINE_BITWISE_CAST(int32_t, NMIN * 2) \ XSIMD_DEFINE_BITWISE_CAST(uint32_t, NMIN * 2) \ XSIMD_DEFINE_BITWISE_CAST(int16_t, NMIN * 4) \ XSIMD_DEFINE_BITWISE_CAST(uint16_t, NMIN * 4) \ XSIMD_DEFINE_BITWISE_CAST(int8_t, NMIN * 8) \ XSIMD_DEFINE_BITWISE_CAST(uint8_t, NMIN * 8) /**************************** * simd_base implementation * ****************************/ /** * @name Static downcast functions */ //@{ /** * Returns a reference to the batch type used for computation. */ template inline auto simd_base::operator()() -> batch_reference { return derived_cast().get(); } /** * Returns a constant reference to the batch type used for computation. */ template inline auto simd_base::operator()() const -> const_batch_reference { return derived_cast().get(); } /** * Returns a reference to the actual derived type of simd_base. */ template inline X& simd_base::derived_cast() { return *static_cast(this); } /** * Returns a constant reference to the actual derived type of simd_base. */ template inline const X& simd_base::derived_cast() const { return *static_cast(this); } //@} /***************************** * simd_batch implementation * *****************************/ template constexpr inline simd_batch::simd_batch(storage_type value) : m_value(value) { } /** * @name Static builders */ //@{ /** * Creates a batch from the single value \c v. * @param v the value used to initialize the batch * @return a new batch instance */ template inline X simd_batch::broadcast(value_type v) { return X(v); } /** * Creates a batch from the buffer \c src. The * memory does not need to be aligned. * @param src the memory buffer to read * @return a new batch instance */ template template inline X simd_batch::from_unaligned(T* src) { X res; res.load_unaligned(src); return res; } /** * Creates a batch from the buffer \c src. The * memory needs to be aligned. * @param src the memory buffer to read * @return a new batch instance */ template template inline X simd_batch::from_aligned(T* src) { X res; res.load_aligned(src); return res; } //@} /** * @name Arithmetic computed assignment */ //@{ /** * Adds the batch \c rhs to \c this. * @param rhs the batch to add. * @return a reference to \c this. */ template inline X& simd_batch::operator+=(const X& rhs) { (*this)() = (*this)() + rhs; return (*this)(); } /** * Adds the scalar \c rhs to each value contained in \c this. * @param rhs the scalar to add. * @return a reference to \c this. */ template inline X& simd_batch::operator+=(const value_type& rhs) { (*this)() = (*this)() + X(rhs); return (*this)(); } /** * Substracts the batch \c rhs to \c this. * @param rhs the batch to substract. * @return a reference to \c this. */ template inline X& simd_batch::operator-=(const X& rhs) { (*this)() = (*this)() - rhs; return (*this)(); } /** * Substracts the scalar \c rhs to each value contained in \c this. * @param rhs the scalar to substract. * @return a reference to \c this. */ template inline X& simd_batch::operator-=(const value_type& rhs) { (*this)() = (*this)() - X(rhs); return (*this)(); } /** * Multiplies \c this with the batch \c rhs. * @param rhs the batch involved in the multiplication. * @return a reference to \c this. */ template inline X& simd_batch::operator*=(const X& rhs) { (*this)() = (*this)() * rhs; return (*this)(); } /** * Multiplies each scalar contained in \c this with the scalar \c rhs. * @param rhs the scalar involved in the multiplication. * @return a reference to \c this. */ template inline X& simd_batch::operator*=(const value_type& rhs) { (*this)() = (*this)() * X(rhs); return (*this)(); } /** * Divides \c this by the batch \c rhs. * @param rhs the batch involved in the division. * @return a reference to \c this. */ template inline X& simd_batch::operator/=(const X& rhs) { (*this)() = (*this)() / rhs; return (*this)(); } /** * Divides each scalar contained in \c this by the scalar \c rhs. * @param rhs the scalar involved in the division. * @return a reference to \c this. */ template inline X& simd_batch::operator/=(const value_type& rhs) { (*this)() = (*this)() / X(rhs); return (*this)(); } //@} /** * @name Bitwise computed assignment */ /** * Assigns the bitwise and of \c rhs and \c this. * @param rhs the batch involved in the operation. * @return a reference to \c this. */ template inline X& simd_batch::operator&=(const X& rhs) { (*this)() = (*this)() & rhs; return (*this)(); } /** * Assigns the bitwise or of \c rhs and \c this. * @param rhs the batch involved in the operation. * @return a reference to \c this. */ template inline X& simd_batch::operator|=(const X& rhs) { (*this)() = (*this)() | rhs; return (*this)(); } /** * Assigns the bitwise xor of \c rhs and \c this. * @param rhs the batch involved in the operation. * @return a reference to \c this. */ template inline X& simd_batch::operator^=(const X& rhs) { (*this)() = (*this)() ^ rhs; return (*this)(); } //@} /** * @name Increment and decrement operators */ //@{ /** * Pre-increment operator. * @return a reference to \c this. */ template inline X& simd_batch::operator++() { (*this)() += value_type(1); return (*this)(); } /** * Post-increment operator. * @return a reference to \c this. */ template inline X& simd_batch::operator++(int) { X tmp = (*this)(); (*this)() += value_type(1); return tmp; } /** * Pre-decrement operator. * @return a reference to \c this. */ template inline X& simd_batch::operator--() { (*this)() -= value_type(1); return (*this)(); } /** * Post-decrement operator. * @return a reference to \c this. */ template inline X& simd_batch::operator--(int) { X tmp = (*this)(); (*this)() -= value_type(1); return tmp; } //@} template inline X& simd_batch::load_aligned(const char* src) { return (*this)().load_aligned(reinterpret_cast(src)); } template inline X& simd_batch::load_unaligned(const char* src) { return (*this)().load_unaligned(reinterpret_cast(src)); } template void simd_batch::store_aligned(char* dst) const { return (*this)().store_aligned(reinterpret_cast(dst)); } template void simd_batch::store_unaligned(char* dst) const { return (*this)().store_unaligned(reinterpret_cast(dst)); } template inline auto simd_batch::get() -> batch_reference { return this->derived_cast(); } template inline auto simd_batch::get() const -> const_batch_reference { return this->derived_cast(); } template inline auto simd_batch::operator[](std::size_t index) -> value_type& { return m_array[index & (size - 1)]; } template inline auto simd_batch::operator[](std::size_t index) const -> const value_type& { return m_array[index & (size - 1)]; } template inline auto simd_batch::begin() -> iterator { return m_array; } template inline auto simd_batch::end() -> iterator { return m_array + size; } template inline auto simd_batch::begin() const -> const_iterator { return cbegin(); } template inline auto simd_batch::end() const -> const_iterator { return cend(); } template inline auto simd_batch::cbegin() const -> const_iterator { return m_array; } template inline auto simd_batch::cend() const -> const_iterator { return m_array + size; } template inline auto simd_batch::rbegin() -> reverse_iterator { return reverse_iterator(end()); } template inline auto simd_batch::rend() -> reverse_iterator { return reverse_iterator(begin()); } template inline auto simd_batch::rbegin() const -> const_reverse_iterator { return crbegin(); } template inline auto simd_batch::rend() const -> const_reverse_iterator { return crend(); } template inline auto simd_batch::crbegin() const -> const_reverse_iterator { return const_reverse_iterator(end()); } template inline auto simd_batch::crend() const -> const_reverse_iterator { return const_reverse_iterator(begin()); } #define XSIMD_UNARY_OP(OP, FUNC) \ template \ inline batch_type_t operator OP(const simd_base& rhs) \ { \ using value_type = typename simd_batch_traits::value_type; \ using kernel = detail::batch_kernel::size>; \ return kernel::FUNC(rhs()); \ } #define XSIMD_BINARY_OP(OP, FUNC) \ template \ inline batch_type_t operator OP(const simd_base& lhs, const simd_base& rhs) \ { \ using value_type = typename simd_batch_traits::value_type; \ using kernel = detail::batch_kernel::size>; \ return kernel::FUNC(lhs(), rhs()); \ } \ \ template \ inline batch_type_t operator OP(const typename simd_batch_traits::value_type& lhs, \ const simd_base& rhs) \ { \ return batch_type_t(lhs) OP rhs(); \ } \ \ template \ inline batch_type_t operator OP(const simd_base& lhs, \ const typename simd_batch_traits::value_type& rhs) \ { \ return lhs() OP batch_type_t(rhs); \ } #define XSIMD_BINARY_BOOL_OP(OP, FUNC) \ template \ inline typename simd_batch_traits::batch_bool_type operator OP(const simd_base& lhs, \ const simd_base& rhs) \ { \ using value_type = typename simd_batch_traits::value_type; \ using kernel = detail::batch_kernel::size>; \ return kernel::FUNC(lhs(), rhs()); \ } \ \ template \ inline typename simd_batch_traits::batch_bool_type operator OP( \ const typename simd_batch_traits::value_type& lhs, const simd_base& rhs) \ { \ return batch_type_t(lhs) OP rhs(); \ } \ \ template \ inline typename simd_batch_traits::batch_bool_type operator OP( \ const simd_base& lhs, const typename simd_batch_traits::value_type& rhs) \ { \ return lhs() OP batch_type_t(rhs); \ } #define XSIMD_BINARY_BOOL_OP_DERIVED(OP, BASE_OP) \ template \ inline typename simd_batch_traits::batch_bool_type operator OP(const simd_base& lhs, \ const simd_base& rhs) \ { \ return rhs() BASE_OP lhs(); \ } \ \ template \ inline typename simd_batch_traits::batch_bool_type operator OP( \ const typename simd_batch_traits::value_type& lhs, const simd_base& rhs) \ { \ return rhs() BASE_OP batch_type_t(lhs); \ } \ \ template \ inline typename simd_batch_traits::batch_bool_type operator OP( \ const simd_base& lhs, const typename simd_batch_traits::value_type& rhs) \ { \ return batch_type_t(rhs) BASE_OP lhs(); \ } /** * @defgroup simd_batch_arithmetic Arithmetic operators */ /** * @ingroup simd_batch_arithmetic * * Computes the opposite of the batch \c rhs. * @tparam X the actual type of batch. * @param rhs batch involved in the operation. * @return the opposite of \c rhs. */ template inline batch_type_t operator-(const simd_base& rhs); XSIMD_UNARY_OP(-, neg) /** * @ingroup simd_batch_arithmetic * * No-op on \c rhs. * @tparam X the actual type of batch. * @param rhs batch involved in the operation. * @return \c rhs. */ template inline X operator+(const simd_batch& rhs) { return rhs(); } /** * @ingroup simd_batch_arithmetic * * Computes the sum of the batches \c lhs and \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the addition. * @param rhs batch involved in the addition. * @return the result of the addition. */ template batch_type_t operator+(const simd_base& lhs, const simd_base& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the sum of the batch \c lhs and the scalar \c rhs. Equivalent to the * sum of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the addition. * @param rhs scalar involved in the addition. * @return the result of the addition. */ template batch_type_t operator+(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the sum of the scalar \c lhs and the batch \c rhs. Equivalent to the * sum of two batches where all the values of the first one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the addition. * @param rhs batch involved in the addition. * @return the result of the addition. */ template batch_type_t operator+(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs); XSIMD_BINARY_OP(+, add) /** * @ingroup simd_batch_arithmetic * * Computes the saturate sum of the batch \c lhs and the batch \c rhs. * \c lhs. * @tparam X the actual type of batch. * @param lhs batch involved in the saturated addition. * @param rhs batch involved in the saturated addition. * @return the result of the saturated addition. */ template inline batch_type_t sadd(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::sadd(lhs(), rhs()); } /** * @ingroup simd_batch_arithmetic * * Computes the saturate sum of the scalar \c lhs and the batch \c rhs. Equivalent to the * saturated sum of two batches where all the values of the first one are initialized to * \c lhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the saturated addition. * @param rhs batch involved in the saturated addition. * @return the result of the saturated addition. */ template inline batch_type_t sadd(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs) { return sadd(batch_type_t(lhs),rhs()); } /** * @ingroup simd_batch_arithmetic * * Computes the saturate sum of the batch \c lhs and the scalar \c rhs. Equivalent to the * saturated sum of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the saturated addition. * @param rhs scalar involved in the saturated addition. * @return the result of the saturated addition. */ template inline batch_type_t sadd(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs) { return sadd(lhs(),batch_type_t(rhs)); } /** * @ingroup simd_batch_arithmetic * * Computes the difference of the batches \c lhs and \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the difference. * @param rhs batch involved in the difference. * @return the result of the difference. */ template batch_type_t operator-(const simd_base& lhs, const simd_base& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the difference of the batch \c lhs and the scalar \c rhs. Equivalent to the * difference of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the difference. * @param rhs scalar involved in the difference. * @return the result of the difference. */ template batch_type_t operator-(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the difference of the scalar \c lhs and the batch \c rhs. Equivalent to the * difference of two batches where all the values of the first one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the difference. * @param rhs batch involved in the difference. * @return the result of the difference. */ template batch_type_t operator-(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs); XSIMD_BINARY_OP(-, sub) /** * @ingroup simd_batch_arithmetic * * Computes the saturate difference of the batch \c lhs and the batch \c rhs. * \c lhs. * @tparam X the actual type of batch. * @param lhs batch involved in the saturated difference. * @param rhs batch involved in the saturated difference. * @return the result of the saturated difference. */ template inline batch_type_t ssub(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::ssub(lhs(), rhs()); } /** * @ingroup simd_batch_arithmetic * * Computes the saturate difference of the scalar \c lhs and the batch \c rhs. Equivalent to the * saturated sum of two batches where all the values of the first one are initialized to * \c lhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the saturated difference. * @param rhs batch involved in the saturated difference. * @return the result of the saturated difference. */ template inline batch_type_t ssub(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs) { return ssub(batch_type_t(lhs),rhs()); } /** * @ingroup simd_batch_arithmetic * * Computes the saturate difference of the batch \c lhs and the scalar \c rhs. Equivalent to the * saturated difference of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the saturated difference. * @param rhs scalar involved in the saturated difference. * @return the result of the saturated difference. */ template inline batch_type_t ssub(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs) { return ssub(lhs(),batch_type_t(rhs)); } /** * @ingroup simd_batch_arithmetic * * Computes the product of the batches \c lhs and \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the product. * @param rhs batch involved in the product. * @return the result of the product. */ template batch_type_t operator*(const simd_base& lhs, const simd_base& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the product of the batch \c lhs and the scalar \c rhs. Equivalent to the * product of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the product. * @param rhs scalar involved in the product. * @return the result of the product. */ template batch_type_t operator*(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the product of the scalar \c lhs and the batch \c rhs. Equivalent to the * difference of two batches where all the values of the first one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the product. * @param rhs batch involved in the product. * @return the result of the product. */ template batch_type_t operator*(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs); XSIMD_BINARY_OP(*, mul) /** * @ingroup simd_batch_arithmetic * * Computes the division of the batch \c lhs by the batch \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the division. * @param rhs batch involved in the division. * @return the result of the division. */ template batch_type_t operator/(const simd_base& lhs, const simd_base& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the division of the batch \c lhs by the scalar \c rhs. Equivalent to the * division of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the division. * @param rhs scalar involved in the division. * @return the result of the division. */ template batch_type_t operator/(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the division of the scalar \c lhs and the batch \c rhs. Equivalent to the * difference of two batches where all the values of the first one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the division. * @param rhs batch involved in the division. * @return the result of the division. */ template batch_type_t operator/(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs); XSIMD_BINARY_OP(/, div) /** * @ingroup simd_batch_arithmetic * * Computes the integer modulo of the batch \c lhs by the batch \c rhs. * @param lhs batch involved in the modulo. * @param rhs batch involved in the modulo. * @return the result of the modulo. */ template batch_type_t operator%(const simd_base& lhs, const simd_base& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the integer modulo of the batch \c lhs by the scalar \c rhs. Equivalent to the * modulo of two batches where all the values of the second one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the modulo. * @param rhs scalar involved in the modulo. * @return the result of the modulo. */ template batch_type_t operator%(const simd_base& lhs, const typename simd_batch_traits::value_type& rhs); /** * @ingroup simd_batch_arithmetic * * Computes the integer modulo of the scalar \c lhs and the batch \c rhs. Equivalent to the * difference of two batches where all the values of the first one are initialized to * \c rhs. * @tparam X the actual type of batch. * @param lhs scalar involved in the modulo. * @param rhs batch involved in the modulo. * @return the result of the modulo. */ template batch_type_t operator%(const typename simd_batch_traits::value_type& lhs, const simd_base& rhs); XSIMD_BINARY_OP(%, mod) /** * @defgroup simd_batch_comparison Comparison operators */ /** * @ingroup simd_batch_comparison * * Element-wise equality comparison of batches \c lhs and \c rhs. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator==(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP(==, eq) /** * @ingroup simd_batch_comparison * * Element-wise inequality comparison of batches \c lhs and \c rhs. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator!=(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP(!=, neq) /** * @ingroup simd_batch_comparison * * Element-wise lesser than comparison of batches \c lhs and \c rhs. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator<(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP(<, lt) /** * @ingroup simd_batch_comparison * * Element-wise lesser or equal to comparison of batches \c lhs and \c rhs. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator<=(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP(<=, lte) /** * @ingroup simd_batch_comparison * * Element-wise greater than comparison of batches \c lhs and \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator>(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP_DERIVED(>, <) /** * @ingroup simd_batch_comparison * * Element-wise greater or equal comparison of batches \c lhs and \c rhs. * @tparam X the actual type of batch. * @param lhs batch involved in the comparison. * @param rhs batch involved in the comparison. * @return a boolean batch. */ template typename simd_batch_traits::batch_bool_type operator>=(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_BOOL_OP_DERIVED(>=, <=) /** * @defgroup simd_batch_bitwise Bitwise operators */ /** * @ingroup simd_batch_bitwise * * Computes the bitwise and of the batches \c lhs and \c rhs. * @param lhs batch involved in the operation. * @param rhs batch involved in the operation. * @return the result of the bitwise and. */ template inline batch_type_t operator&(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_OP(&, bitwise_and) /** * @ingroup simd_batch_bitwise * * Computes the bitwise or of the batches \c lhs and \c rhs. * @param lhs batch involved in the operation. * @param rhs batch involved in the operation. * @return the result of the bitwise or. */ template inline batch_type_t operator|(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_OP(|, bitwise_or) /** * @ingroup simd_batch_bitwise * * Computes the bitwise xor of the batches \c lhs and \c rhs. * @param lhs batch involved in the operation. * @param rhs batch involved in the operation. * @return the result of the bitwise xor. */ template inline batch_type_t operator^(const simd_base& lhs, const simd_base& rhs); XSIMD_BINARY_OP(^, bitwise_xor) /** * @ingroup simd_batch_bitwise * * Computes the bitwise not of the batches \c lhs and \c rhs. * @param rhs batch involved in the operation. * @return the result of the bitwise not. */ template batch_type_t operator~(const simd_base& rhs); XSIMD_UNARY_OP(~, bitwise_not) /** * @ingroup simd_batch_bitwise * * Computes the bitwise andnot of the batches \c lhs and \c rhs. * @param lhs batch involved in the operation. * @param rhs batch involved in the operation. * @return the result of the bitwise andnot. */ template inline batch_type_t bitwise_andnot(const simd_batch& lhs, const simd_batch& rhs) { using value_type = typename simd_batch_traits::value_type; \ using kernel = detail::batch_kernel::size>; \ return kernel::bitwise_andnot(lhs(), rhs()); } /** * Element-wise not of \c rhs. * @tparam X the actual type of batch. * @param rhs batch involved in the logical not operation. * @return boolean batch. */ template inline typename simd_batch_traits::batch_bool_type operator!(const simd_base& rhs) { using b_type = typename X::batch_type; using value_type = typename simd_batch_traits::value_type; return rhs() == b_type(value_type(0)); } /** * Returns the smaller values of the batches \c lhs and \c rhs. * @param lhs a batch of integer or floating point values. * @param rhs a batch of integer or floating point values. * @return a batch of the smaller values. */ template inline batch_type_t min(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::min(lhs(), rhs()); } /** * Returns the larger values of the batches \c lhs and \c rhs. * @param lhs a batch of integer or floating point values. * @param rhs a batch of integer or floating point values. * @return a batch of the larger values. */ template inline batch_type_t max(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::max(lhs(), rhs()); } /** * Returns the smaller values of the batches \c lhs and \c rhs. * @param lhs a batch of floating point values. * @param rhs a batch of floating point values. * @return a batch of the smaller values. */ template inline batch_type_t fmin(const simd_batch& lhs, const simd_batch& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fmin(lhs(), rhs()); } /** * Returns the larger values of the batches \c lhs and \c rhs. * @param lhs a batch of floating point values. * @param rhs a batch of floating point values. * @return a batch of the larger values. */ template inline batch_type_t fmax(const simd_batch& lhs, const simd_batch& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fmax(lhs(), rhs()); } /** * Computes the absolute values of each scalar in the batch \c rhs. * @param rhs batch of integer or floating point values. * @return the asbolute values of \c rhs. */ template inline real_batch_type_t abs(const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::abs(rhs()); } /** * Computes the absolute values of each scalar in the batch \c rhs. * @param rhs batch floating point values. * @return the asbolute values of \c rhs. */ template inline batch_type_t fabs(const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fabs(rhs()); } /** * Computes the square root of the batch \c rhs. * @param rhs batch of floating point values. * @return the square root of \c rhs. */ template inline batch_type_t sqrt(const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::sqrt(rhs()); } /** * Computes (x*y) + z in a single instruction when possible. * @param x a batch of integer or floating point values. * @param y a batch of integer or floating point values. * @param z a batch of integer or floating point values. * @return the result of the fused multiply-add operation. */ template inline batch_type_t fma(const simd_base& x, const simd_base& y, const simd_base& z) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fma(x(), y(), z()); } /** * Computes (x*y) - z in a single instruction when possible. * @param x a batch of integer or floating point values. * @param y a batch of integer or floating point values. * @param z a batch of integer or floating point values. * @return the result of the fused multiply-sub operation. */ template inline batch_type_t fms(const simd_base& x, const simd_base& y, const simd_base& z) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fms(x(), y(), z()); } /** * Computes -(x*y) + z in a single instruction when possible. * @param x a batch of integer or floating point values. * @param y a batch of integer or floating point values. * @param z a batch of integer or floating point values. * @return the result of the fused negated multiply-add operation. */ template inline batch_type_t fnma(const simd_base& x, const simd_base& y, const simd_base& z) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fnma(x(), y(), z()); } /** * Computes -(x*y) - z in a single instruction when possible. * @param x a batch of integer or floating point values. * @param y a batch of integer or floating point values. * @param z a batch of integer or floating point values. * @return the result of the fused negated multiply-sub operation. */ template inline batch_type_t fnms(const simd_base& x, const simd_base& y, const simd_base& z) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::fnms(x(), y(), z()); } /** * @defgroup simd_batch_reducers Reducers */ /** * @ingroup simd_batch_reducers * * Adds all the scalars of the batch \c rhs. * @param rhs batch involved in the reduction * @return the result of the reduction. */ template inline typename simd_batch_traits::value_type hadd(const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::hadd(rhs()); } /** * @ingroup simd_batch_reducers * * Parallel horizontal addition: adds the scalars of each batch * in the array pointed by \c row and store them in a returned * batch. * @param row an array of \c N batches * @return the result of the reduction. */ template enable_if_simd_t haddp(const X* row) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::haddp(row); } /** * @defgroup simd_batch_miscellaneous Miscellaneous */ /** * @ingroup simd_batch_miscellaneous * * Ternary operator for batches: selects values from the batches \c a or \c b * depending on the boolean values in \c cond. Equivalent to * \code{.cpp} * for(std::size_t i = 0; i < N; ++i) * res[i] = cond[i] ? a[i] : b[i]; * \endcode * @param cond batch condition. * @param a batch values for truthy condition. * @param b batch value for falsy condition. * @return the result of the selection. */ template inline batch_type_t select(const typename simd_batch_traits::batch_bool_type& cond, const simd_base& a, const simd_base& b) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::select(cond(), a(), b()); } /** * @ingroup simd_batch_miscellaneous * * Ternary operator for batches: selects values from the batches \c a or \c b * depending on the boolean values in the constant batch \c cond. Equivalent to * \code{.cpp} * for(std::size_t i = 0; i < N; ++i) * res[i] = cond[i] ? a[i] : b[i]; * \endcode * @param cond constant batch condition. * @param a batch values for truthy condition. * @param b batch value for falsy condition. * @return the result of the selection. */ template inline batch_type_t select(const batch_bool_constant::value_type, Masks...>& cond, const simd_base& a, const simd_base& b) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::select(cond, a(), b()); } /** * Unpack and interleave data from the LOW half of batches \c lhs and \c rhs. * Store the results in the Return value. * @param lhs a batch of integer or floating point or double precision values. * @param rhs a batch of integer or floating point or double precision values. * @return a batch of the low part of shuffled values. */ template inline batch_type_t zip_lo(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::zip_lo(lhs(), rhs()); } /** * Unpack and interleave data from the HIGH half of batches \c lhs and \c rhs. * Store the results in the Return value. * @param lhs a batch of integer or floating point or double precision values. * @param rhs a batch of integer or floating point or double precision values. * @return a batch of the high part of shuffled values. */ template inline batch_type_t zip_hi(const simd_base& lhs, const simd_base& rhs) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::zip_hi(lhs(), rhs()); } /** * Extract vector from pair of vectors * extracts the lowest vector elements from the second source \c rhs * and the highest vector elements from the first source \c lhs * The index: 'n' specifies the lowest vector element to extract from the first source register. * Concatenates the results into th Return value. * @param lhs a batch of integer or floating point or double precision values. * @param rhs a batch of integer or floating point or double precision values. * @return. */ template inline batch_type_t extract_pair(const simd_base& lhs, const simd_base& rhs, const int index) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::extract_pair(lhs(), rhs(), index); } /** * Determines if the scalars in the given batch \c x are NaN values. * @param x batch of floating point values. * @return a batch of booleans. */ template inline typename simd_batch_traits::batch_bool_type isnan(const simd_base& x) { using value_type = typename simd_batch_traits::value_type; using kernel = detail::batch_kernel::size>; return kernel::isnan(x()); } /** * Insert the batch \c rhs into the stream \c out. * @tparam X the actual type of batch. * @param out the output stream. * @param rhs the batch to output. * @return the output stream. */ template inline std::ostream& operator<<(std::ostream& out, const simd_batch& rhs) { out << '('; std::size_t s = simd_batch::size; for (std::size_t i = 0; i < s - 1; ++i) { out << rhs()[i] << ", "; } out << rhs()[s - 1] << ')'; return out; } /****************************************** * generic batch operators implementation * ******************************************/ #define GENERIC_OPERATOR_IMPLEMENTATION(OP) \ using traits = simd_batch_traits>; \ constexpr std::size_t align = traits::align; \ alignas(align) T tmp_lhs[N]; \ alignas(align) T tmp_rhs[N]; \ alignas(align) T tmp_res[N]; \ lhs.store_aligned(tmp_lhs); \ rhs.store_aligned(tmp_rhs); \ for (std::size_t i = 0; i < traits::size; ++i) \ { \ tmp_res[i] = tmp_lhs[i] OP tmp_rhs[i]; \ } \ return batch(tmp_res, aligned_mode()) template inline batch operator&&(const batch& lhs, const batch& rhs) { GENERIC_OPERATOR_IMPLEMENTATION(&&); } template inline batch operator||(const batch& lhs, const batch& rhs) { GENERIC_OPERATOR_IMPLEMENTATION(||); } template inline batch operator<<(const batch& lhs, const batch& rhs) { GENERIC_OPERATOR_IMPLEMENTATION(<<); } template inline batch operator>>(const batch& lhs, const batch& rhs) { GENERIC_OPERATOR_IMPLEMENTATION(>>); } /***************************************** * batch cast functions implementation * *****************************************/ template inline batch batch_cast(const batch& x) { return batch_cast_impl::run(x); } /***************************************** * bitwise cast functions implementation * *****************************************/ template inline B bitwise_cast(const batch& x) { return bitwise_cast_impl, B>::run(x); } template inline B bitwise_cast(const batch& x) { return bitwise_cast_impl, B>::run(x); } template inline B bitwise_cast(const batch& x) { return bitwise_cast_impl, B>::run(x); } template inline B bitwise_cast(const batch& x) { return bitwise_cast_impl, B>::run(x); } template inline batch bitwise_cast(const batch_bool& src) { return batch(src.get_value()); } /*********************************** * Workaround for Clang on Windows * ***********************************/ #if defined(_WIN32) && defined(__clang__) /** * On Windows, the return type of fma is the promote type of its * arguments if they are integral or floating point types, float * otherwise. The implementation does not rely on SFINAE to * remove it from the overload resolution set when the argument * types are neither integral types nor floating point type. * * The fma overload defined xsimd accepts simd_base> * arguments, not batch. Thus a call to this latter is not * more specialized than a call to the STL overload, which is * considered. Since there is no mean to convert batch * to float for instance, this results in a compilation error. */ template inline batch fma(const batch& a, const batch& b, const batch& c) { using base_type = simd_base>; const base_type& sba = a; const base_type& sbb = b; const base_type& sbc = c; return fma(sba, sbb, sbc); } #endif } #endif