diff --git a/include/experimental/__p2630_bits/strided_slice.hpp b/include/experimental/__p2630_bits/strided_slice.hpp index 538b7a55..57ca5aba 100644 --- a/include/experimental/__p2630_bits/strided_slice.hpp +++ b/include/experimental/__p2630_bits/strided_slice.hpp @@ -51,4 +51,15 @@ struct extent_slice { static_assert(detail::mdspan_is_index_like_v); }; +template> +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); + static_assert(detail::mdspan_is_index_like_v); + static_assert(detail::mdspan_is_index_like_v); +}; + } // MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index fad36410..bae74f29 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -3,7 +3,8 @@ #pragma once -#include "submdspan_extents.hpp" +#include "constant_wrapper.hpp" +#include "strided_slice.hpp" #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -12,6 +13,19 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { +// Helper for identifying extent_slice and range_slice +template struct is_extent_slice : std::false_type {}; + +template +struct is_extent_slice< + extent_slice> : std::true_type {}; + +template struct is_range_slice : std::false_type {}; + +template +struct is_range_slice< + range_slice> : std::true_type {}; + // ============================================================ // de_ice: extract the value of an integral-constant-like type // ============================================================ @@ -185,6 +199,36 @@ constexpr bool check_static_bounds() else { return true; } + } else if constexpr (is_range_slice::value) { + using first_type = decltype(std::declval().first); + using last_type = decltype(std::declval().last); + + if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(first_type{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(first_type{}))) + { + return false; + } + } + + if constexpr (is_integral_constant_like_v) { + if constexpr ((is_integral_constant_like_v && (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(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, @@ -270,6 +314,71 @@ constexpr bool check_submdspan_slice_mandates( return (check_submdspan_slice_mandate(slices) && ... && true); } + +template +struct calculate_extents +{ + static constexpr IndexType value(SpanType span, StrideType stride) + { + return static_cast(span != 0 ? 1 + divide(span - 1, stride) : 0); + } +}; + +template +struct calculate_extents, constant_wrapper> +{ + static constexpr auto value(constant_wrapper, constant_wrapper) + { + return cw(Span != 0 ? 1 + divide(Span - 1, Stride) : 0)>; + } +}; + + +template +struct calculate_stride +{ + template< class SpanType > + static constexpr auto value(SpanType span, StrideType stride) + { + return span != 0 ? stride : IndexType(1); + } +}; + +template +struct calculate_stride> +{ + template< class SpanType > + static constexpr auto value(SpanType, constant_wrapper) + { + return cw; + } +}; + + +// Some compilers do not do ctad properly with canonical_range_slice +template +constexpr extent_slice +make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride) +{ + return extent_slice{offset, extent, stride}; +} + +template +constexpr auto canonical_range_slice(OffsetType offset, SpanType span, StrideType stride) +{ + return make_extent_slice( + offset, + calculate_extents::value(span, stride), + calculate_stride::value(span, stride) + ); +} + +template +constexpr auto canonical_range_slice(OffsetType offset, SpanType span) +{ + return canonical_range_slice(offset, span, cw); +} + // ============================================================ // canonical_slice: canonicalize a single slice // @@ -301,6 +410,15 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) /* .extent = */ extent, /* .stride = */ stride }; + } + else if constexpr (is_range_slice::value) { + auto c_first = canonical_index(std::move(s.first)); + auto c_last = canonical_index(std::move(s.last)); + return canonical_range_slice( + c_first, + subtract_ice(c_first, c_last), + canonical_index(std::move(s.stride)) + ); } else { // General pair-like case: structured binding into [first, last) auto [s_k0, s_k1] = std::move(s); diff --git a/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index 0f096427..99c306fd 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -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 { @@ -35,13 +36,6 @@ constexpr auto inv_map_rank(std::integral_constant, std::index_ slices...); } -// Helper for identifying strided_slice -template struct is_extent_slice : std::false_type {}; - -template -struct is_extent_slice< - extent_slice> : std::true_type {}; - // first_of(slice): getting begin of slice specifier range MDSPAN_TEMPLATE_REQUIRES( class Integral, @@ -275,6 +269,6 @@ constexpr auto submdspan_extents(const extents &src_exts, using ext_t = extents; return detail::extents_constructor::next_extent( - src_exts, slices...); + src_exts, detail::canonical_slice(slices)...); } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p2630_bits/submdspan_mapping.hpp b/include/experimental/__p2630_bits/submdspan_mapping.hpp index c0a7619e..5e9f7627 100644 --- a/include/experimental/__p2630_bits/submdspan_mapping.hpp +++ b/include/experimental/__p2630_bits/submdspan_mapping.hpp @@ -103,6 +103,15 @@ constexpr bool is_range_like_slice_v< IndexType > = (constant_wrapper::value == IndexType(1)); +template +constexpr bool is_range_like_slice_v< + range_slice< + FirstType, + LastType, + constant_wrapper>, + IndexType + > = (constant_wrapper::value == IndexType(1)); + template struct is_index_slice { constexpr static bool value = std::is_convertible_v; diff --git a/tests/test_canonicalize_slices.cpp b/tests/test_canonicalize_slices.cpp index 7b60b1e9..fc20e7ae 100644 --- a/tests/test_canonicalize_slices.cpp +++ b/tests/test_canonicalize_slices.cpp @@ -276,11 +276,11 @@ TEST(CanonicalizeSlices, Rank2_full) { MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}, 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{ /* .offset = */ 1, /* .extent = */ 4, @@ -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{ + /* .first = */ 1, + /* .last = */ 11, + /* .stride = */ 3 + }; + + const auto expected_slices = tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice{ + size_t(1), size_t(4), size_t(3) + } + }; + const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}; + 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; @@ -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; + using extent_t = std::integral_constant; + using stride_t = std::integral_constant; + const auto slice0 = + MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice{}; + + const auto expected_slices = tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice< + decltype(cw), + decltype(cw), + decltype(cw) + >{cw, cw, cw} + }; + const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}; + test_canonicalize_slices(expected_slices, exts, slice0); +} + } // namespace (anonymous) diff --git a/tests/test_strided_slice.cpp b/tests/test_strided_slice.cpp index a9bd23a4..1da13804 100644 --- a/tests/test_strided_slice.cpp +++ b/tests/test_strided_slice.cpp @@ -9,7 +9,7 @@ namespace { template -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 s{offset, extent, stride}; @@ -60,23 +60,60 @@ static_assert( my_integral_constant >); -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, unsigned(10), long(3)); - test_strided_slice(int(1), IC, long(3)); - test_strided_slice(int(1), unsigned(10), IC); + test_extent_slice(IC, unsigned(10), long(3)); + test_extent_slice(int(1), IC, long(3)); + test_extent_slice(int(1), unsigned(10), IC); using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; - test_strided_slice(cw<1>, unsigned(10), long(3)); - test_strided_slice(int(1), cw, long(3)); - test_strided_slice(int(1), unsigned(10), cw); + test_extent_slice(cw<1>, unsigned(10), long(3)); + test_extent_slice(int(1), cw, long(3)); + test_extent_slice(int(1), unsigned(10), cw); - test_strided_slice(IC2, unsigned(10), long(3)); - test_strided_slice(int(1), IC2, long(3)); - test_strided_slice(int(1), unsigned(10), IC2); + test_extent_slice(IC2, unsigned(10), long(3)); + test_extent_slice(int(1), IC2, long(3)); + test_extent_slice(int(1), unsigned(10), IC2); +} + +template +void test_range_slice(FirstType first, LastType last, StrideType stride) +{ + // Some compilers are bad at CTAD for aggregates. + Kokkos::range_slice s{first, last, stride}; + + auto first2 = s.first; + static_assert(std::is_same_v); + auto last2 = s.last; + static_assert(std::is_same_v); + auto stride2 = s.stride; + static_assert(std::is_same_v); + + 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, unsigned(10), long(3)); + test_range_slice(int(1), IC, long(3)); + test_range_slice(int(1), unsigned(10), IC); + + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + + test_range_slice(cw<1>, unsigned(10), long(3)); + test_range_slice(int(1), cw, long(3)); + test_range_slice(int(1), unsigned(10), cw); + + test_range_slice(IC2, unsigned(10), long(3)); + test_range_slice(int(1), IC2, long(3)); + test_range_slice(int(1), unsigned(10), IC2); } } // namespace (anonymous) diff --git a/tests/test_submdspan.cpp b/tests/test_submdspan.cpp index ffa86737..9ca5eb08 100644 --- a/tests/test_submdspan.cpp +++ b/tests/test_submdspan.cpp @@ -154,6 +154,11 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, std::pair> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, Kokkos::extent_slice> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, std::pair> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::range_slice> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, Kokkos::full_extent_t, int, std::pair, int, int, Kokkos::full_extent_t> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, int, Kokkos::full_extent_t, std::pair, int, Kokkos::full_extent_t, int> // layout_right to layout_right_padded @@ -169,6 +174,12 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, std::pair> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, Kokkos::extent_slice> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10>, Kokkos::extents, Kokkos::range_slice,std::integral_constant,std::integral_constant>> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, std::pair> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::range_slice> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, Kokkos::full_extent_t, int, std::pair, int, int, Kokkos::full_extent_t> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, int, Kokkos::full_extent_t, std::pair, int, Kokkos::full_extent_t, int> // layout_left_padded to layout_left @@ -198,6 +209,12 @@ using submdspan_test_types = , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::extent_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::range_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30,40>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::full_extent_t, int, Kokkos::full_extent_t> // layout_right_padded to layout_right , std::tuple, Kokkos::layout_right, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, int, int> @@ -226,6 +243,12 @@ using submdspan_test_types = , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::extent_slice, int, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, int, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::range_slice, int, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30,40>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::full_extent_t, Kokkos::full_extent_t> // Testing of customization point design , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::full_extent_t> @@ -236,6 +259,8 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, int, Kokkos::full_extent_t> , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, int> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> >; template struct TestSubMDSpan; @@ -279,6 +304,15 @@ struct TestSubMDSpan< return Kokkos::extent_slice, std::integral_constant>{1,std::integral_constant(), std::integral_constant()}; } MDSPAN_INLINE_FUNCTION + static auto create_slice_arg(Kokkos::range_slice) { + return Kokkos::range_slice{1,8,2}; + } + template + MDSPAN_INLINE_FUNCTION + static auto create_slice_arg(Kokkos::range_slice,std::integral_constant, std::integral_constant>) { + return Kokkos::range_slice, std::integral_constant, std::integral_constant>{std::integral_constant(),std::integral_constant(), std::integral_constant()}; + } + MDSPAN_INLINE_FUNCTION static auto create_slice_arg(Kokkos::full_extent_t) { return Kokkos::full_extent; } @@ -315,6 +349,19 @@ struct TestSubMDSpan< } template MDSPAN_INLINE_FUNCTION + static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, + Kokkos::range_slice p, SliceArgs ... slices) { + using idx_t = typename SubMDSpan::index_type; + return (sub_mds.extent(sub_idx)==static_cast(1 + (p.last - p.first - 1) / p.stride)) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); + } + template + MDSPAN_INLINE_FUNCTION + static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, + Kokkos::range_slice,std::integral_constant,std::integral_constant>, SliceArgs ... slices) { + return (sub_mds.extent(sub_idx)==0) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); + } + template + MDSPAN_INLINE_FUNCTION static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, Kokkos::full_extent_t, SliceArgs ... slices) { return (sub_mds.extent(sub_idx)==src_mds.extent(src_idx)) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); }