[PATCH v1 1/3] libstdc++: Implement is_sufficiently_aligned.
Luc Grosheintz
luc.grosheintz@gmail.com
Thu Jul 17 10:32:16 GMT 2025
On 7/17/25 11:14, Jonathan Wakely wrote:
> On Thu, 17 Jul 2025 at 10:05, Luc Grosheintz <luc.grosheintz@gmail.com> wrote:
>>
>>
>>
>> On 7/8/25 16:56, Tomasz Kaminski wrote:
>>> On Thu, Jul 3, 2025 at 12:36 PM Luc Grosheintz <luc.grosheintz@gmail.com>
>>> wrote:
>>>
>>>> This commit implements and tests the function is_sufficiently_aligned
>>>> from P2897R7.
>>>>
>>>> libstdc++-v3/ChangeLog:
>>>>
>>>> * include/bits/align.h (is_sufficiently_aligned): New function.
>>>> * include/bits/version.def (is_sufficiently_aligned): Add.
>>>> * include/bits/version.h: Regenerate.
>>>> * include/std/memory: Add __glibcxx_want_is_sufficiently_aligned.
>>>> * src/c++23/std.cc.in (is_sufficiently_aligned): Add.
>>>> * testsuite/20_util/is_sufficiently_aligned/1.cc: New test.
>>>> * testsuite/20_util/is_sufficiently_aligned/2.cc: New test.
>>>> ---
>>>>
>>> Only one small suggestion on placement of the tests. Otherwise it looks
>>> good.
>>>
>>>> libstdc++-v3/include/bits/align.h | 16 ++++++++++
>>>> libstdc++-v3/include/bits/version.def | 8 +++++
>>>> libstdc++-v3/include/bits/version.h | 10 ++++++
>>>> libstdc++-v3/include/std/memory | 1 +
>>>> libstdc++-v3/src/c++23/std.cc.in | 1 +
>>>> .../20_util/is_sufficiently_aligned/1.cc | 31 +++++++++++++++++++
>>>> .../20_util/is_sufficiently_aligned/2.cc | 7 +++++
>>>> 7 files changed, 74 insertions(+)
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/2.cc
>>>>
>>>> diff --git a/libstdc++-v3/include/bits/align.h
>>>> b/libstdc++-v3/include/bits/align.h
>>>> index 2b40c37e033..fbbe9cb1f9c 100644
>>>> --- a/libstdc++-v3/include/bits/align.h
>>>> +++ b/libstdc++-v3/include/bits/align.h
>>>> @@ -102,6 +102,22 @@ align(size_t __align, size_t __size, void*& __ptr,
>>>> size_t& __space) noexcept
>>>> }
>>>> #endif // __glibcxx_assume_aligned
>>>>
>>>> +#ifdef __glibcxx_is_sufficiently_aligned // C++ >= 26
>>>> + /** @brief Is @a __ptr aligned to an _Align byte boundary?
>>>> + *
>>>> + * @tparam _Align An alignment value
>>>> + * @tparam _Tp An object type
>>>> + *
>>>> + * C++26 20.2.5 [ptr.align]
>>>> + *
>>>> + * @ingroup memory
>>>> + */
>>>> + template<size_t _Align, class _Tp>
>>>> + bool
>>>> + is_sufficiently_aligned(_Tp* __ptr)
>>>> + { return reinterpret_cast<__UINTPTR_TYPE__>(__ptr) % _Align == 0; }
>>>> +#endif // __glibcxx_is_sufficiently_aligned
>>>> +
>>>> _GLIBCXX_END_NAMESPACE_VERSION
>>>> } // namespace
>>>>
>>>> diff --git a/libstdc++-v3/include/bits/version.def
>>>> b/libstdc++-v3/include/bits/version.def
>>>> index f4ba501c403..a2695e67716 100644
>>>> --- a/libstdc++-v3/include/bits/version.def
>>>> +++ b/libstdc++-v3/include/bits/version.def
>>>> @@ -732,6 +732,14 @@ ftms = {
>>>> };
>>>> };
>>>>
>>>> +ftms = {
>>>> + name = is_sufficiently_aligned;
>>>> + values = {
>>>> + v = 202411;
>>>> + cxxmin = 26;
>>>> + };
>>>> +};
>>>> +
>>>> ftms = {
>>>> name = atomic_flag_test;
>>>> values = {
>>>> diff --git a/libstdc++-v3/include/bits/version.h
>>>> b/libstdc++-v3/include/bits/version.h
>>>> index dc8ac07be16..1b17a965239 100644
>>>> --- a/libstdc++-v3/include/bits/version.h
>>>> +++ b/libstdc++-v3/include/bits/version.h
>>>> @@ -815,6 +815,16 @@
>>>> #endif /* !defined(__cpp_lib_assume_aligned) &&
>>>> defined(__glibcxx_want_assume_aligned) */
>>>> #undef __glibcxx_want_assume_aligned
>>>>
>>>> +#if !defined(__cpp_lib_is_sufficiently_aligned)
>>>> +# if (__cplusplus > 202302L)
>>>> +# define __glibcxx_is_sufficiently_aligned 202411L
>>>> +# if defined(__glibcxx_want_all) ||
>>>> defined(__glibcxx_want_is_sufficiently_aligned)
>>>> +# define __cpp_lib_is_sufficiently_aligned 202411L
>>>> +# endif
>>>> +# endif
>>>> +#endif /* !defined(__cpp_lib_is_sufficiently_aligned) &&
>>>> defined(__glibcxx_want_is_sufficiently_aligned) */
>>>> +#undef __glibcxx_want_is_sufficiently_aligned
>>>> +
>>>> #if !defined(__cpp_lib_atomic_flag_test)
>>>> # if (__cplusplus >= 202002L)
>>>> # define __glibcxx_atomic_flag_test 201907L
>>>> diff --git a/libstdc++-v3/include/std/memory
>>>> b/libstdc++-v3/include/std/memory
>>>> index 1da03b3ea6a..ff342ff35f3 100644
>>>> --- a/libstdc++-v3/include/std/memory
>>>> +++ b/libstdc++-v3/include/std/memory
>>>> @@ -110,6 +110,7 @@
>>>> #define __glibcxx_want_constexpr_memory
>>>> #define __glibcxx_want_enable_shared_from_this
>>>> #define __glibcxx_want_indirect
>>>> +#define __glibcxx_want_is_sufficiently_aligned
>>>> #define __glibcxx_want_make_unique
>>>> #define __glibcxx_want_out_ptr
>>>> #define __glibcxx_want_parallel_algorithm
>>>> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
>>>> std.cc.in
>>>> index e692caaa5f9..6f4214ed3a7 100644
>>>> --- a/libstdc++-v3/src/c++23/std.cc.in
>>>> +++ b/libstdc++-v3/src/c++23/std.cc.in
>>>> @@ -1864,6 +1864,7 @@ export namespace std
>>>> using std::allocator_arg_t;
>>>> using std::allocator_traits;
>>>> using std::assume_aligned;
>>>> + using std::is_sufficiently_aligned;
>>>> using std::make_obj_using_allocator;
>>>> using std::pointer_traits;
>>>> using std::to_address;
>>>> diff --git a/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc
>>>> b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc
>>>> new file mode 100644
>>>> index 00000000000..4c2738b57db
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc
>>>> @@ -0,0 +1,31 @@
>>>> +// { dg-do run { target c++26 } }
>>>> +
>>>> +#include <memory>
>>>> +#include <array>
>>>> +#include <testsuite_hooks.h>
>>>> +
>>>> +void
>>>> +test01()
>>>> +{
>>>> + constexpr size_t N = 4;
>>>> + constexpr size_t M = 2*N + 1;
>>>> + alignas(N) std::array<char, M> buffer{};
>>>> +
>>>> + auto* ptr = buffer.data();
>>>> + VERIFY(std::is_sufficiently_aligned<1>(ptr+0));
>>>> + VERIFY(std::is_sufficiently_aligned<1>(ptr+1));
>>>> +
>>>> + VERIFY(std::is_sufficiently_aligned<2>(ptr+0));
>>>> + VERIFY(!std::is_sufficiently_aligned<2>(ptr+1));
>>>> + VERIFY(std::is_sufficiently_aligned<2>(ptr+2));
>>>> +
>>>> + for (size_t i = 0; i < M; ++i)
>>>> + VERIFY(std::is_sufficiently_aligned<N>(ptr + i) == (i % N == 0));
>>>> +}
>>>> +
>>>> +int
>>>> +main()
>>>> +{
>>>> + test01();
>>>> + return 0;
>>>> +}
>>>> diff --git a/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/2.cc
>>>> b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/2.cc
>>>> new file mode 100644
>>>> index 00000000000..8e0f4c07661
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/2.cc
>>>> @@ -0,0 +1,7 @@
>>>> +// { dg-do compile { target c++26 } }
>>>> +
>>>> +#include <memory>
>>>> +
>>>> +#ifndef __cpp_lib_is_sufficiently_aligned
>>>> +#error "Missing FTM"
>>>> +#endif
>>>>
>>> I would integrate that into 1.cc file directly. I do not think we need a
>>> separate test.
>>
>> Now I know they're called version.cc it's easier to find others. There's
>>
>> testsuite/20_util/headers/memory/version.cc
>>
>> which checks:
>>
>> __cpp_lib_allocator_traits_is_always_equal
>> __cpp_lib_addressof_constexpr
>>
>> That feels like the place to also put this one.
>
> Yes, that makes sense.
>
>> It's use of dejagnu is
>> neat too:
>>
>> // { dg-do preprocess { target c++17 } }
>
> The 'preprocess' does what you'd expect, it stops after preprocessing
> (so doesn't bother doing any of compiling and assembling and linking
> and running):
> https://gcc.gnu.org/onlinedocs/gccint/Directives.html#Specify-how-to-build-the-test
> By default this test will only be tested with -std=gnu++17 but people
> who run the testsuite with additional/different -std options can test
> it for -std=c++26:
> https://gcc.gnu.org/onlinedocs/libstdc++/manual/test.html#test.run.permutations
>
> If we really want to be sure the new macro is *always* tested by
> everybody running the tests, it needs to be a separate file with {
> target c++26 } so that the testsuite chooses -std=gnu++26 for it. So
> that's an argument against checking the new macro in the existing
> headers/memory/version.cc file. I don't feel strongly about it though,
> because I test everything with every -std option several times a day,
> and as long as *somebody* is doing that, we'll know it works.
>
> And one day in the future -std=gnu++26 will be the default for all
> tests anyway (but that's years away).
Thank you! This uncovered a misconception of mine, namely that
{ target c++17 }
would automatically run on C++17 and all later versions. Which is
true, by accident, for { target c++23 }. At least in my rather
default config it automatically also checks C++26 if the target
larger than or equal to C++20.
The reason I liked { target c++17 } is that it opens up the
possibility to check that the macro for a C++23 feature is not
set while compiling C++{17,20} code. Similarly, the same FTM can
have different values for different versions of the standard; and
I've seen examples that check these values. Therefore, I have the
impression that testing with multiple versions of the standard is
needed to check FTMs thoroughly.
I'm tempted to say that the code for FTMs is unlikely to break
accidentally. Hence, thorough testing is needed when first
implementing the FTM and then periodically. (Though invariably
such a statement means the unlikely happens a few days later.)
>
>> // { dg-add-options no_pch }
>
> This prevents the test harness from adding an implicit -include
> bits/stdc++.h which includes every libstdc++ header at the very start
> of the file. If the test just wants to check that the macro is defined
> by <memory> then including every other header first would not test
> what you want to test!
That's why I liked that line; and want to add it to mdspan/version.cc.
> The definition of no_pch is in testsuite/lib/dg-options.exp
>
>
>>
>> Now I'm tempted to adjust mdspan/version.cc accordingly.
>>
>>>
>>>> --
>>>> 2.49.0
>>>>
>>>>
>>>
>>
>
More information about the Libstdc++
mailing list