From patchwork Thu Dec 4 21:28:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125934 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 818474C31852 for ; Thu, 4 Dec 2025 21:28:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 818474C31852 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=DET8VpMO X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) by sourceware.org (Postfix) with ESMTPS id D37004B1A364 for ; Thu, 4 Dec 2025 21:26:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D37004B1A364 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D37004B1A364 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883604; cv=none; b=EkmeMrGAndHvJbChWws0RLHPlUdg8sjsSJ+L5iujOIQrcby4rDfSaEstW0ObMCMDbghx+RbS3MvDEBUYyTrSWo0YSHlq1wcBRDmKhlREJtSy4VWmE58RV+WYx0nKC9viwyBvT2dwgANTiPgb/OO/aVJh9ETr7N+ioSM/bi2Yhl4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883604; c=relaxed/simple; bh=pF+vCuMegm1ffdKvJmuhYtqUuucVJBfoypluSaSxDBs=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=nIxqaCsvsVE2OCXeGEBbe0jEerehVKAOC2HWLOKFHNbICnnsS0VFJIs8IGRoJhyF9WIhmDRsTQQY5onBOdh9E/xVA0yrmF97tmj2iPn4grUTky/jHmgKuDTMMF6WJCvFoSF+VWa3OWUbjjSUypyqP/Amg4VD0wUwwlRHEuHA39k= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D37004B1A364 Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-42e2e239ec0so943569f8f.0 for ; Thu, 04 Dec 2025 13:26:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883603; x=1765488403; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Bf4GjIDsab3qPQgyVBAA2K4GysByuw4oWvWnSnHab1o=; b=DET8VpMON/OoXEAZfz9l/QyoLwcFuIF+StnlLI3BxqcxEqvY1jw+Wr2byq6iT59E2v CAu0VFQPA3G4zds3VECws9cemwATTsVxksS7XRsX298iw3dtey+q6ljncvYlfqOQEXAL dEKU5shtSjYDBDA/kFILCVcK/pxdbzT7wfRivy9OztX21mC+5lThG2Fi5WlWu2ruG9Py 0rH/LGdF6GFWjxnsaRuMc3h+ZLjfTGs/fGsA6sHrtnh45w7G3GJfggR643ep2SL/QqB4 g//VEpqxpFnk/90tWmLMAYQkiF4Zop+J0ru67VNZk7LFs2QaTwNzS39ga5h88jrs7LUm Vs3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883603; x=1765488403; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Bf4GjIDsab3qPQgyVBAA2K4GysByuw4oWvWnSnHab1o=; b=NqhlfatTAEE4cK+kGSDt0Xd9Va7k0vG5/nLR0o5LHhmbLXAcArfX0xkj1oUbTb2cO/ Dlz/2obas0FJksgejUdTTGWVC3eg7CT4BktANbrn98PYrhdcncuRM7zdY6ePkwOuOHpU aGuxjdu/1QbWSl27S7iDx56AwcXj4MFJ4wUfGzcwSlW+G1yMMdR2OPXy9TVfTri/THHQ S7cKA7lBt4BK1hqebZh/m2P9JnSHHcmFU6+F1b8ogcOz0sCaiYQ2idlPhmy8Z3g96D2k +He/VqNO+3HZZejoE3sUQdwEVq3AuccJYp+Wqs/uVuoLGLT4Vy/Y7ni8IShRNmahbjgM trag== X-Gm-Message-State: AOJu0YyYv1mpkUEeMz2yk2y8RJTuC0kRsi0ZPHw3NgcdlCbNQbC+T1Ik yLWvJo/j6C52apJmV4kIh6HtjI3Dw5lLLeaMy5NOivJagZluHGNOR68uEeoXqA== X-Gm-Gg: ASbGnct3EDiqZUwyrm+E32Lh9iCnE3g7iZyiDyRJOzy4E6C+AeWulc7JVdtshgxE4Rz rX17lW5zEc+QrI4nuvK70xP7Pfm6JPzDF12wP2Igjh9QHaX9ue20X8i0yNVTt0m7+j+VsJ5k3Vn nZxzBlGpWm1GYMFRYXQ8IndVurp1Tz40vB2rtNvtjo+jafF0z+StNsoh9GchPQg1ifZ1sQot+4c sufDVptpvFyqpz1en2gBZaX56/bUt3PMtP7bPQvoTPUlNRTBrqfAocruxa4ZzFsh274Qn0nNgTC j7HESPjffefLyG2+/vbRCGxd0ctfGPDXYO6b09+Z6UJz1cPUs5wMJ3Q9DVRrbFpcZ4SpVWLS1k/ yDrDbnx9t6eXwIJnlCESZEH5boGJU0JIbheEyL5O9Xuvoa4yNJ4hGhigaBeQfAsVjxH4dUiChat lI60isxPzzW+XEvIYneB9OZNyHp+0wYiqNx3GR7P4FTBHnFr+cxjP5gqjls4kU X-Google-Smtp-Source: AGHT+IETsQUV8/tY0WG3Jbui5Ypd5qgqf1D981J2PESZPeFeFD8ZPefyyKYgq0cNFj0+2nVvfjL4Rg== X-Received: by 2002:a05:6000:2483:b0:42b:3131:5436 with SMTP id ffacd0b85a97d-42f7317286fmr8126191f8f.16.1764883602325; Thu, 04 Dec 2025 13:26:42 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:41 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 1/5] libstdc++: Implement submdspan and submdspan_mapping for layout_left. [PR110352] Date: Thu, 4 Dec 2025 22:28:42 +0100 Message-ID: <20251204212846.248887-2-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Implements `submdspan` and `submdspan_mapping` for layout_left as described in P3663 (Future proofing mdspan). When computing the offset of the submdspan, one must check that the lower bound of the slice range isn't out-of-range. There's a few cases when the lower bound is never out-of-range: - full_extent and exts.extent(k) != 0, - collapsing slice types. If those conditions are known to hold, no checks are generated. Similarly, if all slices are full_extent, there's no need to call mapping(0,...,0) for standardized mappings. The implementation prepares to use the symmetry between layout_left and layout_right and introduces concepts like a "layout side", i.e. left, right or unknown/strided. The tests use an iterator to replace nested for-loops. Which also makes it easier to write the core test logic in a rank-independent manner. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_left::mapping::submdspan_mapping): New friend function. (submdspan): New function. * src/c++23/std.cc.in: Add submdspan. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: New test. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: New test. * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: New test. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 557 ++++++++++++++++-- libstdc++-v3/src/c++23/std.cc.in | 2 +- .../mdspan/submdspan/submdspan.cc | 369 ++++++++++++ .../mdspan/submdspan/submdspan_mapping.cc | 136 +++++ .../mdspan/submdspan/submdspan_neg.cc | 161 +++++ 5 files changed, 1163 insertions(+), 62 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc create mode 100644 libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index bfec288aded..54739c0008d 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -357,40 +357,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static_assert(__is_standard_integer<_ExtentType>::value || __detail::__integral_constant_like<_ExtentType>); static_assert(__is_standard_integer<_StrideType>::value || __detail::__integral_constant_like<_StrideType>); using offset_type = _OffsetType; using extent_type = _ExtentType; using stride_type = _StrideType; [[no_unique_address]] offset_type offset{}; [[no_unique_address]] extent_type extent{}; [[no_unique_address]] stride_type stride{}; }; template struct submdspan_mapping_result { [[no_unique_address]] _Mapping mapping = _Mapping(); size_t offset{}; }; + + template + constexpr bool __is_submdspan_mapping_result = false; + + template + constexpr bool __is_submdspan_mapping_result> = true; + + template + concept __submdspan_mapping_result = __is_submdspan_mapping_result<_Mapping>; + #endif // __glibcxx_submdspan template class extents { static_assert(__is_standard_integer<_IndexType>::value, "IndexType must be a signed or unsigned integer type"); static_assert( (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...), "Extents must either be dynamic or representable as IndexType"); using _Storage = __mdspan::_ExtentsStorage< _IndexType, array{_Extents...}>; [[no_unique_address]] _Storage _M_exts; public: using index_type = _IndexType; using size_type = make_unsigned_t; using rank_type = size_t; @@ -572,40 +582,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template constexpr typename _Extents::index_type __fwd_prod(const _Extents& __exts, size_t __r) noexcept { constexpr size_t __rank = _Extents::rank(); constexpr auto& __sta_exts = __static_extents<_Extents>(); if constexpr (__rank == 1) return 1; else if constexpr (__rank == 2) return __r == 0 ? 1 : __exts.extent(0); else if constexpr (__all_dynamic(std::span(__sta_exts).first(__rank-1))) return __extents_prod(__exts, 1, 0, __r); else { size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r]; return __extents_prod(__exts, __sta_prod, 0, __r); } } + template + consteval _IndexType + __fwd_prod(span __values) + { + _IndexType __ret = 1; + for(auto __value : __values) + __ret *= __value; + return __ret; + } + // Preconditions: _r < _Extents::rank() template constexpr typename _Extents::index_type __rev_prod(const _Extents& __exts, size_t __r) noexcept { constexpr size_t __rank = _Extents::rank(); constexpr auto& __sta_exts = __static_extents<_Extents>(); if constexpr (__rank == 1) return 1; else if constexpr (__rank == 2) return __r == 0 ? __exts.extent(1) : 1; else if constexpr (__all_dynamic(std::span(__sta_exts).last(__rank-1))) return __extents_prod(__exts, 1, __r + 1, __rank); else { size_t __sta_prod = __rev_partial_prods<__sta_exts>[__r]; return __extents_prod(__exts, __sta_prod, __r + 1, __rank); } } @@ -849,64 +869,503 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return std::cmp_less_equal(__min, __value) && std::cmp_less_equal(__value, __max); } template constexpr bool __is_constant_wrapper = false; template<_CwFixedValue _Xv, typename _Tp> constexpr bool __is_constant_wrapper> = true; template constexpr auto __extract_extent(const _Extents& __exts) { using _IndexType = typename _Extents::index_type; return extents<_IndexType, _Extents::static_extent(_Index)>{ __exts.extent(_Index)}; } + template + concept __acceptable_slice_type = same_as<_Slice, full_extent_t> + || same_as<_Slice, _IndexType> || __is_constant_wrapper<_Slice> + || __is_strided_slice<_Slice>; + template consteval auto __subrank() { return (static_cast(!convertible_to<_Slices, _IndexType>) + ... + 0); } template consteval auto __inv_map_rank() { constexpr auto __rank = sizeof...(_Slices); constexpr auto __sub_rank = __subrank<_IndexType, _Slices...>(); auto __map = std::array{}; auto __is_int_like = std::array{ convertible_to<_Slices, _IndexType>...}; size_t __i = 0; for (size_t __k = 0; __k < __rank; ++__k) if (!__is_int_like[__k]) __map[__i++] = __k; return __map; } + + template + constexpr auto + __slice_begin(_Slice __slice) + { + if constexpr (same_as<_Slice, full_extent_t>) + return 0; + else if constexpr (__is_strided_slice<_Slice>) + return __slice.offset; + else + return __slice; // collapsing slice + } + + template + constexpr size_t + __suboffset(const _Mapping& __mapping, const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + auto __any_past_the_end = [&](index_sequence<_Is...>) + { + auto __is_past_the_end = [](const auto& __slice, const auto& __ext) + { + using _Slice = remove_cvref_t; + if constexpr (is_convertible_v<_Slice, _IndexType>) + return false; + else if constexpr (same_as<_Slice, full_extent_t> + && __ext.static_extent(0) != 0 + && __ext.static_extent(0) != dynamic_extent) + return false; + else + return __mdspan::__slice_begin(__slice) == __ext.extent(0); + }; + + const auto& __exts = __mapping.extents(); + return ((__is_past_the_end(__slices...[_Is], + __mdspan::__extract_extent<_Is>(__exts))) || ...); + }; + + if constexpr ((same_as<_Slices, full_extent_t> && ...)) + return __mdspan::__offset(__mapping); + + if (__any_past_the_end(std::make_index_sequence())) + return __mapping.required_span_size(); + return __mapping(__mdspan::__slice_begin(__slices)...); + } + + template + consteval size_t + __static_slice_extent() + { + if constexpr (same_as<_Slice, full_extent_t>) + return _Extent; + else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>) + return 0; + else if constexpr (__is_constant_wrapper + && __is_constant_wrapper) + return 1 + ((typename _Slice::extent_type{}) - 1) + / (typename _Slice::stride_type{}); + else + return dynamic_extent; + } + + template + constexpr typename _Extents::index_type + __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) + { + if constexpr (__is_strided_slice<_Slice>) + return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / __slice.stride; + else + return __exts.extent(_K); + } + + template + requires (sizeof...(_Slices) == sizeof...(_Extents)) + constexpr auto + __subextents(const extents<_IndexType, _Extents...>& __exts, + _Slices... __slices) + { + constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); + auto __impl = [&](std::index_sequence<_Indices...>) + { + using _SubExts = extents<_IndexType, + __mdspan::__static_slice_extent<_IndexType, + _Extents...[__inv_map[_Indices]], + _Slices...[__inv_map[_Indices]]>()...>; + if constexpr (_SubExts::rank_dynamic() == 0) + return _SubExts{}; + else + { + using _StaticSubExtents = __mdspan::_StaticExtents< + __mdspan::__static_extents<_SubExts>()>; + auto __create = [&](std::index_sequence<_Is...>) + { + constexpr auto __slice_idx = [__inv_map](size_t __i) consteval + { + return __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; + }; + + return _SubExts{__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>( + __exts, __slices...[__slice_idx(_Is)])...}; + }; + constexpr auto __dyn_subrank = _SubExts::rank_dynamic(); + return __create(std::make_index_sequence<__dyn_subrank>()); + } + }; + + return __impl(std::make_index_sequence<__inv_map.size()>()); + } + + enum class _LayoutSide + { + __left, + __right, + __unknown + }; + + template + consteval _LayoutSide + __mapping_side() + { + if constexpr (__is_left_padded_mapping<_Mapping> + || __mapping_of) + return _LayoutSide::__left; + if constexpr (__is_right_padded_mapping<_Mapping> + || __mapping_of) + return _LayoutSide::__right; + else + return _LayoutSide::__unknown; + } + + template<_LayoutSide _Side, size_t _Rank> + struct _StridesTrait + { + static constexpr const _LayoutSide _S_side = _Side; + + static constexpr size_t + _S_idx(size_t __k) noexcept + { + if constexpr (_Side == _LayoutSide::__left) + return __k; + else + return _Rank - 1 - __k; + } + + // Unifies the formulas for computing strides for padded and unpadded + // layouts. + template + static constexpr typename _Mapping::index_type + _S_padded_extent(const _Mapping& __mapping, size_t __k) + { + if (__k == 0) + return __mapping.stride(_S_idx(1)); + else + return __mapping.extents().extent(_S_idx(__k)); + } + + template + static consteval auto + _S_inv_map() + { + static_assert(_Side != _LayoutSide::__unknown); + auto __impl = [&](std::index_sequence<_Is...>) + { + return __mdspan::__inv_map_rank<_IndexType, _Slices...[_S_idx(_Is)]...>(); + }; + return __impl(std::make_index_sequence<_Rank>()); + } + }; + + template + constexpr auto + __substrides_generic(const _Mapping& __mapping, const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + if constexpr (_SubExts::rank() == 0) + return array<_IndexType, _SubExts::rank()>{}; + else + { + auto __stride = [&__mapping](size_t __k, auto __slice) -> _IndexType + { + if constexpr (__is_strided_slice) + if (__slice.stride < __slice.extent) + return __mapping.stride(__k) * __slice.stride; + return __mapping.stride(__k); + }; + + auto __impl = [&](std::index_sequence<_Is...>) + { + constexpr auto __inv_map + = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); + return array<_IndexType, _SubExts::rank()>{ + __stride(__inv_map[_Is], __slices...[__inv_map[_Is]])...}; + }; + return __impl(std::make_index_sequence<_SubExts::rank()>()); + } + }; + + template + constexpr auto + __substrides_standardized(const _Mapping& __mapping, + const _Slices&... __slices) + { + using _IndexType = typename _Mapping::index_type; + using _Trait = _StridesTrait<__mapping_side<_Mapping>(), + _Mapping::extents_type::rank()>; + using _SubTrait = _StridesTrait<__mapping_side<_Mapping>(), _SubExts::rank()>; + + constexpr size_t __sub_rank = _SubExts::rank(); + + std::array<_IndexType, __sub_rank> __ret; + if constexpr (__sub_rank > 0) + { + constexpr auto __inv_map + = _Trait::template _S_inv_map<_IndexType, _Slices...>(); + auto __loop = [&](std::index_sequence<_Ks...>) + { + size_t __i0 = 0; + size_t __stride = 1; + auto __body = [&](size_t __k, auto __slice) + { + for (size_t __i = __i0; __i < __inv_map[__k]; ++__i) + __stride *= _Trait::_S_padded_extent(__mapping, __i); + + size_t __krev = _SubTrait::_S_idx(__k); + if constexpr (__is_strided_slice) + __ret[__krev] = __stride * __slice.stride; + else + __ret[__krev] = __stride; + + __i0 = __inv_map[__k]; + }; + + ((__body(_Ks, __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...); + }; + __loop(std::make_index_sequence<__sub_rank>()); + } + return __ret; + } + + + template + constexpr auto + __substrides(const _Mapping& __mapping, const _Slices&... __slices) + { + if constexpr (__mdspan::__mapping_side<_Mapping>() == _LayoutSide::__unknown) + return __mdspan::__substrides_generic<_SubExts>(__mapping, __slices...); + else + return __mdspan::__substrides_standardized<_SubExts>(__mapping, __slices...); + } + + template + concept __is_unit_stride_slice = (__mdspan::__is_strided_slice<_Slice> + && __mdspan::__is_constant_wrapper + && _Slice::stride_type::value == 1) + || std::same_as<_Slice, full_extent_t>; + + // These are (forced) exclusive categories: + // - full & collapsing: obvious, + // - unit_strided_slice: strided_slice{a, b, cw<1>}, but not `full`, + // - strided_slice: strided_slice{a, b, c} with c != cw<1>. + enum class _SliceKind + { + __strided_slice, + __unit_strided_slice, + __full, + __collapsing + }; + + template + consteval _SliceKind + __make_slice_kind() + { + if constexpr (std::same_as<_Slice, full_extent_t>) + return _SliceKind::__full; + else if constexpr (__mdspan::__is_strided_slice<_Slice>) + { + if constexpr (__mdspan::__is_unit_stride_slice<_Slice>) + return _SliceKind::__unit_strided_slice; + else + return _SliceKind::__strided_slice; + } + else + return _SliceKind::__collapsing; + } + + template + consteval array<_SliceKind, sizeof...(_Slices)> + __make_slice_kind_array() + { + return array<_SliceKind, sizeof...(_Slices)>{ + __mdspan::__make_slice_kind<_Slices>()...}; + } + + // _BlockSize - 1 + // [full, ..., full, unit_slice , *] + template + consteval bool + __is_block(span __slice_kinds) + { + if (_BlockSize == 0) + return false; + + if (_BlockSize > __slice_kinds.size()) + return false; + + for (size_t __i = 0; __i < _BlockSize - 1; ++__i) + if (__slice_kinds[__i] != _SliceKind::__full) + return false; + + auto __last = __slice_kinds[_BlockSize - 1]; + return __last == _SliceKind::__full + || __last == _SliceKind::__unit_strided_slice; + } + + // __u __u + _SubRank-2 + // [unit_slice, i, ..., k, full, ..., full, unit_slice, *] + template + static consteval size_t + __padded_block_begin_generic(span __slice_kinds) + { + if (__slice_kinds[0] != _SliceKind::__full + && __slice_kinds[0] != _SliceKind::__unit_strided_slice) + return dynamic_extent; + else if (__slice_kinds.size() == 1) + return dynamic_extent; + else + { + static_assert(_SubRank != dynamic_extent, + "The implementation can't handle submdspans with rank == size_t(-1)"); + + size_t __u = 1; + while(__u < __slice_kinds.size() + && __slice_kinds[__u] == _SliceKind::__collapsing) + ++__u; + + if (__mdspan::__is_block<_SubRank-1>(__slice_kinds.subspan(__u))) + return __u; + return dynamic_extent; + } + } + + template<_LayoutSide _Side, size_t _SubRank, size_t _Nm> + static consteval size_t + __padded_block_begin(span __slice_kinds) + { + if constexpr (_Side == _LayoutSide::__left) + return __mdspan::__padded_block_begin_generic<_SubRank>(__slice_kinds); + else + { + std::array<_SliceKind, _Nm> __rev_slice_kinds; + for(size_t __i = 0; __i < _Nm; ++__i) + __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; + + auto __u = __mdspan::__padded_block_begin_generic<_SubRank>( + std::span(__rev_slice_kinds)); + return __u == dynamic_extent ? dynamic_extent : _Nm - 1 - __u; + } + } + + template<_LayoutSide _Side> + struct _SubMdspanMapping; + + template<> + struct _SubMdspanMapping<_LayoutSide::__left> + { + using _Layout = layout_left; + template using _PaddedLayout = layout_left_padded<_Pad>; + + template + static consteval size_t + _S_pad() + { + using _Extents = typename _Mapping::extents_type; + constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(0, _Us); + if constexpr (!__mdspan::__all_static(__sta_exts)) + return dynamic_extent; + else + return __mdspan::__fwd_prod(__sta_exts); + } + + template + static consteval bool + _S_is_unpadded_submdspan(span __slice_kinds) + { return __mdspan::__is_block<_SubRank>(__slice_kinds); } + }; + + template + constexpr auto + __submdspan_mapping_impl(const _Mapping& __mapping) + { return submdspan_mapping_result{__mapping, 0}; } + + template + requires (sizeof...(_Slices) > 0) + constexpr auto + __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices) + { + constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); + using _Trait = _SubMdspanMapping<__side>; + + constexpr auto __slice_kinds = __mdspan::__make_slice_kind_array<_Slices...>(); + auto __offset = __mdspan::__suboffset(__mapping, __slices...); + auto __sub_exts = __mdspan::__subextents(__mapping.extents(), __slices...); + using _SubExts = decltype(__sub_exts); + constexpr auto __sub_rank = _SubExts::rank(); + if constexpr (__sub_rank == 0) + return submdspan_mapping_result{ + typename _Trait::_Layout::mapping(__sub_exts), __offset}; + else if constexpr ( + _Trait::template _S_is_unpadded_submdspan<__sub_rank>( + std::span(__slice_kinds))) + return submdspan_mapping_result{ + typename _Trait::_Layout::mapping(__sub_exts), __offset}; + else if constexpr ( + constexpr auto __u = __padded_block_begin<__side, __sub_rank>( + span(__slice_kinds)); + __u != dynamic_extent) + { + constexpr auto __pad = _Trait::template _S_pad<_Mapping, __u>(); + using _Layout = typename _Trait::template _PaddedLayout<__pad>; + return submdspan_mapping_result{ + typename _Layout::mapping(__sub_exts, __mapping.stride(__u)), + __offset}; + } + else + { + auto __sub_strides + = __mdspan::__substrides<_SubExts>(__mapping, __slices...); + return submdspan_mapping_result{ + layout_stride::mapping(__sub_exts, __sub_strides), __offset}; + } + } + + // Enables ADL-only calls from submdspan. + void submdspan_mapping() = delete; #endif // __glibcxx_submdspan } template 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, "The size of extents_type must be representable as index_type"); constexpr mapping() noexcept = default; constexpr @@ -1014,40 +1473,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template 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 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)); } +#if __glibcxx_submdspan + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); } +#endif // __glibcxx_submdspan + [[no_unique_address]] extents_type _M_extents{}; }; namespace __mdspan { template constexpr typename _Extents::index_type __linear_index_right(const _Extents& __exts, _Indices... __indices) noexcept { using _IndexType = typename _Extents::index_type; array<_IndexType, sizeof...(__indices)> __ind_arr{__indices...}; _IndexType __res = 0; if constexpr (sizeof...(__indices) > 0) { _IndexType __mult = 1; auto __update = [&, __pos = __exts.rank()](_IndexType) mutable { --__pos; _GLIBCXX_DEBUG_ASSERT(cmp_less(__ind_arr[__pos], @@ -2673,118 +3140,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION else if constexpr (__is_constant_wrapper<_Slice> && _Extent != dynamic_extent) static_assert(std::cmp_less(_Slice::value, _Extent)); else if constexpr (convertible_to<_Slice, _IndexType>) __glibcxx_assert(__slice < __ext.extent(0)); } template constexpr void __check_valid_slices(const _Extents& __exts, const _Slices&... __slices) { constexpr auto __rank = _Extents::rank(); auto __impl = [&](index_sequence<_Is...>) { ((__mdspan::__check_valid_slice(__extract_extent<_Is>(__exts), __slices...[_Is])),...); }; __impl(make_index_sequence<__rank>()); } - template - consteval size_t - __static_slice_extent() + template + concept __sliceable_mapping = requires(_Mapping __m) { - if constexpr (same_as<_Slice, full_extent_t>) - return _Extent; - else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>) - return 0; - else if constexpr (__is_constant_wrapper - && __is_constant_wrapper) - return 1 + ((typename _Slice::extent_type{}) - 1) - / (typename _Slice::stride_type{}); - else - return dynamic_extent; - } - - template - constexpr typename _Extents::index_type - __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) - { - if constexpr (__is_strided_slice<_Slice>) - return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / __slice.stride; - else - return __exts.extent(_K); - } - - template - requires (sizeof...(_Slices) == sizeof...(_Extents)) - constexpr auto - __subextents(const extents<_IndexType, _Extents...>& __exts, - _Slices... __slices) - { - constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); - auto __impl = [&](index_sequence<_Indices...>) { - using _SubExtents = extents<_IndexType, - (__mdspan::__static_slice_extent<_IndexType, - _Extents...[__inv_map[_Indices]], - _Slices...[__inv_map[_Indices]]>())...>; - if constexpr (_SubExtents::rank_dynamic() == 0) - return _SubExtents{}; - else - { - using _StaticSubExtents = __mdspan::_StaticExtents< - __mdspan::__static_extents<_SubExtents>()>; - auto __create = [&](index_sequence<_Is...>) - { - constexpr auto __slice_idx = [__inv_map](size_t __i) consteval - { - return __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; - }; - - return _SubExtents{ - (__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>( - __exts, __slices...[__slice_idx(_Is)]))...}; - }; - constexpr auto __dyn_subrank = _SubExtents::rank_dynamic(); - return __create(make_index_sequence<__dyn_subrank>()); - } - }; - - return __impl(make_index_sequence<__inv_map.size()>()); - } + [__m](index_sequence<_Is...>) + { + return submdspan_mapping(__m, ((void) _Is, full_extent)...); + }(make_index_sequence<_Mapping::extents_type::rank()>()) + } -> __submdspan_mapping_result; + }; } template requires (sizeof...(_RawSlices) == sizeof...(_Extents)) constexpr auto submdspan_extents(const extents<_IndexType, _Extents...>& __exts, _RawSlices... __raw_slices) { auto __impl = [&__exts](auto... __slices) { __mdspan::__check_valid_slices(__exts, __slices...); return __mdspan::__subextents(__exts, __slices...); }; return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); } template requires (sizeof...(_Extents) == sizeof...(_RawSlices)) constexpr auto submdspan_canonicalize_slices(const extents<_IndexType, _Extents...>& __exts, _RawSlices... __raw_slices) { auto __impl = [&__exts](auto... __slices) { __mdspan::__check_valid_slices(__exts, __slices...); return std::make_tuple(__slices...); }; return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); } + + template + requires (sizeof...(_RawSlices) == _Extents::rank() + && __mdspan::__sliceable_mapping>) + constexpr auto + submdspan( + const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md, + _RawSlices... __raw_slices) + { + using _IndexType = typename _Extents::index_type; + auto __impl = [&__md](auto... __slices) + { + using __mdspan::submdspan_mapping; + __mdspan::__check_valid_slices(__md.extents(), __slices...); + auto __result = submdspan_mapping(__md.mapping(), __slices...); + return std::mdspan(__md.accessor().offset(__md.data_handle(), __result.offset), + __result.mapping, typename _Accessor::offset_policy(__md.accessor())); + }; + return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); + } #endif // __glibcxx_submdspan _GLIBCXX_END_NAMESPACE_VERSION } #endif #endif diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index c2a9293b05a..2dac6a6d887 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -1868,42 +1868,42 @@ export namespace std #endif using std::layout_left; using std::layout_right; using std::layout_stride; using std::default_accessor; #if __glibcxx_aligned_accessor using std::aligned_accessor; #endif using std::mdspan; #if __glibcxx_padded_layouts using std::layout_left_padded; using std::layout_right_padded; #endif #if __glibcxx_submdspan using std::strided_slice; using std::full_extent_t; using std::full_extent; using std::submdspan_mapping_result; using std::submdspan_canonicalize_slices; using std::submdspan_extents; + using std::submdspan; #endif - // FIXME mdsubspan } #endif // 20.2 export namespace std { using std::align; using std::allocator; using std::allocator_arg; using std::allocator_arg_t; using std::allocator_traits; using std::assume_aligned; #if __glibcxx_is_sufficiently_aligned using std::is_sufficiently_aligned; #endif using std::make_obj_using_allocator; using std::pointer_traits; using std::to_address; using std::uninitialized_construct_using_allocator; using std::uses_allocator; diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc new file mode 100644 index 00000000000..53e91407a9c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -0,0 +1,369 @@ +// { dg-do run { target c++26 } } +#include + +#include // TODO remove +#include +#include +#include "../layout_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +template + constexpr bool is_strided_slice = false; + +template + constexpr bool is_strided_slice> = true; + +template + constexpr void + fill(const MDSpan& md) + { + using IndexType = typename MDSpan::index_type; + auto exts = md.extents(); + if constexpr (exts.rank() == 3) + for(IndexType i = 0; i < exts.extent(0); ++i) + for(IndexType j = 0; j < exts.extent(1); ++j) + for(IndexType k = 0; k < exts.extent(2); ++k) + md[i, j, k] = 100 * i + 10 * j + k; + } + +template + class multi_index_generator + { + class EndIt + { }; + + class BeginIt + { + public: + constexpr + BeginIt(const std::array& shape) + : M_shape(shape) + { } + + constexpr BeginIt& + operator++() + { + if constexpr (Rank > 0) + { + ++M_indices[Rank-1]; + for(size_t i = Rank; i > 1; --i) + if (M_indices[i-1] == M_shape[i-1]) + { + M_indices[i-1] = 0; + ++M_indices[i-2]; + } + } + return *this; + } + + constexpr auto + operator*() + { return M_indices; } + + constexpr bool + operator==(EndIt) + { + if constexpr (Rank > 0) + return M_indices[0] == M_shape[0]; + else + return true; + } + + private: + std::array M_indices{}; + std::array M_shape; + }; + + public: + constexpr + multi_index_generator(std::array shape) + : M_shape(shape) + { } + + constexpr BeginIt + begin() const + { return BeginIt(M_shape); } + + constexpr EndIt + end() const + { return EndIt{}; } + + private: + std::array M_shape; + }; + +constexpr bool +test_multi_index() +{ + auto shape = std::array{3, 5, 7, 1}; + + std::vector> expected; + for (int i = 0; i < shape[0]; ++i) + for (int j = 0; j < shape[1]; ++j) + for (int k = 0; k + consteval auto + inv_collapsed_index_map() + { + constexpr size_t rank = sizeof...(Slices); + auto is_collapsing = std::array{std::same_as...}; + constexpr auto collapsed_rank = ((!std::same_as) + ... + 0); + + std::array ret; + if constexpr (collapsed_rank > 0) + for(size_t k = 0, i = 0; i < rank; ++i) + if (!is_collapsing[i]) + ret[k++] = i; + return ret; + } + +static_assert(inv_collapsed_index_map() + == std::array{}); + +static_assert(inv_collapsed_index_map() + == std::array{1}); + +template + constexpr std::vector + make_selection(IndexType extent, const Slice& slice) + { + if constexpr (std::convertible_to) + return {static_cast(slice)}; + else if constexpr (std::same_as) + { + auto ret = std::vector(static_cast(extent)); + std::ranges::iota(ret, 0); + return ret; + } + else if constexpr (is_strided_slice) + { + auto ret = std::vector{}; + size_t n = static_cast(slice.extent); + for(size_t i = 0; i < n; i += slice.stride) + ret.push_back(slice.offset + i); + return ret; + } + else + { + auto [begin, end] = slice; + auto ret = std::vector(static_cast(end - begin)); + std::ranges::iota(ret, begin); + return ret; + } + } + +template + constexpr bool + check_selection(std::index_sequence, auto md, Slices... slices) + { + auto exts = md.extents(); + auto outer_shape = std::array{exts.extent(0), exts.extent(1), exts.extent(2)}; + + constexpr auto full_index = inv_collapsed_index_map(); + auto make_slice = [](size_t i, auto slice) + { + if constexpr (std::same_as) + return i; + else + return slice; + }; + + auto loop_body = [&](std::index_sequence, auto ijk, + auto... slices) + { + auto submd = submdspan(md, slices...[I]...); + auto selection = std::tuple{make_selection(exts.extent(I), slices...[I])...}; + auto inner_shape = std::array{ + std::get(selection).size()... + }; + + for (auto ij : multi_index_generator(inner_shape)) + { + ((ijk[full_index[J]] = get(selection)[ij[J]]),...); + VERIFY(submd[ij] == md[ijk]); + } + }; + + for (auto ijk : multi_index_generator(outer_shape)) + loop_body(std::make_index_sequence(), ijk, + make_slice(ijk[I], slices...[I])...); + return true; + } + +template + constexpr bool + check_selection(std::mdspan md, Slices... slices) + { + auto indices = std::make_index_sequence(); + return check_selection(indices, md, slices...); + } + +template + constexpr bool + check_selection(std::extentsexts, Slices... slices) + { + auto run = [&](auto m) + { + auto storage = std::vector(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + fill(md); + return check_selection(md, slices...); + }; + + if constexpr (std::same_as) + { + auto m = typename Layout::mapping(exts, std::array{15, 2, 50}); + return run(m); + } + else + { + auto m = typename Layout::mapping(exts); + return run(m); + } + } + +template + constexpr bool + test_scalar_selection(auto exts) + { + check_selection(exts, collapse{}, collapse{}, collapse{}); + return true; + } + +template + constexpr bool + test_full_lines(auto exts) + { + check_selection(exts, all, collapse{}, collapse{}); + check_selection(exts, collapse{}, all, collapse{}); + check_selection(exts, collapse{}, collapse{}, all); + return true; + } + +template + constexpr bool + test_full_blocks(auto exts) + { + check_selection(exts, all, all, collapse{}); + check_selection(exts, all, collapse{}, all); + check_selection(exts, collapse{}, all, all); + return true; + } + +template + constexpr bool + test_cubes(auto exts) + { + auto s0 = std::pair{0, 2}; + auto s1 = std::pair{1, 4}; + auto s2 = std::pair{3, 7}; + + check_selection(exts, all, all, all); + check_selection(exts, all, all, s2); + check_selection(exts, s0, all, all); + check_selection(exts, s0, all, s2); + check_selection(exts, s0, s1, s2); + return true; + } + +template + constexpr bool + test_strided_line_selection(auto exts) + { + auto check = [&](auto s) + { + check_selection(exts, collapse{}, s, collapse{}); + }; + + check(std::strided_slice(0, 2, 2)); + check(std::strided_slice(0, 3, 2)); + check(std::strided_slice(1, 3, 2)); + check(std::strided_slice(1, std::cw<3>, std::cw<2>)); + return true; + } + +template + constexpr bool + test_strided_box_selection(auto exts) + { + auto s0 = std::strided_slice(0, 3, 2); + auto s1 = std::strided_slice(1, 4, 2); + auto s2 = std::strided_slice(0, 7, 3); + + check_selection(exts, s0, s1, s2); + return true; + } + +template + constexpr bool + test_all_cheap() + { + constexpr auto dyn_exts = std::extents(3, 5, 7); + constexpr auto sta_exts = std::extents{}; + + test_scalar_selection(dyn_exts); + test_scalar_selection(sta_exts); + static_assert(test_scalar_selection(dyn_exts)); + static_assert(test_scalar_selection(sta_exts)); + + test_full_lines(dyn_exts); + test_full_lines(sta_exts); + static_assert(test_full_lines(dyn_exts)); + static_assert(test_full_lines(sta_exts)); + + test_strided_box_selection(dyn_exts); + test_strided_box_selection(sta_exts); + static_assert(test_strided_box_selection(dyn_exts)); + static_assert(test_strided_box_selection(sta_exts)); + return true; + } + +template + constexpr bool + test_all_expensive() + { + auto run = [](auto exts) + { + test_full_blocks(exts); + test_cubes(exts); + }; + + run(std::extents(3, 5, 7)); + run(std::extents{}); + return true; + } + +template + constexpr bool + test_all() + { + test_all_cheap(); + test_all_expensive(); + return true; + } + +int +main() +{ + test_all(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc new file mode 100644 index 00000000000..a37d3cd588f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -0,0 +1,136 @@ +// { dg-do run { target c++26 } } +#include + +#include // TODO remove +#include "../layout_traits.h" +#include + +constexpr size_t dyn = std::dynamic_extent; + +template + constexpr auto + call_submdspan_mapping(const Mapping& m, std::tuple slices) + { + auto impl = [&](std::index_sequence) + { return submdspan_mapping(m, get(slices)...); }; + return impl(std::make_index_sequence()); + } + +template + constexpr bool + test_layout_unpadded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename(); + using Traits = LayoutTraits; + + { + auto m0 = typename Layout::mapping(std::extents()); + auto result = submdspan_mapping(m0); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto all = std::full_extent; + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{0, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto s0 = std::strided_slice{1, 1, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, 0, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded); + } + + { + auto s0 = std::strided_slice{1, 2, 1}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto slices = std::tuple{1, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + + { + auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>}; + auto slices = std::tuple{all, all, all, s3, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + return true; + } + +template + constexpr bool + test_layout_unpadded_padding_value() + { + using Traits = LayoutTraits()>; + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw}; + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw}; + auto all = std::full_extent; + + auto check = [&](auto exts, size_t expected) + { + auto m = typename Layout::mapping(Traits::make_extents(exts)); + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + auto padding_value = decltype(result.mapping)::padding_value; + VERIFY(padding_value == expected); + }; + + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); + check(std::extents(3, 5, 7, 11, 13), dyn); + return true; + } + +int +main() +{ + test_layout_unpadded_return_types(); + static_assert(test_layout_unpadded_return_types()); + + test_layout_unpadded_padding_value(); + static_assert(test_layout_unpadded_padding_value()); + return 0; +} + diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc new file mode 100644 index 00000000000..cdc8a2b7e23 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc @@ -0,0 +1,161 @@ +// { dg-do compile { target c++26 } } +#include + +#include + +template + constexpr bool + check_slice_range(Slices... slices) + { + auto m = typename Layout::mapping>{}; + auto storage = std::vector(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + + auto submd = submdspan(md, slices...); // { dg-error "expansion of" } + (void) submd; + return true; + } + +template + constexpr bool + test_int_under() + { + check_slice_range(1, -1, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_int_over() + { + check_slice_range(1, 5, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_under() + { + check_slice_range(1, std::tuple{-1, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_reversed() + { + check_slice_range(1, std::tuple{3, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_reversed()); // { dg-error "expansion of" } + +template + constexpr bool + test_tuple_over() + { + check_slice_range(1, std::tuple{0, 6}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_zero() + { + check_slice_range(1, std::strided_slice{1, 1, 0}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_offset_under() + { + check_slice_range(1, std::strided_slice{-1, 1, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_offset_over() + { + check_slice_range(1, std::strided_slice{6, 0, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } + +template + constexpr bool + test_strided_slice_extent_over() + { + check_slice_range(1, std::strided_slice{1, 5, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } + +namespace adl +{ + struct NoFull + { + template + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr auto + submdspan_mapping(mapping, int) + { return std::submdspan_mapping_result{mapping{}, 0}; } + }; + }; + + struct WrongReturnValue + { + template + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr int + submdspan_mapping(mapping, std::full_extent_t) + { return 42; } + }; + }; +} + +constexpr bool +test_invalid_mapping1() +{ + using Extents = std::extents; + auto map = adl::NoFull::mapping{}; + std::mdspan m(nullptr, map); + std::submdspan(m, 1); // { dg-error "no matching function" } + return true; +} +static_assert(test_invalid_mapping1()); + +constexpr bool +test_invalid_mapping2() +{ + using Extents = std::extents; + auto map = adl::WrongReturnValue::mapping{}; + std::mdspan m(nullptr, map); + std::submdspan(m, std::full_extent); // { dg-error "no matching function" } + return true; +} +static_assert(test_invalid_mapping2()); + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "non-constant condition" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "does not satisfy placeholder constraints" } From patchwork Thu Dec 4 21:28:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125935 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AF3B34C318AC for ; Thu, 4 Dec 2025 21:29:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AF3B34C318AC Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=aO/ySdNI X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) by sourceware.org (Postfix) with ESMTPS id D95CE4B1A361 for ; Thu, 4 Dec 2025 21:26:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D95CE4B1A361 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D95CE4B1A361 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.221.44 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883605; cv=none; b=Y+JW8Qws3mW/ltyg5VBkYii5Pc/TBQtOHPyEPCnmp02i00RVXe0uHNjWiuBfk/aSGhS5DuD8RtWAKuKcSEtEYVjzezYUdk8UyIjVT0+8olN3ug/urJN5AVYxHOki92tQkeun5E3pf9HwGX5hTpL0X34sd4Y0oBFyNktX2qlyGgI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883605; c=relaxed/simple; bh=tcNcss8Qw09hQwvZr8XIFNYiDpvtaIip5F2N1z07Ed4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=EiTca2Hl4QHOF1+t7FQ0rlm28hxvr2IlhIxf5Zc9dUmlbo8qcO30/XcgpQPJ4iLGPSthR5MMTyaqBYKu2xPGMAG2exDRLH6QF0aJq5VZ0MgcIJzVeux/vD4WyFRz9JdnYmgsmbLZRg5QH66fcAKfmdzTx0eql8VWPKcAeHq+bTg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D95CE4B1A361 Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-42e2e2eccd2so981596f8f.1 for ; Thu, 04 Dec 2025 13:26:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883604; x=1765488404; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9oS/cTCabovLNYVAOv0lbdpNIs5SPJ9FWb0s81VJ9G0=; b=aO/ySdNImzGQ3ebD0S/QbUnfKcegmuqGHQY3D+lUwRohlaLLmQIcUmJ/7bALhn/Krr gf4DIpZU+mu9QBQWkJxubpWVUxl6BgePqzjY+Jlk9OAjsdGlmDiezTlRZ80k+xpRPIKz es+FPblnBe6EJQLOm466rPy5F1V8aTV9Tgw4S0NaGzgWkFYaO7ysBOPlnoEKCgvNuZlh +mjqPg+XQ2d81tLyXVl1KYMSqst84jZi6PwJvIHf9P0Ed5uU8NIqwEnidCbp8oGbCNp7 EyiB6OQjhjlhuJCu9RxooYybXDp+mlGviu+wQpAC3UbMev2yY7uljTZeuAar2cW36Fee oXBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883604; x=1765488404; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=9oS/cTCabovLNYVAOv0lbdpNIs5SPJ9FWb0s81VJ9G0=; b=uHmcQMs30YNdWRMp3IK3txc/GdvsTejFc7ChPIfI+VAlyaYfFeQ0CcSAj65n4nVnCn cBWScgP4YXEZeJfVYPU4A9cv+OyIRUiX3L04mrD3TRhVK5TQdaftm1owLjho/Tu2yMwX KCTk/PVuLY0ZfTxKDld74R2LvslgIQEja+6z4rWslq6MOvzaZz4p25dnm8qoY88/rDEF NfNBB33KS1jWFkPdN8+eulhYq/7v2zLTIXsN+UwZbjfR7L4fqr9jctwJ2GQVZyjvYbEF sPdpOXn3Afb0a31XWg9ZBfSpEESyrJkS2wFw8Y4CBRZT/51l1awsXcvn2+7DQtrCnW6P wRrg== X-Gm-Message-State: AOJu0YxXQttU2+W1tVxq0nAS2r474ajwoA4hx/60UL7sfgIuoHbj8E2E qERSFXb6xg0IlDl/6dwp/H7lQsLhEKnAYKkx7zaICcH5lLWkRMM+dVMhoaTcqg== X-Gm-Gg: ASbGncvJ+ccR+psJ/5+j9O76XgjG3WTkBA2BR07JEZiCbEf/ZCthI/TGt9+o13NG8Qr v8ScM0wj8ZJ29HFi3dLhBIDNd/3ms4KU7zJogC3l+XRRCGUY9rYS3xROgSItRhv0phf8O+7ZGf3 +Tez/QfkHYrRwzLplNR73SHGMkZgO8aBLLtRzFR9Sly2JzmNT306WA+YvHm6qA6HyldloNPGswo n0eIl2leiURrqSqTfCPI4L7XCNZJX3YGXPt8fCNZW5hlXXCuuQ3ucnShjMETFmRwR5tfT6tU8AW 4wMAfvHbwimHxRPdmEggHjH3AiSW+U8gBq5xrYwcb2KCGYQNczPnHQxpp7Zg9osHQAH7ruBJrON P5mWI/VhZVheyH09hvAZ59TWIzfL2U7LsesNtMq963W+EWgUDFKboDDELUkGng2+yREE/pZtBro NX5O4SprnCRrJ6LIFGW+Itt8Nm87+nt3zNK6R312xvVhXiw8Zpk+S8qxnOH/wBrcGE6WP1JaE= X-Google-Smtp-Source: AGHT+IHYD2EAWCspPRizhXJMstMt5pb5P5WVGUkjmiiHLuF42VKcpAP+bUd6VoD5qn5fr/Mu+GRXmA== X-Received: by 2002:a05:6000:1a8a:b0:42b:5628:f4a3 with SMTP id ffacd0b85a97d-42f731678e9mr8197158f8f.1.1764883603527; Thu, 04 Dec 2025 13:26:43 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:43 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 2/5] libstdc++: Implement submdspan_mapping for layout_right. [PR110352] Date: Thu, 4 Dec 2025 22:28:43 +0100 Message-ID: <20251204212846.248887-3-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Adds submdspan_mapping for layout_right as described in P3663. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_right::mapping::submdspan_mapping): New friend function. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: Instantiate tests for layout_right. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: Ditto. * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: Ditto. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 39 +++++++++++++++++++ .../mdspan/submdspan/submdspan.cc | 1 + .../mdspan/submdspan/submdspan_mapping.cc | 6 +++ .../mdspan/submdspan/submdspan_neg.cc | 9 +++++ 4 files changed, 55 insertions(+) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index 54739c0008d..d8da7b41868 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -1284,40 +1284,71 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template using _PaddedLayout = layout_left_padded<_Pad>; template static consteval size_t _S_pad() { using _Extents = typename _Mapping::extents_type; constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(0, _Us); if constexpr (!__mdspan::__all_static(__sta_exts)) return dynamic_extent; else return __mdspan::__fwd_prod(__sta_exts); } template static consteval bool _S_is_unpadded_submdspan(span __slice_kinds) { return __mdspan::__is_block<_SubRank>(__slice_kinds); } }; + template<> + struct _SubMdspanMapping<_LayoutSide::__right> + { + using _Layout = layout_right; + template using _PaddedLayout = layout_right_padded<_Pad>; + + template + static consteval size_t + _S_pad() + { + using _Extents = typename _Mapping::extents_type; + constexpr auto __rank = _Extents::rank(); + constexpr auto __sta_exts + = __mdspan::__static_extents<_Extents>(_Us + 1, __rank); + if constexpr (!__mdspan::__all_static(__sta_exts)) + return dynamic_extent; + else + return __fwd_prod(__sta_exts); + } + + template + static consteval bool + _S_is_unpadded_submdspan(span __slice_kinds) + { + auto __rev_slice_kinds = array<_SliceKind, _Nm>{}; + for(size_t __i = 0; __i < _Nm; ++__i) + __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; + return __mdspan::__is_block<_SubRank>(span(__rev_slice_kinds)); + } + }; + template constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping) { return submdspan_mapping_result{__mapping, 0}; } template requires (sizeof...(_Slices) > 0) constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices) { constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); using _Trait = _SubMdspanMapping<__side>; constexpr auto __slice_kinds = __mdspan::__make_slice_kind_array<_Slices...>(); auto __offset = __mdspan::__suboffset(__mapping, __slices...); auto __sub_exts = __mdspan::__subextents(__mapping.extents(), __slices...); using _SubExts = decltype(__sub_exts); constexpr auto __sub_rank = _SubExts::rank(); if constexpr (__sub_rank == 0) return submdspan_mapping_result{ @@ -1656,40 +1687,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template 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 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)); } +#if __glibcxx_submdspan + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); } +#endif // __glibcxx_submdspan + [[no_unique_address]] extents_type _M_extents{}; }; namespace __mdspan { template concept __mapping_alike = requires { requires __is_extents; { _Mp::is_always_strided() } -> same_as; { _Mp::is_always_exhaustive() } -> same_as; { _Mp::is_always_unique() } -> same_as; bool_constant<_Mp::is_always_strided()>::value; bool_constant<_Mp::is_always_exhaustive()>::value; bool_constant<_Mp::is_always_unique()>::value; }; template constexpr typename _Mapping::index_type __linear_index_strides(const _Mapping& __m, _Indices... __indices) diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc index 53e91407a9c..cd6e9454b17 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -348,22 +348,23 @@ template }; run(std::extents(3, 5, 7)); run(std::extents{}); return true; } template constexpr bool test_all() { test_all_cheap(); test_all_expensive(); return true; } int main() { test_all(); + test_all(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc index a37d3cd588f..cc832cdb415 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -112,25 +112,31 @@ template auto m = typename Layout::mapping(Traits::make_extents(exts)); auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); auto padding_value = decltype(result.mapping)::padding_value; VERIFY(padding_value == expected); }; check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); check(std::extents(3, 5, 7, 11, 13), dyn); return true; } int main() { test_layout_unpadded_return_types(); static_assert(test_layout_unpadded_return_types()); + test_layout_unpadded_return_types(); + static_assert(test_layout_unpadded_return_types()); + test_layout_unpadded_padding_value(); static_assert(test_layout_unpadded_padding_value()); + + test_layout_unpadded_padding_value(); + static_assert(test_layout_unpadded_padding_value()); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc index cdc8a2b7e23..5d977822dfe 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc @@ -7,112 +7,121 @@ template constexpr bool check_slice_range(Slices... slices) { auto m = typename Layout::mapping>{}; auto storage = std::vector(m.required_span_size()); auto md = std::mdspan(storage.data(), m); auto submd = submdspan(md, slices...); // { dg-error "expansion of" } (void) submd; return true; } template constexpr bool test_int_under() { check_slice_range(1, -1, 2); // { dg-error "expansion of" } return true; } static_assert(test_int_under()); // { dg-error "expansion of" } +static_assert(test_int_under()); // { dg-error "expansion of" } template constexpr bool test_int_over() { check_slice_range(1, 5, 2); // { dg-error "expansion of" } return true; } static_assert(test_int_over()); // { dg-error "expansion of" } +static_assert(test_int_over()); // { dg-error "expansion of" } template constexpr bool test_tuple_under() { check_slice_range(1, std::tuple{-1, 2}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_under()); // { dg-error "expansion of" } +static_assert(test_tuple_under()); // { dg-error "expansion of" } template constexpr bool test_tuple_reversed() { check_slice_range(1, std::tuple{3, 2}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_reversed()); // { dg-error "expansion of" } +static_assert(test_tuple_reversed()); // { dg-error "expansion of" } template constexpr bool test_tuple_over() { check_slice_range(1, std::tuple{0, 6}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_over()); // { dg-error "expansion of" } +static_assert(test_tuple_over()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_zero() { check_slice_range(1, std::strided_slice{1, 1, 0}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } +static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_offset_under() { check_slice_range(1, std::strided_slice{-1, 1, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_offset_over() { check_slice_range(1, std::strided_slice{6, 0, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_extent_over() { check_slice_range(1, std::strided_slice{1, 5, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } +static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } namespace adl { struct NoFull { template class mapping { public: using extents_type = Extents; using index_type = typename extents_type::index_type; private: friend constexpr auto submdspan_mapping(mapping, int) { return std::submdspan_mapping_result{mapping{}, 0}; } }; }; struct WrongReturnValue From patchwork Thu Dec 4 21:28:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125937 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1EB724BB593C for ; Thu, 4 Dec 2025 21:32:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1EB724BB593C Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=TMnSrlIU X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) by sourceware.org (Postfix) with ESMTPS id AE38C4BBCDB5 for ; Thu, 4 Dec 2025 21:26:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AE38C4BBCDB5 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org AE38C4BBCDB5 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883605; cv=none; b=x9iI4TCNciNBfEVKrQyq6xdAEz3ww9sWVUKH+0p6uB1kfKnXFvu959fdzXLTHvdyjUgvl2TmbAhh2JhMRE6vHmkRdDFzf8/WgnyP688LXHHXs/vrWaScAMf+Fks+G8LOqOMGkncO73G3zPyuva+ui/BBmQQtU1l8+caCYyECQC4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883605; c=relaxed/simple; bh=xOIXqIjHMbjrcb/hpQHcqErj1OF04Je8iYLTOLxkQA0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=vFG/FC+j8oBk1ytbCft3qGy2IMysVUWyyqzRoXYmLzwJqsqXBpZdDZA0dsOlnIH3yF84z7TJjshVa+EE8Hb1CyT9jmrdrf54eT1D4R+HUwDHQ/wts/VVX/HC8ZKdtFFDtnG8ymlM1+qr/+Thqp8fnH1kxLnX2a6zjjph4TXcxNc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AE38C4BBCDB5 Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-4779adb38d3so13154115e9.2 for ; Thu, 04 Dec 2025 13:26:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883604; x=1765488404; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ZTXGtAxNv4kJke2HrPob/wqWmj7NPyUfRm99bykGVgE=; b=TMnSrlIU+e4piJ6k93azZzxLMtQgSG4rlFet+y9khWBd7mOpNSf1lxdIOB2AGLGJxL zLLFhaeFs7cTSxvvan5q7+QJZLBZ+3f25D4y5l3yZhlQ6PTzm4AG6qW7WRi2xzrRWlAU MmdAsfRHJBBFwmEITdUMVU8mlQAmaNqEZic0si1Z4g9sfP4yTmHTeNQ+P8WionMW1Da1 GOqn2J5geov98MFTBuibKEvh/GLphS3OjWFbujyemmWNziaqxhxCK07kM1QlD77vpc7u JjUToLGY6pdwzbSv3OshSmOZpavQMUbuvF65M5VKutKqWRku7l7QiqjwdWN+mamrb/yx qfPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883604; x=1765488404; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ZTXGtAxNv4kJke2HrPob/wqWmj7NPyUfRm99bykGVgE=; b=Z9pyWhJdHHSG+bIFdfyXnwgfiQgAVUSjc1ZTHnptyogc6K1OV4MUl21PcrdyVJmkXs thq5H+gb6smrfFBdi/k6P7GsLvwCVoXokfE09c3UAdbRipVn6uJzs6nKSroAYNWfCk4q 1YTW9jwMnwfqPLfxq/aGezYQ/RjYCAeeIbBqBm6zwEEJR60XekNBGiNXbhi16W+C2u/m otlcWSpVKvAfiESrlFe/+CN1fweuGlFlsNyDK1H4zhR/KNWk01tbiw34GemDsU1mc6q2 wRum+t+9NlxotRdc2ZyxjwCh/gsubDqGnnhzXKhwNprJiKhZpivPQRqJRE5IjGKKsVq/ Iouw== X-Gm-Message-State: AOJu0Yy1AolzY/zhWwWvUSFQ5A7c9Qkh1NzTfkQuIomeqh7mwQj/U1Om K1Kv3awXNGkyxsW0BicnvyBMgsssiQpLq3LIIBqVBd44GuBlYjtqXiKr2KQbGw== X-Gm-Gg: ASbGncvwZHdsHT4UkuxT7cmA2RtFylZMdKXznTm259z/2h7qvqEIRawDwuXlRzLVuzR +QExq6bfuGdS/pt69Un5bjLJXPiXr+RlJftqY7dImr3Mn2jYcvxeY5Onmapv62gbl6qXLvWMM9I PmCV+mwK5Y4g0pJACt5ymSmYk6/6yZ3UdbO7LvrXzfzcLOE/PZUKRSrqvr63vbOMd8FZG5sdhgi lvsKzeyv2FBppOSbhigL4FByVMN3xOzCd1CGT9QKYjYyKAJ/en920sKuC6qUhKF7x+KMMByPlRr Yc0gbThfL6n4Vlqvosf70DsDgJj1yKeg8pRJp+f1lG4kaFUTTdKF4mpDmskG7vIAOHEx/YA+fdg UOfHROGTUHa88uTRrUGsW2Sn14KZ1mCG72T0clWWimoTsC/JrFnrfpmcwL6AGYiHFbrrDlbnT0o lUwr+/+l/6lWmG6yBHdCviRUzRFfmV64u4+3bPTVYDxBPDPYoTzCWzAbeVOyTP2n9/my/q93I= X-Google-Smtp-Source: AGHT+IFSneJCrHpDeqPvvh7O1svJLqC5qPbFBChuYOg83zny2rL0tcal7oqQUDqXmKM170EYqBn2Eg== X-Received: by 2002:a05:600c:4592:b0:46e:53cb:9e7f with SMTP id 5b1f17b1804b1-4792f38615amr40949485e9.18.1764883604478; Thu, 04 Dec 2025 13:26:44 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:43 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 3/5] libstdc++: Implement submdspan_mapping for layout_stride. [PR110352] Date: Thu, 4 Dec 2025 22:28:44 +0100 Message-ID: <20251204212846.248887-4-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Add submdspan_mapping for layout_stride as in P3663. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_stride::mapping::submdspan_mapping): New friend function. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: Instantiate tests for layout_stride. * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: Ditto. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: Add tests for layout_stride. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 25 +++++++++++++++++++ .../mdspan/submdspan/submdspan.cc | 1 + .../mdspan/submdspan/submdspan_mapping.cc | 18 +++++++++++++ .../mdspan/submdspan/submdspan_neg.cc | 9 +++++++ 4 files changed, 53 insertions(+) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index d8da7b41868..8b6c24885ae 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -1905,40 +1905,65 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr index_type stride(rank_type __r) const noexcept { return _M_strides[__r]; } template<__mdspan::__mapping_alike _OMapping> requires ((extents_type::rank() == _OMapping::extents_type::rank()) && _OMapping::is_always_strided()) friend constexpr bool operator==(const mapping& __self, const _OMapping& __other) noexcept { if (__self.extents() != __other.extents()) return false; if constexpr (extents_type::rank() > 0) for (size_t __i = 0; __i < extents_type::rank(); ++__i) if (!cmp_equal(__self.stride(__i), __other.stride(__i))) return false; return __mdspan::__offset(__other) == 0; } private: +#if __glibcxx_submdspan + constexpr auto + _M_submdspan_mapping_impl() const + { return submdspan_mapping_result{*this, 0}; } + + template + requires (sizeof...(_Slices) > 0) + constexpr auto + _M_submdspan_mapping_impl(_Slices... __slices) const + { + auto __offset = __mdspan::__suboffset(*this, __slices...); + auto __sub_exts = __mdspan::__subextents(extents(), __slices...); + auto __sub_strides + = __mdspan::__substrides(*this, __slices...); + return submdspan_mapping_result{ + layout_stride::mapping(__sub_exts, __sub_strides), __offset}; + } + + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mapping._M_submdspan_mapping_impl(__slices...); } +#endif + using _Strides = typename __array_traits::_Type; [[no_unique_address]] extents_type _M_extents; [[no_unique_address]] _Strides _M_strides; }; #ifdef __glibcxx_padded_layouts namespace __mdspan { constexpr size_t __least_multiple(size_t __x, size_t __y) { if (__x <= 1) return __y; return (__y / __x + (__y % __x != 0)) * __x ; } template constexpr bool __is_representable_least_multiple(size_t __x, size_t __y) diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc index cd6e9454b17..645c4711294 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -349,22 +349,23 @@ template run(std::extents(3, 5, 7)); run(std::extents{}); return true; } template constexpr bool test_all() { test_all_cheap(); test_all_expensive(); return true; } int main() { test_all(); test_all(); + test_all(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc index cc832cdb415..cf6167dc3b0 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -106,37 +106,55 @@ template auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw}; auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw}; auto all = std::full_extent; auto check = [&](auto exts, size_t expected) { auto m = typename Layout::mapping(Traits::make_extents(exts)); auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); auto padding_value = decltype(result.mapping)::padding_value; VERIFY(padding_value == expected); }; check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); check(std::extents(3, 5, 7, 11, 13), dyn); return true; } +constexpr bool +test_layout_stride_return_types() +{ + auto exts = std::extents(3, 5); + auto m = std::layout_stride::mapping(exts, std::array{2, 12}); + + using index_type = decltype(exts)::index_type; + auto s1 = std::strided_slice{index_type(2), index_type(2), + std::cw}; + auto result = submdspan_mapping(m, index_type(1), s1); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as); + return true; +} + int main() { test_layout_unpadded_return_types(); static_assert(test_layout_unpadded_return_types()); test_layout_unpadded_return_types(); static_assert(test_layout_unpadded_return_types()); + test_layout_stride_return_types(); + static_assert(test_layout_stride_return_types()); + test_layout_unpadded_padding_value(); static_assert(test_layout_unpadded_padding_value()); test_layout_unpadded_padding_value(); static_assert(test_layout_unpadded_padding_value()); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc index 5d977822dfe..8ee8af08fc1 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc @@ -8,120 +8,129 @@ template check_slice_range(Slices... slices) { auto m = typename Layout::mapping>{}; auto storage = std::vector(m.required_span_size()); auto md = std::mdspan(storage.data(), m); auto submd = submdspan(md, slices...); // { dg-error "expansion of" } (void) submd; return true; } template constexpr bool test_int_under() { check_slice_range(1, -1, 2); // { dg-error "expansion of" } return true; } static_assert(test_int_under()); // { dg-error "expansion of" } static_assert(test_int_under()); // { dg-error "expansion of" } +static_assert(test_int_under()); // { dg-error "expansion of" } template constexpr bool test_int_over() { check_slice_range(1, 5, 2); // { dg-error "expansion of" } return true; } static_assert(test_int_over()); // { dg-error "expansion of" } static_assert(test_int_over()); // { dg-error "expansion of" } +static_assert(test_int_over()); // { dg-error "expansion of" } template constexpr bool test_tuple_under() { check_slice_range(1, std::tuple{-1, 2}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_under()); // { dg-error "expansion of" } static_assert(test_tuple_under()); // { dg-error "expansion of" } +static_assert(test_tuple_under()); // { dg-error "expansion of" } template constexpr bool test_tuple_reversed() { check_slice_range(1, std::tuple{3, 2}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_reversed()); // { dg-error "expansion of" } static_assert(test_tuple_reversed()); // { dg-error "expansion of" } +static_assert(test_tuple_reversed()); // { dg-error "expansion of" } template constexpr bool test_tuple_over() { check_slice_range(1, std::tuple{0, 6}, 2); // { dg-error "expansion of" } return true; } static_assert(test_tuple_over()); // { dg-error "expansion of" } static_assert(test_tuple_over()); // { dg-error "expansion of" } +static_assert(test_tuple_over()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_zero() { check_slice_range(1, std::strided_slice{1, 1, 0}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } +static_assert(test_strided_slice_zero()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_offset_under() { check_slice_range(1, std::strided_slice{-1, 1, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_under()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_offset_over() { check_slice_range(1, std::strided_slice{6, 0, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_over()); // { dg-error "expansion of" } template constexpr bool test_strided_slice_extent_over() { check_slice_range(1, std::strided_slice{1, 5, 1}, 2); // { dg-error "expansion of" } return true; } static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } +static_assert(test_strided_slice_extent_over()); // { dg-error "expansion of" } namespace adl { struct NoFull { template class mapping { public: using extents_type = Extents; using index_type = typename extents_type::index_type; private: friend constexpr auto submdspan_mapping(mapping, int) { return std::submdspan_mapping_result{mapping{}, 0}; } }; }; struct WrongReturnValue From patchwork Thu Dec 4 21:28:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125938 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 20D1C4B1A361 for ; Thu, 4 Dec 2025 21:33:14 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 20D1C4B1A361 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=YZUcweRp X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-f52.google.com (mail-wr1-f52.google.com [209.85.221.52]) by sourceware.org (Postfix) with ESMTPS id 10C3A4BC7EDF for ; Thu, 4 Dec 2025 21:26:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 10C3A4BC7EDF Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 10C3A4BC7EDF Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.221.52 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883607; cv=none; b=jfVPRZJ8xbjljK9xHu1CmOj3GgdV2Me8ONCTvqa5YkE7EGol3bJuVqvR3fDDumvzxmlaZGPf/byfcjRtumEBTciDeDLGmgn3/6WojjEVX8nQ3l+MM8QTU1Fe9hoLLb2YDFNQtjj+4lX4IgjeW4hx7vEK/+113/Dc8tLXcFrL4xs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883607; c=relaxed/simple; bh=C0lpsC+ZLldJrGDLMy2OQKD5OzI/G9kML5kTqSI6wk4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=b3PebQcTywWRq7SeGFDZaaP6fikdJtt8tFO4nOd7xmSoby5x1Vy4vTgoV0GDfH90Co2LTdR1y7Xjrj1ceinNOcR63n1xakSgAzf25MevCdGQgoBSyv3qpl/uT5+FyDBFY/b9ANI4EAcq0TAekxjpcPx0yINXgH2brwb0egZpvco= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 10C3A4BC7EDF Received: by mail-wr1-f52.google.com with SMTP id ffacd0b85a97d-42e2e167067so733926f8f.2 for ; Thu, 04 Dec 2025 13:26:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883606; x=1765488406; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=44mmVcNXGoBS8wyZEN1+F+nHzy0GbOgIjFX4kwDUmFY=; b=YZUcweRpOdAu0GWxR9YYQyNshmBZxzWQ3YA9/iQlE77QBzrr7Jm9kYZ0szPCGdRhyG cnCj0yeOe3C5g3WbRQBrZDyvujpIYjn9kDXSeesdFgkDZu2Nc98mrwfbuDSpvV806LXi WbvB+wbw5uhXb+u6sURlTbEUAzQMcWjfJSeQ4vLpLOtEjcKTCh4E0cE4hGTEh0t0blOo mToAcUz2GtZKCLtatadus6L8cHqgm++NQ84luRXcoWleWT/Z2ZkXuLtEmkeK9VtNg4EZ NmQgwuPpKtb6tBwSErOT7SbtFmAGzpgxyj8IOBtNPo/+1U2ggpbOH07SglIInJk/2ayd K/4Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883606; x=1765488406; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=44mmVcNXGoBS8wyZEN1+F+nHzy0GbOgIjFX4kwDUmFY=; b=Y974iZ/lSePDWfuprm9c2C92m7xwmsCv2ITQ6ts4WPv2ifLA4DNRaQ/OwU6dLaw/Al SHbD6zFWtHfPm4POfzXyZlaic67MHtrDFE9PrvehVzML9ElxNH83uj7v5itxedFx5GfT b+GdOJBFMaBU3JiKYicB1vvGkcDj8EENlMJfoK93sU+CULDywXA8StR7i0NcvQItde76 RB7l7z9G+vqe3zDpVnn1XhZ90FTjt5EeC9x9oJnsYHgrVOP1xw2p7nXampqnVIPq2rMJ V6DNlGNojq9FW7NUCMZUut5GyPW6FgsFG/pRoo7P46uaeIW0tMHTZkLrBGzxsAxZ0n+B I76w== X-Gm-Message-State: AOJu0YzlVt48JREpuWm53LDHm7OArc9ejlsIhsBlzlh60X4x1dPYuvFK hns5CH0azW0MyCjDlXIwwNAIK+WnlrFgWKGui+zqOlRW9Ca3UJF3iXK3 X-Gm-Gg: ASbGncuvlfogoN/JsS8/Z4tzI0T66R5qq8OuuJkf5nD376mS/8B77mb53aogQ/t8CnG KfkOO3JClLw5X/Uss8Hr7q4RXMMOncC+Z0Hx5CuLBKE2UulZdn34AZiUAVTLtjkNM77AcZ0HKL4 KeEZMbGKMtAKu4hnArN1CwK3MCf5BT5myZA49WbdHZpnE3gBSf6O3eXzGNoknmOC69YbvCz84d7 E76qcHsYrs7qGx1ip5dhKXxOxM1Lt8wIThhiabECdGy2wiulkNmlUpUJ+nad2+TNugOTWA3bW6o 7yRq8mzwcU/gZrDa9Awd5UMQW8h1xr72nxPmmHAOj6hoej+yYbFIonfcJvi/DA6fI6a0dIJhVkI T/J2c3eQPlSR5kZ0OwXaVQQlZnc1LgEQcMzf2BO5TTRw7fWNiYLc5D1t8B8F/2AAqaxFVjqP7oo Nd+bqifLG3+6BXmDAdVEFX7peHV1HIDKBtcI6K33tHc8Ihr0Jj/hngK+ssUFfh X-Google-Smtp-Source: AGHT+IEaagbF2huD8QLoxn2lzCw/AGBPxHGnKsxmwMuMZQvb17YRWRdijPaKXte6/RtKnsp9Z27XOA== X-Received: by 2002:a05:6000:4011:b0:429:d4e1:cbb5 with SMTP id ffacd0b85a97d-42f7317420cmr8510098f8f.8.1764883605716; Thu, 04 Dec 2025 13:26:45 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:45 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 4/5] libstdc++: Implement submdspan_mapping for layout_left_padded. [PR110352] Date: Thu, 4 Dec 2025 22:28:45 +0100 Message-ID: <20251204212846.248887-5-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-13.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Implements submdspan for layout_left_padded as described in P3663. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_left_padded::submdspan_mapping): New friend method. * testsuite/23_containers/mdspan/layout_traits.h (LayoutTraits::layout_same_padded): New template type alias. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: Restructed and new tests for layout_left_padded. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: Ditto. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 55 +++++- .../23_containers/mdspan/layout_traits.h | 4 + .../mdspan/submdspan/submdspan.cc | 3 + .../mdspan/submdspan/submdspan_mapping.cc | 179 ++++++++++++++++-- 4 files changed, 216 insertions(+), 25 deletions(-) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index 8b6c24885ae..21dbe6def22 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -795,40 +795,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION || (__static_quotient<_Extents, _IndexType>() != 0); template concept __mapping_of = is_same_v, _Mapping>; template typename _Layout, typename _Mapping> concept __padded_mapping_of = __mapping_of< _Layout<_Mapping::padding_value>, _Mapping>; #ifdef __glibcxx_padded_layouts template constexpr bool __is_left_padded_mapping = __padded_mapping_of< layout_left_padded, _Mapping>; template constexpr bool __is_right_padded_mapping = __padded_mapping_of< layout_right_padded, _Mapping>; + + template + constexpr bool __is_padded_mapping = __is_left_padded_mapping<_Mapping> + || __is_right_padded_mapping<_Mapping>; #endif template consteval size_t __get_static_stride() { return _PaddedMapping::_PaddedStorage::_S_static_stride; } template concept __standardized_mapping = __mapping_of || __mapping_of || __mapping_of #ifdef __glibcxx_padded_layouts || __is_left_padded_mapping<_Mapping> || __is_right_padded_mapping<_Mapping> #endif ; // A tag type to create internal ctors. class __internal_ctor { }; @@ -1257,110 +1261,144 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<_LayoutSide _Side, size_t _SubRank, size_t _Nm> static consteval size_t __padded_block_begin(span __slice_kinds) { if constexpr (_Side == _LayoutSide::__left) return __mdspan::__padded_block_begin_generic<_SubRank>(__slice_kinds); else { std::array<_SliceKind, _Nm> __rev_slice_kinds; for(size_t __i = 0; __i < _Nm; ++__i) __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; auto __u = __mdspan::__padded_block_begin_generic<_SubRank>( std::span(__rev_slice_kinds)); return __u == dynamic_extent ? dynamic_extent : _Nm - 1 - __u; } } - template<_LayoutSide _Side> + template<_LayoutSide _Side, bool _Padded> struct _SubMdspanMapping; template<> - struct _SubMdspanMapping<_LayoutSide::__left> + struct _SubMdspanMapping<_LayoutSide::__left, false> { using _Layout = layout_left; template using _PaddedLayout = layout_left_padded<_Pad>; template static consteval size_t _S_pad() { using _Extents = typename _Mapping::extents_type; constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(0, _Us); if constexpr (!__mdspan::__all_static(__sta_exts)) return dynamic_extent; else return __mdspan::__fwd_prod(__sta_exts); } template static consteval bool _S_is_unpadded_submdspan(span __slice_kinds) { return __mdspan::__is_block<_SubRank>(__slice_kinds); } }; template<> - struct _SubMdspanMapping<_LayoutSide::__right> + struct _SubMdspanMapping<_LayoutSide::__left, true> + { + using _Layout = layout_left; + template using _PaddedLayout = layout_left_padded<_Pad>; + + template + static consteval size_t + _S_pad() + { + using _Extents = typename _Mapping::extents_type; + constexpr auto __sta_exts + = __mdspan::__static_extents<_Extents>(1, _Us); + constexpr auto __sta_padstride + = __mdspan::__get_static_stride<_Mapping>(); + if constexpr (__sta_padstride == dynamic_extent + || !__mdspan::__all_static(__sta_exts)) + return dynamic_extent; + else + return __sta_padstride * __mdspan::__fwd_prod(__sta_exts); + } + + template + static consteval bool + _S_is_unpadded_submdspan(span __slice_kinds) + { + if constexpr (_SubRank == 1) + return __slice_kinds[0] == _SliceKind::__unit_strided_slice + || __slice_kinds[0] == _SliceKind::__full; + else + return false; + } + }; + + template<> + struct _SubMdspanMapping<_LayoutSide::__right, false> { using _Layout = layout_right; template using _PaddedLayout = layout_right_padded<_Pad>; template static consteval size_t _S_pad() { using _Extents = typename _Mapping::extents_type; constexpr auto __rank = _Extents::rank(); constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(_Us + 1, __rank); if constexpr (!__mdspan::__all_static(__sta_exts)) return dynamic_extent; else return __fwd_prod(__sta_exts); } template static consteval bool _S_is_unpadded_submdspan(span __slice_kinds) { auto __rev_slice_kinds = array<_SliceKind, _Nm>{}; for(size_t __i = 0; __i < _Nm; ++__i) __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; return __mdspan::__is_block<_SubRank>(span(__rev_slice_kinds)); } }; template constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping) { return submdspan_mapping_result{__mapping, 0}; } template requires (sizeof...(_Slices) > 0) constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices) { constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); - using _Trait = _SubMdspanMapping<__side>; + using _Trait = _SubMdspanMapping<__side, __is_padded_mapping<_Mapping>>; constexpr auto __slice_kinds = __mdspan::__make_slice_kind_array<_Slices...>(); auto __offset = __mdspan::__suboffset(__mapping, __slices...); auto __sub_exts = __mdspan::__subextents(__mapping.extents(), __slices...); using _SubExts = decltype(__sub_exts); constexpr auto __sub_rank = _SubExts::rank(); if constexpr (__sub_rank == 0) return submdspan_mapping_result{ typename _Trait::_Layout::mapping(__sub_exts), __offset}; else if constexpr ( _Trait::template _S_is_unpadded_submdspan<__sub_rank>( std::span(__slice_kinds))) return submdspan_mapping_result{ typename _Trait::_Layout::mapping(__sub_exts), __offset}; else if constexpr ( constexpr auto __u = __padded_block_begin<__side, __sub_rank>( span(__slice_kinds)); __u != dynamic_extent) { constexpr auto __pad = _Trait::template _S_pad<_Mapping, __u>(); @@ -2536,40 +2574,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr index_type stride(rank_type __r) const noexcept { __glibcxx_assert(__r < _S_rank); if (__r == 0) return 1; else return static_cast( static_cast(_M_padstride()) * static_cast(__mdspan::__fwd_prod(extents(), 1, __r))); } template requires(__mdspan::__is_left_padded_mapping<_LeftpadMapping> && _LeftpadMapping::extents_type::rank() == _S_rank) friend constexpr bool operator==(const mapping& __self, const _LeftpadMapping& __other) noexcept { return __self._M_storage._M_equal(__other); } + + private: +#if __glibcxx_submdspan + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); } +#endif // __glibcxx_submdspan }; template template class layout_right_padded<_PaddingValue>::mapping { public: static constexpr size_t padding_value = _PaddingValue; 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_right_padded<_PaddingValue>; private: static constexpr size_t _S_rank = extents_type::rank(); using _PaddedStorage = __mdspan::_PaddedStorage<_PaddingValue, _Extents, __mdspan::_RightPaddedLayoutTraits<_S_rank>>; [[no_unique_address]] _PaddedStorage _M_storage; consteval friend size_t diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h index 619cab53b9e..f0aeac320de 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h @@ -70,79 +70,83 @@ struct DeducePaddingSide constexpr static PaddingSide from_typename() { if constexpr (std::same_as) return PaddingSide::Left; else if constexpr (is_left_padded) return PaddingSide::Left; else return PaddingSide::Right; } }; template struct LayoutTraits; template<> struct LayoutTraits { using layout_same = std::layout_left; using layout_other = std::layout_right; + template + using layout_same_padded = std::layout_left_padded; template using extents_type = Extents; template constexpr static extents_type make_extents(const Extents& exts) { return exts; } template constexpr static std::array make_array(const std::array& a) { return a; } template constexpr static auto make_indices(Indices... indices) { return std::array{indices...}; } template constexpr static std::tuple make_tuple(const std::tuple& tup) { return tup; } template constexpr static auto padded_stride(const Mapping& m) { return m.stride(1); } template constexpr static auto padded_extent(const Extents& exts) { return exts.extent(0); } }; template<> struct LayoutTraits { using layout_same = std::layout_right; + template + using layout_same_padded = std::layout_right_padded; using layout_other = std::layout_left; template constexpr static auto make_extents(const std::extents& exts) { constexpr size_t rank = sizeof...(Extents); auto impl = [&](std::index_sequence) { auto dyn_exts = make_array(dynamic_extents_array(exts)); return std::extents(dyn_exts); }; return impl(std::make_index_sequence()); } template using extents_type = decltype(make_extents(std::declval())); template constexpr static std::array diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc index 645c4711294..927b5d06c6b 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -348,24 +348,27 @@ template }; run(std::extents(3, 5, 7)); run(std::extents{}); return true; } template constexpr bool test_all() { test_all_cheap(); test_all_expensive(); return true; } int main() { test_all(); + test_all>(); + test_all>(); + test_all>(); test_all(); test_all(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc index cf6167dc3b0..50836968a06 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -1,69 +1,63 @@ // { dg-do run { target c++26 } } #include #include // TODO remove #include "../layout_traits.h" #include constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; template constexpr auto call_submdspan_mapping(const Mapping& m, std::tuple slices) { auto impl = [&](std::index_sequence) { return submdspan_mapping(m, get(slices)...); }; return impl(std::make_index_sequence()); } template constexpr bool - test_layout_unpadded_return_types() + test_layout_common_return_types() { constexpr auto padding_side = DeducePaddingSide::from_typename(); using Traits = LayoutTraits; + using layout_unpadded = typename Traits::layout_same; { auto m0 = typename Layout::mapping(std::extents()); auto result = submdspan_mapping(m0); using layout_type = typename decltype(result.mapping)::layout_type; static_assert(std::same_as); } auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); auto m = typename Layout::mapping(exts); - auto all = std::full_extent; auto s251 = std::strided_slice{2, 5, std::cw<1>}; { auto slices = std::tuple{0, 0, 0, 0, 0}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); using layout_type = typename decltype(result.mapping)::layout_type; - static_assert(std::same_as); - } - - { - auto slices = std::tuple{all, all, all, s251, 0}; - auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); - using layout_type = typename decltype(result.mapping)::layout_type; - static_assert(std::same_as); + static_assert(std::same_as); } { auto s0 = std::strided_slice{1, 1, std::cw<1>}; auto slices = std::tuple{s0, all, all, s251, 0}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); using layout_type = typename decltype(result.mapping)::layout_type; static_assert(is_same_padded); } { auto s0 = std::strided_slice{1, 2, std::cw<1>}; auto slices = std::tuple{s0, all, all, s251, 0}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); using layout_type = typename decltype(result.mapping)::layout_type; static_assert(is_same_padded); } { auto s0 = std::strided_slice{1, 2, std::cw<1>}; @@ -81,80 +75,223 @@ template static_assert(std::same_as); } { auto slices = std::tuple{1, all, all, s251, 0}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); using layout_type = decltype(result.mapping)::layout_type; static_assert(std::same_as); } { auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>}; auto slices = std::tuple{all, all, all, s3, 0}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); using layout_type = decltype(result.mapping)::layout_type; static_assert(std::same_as); } return true; } +template + constexpr bool + test_layout_unpadded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename(); + using Traits = LayoutTraits; + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + return true; + } + +template + constexpr bool + test_layout_padded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename(); + using Traits = LayoutTraits; + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same_padded; + static_assert(std::same_as); + } + + { + auto slices = std::tuple{all, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same; + static_assert(std::same_as); + } + + { + auto s121 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s121, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same; + static_assert(std::same_as); + } + + { + auto s121 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{0, s121, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as); + } + return true; + } + template constexpr bool test_layout_unpadded_padding_value() { using Traits = LayoutTraits()>; auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw}; auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw}; - auto all = std::full_extent; auto check = [&](auto exts, size_t expected) { auto m = typename Layout::mapping(Traits::make_extents(exts)); auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); auto padding_value = decltype(result.mapping)::padding_value; VERIFY(padding_value == expected); }; check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); check(std::extents(3, 5, 7, 11, 13), dyn); return true; } +template +constexpr size_t static_padding_value = 1; + +template +constexpr size_t static_padding_value> = PaddingValue; + +template +constexpr size_t static_padding_value> = PaddingValue; + +template + constexpr bool + test_layout_padded_padding_value() + { + using Traits = LayoutTraits()>; + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw}; + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw}; + + auto check = [&](auto exts, size_t expected) + { + auto m = typename Layout::mapping(Traits::make_extents(exts)); + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + auto padding_value = decltype(result.mapping)::padding_value; + VERIFY(padding_value == expected); + }; + + auto pad = [](int n, int m) -> size_t + { + constexpr auto padding_value = static_padding_value; + if constexpr (padding_value != dyn) + { + auto npad = ((n + padding_value - 1) / padding_value) * padding_value; + return npad * m; + } + else + return dyn; + }; + + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), pad(3, 5)); + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), pad(3, 5)); + check(std::extents(std::cw<3>, std::cw<6>, 7, 11, 13), pad(3, 6)); + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); + check(std::extents(3, 5, 7, 11, 13), dyn); + return true; + } + constexpr bool test_layout_stride_return_types() { auto exts = std::extents(3, 5); auto m = std::layout_stride::mapping(exts, std::array{2, 12}); using index_type = decltype(exts)::index_type; auto s1 = std::strided_slice{index_type(2), index_type(2), std::cw}; auto result = submdspan_mapping(m, index_type(1), s1); using layout_type = decltype(result.mapping)::layout_type; static_assert(std::same_as); return true; } +template + constexpr bool + test_return_types_all() + { + return true; + } + +template + constexpr bool + test_return_types_unpadded_all() + { + test_layout_common_return_types(); + static_assert(test_layout_common_return_types()); + + test_layout_unpadded_return_types(); + static_assert(test_layout_unpadded_return_types()); + + test_layout_unpadded_padding_value(); + static_assert(test_layout_unpadded_padding_value()); + return true; + } + +template + constexpr bool + test_return_types_padded_all() + { + test_layout_common_return_types(); + static_assert(test_layout_common_return_types()); + + test_layout_padded_return_types(); + static_assert(test_layout_padded_return_types()); + + test_layout_padded_padding_value(); + static_assert(test_layout_padded_padding_value()); + return true; + } + int main() { - test_layout_unpadded_return_types(); - static_assert(test_layout_unpadded_return_types()); + test_return_types_unpadded_all(); + test_return_types_unpadded_all(); - test_layout_unpadded_return_types(); - static_assert(test_layout_unpadded_return_types()); + test_return_types_padded_all>(); + test_return_types_padded_all>(); + test_return_types_padded_all>(); test_layout_stride_return_types(); static_assert(test_layout_stride_return_types()); - - test_layout_unpadded_padding_value(); - static_assert(test_layout_unpadded_padding_value()); - - test_layout_unpadded_padding_value(); - static_assert(test_layout_unpadded_padding_value()); return 0; } From patchwork Thu Dec 4 21:28:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luc Grosheintz X-Patchwork-Id: 125936 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 598DA4CCCA0E for ; Thu, 4 Dec 2025 21:30:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 598DA4CCCA0E Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=XF8kf6SB X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) by sourceware.org (Postfix) with ESMTPS id 3AA1C4C3188E for ; Thu, 4 Dec 2025 21:26:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3AA1C4C3188E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3AA1C4C3188E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883608; cv=none; b=MCxb5gtUvgHPnF7u5J6iRbNqhznWKBL8Jp3H+0BoOlubosEgyfLqj6Hlj055+3R3ZJ6JoevkyzDGq7yZN5pe+rhaAQXiKFSo8nY2NdUG50hbIjT2bDmaGEta6zPPXDTxECT+xZjptj2pBOB5UnIVvfg/OdwdZHu6OefN1dBrrN4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1764883608; c=relaxed/simple; bh=buwaIi5Eq8tLHBERGsDZuSsreKtHWg0DbyLVN+VMB+c=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=vKEhHsgpFH6p9p/0wnw2B58//V9FDJuFYNmH1dEr+IV87XtPwue55fhDMSvxGdE6T7UpPIMbVtCIvJWoFsUklP538pKzaUR1IvsILoEsaGpl3Bbcp5Ac8svyOs/gmQCHF0LlzFWoehda7ZlLZfVYJzw83hmYk6eInV9kesq5Gxc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3AA1C4C3188E Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-477619f8ae5so12163725e9.3 for ; Thu, 04 Dec 2025 13:26:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1764883607; x=1765488407; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ndtpA6QbUpwM5JFbJ7tpVmKCBjCzmCDtWSudqFDU884=; b=XF8kf6SBH6MslhOzXY9oobIPJhCqz2evJ70o7aLRLBJvykOO3HiZVQn+MPds2LJ/Um PICN2Fe/QxdP/sOxvp/18n2SXkNWzROK9urZ4N1SYCQQRDQMSj7BKSj0n/EyDXz8e8Di GDYGGvpl6OOEQ2OxoC1jQhDs2N4JwIpczYrxm9m2BquDo/DqBWqwFDNuZ2xaJ6mA1rwG HNuj4aTPFk6/7M0eo1WDMs9OP+3H7cR2fdg7qzEnPOO6t64hKjiVvpUhINwdEjReEr7Y y1SnivC8zpwqUKHBcSq/a7+PPraW4sb6ZlFj6XaBXLchDuzm5Bzfl+6KtYWzCQzB19sH 2ivA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1764883607; x=1765488407; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ndtpA6QbUpwM5JFbJ7tpVmKCBjCzmCDtWSudqFDU884=; b=cpckNOjRqBGq1zEZVI2sgRzeIO0si4JEzhEfV82/F4R+5MeafKZg5jjdcsJZRUzjJ4 erQg8uw3AEiBV/E9PqRZHyWyQvFZShUFc4NmfRJ2hJC26QBNQ2/c6c7is5kNa/OVHm27 GMTVNsnf1YlUgprT5uzilLuuhH7MJB6IJZVUIxuYj3qDrfUWZRE6db5vItEP+JkBvGy8 nCWf1xsRJCBNPYmTO7sxO9tNjJ+BwVd8sBxXL9qLcsjM72t3wrS6zqCYTlMtiCDnFhWV im6WuJYoWAty/0XYPQVpeekpiDGjZgcIukyNnRJSDnn132GziCi+34gmAoXg85n/1el4 0n6w== X-Gm-Message-State: AOJu0Yw2ufOYa2cBbQ02BCtlV6tq9gK1LPbgdTLwN50F3ONMeXZECox9 bIRqWzli457IwPfiRwWQIqEebpE4ksXL1WHWii84Y80POvmZUMpEg+zT X-Gm-Gg: ASbGncv630lQdPuLCl6P3Nr4qSt/aLvgrXvhKVX5Ai3nm3WZyE9Uu3ERSh1RMk35v/k IowiNDcMVG1HAtap8CJCMrfQ4TYb32RrFtGVG7VoPVPj/QUkwx4dTblm9UzlbG2XyFGKVdJ6J/J oBDP4v72D4ShuP+FxA2L9t4Dz7IR0Kz6avL3e61H6x1z5m4NthYQroSj1DS8jQihkXz7S5+g78A VereDOwBPYhUw4T3TFxLRbGUlNGRhnpaO3+QzrIW0m+Cfki0BYuJMheUu1PwadDhmocv0AMD6/c 7KR9XLhJLe7iBIIkCztdWuqb0Ee0IC6CGjUxtlkEwypbuIhGkrr1jY+izNMxaEIHoQYbUN8pWdx QbE9PkKO9P1Fh9lpn4jwKYquYUex3iEJfztqYSg7QRkQSA2xPqwhBfjVeQ3UfGEA48JBtxjxo5M DQCqxMU5UpK5DkSsyEFABKpk7y/KwBssTXDk7NXlKk6BuGiCV6bQt2FC5DVxV1 X-Google-Smtp-Source: AGHT+IHSw/E9tP3dX7zXRjzGvgSBrsp2i3SpwSzN52C5DSue3MMnqYKaAOagh6RP3w3GOfpzpiBD3g== X-Received: by 2002:a05:600c:45ca:b0:46f:b32e:5094 with SMTP id 5b1f17b1804b1-4792af5e38emr86299115e9.32.1764883606802; Thu, 04 Dec 2025 13:26:46 -0800 (PST) Received: from piglet.localdomain (212-51-146-223.fiber7.init7.net. [212.51.146.223]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-42f7d331e29sm5354432f8f.32.2025.12.04.13.26.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Dec 2025 13:26:46 -0800 (PST) From: Luc Grosheintz To: libstdc++@gcc.gnu.org Cc: gcc-patches@gcc.gnu.org, Luc Grosheintz Subject: [PATCH v4 5/5] libstdc++: Implement submdspan_mapping for layout_right_padded. [PR110352] Date: Thu, 4 Dec 2025 22:28:46 +0100 Message-ID: <20251204212846.248887-6-luc.grosheintz@gmail.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251204212846.248887-1-luc.grosheintz@gmail.com> References: <20251204212846.248887-1-luc.grosheintz@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-13.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_BLOCKED, RCVD_IN_MSPIKE_H2, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org Implements submdspan for layout_right_padded as described in P3663. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (layout_right_padded::submdspan_mapping): New friend method. * testsuite/23_containers/mdspan/submdspan/submdspan.cc: Instantiate test for layout_right_padded. * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: Ditto. Signed-off-by: Luc Grosheintz --- libstdc++-v3/include/std/mdspan | 45 +++++++++++++++++++ .../mdspan/submdspan/submdspan.cc | 7 ++- .../mdspan/submdspan/submdspan_mapping.cc | 4 ++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index 21dbe6def22..269522f4a5a 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -1353,40 +1353,76 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr auto __rank = _Extents::rank(); constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(_Us + 1, __rank); if constexpr (!__mdspan::__all_static(__sta_exts)) return dynamic_extent; else return __fwd_prod(__sta_exts); } template static consteval bool _S_is_unpadded_submdspan(span __slice_kinds) { auto __rev_slice_kinds = array<_SliceKind, _Nm>{}; for(size_t __i = 0; __i < _Nm; ++__i) __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i]; return __mdspan::__is_block<_SubRank>(span(__rev_slice_kinds)); } }; + template<> + struct _SubMdspanMapping<_LayoutSide::__right, true> + { + using _Layout = layout_right; + template using _PaddedLayout = layout_right_padded<_Pad>; + + template + static consteval size_t + _S_pad() + { + using _Extents = typename _Mapping::extents_type; + constexpr auto __rank = _Extents::rank(); + constexpr auto __sta_exts + = __mdspan::__static_extents<_Extents>(_Us + 1, __rank - 1); + constexpr auto __sta_padstride + = __mdspan::__get_static_stride<_Mapping>(); + if constexpr (__sta_padstride == dynamic_extent + || !__mdspan::__all_static(__sta_exts)) + return dynamic_extent; + else + return __sta_padstride * __mdspan::__fwd_prod(__sta_exts); + } + + template + static consteval bool + _S_is_unpadded_submdspan(span __slice_kinds) + { + if constexpr (_SubRank == 1) + return __slice_kinds[_Nm - 1] == _SliceKind::__unit_strided_slice + || __slice_kinds[_Nm - 1] == _SliceKind::__full; + else + return false; + } + }; + + template constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping) { return submdspan_mapping_result{__mapping, 0}; } template requires (sizeof...(_Slices) > 0) constexpr auto __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices) { constexpr auto __side = __mdspan::__mapping_side<_Mapping>(); using _Trait = _SubMdspanMapping<__side, __is_padded_mapping<_Mapping>>; constexpr auto __slice_kinds = __mdspan::__make_slice_kind_array<_Slices...>(); auto __offset = __mdspan::__suboffset(__mapping, __slices...); auto __sub_exts = __mdspan::__subextents(__mapping.extents(), __slices...); using _SubExts = decltype(__sub_exts); constexpr auto __sub_rank = _SubExts::rank(); if constexpr (__sub_rank == 0) return submdspan_mapping_result{ @@ -2748,40 +2784,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (_S_rank <= 1) return 1; else if (__r == _S_rank - 1) return 1; else if (__r == _S_rank - 2) return _M_padstride(); else return static_cast( static_cast(_M_padstride()) * static_cast(__mdspan::__fwd_prod( extents(), __r + 1, _S_rank - 1))); } template requires(__mdspan::__is_right_padded_mapping<_RightPaddedMapping> && _RightPaddedMapping::extents_type::rank() == _S_rank) friend constexpr bool operator==(const mapping& __self, const _RightPaddedMapping& __other) noexcept { return __self._M_storage._M_equal(__other); } + +#if __glibcxx_submdspan + private: + template<__mdspan::__acceptable_slice_type... _Slices> + requires (extents_type::rank() == sizeof...(_Slices)) + friend constexpr auto + submdspan_mapping(const mapping& __mapping, _Slices... __slices) + { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); } +#endif // __glibcxx_submdspan }; #endif // __glibcxx_padded_layouts template struct default_accessor { static_assert(!is_array_v<_ElementType>, "ElementType must not be an array type"); static_assert(!is_abstract_v<_ElementType>, "ElementType must not be an abstract class type"); using offset_policy = default_accessor; using element_type = _ElementType; using reference = element_type&; using data_handle_type = element_type*; constexpr default_accessor() noexcept = default; template diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc index 927b5d06c6b..f8382847fe3 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc @@ -348,27 +348,30 @@ template }; run(std::extents(3, 5, 7)); run(std::extents{}); return true; } template constexpr bool test_all() { test_all_cheap(); test_all_expensive(); return true; } int main() { test_all(); + test_all(); + test_all(); test_all>(); test_all>(); test_all>(); - test_all(); - test_all(); + test_all>(); + test_all>(); + test_all>(); return 0; } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc index 50836968a06..efd71d10f9d 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -273,25 +273,29 @@ template static_assert(test_layout_common_return_types()); test_layout_padded_return_types(); static_assert(test_layout_padded_return_types()); test_layout_padded_padding_value(); static_assert(test_layout_padded_padding_value()); return true; } int main() { test_return_types_unpadded_all(); test_return_types_unpadded_all(); test_return_types_padded_all>(); test_return_types_padded_all>(); test_return_types_padded_all>(); + test_return_types_padded_all>(); + test_return_types_padded_all>(); + test_return_types_padded_all>(); + test_layout_stride_return_types(); static_assert(test_layout_stride_return_types()); return 0; }