[libcxx-commits] [libcxx] [libc++] Improve aligned allocation support with picolibc. (PR #96086)

Simon Tatham via libcxx-commits libcxx-commits at lists.llvm.org
Wed Jun 19 08:58:19 PDT 2024


https://github.com/statham-arm created https://github.com/llvm/llvm-project/pull/96086

picolibc has two memory allocators, one of which doesn't provide posix_memalign(). But both of them provide C11 aligned_alloc(). Unfortunately the libc++ test support code has no option to use aligned_alloc, only posix_memalign or MSVC-style _aligned_malloc.

This patch modifies the aligned allocation in libc++ test support to use the __libcpp_aligned_alloc helper function already defined in <include/__memory>, if it's available. That already has an option to use aligned_alloc and knows how to use it right.

Also added version detection for picolibc in <include/__config>, so that __libcpp_aligned_alloc can switch to aligned_alloc() if that's a better choice than posix_memalign.

Finally, I couldn't resist renaming the test support function `alocate_aligned_impl` so that it spells 'allocate' correctly. (Keeping track of identifiers in software is hard enough without also having to remember which ones have which spelling mistakes.)

>From 302adb4fa01e15352e6a59c17ca10edbcb4126e6 Mon Sep 17 00:00:00 2001
From: Simon Tatham <simon.tatham at arm.com>
Date: Wed, 19 Jun 2024 15:06:44 +0100
Subject: [PATCH] [libc++] Improve aligned allocation support with picolibc.

picolibc has two memory allocators, one of which doesn't provide
posix_memalign(). But both of them provide C11 aligned_alloc().
Unfortunately the libc++ test support code has no option to use
aligned_alloc, only posix_memalign or MSVC-style _aligned_malloc.

This patch modifies the aligned allocation in libc++ test support to
use the __libcpp_aligned_alloc helper function already defined in
<include/__memory>, if it's available. That already has an option to
use aligned_alloc and knows how to use it right.

Also added version detection for picolibc in <include/__config>, so
that __libcpp_aligned_alloc can switch to aligned_alloc() if that's a
better choice than posix_memalign.

Finally, I couldn't resist renaming the test support function
`alocate_aligned_impl` so that it spells 'allocate' correctly.
(Keeping track of identifiers in software is hard enough without also
having to remember which ones have which spelling mistakes.)
---
 libcxx/include/__config                 | 13 +++++++++++++
 libcxx/include/__memory/aligned_alloc.h |  2 +-
 libcxx/test/support/count_new.h         | 20 +++++++++++++-------
 3 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/libcxx/include/__config b/libcxx/include/__config
index dfb14fd6a380c..6fd668ae45b46 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -679,6 +679,9 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
 #  endif
 
+// _LIBCPP_HAS_NO_C11_ALIGNED_ALLOC should be defined on platforms
+// where you would expect C11 aligned_alloc() to exist but it doesn't.
+
 // 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__)
@@ -691,6 +694,16 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_HAS_NO_C11_ALIGNED_ALLOC
 #  endif
 
+// _LIBCPP_HAS_C11_ALIGNED_ALLOC may be defined on platforms where C11
+// aligned_alloc() is known to exist even if you might not expect it.
+
+// picolibc has two memory allocators, one of which doesn't provide
+// posix_memalign(). But they both reliably provide C11
+// aligned_alloc(), at least since 1.4.4.
+#  if defined(__PICOLIBC__) && (__PICOLIBC__ > 1 || (__PICOLIBC__ == 1 && (__PICOLIBC_MINOR__ > 4 || (__PICOLIBC_MINOR__ == 4 && __PICOLIBC_PATCHLEVEL__ >= 4))))
+#    define _LIBCPP_HAS_C11_ALIGNED_ALLOC
+#  endif
+
 #  if defined(__APPLE__) || defined(__FreeBSD__)
 #    define _LIBCPP_HAS_DEFAULTRUNELOCALE
 #  endif
diff --git a/libcxx/include/__memory/aligned_alloc.h b/libcxx/include/__memory/aligned_alloc.h
index cb424328bcafc..f4bef48cd7f55 100644
--- a/libcxx/include/__memory/aligned_alloc.h
+++ b/libcxx/include/__memory/aligned_alloc.h
@@ -30,7 +30,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 inline _LIBCPP_HIDE_FROM_ABI 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 >= 17 && !defined(_LIBCPP_HAS_NO_C11_ALIGNED_ALLOC)
+#  elif defined(_LIBCPP_HAS_C11_ALIGNED_ALLOC) || (_LIBCPP_STD_VER >= 17 && !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
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index 0a95e05b72421..b20157fd0b271 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -16,6 +16,8 @@
 #include <new>
 #include <type_traits>
 
+#include <__memory/aligned_alloc.h> // reuse aligned allocation helper
+
 #include "test_macros.h"
 
 #if defined(TEST_HAS_SANITIZERS)
@@ -455,10 +457,12 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
 #      define USE_ALIGNED_ALLOC
 #    endif
 
-inline void* alocate_aligned_impl(std::size_t size, std::align_val_t align) {
+inline void* allocate_aligned_impl(std::size_t size, std::align_val_t align) {
   const std::size_t alignment = static_cast<std::size_t>(align);
   void* ret                   = nullptr;
-#    ifdef USE_ALIGNED_ALLOC
+#    ifndef _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
+  ret = std::__libcpp_aligned_alloc(alignment, size);
+#    elif defined USE_ALIGNED_ALLOC
   ret = _aligned_malloc(size, alignment);
 #    else
   assert(posix_memalign(&ret, std::max(alignment, sizeof(void*)), size) != EINVAL);
@@ -468,7 +472,9 @@ inline void* alocate_aligned_impl(std::size_t size, std::align_val_t align) {
 
 inline void free_aligned_impl(void* ptr, std::align_val_t) {
   if (ptr) {
-#    ifdef USE_ALIGNED_ALLOC
+#    ifndef _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
+    std::__libcpp_aligned_free(ptr);
+#    elif defined USE_ALIGNED_ALLOC
     ::_aligned_free(ptr);
 #    else
     ::free(ptr);
@@ -479,7 +485,7 @@ inline void free_aligned_impl(void* ptr, std::align_val_t) {
 // operator new(size_t, align_val_t[, nothrow_t]) and operator delete(size_t, align_val_t[, nothrow_t])
 void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewCalled(s, static_cast<std::size_t>(av));
-  void* p = alocate_aligned_impl(s, av);
+  void* p = allocate_aligned_impl(s, av);
   if (p == nullptr)
     detail::throw_bad_alloc_helper();
   return p;
@@ -495,7 +501,7 @@ void* operator new(std::size_t s, std::align_val_t av, std::nothrow_t const&) TE
     return nullptr;
   }
 #    endif
-  return alocate_aligned_impl(s, av);
+  return allocate_aligned_impl(s, av);
 }
 
 void operator delete(void* p, std::align_val_t av) TEST_NOEXCEPT {
@@ -511,7 +517,7 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
 // operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
 void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
   getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast<std::size_t>(av));
-  void* p = alocate_aligned_impl(s, av);
+  void* p = allocate_aligned_impl(s, av);
   if (p == nullptr)
     detail::throw_bad_alloc_helper();
   return p;
@@ -527,7 +533,7 @@ void* operator new[](std::size_t s, std::align_val_t av, std::nothrow_t const&)
     return nullptr;
   }
 #    endif
-  return alocate_aligned_impl(s, av);
+  return allocate_aligned_impl(s, av);
 }
 
 void operator delete[](void* p, std::align_val_t av) TEST_NOEXCEPT {



More information about the libcxx-commits mailing list