Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 29 additions & 136 deletions include/xsimd/arch/common/xsimd_common_swizzle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "../../config/xsimd_macros.hpp"

#include <cstddef>
#include <cstdint>
#include <type_traits>

namespace xsimd
Expand All @@ -27,146 +26,46 @@ namespace xsimd
{
namespace detail
{
// ────────────────────────────────────────────────────────────────────────
// get_at<I,Values...> → the I-th element of the pack
template <typename T, std::size_t I, T V0, T... Vs>
struct get_at
{
static constexpr T value = get_at<T, I - 1, Vs...>::value;
};
template <typename T, T V0, T... Vs>
struct get_at<T, 0, V0, Vs...>
{
static constexpr T value = V0;
};

// ────────────────────────────────────────────────────────────────────────
// identity_impl
template <std::size_t /*I*/, typename T>
XSIMD_INLINE constexpr bool identity_impl() noexcept { return true; }
template <std::size_t I, typename T, T V0, T... Vs>
XSIMD_INLINE constexpr bool identity_impl() noexcept
// v[i] == i for every i
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_identity() noexcept
{
return V0 == static_cast<T>(I)
&& identity_impl<I + 1, T, Vs...>();
std::size_t i = 0;
return ((Vs == static_cast<T>(i++)) && ...);
}

// ────────────────────────────────────────────────────────────────────────
// dup_lo_impl
template <std::size_t I, std::size_t N, typename T,
T... Vs, std::enable_if_t<I == N / 2, int> = 0>
XSIMD_INLINE constexpr bool dup_lo_impl() noexcept { return true; }

template <std::size_t I, std::size_t N, typename T,
T... Vs, std::enable_if_t<(I < N / 2), int> = 0>
XSIMD_INLINE constexpr bool dup_lo_impl() noexcept
// every index points into the low / high half
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_only_from_lo() noexcept
{
return get_at<T, I, Vs...>::value < static_cast<T>(N / 2)
&& get_at<T, I + N / 2, Vs...>::value == get_at<T, I, Vs...>::value
&& dup_lo_impl<I + 1, N, T, Vs...>();
return ((Vs < static_cast<T>(sizeof...(Vs) / 2)) && ...);
}

// ────────────────────────────────────────────────────────────────────────
// dup_hi_impl
template <std::size_t I, std::size_t N, typename T,
T... Vs, std::enable_if_t<I == N / 2, int> = 0>
XSIMD_INLINE constexpr bool dup_hi_impl() noexcept { return true; }

template <std::size_t I, std::size_t N, typename T,
T... Vs, std::enable_if_t<(I < N / 2), int> = 0>
XSIMD_INLINE constexpr bool dup_hi_impl() noexcept
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_only_from_hi() noexcept
{
return get_at<T, I, Vs...>::value >= static_cast<T>(N / 2)
&& get_at<T, I, Vs...>::value < static_cast<T>(N)
&& get_at<T, I + N / 2, Vs...>::value == get_at<T, I, Vs...>::value
&& dup_hi_impl<I + 1, N, T, Vs...>();
return ((Vs >= static_cast<T>(sizeof...(Vs) / 2)) && ...);
}

// ────────────────────────────────────────────────────────────────────────
// only_from_lo
template <typename T, T Size, T First, T... Vals>
struct only_from_lo_impl;

template <typename T, T Size, T Last>
struct only_from_lo_impl<T, Size, Last>
{
static constexpr bool value = (Last < (Size / 2));
};

template <typename T, T Size, T First, T... Vals>
struct only_from_lo_impl
{
static constexpr bool value = (First < (Size / 2)) && only_from_lo_impl<T, Size, Vals...>::value;
};

template <typename T, T... Vals>
constexpr bool is_only_from_lo()
{
return only_from_lo_impl<T, sizeof...(Vals), Vals...>::value;
};

// ────────────────────────────────────────────────────────────────────────
// only_from_hi
template <typename T, T Size, T First, T... Vals>
struct only_from_hi_impl;

template <typename T, T Size, T Last>
struct only_from_hi_impl<T, Size, Last>
{
static constexpr bool value = (Last >= (Size / 2));
};

template <typename T, T Size, T First, T... Vals>
struct only_from_hi_impl
{
static constexpr bool value = (First >= (Size / 2)) && only_from_hi_impl<T, Size, Vals...>::value;
};

template <typename T, T... Vals>
constexpr bool is_only_from_hi()
{
return only_from_hi_impl<T, sizeof...(Vals), Vals...>::value;
};

// ────────────────────────────────────────────────────────────────────────
// 1) helper to get the I-th value from the Vs pack
template <std::size_t I, uint32_t Head, uint32_t... Tail>
struct get_nth_value
{
static constexpr uint32_t value = get_nth_value<I - 1, Tail...>::value;
};
template <uint32_t Head, uint32_t... Tail>
struct get_nth_value<0, Head, Tail...>
{
static constexpr uint32_t value = Head;
};

// ────────────────────────────────────────────────────────────────────────
// 2) recursive cross‐lane test: true if any output‐lane i pulls from the opposite half
template <std::size_t I,
std::size_t N,
std::size_t H,
uint32_t... Vs>
struct cross_impl
{
// does element I cross? (i.e. i<H but V>=H) or (i>=H but V<H)
static constexpr uint32_t Vi = get_nth_value<I, Vs...>::value;
static constexpr bool curr = (I < H ? (Vi >= H) : (Vi < H));
static constexpr bool next = cross_impl<I + 1, N, H, Vs...>::value;
static constexpr bool value = curr || next;
};
template <std::size_t N, std::size_t H, uint32_t... Vs>
struct cross_impl<N, N, H, Vs...>
{
static constexpr bool value = false;
};
template <uint32_t... Vs>
XSIMD_INLINE constexpr bool is_cross_lane() noexcept
{
static_assert(sizeof...(Vs) >= 1, "Need at least one lane");
return cross_impl<0, sizeof...(Vs), sizeof...(Vs) / 2, Vs...>::value;
// both halves read the same indices, all taken from the Hi ? high : low half
template <typename T, bool Hi, T... Vs>
XSIMD_INLINE constexpr bool is_dup_from_half() noexcept
{
constexpr std::size_t half = sizeof...(Vs) / 2;
constexpr T lo = Hi ? static_cast<T>(half) : T(0);
constexpr T hi = Hi ? static_cast<T>(sizeof...(Vs)) : static_cast<T>(half);
constexpr T v[] = { Vs... };
for (std::size_t i = 0; i < half; ++i)
if (v[i] < lo || v[i] >= hi || v[i + half] != v[i])
return false;
return true;
}

template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_dup_lo() noexcept { return is_dup_from_half<T, false, Vs...>(); }
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_dup_hi() noexcept { return is_dup_from_half<T, true, Vs...>(); }

/**
* @brief Internal: Check if a swizzle pattern crosses lane boundaries
*
Expand Down Expand Up @@ -202,12 +101,6 @@ namespace xsimd
return false;
}

template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_identity() noexcept { return detail::identity_impl<0, T, Vs...>(); }
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_dup_lo() noexcept { return detail::dup_lo_impl<0, sizeof...(Vs), T, Vs...>(); }
template <typename T, T... Vs>
XSIMD_INLINE constexpr bool is_dup_hi() noexcept { return detail::dup_hi_impl<0, sizeof...(Vs), T, Vs...>(); }
template <typename T, class A, T... Vs>
XSIMD_INLINE constexpr bool is_identity(batch_constant<T, A, Vs...>) noexcept { return is_identity<T, Vs...>(); }
template <typename T, class A, T... Vs>
Expand Down
16 changes: 16 additions & 0 deletions test/test_batch_manip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ namespace xsimd
// 4-lane dup-hi (repeat 2..3 twice)
static_assert(is_dup_hi<std::uint32_t, 2, 3, 2, 3>(), "4-lane dup_hi failed");
static_assert(!is_dup_lo<std::uint32_t, 2, 3, 2, 3>(), "4-lane dup_lo on dup_hi");
// ────────────────────────────────────────────────────────────────────────
// narrow and signed index types, as used by the sse2 / avx / avx512bw kernels
static_assert(is_identity<std::uint16_t, 0, 1, 2, 3, 4, 5, 6, 7>(), "uint16_t identity failed");
static_assert(is_dup_lo<std::uint16_t, 0, 3, 1, 2, 0, 3, 1, 2>(), "uint16_t dup_lo failed");
static_assert(is_dup_hi<std::uint16_t, 7, 4, 4, 5, 7, 4, 4, 5>(), "uint16_t dup_hi failed");
static_assert(is_identity<std::uint8_t, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15>(), "uint8_t identity failed");
static_assert(is_dup_hi<std::int8_t, 4, 7, 5, 5, 4, 7, 5, 5>(), "int8_t dup_hi failed");
static_assert(!is_dup_hi<std::int8_t, 4, 7, 5, 5, 4, 7, 5, 4>(), "int8_t dup_hi on non-dup");
static_assert(!is_dup_hi<std::int8_t, 4, 7, 5, 8, 4, 7, 5, 8>(), "int8_t dup_hi with out-of-range index");
static_assert(is_only_from_lo<std::int64_t, 0, 1, 1, 0>(), "int64_t only_from_lo failed");
static_assert(is_only_from_hi<std::int64_t, 2, 3, 3, 2>(), "int64_t only_from_hi failed");
// degenerate pack sizes
static_assert(is_identity<std::uint32_t, 0>(), "1-lane identity failed");
static_assert(!is_identity<std::uint32_t, 1>(), "1-lane identity on non-zero");
static_assert(is_dup_lo<std::uint32_t, 0, 0>(), "2-lane dup_lo failed");
static_assert(is_dup_hi<std::uint32_t, 1, 1>(), "2-lane dup_hi failed");

static_assert(is_cross_lane<double, 0, 1, 0, 1>(), "dup-lo only → crossing");
static_assert(is_cross_lane<double, 2, 3, 2, 3>(), "dup-hi only → crossing");
Expand Down
Loading