From afe91c84e49b5ba0a0da75612f9e207258887dd8 Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Wed, 5 Aug 2026 18:58:49 +0200 Subject: [PATCH 1/3] Migrate recursive template instantiations --- include/xsimd/arch/xsimd_avx512f.hpp | 26 +++++++++----------- include/xsimd/arch/xsimd_avx512vl_128.hpp | 2 +- include/xsimd/arch/xsimd_avx512vl_256.hpp | 2 +- include/xsimd/config/xsimd_arch.hpp | 21 ++-------------- include/xsimd/types/xsimd_batch_constant.hpp | 20 ++++----------- include/xsimd/utils/bits.hpp | 13 +++------- 6 files changed, 24 insertions(+), 60 deletions(-) diff --git a/include/xsimd/arch/xsimd_avx512f.hpp b/include/xsimd/arch/xsimd_avx512f.hpp index 658b7d448..8bfc1feb5 100644 --- a/include/xsimd/arch/xsimd_avx512f.hpp +++ b/include/xsimd/arch/xsimd_avx512f.hpp @@ -2258,7 +2258,7 @@ namespace xsimd 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++))... }; + ((r |= register_type(values ? 1 : 0) << (shift++)), ...); return r; } @@ -2656,21 +2656,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..c2894c818 100644 --- a/include/xsimd/arch/xsimd_avx512vl_128.hpp +++ b/include/xsimd/arch/xsimd_avx512vl_128.hpp @@ -176,7 +176,7 @@ namespace xsimd 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++))... }; + ((r |= register_type(values ? 1 : 0) << (shift++)), ...); return r; } diff --git a/include/xsimd/arch/xsimd_avx512vl_256.hpp b/include/xsimd/arch/xsimd_avx512vl_256.hpp index 3d58a67f5..3c3108d9c 100644 --- a/include/xsimd/arch/xsimd_avx512vl_256.hpp +++ b/include/xsimd/arch/xsimd_avx512vl_256.hpp @@ -176,7 +176,7 @@ namespace xsimd 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++))... }; + ((r |= register_type(values ? 1 : 0) << (shift++)), ...); return r; } 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..9cbe93b43 100644 --- a/include/xsimd/types/xsimd_batch_constant.hpp +++ b/include/xsimd/types/xsimd_batch_constant.hpp @@ -191,25 +191,15 @@ 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 + template + static constexpr uint64_t build_bits(std::index_sequence) noexcept { - static constexpr uint64_t value = (Current ? (uint64_t(1) << I) : 0u) - | build_bits_helper::value; - }; + return (uint64_t(0) | ... | (Values ? (uint64_t(1) << Is) : uint64_t(0))); + } static constexpr uint64_t bits() noexcept { - return build_bits_helper<0, Values...>::value; + return build_bits(std::make_index_sequence()); } 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..f789dbad3 100644 --- a/include/xsimd/utils/bits.hpp +++ b/include/xsimd/utils/bits.hpp @@ -19,20 +19,13 @@ namespace xsimd { namespace utils { - template - constexpr I make_bit_mask(I bit) - { - 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); - } - template constexpr I make_bit_mask(I bit, Args... bits) { - // 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)...); + [[maybe_unused]] constexpr I bit_count = static_cast(8 * sizeof(I)); + assert(((bit < bit_count) && ... && (static_cast(bits) < bit_count))); + return static_cast(((I { 1 } << bit) | ... | (I { 1 } << static_cast(bits)))); } template From 50806dbaa07aa2f9ea9ec8ee016138993c739f8a Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Mon, 10 Aug 2026 10:07:59 +0200 Subject: [PATCH 2/3] Address review --- include/xsimd/arch/xsimd_avx512f.hpp | 8 +++--- include/xsimd/arch/xsimd_avx512vl_128.hpp | 8 +++--- include/xsimd/arch/xsimd_avx512vl_256.hpp | 8 +++--- include/xsimd/utils/bits.hpp | 30 +++++++++++++++++------ test/test_utils_bits.cpp | 21 ++++++++++++++++ 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/include/xsimd/arch/xsimd_avx512f.hpp b/include/xsimd/arch/xsimd_avx512f.hpp index 8bfc1feb5..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; - ((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 diff --git a/include/xsimd/arch/xsimd_avx512vl_128.hpp b/include/xsimd/arch/xsimd_avx512vl_128.hpp index c2894c818..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; - ((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 3c3108d9c..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; - ((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/utils/bits.hpp b/include/xsimd/utils/bits.hpp index f789dbad3..4faa0d970 100644 --- a/include/xsimd/utils/bits.hpp +++ b/include/xsimd/utils/bits.hpp @@ -19,16 +19,32 @@ namespace xsimd { namespace utils { - template - constexpr I make_bit_mask(I bit, Args... bits) + template + constexpr auto make_bit_mask(Args... bits) + { + 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))); + } + + /** + * 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 { static_assert(std::is_unsigned_v, "Bit operations must be done on unsigned integers"); - [[maybe_unused]] constexpr I bit_count = static_cast(8 * sizeof(I)); - assert(((bit < bit_count) && ... && (static_cast(bits) < bit_count))); - return static_cast(((I { 1 } << bit) | ... | (I { 1 } << 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"); @@ -36,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)); From 8c930d0e60978fc2d9f08d1094076ea66fe4e44d Mon Sep 17 00:00:00 2001 From: AntoinePrv Date: Mon, 10 Aug 2026 11:49:01 +0200 Subject: [PATCH 3/3] More use of make_mask_from_bools --- include/xsimd/types/xsimd_batch_constant.hpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/include/xsimd/types/xsimd_batch_constant.hpp b/include/xsimd/types/xsimd_batch_constant.hpp index 9cbe93b43..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,15 +184,9 @@ namespace xsimd private: // Build a 64-bit mask from Values... (LSB = index 0) - template - static constexpr uint64_t build_bits(std::index_sequence) noexcept - { - return (uint64_t(0) | ... | (Values ? (uint64_t(1) << Is) : uint64_t(0))); - } - static constexpr uint64_t bits() noexcept { - return build_bits(std::make_index_sequence()); + return utils::make_bit_mask_from_bools(Values...); } static constexpr uint64_t low_mask(std::size_t k) noexcept {