diff --git a/include/xsimd/arch/xsimd_avx512f.hpp b/include/xsimd/arch/xsimd_avx512f.hpp index 658b7d448..e43a0d19c 100644 --- a/include/xsimd/arch/xsimd_avx512f.hpp +++ b/include/xsimd/arch/xsimd_avx512f.hpp @@ -14,6 +14,7 @@ #include "../types/xsimd_avx512f_register.hpp" #include "../types/xsimd_batch_constant.hpp" +#include "../utils/bits.hpp" #include #include @@ -2255,11 +2256,8 @@ namespace xsimd XSIMD_INLINE batch_bool set(batch_bool const&, requires_arch, Values... values) noexcept { static_assert(sizeof...(Values) == batch_bool::size, "consistent init"); - using register_type = typename batch_bool::register_type; - register_type r = 0; - unsigned shift = 0; - (void)std::initializer_list { (r |= register_type(values ? 1 : 0) << (shift++))... }; - return r; + using reg_t = typename batch_bool::register_type; + return ::xsimd::utils::make_bit_mask_from_bools(values...); } // shuffle @@ -2656,21 +2654,19 @@ namespace xsimd namespace detail { - template - struct is_pair_of_contiguous_indices; - - template - struct is_pair_of_contiguous_indices : std::true_type + template + constexpr bool is_pair_of_contiguous_indices_impl(std::index_sequence) noexcept { - }; + constexpr T idx[] = { Idx... }; + return (... && (idx[2 * Is] % 2 == 0 && idx[2 * Is] + 1 == idx[2 * Is + 1])); + } - template - struct is_pair_of_contiguous_indices : std::conditional_t<(Idx0 % 2 == 0) && (Idx0 + 1 == Idx1), is_pair_of_contiguous_indices, std::false_type> + template + constexpr bool is_pair_of_contiguous_indices() noexcept { - }; - - template - inline constexpr bool is_pair_of_contiguous_indices_v = is_pair_of_contiguous_indices::value; + static_assert(sizeof...(Idx) % 2 == 0, "indices come in pairs"); + return is_pair_of_contiguous_indices_impl(std::make_index_sequence()); + } template XSIMD_INLINE batch swizzle(batch const& self, batch_constant mask, requires_arch) noexcept { - if constexpr (detail::is_pair_of_contiguous_indices_v) + if constexpr (detail::is_pair_of_contiguous_indices()) { constexpr typename detail::fold_batch_constant::type mask32; return _mm512_permutexvar_epi32(static_cast>(mask32), self); diff --git a/include/xsimd/arch/xsimd_avx512vl_128.hpp b/include/xsimd/arch/xsimd_avx512vl_128.hpp index 9922182fc..3058bad45 100644 --- a/include/xsimd/arch/xsimd_avx512vl_128.hpp +++ b/include/xsimd/arch/xsimd_avx512vl_128.hpp @@ -15,6 +15,7 @@ #include "../types/xsimd_avx512vl_register.hpp" #include "../types/xsimd_batch_constant.hpp" +#include "../utils/bits.hpp" #include @@ -173,11 +174,8 @@ namespace xsimd XSIMD_INLINE batch_bool set(batch_bool const&, requires_arch, Values... values) noexcept { static_assert(sizeof...(Values) == batch_bool::size, "consistent init"); - using register_type = typename batch_bool::register_type; - register_type r = 0; - unsigned shift = 0; - (void)std::initializer_list { (r |= register_type(values ? 1 : 0) << (shift++))... }; - return r; + using reg_t = typename batch_bool::register_type; + return ::xsimd::utils::make_bit_mask_from_bools(values...); } // store diff --git a/include/xsimd/arch/xsimd_avx512vl_256.hpp b/include/xsimd/arch/xsimd_avx512vl_256.hpp index 3d58a67f5..2bfefcb13 100644 --- a/include/xsimd/arch/xsimd_avx512vl_256.hpp +++ b/include/xsimd/arch/xsimd_avx512vl_256.hpp @@ -15,6 +15,7 @@ #include "../types/xsimd_avx512vl_register.hpp" #include "../types/xsimd_batch_constant.hpp" +#include "../utils/bits.hpp" #include @@ -173,11 +174,8 @@ namespace xsimd XSIMD_INLINE batch_bool set(batch_bool const&, requires_arch, Values... values) noexcept { static_assert(sizeof...(Values) == batch_bool::size, "consistent init"); - using register_type = typename batch_bool::register_type; - register_type r = 0; - unsigned shift = 0; - (void)std::initializer_list { (r |= register_type(values ? 1 : 0) << (shift++))... }; - return r; + using reg_t = typename batch_bool::register_type; + return ::xsimd::utils::make_bit_mask_from_bools(values...); } // store diff --git a/include/xsimd/config/xsimd_arch.hpp b/include/xsimd/config/xsimd_arch.hpp index 329421833..ac5546bbc 100644 --- a/include/xsimd/config/xsimd_arch.hpp +++ b/include/xsimd/config/xsimd_arch.hpp @@ -16,7 +16,6 @@ #include "./xsimd_config.hpp" #include "./xsimd_cpuid.hpp" -#include #include #include @@ -40,22 +39,6 @@ namespace xsimd namespace detail { - // Checks whether T appears in Tys. - template - struct contains; - - template - struct contains : std::false_type - { - }; - - template - struct contains - : std::conditional_t, std::true_type, - contains> - { - }; - template XSIMD_INLINE constexpr T max_of(T value) noexcept { @@ -100,13 +83,13 @@ namespace xsimd template static constexpr bool contains() noexcept { - return detail::contains::value; + return (std::is_same_v || ...); } template static XSIMD_INLINE void for_each(F&& f) noexcept { - (void)std::initializer_list { (f(Archs {}), true)... }; + (f(Archs {}), ...); } static constexpr std::size_t alignment() noexcept diff --git a/include/xsimd/types/xsimd_batch_constant.hpp b/include/xsimd/types/xsimd_batch_constant.hpp index c6f045f85..d7a18069e 100644 --- a/include/xsimd/types/xsimd_batch_constant.hpp +++ b/include/xsimd/types/xsimd_batch_constant.hpp @@ -13,6 +13,7 @@ #define XSIMD_BATCH_CONSTANT_HPP #include "../config/xsimd_config.hpp" +#include "../utils/bits.hpp" #include "./xsimd_batch.hpp" #include "./xsimd_utils.hpp" @@ -61,7 +62,7 @@ namespace xsimd static constexpr int mask() noexcept { - return mask_helper(0, static_cast(Values)...); + return static_cast(bits()); } static constexpr bool none() noexcept @@ -128,14 +129,6 @@ namespace xsimd } private: - static constexpr int mask_helper(int acc) noexcept { return acc; } - - template - static constexpr int mask_helper(int acc, int mask, Tys... masks) noexcept - { - return mask_helper(acc | mask, (masks << 1)...); - } - struct logical_or { constexpr bool operator()(bool x, bool y) const { return x || y; } @@ -191,25 +184,9 @@ namespace xsimd private: // Build a 64-bit mask from Values... (LSB = index 0) - template - struct build_bits_helper; - - template - struct build_bits_helper - { - static constexpr uint64_t value = 0u; - }; - - template - struct build_bits_helper - { - static constexpr uint64_t value = (Current ? (uint64_t(1) << I) : 0u) - | build_bits_helper::value; - }; - static constexpr uint64_t bits() noexcept { - return build_bits_helper<0, Values...>::value; + return utils::make_bit_mask_from_bools(Values...); } static constexpr uint64_t low_mask(std::size_t k) noexcept { diff --git a/include/xsimd/utils/bits.hpp b/include/xsimd/utils/bits.hpp index 5d2efbc6d..4faa0d970 100644 --- a/include/xsimd/utils/bits.hpp +++ b/include/xsimd/utils/bits.hpp @@ -19,23 +19,32 @@ namespace xsimd { namespace utils { - template - constexpr I make_bit_mask(I bit) + template + constexpr auto make_bit_mask(Args... bits) { - static_assert(std::is_unsigned_v, "Bit operations must be done on unsigned integers"); - assert(bit < static_cast(8 * sizeof(I))); - return static_cast(I { 1 } << bit); + using out_type = std::common_type_t...>; + [[maybe_unused]] constexpr auto bit_count = static_cast(8 * sizeof(out_type)); + assert((((static_cast(bits) < bit_count) && ...))); + return static_cast((0u | ... | (1u << bits))); } - template - constexpr I make_bit_mask(I bit, Args... bits) + /** + * Return a mask whose bit `i` holds the truth value of the `i`th argument. + */ + template + constexpr I make_bit_mask_from_bools(Bools... bools) noexcept { - // TODO(C++17): Use fold expression static_assert(std::is_unsigned_v, "Bit operations must be done on unsigned integers"); - return make_bit_mask(bit) | make_bit_mask(static_cast(bits)...); + static_assert(sizeof...(Bools) <= 8 * sizeof(I), "Not enough bits to hold all the values"); + // GCC's -Wsequence-point does not model fold sequencing and rejects `shift++` inside + // the fold operand, hence the comma to separate read from increment. + I mask = 0; + unsigned shift = 0; + ((mask |= static_cast(static_cast(bools ? 1 : 0) << shift), ++shift), ...); + return mask; } - template + template constexpr bool all_bits_set(I value) { static_assert(std::is_unsigned_v, "Bit operations must be done on unsigned integers"); @@ -43,7 +52,7 @@ namespace xsimd return (value & mask) == mask; } - template + template constexpr I set_bit(I value) { static_assert(std::is_unsigned_v, "Bit operations must be done on unsigned integers"); diff --git a/test/test_utils_bits.cpp b/test/test_utils_bits.cpp index c1ac5af1b..b3de91987 100644 --- a/test/test_utils_bits.cpp +++ b/test/test_utils_bits.cpp @@ -17,6 +17,8 @@ TEST_CASE("[utils::make_bit_mask] single bit") { + CHECK_EQ(xsimd::utils::make_bit_mask(), 0x0); + CHECK_EQ(xsimd::utils::make_bit_mask(1), 0b0010); CHECK_EQ(xsimd::utils::make_bit_mask(0), 0x01); CHECK_EQ(xsimd::utils::make_bit_mask(7), 0x80); CHECK_EQ(xsimd::utils::make_bit_mask(0), 0x01u); @@ -29,6 +31,25 @@ TEST_CASE("[utils::make_bit_mask] multiple bits") CHECK_EQ(xsimd::utils::make_bit_mask(0, 2, 4), 0b00010101); } +TEST_CASE("[utils::make_bit_mask_from_bools]") +{ + CHECK_EQ(xsimd::utils::make_bit_mask_from_bools(), 0x00); + CHECK_EQ(xsimd::utils::make_bit_mask_from_bools(true), 0x01); + CHECK_EQ(xsimd::utils::make_bit_mask_from_bools(false), 0x00); + // First argument goes in the lowest bit + CHECK_EQ(xsimd::utils::make_bit_mask_from_bools(true, false, false), 0b001); + CHECK_EQ(xsimd::utils::make_bit_mask_from_bools(false, false, true), 0b100); + // Full width + CHECK_EQ( + xsimd::utils::make_bit_mask_from_bools(true, true, true, true, true, true, true, true), + 0xFF); + CHECK_EQ( + xsimd::utils::make_bit_mask_from_bools( + true, false, true, false, true, false, true, false, + true, false, true, false, true, false, true, false), + 0x5555); +} + TEST_CASE("[utils::all_bits_set] basic") { CHECK(xsimd::utils::all_bits_set<0>(0x01u));