diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -170,7 +170,7 @@ ------------------------------------------------------------------- ``__cpp_lib_array_constexpr`` ``201811L`` ------------------------------------------------- ----------------- - ``__cpp_lib_assume_aligned`` *unimplemented* + ``__cpp_lib_assume_aligned`` ``201811L`` ------------------------------------------------- ----------------- ``__cpp_lib_atomic_flag_test`` ``201907L`` ------------------------------------------------- ----------------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -66,7 +66,7 @@ "`P0919R3 `__","LWG","Heterogeneous lookup for unordered containers","San Diego","|Complete|","12.0" "`P0972R0 `__","LWG"," ``zero()``\ , ``min()``\ , and ``max()``\ should be noexcept","San Diego","|Complete|","8.0" "`P1006R1 `__","LWG","Constexpr in std::pointer_traits","San Diego","|Complete|","8.0" -"`P1007R3 `__","LWG","``std::assume_aligned``\ ","San Diego","* *","" +"`P1007R3 `__","LWG","``std::assume_aligned``\ ","San Diego","|Complete|","15.0" "`P1020R1 `__","LWG","Smart pointer creation with default initialization","San Diego","* *","" "`P1032R1 `__","LWG","Misc constexpr bits","San Diego","|Complete|","13.0" "`P1085R2 `__","LWG","Should Span be Regular?","San Diego","|Complete|","8.0" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -291,6 +291,7 @@ __memory/allocator.h __memory/allocator_arg_t.h __memory/allocator_traits.h + __memory/assume_aligned.h __memory/auto_ptr.h __memory/compressed_pair.h __memory/concepts.h diff --git a/libcxx/include/__memory/assume_aligned.h b/libcxx/include/__memory/assume_aligned.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__memory/assume_aligned.h @@ -0,0 +1,46 @@ +// -*- 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_ASSUME_ALIGNED_H +#define _LIBCPP___MEMORY_ASSUME_ALIGNED_H + +#include <__assert> +#include <__config> +#include +#include +#include // for is_constant_evaluated() + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +template +[[nodiscard]] +_LIBCPP_HIDE_FROM_ABI +constexpr _Tp* assume_aligned(_Tp* __ptr) { + static_assert(_Np != 0 && (_Np & (_Np - 1)) == 0, + "std::assume_aligned(p) requires N to be a power of two"); + + if (is_constant_evaluated()) { + return __ptr; + } else { + _LIBCPP_ASSERT(reinterpret_cast(__ptr) % _Np == 0, "Alignment assumption is violated"); + return static_cast<_Tp*>(__builtin_assume_aligned(__ptr, _Np)); + } +} + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___MEMORY_ASSUME_ALIGNED_H diff --git a/libcxx/include/memory b/libcxx/include/memory --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -828,8 +828,12 @@ template inline constexpr bool uses_allocator_v = uses_allocator::value; +// [ptr.align] void* align(size_t alignment, size_t size, void*& ptr, size_t& space); +template +[[nodiscard]] constexpr T* assume_aligned(T* ptr); // since C++20 + } // std */ @@ -842,6 +846,7 @@ #include <__memory/allocator.h> #include <__memory/allocator_arg_t.h> #include <__memory/allocator_traits.h> +#include <__memory/assume_aligned.h> #include <__memory/compressed_pair.h> #include <__memory/concepts.h> #include <__memory/construct_at.h> @@ -1130,7 +1135,6 @@ } }; - _LIBCPP_END_NAMESPACE_STD #if defined(_LIBCPP_HAS_PARALLEL_ALGORITHMS) && _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -713,6 +713,7 @@ module allocator { private header "__memory/allocator.h" } module allocator_arg_t { private header "__memory/allocator_arg_t.h" } module allocator_traits { private header "__memory/allocator_traits.h" } + module assume_aligned { private header "__memory/assume_aligned.h" } module auto_ptr { private header "__memory/auto_ptr.h" } module compressed_pair { private header "__memory/compressed_pair.h" } module concepts { private header "__memory/concepts.h" } diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -288,7 +288,7 @@ #if _LIBCPP_STD_VER > 17 # undef __cpp_lib_array_constexpr # define __cpp_lib_array_constexpr 201811L -// # define __cpp_lib_assume_aligned 201811L +# define __cpp_lib_assume_aligned 201811L # define __cpp_lib_atomic_flag_test 201907L // # define __cpp_lib_atomic_float 201711L # define __cpp_lib_atomic_lock_free_type_aliases 201907L diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -322,6 +322,7 @@ #include <__memory/allocator.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocator.h'}} #include <__memory/allocator_arg_t.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocator_arg_t.h'}} #include <__memory/allocator_traits.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocator_traits.h'}} +#include <__memory/assume_aligned.h> // expected-error@*:* {{use of private header from outside its module: '__memory/assume_aligned.h'}} #include <__memory/auto_ptr.h> // expected-error@*:* {{use of private header from outside its module: '__memory/auto_ptr.h'}} #include <__memory/compressed_pair.h> // expected-error@*:* {{use of private header from outside its module: '__memory/compressed_pair.h'}} #include <__memory/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__memory/concepts.h'}} diff --git a/libcxx/test/libcxx/utilities/memory/ptr.align/assume_aligned.power2.verify.cpp b/libcxx/test/libcxx/utilities/memory/ptr.align/assume_aligned.power2.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/memory/ptr.align/assume_aligned.power2.verify.cpp @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// #include + +// template +// [[nodiscard]] constexpr T* assume_aligned(T* ptr); + +// This test checks that we static_assert inside std::assume_aligned(p) +// when N is not a power of two. However, Clang will already emit an error +// in its own __builtin_assume_aligned, so we ignore that additional error +// for the purpose of this test. We also ignore the additional warning about +// remainder by 0 being undefined. +// ADDITIONAL_COMPILE_FLAGS: -Xclang -verify-ignore-unexpected=error -Xclang -verify-ignore-unexpected=warning + +#include + +void f() { + int *p = nullptr; + (void)std::assume_aligned<0>(p); // expected-error@*:* {{std::assume_aligned(p) requires N to be a power of two}} + (void)std::assume_aligned<3>(p); // expected-error@*:* {{std::assume_aligned(p) requires N to be a power of two}} + (void)std::assume_aligned<5>(p); // expected-error@*:* {{std::assume_aligned(p) requires N to be a power of two}} + (void)std::assume_aligned<33>(p); // expected-error@*:* {{std::assume_aligned(p) requires N to be a power of two}} +} diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp @@ -300,17 +300,11 @@ # error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++20" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should be defined in c++20" -# endif -# if __cpp_lib_assume_aligned != 201811L -# error "__cpp_lib_assume_aligned should have the value 201811L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_assume_aligned +# error "__cpp_lib_assume_aligned should be defined in c++20" +# endif +# if __cpp_lib_assume_aligned != 201811L +# error "__cpp_lib_assume_aligned should have the value 201811L in c++20" # endif # ifndef __cpp_lib_atomic_value_initialization @@ -436,17 +430,11 @@ # error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++2b" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should be defined in c++2b" -# endif -# if __cpp_lib_assume_aligned != 201811L -# error "__cpp_lib_assume_aligned should have the value 201811L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_assume_aligned +# error "__cpp_lib_assume_aligned should be defined in c++2b" +# endif +# if __cpp_lib_assume_aligned != 201811L +# error "__cpp_lib_assume_aligned should have the value 201811L in c++2b" # endif # ifndef __cpp_lib_atomic_value_initialization diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -2446,17 +2446,11 @@ # error "__cpp_lib_associative_heterogeneous_erasure should not be defined before c++2b" # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should be defined in c++20" -# endif -# if __cpp_lib_assume_aligned != 201811L -# error "__cpp_lib_assume_aligned should have the value 201811L in c++20" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_assume_aligned +# error "__cpp_lib_assume_aligned should be defined in c++20" +# endif +# if __cpp_lib_assume_aligned != 201811L +# error "__cpp_lib_assume_aligned should have the value 201811L in c++20" # endif # ifndef __cpp_lib_atomic_flag_test @@ -3669,17 +3663,11 @@ # endif # endif -# if !defined(_LIBCPP_VERSION) -# ifndef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should be defined in c++2b" -# endif -# if __cpp_lib_assume_aligned != 201811L -# error "__cpp_lib_assume_aligned should have the value 201811L in c++2b" -# endif -# else // _LIBCPP_VERSION -# ifdef __cpp_lib_assume_aligned -# error "__cpp_lib_assume_aligned should not be defined because it is unimplemented in libc++!" -# endif +# ifndef __cpp_lib_assume_aligned +# error "__cpp_lib_assume_aligned should be defined in c++2b" +# endif +# if __cpp_lib_assume_aligned != 201811L +# error "__cpp_lib_assume_aligned should have the value 201811L in c++2b" # endif # ifndef __cpp_lib_atomic_flag_test diff --git a/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.nodiscard.verify.cpp b/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.nodiscard.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.nodiscard.verify.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14, c++17 + +// #include + +// template +// [[nodiscard]] constexpr T* assume_aligned(T* ptr); + +#include + +void f() { + int *p = nullptr; + std::assume_aligned<4>(p); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.pass.cpp b/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/memory/ptr.align/assume_aligned.pass.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// #include + +// template +// [[nodiscard]] constexpr T* assume_aligned(T* ptr); + +#include +#include +#include + +#include "test_macros.h" + +template +constexpr void check(T* p) { + ASSERT_SAME_TYPE(T*, decltype(std::assume_aligned<1>(p))); + constexpr std::size_t alignment = alignof(T); + + if constexpr (alignment >= 1) + assert(p == std::assume_aligned<1>(p)); + if constexpr (alignment >= 2) + assert(p == std::assume_aligned<2>(p)); + if constexpr (alignment >= 4) + assert(p == std::assume_aligned<4>(p)); + if constexpr (alignment >= 8) + assert(p == std::assume_aligned<8>(p)); + if constexpr (alignment >= 16) + assert(p == std::assume_aligned<16>(p)); + if constexpr (alignment >= 32) + assert(p == std::assume_aligned<32>(p)); + if constexpr (alignment >= 64) + assert(p == std::assume_aligned<64>(p)); + if constexpr (alignment >= 128) + assert(p == std::assume_aligned<128>(p)); +} + +struct S { }; +struct alignas( 4) S4 { }; +struct alignas( 8) S8 { }; +struct alignas( 16) S16 { }; +struct alignas( 32) S32 { }; +struct alignas( 64) S64 { }; +struct alignas(128) S128 { }; + +constexpr bool tests() { + char c; + int i; + long l; + double d; + long double ld; + check( &c); + check( &i); + check( &l); + check( &d); + check(&ld); + + S s; + S4 s4; + S8 s8; + S16 s16; + S32 s32; + S64 s64; + S128 s128; + check(&s); + check(&s4); + check(&s8); + check(&s16); + check(&s32); + check(&s64); + check(&s128); + + return true; +} + +int main(int, char**) { + tests(); + static_assert(tests()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -103,7 +103,6 @@ "name": "__cpp_lib_assume_aligned", "values": { "c++20": 201811 }, "headers": ["memory"], - "unimplemented": True, }, { "name": "__cpp_lib_atomic_flag_test", "values": { "c++20": 201907 },