Skip to content
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

[libc] Fix malloc Block alignment issue on riscv32 #117815

Merged
merged 6 commits into from
Jan 16, 2025

Conversation

mysterymath
Copy link
Contributor

Aligning blocks to max_align_t is neither necessary nor sufficient to ensure that the usable_space() is so aligned. Instead, we make this an invariant of Block and maintain it in init() and split().

This allows targets like riscv32 with small pointers and large max_align_t to maintain the property that all available blocks are aligned for malloc(). This change adjusts the tests to match and also updates them closer to llvm-libc style.

@llvmbot
Copy link
Member

llvmbot commented Nov 27, 2024

@llvm/pr-subscribers-libc

Author: Daniel Thornburgh (mysterymath)

Changes

Aligning blocks to max_align_t is neither necessary nor sufficient to ensure that the usable_space() is so aligned. Instead, we make this an invariant of Block and maintain it in init() and split().

This allows targets like riscv32 with small pointers and large max_align_t to maintain the property that all available blocks are aligned for malloc(). This change adjusts the tests to match and also updates them closer to llvm-libc style.


Patch is 53.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117815.diff

7 Files Affected:

  • (modified) libc/fuzzing/__support/freelist_heap_fuzz.cpp (+3-3)
  • (modified) libc/src/__support/CMakeLists.txt (+1)
  • (modified) libc/src/__support/block.h (+127-124)
  • (modified) libc/src/__support/freelist_heap.h (+6-17)
  • (modified) libc/test/src/__support/block_test.cpp (+128-193)
  • (modified) libc/test/src/__support/freelist_heap_test.cpp (+40-66)
  • (modified) libc/test/src/__support/freelist_malloc_test.cpp (+8-13)
diff --git a/libc/fuzzing/__support/freelist_heap_fuzz.cpp b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
index d152d0f35499f8..d5764adcbb2642 100644
--- a/libc/fuzzing/__support/freelist_heap_fuzz.cpp
+++ b/libc/fuzzing/__support/freelist_heap_fuzz.cpp
@@ -100,13 +100,13 @@ optional<AllocType> choose<AllocType>(const uint8_t *&data, size_t &remainder) {
       *raw % static_cast<uint8_t>(AllocType::NUM_ALLOC_TYPES));
 }
 
-constexpr size_t heap_size = 64 * 1024;
+constexpr size_t HEAP_SIZE = 64 * 1024;
 
 optional<size_t> choose_size(const uint8_t *&data, size_t &remainder) {
   auto raw = choose<size_t>(data, remainder);
   if (!raw)
     return nullopt;
-  return *raw % heap_size;
+  return *raw % HEAP_SIZE;
 }
 
 optional<size_t> choose_alloc_idx(const AllocVec &allocs, const uint8_t *&data,
@@ -126,7 +126,7 @@ optional<size_t> choose_alloc_idx(const AllocVec &allocs, const uint8_t *&data,
   TYPE NAME = *maybe_##NAME
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t remainder) {
-  LIBC_NAMESPACE::FreeListHeapBuffer<heap_size> heap;
+  LIBC_NAMESPACE::FreeListHeapBuffer<HEAP_SIZE> heap;
   AllocVec allocs(heap);
 
   uint8_t canary = 0;
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 8f85740f70a06e..f11ea3e84af559 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -12,6 +12,7 @@ add_header_library(
     libc.src.__support.CPP.optional
     libc.src.__support.CPP.span
     libc.src.__support.CPP.type_traits
+    libc.src.__support.math_extras
 )
 
 add_object_library(
diff --git a/libc/src/__support/block.h b/libc/src/__support/block.h
index 9ca3f11530c4ba..4c51f26cf4776a 100644
--- a/libc/src/__support/block.h
+++ b/libc/src/__support/block.h
@@ -18,6 +18,7 @@
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/math_extras.h"
 
 #include <stdint.h>
 
@@ -49,8 +50,7 @@ LIBC_INLINE constexpr T *align_down(T *value, size_t alignment) {
 
 /// Returns the value rounded up to the nearest multiple of alignment.
 LIBC_INLINE constexpr size_t align_up(size_t value, size_t alignment) {
-  __builtin_add_overflow(value, alignment - 1, &value);
-  return align_down(value, alignment);
+  return align_down(value + alignment - 1, alignment);
 }
 
 /// Returns the value rounded up to the nearest multiple of alignment.
@@ -69,7 +69,9 @@ using cpp::optional;
 /// is also the block's size.
 ///
 /// Blocks will always be aligned to a `ALIGNMENT` boundary. Block sizes will
-/// always be rounded up to a multiple of `ALIGNMENT`.
+/// always be rounded up to a multiple of `ALIGNMENT`. All blocks have their
+/// usable space aligned to some multiple of max_align_t. This also implies that
+/// block outer sizes are aligned to max_align_t.
 ///
 /// As an example, the diagram below represents two contiguous `Block`s. The
 /// indices indicate byte offsets:
@@ -122,15 +124,16 @@ class Block {
   static constexpr size_t SIZE_MASK = ~(PREV_FREE_MASK | LAST_MASK);
 
 public:
-  static constexpr size_t ALIGNMENT = cpp::max(alignof(max_align_t), size_t{4});
+  static constexpr size_t ALIGNMENT = alignof(size_t);
   static const size_t BLOCK_OVERHEAD;
 
   // No copy or move.
   Block(const Block &other) = delete;
   Block &operator=(const Block &other) = delete;
 
-  /// Creates the first block for a given memory region, followed by a sentinel
-  /// last block. Returns the first block.
+  /// Initializes a given memory region into a first block and a sentinel last
+  /// block. Returns the first block, which has its usable space aligned to
+  /// max_align_t.
   static optional<Block *> init(ByteSpan region);
 
   /// @returns  A pointer to a `Block`, given a pointer to the start of the
@@ -186,6 +189,9 @@ class Block {
   }
 
   /// @returns A pointer to the usable space inside this block.
+  ///
+  /// Unless specifically requested otherwise, this will be aligned to
+  /// max_align_t.
   LIBC_INLINE cpp::byte *usable_space() {
     return reinterpret_cast<cpp::byte *>(this) + BLOCK_OVERHEAD;
   }
@@ -201,11 +207,13 @@ class Block {
   /// Attempts to split this block.
   ///
   /// If successful, the block will have an inner size of at least
-  /// `new_inner_size`, rounded to ensure that the split point is on an
-  /// ALIGNMENT boundary. The remaining space will be returned as a new block.
-  /// Note that the prev_ field of the next block counts as part of the inner
-  /// size of the returnd block.
-  optional<Block *> split(size_t new_inner_size);
+  /// `new_inner_size`. The remaining space will be returned as a new block,
+  /// with usable space aligned to `usable_space_alignment` and with the block
+  /// boundary aligned to ALIGNMENT. Note that the prev_ field of the next block
+  /// counts as part of the inner size of the block. `usable_space_alignment`
+  /// must be a multiple of max_align_t.
+  optional<Block *> split(size_t new_inner_size,
+                          size_t usable_space_alignment = alignof(max_align_t));
 
   /// Merges this block with the one that comes after it.
   bool merge_next();
@@ -256,38 +264,39 @@ class Block {
     return reinterpret_cast<uintptr_t>(usable_space()) % alignment == 0;
   }
 
-  /// @returns The new inner size of this block that would give the usable
-  /// space of the next block the given alignment.
-  LIBC_INLINE size_t padding_for_alignment(size_t alignment) const {
-    if (is_usable_space_aligned(alignment))
-      return 0;
-
-    // We need to ensure we can always split this block into a "padding" block
-    // and the aligned block. To do this, we need enough extra space for at
-    // least one block.
-    //
-    // |block   |usable_space                          |
-    // |........|......................................|
-    //                            ^
-    //                            Alignment requirement
-    //
+  // Returns the minimum inner size necessary for a block of that size to
+  // always be able to allocate at the given size and alignment.
+  //
+  // Returns 0 if there is no such size.
+  LIBC_INLINE static size_t min_size_for_allocation(size_t alignment,
+                                                    size_t size) {
+    LIBC_ASSERT(alignment >= alignof(max_align_t) &&
+                alignment % alignof(max_align_t) == 0 &&
+                "alignment must be multiple of max_align_t");
+
+    if (alignment == alignof(max_align_t))
+      return size;
+
+    // We must create a padding block to align the usable space of the next
+    // block. The next block's usable space can be found by advancing by
+    // BLOCK_OVERHEAD then aligning up. The amount advanced is the amount of
+    // additional inner size required.
     //
-    // |block   |space   |block   |usable_space        |
-    // |........|........|........|....................|
-    //                            ^
-    //                            Alignment requirement
-    //
-    alignment = cpp::max(alignment, ALIGNMENT);
-    uintptr_t start = reinterpret_cast<uintptr_t>(usable_space());
-    uintptr_t next_usable_space = align_up(start + BLOCK_OVERHEAD, alignment);
-    uintptr_t next_block = next_usable_space - BLOCK_OVERHEAD;
-    return next_block - start + sizeof(prev_);
+    // The minimum advancment is BLOCK_OVERHEAD, since the resulting position
+    // may happen to be aligned. What is the maximum? Advancing by
+    // BLOCK_OVERHEAD leaves the result still aligned to max_align_t. So the
+    // most additional advancement required would be if the point is exactly
+    // alignof(max_align_t) past alignment. The remaining size to the next
+    // alignment would then be alignment - alignof(max_align_t). So the total
+    // maximum advancement required is BLOCK_OVERHEAD + alignment -
+    // alignof(max_align_t).
+    if (add_overflow(size, BLOCK_OVERHEAD, size))
+      return 0;
+    if (add_overflow(size, alignment - alignof(max_align_t), size))
+      return 0;
+    return size;
   }
 
-  // Check that we can `allocate` a block with a given alignment and size from
-  // this existing block.
-  bool can_allocate(size_t alignment, size_t size) const;
-
   // This is the return type for `allocate` which can split one block into up to
   // three blocks.
   struct BlockInfo {
@@ -309,25 +318,23 @@ class Block {
     Block *next;
   };
 
-  // Divide a block into up to 3 blocks according to `BlockInfo`. This should
-  // only be called if `can_allocate` returns true.
+  // Divide a block into up to 3 blocks according to `BlockInfo`. Behavior is
+  // undefined if allocation is not possible for the given size and alignment.
   static BlockInfo allocate(Block *block, size_t alignment, size_t size);
 
 private:
   /// Construct a block to represent a span of bytes. Overwrites only enough
   /// memory for the block header; the rest of the span is left alone.
   LIBC_INLINE static Block *as_block(ByteSpan bytes) {
+    LIBC_ASSERT(reinterpret_cast<uintptr_t>(bytes.data()) % ALIGNMENT == 0 &&
+                "block start must be suitably aligned");
     return ::new (bytes.data()) Block(bytes.size());
   }
 
-  /// Like `split`, but assumes the caller has already checked to parameters to
-  /// ensure the split will succeed.
-  Block *split_impl(size_t new_inner_size);
-
   /// Offset from this block to the previous block. 0 if this is the first
   /// block. This field is only alive when the previous block is free;
   /// otherwise, its memory is reused as part of the previous block's usable
-  /// space.
+  /// space
   size_t prev_ = 0;
 
   /// Offset from this block to the next block. Valid even if this is the last
@@ -343,93 +350,78 @@ class Block {
   ///   previous block is free.
   /// * If the `last` flag is set, the block is the sentinel last block. It is
   ///   summarily considered used and has no next block.
-} __attribute__((packed, aligned(cpp::max(alignof(max_align_t), size_t{4}))));
-
-inline constexpr size_t Block::BLOCK_OVERHEAD =
-    align_up(sizeof(Block), ALIGNMENT);
-
-LIBC_INLINE ByteSpan get_aligned_subspan(ByteSpan bytes, size_t alignment) {
-  if (bytes.data() == nullptr)
-    return ByteSpan();
+};
 
-  auto unaligned_start = reinterpret_cast<uintptr_t>(bytes.data());
-  auto aligned_start = align_up(unaligned_start, alignment);
-  auto unaligned_end = unaligned_start + bytes.size();
-  auto aligned_end = align_down(unaligned_end, alignment);
+static_assert(alignof(max_align_t) > 4,
+              "the low two bits of block sizes must be free for use as flags");
 
-  if (aligned_end <= aligned_start)
-    return ByteSpan();
-
-  return bytes.subspan(aligned_start - unaligned_start,
-                       aligned_end - aligned_start);
-}
+inline constexpr size_t Block::BLOCK_OVERHEAD = sizeof(Block);
 
 LIBC_INLINE
 optional<Block *> Block::init(ByteSpan region) {
-  optional<ByteSpan> result = get_aligned_subspan(region, ALIGNMENT);
-  if (!result)
+  if (!region.data())
     return {};
 
-  region = result.value();
-  // Two blocks are allocated: a free block and a sentinel last block.
-  if (region.size() < 2 * BLOCK_OVERHEAD)
-    return {};
+  uintptr_t start = reinterpret_cast<uintptr_t>(region.data());
+  uintptr_t end = start + region.size();
+
+  // Find the earliest point after start that is aligned to max_align_t and has
+  // at least BLOCK_OVERHEAD before it.
+  uintptr_t usable_space =
+      align_up(start + BLOCK_OVERHEAD, alignof(max_align_t));
+
+  uintptr_t block_start = usable_space - BLOCK_OVERHEAD;
+  LIBC_ASSERT(block_start % ALIGNMENT == 0 && "block start must be aligned");
 
-  if (cpp::numeric_limits<size_t>::max() < region.size())
+  // A last block must be able to follow the first block.
+  if (usable_space + align_up(BLOCK_OVERHEAD, alignof(max_align_t)) > end)
     return {};
 
-  Block *block = as_block(region.first(region.size() - BLOCK_OVERHEAD));
-  Block *last = as_block(region.last(BLOCK_OVERHEAD));
+  // Given that there is room, the last block starts BLOCK_OVERHEAD
+  // before the last aligned point in the region.
+  uintptr_t last_start = align_down(end, alignof(max_align_t)) - BLOCK_OVERHEAD;
+  LIBC_ASSERT(last_start % ALIGNMENT == 0 &&
+              "last block start must be aligned");
+
+  auto *block_start_ptr = reinterpret_cast<cpp::byte *>(block_start);
+  auto *last_start_ptr = reinterpret_cast<cpp::byte *>(last_start);
+
+  Block *block = as_block({block_start_ptr, last_start_ptr});
+  LIBC_ASSERT(block->is_usable_space_aligned(alignof(max_align_t)) &&
+              "newly initialized block should be aligned to max_align_t");
+  Block *last = as_block({last_start_ptr, BLOCK_OVERHEAD});
+  LIBC_ASSERT(last->is_usable_space_aligned(alignof(max_align_t)) &&
+              "last block should be aligned to max_align_t");
   block->mark_free();
   last->mark_last();
+  LIBC_ASSERT(block->outer_size() % alignof(max_align_t) == 0 &&
+              "outer sizes should be aligned to max_align_t");
   return block;
 }
 
-LIBC_INLINE
-bool Block::can_allocate(size_t alignment, size_t size) const {
-  if (inner_size() < size)
-    return false;
-  if (is_usable_space_aligned(alignment))
-    return true;
-
-  // Alignment isn't met, so a padding block is needed. Determine amount of
-  // inner_size() consumed by the padding block.
-  size_t padding_size = padding_for_alignment(alignment) - sizeof(prev_);
-
-  // Check that there is room for the allocation in the following aligned block.
-  size_t aligned_inner_size = inner_size() - padding_size - BLOCK_OVERHEAD;
-  return size <= aligned_inner_size;
-}
-
 LIBC_INLINE
 Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
-  LIBC_ASSERT(
-      block->can_allocate(alignment, size) &&
-      "Calls to this function for a given alignment and size should only be "
-      "done if `can_allocate` for these parameters returns true.");
+  LIBC_ASSERT(alignment % alignof(max_align_t) == 0 &&
+              "alignment must be a multiple of max_align_t");
 
   BlockInfo info{block, /*prev=*/nullptr, /*next=*/nullptr};
 
   if (!info.block->is_usable_space_aligned(alignment)) {
     Block *original = info.block;
-    optional<Block *> maybe_aligned_block =
-        original->split(info.block->padding_for_alignment(alignment));
+    // The padding block has no minimum size requirement.
+    optional<Block *> maybe_aligned_block = original->split(0, alignment);
     LIBC_ASSERT(maybe_aligned_block.has_value() &&
-                "This split should always result in a new block. The check in "
-                "`can_allocate` ensures that we have enough space here to make "
-                "two blocks.");
+                "it should always be possible to split for alignment");
 
     if (Block *prev = original->prev_free()) {
-      // If there is a free block before this, we can merge the current one with
-      // the newly created one.
+      // If there is a free block before this, we can merge the current one
+      // with the newly created one.
       prev->merge_next();
     } else {
       info.prev = original;
     }
 
     Block *aligned_block = *maybe_aligned_block;
-    LIBC_ASSERT(aligned_block->is_usable_space_aligned(alignment) &&
-                "The aligned block isn't aligned somehow.");
     info.block = aligned_block;
   }
 
@@ -441,33 +433,44 @@ Block::BlockInfo Block::allocate(Block *block, size_t alignment, size_t size) {
 }
 
 LIBC_INLINE
-optional<Block *> Block::split(size_t new_inner_size) {
+optional<Block *> Block::split(size_t new_inner_size,
+                               size_t usable_space_alignment) {
   if (used())
     return {};
-  // The prev_ field of the next block is always available, so there is a
-  // minimum size to a block created through splitting.
-  if (new_inner_size < sizeof(prev_))
-    new_inner_size = sizeof(prev_);
-
-  size_t old_inner_size = inner_size();
-  new_inner_size =
-      align_up(new_inner_size - sizeof(prev_), ALIGNMENT) + sizeof(prev_);
-  if (old_inner_size < new_inner_size)
-    return {};
 
-  if (old_inner_size - new_inner_size < BLOCK_OVERHEAD)
-    return {};
+  LIBC_ASSERT(usable_space_alignment % alignof(max_align_t) == 0 &&
+              "alignment must be a multiple of max_align_t");
 
-  return split_impl(new_inner_size);
-}
+  size_t old_outer_size = outer_size();
 
-LIBC_INLINE
-Block *Block::split_impl(size_t new_inner_size) {
-  size_t outer_size1 = outer_size(new_inner_size);
-  LIBC_ASSERT(outer_size1 % ALIGNMENT == 0 && "new size must be aligned");
-  ByteSpan new_region = region().subspan(outer_size1);
+  // Compute the minimum outer size that produces a block of at least
+  // new_inner_size.
+  new_inner_size = cpp::max(new_inner_size, sizeof(prev_));
+  size_t new_outer_size = outer_size(new_inner_size);
+
+  // Compute the minimum start of the next block's usable space.
+  uintptr_t start = reinterpret_cast<uintptr_t>(this);
+  uintptr_t next_usable_space = start + new_outer_size + BLOCK_OVERHEAD;
+
+  // Align up the next block's usable space to the requested alignment.
+  next_usable_space = align_up(next_usable_space, usable_space_alignment);
+
+  // Find the starting point of the next block.
+  uintptr_t next_block_start = next_usable_space - BLOCK_OVERHEAD;
+  LIBC_ASSERT(next_block_start % ALIGNMENT == 0 &&
+              "next block must be suitably aligned");
+
+  new_outer_size = next_block_start - start;
+  LIBC_ASSERT(new_outer_size % alignof(max_align_t) == 0 &&
+              "new size must be aligned to max_align_t");
+
+  if (old_outer_size < new_outer_size ||
+      old_outer_size - new_outer_size < BLOCK_OVERHEAD)
+    return {};
+
+  ByteSpan new_region = region().subspan(new_outer_size);
   next_ &= ~SIZE_MASK;
-  next_ |= outer_size1;
+  next_ |= new_outer_size;
 
   Block *new_block = as_block(new_region);
   mark_free(); // Free status for this block is now stored in new_block.
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index 8fa36257cb91ae..d58685194aeb81 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -89,28 +89,14 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
   if (!is_initialized)
     init();
 
-  size_t request_size = size;
-
-  // TODO: usable_space should always be aligned to max_align_t.
-  if (alignment > alignof(max_align_t) ||
-      (Block::BLOCK_OVERHEAD % alignof(max_align_t) != 0)) {
-    // TODO: This bound isn't precisely calculated yet. It assumes one extra
-    // Block::ALIGNMENT to accomodate the possibility for padding block
-    // overhead. (alignment - 1) ensures that there is an aligned point
-    // somewhere in usable_space, but this isn't tight either, since
-    // usable_space is also already somewhat aligned.
-    if (add_overflow(size, (alignment - 1) + Block::ALIGNMENT, request_size))
-      return nullptr;
-  }
+  size_t request_size = Block::min_size_for_allocation(alignment, size);
+  if (!request_size)
+    return nullptr;
 
   Block *block = free_store.remove_best_fit(request_size);
   if (!block)
     return nullptr;
 
-  LIBC_ASSERT(block->can_allocate(alignment, size) &&
-              "block should always be large enough to allocate at the correct "
-              "alignment");
-
   auto block_info = Block::allocate(block, alignment, size);
   if (block_info.next)
     free_store.insert(block_info.next);
@@ -135,6 +121,9 @@ LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
   if (size % alignment != 0)
     return nullptr;
 
+  // The minimum alignment supported by Block is max_align_t.
+  alignment = cpp::max(alignment, alignof(max_align_t));
+
   return allocate_impl(alignment, size);
 }
 
diff --git a/libc/test/src/__support/block_test.cpp b/libc/test/src/__support/block_test.cpp
index 5e437db51b6092..15a33c4fe3c967 100644
--- a/libc/test/src/__support/block_test.cpp
+++ b/libc/test/src/__support/block_test.cpp
@@ -21,37 +21,47 @@ using LIBC_NAMESPACE::cpp::byte;
 using LIBC_NAMESPACE::cpp::span;
 
 TEST(LlvmLibcBlockTest, CanCreateSingleAlignedBlock) {
-  constexpr size_t kN = 1024;
-  alignas(Block::ALIGNMENT) array<byte, kN> bytes;
+  alignas(Block::ALIGNMENT) array<byte, 1024> bytes;
 
   auto result = Block::init(bytes);
   ASSERT_TRUE(result.has_value());
   Block *block = *result;
 
+  EXPECT_EQ(reinterpret_cas...
[truncated]

@mysterymath mysterymath force-pushed the libc-malloc-block branch 8 times, most recently from 996928e to 4702385 Compare November 27, 2024 20:23
Aligning blocks to max_align_t is neither necessary nor sufficient to
ensure that the usable_space() is so aligned. Instead, we make this an
invariant of Block and maintain it in init() and split().

This allows targets like riscv32 with small pointers and large
max_align_t to maintain the property that all available blocks are
aligned for malloc().
@mysterymath
Copy link
Contributor Author

This patch had a bit too much [NFC] stuff folded into it, so I stripped out that out to hopefully make it easier to review. PTAL!

Copy link
Contributor

@PiJoules PiJoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For each of the comments that mention returning something aligned to max_align_t, could you add a LIBC_ASSERT for the return value to ensure this?

Comment on lines +133 to +135
/// Initializes a given memory region into a first block and a sentinel last
/// block. Returns the first block, which has its usable space aligned to
/// max_align_t.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a LIBC_ASSERT to the end of init that checks the alignment?

Copy link
Contributor Author

@mysterymath mysterymath Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an invariant for all Blocks, so I've checked for this in the block constructor. init necessarily calls out to that constructor, so that should be sufficient to establish it for init.

Comment on lines 192 to 193
/// Unless specifically requested otherwise, this will be aligned to
/// max_align_t.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add an assert that checks the returned alignment here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above; establishing these in the constructor should allow us to avoid repeating the checks throughout the implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm; see below. Added.

Copy link
Contributor

@PiJoules PiJoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of a large nit, but in a separate NFC patch, could you also introduce something like typdef size_t prev_field_t and replace all the hardcoded sizeof(size_t)s with sizeof(prev_field_t)? This way it's a little more informative on what the individual sizeof(size_t)s are which I assume pertain to the prev_field.

@mysterymath
Copy link
Contributor Author

mysterymath commented Jan 14, 2025

Kind of a large nit, but in a separate NFC patch, could you also introduce something like typdef size_t prev_field_t and replace all the hardcoded sizeof(size_t)s with sizeof(prev_field_t)? This way it's a little more informative on what the individual sizeof(size_t)s are which I assume pertain to the prev_field.

Yeah, I think that's a good idea, although I'd probably break out a Block::prev_field_size rather than making a new type. I'll add it to my NFC patch chain that will follow this one.

Copy link
Contributor

@PiJoules PiJoules left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are ALIGNMENT still needed after this patch? It looks like we unconditionally align up to multiples of alignof(max_align_t).

@mysterymath
Copy link
Contributor Author

Are ALIGNMENT still needed after this patch? It looks like we unconditionally align up to multiples of alignof(max_align_t).

It's not, but removing it was one of the things that muddied this patch. I'll go through and strip it out in a follow-up NFC patch.

@mysterymath
Copy link
Contributor Author

For each of the comments that mention returning something aligned to max_align_t, could you add a LIBC_ASSERT for the return value to ensure this?

I've added this to the constructor and mutators, since there are fewer of these than places that expose the alignment invariant, and those are the only places it could break. I had missed an assertion in ::split.

@mysterymath
Copy link
Contributor Author

For each of the comments that mention returning something aligned to max_align_t, could you add a LIBC_ASSERT for the return value to ensure this?

I've added this to the constructor and mutators, since there are fewer of these than places that expose the alignment invariant, and those are the only places it could break. I had missed an assertion in ::split.

Actually now that I think of it, the fact that this was missed means that it's probably a good idea to check in the accessors too. I'll add that.

Comment on lines 290 to 296
// The minimum advancment is sizeof(Block), since the resulting position may
// happen to be aligned. What is the maximum? Advancing by sizeof(Block)
// leaves the result still aligned to alignof(Block). So the most additional
// advancement required would be if the point is exactly alignof(Block) past
// alignment. The remaining size to the next alignment would then be
// alignment - alignof(Block). So the total maximum advancement required is
// sizeof(Block) + alignment - alignof(Block).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had some trouble thinking about why the max is calculated this way but @mysterymath explained offline and I'm going to write down the explanation which can hopefully be added here to expand on the max calculation.

When picking a new size, we need to ensure we can always pick a point inside that block that is aligned to alignment. We also need to make sure that advancing by the requested size will ensure the address at the end of the new size is Block-aligned (so we can always allocate new Blocks after it).

Assuming the alignment is just one max_align_t, we can always return the size size the new inner size would always be aligned. Otherwise, we need to advance by a little extra space.

The minimum additional advancement is sizeof(Block), since the resulting position may happen to be aligned. This space is also needed just to hold the next block this can be partitioned into.

What is the maximum? We know that requesting an additional size by `alignment` will always ensure there's a point in the new allocation that will be aligned to `alignment`, so we can add that to our new size. However, let's say our new usable_space would already be aligned to `alignment`, then advancing by `alignment` would not be necessary since the usable_space would already be aligned. If instead the usable_space were one byte after an alignment point, then we'd only need to advance the size by at most `alignment - 1` byte to find a point in the new block that can be aligned to `alignment`. Over the contiguous memory region Blocks operate under, any Block split always ensures partitions are at minimum Block-aligned. Therefore, the most we need to advance is actually `alignment - alignof(Block)`. This way, even if advancing by `size + sizeof(Block)` happens to be aligned to `alignment`, we don't need to request the full `alignment`. Leaving out the `alignof(Block)` also ensures the partition after this one will always be Block-aligned.

Feel free to correct/expand on anything I got wrong with my understanding here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've rewritten this to be more explicit about the goals and mechanisms in light of our discussion.

static BlockInfo allocate(Block *block, size_t alignment, size_t size);

LIBC_INLINE static uintptr_t next_possible_block_start(
uintptr_t ptr, size_t usable_space_alignment = alignof(max_align_t)) {
return align_up(ptr + sizeof(Block), usable_space_alignment) -
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be overthinking here, but is it possible for ptr + sizeof(Block) causing next_possible_block_start to also overflow? I imagine it's very unlikely, but I'm thinking it's possible through Block::split if min_outer_size is large enough there(?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to disregard this since this is probably me nitpicking on potential overflows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These conveniently return the correct answer in modulo arithmetic, so I modified their uses to detect overflow and react accordingly.

// Ensure we can allocate everything up to the block size within this block.
for (size_t i = 0; i < kN - 2 * Block::BLOCK_OVERHEAD; ++i) {
alignas(Block::ALIGNMENT) array<byte, kN> bytes{};
for (size_t i = 0; i < 1024; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could you keep the 1024 as a variable to indicate this is the buffer size rather than have magic numbers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@mysterymath mysterymath merged commit 60de7dc into llvm:main Jan 16, 2025
11 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 16, 2025

LLVM Buildbot has detected a new failure on builder libc-riscv32-qemu-yocto-fullbuild-dbg running on rv32gc-qemu-system while building libc at step 4 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/196/builds/3664

Here is the relevant piece of the build log for the reference
Step 4 (annotate) failure: 'python ../llvm-zorg/zorg/buildbot/builders/annotated/libc-linux.py ...' (failure)
...
  103 |   for (int i = 0; i < N; i++) {
      |                   ~ ^ ~
/home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/llvm-project/libc/test/src/__support/freelist_heap_test.cpp:249:20: warning: unused variable 'BUFFER_SIZE' [-Wunused-variable]
  249 |   constexpr size_t BUFFER_SIZE = 4096;
      |                    ^~~~~~~~~~~
2 warnings generated.
[11/952] Building CXX object libc/test/src/__support/CMakeFiles/libc.test.src.__support.block_test.__unit__.__build__.dir/block_test.cpp.o
[12/952] Linking CXX executable libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__
[13/952] Linking CXX executable libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__
[14/952] Running unit test libc.test.src.__support.freestore_test.__unit__
FAILED: libc/test/src/__support/CMakeFiles/libc.test.src.__support.freestore_test.__unit__ /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support/CMakeFiles/libc.test.src.__support.freestore_test.__unit__ 
cd /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support && /home/libcrv32buildbot/cross.sh /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19136: Permission denied
[==========] Running 3 tests from 1 test suite.
[ RUN      ] LlvmLibcFreeStore.TooSmall
/home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/llvm-project/libc/src/__support/block.h:188: Assertion failed: 'reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 && "usable space must be aligned to a multiple of max_align_t"' in function: 'const cpp::byte *__llvm_libc_20_0_0_git::Block::usable_space() const'
[15/952] Running unit test libc.test.src.__support.CPP.mutex_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19117: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcMutexTest.Basic
[       OK ] LlvmLibcMutexTest.Basic (572 us)
[ RUN      ] LlvmLibcMutexTest.AcquireLocked
[       OK ] LlvmLibcMutexTest.AcquireLocked (461 us)
Ran 2 tests.  PASS: 2  FAIL: 0
[16/952] Running unit test libc.test.src.__support.CPP.algorithm_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19119: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcAlgorithmTest.FindIfNot
[       OK ] LlvmLibcAlgorithmTest.FindIfNot (969 us)
[ RUN      ] LlvmLibcAlgorithmTest.AllOf
[       OK ] LlvmLibcAlgorithmTest.AllOf (653 us)
Ran 2 tests.  PASS: 2  FAIL: 0
[17/952] Running unit test libc.test.src.__support.CPP.limits_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19155: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcLimitsTest.LimitsFollowSpec
Step 8 (libc-unit-tests) failure: libc-unit-tests (failure)
...
  103 |   for (int i = 0; i < N; i++) {
      |                   ~ ^ ~
/home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/llvm-project/libc/test/src/__support/freelist_heap_test.cpp:249:20: warning: unused variable 'BUFFER_SIZE' [-Wunused-variable]
  249 |   constexpr size_t BUFFER_SIZE = 4096;
      |                    ^~~~~~~~~~~
2 warnings generated.
[11/952] Building CXX object libc/test/src/__support/CMakeFiles/libc.test.src.__support.block_test.__unit__.__build__.dir/block_test.cpp.o
[12/952] Linking CXX executable libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__
[13/952] Linking CXX executable libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__
[14/952] Running unit test libc.test.src.__support.freestore_test.__unit__
FAILED: libc/test/src/__support/CMakeFiles/libc.test.src.__support.freestore_test.__unit__ /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support/CMakeFiles/libc.test.src.__support.freestore_test.__unit__ 
cd /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support && /home/libcrv32buildbot/cross.sh /home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/build/libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19136: Permission denied
[==========] Running 3 tests from 1 test suite.
[ RUN      ] LlvmLibcFreeStore.TooSmall
/home/libcrv32buildbot/bbroot/libc-riscv32-qemu-yocto-fullbuild-dbg/llvm-project/libc/src/__support/block.h:188: Assertion failed: 'reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 && "usable space must be aligned to a multiple of max_align_t"' in function: 'const cpp::byte *__llvm_libc_20_0_0_git::Block::usable_space() const'
[15/952] Running unit test libc.test.src.__support.CPP.mutex_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19117: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcMutexTest.Basic
[       OK ] LlvmLibcMutexTest.Basic (572 us)
[ RUN      ] LlvmLibcMutexTest.AcquireLocked
[       OK ] LlvmLibcMutexTest.AcquireLocked (461 us)
Ran 2 tests.  PASS: 2  FAIL: 0
[16/952] Running unit test libc.test.src.__support.CPP.algorithm_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19119: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcAlgorithmTest.FindIfNot
[       OK ] LlvmLibcAlgorithmTest.FindIfNot (969 us)
[ RUN      ] LlvmLibcAlgorithmTest.AllOf
[       OK ] LlvmLibcAlgorithmTest.AllOf (653 us)
Ran 2 tests.  PASS: 2  FAIL: 0
[17/952] Running unit test libc.test.src.__support.CPP.limits_test.__unit__
WARNING: libc/test/src/__support/libc.test.src.__support.block_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_heap_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freelist_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freestore_test.__unit__.__build__ failed verification -- update discarded (will try again).
WARNING: libc/test/src/__support/libc.test.src.__support.freetrie_test.__unit__.__build__ failed verification -- update discarded (will try again).
sh: line 1: /timer.19155: Permission denied
[==========] Running 2 tests from 1 test suite.
[ RUN      ] LlvmLibcLimitsTest.LimitsFollowSpec

@llvm-ci
Copy link
Collaborator

llvm-ci commented Jan 16, 2025

LLVM Buildbot has detected a new failure on builder openmp-offload-libc-amdgpu-runtime running on omp-vega20-1 while building libc at step 7 "Add check check-offload".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/11847

Here is the relevant piece of the build log for the reference
Step 7 (Add check check-offload) failure: test (failure)
******************** TEST 'libomptarget :: amdgcn-amd-amdhsa :: offloading/pgo1.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/Output/pgo1.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-instr-generate      -Xclang "-fprofile-instrument=clang"
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/Output/pgo1.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-instr-generate -Xclang -fprofile-instrument=clang
# RUN: at line 3
/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/Output/pgo1.c.tmp 2>&1 | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c      --check-prefix="CLANG-PGO"
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/Output/pgo1.c.tmp
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c --check-prefix=CLANG-PGO
# .---command stderr------------
# | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c:32:20: error: CLANG-PGO-NEXT: expected string not found in input
# | // CLANG-PGO-NEXT: [ 0 11 20 ]
# |                    ^
# | <stdin>:3:28: note: scanning from here
# | ======== Counters =========
# |                            ^
# | <stdin>:4:1: note: possible intended match here
# | [ 0 14 20 ]
# | ^
# | 
# | Input file: <stdin>
# | Check file: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/pgo1.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |            1: ======= GPU Profile ======= 
# |            2: Target: amdgcn-amd-amdhsa 
# |            3: ======== Counters ========= 
# | next:32'0                                X error: no match found
# |            4: [ 0 14 20 ] 
# | next:32'0     ~~~~~~~~~~~~
# | next:32'1     ?            possible intended match
# |            5: [ 10 ] 
# | next:32'0     ~~~~~~~
# |            6: [ 20 ] 
# | next:32'0     ~~~~~~~
# |            7: ========== Data =========== 
# | next:32'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            8: { 9515997471539760012 4749112401 0xffffffffffffffd8 0x0 0x0 0x0 3 [...] 0 } 
# | next:32'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            9: { 3666282617048535130 24 0xffffffffffffffb0 0x0 0x0 0x0 1 [...] 0 } 
# | next:32'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            .
# |            .
# |            .
...

Copy link
Member

@nickdesaulniers nickdesaulniers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mysterymath , the 32b riscv buidbot is failing at this PR with the two errors. Can you PTAL?

// if the underlying buffer is unaligned. This is so we can check that we can
// still get aligned allocations even if the underlying buffer is not aligned to
// the alignments we request.
TEST(LlvmLibcFreeListHeap, AlignedAllocUnalignedBuffer) {
constexpr size_t BUFFER_SIZE = 4096;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm-project/libc/test/src/__support/freelist_heap_test.cpp:249:20: warning: unused variable 'BUFFER_SIZE' [-Wunused-variable]
  249 |   constexpr size_t BUFFER_SIZE = 4096;
      |                    ^~~~~~~~~~~

return reinterpret_cast<const cpp::byte *>(this) + BLOCK_OVERHEAD;
const auto *s = reinterpret_cast<const cpp::byte *>(this) + BLOCK_OVERHEAD;
LIBC_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 &&
"usable space must be aligned to a multiple of max_align_t");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm-project/libc/src/__support/block.h:188: Assertion failed: 'reinterpret_cast<uintptr_t>(s) % alignof(max_align_t) == 0 && "usable space must be aligned to a multiple of max_align_t"' in function: 'const cpp::byte *__llvm_libc_20_0_0_git::Block::usable_space() const'

@mysterymath
Copy link
Contributor Author

mysterymath commented Jan 17, 2025

Hi @mysterymath , the 32b riscv buidbot is failing at this PR with the two errors. Can you PTAL?

Sorry about this; I was able to fix forward:

https://lab.llvm.org/buildbot/#/builders/196/builds/3750

4-byte pointers and 16-byte max_align_t has been a stress-test for Block and its tests; I'm both grateful and vaguely horrified at such a large alignment on such a small platform.

@nickdesaulniers
Copy link
Member

Sorry about this; I was able to fix forward:

Thx!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants