-
Notifications
You must be signed in to change notification settings - Fork 18.7k
[libc++] Add __new_at_least #215813
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
base: main
Are you sure you want to change the base?
[libc++] Add __new_at_least #215813
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,8 +13,11 @@ | |||||||
| #include <__cstddef/max_align_t.h> | ||||||||
| #include <__cstddef/size_t.h> | ||||||||
| #include <__new/align_val_t.h> | ||||||||
| #include <__new/allocation_result.h> | ||||||||
| #include <__new/new_at_least.h> | ||||||||
| #include <__type_traits/type_identity.h> | ||||||||
| #include <__utility/element_count.h> | ||||||||
| #include <cstdint> | ||||||||
|
|
||||||||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||||||||
| # pragma GCC system_header | ||||||||
|
|
@@ -42,6 +45,34 @@ __libcpp_allocate(__element_count __n, [[__maybe_unused__]] size_t __align = _LI | |||||||
| return static_cast<_Tp*>(__builtin_operator_new(__size)); | ||||||||
| } | ||||||||
|
|
||||||||
| template <class _Tp> | ||||||||
| _LIBCPP_NO_CFI __allocation_result<_Tp*> | ||||||||
| __libcpp_allocate_at_least(__element_count __n, [[__maybe_unused__]] size_t __align = _LIBCPP_ALIGNOF(_Tp)) { | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just recording the discussion we had just now: We could then apply the same treatment to |
||||||||
| #if _LIBCPP_AVAILABILITY_HAS_NEW_AT_LEAST | ||||||||
| size_t __size = static_cast<size_t>(__n) * sizeof(_Tp); | ||||||||
| # if _LIBCPP_HAS_ALIGNED_ALLOCATION | ||||||||
| if (__is_overaligned_for_new(__align)) { | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can simplify to: #if _LIBCPP_HAS_ALIGNED_ALLOCATION
auto [__ptr, __count] = __is_overaligned_for_new(__align)
? std::__new_at_least(__size, static_cast<align_val_t>(__align))
: std::__new_at_least(__size);
#else
auto [__ptr, __count] = std::__new_at_least(__size);
#endif
_LIBCPP_ASSUME(...);
_LIBCPP_ASSUME(...);
_LIBCPP_ASSUME(...);
return __allocation_result<_Tp*>(static_cast<_Tp*>(__ptr), __count / sizeof(_Tp)); |
||||||||
| auto [__ptr, __count] = std::__new_at_least(__size, static_cast<align_val_t>(__align)); | ||||||||
|
philnik777 marked this conversation as resolved.
|
||||||||
| # if __has_builtin(__builtin_assume) | ||||||||
| __builtin_assume(__ptr); | ||||||||
| __builtin_assume((reinterpret_cast<uintptr_t>(__ptr) & (__align - 1)) == 0); | ||||||||
| __builtin_assume(__count >= __size); | ||||||||
| # endif | ||||||||
|
Comment on lines
+56
to
+60
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should redefine llvm-project/libcxx/include/__assert Lines 26 to 28 in 6160b5a
Oh and we should probably define that in |
||||||||
| return {static_cast<_Tp*>(__ptr), __count / sizeof(_Tp)}; | ||||||||
| } | ||||||||
| # endif | ||||||||
| auto [__ptr, __count] = std::__new_at_least(__size); | ||||||||
| # if __has_builtin(__builtin_assume) | ||||||||
| __builtin_assume(__ptr); | ||||||||
| __builtin_assume((reinterpret_cast<uintptr_t>(__ptr) & (__align - 1)) == 0); | ||||||||
| __builtin_assume(__count >= __size); | ||||||||
| # endif | ||||||||
| return __allocation_result<_Tp*>(static_cast<_Tp*>(__ptr), __count / sizeof(_Tp)); | ||||||||
| #else | ||||||||
| return __allocation_result<_Tp*>(std::__libcpp_allocate<_Tp>(__n, __align), __n); | ||||||||
| #endif | ||||||||
| } | ||||||||
|
|
||||||||
| #if defined(__cpp_sized_deallocation) && __cpp_sized_deallocation >= 201309L | ||||||||
| # define _LIBCPP_ONLY_IF_SIZED_DEALLOCATION(...) __VA_ARGS__ | ||||||||
| #else | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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___NEW_ALLOCATION_RESULT_H | ||
| #define _LIBCPP___NEW_ALLOCATION_RESULT_H | ||
|
|
||
| #include <__config> | ||
| #include <__cstddef/size_t.h> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
|
|
||
| template <class _Pointer, class _SizeT = size_t> | ||
| struct __allocation_result { | ||
| _Pointer ptr; | ||
| _SizeT count; | ||
|
|
||
| _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __allocation_result(_Pointer __ptr, _SizeT __count) | ||
| : ptr(__ptr), count(__count) {} | ||
| }; | ||
| _LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__allocation_result); | ||
|
|
||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP___NEW_ALLOCATION_RESULT_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // 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___NEW_NEW_AT_LEAST_H | ||
| #define _LIBCPP___NEW_NEW_AT_LEAST_H | ||
|
|
||
| #include <__config> | ||
| #include <__cstddef/size_t.h> | ||
| #include <__new/align_val_t.h> | ||
| #include <__new/allocation_result.h> | ||
|
|
||
| #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) | ||
| # pragma GCC system_header | ||
| #endif | ||
|
|
||
| #if _LIBCPP_AVAILABILITY_HAS_NEW_AT_LEAST | ||
|
|
||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||
| _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS | ||
|
|
||
| // `__new_at_least` acts like an overload of `operator new` which takes a size (and possibly alignment) and returns | ||
| // a pointer as well as the actually allocated amount of memory. If the user replaces the relevant `operator new` | ||
| // overload this will fall back to calling that. Otherwise it tries to allocate in a way to get the actually allocated | ||
| // size, depending on what the platform provides. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should document that this CANNOT be used in places where we would want to call |
||
|
|
||
| _LIBCPP_MALLOC_SPAN _LIBCPP_EXPORTED_FROM_ABI __allocation_result<void*> __new_at_least(size_t); | ||
|
philnik777 marked this conversation as resolved.
|
||
| # if _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION | ||
| _LIBCPP_MALLOC_SPAN _LIBCPP_EXPORTED_FROM_ABI __allocation_result<void*> __new_at_least(size_t, align_val_t); | ||
| # endif | ||
|
|
||
| _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS | ||
| _LIBCPP_END_NAMESPACE_STD | ||
|
|
||
| #endif // _LIBCPP_AVAILABILITY_HAS_NEW_AT_LEAST | ||
|
|
||
| #endif // _LIBCPP___NEW_NEW_AT_LEAST_H | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -6,6 +6,8 @@ | |||||
| // | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we would need to put |
||||||
| //===----------------------------------------------------------------------===// | ||||||
|
|
||||||
| #include <__memory/allocate_at_least.h> | ||||||
| #include <__new/new_at_least.h> | ||||||
| #include <cstddef> | ||||||
| #include <cstdlib> | ||||||
| #include <new> | ||||||
|
|
@@ -216,3 +218,90 @@ OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignmen | |||||
| ::operator delete[](ptr, alignment); | ||||||
| } | ||||||
| #endif // _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION | ||||||
|
|
||||||
| // This part implements __new_at_least, a version of operator new that returns the actually allocated amount of memory | ||||||
| // in addition to the pointer. Since users are allowed to replace operator new, we have to check whether it is replaced | ||||||
| // and fall back to that. Otherwise we can use platform-specific APIs for an improved implementation. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| // | ||||||
| // We do that check via `gnu::ifunc` if it's available. Otherwise we don't do anything and just unconditionally forward | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| // to `operator new(size_t{, align_val_t})`. `gnu::ifunc` takes the mangled name of a resolver function. That resolver | ||||||
| // function returns a pointer to the function that should be linked. We make use of `__is_function_overridden` to detect | ||||||
| // whether we can use our own special implementation or have to fall back to a user-provided operator new. This approach | ||||||
| // avoids any repeated checks in a very hot path. | ||||||
|
|
||||||
| #ifdef __APPLE__ | ||||||
|
philnik777 marked this conversation as resolved.
|
||||||
| # include <malloc/malloc.h> | ||||||
| #elifdef __FreeBSD__ | ||||||
| # include <malloc_np.h> | ||||||
| #endif | ||||||
|
|
||||||
| // FIXME: Clang should really accept functions in [[gnu::ifunc]] (or possibly [[clang::ifunc]]) | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we have a bug report for that? It would make this a whole lot more portable. |
||||||
| using std::__allocation_result; | ||||||
|
|
||||||
| using new_t = void*(std::size_t); | ||||||
| using new_aligned_t = void*(std::size_t, std::align_val_t); | ||||||
|
|
||||||
| using new_at_least_t = __allocation_result<void*>(std::size_t); | ||||||
| using new_at_least_aligned_t = __allocation_result<void*>(std::size_t, std::align_val_t); | ||||||
|
|
||||||
| [[maybe_unused]] static new_at_least_t* new_at_least_resolver() { | ||||||
| if (std::__is_function_overridden < new_t, operator new>()) { | ||||||
| return [](std::size_t size) -> __allocation_result<void*> { return {::operator new(size), size}; }; | ||||||
| } else { | ||||||
| return [](std::size_t size) -> __allocation_result<void*> { | ||||||
| #ifdef __APPLE__ | ||||||
| auto good_size = ::malloc_good_size(size); | ||||||
| return {operator_new_impl<on_failure::throw_bad_alloc>(good_size), good_size}; | ||||||
| #elifdef __FreeBSD__ | ||||||
| auto good_size = ::nallocx(size, 0); | ||||||
| return {operator_new_impl<on_failure::throw_bad_alloc>(good_size), good_size}; | ||||||
| #else | ||||||
| // Other platforms should specialize this for their system allocator | ||||||
| return {operator_new_impl<on_failure::throw_bad_alloc>(size), size}; | ||||||
| #endif | ||||||
| }; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| [[maybe_unused]] static new_at_least_aligned_t* new_at_least_aligned_resolver() { | ||||||
| if (std::__is_function_overridden < new_aligned_t, operator new>()) { | ||||||
|
ldionne marked this conversation as resolved.
|
||||||
| return [](std::size_t size, std::align_val_t align) -> __allocation_result<void*> { | ||||||
| return {::operator new(size, align), size}; | ||||||
| }; | ||||||
| } else { | ||||||
| return [](std::size_t size, std::align_val_t align) -> __allocation_result<void*> { | ||||||
| #ifdef __APPLE__ | ||||||
| auto good_size = ::malloc_good_size(size); | ||||||
| return {operator_new_aligned_impl<on_failure::throw_bad_alloc>(good_size, align), good_size}; | ||||||
| #elifdef __FreeBSD__ | ||||||
| auto good_size = ::nallocx(size, MALLOCX_ALIGN(static_cast<size_t>(align))); | ||||||
| return {operator_new_aligned_impl<on_failure::throw_bad_alloc>(good_size), good_size}; | ||||||
| #else | ||||||
| return {operator_new_aligned_impl<on_failure::throw_bad_alloc>(size, align), size}; | ||||||
| #endif | ||||||
| }; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||||||
| _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS | ||||||
|
|
||||||
| #if __has_cpp_attribute(gnu::ifunc) | ||||||
|
|
||||||
| [[gnu::ifunc("_ZL21new_at_least_resolverv")]] new_at_least_t __new_at_least; | ||||||
| [[gnu::ifunc("_ZL29new_at_least_aligned_resolverv")]] new_at_least_aligned_t __new_at_least; | ||||||
|
|
||||||
| #else | ||||||
|
|
||||||
| std::__allocation_result<void*> __new_at_least(std::size_t size) { return {::operator new(size), size}; } | ||||||
|
|
||||||
| # if _LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION | ||||||
| std::__allocation_result<void*> __new_at_least(std::size_t size, std::align_val_t align) { | ||||||
| return {::operator new(size, align), size}; | ||||||
| } | ||||||
| # endif | ||||||
|
|
||||||
| #endif | ||||||
|
|
||||||
| _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS | ||||||
| _LIBCPP_END_NAMESPACE_STD | ||||||
Uh oh!
There was an error while loading. Please reload this page.