Skip to content
Merged
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
11 changes: 11 additions & 0 deletions include/experimental/__p2630_bits/strided_slice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ struct extent_slice {
static_assert(detail::mdspan_is_index_like_v<StrideType>);
};

template<class FirstType, class LastType, class StrideType = detail::constant_wrapper<size_t(1)>>
struct range_slice {
MDSPAN_IMPL_NO_UNIQUE_ADDRESS FirstType first{};
MDSPAN_IMPL_NO_UNIQUE_ADDRESS LastType last{};
MDSPAN_IMPL_NO_UNIQUE_ADDRESS StrideType stride{};

static_assert(detail::mdspan_is_index_like_v<FirstType>);
static_assert(detail::mdspan_is_index_like_v<LastType>);
static_assert(detail::mdspan_is_index_like_v<StrideType>);
};

} // MDSPAN_IMPL_STANDARD_NAMESPACE
120 changes: 119 additions & 1 deletion include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

#pragma once

#include "submdspan_extents.hpp"
#include "constant_wrapper.hpp"
#include "strided_slice.hpp"
#include <complex>

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
Expand All @@ -12,6 +13,19 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE {

namespace detail {

// Helper for identifying extent_slice and range_slice
template <class T> struct is_extent_slice : std::false_type {};

template <class OffsetType, class ExtentType, class StrideType>
struct is_extent_slice<
extent_slice<OffsetType, ExtentType, StrideType>> : std::true_type {};

template <class T> struct is_range_slice : std::false_type {};

template<class FirstType, class LastType, class StrideType>
struct is_range_slice<
range_slice<FirstType, LastType, StrideType>> : std::true_type {};

// ============================================================
// de_ice: extract the value of an integral-constant-like type
// ============================================================
Expand Down Expand Up @@ -185,6 +199,36 @@ constexpr bool check_static_bounds()
else {
return true;
}
} else if constexpr (is_range_slice<S_k>::value) {
using first_type = decltype(std::declval<S_k>().first);
using last_type = decltype(std::declval<S_k>().last);

if constexpr (is_integral_constant_like_v<first_type>) {
if constexpr (de_ice(first_type{}) < 0) {
return false;
}
else if constexpr (
Exts_k != dynamic_extent &&
Exts_k < static_cast<size_t>(de_ice(first_type{})))
{
return false;
}
}

if constexpr (is_integral_constant_like_v<last_type>) {
if constexpr ((is_integral_constant_like_v<first_type> && (de_ice(first_type()) > de_ice(last_type{})))
|| de_ice(last_type{}) < 0) {
return false;
}
else if constexpr (
Exts_k != dynamic_extent &&
Exts_k < static_cast<size_t>(de_ice(last_type{})))
{
return false;
}
}

return true;
} else {
// General pair-like case: attempt to get the first and second elements.
// If S_k cannot be structured-bound into two elements, this is ill-formed,
Expand Down Expand Up @@ -270,6 +314,71 @@ constexpr bool check_submdspan_slice_mandates(
return (check_submdspan_slice_mandate<typename Extents::index_type, Extents::static_extent(Idx), Slices>(slices) && ... && true);
}


template<class IndexType, class SpanType, class StrideType>
struct calculate_extents
{
static constexpr IndexType value(SpanType span, StrideType stride)
{
return static_cast<IndexType>(span != 0 ? 1 + divide<IndexType>(span - 1, stride) : 0);
}
};

template<class IndexType, auto Span, auto Stride>
struct calculate_extents<IndexType, constant_wrapper<Span>, constant_wrapper<Stride>>
{
static constexpr auto value(constant_wrapper<Span>, constant_wrapper<Stride>)
{
return cw<static_cast<IndexType>(Span != 0 ? 1 + divide<IndexType>(Span - 1, Stride) : 0)>;
}
};


template<class IndexType, class StrideType>
struct calculate_stride
{
template< class SpanType >
static constexpr auto value(SpanType span, StrideType stride)
{
return span != 0 ? stride : IndexType(1);
}
};

template<class IndexType, auto Stride>
struct calculate_stride<IndexType, constant_wrapper<Stride>>
{
template< class SpanType >
static constexpr auto value(SpanType, constant_wrapper<Stride>)
{
return cw<Stride>;
}
};


// Some compilers do not do ctad properly with canonical_range_slice
template<class OffsetType, class ExtentType, class StrideType>
constexpr extent_slice<OffsetType, ExtentType, StrideType>
make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride)
{
return extent_slice<OffsetType, ExtentType, StrideType>{offset, extent, stride};
}
Comment on lines +358 to +364

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an optional suggestion -- sometimes return type deduction can slow down builds.

Suggested change
// Some compilers do not do ctad properly with canonical_range_slice
template<class OffsetType, class ExtentType, class StrideType>
constexpr auto make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride)
{
return extent_slice<OffsetType, ExtentType, StrideType>{offset, extent, stride};
}
// Some compilers do not do ctad properly with canonical_range_slice
template<class OffsetType, class ExtentType, class StrideType>
constexpr extent_slice<OffsetType, ExtentType, StrideType>
make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride)
{
return {offset, extent, stride};
}


template<class IndexType, class OffsetType, class SpanType, class StrideType>
constexpr auto canonical_range_slice(OffsetType offset, SpanType span, StrideType stride)
{
return make_extent_slice(
offset,
calculate_extents<IndexType, SpanType, StrideType>::value(span, stride),
calculate_stride<IndexType, StrideType>::value(span, stride)
);
}

template<class IndexType, class OffsetType, class SpanType>
constexpr auto canonical_range_slice(OffsetType offset, SpanType span)
{
return canonical_range_slice<IndexType>(offset, span, cw<IndexType(1)>);
}

// ============================================================
// canonical_slice: canonicalize a single slice
//
Expand Down Expand Up @@ -301,6 +410,15 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s)
/* .extent = */ extent,
/* .stride = */ stride
};
}
else if constexpr (is_range_slice<Slice>::value) {
auto c_first = canonical_index<IndexType>(std::move(s.first));
auto c_last = canonical_index<IndexType>(std::move(s.last));
return canonical_range_slice<IndexType>(
c_first,
subtract_ice<IndexType>(c_first, c_last),
canonical_index<IndexType>(std::move(s.stride))
);
} else {
// General pair-like case: structured binding into [first, last)
auto [s_k0, s_k1] = std::move(s);
Expand Down
10 changes: 2 additions & 8 deletions include/experimental/__p2630_bits/submdspan_extents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "constant_wrapper.hpp"
#include "strided_slice.hpp"
#include "../__p0009_bits/utility.hpp"
#include "submdspan_canonicalize_slices.hpp"

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
namespace detail {
Expand Down Expand Up @@ -35,13 +36,6 @@ constexpr auto inv_map_rank(std::integral_constant<size_t, Counter>, std::index_
slices...);
}

// Helper for identifying strided_slice
template <class T> struct is_extent_slice : std::false_type {};

template <class OffsetType, class ExtentType, class StrideType>
struct is_extent_slice<
extent_slice<OffsetType, ExtentType, StrideType>> : std::true_type {};

// first_of(slice): getting begin of slice specifier range
MDSPAN_TEMPLATE_REQUIRES(
class Integral,
Expand Down Expand Up @@ -275,6 +269,6 @@ constexpr auto submdspan_extents(const extents<IndexType, Extents...> &src_exts,

using ext_t = extents<IndexType, Extents...>;
return detail::extents_constructor<ext_t::rank(), ext_t>::next_extent(
src_exts, slices...);
src_exts, detail::canonical_slice<IndexType>(slices)...);
}
} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE
9 changes: 9 additions & 0 deletions include/experimental/__p2630_bits/submdspan_mapping.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ constexpr bool is_range_like_slice_v<
IndexType
> = (constant_wrapper<Stride>::value == IndexType(1));

template<class FirstType, class LastType, auto Stride, class IndexType>
constexpr bool is_range_like_slice_v<
range_slice<
FirstType,
LastType,
constant_wrapper<Stride>>,
IndexType
> = (constant_wrapper<Stride>::value == IndexType(1));

template<class SliceSpecifier, class IndexType>
struct is_index_slice {
constexpr static bool value = std::is_convertible_v<SliceSpecifier, IndexType>;
Expand Down
47 changes: 44 additions & 3 deletions tests/test_canonicalize_slices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,11 +276,11 @@ TEST(CanonicalizeSlices, Rank2_full) {
MDSPAN_IMPL_STANDARD_NAMESPACE::extents<int, 11, 13>{}, full, full);
}

TEST(CanonicalizeSlices, Rank1_strided_slice_dynamic) {
TEST(CanonicalizeSlices, Rank1_extent_slice_dynamic) {
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple;

// strided_slice with all dynamic values
// extent_slice with all dynamic values
const auto slice0 = MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice<int, int, int>{
/* .offset = */ 1,
/* .extent = */ 4,
Expand All @@ -296,7 +296,27 @@ TEST(CanonicalizeSlices, Rank1_strided_slice_dynamic) {
test_canonicalize_slices(expected_slices, exts, slice0);
}

TEST(CanonicalizeSlices, Rank1_strided_slice_static) {
TEST(CanonicalizeSlices, Rank1_range_slice_dynamic) {
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple;

// range_slice with all dynamic values
const auto slice0 = MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice<int, int, int>{
/* .first = */ 1,
/* .last = */ 11,
/* .stride = */ 3
};

const auto expected_slices = tuple{
MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice<size_t, size_t, size_t>{
size_t(1), size_t(4), size_t(3)
}
};
const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents<size_t, 11>{};
test_canonicalize_slices(expected_slices, exts, slice0);
}

TEST(CanonicalizeSlices, Rank1_extent_slice_static) {
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple;

Expand All @@ -317,4 +337,25 @@ TEST(CanonicalizeSlices, Rank1_strided_slice_static) {
test_canonicalize_slices(expected_slices, exts, slice0);
}

TEST(CanonicalizeSlices, Rank1_range_slice_static) {
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;
using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple;

using offset_t = std::integral_constant<int, 1>;
using extent_t = std::integral_constant<int, 11>;
using stride_t = std::integral_constant<int, 3>;
const auto slice0 =
MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice<offset_t, extent_t, stride_t>{};

const auto expected_slices = tuple{
MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice<
decltype(cw<size_t(1)>),
decltype(cw<size_t(4)>),
decltype(cw<size_t(3)>)
>{cw<size_t(1)>, cw<size_t(4)>, cw<size_t(3)>}
};
const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents<size_t, 11>{};
test_canonicalize_slices(expected_slices, exts, slice0);
}

} // namespace (anonymous)
63 changes: 50 additions & 13 deletions tests/test_strided_slice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace {

template<class OffsetType, class ExtentType, class StrideType>
void test_strided_slice(OffsetType offset, ExtentType extent, StrideType stride)
void test_extent_slice(OffsetType offset, ExtentType extent, StrideType stride)
{
// Some compilers are bad at CTAD for aggregates.
Kokkos::extent_slice<OffsetType, ExtentType, StrideType> s{offset, extent, stride};
Expand Down Expand Up @@ -60,23 +60,60 @@ static_assert(
my_integral_constant<int, 1>
>);

TEST(StridedSlice, WellFormed) {
test_strided_slice(int(1), unsigned(10), long(3));
test_strided_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3));
TEST(ExtentSlice, WellFormed) {
test_extent_slice(int(1), unsigned(10), long(3));
test_extent_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3));

test_strided_slice(IC<int, 1>, unsigned(10), long(3));
test_strided_slice(int(1), IC<unsigned, 10>, long(3));
test_strided_slice(int(1), unsigned(10), IC<long, 3>);
test_extent_slice(IC<int, 1>, unsigned(10), long(3));
test_extent_slice(int(1), IC<unsigned, 10>, long(3));
test_extent_slice(int(1), unsigned(10), IC<long, 3>);

using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;

test_strided_slice(cw<1>, unsigned(10), long(3));
test_strided_slice(int(1), cw<unsigned(10)>, long(3));
test_strided_slice(int(1), unsigned(10), cw<long(3)>);
test_extent_slice(cw<1>, unsigned(10), long(3));
test_extent_slice(int(1), cw<unsigned(10)>, long(3));
test_extent_slice(int(1), unsigned(10), cw<long(3)>);

test_strided_slice(IC2<int, 1>, unsigned(10), long(3));
test_strided_slice(int(1), IC2<unsigned, 10>, long(3));
test_strided_slice(int(1), unsigned(10), IC2<long, 3>);
test_extent_slice(IC2<int, 1>, unsigned(10), long(3));
test_extent_slice(int(1), IC2<unsigned, 10>, long(3));
test_extent_slice(int(1), unsigned(10), IC2<long, 3>);
}

template<class FirstType, class LastType, class StrideType>
void test_range_slice(FirstType first, LastType last, StrideType stride)
{
// Some compilers are bad at CTAD for aggregates.
Kokkos::range_slice<FirstType, LastType, StrideType> s{first, last, stride};

auto first2 = s.first;
static_assert(std::is_same_v<FirstType, decltype(first2)>);
auto last2 = s.last;
static_assert(std::is_same_v<LastType, decltype(last2)>);
auto stride2 = s.stride;
static_assert(std::is_same_v<StrideType, decltype(stride2)>);

ASSERT_EQ(first2, first);
ASSERT_EQ(last2, last);
ASSERT_EQ(stride2, stride);
}

TEST(RangeSlice, WellFormed) {
test_range_slice(int(1), unsigned(10), long(3));
test_range_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3));

test_range_slice(IC<int, 1>, unsigned(10), long(3));
test_range_slice(int(1), IC<unsigned, 10>, long(3));
test_range_slice(int(1), unsigned(10), IC<long, 3>);

using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw;

test_range_slice(cw<1>, unsigned(10), long(3));
test_range_slice(int(1), cw<unsigned(10)>, long(3));
test_range_slice(int(1), unsigned(10), cw<long(3)>);

test_range_slice(IC2<int, 1>, unsigned(10), long(3));
test_range_slice(int(1), IC2<unsigned, 10>, long(3));
test_range_slice(int(1), unsigned(10), IC2<long, 3>);
}

} // namespace (anonymous)
Loading
Loading