Skip to content

[libc++] Implement P2897R7 aligned_accessor: An mdspan accessor expressing pointer over-alignment #122603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2766103
[libc++] Implement P2897R7 aligned_accessor: An mdspan accessor expre…
dalg24 Jan 11, 2025
e36bc1c
Run the generate scripts
dalg24 Jan 12, 2025
1c57f20
Add missing header include for default_accessor
dalg24 Jan 13, 2025
68ea167
Missing header includes in tests
dalg24 Jan 13, 2025
1db762c
Fix access and offset tests
dalg24 Jan 13, 2025
625a421
Merge branch 'main' of https://github.com/llvm/llvm-project into alig…
dalg24 Jan 31, 2025
d2eccee
Add a release note
dalg24 Jan 31, 2025
64f14e7
Update release version 20 -> 21
dalg24 Jan 31, 2025
baefcf9
Fix header include <cstd{int -> def}> and qualify std::size_t
dalg24 Jan 31, 2025
47d3c56
Move verify tests to test/libcxx/
dalg24 Jan 31, 2025
eda7039
Drop superfluous constexpr, fix includes, and qualify std::size_t
dalg24 Jan 31, 2025
2cd1d28
Drop stray commented empty lines and superfluous constexpr
dalg24 Jan 31, 2025
8376d2f
Fix access and offset tests to work with constexpr
dalg24 Jan 31, 2025
a863a60
Correct oversight stray constexpr
dalg24 Feb 1, 2025
80e4a2d
Merge branch 'main' of https://github.com/llvm/llvm-project into alig…
dalg24 Mar 10, 2025
4abd141
Add coverage for which sizeof(T) is not equal to alignof(T)
dalg24 Mar 10, 2025
70f331a
Do not assume that sizeof(T) is the same as alignof(T) in test
dalg24 Mar 10, 2025
ac9792a
Merge branch 'main' into aligned_accessor
ldionne Mar 31, 2025
c3dfcda
Merge branch 'main' of https://github.com/llvm/llvm-project into alig…
dalg24 Apr 3, 2025
14c4606
Merge branch 'main' into aligned_accessor
dalg24 Apr 7, 2025
a55bef7
Merge branch 'main' into aligned_accessor
frederick-vs-ja Apr 9, 2025
9b5ef48
Merge branch 'main' of https://github.com/llvm/llvm-project into alig…
dalg24 Apr 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ Status
---------------------------------------------------------- -----------------
**C++26**
----------------------------------------------------------------------------
``__cpp_lib_aligned_accessor`` ``202411L``
---------------------------------------------------------- -----------------
``__cpp_lib_associative_heterogeneous_insertion`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_atomic_min_max`` *unimplemented*
Expand Down Expand Up @@ -456,6 +458,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_inplace_vector`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_is_sufficiently_aligned`` ``202411L``
---------------------------------------------------------- -----------------
``__cpp_lib_is_virtual_base_of`` ``202406L``
---------------------------------------------------------- -----------------
``__cpp_lib_is_within_lifetime`` *unimplemented*
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Implemented Papers
- P2562R1: ``constexpr`` Stable Sorting (`Github <https://github.com/llvm/llvm-project/issues/105360>`__)
- P0472R3: Put std::monostate in <utility> (`Github <https://github.com/llvm/llvm-project/issues/127874>`__)
- P1222R4: A Standard ``flat_set`` (`Github <https://github.com/llvm/llvm-project/issues/105193>`__)
- P2897R7: ``aligned_accessor``: An mdspan accessor expressing pointer over-alignment (`Github <https://github.com/llvm/llvm-project/issues/118372>`__)
- P3247R2: Deprecate the notion of trivial types (`Github <https://github.com/llvm/llvm-project/issues/118387>`__)

Improvements and New Features
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","","",""
"`P3379R0 <https://wg21.link/P3379R0>`__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","","",""
"`P2862R1 <https://wg21.link/P2862R1>`__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","","",""
"`P2897R7 <https://wg21.link/P2897R7>`__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","","",""
"`P2897R7 <https://wg21.link/P2897R7>`__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","|Complete|","21",""
"`P3355R1 <https://wg21.link/P3355R1>`__","Fix ``submdspan`` for C++26","2024-11 (Wrocław)","","",""
"`P3222R0 <https://wg21.link/P3222R0>`__","Fix C++26 by adding transposed special cases for P2642 layouts","2024-11 (Wrocław)","","",""
"`P3050R2 <https://wg21.link/P3050R2>`__","Fix C++26 by optimizing ``linalg::conjugated`` for noncomplex value types","2024-11 (Wrocław)","","",""
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ set(files
__math/traits.h
__math/trigonometric_functions.h
__mbstate_t.h
__mdspan/aligned_accessor.h
__mdspan/default_accessor.h
__mdspan/extents.h
__mdspan/layout_left.h
Expand All @@ -569,6 +570,7 @@ set(files
__memory/destroy.h
__memory/destruct_n.h
__memory/inout_ptr.h
__memory/is_sufficiently_aligned.h
__memory/noexcept_move_assign_container.h
__memory/out_ptr.h
__memory/pointer_traits.h
Expand Down
87 changes: 87 additions & 0 deletions libcxx/include/__mdspan/aligned_accessor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
//===---------------------------------------------------------------------===//

#ifndef _LIBCPP___MDSPAN_ALIGNED_ACCESSOR_H
#define _LIBCPP___MDSPAN_ALIGNED_ACCESSOR_H

#include <__config>
#include <__cstddef/size_t.h>
#include <__mdspan/default_accessor.h>
#include <__memory/assume_aligned.h>
#include <__type_traits/is_abstract.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/remove_const.h>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_PUSH_MACROS
#include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 26

template <class _ElementType, size_t _ByteAlignment>
struct aligned_accessor {
static_assert(_ByteAlignment != 0 && (_ByteAlignment & (_ByteAlignment - 1)) == 0,
"aligned_accessor: byte alignment must be a power of two");
static_assert(_ByteAlignment >= alignof(_ElementType), "aligned_accessor: insufficient byte alignment");
static_assert(!is_array_v<_ElementType>, "aligned_accessor: template argument may not be an array type");
static_assert(!is_abstract_v<_ElementType>, "aligned_accessor: template argument may not be an abstract class");

using offset_policy = default_accessor<_ElementType>;
using element_type = _ElementType;
using reference = _ElementType&;
using data_handle_type = _ElementType*;

static constexpr size_t byte_alignment = _ByteAlignment;

_LIBCPP_HIDE_FROM_ABI constexpr aligned_accessor() noexcept = default;

template <class _OtherElementType, size_t _OtherByteAlignment>
requires(is_convertible_v<_OtherElementType (*)[], element_type (*)[]> && _OtherByteAlignment >= byte_alignment)
_LIBCPP_HIDE_FROM_ABI constexpr aligned_accessor(aligned_accessor<_OtherElementType, _OtherByteAlignment>) noexcept {}

template <class _OtherElementType>
requires(is_convertible_v<_OtherElementType (*)[], element_type (*)[]>)
_LIBCPP_HIDE_FROM_ABI explicit constexpr aligned_accessor(default_accessor<_OtherElementType>) noexcept {}

template <class _OtherElementType>
requires(is_convertible_v<element_type (*)[], _OtherElementType (*)[]>)
_LIBCPP_HIDE_FROM_ABI constexpr operator default_accessor<_OtherElementType>() const noexcept {
return {};
}

_LIBCPP_HIDE_FROM_ABI constexpr reference access(data_handle_type __p, size_t __i) const noexcept {
return std::assume_aligned<byte_alignment>(__p)[__i];
}

_LIBCPP_HIDE_FROM_ABI constexpr typename offset_policy::data_handle_type
offset(data_handle_type __p, size_t __i) const noexcept {
return std::assume_aligned<byte_alignment>(__p) + __i;
}
};

#endif // _LIBCPP_STD_VER >= 26

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP___MDSPAN_ALIGNED_ACCESSOR_H
9 changes: 5 additions & 4 deletions libcxx/include/__mdspan/mdspan.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <__assert>
#include <__config>
#include <__fwd/mdspan.h>
#include <__mdspan/aligned_accessor.h>
#include <__mdspan/default_accessor.h>
#include <__mdspan/extents.h>
#include <__memory/addressof.h>
Expand Down Expand Up @@ -267,13 +268,13 @@ class mdspan {
# if _LIBCPP_STD_VER >= 26
template <class _ElementType, class... _OtherIndexTypes>
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
explicit mdspan(_ElementType*,
_OtherIndexTypes...) -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
explicit mdspan(_ElementType*, _OtherIndexTypes...)
-> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
# else
template <class _ElementType, class... _OtherIndexTypes>
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
explicit mdspan(_ElementType*,
_OtherIndexTypes...) -> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
explicit mdspan(_ElementType*, _OtherIndexTypes...)
-> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
# endif

template <class _Pointer>
Expand Down
34 changes: 34 additions & 0 deletions libcxx/include/__memory/is_sufficiently_aligned.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___MEMORY_IS_SUFFICIENTLY_ALIGNED_H
#define _LIBCPP___MEMORY_IS_SUFFICIENTLY_ALIGNED_H

#include <__config>
#include <__cstddef/size_t.h>
#include <cstdint>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 26

template <size_t _Alignment, class _Tp>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just add this to assume_aligned.h. These two seem to be quite closely related.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a very strong preference, but IMO it makes more sense to leave it in a separate header since they're really different functionality, even though both relate to "alignment stuff". Not a strong opinion either way, I'd let the author decide on this one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My slight preference is to keep it in a separate header.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think they're just related as "alignment stuff". is_sufficiently_aligned is the precondition for assume_aligned. If is_sufficiently_aligned wasn't (IMO unnecessarily) non-constexpr I'd be strongly in favor of checking the precondition that way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If is_sufficiently_aligned wasn't (IMO unnecessarily) non-constexpr I'd be strongly in favor of checking the precondition that way.

I don't know that it is implementable. How would you check alignment in a constant expression?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack, I also don't see how this can be implemented inside a constant expression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://godbolt.org/z/7xGqasbGz is one implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using __builtin_constant_p like this is a bad idea; it won't produce the effects required by the standard.
See https://godbolt.org/z/jerx3KjW4

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using __builtin_constant_p like this is a bad idea; it won't produce the effects required by the standard. See https://godbolt.org/z/jerx3KjW4

That's only the implementation for constant evaluation. You need a separate one for the runtime.

_LIBCPP_HIDE_FROM_ABI bool is_sufficiently_aligned(_Tp* __ptr) {
return reinterpret_cast<uintptr_t>(__ptr) % _Alignment == 0;
}

#endif // _LIBCPP_STD_VER >= 26

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___MEMORY_IS_SUFFICIENTLY_ALIGNED_H
38 changes: 37 additions & 1 deletion libcxx/include/mdspan
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ namespace std {
template<class ElementType>
class default_accessor;

// [mdspan.accessor.aligned], class template aligned_accessor
template<class ElementType, size_t ByteAlignment>
class aligned_accessor; // since C++26

// [mdspan.mdspan], class template mdspan
template<class ElementType, class Extents, class LayoutPolicy = layout_right,
class AccessorPolicy = default_accessor<ElementType>>
class mdspan; // not implemented yet
class mdspan;
}

// extents synopsis
Expand Down Expand Up @@ -269,6 +273,38 @@ namespace std {
};
}

// aligned_accessor synopsis

namespace std {
template<class ElementType, size_t ByteAlignment>
struct aligned_accessor {
using offset_policy = default_accessor<ElementType>;
using element_type = ElementType;
using reference = ElementType&;
using data_handle_type = ElementType*;

static constexpr size_t byte_alignment = ByteAlignment;

constexpr aligned_accessor() noexcept = default;

template<class OtherElementType, size_t OtherByteAlignment>
constexpr aligned_accessor(
aligned_accessor<OtherElementType, OtherByteAlignment>) noexcept;

template<class OtherElementType>
explicit constexpr aligned_accessor(
default_accessor<OtherElementType>) noexcept;

template<class OtherElementType>
constexpr operator default_accessor<OtherElementType>() const noexcept;

constexpr reference access(data_handle_type p, size_t i) const noexcept;

constexpr typename offset_policy::data_handle_type
offset(data_handle_type p, size_t i) const noexcept;
};
}

// mdspan synopsis

namespace std {
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/memory
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,9 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
template<size_t N, class T>
[[nodiscard]] constexpr T* assume_aligned(T* ptr); // since C++20

template<size_t Alignment, class T>
bool is_sufficiently_aligned(T* ptr); // since C++26

// [out.ptr.t], class template out_ptr_t
template<class Smart, class Pointer, class... Args>
class out_ptr_t; // since c++23
Expand Down Expand Up @@ -945,6 +948,7 @@ template<class Pointer = void, class Smart, class... Args>
# include <__memory/allocator_traits.h>
# include <__memory/auto_ptr.h>
# include <__memory/inout_ptr.h>
# include <__memory/is_sufficiently_aligned.h>
# include <__memory/out_ptr.h>
# include <__memory/pointer_traits.h>
# include <__memory/raw_storage_iterator.h>
Expand Down
2 changes: 2 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,7 @@ module std [system] {
}

module mdspan {
module aligned_accessor { header "__mdspan/aligned_accessor.h" }
module default_accessor { header "__mdspan/default_accessor.h" }
module extents { header "__mdspan/extents.h" }
module fwd { header "__fwd/mdspan.h" }
Expand Down Expand Up @@ -1631,6 +1632,7 @@ module std [system] {
module destruct_n { header "__memory/destruct_n.h" }
module fwd { header "__fwd/memory.h" }
module inout_ptr { header "__memory/inout_ptr.h" }
module is_sufficiently_aligned { header "__memory/is_sufficiently_aligned.h" }
module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" }
module out_ptr { header "__memory/out_ptr.h" }
module pointer_traits { header "__memory/pointer_traits.h" }
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Macro name Value Headers
__cpp_lib_adaptor_iterator_pair_constructor 202106L <queue> <stack>
__cpp_lib_addressof_constexpr 201603L <memory>
__cpp_lib_aligned_accessor 202411L <mdspan>
__cpp_lib_allocate_at_least 202302L <memory>
__cpp_lib_allocator_traits_is_always_equal 201411L <deque> <forward_list> <list>
<map> <memory> <scoped_allocator>
Expand Down Expand Up @@ -148,6 +149,7 @@ __cpp_lib_is_nothrow_convertible 201806L <type_traits>
__cpp_lib_is_null_pointer 201309L <type_traits>
__cpp_lib_is_pointer_interconvertible 201907L <type_traits>
__cpp_lib_is_scoped_enum 202011L <type_traits>
__cpp_lib_is_sufficiently_aligned 202411L <memory>
__cpp_lib_is_swappable 201603L <type_traits>
__cpp_lib_is_virtual_base_of 202406L <type_traits>
__cpp_lib_is_within_lifetime 202306L <type_traits>
Expand Down Expand Up @@ -532,6 +534,7 @@ __cpp_lib_void_t 201411L <type_traits>
#endif

#if _LIBCPP_STD_VER >= 26
# define __cpp_lib_aligned_accessor 202411L
// # define __cpp_lib_associative_heterogeneous_insertion 202306L
// # define __cpp_lib_atomic_min_max 202403L
# undef __cpp_lib_bind_front
Expand Down Expand Up @@ -562,6 +565,7 @@ __cpp_lib_void_t 201411L <type_traits>
// # define __cpp_lib_generate_random 202403L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_inplace_vector 202406L
# define __cpp_lib_is_sufficiently_aligned 202411L
# if __has_builtin(__builtin_is_virtual_base_of)
# define __cpp_lib_is_virtual_base_of 202406L
# endif
Expand Down
5 changes: 5 additions & 0 deletions libcxx/modules/std/mdspan.inc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export namespace std {
// [mdspan.accessor.default], class template default_accessor
using std::default_accessor;

# if _LIBCPP_STD_VER >= 26
// [mdspan.accessor.aligned], class template aligned_accessor
using std::aligned_accessor;
# endif // _LIBCPP_STD_VER >= 26

// [mdspan.mdspan], class template mdspan
using std::mdspan;
#endif // _LIBCPP_STD_VER >= 23
Expand Down
3 changes: 3 additions & 0 deletions libcxx/modules/std/memory.inc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export namespace std {
// [ptr.align], pointer alignment
using std::align;
using std::assume_aligned;
#if _LIBCPP_STD_VER >= 26
using std::is_sufficiently_aligned;
#endif

// [obj.lifetime], explicit lifetime management
// using std::start_lifetime_as;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23

// <mdspan>

// template<class ElementType, size_t ByteAlignment>
// class aligned_accessor;

// ByteAlignement is required to be a power of two and greater or equal to alignof(ElementType).

#include <mdspan>

void not_power_of_two() {
// expected-error-re@*:* {{static assertion failed {{.*}}aligned_accessor: byte alignment must be a power of two}}
[[maybe_unused]] std::aligned_accessor<int, 12> acc;
}

struct alignas(8) S {};

void insufficiently_aligned() {
// expected-error-re@*:* {{static assertion failed {{.*}}aligned_accessor: insufficient byte alignment}}
[[maybe_unused]] std::aligned_accessor<S, 4> acc;
}
Loading
Loading