[libc-commits] [libc] [libc] Restructure freelist heap free store (PR #200710)

via libc-commits libc-commits at lists.llvm.org
Sun May 31 21:30:01 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Schrodinger ZHU Yifan (SchrodingerZhu)

<details>
<summary>Changes</summary>

Summary:
- Move the trie-backed free store into freetrie.h as TrieFreeStore.
- Rename remove_best_fit to find_and_remove_fit.
- Remove the separate freestore.h wrapper and update tests/build deps.

Testing:
- ninja -C /tmp/llvm-libc-tlsf-build libc.test.src.__support.tlsf_freestore_test.__unit__ libc.test.src.__support.triefreestore_test.__unit__ libc.test.src.__support.freetrie_test.__unit__ libc.test.src.__support.freelist_test.__unit__

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


6 Files Affected:

- (modified) libc/src/__support/CMakeLists.txt (+1-9) 
- (modified) libc/src/__support/freelist_heap.h (+9-4) 
- (removed) libc/src/__support/freestore.h (-113) 
- (modified) libc/src/__support/freetrie.h (+102-1) 
- (modified) libc/test/src/__support/CMakeLists.txt (+2-3) 
- (renamed) libc/test/src/__support/triefreestore_test.cpp (+24-16) 


``````````diff
diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index ada489046ef9e..89c4379381956 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -49,14 +49,7 @@ add_object_library(
   DEPENDS
     .block
     .freelist
-)
-
-add_header_library(
-  freestore
-  HDRS
-    freestore.h
-  DEPENDS
-    .freetrie
+    libc.src.__support.CPP.array
 )
 
 libc_set_definition(libc_freelist_malloc_size "LIBC_FREELIST_MALLOC_SIZE=${LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE}")
@@ -71,7 +64,6 @@ add_object_library(
   DEPENDS
     .block
     .freelist
-    .freestore
     .freetrie
     libc.src.__support.CPP.cstddef
     libc.src.__support.CPP.array
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index 66f3739efe214..bc992dd71da6d 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -1,10 +1,15 @@
-//===-- Interface for freelist_heap ---------------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation header for freelist_heap.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
 #define LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
@@ -12,7 +17,7 @@
 #include <stddef.h>
 
 #include "block.h"
-#include "freestore.h"
+#include "freetrie.h"
 #include "src/__support/CPP/optional.h"
 #include "src/__support/CPP/span.h"
 #include "src/__support/libc_assert.h"
@@ -62,7 +67,7 @@ class FreeListHeap {
   cpp::byte *begin;
   cpp::byte *end;
   bool is_initialized = false;
-  FreeStore free_store;
+  TrieFreeStore free_store;
 };
 
 template <size_t BUFF_SIZE> class FreeListHeapBuffer : public FreeListHeap {
@@ -93,7 +98,7 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
   if (!request_size)
     return nullptr;
 
-  Block *block = free_store.remove_best_fit(request_size);
+  Block *block = free_store.find_and_remove_fit(request_size);
   if (!block)
     return nullptr;
 
diff --git a/libc/src/__support/freestore.h b/libc/src/__support/freestore.h
deleted file mode 100644
index 2dcb4b10b93d5..0000000000000
--- a/libc/src/__support/freestore.h
+++ /dev/null
@@ -1,113 +0,0 @@
-//===-- Interface for freestore ------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
-#define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
-
-#include "freetrie.h"
-
-namespace LIBC_NAMESPACE_DECL {
-
-/// A best-fit store of variously-sized free blocks. Blocks can be inserted and
-/// removed in logarithmic time.
-class FreeStore {
-public:
-  FreeStore() = default;
-  FreeStore(const FreeStore &other) = delete;
-  FreeStore &operator=(const FreeStore &other) = delete;
-
-  /// Sets the range of possible block sizes. This can only be called when the
-  /// trie is empty.
-  LIBC_INLINE void set_range(FreeTrie::SizeRange range) {
-    large_trie.set_range(range);
-  }
-
-  /// Insert a free block. If the block is too small to be tracked, nothing
-  /// happens.
-  void insert(Block *block);
-
-  /// Remove a free block. If the block is too small to be tracked, nothing
-  /// happens.
-  void remove(Block *block);
-
-  /// Remove a best-fit free block that can contain the given size when
-  /// allocated. Returns nullptr if there is no such block.
-  Block *remove_best_fit(size_t size);
-
-private:
-  static constexpr size_t MIN_OUTER_SIZE =
-      align_up(sizeof(Block) + sizeof(FreeList::Node), Block::MIN_ALIGN);
-  static constexpr size_t MIN_LARGE_OUTER_SIZE =
-      align_up(sizeof(Block) + sizeof(FreeTrie::Node), Block::MIN_ALIGN);
-  static constexpr size_t NUM_SMALL_SIZES =
-      (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / Block::MIN_ALIGN;
-
-  LIBC_INLINE static bool too_small(Block *block) {
-    return block->outer_size() < MIN_OUTER_SIZE;
-  }
-  LIBC_INLINE static bool is_small(Block *block) {
-    return block->outer_size() < MIN_LARGE_OUTER_SIZE;
-  }
-
-  FreeList &small_list(Block *block);
-  FreeList *find_best_small_fit(size_t size);
-
-  cpp::array<FreeList, NUM_SMALL_SIZES> small_lists;
-  FreeTrie large_trie;
-};
-
-LIBC_INLINE void FreeStore::insert(Block *block) {
-  if (too_small(block))
-    return;
-  if (is_small(block))
-    small_list(block).push(block);
-  else
-    large_trie.push(block);
-}
-
-LIBC_INLINE void FreeStore::remove(Block *block) {
-  if (too_small(block))
-    return;
-  if (is_small(block)) {
-    small_list(block).remove(
-        reinterpret_cast<FreeList::Node *>(block->usable_space()));
-  } else {
-    large_trie.remove(
-        reinterpret_cast<FreeTrie::Node *>(block->usable_space()));
-  }
-}
-
-LIBC_INLINE Block *FreeStore::remove_best_fit(size_t size) {
-  if (FreeList *list = find_best_small_fit(size)) {
-    Block *block = list->front();
-    list->pop();
-    return block;
-  }
-  if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) {
-    Block *block = best_fit->block();
-    large_trie.remove(best_fit);
-    return block;
-  }
-  return nullptr;
-}
-
-LIBC_INLINE FreeList &FreeStore::small_list(Block *block) {
-  LIBC_ASSERT(is_small(block) && "only legal for small blocks");
-  return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / Block::MIN_ALIGN];
-}
-
-LIBC_INLINE FreeList *FreeStore::find_best_small_fit(size_t size) {
-  for (FreeList &list : small_lists)
-    if (!list.empty() && list.size() >= size)
-      return &list;
-  return nullptr;
-}
-
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
diff --git a/libc/src/__support/freetrie.h b/libc/src/__support/freetrie.h
index c1a8306c6f8d2..ce8b80d280ae3 100644
--- a/libc/src/__support/freetrie.h
+++ b/libc/src/__support/freetrie.h
@@ -1,15 +1,21 @@
-//===-- Interface for freetrie --------------------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of FreeTrie and TrieFreeStore.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
 #define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
 
 #include "freelist.h"
+#include "src/__support/CPP/array.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -117,6 +123,54 @@ class FreeTrie {
   SizeRange range;
 };
 
+/// A best-fit store of variously-sized free blocks. Blocks can be inserted and
+/// removed in logarithmic time.
+class TrieFreeStore {
+public:
+  TrieFreeStore() = default;
+  TrieFreeStore(const TrieFreeStore &other) = delete;
+  TrieFreeStore &operator=(const TrieFreeStore &other) = delete;
+
+  /// Sets the range of possible block sizes. This can only be called when the
+  /// trie is empty.
+  LIBC_INLINE void set_range(FreeTrie::SizeRange range) {
+    large_trie.set_range(range);
+  }
+
+  /// Insert a free block. If the block is too small to be tracked, nothing
+  /// happens.
+  void insert(Block *block);
+
+  /// Remove a free block. If the block is too small to be tracked, nothing
+  /// happens.
+  void remove(Block *block);
+
+  /// Remove a best-fit free block that can contain the given size when
+  /// allocated. Returns nullptr if there is no such block.
+  Block *find_and_remove_fit(size_t size);
+
+private:
+  static constexpr size_t MIN_OUTER_SIZE =
+      align_up(sizeof(Block) + sizeof(FreeList::Node), Block::MIN_ALIGN);
+  static constexpr size_t MIN_LARGE_OUTER_SIZE =
+      align_up(sizeof(Block) + sizeof(FreeTrie::Node), Block::MIN_ALIGN);
+  static constexpr size_t NUM_SMALL_SIZES =
+      (MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / Block::MIN_ALIGN;
+
+  LIBC_INLINE static bool too_small(Block *block) {
+    return block->outer_size() < MIN_OUTER_SIZE;
+  }
+  LIBC_INLINE static bool is_small(Block *block) {
+    return block->outer_size() < MIN_LARGE_OUTER_SIZE;
+  }
+
+  FreeList &small_list(Block *block);
+  FreeList *find_best_small_fit(size_t size);
+
+  cpp::array<FreeList, NUM_SMALL_SIZES> small_lists;
+  FreeTrie large_trie;
+};
+
 LIBC_INLINE void FreeTrie::push(Block *block) {
   LIBC_ASSERT(block->inner_size_free() >= sizeof(Node) &&
               "block too small to accomodate free trie node");
@@ -232,6 +286,53 @@ LIBC_INLINE FreeTrie::Node *FreeTrie::find_best_fit(size_t size) {
   }
 }
 
+LIBC_INLINE void TrieFreeStore::insert(Block *block) {
+  if (too_small(block))
+    return;
+  if (is_small(block))
+    small_list(block).push(block);
+  else
+    large_trie.push(block);
+}
+
+LIBC_INLINE void TrieFreeStore::remove(Block *block) {
+  if (too_small(block))
+    return;
+  if (is_small(block)) {
+    small_list(block).remove(
+        reinterpret_cast<FreeList::Node *>(block->usable_space()));
+  } else {
+    large_trie.remove(
+        reinterpret_cast<FreeTrie::Node *>(block->usable_space()));
+  }
+}
+
+LIBC_INLINE Block *TrieFreeStore::find_and_remove_fit(size_t size) {
+  if (FreeList *list = find_best_small_fit(size)) {
+    Block *block = list->front();
+    list->pop();
+    return block;
+  }
+  if (FreeTrie::Node *best_fit = large_trie.find_best_fit(size)) {
+    Block *block = best_fit->block();
+    large_trie.remove(best_fit);
+    return block;
+  }
+  return nullptr;
+}
+
+LIBC_INLINE FreeList &TrieFreeStore::small_list(Block *block) {
+  LIBC_ASSERT(is_small(block) && "only legal for small blocks");
+  return small_lists[(block->outer_size() - MIN_OUTER_SIZE) / Block::MIN_ALIGN];
+}
+
+LIBC_INLINE FreeList *TrieFreeStore::find_best_small_fit(size_t size) {
+  for (FreeList &list : small_lists)
+    if (!list.empty() && list.size() >= size)
+      return &list;
+  return nullptr;
+}
+
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt
index 6b9c1b4ac8cc7..042fdf3eba40c 100644
--- a/libc/test/src/__support/CMakeLists.txt
+++ b/libc/test/src/__support/CMakeLists.txt
@@ -41,16 +41,15 @@ if(NOT LIBC_TARGET_OS_IS_GPU)
   )
 
   add_libc_test(
-    freestore_test
+    triefreestore_test
     SUITE
       libc-support-tests
     SRCS
-      freestore_test.cpp
+      triefreestore_test.cpp
     DEPENDS
       libc.src.__support.CPP.optional
       libc.src.__support.block
       libc.src.__support.freelist
-      libc.src.__support.freestore
       libc.src.__support.freetrie
   )
 endif()
diff --git a/libc/test/src/__support/freestore_test.cpp b/libc/test/src/__support/triefreestore_test.cpp
similarity index 73%
rename from libc/test/src/__support/freestore_test.cpp
rename to libc/test/src/__support/triefreestore_test.cpp
index 7017d6b9ebe93..dfecbf6153085 100644
--- a/libc/test/src/__support/freestore_test.cpp
+++ b/libc/test/src/__support/triefreestore_test.cpp
@@ -1,25 +1,30 @@
-//===-- Unittests for a freestore -------------------------------*- C++ -*-===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for TrieFreeStore.
+///
+//===----------------------------------------------------------------------===//
 
 #include <stddef.h>
 
-#include "src/__support/freestore.h"
+#include "src/__support/freetrie.h"
 #include "test/UnitTest/Test.h"
 
 using LIBC_NAMESPACE::Block;
 using LIBC_NAMESPACE::FreeList;
-using LIBC_NAMESPACE::FreeStore;
 using LIBC_NAMESPACE::FreeTrie;
+using LIBC_NAMESPACE::TrieFreeStore;
 using LIBC_NAMESPACE::cpp::byte;
 using LIBC_NAMESPACE::cpp::optional;
 
 // Inserting or removing blocks too small to be tracked does nothing.
-TEST(LlvmLibcFreeStore, TooSmall) {
+TEST(LlvmLibcTrieFreeStore, TooSmall) {
   byte mem[1024];
   optional<Block *> maybeBlock = Block::init(mem);
   ASSERT_TRUE(maybeBlock.has_value());
@@ -32,16 +37,16 @@ TEST(LlvmLibcFreeStore, TooSmall) {
     return;
   Block *remainder = *maybeBlock;
 
-  FreeStore store;
+  TrieFreeStore store;
   store.set_range({0, 4096});
   store.insert(too_small);
   store.insert(remainder);
 
-  EXPECT_EQ(store.remove_best_fit(too_small->inner_size()), remainder);
+  EXPECT_EQ(store.find_and_remove_fit(too_small->inner_size()), remainder);
   store.remove(too_small);
 }
 
-TEST(LlvmLibcFreeStore, RemoveBestFit) {
+TEST(LlvmLibcTrieFreeStore, FindAndRemoveFit) {
   byte mem[1024];
   optional<Block *> maybeBlock = Block::init(mem);
   ASSERT_TRUE(maybeBlock.has_value());
@@ -60,7 +65,7 @@ TEST(LlvmLibcFreeStore, RemoveBestFit) {
 
   Block *remainder = *maybeBlock;
 
-  FreeStore store;
+  TrieFreeStore store;
   store.set_range({0, 4096});
   store.insert(smallest);
   if (largest_small != smallest)
@@ -68,23 +73,26 @@ TEST(LlvmLibcFreeStore, RemoveBestFit) {
   store.insert(remainder);
 
   // Find exact match for smallest.
-  ASSERT_EQ(store.remove_best_fit(smallest->inner_size()), smallest);
+  ASSERT_EQ(store.find_and_remove_fit(smallest->inner_size()), smallest);
   store.insert(smallest);
 
   // Find exact match for largest.
-  ASSERT_EQ(store.remove_best_fit(largest_small->inner_size()), largest_small);
+  ASSERT_EQ(store.find_and_remove_fit(largest_small->inner_size()),
+            largest_small);
   store.insert(largest_small);
 
   // Search small list for best fit.
   Block *next_smallest = largest_small == smallest ? remainder : largest_small;
-  ASSERT_EQ(store.remove_best_fit(smallest->inner_size() + 1), next_smallest);
+  ASSERT_EQ(store.find_and_remove_fit(smallest->inner_size() + 1),
+            next_smallest);
   store.insert(next_smallest);
 
   // Continue search for best fit to large blocks.
-  EXPECT_EQ(store.remove_best_fit(largest_small->inner_size() + 1), remainder);
+  EXPECT_EQ(store.find_and_remove_fit(largest_small->inner_size() + 1),
+            remainder);
 }
 
-TEST(LlvmLibcFreeStore, Remove) {
+TEST(LlvmLibcTrieFreeStore, Remove) {
   byte mem[1024];
   optional<Block *> maybeBlock = Block::init(mem);
   ASSERT_TRUE(maybeBlock.has_value());
@@ -95,15 +103,15 @@ TEST(LlvmLibcFreeStore, Remove) {
 
   Block *remainder = *maybeBlock;
 
-  FreeStore store;
+  TrieFreeStore store;
   store.set_range({0, 4096});
   store.insert(small);
   store.insert(remainder);
 
   store.remove(remainder);
-  ASSERT_EQ(store.remove_best_fit(remainder->inner_size()),
+  ASSERT_EQ(store.find_and_remove_fit(remainder->inner_size()),
             static_cast<Block *>(nullptr));
   store.remove(small);
-  ASSERT_EQ(store.remove_best_fit(small->inner_size()),
+  ASSERT_EQ(store.find_and_remove_fit(small->inner_size()),
             static_cast<Block *>(nullptr));
 }

``````````

</details>


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


More information about the libc-commits mailing list