/*************************************************************************** * 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_UTILS_HPP #define XSIMD_UTILS_HPP #include #include #include #ifdef XSIMD_ENABLE_XTL_COMPLEX #include "xtl/xcomplex.hpp" #endif /* For Shift instruction: vshlq_n_u8/vshrq_n_u8 (lhs, n), * 'n' must be a constant and is the compile-time literal constant. * * This Macro is to fix compiling issues from llvm(clang): * "argument must be a constant..." * */ #define EXPAND(...) __VA_ARGS__ #define CASE_LHS(op, i) \ case i: return op(lhs, i); #define XSIMD_REPEAT_8_0(op, addx) \ CASE_LHS(EXPAND(op), 1 + addx); \ CASE_LHS(EXPAND(op), 2 + addx); \ CASE_LHS(EXPAND(op), 3 + addx); \ CASE_LHS(EXPAND(op), 4 + addx); \ CASE_LHS(EXPAND(op), 5 + addx); \ CASE_LHS(EXPAND(op), 6 + addx); \ CASE_LHS(EXPAND(op), 7 + addx); #define XSIMD_REPEAT_8_N(op, addx) \ CASE_LHS(EXPAND(op), 0 + addx); \ XSIMD_REPEAT_8_0(op, addx); #define XSIMD_REPEAT_8(op) \ XSIMD_REPEAT_8_0(op, 0); #define XSIMD_REPEAT_16_0(op, addx) \ XSIMD_REPEAT_8_0(op, 0 + addx); \ XSIMD_REPEAT_8_N(op, 8 + addx); #define XSIMD_REPEAT_16_N(op, addx) \ XSIMD_REPEAT_8_N(op, 0 + addx); \ XSIMD_REPEAT_8_N(op, 8 + addx); #define XSIMD_REPEAT_16(op) \ XSIMD_REPEAT_16_0(op, 0); #define XSIMD_REPEAT_32_0(op, addx) \ XSIMD_REPEAT_16_0(op, 0 + addx); \ XSIMD_REPEAT_16_N(op, 16 + addx); #define XSIMD_REPEAT_32_N(op, addx) \ XSIMD_REPEAT_16_N(op, 0 + addx); \ XSIMD_REPEAT_16_N(op, 16 + addx); #define XSIMD_REPEAT_32(op) \ XSIMD_REPEAT_32_0(op, 0); #define XSIMD_REPEAT_64(op) \ XSIMD_REPEAT_32_0(op, 0); \ XSIMD_REPEAT_32_N(op, 32); /* The Macro is for vext (lhs, rhs, n) * * _mm_alignr_epi8, _mm_alignr_epi32 ... */ #define CASE_LHS_RHS(op, i) \ case i: return op(lhs, rhs, i); #define XSIMD_REPEAT_2_0(op, addx) \ CASE_LHS_RHS(EXPAND(op), 1 + addx); #define XSIMD_REPEAT_2_N(op, addx) \ CASE_LHS_RHS(EXPAND(op), 0 + addx); \ XSIMD_REPEAT_2_0(op, addx); #define XSIMD_REPEAT_2(op) \ XSIMD_REPEAT_2_0(op, 0); #define XSIMD_REPEAT_4_0(op, addx) \ XSIMD_REPEAT_2_0(op, 0 + addx); \ XSIMD_REPEAT_2_N(op, 2 + addx); #define XSIMD_REPEAT_4_N(op, addx) \ XSIMD_REPEAT_2_N(op, 0 + addx); \ XSIMD_REPEAT_2_N(op, 2 + addx); #define XSIMD_REPEAT_4(op) \ XSIMD_REPEAT_4_0(op, 0); #define XSIMD_REPEAT_8_0_v2(op, addx) \ XSIMD_REPEAT_4_0(op, 0 + addx); \ XSIMD_REPEAT_4_N(op, 4 + addx); #define XSIMD_REPEAT_8_N_v2(op, addx) \ XSIMD_REPEAT_4_N(op, 0 + addx); \ XSIMD_REPEAT_4_N(op, 4 + addx); #define XSIMD_REPEAT_8_v2(op) \ XSIMD_REPEAT_8_0_v2(op, 0); #define XSIMD_REPEAT_16_0_v2(op, addx) \ XSIMD_REPEAT_8_0_v2(op, 0 + addx); \ XSIMD_REPEAT_8_N_v2(op, 8 + addx); #define XSIMD_REPEAT_16_N_v2(op, addx) \ XSIMD_REPEAT_8_N_v2(op, 0 + addx); \ XSIMD_REPEAT_8_N_v2(op, 8 + addx); #define XSIMD_REPEAT_16_v2(op) \ XSIMD_REPEAT_16_0_v2(op, 0); #define XSIMD_REPEAT_32_0_v2(op, addx) \ XSIMD_REPEAT_16_0_v2(op, 0 + addx); \ XSIMD_REPEAT_16_N_v2(op, 16 + addx); #define XSIMD_REPEAT_32_N_v2(op, addx) \ XSIMD_REPEAT_16_N_v2(op, 0 + addx); \ XSIMD_REPEAT_16_N_v2(op, 16 + addx); #define XSIMD_REPEAT_32_v2(op) \ XSIMD_REPEAT_32_0_v2(op, 0); #define XSIMD_REPEAT_64_v2(op) \ XSIMD_REPEAT_32_0_v2(op, 0); \ XSIMD_REPEAT_32_N_v2(op, 32); namespace xsimd { template class batch; template class batch_bool; /************** * as_integer * **************/ template struct as_integer : std::make_signed { }; template <> struct as_integer { using type = int32_t; }; template <> struct as_integer { using type = int64_t; }; template struct as_integer> { using type = batch::type, N>; }; template using as_integer_t = typename as_integer::type; /*********************** * as_unsigned_integer * ***********************/ template struct as_unsigned_integer : std::make_unsigned { }; template <> struct as_unsigned_integer { using type = uint32_t; }; template <> struct as_unsigned_integer { using type = uint64_t; }; template struct as_unsigned_integer> { using type = batch::type, N>; }; template using as_unsigned_integer_t = typename as_unsigned_integer::type; /****************** * flip_sign_type * ******************/ namespace detail { template struct flipped_sign_type_impl : std::make_signed { }; template struct flipped_sign_type_impl : std::make_unsigned { }; } template struct flipped_sign_type : detail::flipped_sign_type_impl::value> { }; template using flipped_sign_type_t = typename flipped_sign_type::type; /*********** * as_float * ************/ template struct as_float; template <> struct as_float { using type = float; }; template <> struct as_float { using type = double; }; template struct as_float> { using type = batch::type, N>; }; template using as_float_t = typename as_float::type; /************** * as_logical * **************/ template struct as_logical; template struct as_logical> { using type = batch_bool; }; template using as_logical_t = typename as_logical::type; /******************** * primitive caster * ********************/ namespace detail { template union generic_caster { UI ui; I i; F f; constexpr generic_caster(UI t) : ui(t) {} constexpr generic_caster(I t) : i(t) {} constexpr generic_caster(F t) : f(t) {} }; using caster32_t = generic_caster; using caster64_t = generic_caster; template struct caster; template <> struct caster { using type = caster32_t; }; template <> struct caster { using type = caster64_t; }; template using caster_t = typename caster::type; } /**************************** * to/from_unsigned_integer * ****************************/ namespace detail { template union unsigned_convertor { T data; as_unsigned_integer_t bits; }; template as_unsigned_integer_t to_unsigned_integer(const T& input) { unsigned_convertor convertor; convertor.data = input; return convertor.bits; } template T from_unsigned_integer(const as_unsigned_integer_t& input) { unsigned_convertor convertor; convertor.bits = input; return convertor.data; } } /***************************************** * Backport of index_sequence from c++14 * *****************************************/ // TODO: Remove this once we drop C++11 support namespace detail { template struct identity { using type = T; }; #ifdef __cpp_lib_integer_sequence using std::integer_sequence; using std::index_sequence; using std::make_index_sequence; using std::index_sequence_for; #else template struct integer_sequence { using value_type = T; static constexpr std::size_t size() noexcept { return sizeof...(Is); } }; template using index_sequence = integer_sequence; template struct make_index_sequence_concat; template struct make_index_sequence_concat, index_sequence> : identity> {}; template struct make_index_sequence_impl; template using make_index_sequence = typename make_index_sequence_impl::type; template struct make_index_sequence_impl : make_index_sequence_concat, make_index_sequence> {}; template <> struct make_index_sequence_impl<0> : identity> {}; template <> struct make_index_sequence_impl<1> : identity> {}; template using index_sequence_for = make_index_sequence; #endif } #define XSIMD_MACRO_UNROLL_BINARY(FUNC) \ constexpr std::size_t size = simd_batch_traits::size; \ using tmp_value_type = typename simd_batch_traits::value_type; \ alignas(simd_batch_traits::align) tmp_value_type tmp_lhs[size], tmp_rhs[size], tmp_res[size]; \ lhs.store_aligned(tmp_lhs); \ rhs.store_aligned(tmp_rhs); \ unroller([&](std::size_t i) { \ tmp_res[i] = tmp_lhs[i] FUNC tmp_rhs[i]; \ }); \ return batch_type(&tmp_res[0], aligned_mode()); template inline void unroller_impl(F&& f, detail::index_sequence) { static_cast(std::initializer_list{(f(I), 0)...}); } template inline void unroller(F&& f) { unroller_impl(f, detail::make_index_sequence{}); } /***************************************** * Supplementary std::array constructors * *****************************************/ namespace detail { // std::array constructor from scalar value ("broadcast") template constexpr std::array array_from_scalar_impl(const T& scalar, index_sequence) { // You can safely ignore this silly ternary, the "scalar" is all // that matters. The rest is just a dirty workaround... return std::array{ (Is+1) ? scalar : T() ... }; } template constexpr std::array array_from_scalar(const T& scalar) { return array_from_scalar_impl(scalar, make_index_sequence()); } // std::array constructor from C-style pointer (handled as an array) template constexpr std::array array_from_pointer_impl(const T* c_array, index_sequence) { return std::array{ c_array[Is]... }; } template constexpr std::array array_from_pointer(const T* c_array) { return array_from_pointer_impl(c_array, make_index_sequence()); } } /************************ * is_array_initializer * ************************/ namespace detail { template struct bool_pack; template using all_true = std::is_same< bool_pack, bool_pack >; template using is_all_convertible = all_true::value...>; template using is_array_initializer = std::enable_if< (sizeof...(Args) == N) && is_all_convertible::value >; // Check that a variadic argument pack is a list of N values of type T, // as usable for instantiating a value of type std::array. template using is_array_initializer_t = typename is_array_initializer::type; } /************** * is_complex * **************/ // This is used in both xsimd_complex_base.hpp and xsimd_traits.hpp // However xsimd_traits.hpp indirectly includes xsimd_complex_base.hpp // so we cannot define is_complex in xsimd_traits.hpp. Besides, if // no file defining batches is included, we still need this definition // in xsimd_traits.hpp, so let's define it here. namespace detail { template struct is_complex : std::false_type { }; template struct is_complex> : std::true_type { }; #ifdef XSIMD_ENABLE_XTL_COMPLEX template struct is_complex> : std::true_type { }; #endif } } #endif