mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 18:26:37 +00:00
[libc++] Use aligned_alloc instead of posix_memalign for C++17
C++17 defines the C11 `aligned_alloc`, so we can use that instead of posix_memalign. This change allows building against picolibc without defining _DEFAULT_SOURCE/_GNU_SOURCE. The C11 `aligned_alloc` function should be available on all supported non-Windows platforms except for macOS where we need version 10.15. There is one caveat: aligned_alloc() requires that __size is a multiple of __alignment, but [new.delete.general] only states "if the value of an alignment argument passed to any of these functions is not a valid alignment value, the behavior is undefined". To handle calls such as ::operator new(1, std::align_val_t(128)), we round up __size to __alignment (and check for wrap-around). This is required at least for macOS where aligned_alloc(128, 1) returns an error instead of allocating memory (glibc ignores the specification). Differential Revision: https://reviews.llvm.org/D138196
This commit is contained in:
parent
659c512b06
commit
eb6fbad711
@ -720,6 +720,15 @@ _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
|
||||
# define _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
|
||||
# endif
|
||||
|
||||
// It is not yet possible to use aligned_alloc() on all Apple platforms since
|
||||
// 10.15 was the first version to ship an implementation of aligned_alloc().
|
||||
# if defined(__APPLE__)
|
||||
# if (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
|
||||
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101500)
|
||||
# define _LIBCPP_HAS_NO_C11_ALIGNED_ALLOC
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
# define _LIBCPP_HAS_DEFAULTRUNELOCALE
|
||||
# endif
|
||||
|
@ -337,16 +337,26 @@ inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate_unsized(void* __ptr, s
|
||||
// chances are that you want to use `__libcpp_allocate` instead.
|
||||
//
|
||||
// Returns the allocated memory, or `nullptr` on failure.
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
|
||||
#if defined(_LIBCPP_MSVCRT_LIKE)
|
||||
return ::_aligned_malloc(__size, __alignment);
|
||||
#else
|
||||
void* __result = nullptr;
|
||||
(void)::posix_memalign(&__result, __alignment, __size);
|
||||
// If posix_memalign fails, __result is unmodified so we still return `nullptr`.
|
||||
return __result;
|
||||
#endif
|
||||
inline _LIBCPP_INLINE_VISIBILITY void* __libcpp_aligned_alloc(std::size_t __alignment, std::size_t __size) {
|
||||
# if defined(_LIBCPP_MSVCRT_LIKE)
|
||||
return ::_aligned_malloc(__size, __alignment);
|
||||
# elif _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_C11_ALIGNED_ALLOC)
|
||||
// aligned_alloc() requires that __size is a multiple of __alignment,
|
||||
// but for C++ [new.delete.general], only states "if the value of an
|
||||
// alignment argument passed to any of these functions is not a valid
|
||||
// alignment value, the behavior is undefined".
|
||||
// To handle calls such as ::operator new(1, std::align_val_t(128)), we
|
||||
// round __size up to the next multiple of __alignment.
|
||||
size_t __rounded_size = (__size + __alignment - 1) & ~(__alignment - 1);
|
||||
// Rounding up could have wrapped around to zero, so we have to add another
|
||||
// max() ternary to the actual call site to avoid succeeded in that case.
|
||||
return ::aligned_alloc(__alignment, __size > __rounded_size ? __size : __rounded_size);
|
||||
# else
|
||||
void* __result = nullptr;
|
||||
(void)::posix_memalign(&__result, __alignment, __size);
|
||||
// If posix_memalign fails, __result is unmodified so we still return `nullptr`.
|
||||
return __result;
|
||||
# endif
|
||||
}
|
||||
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -28,6 +28,33 @@
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
static void test_allocations(size_t size, size_t alignment) {
|
||||
{
|
||||
void* ptr = ::operator new(size, std::align_val_t(alignment));
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0);
|
||||
::operator delete(ptr, std::align_val_t(alignment));
|
||||
}
|
||||
{
|
||||
void* ptr = ::operator new(size, std::align_val_t(alignment), std::nothrow);
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0);
|
||||
::operator delete(ptr, std::align_val_t(alignment), std::nothrow);
|
||||
}
|
||||
{
|
||||
void* ptr = ::operator new[](size, std::align_val_t(alignment));
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0);
|
||||
::operator delete[](ptr, std::align_val_t(alignment));
|
||||
}
|
||||
{
|
||||
void* ptr = ::operator new[](size, std::align_val_t(alignment), std::nothrow);
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % alignment == 0);
|
||||
::operator delete[](ptr, std::align_val_t(alignment), std::nothrow);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
{
|
||||
static_assert(std::is_enum<std::align_val_t>::value, "");
|
||||
@ -49,30 +76,24 @@ int main(int, char**) {
|
||||
assert(a == std::align_val_t(0));
|
||||
assert(b == std::align_val_t(32));
|
||||
}
|
||||
{
|
||||
void *ptr = ::operator new(1, std::align_val_t(128));
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % 128 == 0);
|
||||
::operator delete(ptr, std::align_val_t(128));
|
||||
}
|
||||
{
|
||||
void *ptr = ::operator new(1, std::align_val_t(128), std::nothrow);
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % 128 == 0);
|
||||
::operator delete(ptr, std::align_val_t(128), std::nothrow);
|
||||
}
|
||||
{
|
||||
void *ptr = ::operator new[](1, std::align_val_t(128));
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % 128 == 0);
|
||||
::operator delete[](ptr, std::align_val_t(128));
|
||||
}
|
||||
{
|
||||
void *ptr = ::operator new[](1, std::align_val_t(128), std::nothrow);
|
||||
assert(ptr);
|
||||
assert(reinterpret_cast<std::uintptr_t>(ptr) % 128 == 0);
|
||||
::operator delete[](ptr, std::align_val_t(128), std::nothrow);
|
||||
}
|
||||
// First, check the basic case, a large allocation with alignment==size.
|
||||
test_allocations(64, 64);
|
||||
// Size being a multiple of alignment also needs to be supported.
|
||||
test_allocations(64, 32);
|
||||
// When aligned allocation is implemented using posix_memalign,
|
||||
// that function requires a minimum alignment of sizeof(void*).
|
||||
// Check that we can also create overaligned allocations with
|
||||
// an alignment argument less than sizeof(void*).
|
||||
test_allocations(2, 2);
|
||||
// When implemented using the C11 aligned_alloc() function,
|
||||
// that requires that size be a multiple of alignment.
|
||||
// However, the C++ operator new has no such requirements.
|
||||
// Check that we can create an overaligned allocation that does
|
||||
// adhere to not have this constraint.
|
||||
test_allocations(1, 128);
|
||||
// Finally, test size > alignment, but with size not being
|
||||
// a multiple of alignment.
|
||||
test_allocations(65, 32);
|
||||
#ifndef TEST_HAS_NO_RTTI
|
||||
{
|
||||
// Check that libc++ doesn't define align_val_t in a versioning namespace.
|
||||
|
Loading…
x
Reference in New Issue
Block a user