[libc-commits] [libc] [libc] Add helper function for aligning pointers and values (PR #183324)

via libc-commits libc-commits at lists.llvm.org
Wed Feb 25 08:14:43 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Joseph Huber (jhuber6)

<details>
<summary>Changes</summary>

Summary:
This is duplicated in a few places, make a common utility for it.


---
Full diff: https://github.com/llvm/llvm-project/pull/183324.diff


7 Files Affected:

- (modified) libc/src/__support/GPU/allocator.cpp (+9-16) 
- (modified) libc/src/__support/arg_list.h (+1-5) 
- (modified) libc/src/__support/block.h (-12) 
- (modified) libc/src/__support/math_extras.h (+26) 
- (modified) libc/src/string/memory_utils/generic/inline_strlen.h (+4-3) 
- (modified) libc/test/IntegrationTest/test.cpp (+2-1) 
- (modified) libc/test/UnitTest/HermeticTestUtils.cpp (+2-2) 


``````````diff
diff --git a/libc/src/__support/GPU/allocator.cpp b/libc/src/__support/GPU/allocator.cpp
index 02fdcd9759ffe..260971d578fa6 100644
--- a/libc/src/__support/GPU/allocator.cpp
+++ b/libc/src/__support/GPU/allocator.cpp
@@ -23,6 +23,7 @@
 #include "src/__support/GPU/fixedstack.h"
 #include "src/__support/GPU/utils.h"
 #include "src/__support/RPC/rpc_client.h"
+#include "src/__support/math_extras.h"
 #include "src/__support/threads/sleep.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 
@@ -124,13 +125,6 @@ static inline constexpr uint32_t get_chunk_id(uint32_t x) {
   return cpp::popcount(y) + 3 * (BITS_IN_WORD - cpp::countl_zero(y)) - 7;
 }
 
-// Rounds to the nearest power of two.
-template <uint32_t N, typename T>
-static inline constexpr T round_up(const T x) {
-  static_assert(((N - 1) & N) == 0, "N must be a power of two");
-  return (x + N) & ~(N - 1);
-}
-
 // Perform a lane parallel memset on a uint32_t pointer.
 void uniform_memset(uint32_t *s, uint32_t c, uint32_t n, uint64_t lane_mask) {
   uint32_t workers = cpp::popcount(lane_mask);
@@ -235,7 +229,7 @@ struct Slab {
 
   // Get the number of bytes needed to contain the bitfield bits.
   constexpr static uint32_t bitfield_bytes(uint32_t chunk_size) {
-    return __builtin_align_up(
+    return align_up(
         ((num_chunks(chunk_size) + BITS_IN_WORD - 1) / BITS_IN_WORD) * 8,
         MIN_ALIGNMENT + 1);
   }
@@ -335,12 +329,11 @@ struct Slab {
         uint32_t after = before | bitmask;
         uint64_t waiting = gpu::ballot(lane_mask, !result);
         if (!result)
-          start =
-              gpu::shuffle(waiting, cpp::countr_zero(waiting),
-                           ~after ? __builtin_align_down(index, BITS_IN_WORD) +
-                                        cpp::countr_zero(~after)
-                                  : __builtin_align_down(
-                                        impl::xorshift32(state), BITS_IN_WORD));
+          start = gpu::shuffle(
+              waiting, cpp::countr_zero(waiting),
+              ~after
+                  ? align_down(index, BITS_IN_WORD) + cpp::countr_zero(~after)
+                  : align_down(impl::xorshift32(state), BITS_IN_WORD));
         if (!result)
           sleep_briefly();
       }
@@ -605,7 +598,7 @@ void *allocate(uint64_t size) {
 
   // Allocations requiring a full slab or more go directly to memory.
   if (size >= SLAB_SIZE / 2)
-    return impl::rpc_allocate(impl::round_up<SLAB_SIZE>(size));
+    return impl::rpc_allocate(align_up(size, SLAB_SIZE));
 
   // Try to find a slab for the rounded up chunk size and allocate from it.
   uint32_t chunk_size = impl::get_chunk_size(static_cast<uint32_t>(size));
@@ -683,7 +676,7 @@ void *aligned_allocate(uint32_t alignment, uint64_t size) {
   // alignment and then round up. The index logic will round down properly.
   uint64_t rounded = size + alignment - MIN_ALIGNMENT;
   void *ptr = gpu::allocate(rounded);
-  return __builtin_align_up(ptr, alignment);
+  return align_up(ptr, alignment);
 }
 
 } // namespace gpu
diff --git a/libc/src/__support/arg_list.h b/libc/src/__support/arg_list.h
index 7b78a9c0fe619..864c3c76d4ccc 100644
--- a/libc/src/__support/arg_list.h
+++ b/libc/src/__support/arg_list.h
@@ -12,6 +12,7 @@
 #include "hdr/stdint_proxy.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/math_extras.h"
 #include "src/string/memory_utils/inline_memcpy.h"
 
 #include <stdarg.h>
@@ -20,11 +21,6 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
-template <typename V, typename A>
-LIBC_INLINE constexpr V align_up(V val, A align) {
-  return ((val + V(align) - 1) / V(align)) * V(align);
-}
-
 class ArgList {
   va_list vlist;
 
diff --git a/libc/src/__support/block.h b/libc/src/__support/block.h
index d45af1079a5bc..5cbf1b70fa54c 100644
--- a/libc/src/__support/block.h
+++ b/libc/src/__support/block.h
@@ -23,18 +23,6 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-/// Returns the value rounded down to the nearest multiple of alignment.
-LIBC_INLINE constexpr size_t align_down(size_t value, size_t alignment) {
-  // Note this shouldn't overflow since the result will always be <= value.
-  return (value / alignment) * alignment;
-}
-
-/// Returns the value rounded up to the nearest multiple of alignment. May wrap
-/// around.
-LIBC_INLINE constexpr size_t align_up(size_t value, size_t alignment) {
-  return align_down(value + alignment - 1, alignment);
-}
-
 using ByteSpan = cpp::span<LIBC_NAMESPACE::cpp::byte>;
 using cpp::optional;
 
diff --git a/libc/src/__support/math_extras.h b/libc/src/__support/math_extras.h
index 3d0f2703b69a4..c5c54bc97a4de 100644
--- a/libc/src/__support/math_extras.h
+++ b/libc/src/__support/math_extras.h
@@ -171,6 +171,32 @@ count_zeros(T value) {
   return cpp::popcount<T>(static_cast<T>(~value));
 }
 
+// Returns the value rounded down to the nearest multiple of alignment.
+// Alignment must be a power of two.
+template <typename T>
+LIBC_INLINE constexpr T align_down(T value, size_t alignment) {
+#if __has_builtin(__builtin_align_down)
+  return __builtin_align_down(value, alignment);
+#else
+  using A = cpp::conditional_t<cpp::is_pointer_v<T>, uintptr_t, T>;
+  return cpp::bit_cast<T>(
+      static_cast<A>(cpp::bit_cast<A>(value) & ~(A(alignment) - 1)));
+#endif
+}
+
+// Returns the value rounded up to the nearest multiple of alignment.
+// Alignment must be a power of two.
+template <typename T>
+LIBC_INLINE constexpr T align_up(T value, size_t alignment) {
+#if __has_builtin(__builtin_align_up)
+  return __builtin_align_up(value, alignment);
+#else
+  using A = cpp::conditional_t<cpp::is_pointer_v<T>, uintptr_t, T>;
+  return cpp::bit_cast<T>(static_cast<A>(
+      (cpp::bit_cast<A>(value) + A(alignment) - 1) & ~(A(alignment) - 1)));
+#endif
+}
+
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC___SUPPORT_MATH_EXTRAS_H
diff --git a/libc/src/string/memory_utils/generic/inline_strlen.h b/libc/src/string/memory_utils/generic/inline_strlen.h
index e9f1542f41424..7dbace5384bd9 100644
--- a/libc/src/string/memory_utils/generic/inline_strlen.h
+++ b/libc/src/string/memory_utils/generic/inline_strlen.h
@@ -12,6 +12,7 @@
 #include "src/__support/CPP/bit.h"
 #include "src/__support/CPP/simd.h"
 #include "src/__support/common.h"
+#include "src/__support/math_extras.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace clang_vector {
@@ -29,8 +30,8 @@ LIBC_NO_SANITIZE_OOB_ACCESS LIBC_INLINE size_t string_length(const char *src) {
   constexpr cpp::simd<char> null_byte = cpp::splat('\0');
 
   size_t alignment = alignof(cpp::simd<char>);
-  const cpp::simd<char> *aligned = reinterpret_cast<const cpp::simd<char> *>(
-      __builtin_align_down(src, alignment));
+  const cpp::simd<char> *aligned =
+      reinterpret_cast<const cpp::simd<char> *>(align_down(src, alignment));
 
   cpp::simd<char> chars = cpp::load<cpp::simd<char>>(aligned, /*aligned=*/true);
   cpp::simd_mask<char> mask = chars == null_byte;
@@ -64,7 +65,7 @@ find_first_character(const unsigned char *s, unsigned char c, size_t n) {
 
   size_t alignment = alignof(Vector);
   const Vector *aligned =
-      reinterpret_cast<const Vector *>(__builtin_align_down(s, alignment));
+      reinterpret_cast<const Vector *>(align_down(s, alignment));
 
   Vector chars = cpp::load<Vector>(aligned, /*aligned=*/true);
   Mask cmp_v = chars == c_byte;
diff --git a/libc/test/IntegrationTest/test.cpp b/libc/test/IntegrationTest/test.cpp
index 19eb255e57b04..86966acef78da 100644
--- a/libc/test/IntegrationTest/test.cpp
+++ b/libc/test/IntegrationTest/test.cpp
@@ -9,6 +9,7 @@
 #include "src/__support/CPP/atomic.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/math_extras.h"
 #include <stddef.h>
 
 #ifdef LIBC_TARGET_ARCH_IS_AARCH64
@@ -72,7 +73,7 @@ extern "C" {
 
 void *malloc(size_t size) {
   LIBC_NAMESPACE::cpp::AtomicRef<size_t> ref(ptr);
-  size = (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
+  size = LIBC_NAMESPACE::align_up(size, ALIGNMENT);
   size_t old_ptr =
       ref.fetch_add(size, LIBC_NAMESPACE::cpp::MemoryOrder::RELAXED);
   if (static_cast<size_t>(old_ptr + size) >= MEMORY_SIZE)
diff --git a/libc/test/UnitTest/HermeticTestUtils.cpp b/libc/test/UnitTest/HermeticTestUtils.cpp
index 3c82d1e963643..100130f3a018d 100644
--- a/libc/test/UnitTest/HermeticTestUtils.cpp
+++ b/libc/test/UnitTest/HermeticTestUtils.cpp
@@ -9,6 +9,7 @@
 #include "hdr/stdint_proxy.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/math_extras.h"
 #include <stddef.h>
 
 #ifdef LIBC_TARGET_ARCH_IS_AARCH64
@@ -80,9 +81,8 @@ int atexit(void (*func)(void)) { return LIBC_NAMESPACE::atexit(func); }
 
 void *malloc(size_t s) {
   // Keep the bump pointer aligned on an eight byte boundary.
-  s = ((s + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
   void *mem = ptr;
-  ptr += s;
+  ptr += LIBC_NAMESPACE::align_up(s, ALIGNMENT);
   return static_cast<uint64_t>(ptr - memory) >= MEMORY_SIZE ? nullptr : mem;
 }
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/183324


More information about the libc-commits mailing list