Path: blob/main/contrib/llvm-project/libcxx/include/__mdspan/layout_stride.h
35262 views
// -*- C++ -*-1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7// Kokkos v. 4.08// Copyright (2022) National Technology & Engineering9// Solutions of Sandia, LLC (NTESS).10//11// Under the terms of Contract DE-NA0003525 with NTESS,12// the U.S. Government retains certain rights in this software.13//14//===---------------------------------------------------------------------===//1516#ifndef _LIBCPP___MDSPAN_LAYOUT_STRIDE_H17#define _LIBCPP___MDSPAN_LAYOUT_STRIDE_H1819#include <__assert>20#include <__config>21#include <__fwd/mdspan.h>22#include <__mdspan/extents.h>23#include <__type_traits/is_constructible.h>24#include <__type_traits/is_convertible.h>25#include <__type_traits/is_nothrow_constructible.h>26#include <__utility/as_const.h>27#include <__utility/integer_sequence.h>28#include <__utility/swap.h>29#include <array>30#include <cinttypes>31#include <cstddef>32#include <limits>3334#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)35# pragma GCC system_header36#endif3738_LIBCPP_PUSH_MACROS39#include <__undef_macros>4041_LIBCPP_BEGIN_NAMESPACE_STD4243#if _LIBCPP_STD_VER >= 234445namespace __mdspan_detail {46template <class _Layout, class _Mapping>47constexpr bool __is_mapping_of =48is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>, _Mapping>;4950template <class _Mapping>51concept __layout_mapping_alike = requires {52requires __is_mapping_of<typename _Mapping::layout_type, _Mapping>;53requires __is_extents_v<typename _Mapping::extents_type>;54{ _Mapping::is_always_strided() } -> same_as<bool>;55{ _Mapping::is_always_exhaustive() } -> same_as<bool>;56{ _Mapping::is_always_unique() } -> same_as<bool>;57bool_constant<_Mapping::is_always_strided()>::value;58bool_constant<_Mapping::is_always_exhaustive()>::value;59bool_constant<_Mapping::is_always_unique()>::value;60};61} // namespace __mdspan_detail6263template <class _Extents>64class layout_stride::mapping {65public:66static_assert(__mdspan_detail::__is_extents<_Extents>::value,67"layout_stride::mapping template argument must be a specialization of extents.");6869using extents_type = _Extents;70using index_type = typename extents_type::index_type;71using size_type = typename extents_type::size_type;72using rank_type = typename extents_type::rank_type;73using layout_type = layout_stride;7475private:76static constexpr rank_type __rank_ = extents_type::rank();7778// Used for default construction check and mandates79_LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {80if constexpr (__rank_ == 0)81return true;8283index_type __prod = __ext.extent(0);84for (rank_type __r = 1; __r < __rank_; __r++) {85bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);86if (__overflowed)87return false;88}89return true;90}9192template <class _OtherIndexType>93_LIBCPP_HIDE_FROM_ABI static constexpr bool94__required_span_size_is_representable(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) {95if constexpr (__rank_ == 0)96return true;9798index_type __size = 1;99for (rank_type __r = 0; __r < __rank_; __r++) {100// We can only check correct conversion of _OtherIndexType if it is an integral101if constexpr (is_integral_v<_OtherIndexType>) {102using _CommonType = common_type_t<index_type, _OtherIndexType>;103if (static_cast<_CommonType>(__strides[__r]) > static_cast<_CommonType>(numeric_limits<index_type>::max()))104return false;105}106if (__ext.extent(__r) == static_cast<index_type>(0))107return true;108index_type __prod = (__ext.extent(__r) - 1);109bool __overflowed_mul = __builtin_mul_overflow(__prod, static_cast<index_type>(__strides[__r]), &__prod);110if (__overflowed_mul)111return false;112bool __overflowed_add = __builtin_add_overflow(__size, __prod, &__size);113if (__overflowed_add)114return false;115}116return true;117}118119// compute offset of a strided layout mapping120template <class _StridedMapping>121_LIBCPP_HIDE_FROM_ABI static constexpr index_type __offset(const _StridedMapping& __mapping) {122if constexpr (_StridedMapping::extents_type::rank() == 0) {123return static_cast<index_type>(__mapping());124} else if (__mapping.required_span_size() == static_cast<typename _StridedMapping::index_type>(0)) {125return static_cast<index_type>(0);126} else {127return [&]<size_t... _Pos>(index_sequence<_Pos...>) {128return static_cast<index_type>(__mapping((_Pos ? 0 : 0)...));129}(make_index_sequence<__rank_>());130}131}132133// compute the permutation for sorting the stride array134// we never actually sort the stride array135_LIBCPP_HIDE_FROM_ABI constexpr void __bubble_sort_by_strides(array<rank_type, __rank_>& __permute) const {136for (rank_type __i = __rank_ - 1; __i > 0; __i--) {137for (rank_type __r = 0; __r < __i; __r++) {138if (__strides_[__permute[__r]] > __strides_[__permute[__r + 1]]) {139swap(__permute[__r], __permute[__r + 1]);140} else {141// if two strides are the same then one of the associated extents must be 1 or 0142// both could be, but you can't have one larger than 1 come first143if ((__strides_[__permute[__r]] == __strides_[__permute[__r + 1]]) &&144(__extents_.extent(__permute[__r]) > static_cast<index_type>(1)))145swap(__permute[__r], __permute[__r + 1]);146}147}148}149}150151static_assert(extents_type::rank_dynamic() > 0 || __required_span_size_is_representable(extents_type()),152"layout_stride::mapping product of static extents must be representable as index_type.");153154public:155// [mdspan.layout.stride.cons], constructors156_LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept : __extents_(extents_type()) {157// Note the nominal precondition is covered by above static assert since158// if rank_dynamic is != 0 required_span_size is zero for default construction159if constexpr (__rank_ > 0) {160index_type __stride = 1;161for (rank_type __r = __rank_ - 1; __r > static_cast<rank_type>(0); __r--) {162__strides_[__r] = __stride;163__stride *= __extents_.extent(__r);164}165__strides_[0] = __stride;166}167}168169_LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;170171template <class _OtherIndexType>172requires(is_convertible_v<const _OtherIndexType&, index_type> &&173is_nothrow_constructible_v<index_type, const _OtherIndexType&>)174_LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) noexcept175: __extents_(__ext), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {176return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{177static_cast<index_type>(std::as_const(__strides[_Pos]))...};178}(make_index_sequence<__rank_>())) {179_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(180([&]<size_t... _Pos>(index_sequence<_Pos...>) {181// For integrals we can do a pre-conversion check, for other types not182if constexpr (is_integral_v<_OtherIndexType>) {183return ((__strides[_Pos] > static_cast<_OtherIndexType>(0)) && ... && true);184} else {185return ((static_cast<index_type>(__strides[_Pos]) > static_cast<index_type>(0)) && ... && true);186}187}(make_index_sequence<__rank_>())),188"layout_stride::mapping ctor: all strides must be greater than 0");189_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(190__required_span_size_is_representable(__ext, __strides),191"layout_stride::mapping ctor: required span size is not representable as index_type.");192if constexpr (__rank_ > 1) {193_LIBCPP_ASSERT_UNCATEGORIZED(194([&]<size_t... _Pos>(index_sequence<_Pos...>) {195// basically sort the dimensions based on strides and extents, sorting is represented in permute array196array<rank_type, __rank_> __permute{_Pos...};197__bubble_sort_by_strides(__permute);198199// check that this permutations represents a growing set200for (rank_type __i = 1; __i < __rank_; __i++)201if (static_cast<index_type>(__strides[__permute[__i]]) <202static_cast<index_type>(__strides[__permute[__i - 1]]) * __extents_.extent(__permute[__i - 1]))203return false;204return true;205}(make_index_sequence<__rank_>())),206"layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");207}208}209210template <class _OtherIndexType>211requires(is_convertible_v<const _OtherIndexType&, index_type> &&212is_nothrow_constructible_v<index_type, const _OtherIndexType&>)213_LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext,214const array<_OtherIndexType, __rank_>& __strides) noexcept215: mapping(__ext, span(__strides)) {}216217template <class _StridedLayoutMapping>218requires(__mdspan_detail::__layout_mapping_alike<_StridedLayoutMapping> &&219is_constructible_v<extents_type, typename _StridedLayoutMapping::extents_type> &&220_StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided())221_LIBCPP_HIDE_FROM_ABI constexpr explicit(222!(is_convertible_v<typename _StridedLayoutMapping::extents_type, extents_type> &&223(__mdspan_detail::__is_mapping_of<layout_left, _StridedLayoutMapping> ||224__mdspan_detail::__is_mapping_of<layout_right, _StridedLayoutMapping> ||225__mdspan_detail::__is_mapping_of<layout_stride, _StridedLayoutMapping>)))226mapping(const _StridedLayoutMapping& __other) noexcept227: __extents_(__other.extents()), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {228// stride() only compiles for rank > 0229if constexpr (__rank_ > 0) {230return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{231static_cast<index_type>(__other.stride(_Pos))...};232} else {233return __mdspan_detail::__possibly_empty_array<index_type, 0>{};234}235}(make_index_sequence<__rank_>())) {236// stride() only compiles for rank > 0237if constexpr (__rank_ > 0) {238_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(239([&]<size_t... _Pos>(index_sequence<_Pos...>) {240return ((static_cast<index_type>(__other.stride(_Pos)) > static_cast<index_type>(0)) && ... && true);241}(make_index_sequence<__rank_>())),242"layout_stride::mapping converting ctor: all strides must be greater than 0");243}244_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(245__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),246"layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");247_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(static_cast<index_type>(0) == __offset(__other),248"layout_stride::mapping converting ctor: base offset of mapping must be zero.");249}250251_LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;252253// [mdspan.layout.stride.obs], observers254_LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }255256_LIBCPP_HIDE_FROM_ABI constexpr array<index_type, __rank_> strides() const noexcept {257return [&]<size_t... _Pos>(index_sequence<_Pos...>) {258return array<index_type, __rank_>{__strides_[_Pos]...};259}(make_index_sequence<__rank_>());260}261262_LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {263if constexpr (__rank_ == 0) {264return static_cast<index_type>(1);265} else {266return [&]<size_t... _Pos>(index_sequence<_Pos...>) {267if ((__extents_.extent(_Pos) * ... * 1) == 0)268return static_cast<index_type>(0);269else270return static_cast<index_type>(271static_cast<index_type>(1) +272(((__extents_.extent(_Pos) - static_cast<index_type>(1)) * __strides_[_Pos]) + ... +273static_cast<index_type>(0)));274}(make_index_sequence<__rank_>());275}276}277278template <class... _Indices>279requires((sizeof...(_Indices) == __rank_) && (is_convertible_v<_Indices, index_type> && ...) &&280(is_nothrow_constructible_v<index_type, _Indices> && ...))281_LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept {282// Mappings are generally meant to be used for accessing allocations and are meant to guarantee to never283// return a value exceeding required_span_size(), which is used to know how large an allocation one needs284// Thus, this is a canonical point in multi-dimensional data structures to make invalid element access checks285// However, mdspan does check this on its own, so for now we avoid double checking in hardened mode286_LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),287"layout_stride::mapping: out of bounds indexing");288return [&]<size_t... _Pos>(index_sequence<_Pos...>) {289return ((static_cast<index_type>(__idx) * __strides_[_Pos]) + ... + index_type(0));290}(make_index_sequence<sizeof...(_Indices)>());291}292293_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }294_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }295_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }296297_LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }298// The answer of this function is fairly complex in the case where one or more299// extents are zero.300// Technically it is meaningless to query is_exhaustive() in that case, but unfortunately301// the way the standard defines this function, we can't give a simple true or false then.302_LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {303if constexpr (__rank_ == 0)304return true;305else {306index_type __span_size = required_span_size();307if (__span_size == static_cast<index_type>(0)) {308if constexpr (__rank_ == 1)309return __strides_[0] == 1;310else {311rank_type __r_largest = 0;312for (rank_type __r = 1; __r < __rank_; __r++)313if (__strides_[__r] > __strides_[__r_largest])314__r_largest = __r;315for (rank_type __r = 0; __r < __rank_; __r++)316if (__extents_.extent(__r) == 0 && __r != __r_largest)317return false;318return true;319}320} else {321return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {322return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));323}(make_index_sequence<__rank_>());324}325}326}327_LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }328329// according to the standard layout_stride does not have a constraint on stride(r) for rank>0330// it still has the precondition though331_LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept {332_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__r < __rank_, "layout_stride::mapping::stride(): invalid rank index");333return __strides_[__r];334}335336template <class _OtherMapping>337requires(__mdspan_detail::__layout_mapping_alike<_OtherMapping> &&338(_OtherMapping::extents_type::rank() == __rank_) && _OtherMapping::is_always_strided())339_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const mapping& __lhs, const _OtherMapping& __rhs) noexcept {340if (__offset(__rhs))341return false;342if constexpr (__rank_ == 0)343return true;344else {345return __lhs.extents() == __rhs.extents() && [&]<size_t... _Pos>(index_sequence<_Pos...>) {346// avoid warning when comparing signed and unsigner integers and pick the wider of two types347using _CommonType = common_type_t<index_type, typename _OtherMapping::index_type>;348return ((static_cast<_CommonType>(__lhs.stride(_Pos)) == static_cast<_CommonType>(__rhs.stride(_Pos))) && ... &&349true);350}(make_index_sequence<__rank_>());351}352}353354private:355_LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};356_LIBCPP_NO_UNIQUE_ADDRESS __mdspan_detail::__possibly_empty_array<index_type, __rank_> __strides_{};357};358359#endif // _LIBCPP_STD_VER >= 23360361_LIBCPP_END_NAMESPACE_STD362363_LIBCPP_POP_MACROS364365#endif // _LIBCPP___MDSPAN_LAYOUT_STRIDE_H366367368