[libc-commits] [libc] [libc] Add helper function for aligning pointers and values (PR #183324)
Joseph Huber via libc-commits
libc-commits at lists.llvm.org
Wed Feb 25 08:39:19 PST 2026
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/183324
>From 4d4350620161e52e7fe88b8d601646c62f989452 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Wed, 25 Feb 2026 10:05:59 -0600
Subject: [PATCH] [libc] Add helper function for aligning pointers and values
Summary:
This is duplicated in a few places, make a common utility for it.
---
libc/src/__support/GPU/allocator.cpp | 25 +++++-------
libc/src/__support/arg_list.h | 6 +--
libc/src/__support/block.h | 16 +-------
libc/src/__support/math_extras.h | 39 +++++++++++++++++++
.../memory_utils/generic/inline_strlen.h | 7 ++--
libc/test/IntegrationTest/test.cpp | 3 +-
libc/test/UnitTest/HermeticTestUtils.cpp | 4 +-
7 files changed, 59 insertions(+), 41 deletions(-)
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..5227d62c9c565 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;
@@ -313,13 +301,13 @@ class Block {
LIBC_INLINE static uintptr_t
next_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
- return align_up(ptr + sizeof(Block), usable_space_alignment) -
+ return align_to(ptr + sizeof(Block), usable_space_alignment) -
sizeof(Block);
}
LIBC_INLINE static uintptr_t
prev_possible_block_start(uintptr_t ptr,
size_t usable_space_alignment = MIN_ALIGN) {
- return align_down(ptr, usable_space_alignment) - sizeof(Block);
+ return align_to_down(ptr, usable_space_alignment) - sizeof(Block);
}
private:
diff --git a/libc/src/__support/math_extras.h b/libc/src/__support/math_extras.h
index 3d0f2703b69a4..dde7c23f72a9e 100644
--- a/libc/src/__support/math_extras.h
+++ b/libc/src/__support/math_extras.h
@@ -171,6 +171,45 @@ 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
+}
+
+// Returns the value rounded down to the nearest multiple of alignment.
+// Works for any positive alignment value, not just powers of two.
+template <typename T>
+LIBC_INLINE constexpr T align_to_down(T value, T alignment) {
+ return (value / alignment) * alignment;
+}
+
+// Returns the value rounded up to the nearest multiple of alignment.
+// Works for any positive alignment value, not just powers of two.
+template <typename T> LIBC_INLINE constexpr T align_to(T value, T alignment) {
+ return align_to_down(value + alignment - 1, alignment);
+}
+
} // 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;
}
More information about the libc-commits
mailing list