On Mon, May 26, 2025 at 9:15 PM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> > > On 5/26/25 18:17, Tomasz Kaminski wrote: > > On Mon, May 26, 2025 at 4:15 PM Luc Grosheintz <luc.groshei...@gmail.com > > > > wrote: > > > >> Implements the parts of layout_left that don't depend on any of the > >> other layouts. > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/std/mdspan (layout_left): New class. > >> * src/c++23/std.cc.in: Add layout_left. > >> > >> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> > >> --- > >> libstdc++-v3/include/std/mdspan | 304 ++++++++++++++++++++++++++++++- > >> libstdc++-v3/src/c++23/std.cc.in | 1 + > >> 2 files changed, 304 insertions(+), 1 deletion(-) > >> > >> diff --git a/libstdc++-v3/include/std/mdspan > >> b/libstdc++-v3/include/std/mdspan > >> index 0f49b0e09a0..d81072596b4 100644 > >> --- a/libstdc++-v3/include/std/mdspan > >> +++ b/libstdc++-v3/include/std/mdspan > >> @@ -144,6 +144,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> { return __exts[__i]; }); > >> } > >> > >> + static constexpr span<const size_t> > >> + _S_static_extents(size_t __begin, size_t __end) noexcept > >> + { > >> + return {_Extents.data() + __begin, _Extents.data() + __end}; > >> + } > >> > > Oh, I think I was very unclear, regarding removing the dependency on > > index_type. > > What I was thinking of, is changing this function to: > > + static consteval array<size_t, rank()>& > > + _S_static_extents() noexcept > > + { > > + return _Extents; > > + } > > Sorry, this was clear. I implemented it once, then we found the > issues with overflow and while cleaning out the mess I'd made > to compute things with an __unsigned_prod, I reverted this back > to the original version. > > If you look at __static_extents_prod, it already doesn't depend > on the index_type. Therefore, it a) felt like a change related > to something we'd agreed to postpone; and b) I preferred the > symmetry between __static_extents and __dynamic_extents; I'll > change it to the proposed version. > I think we are good keeping current versions. and then return back to it later, to see if we can make static_extent being folded at compile time, and look at some runtime instruction/binary size tradeoffs later. > > > > >> + > >> + constexpr span<const _IndexType> > >> + _M_dynamic_extents(size_t __begin, size_t __end) const noexcept > >> + requires (_Extents.size() > 0) > >> + { > >> + return {_M_dyn_exts + _S_dynamic_index[__begin], > >> + _M_dyn_exts + _S_dynamic_index[__end]}; > >> + } > >> + > >> private: > >> using _S_storage = __array_traits<_IndexType, > >> _S_rank_dynamic>::_Type; > >> [[no_unique_address]] _S_storage _M_dyn_exts{}; > >> @@ -160,6 +174,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> || _Extent <= numeric_limits<_IndexType>::max(); > >> } > >> > >> + namespace __mdspan > >> + { > >> + template<typename _Extents> > >> + constexpr span<const size_t> > >> + __static_extents(size_t __begin = 0, size_t __end = > >> _Extents::rank()) > >> + { return _Extents::_S_storage::_S_static_extents(__begin, > __end); } > > > > Also adjusting this one to have: > > template<typename _Extents> > > constexpr const std::array<size_t, N>& > > __static_extents() > > + { return _Extents::_S_storage::_S_static_extents; } > > > >> + > >> + template<typename _Extents> > >> + constexpr span<const typename _Extents::index_type> > >> + __dynamic_extents(const _Extents& __exts, size_t __begin = 0, > >> + size_t __end = _Extents::rank()) > >> + { > >> + return __exts._M_exts._M_dynamic_extents(__begin, __end); > >> + } > >> + } > >> + > >> template<typename _IndexType, size_t... _Extents> > >> class extents > >> { > >> @@ -251,7 +281,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> : _M_exts(span<const _OIndexType, _Nm>(__exts)) > >> { } > >> > >> - > >> template<__mdspan::__valid_index_type<index_type> _OIndexType, > >> size_t _Nm> > >> requires (_Nm == rank() || _Nm == rank_dynamic()) > >> constexpr explicit(_Nm != rank_dynamic()) > >> @@ -276,6 +305,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> } > >> > >> private: > >> + friend span<const size_t> > >> + __mdspan::__static_extents<extents>(size_t, size_t); > >> + > >> + friend span<const index_type> > >> + __mdspan::__dynamic_extents<extents>(const extents&, size_t, > >> size_t); > >> + > >> using _S_storage = __mdspan::_ExtentsStorage< > >> _IndexType, array<size_t, sizeof...(_Extents)>{_Extents...}>; > >> [[no_unique_address]] _S_storage _M_exts; > >> @@ -286,6 +321,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> > >> namespace __mdspan > >> { > >> + template<typename _Tp, size_t _Nm> > >> + constexpr bool > >> + __contains_zero(span<_Tp, _Nm> __exts) > >> + { > >> + for (size_t __i = 0; __i < __exts.size(); ++__i) > >> + if (__exts[__i] == 0) > >> + return true; > >> + return false; > >> + } > >> + > >> + constexpr size_t > >> + __static_extents_prod(const auto& __sta_exts) > >> > > Then this could be implemented as: > > template<auto& _Exts> > > __static_extents_prod(size_t __begin, size_t __end); > > // We provide the array as template parameter. > > { > > } > > > >> + { > >> + size_t __ret = 1; > >> + for (auto __factor : span(_Ext).subspan(__begin, __end)) > >> + if (__factor != dynamic_extent) > >> + __ret *= __factor; > >> + return __ret; > >> + } > >> + > >> + template<typename _Extents> > >> + constexpr typename _Extents::index_type > >> + __exts_prod(const _Extents& __exts, size_t __begin, size_t __end) > >> noexcept > >> + { > >> + using _IndexType = typename _Extents::index_type; > >> + > >> + auto __sta_exts = __static_extents<_Extents>(__begin, __end); > >> + size_t __sta_prod = __static_extents_prod(__sta_exts); > >> > > And then it will be called as follows: > > constexpr auto& __sta_exts = __static_extents<_Extents>(); > > size_t __sta_prod = __static_extents_prod<__sta_exts>(__begin, > __end); > > This way __static_extents_prod will not depend on index type. > > The current implementation of __static_extents_prod doesn't depend > on index_type either. The static extents are returned as a > span<const size_t>. > > > We seem to also compute the product twice. > > > >> + > >> + size_t __ret = 1; > >> + if constexpr (_Extents::rank_dynamic() != _Extents::rank()) > >> + __ret = __static_extents_prod(__sta_exts); > >> + > >> + if (__ret == 0) > >> + return 0; > >> + > >> + if constexpr (_Extents::rank_dynamic() > 0) > >> + for (auto __factor : __dynamic_extents(__exts, __begin, > __end)) > >> + __ret *= size_t(__factor); > >> + return _IndexType(__ret); > >> + } > >> + > >> + template<typename _Extents> > >> + constexpr typename _Extents::index_type > >> + __fwd_prod(const _Extents& __exts, size_t __r) noexcept > >> + { return __exts_prod(__exts, 0, __r); } > >> + > >> + template<typename _Extents> > >> + constexpr typename _Extents::index_type > >> + __rev_prod(const _Extents& __exts, size_t __r) noexcept > >> + { return __exts_prod(__exts, __r + 1, __exts.rank()); } > >> + > >> template<typename _IndexType, size_t... _Counts> > >> auto __build_dextents_type(integer_sequence<size_t, _Counts...>) > >> -> extents<_IndexType, ((void) _Counts, dynamic_extent)...>; > >> @@ -304,6 +391,221 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> explicit extents(_Integrals...) -> > >> extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>; > >> > >> + struct layout_left > >> + { > >> + template<typename _Extents> > >> + class mapping; > >> + }; > >> + > >> + namespace __mdspan > >> + { > >> + template<typename _Tp> > >> + constexpr bool __is_extents = false; > >> + > >> + template<typename _IndexType, size_t... _Extents> > >> + constexpr bool __is_extents<extents<_IndexType, _Extents...>> = > >> true; > >> + > >> + template<typename _Extents, typename... _Indices> > >> + constexpr typename _Extents::index_type > >> + __linear_index_left(const _Extents& __exts, _Indices... > __indices) > >> + { > >> + using _IndexType = typename _Extents::index_type; > >> + _IndexType __res = 0; > >> + if constexpr (sizeof...(__indices) > 0) > >> + { > >> + _IndexType __mult = 1; > >> + auto __update = [&, __pos = 0u](_IndexType __idx) mutable > >> + { > >> + __res += __idx * __mult; > >> + __mult *= __exts.extent(__pos); > >> + ++__pos; > >> + }; > >> + (__update(__indices), ...); > >> + } > >> + return __res; > >> + } > >> + > >> + template<typename _Extents, > >> + typename _IndexType = typename _Extents::index_type> > >> + consteval _IndexType > >> + __static_quotient(_IndexType __nom = > >> numeric_limits<_IndexType>::max()) > >> + { > >> + auto __sta_exts = __static_extents<_Extents>(); > >> + for (auto __factor : __sta_exts) > >> + { > >> + if (__factor != dynamic_extent) > >> + __nom /= _IndexType(__factor); > >> + if (__nom == 0) > >> + break; > >> + } > >> + return __nom; > >> + } > >> + > >> + template<typename _Extents> > >> + constexpr bool > >> + __is_representable_extents(const _Extents& __exts) > >> + { > >> + using _IndexType = _Extents::index_type; > >> + > >> + if constexpr (__contains_zero(__static_extents<_Extents>())) > >> + return true; > >> + else > >> + { > >> + constexpr auto __sta_quo = __static_quotient<_Extents>(); > >> > > + if constexpr (_Extents::rank_dynamic() == 0) > >> + return __sta_quo != 0; > >> + else > >> + { > >> + auto __dyn_exts = __dynamic_extents(__exts); > >> + if (__contains_zero(__dyn_exts)) > >> + return true; > >> + > >> + if constexpr (__sta_quo == 0) > >> + return false; > >> + else > >> + { > >> + constexpr auto __dyn_rank = > _Extents::rank_dynamic(); > >> > > These seem to be unused. > > > >> + auto __dyn_quo = _IndexType(__sta_quo); > >> + for (auto __factor : __dyn_exts) > >> + { > >> + __dyn_quo /= __factor; > >> + if (__dyn_quo == 0) > >> + return false; > >> + } > >> + return true; > >> + } > >> + } > >> + } > >> + } > >> + > >> + template<typename _Extents, typename _IndexType> > >> + concept __representable_size = _Extents::rank_dynamic() != 0 > >> + || __contains_zero(__static_extents<_Extents>()) > >> + || (__static_quotient<_Extents, _IndexType>() != 0); > >> + > >> + template<typename _Layout, typename _Mapping> > >> + concept __mapping_of = > >> + is_same_v<typename _Layout::mapping<typename > >> _Mapping::extents_type>, > >> + _Mapping>; > >> + > >> + template<typename _Mapping> > >> + concept __standardized_mapping = __mapping_of<layout_left, > >> _Mapping>; > >> + > >> + template<typename M> > >> + concept __mapping_like = requires > >> + { > >> + requires __is_extents<typename M::extents_type>; > >> + { M::is_always_strided() } -> same_as<bool>; > >> + { M::is_always_exhaustive() } -> same_as<bool>; > >> + { M::is_always_unique() } -> same_as<bool>; > >> + bool_constant<M::is_always_strided()>::value; > >> + bool_constant<M::is_always_exhaustive()>::value; > >> + bool_constant<M::is_always_unique()>::value; > >> + }; > >> + > >> + // A tag type to create internal ctors. > >> + class __internal_ctor > >> + { }; > >> + } > >> + > >> + template<typename _Extents> > >> + class layout_left::mapping > >> + { > >> + public: > >> + using extents_type = _Extents; > >> + using index_type = typename extents_type::index_type; > >> + using size_type = typename extents_type::size_type; > >> + using rank_type = typename extents_type::rank_type; > >> + using layout_type = layout_left; > >> + > >> + static_assert(__mdspan::__representable_size<_Extents, > index_type>, > >> + "The size of extents_type must be representable as index_type"); > >> + > >> + constexpr > >> + mapping() noexcept = default; > >> + > >> + constexpr > >> + mapping(const mapping&) noexcept = default; > >> + > >> + constexpr > >> + mapping(const _Extents& __extents) noexcept > >> + : _M_extents(__extents) > >> + { > >> __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); } > >> + > >> + template<typename _OExtents> > >> + requires (is_constructible_v<extents_type, _OExtents>) > >> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) > >> + mapping(const mapping<_OExtents>& __other) noexcept > >> + : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> + { } > >> + > >> + constexpr mapping& > >> + operator=(const mapping&) noexcept = default; > >> + > >> + constexpr const _Extents& > >> + extents() const noexcept { return _M_extents; } > >> + > >> + constexpr index_type > >> + required_span_size() const noexcept > >> + { return __mdspan::__fwd_prod(_M_extents, extents_type::rank()); > } > >> + > >> + template<__mdspan::__valid_index_type<index_type>... _Indices> > >> + requires (sizeof...(_Indices) == extents_type::rank()) > >> + constexpr index_type > >> + operator()(_Indices... __indices) const noexcept > >> + { > >> + return __mdspan::__linear_index_left(_M_extents, > >> + static_cast<index_type>(__indices)...); > >> + } > >> + > >> + static constexpr bool > >> + is_always_unique() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_always_exhaustive() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_always_strided() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_unique() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_exhaustive() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_strided() noexcept { return true; } > >> + > >> + constexpr index_type > >> + stride(rank_type __i) const noexcept > >> + requires (extents_type::rank() > 0) > >> + { > >> + __glibcxx_assert(__i < extents_type::rank()); > >> + return __mdspan::__fwd_prod(_M_extents, __i); > >> + } > >> + > >> + template<typename _OExtents> > >> + requires (extents_type::rank() == _OExtents::rank()) > >> + friend constexpr bool > >> + operator==(const mapping& __self, const mapping<_OExtents>& > >> __other) > >> + noexcept > >> + { return __self.extents() == __other.extents(); } > >> + > >> + private: > >> + template<typename _OExtents> > >> + constexpr explicit > >> + mapping(const _OExtents& __oexts, __mdspan::__internal_ctor) > >> noexcept > >> + : _M_extents(__oexts) > >> + { > >> + static_assert(__mdspan::__representable_size<_OExtents, > >> index_type>, > >> + "The size of OtherExtents must be representable as > >> index_type"); > >> + > >> __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents)); > >> + } > >> + > >> + public: > >> + [[no_unique_address]] _Extents _M_extents{}; > >> + }; > >> + > >> _GLIBCXX_END_NAMESPACE_VERSION > >> } > >> #endif > >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/ > >> std.cc.in > >> index 417c8a1a562..b4b0bb24a8c 100644 > >> --- a/libstdc++-v3/src/c++23/std.cc.in > >> +++ b/libstdc++-v3/src/c++23/std.cc.in > >> @@ -1841,6 +1841,7 @@ export namespace std > >> export namespace std > >> { > >> using std::extents; > >> + using std::layout_left; > >> // FIXME layout_*, default_accessor and mdspan > >> } > >> #endif > >> -- > >> 2.49.0 > >> > >> > > > >