Skip to content

Commit b4ff893

Browse files
committed
[libc++][mdspan] Implement layout_left
This commit implements layout_left in support of C++23 mdspan (https://wg21.link/p0009). layout_left is a layout mapping policy whose index mapping corresponds to the memory layout of Fortran arrays. Thus the left most index has stride-1 access, and the right most index is associated with the largest stride. Co-authored-by: Damien L-G <dalg24@gmail.com> Differential Revision: https://reviews.llvm.org/D153783
1 parent 9979417 commit b4ff893

28 files changed

+1653
-16
lines changed

libcxx/docs/Status/Cxx23.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Paper Status
4040

4141
.. note::
4242

43-
.. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, and ``layout_right`` are implemented.
43+
.. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, and ``layout_right`` are implemented.
4444
.. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
4545
.. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
4646
clang doesn't issue a diagnostic for deprecated using template declarations.

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ set(files
478478
__locale_dir/locale_base_api/locale_guard.h
479479
__mbstate_t.h
480480
__mdspan/extents.h
481+
__mdspan/layout_left.h
481482
__mdspan/layout_right.h
482483
__memory/addressof.h
483484
__memory/align.h

libcxx/include/__fwd/mdspan.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3030

3131
#if _LIBCPP_STD_VER >= 23
3232

33-
/*
34-
// Will be implemented with follow on revision
3533
// Layout policy with a mapping which corresponds to FORTRAN-style array layouts
3634
struct layout_left {
37-
template<class Extents>
38-
class mapping;
35+
template <class Extents>
36+
class mapping;
3937
};
40-
*/
4138

4239
// Layout policy with a mapping which corresponds to C-style array layouts
4340
struct layout_right {
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
// Kokkos v. 4.0
9+
// Copyright (2022) National Technology & Engineering
10+
// Solutions of Sandia, LLC (NTESS).
11+
//
12+
// Under the terms of Contract DE-NA0003525 with NTESS,
13+
// the U.S. Government retains certain rights in this software.
14+
//
15+
//===---------------------------------------------------------------------===//
16+
17+
#ifndef _LIBCPP___MDSPAN_LAYOUT_LEFT_H
18+
#define _LIBCPP___MDSPAN_LAYOUT_LEFT_H
19+
20+
#include <__assert>
21+
#include <__config>
22+
#include <__fwd/mdspan.h>
23+
#include <__mdspan/extents.h>
24+
#include <__type_traits/is_constructible.h>
25+
#include <__type_traits/is_convertible.h>
26+
#include <__type_traits/is_nothrow_constructible.h>
27+
#include <__utility/integer_sequence.h>
28+
#include <array>
29+
#include <cinttypes>
30+
#include <cstddef>
31+
#include <limits>
32+
33+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
34+
# pragma GCC system_header
35+
#endif
36+
37+
_LIBCPP_PUSH_MACROS
38+
#include <__undef_macros>
39+
40+
_LIBCPP_BEGIN_NAMESPACE_STD
41+
42+
#if _LIBCPP_STD_VER >= 23
43+
44+
template <class _Extents>
45+
class layout_left::mapping {
46+
public:
47+
static_assert(__mdspan_detail::__is_extents<_Extents>::value,
48+
"layout_left::mapping template argument must be a specialization of extents.");
49+
50+
using extents_type = _Extents;
51+
using index_type = typename extents_type::index_type;
52+
using size_type = typename extents_type::size_type;
53+
using rank_type = typename extents_type::rank_type;
54+
using layout_type = layout_left;
55+
56+
private:
57+
_LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {
58+
if constexpr (extents_type::rank() == 0)
59+
return true;
60+
61+
index_type __prod = __ext.extent(0);
62+
for (rank_type __r = 1; __r < extents_type::rank(); __r++) {
63+
bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);
64+
if (__overflowed)
65+
return false;
66+
}
67+
return true;
68+
}
69+
70+
static_assert((extents_type::rank_dynamic() > 0) || __required_span_size_is_representable(extents_type()),
71+
"layout_left::mapping product of static extents must be representable as index_type.");
72+
73+
public:
74+
// [mdspan.layout.left.cons], constructors
75+
_LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept = default;
76+
_LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;
77+
_LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext) noexcept : __extents_(__ext) {
78+
_LIBCPP_ASSERT(__required_span_size_is_representable(__ext),
79+
"layout_left::mapping extents ctor: product of extents must be representable as index_type.");
80+
}
81+
82+
template <class _OtherExtents>
83+
requires(is_constructible_v<extents_type, _OtherExtents>)
84+
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
85+
mapping(const mapping<_OtherExtents>& __other) noexcept
86+
: __extents_(__other.extents()) {
87+
_LIBCPP_ASSERT(
88+
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
89+
"layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
90+
}
91+
92+
template <class _OtherExtents>
93+
requires(is_constructible_v<extents_type, _OtherExtents> && _OtherExtents::rank() <= 1)
94+
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
95+
mapping(const layout_right::mapping<_OtherExtents>& __other) noexcept
96+
: __extents_(__other.extents()) {
97+
_LIBCPP_ASSERT(
98+
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
99+
"layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
100+
}
101+
102+
// FIXME: add when we add other layouts
103+
# if 0
104+
template<class _OtherExtents>
105+
constexpr explicit(extents_type::rank() > 0)
106+
mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
107+
# endif
108+
109+
_LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
110+
111+
// [mdspan.layout.left.obs], observers
112+
_LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }
113+
114+
_LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {
115+
index_type __size = 1;
116+
for (size_t __r = 0; __r < extents_type::rank(); __r++)
117+
__size *= __extents_.extent(__r);
118+
return __size;
119+
}
120+
121+
template <class... _Indices>
122+
requires((sizeof...(_Indices) == extents_type::rank()) && (is_convertible_v<_Indices, index_type> && ...) &&
123+
(is_nothrow_constructible_v<index_type, _Indices> && ...))
124+
_LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept {
125+
_LIBCPP_ASSERT(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
126+
"layout_left::mapping: out of bounds indexing");
127+
array<index_type, extents_type::rank()> __idx_a{static_cast<index_type>(__idx)...};
128+
return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
129+
index_type __res = 0;
130+
((__res = __idx_a[extents_type::rank() - 1 - _Pos] + __extents_.extent(extents_type::rank() - 1 - _Pos) * __res),
131+
...);
132+
return __res;
133+
}(make_index_sequence<sizeof...(_Indices)>());
134+
}
135+
136+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
137+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return true; }
138+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
139+
140+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
141+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_exhaustive() noexcept { return true; }
142+
_LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
143+
144+
_LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept
145+
requires(extents_type::rank() > 0)
146+
{
147+
_LIBCPP_ASSERT(__r < extents_type::rank(), "layout_left::mapping::stride(): invalid rank index");
148+
index_type __s = 1;
149+
for (rank_type __i = extents_type::rank() - 1; __i > __r; __i--)
150+
__s *= __extents_.extent(__i);
151+
return __s;
152+
}
153+
154+
template <class _OtherExtents>
155+
requires(_OtherExtents::rank() == extents_type::rank())
156+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool
157+
operator==(const mapping& __lhs, const mapping<_OtherExtents>& __rhs) noexcept {
158+
return __lhs.extents() == __rhs.extents();
159+
}
160+
161+
private:
162+
extents_type __extents_{}; // exposition only
163+
};
164+
165+
#endif // _LIBCPP_STD_VER >= 23
166+
167+
_LIBCPP_END_NAMESPACE_STD
168+
169+
_LIBCPP_POP_MACROS
170+
171+
#endif // _LIBCPP___MDSPAN_LAYOUT_LEFT_H

libcxx/include/__mdspan/layout_right.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,18 @@ class layout_right::mapping {
8888
"layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
8989
}
9090

91+
template <class _OtherExtents>
92+
requires(is_constructible_v<extents_type, _OtherExtents> && _OtherExtents::rank() <= 1)
93+
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
94+
mapping(const layout_left::mapping<_OtherExtents>& __other) noexcept
95+
: __extents_(__other.extents()) {
96+
_LIBCPP_ASSERT(
97+
__mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
98+
"layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
99+
}
100+
91101
// FIXME: add when we add other layouts
92102
# if 0
93-
template<class _OtherExtents>
94-
constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
95-
mapping(const layout_left::mapping<_OtherExtents>&) noexcept {}
96-
97103
template<class _OtherExtents>
98104
constexpr explicit(extents_type::rank() > 0)
99105
mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;

libcxx/include/mdspan

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace std {
2121
using dextents = see below;
2222

2323
// [mdspan.layout], layout mapping
24-
struct layout_left; // not implemented yet
24+
struct layout_left;
2525
struct layout_right;
2626
struct layout_stride; // not implemented yet
2727

@@ -82,6 +82,60 @@ namespace std {
8282
-> see below;
8383
}
8484

85+
// layout_left synopsis
86+
87+
namespace std {
88+
template<class Extents>
89+
class layout_left::mapping {
90+
public:
91+
using extents_type = Extents;
92+
using index_type = typename extents_type::index_type;
93+
using size_type = typename extents_type::size_type;
94+
using rank_type = typename extents_type::rank_type;
95+
using layout_type = layout_left;
96+
97+
// [mdspan.layout.right.cons], constructors
98+
constexpr mapping() noexcept = default;
99+
constexpr mapping(const mapping&) noexcept = default;
100+
constexpr mapping(const extents_type&) noexcept;
101+
template<class OtherExtents>
102+
constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
103+
mapping(const mapping<OtherExtents>&) noexcept;
104+
template<class OtherExtents>
105+
constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
106+
mapping(const layout_right::mapping<OtherExtents>&) noexcept;
107+
template<class OtherExtents>
108+
constexpr explicit(extents_type::rank() > 0)
109+
mapping(const layout_stride::mapping<OtherExtents>&) noexcept;
110+
111+
constexpr mapping& operator=(const mapping&) noexcept = default;
112+
113+
// [mdspan.layout.right.obs], observers
114+
constexpr const extents_type& extents() const noexcept { return extents_; }
115+
116+
constexpr index_type required_span_size() const noexcept;
117+
118+
template<class... Indices>
119+
constexpr index_type operator()(Indices...) const noexcept;
120+
121+
static constexpr bool is_always_unique() noexcept { return true; }
122+
static constexpr bool is_always_exhaustive() noexcept { return true; }
123+
static constexpr bool is_always_strided() noexcept { return true; }
124+
125+
static constexpr bool is_unique() noexcept { return true; }
126+
static constexpr bool is_exhaustive() noexcept { return true; }
127+
static constexpr bool is_strided() noexcept { return true; }
128+
129+
constexpr index_type stride(rank_type) const noexcept;
130+
131+
template<class OtherExtents>
132+
friend constexpr bool operator==(const mapping&, const mapping<OtherExtents>&) noexcept;
133+
134+
private:
135+
extents_type extents_{}; // exposition only
136+
};
137+
}
138+
85139
// layout_right synopsis
86140

87141
namespace std {
@@ -144,6 +198,7 @@ namespace std {
144198
#include <__config>
145199
#include <__fwd/mdspan.h>
146200
#include <__mdspan/extents.h>
201+
#include <__mdspan/layout_left.h>
147202
#include <__mdspan/layout_right.h>
148203

149204
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

libcxx/include/module.modulemap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@ module std [system] {
11741174

11751175
module __mdspan {
11761176
module extents { private header "__mdspan/extents.h" }
1177+
module layout_left { private header "__mdspan/layout_left.h" }
11771178
module layout_right { private header "__mdspan/layout_right.h" }
11781179
module mdspan_fwd { private header "__fwd/mdspan.h" }
11791180
}

libcxx/modules/std/mdspan.cppm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export namespace std {
1919
using std::dextents;
2020

2121
// [mdspan.layout], layout mapping
22-
// using std::layout_left;
22+
using std::layout_left;
2323
using std::layout_right;
2424
// using std::layout_stride;
2525

libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
10-
#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
9+
#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H
10+
#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H
1111

1212
struct IntType {
1313
int val;
@@ -20,4 +20,4 @@ struct IntType {
2020
constexpr operator char() const noexcept { return val; }
2121
};
2222

23-
#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
23+
#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H

0 commit comments

Comments
 (0)