[libc-commits] [libc] [libc] Restructure freelist heap free store (PR #200710)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Sun May 31 21:29:22 PDT 2026
https://github.com/SchrodingerZhu created https://github.com/llvm/llvm-project/pull/200710
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__
>From 671ce68ca5262dddca4434075502326efdb775b0 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yfzhu at google.com>
Date: Mon, 1 Jun 2026 00:07:22 -0400
Subject: [PATCH] [libc] Restructure freelist heap free store
Assisted-by: AI tools, checked manually
---
libc/src/__support/CMakeLists.txt | 10 +-
libc/src/__support/freelist_heap.h | 13 +-
libc/src/__support/freestore.h | 113 ------------------
libc/src/__support/freetrie.h | 103 +++++++++++++++-
libc/test/src/__support/CMakeLists.txt | 5 +-
...estore_test.cpp => triefreestore_test.cpp} | 40 ++++---
6 files changed, 138 insertions(+), 146 deletions(-)
delete mode 100644 libc/src/__support/freestore.h
rename libc/test/src/__support/{freestore_test.cpp => triefreestore_test.cpp} (73%)
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));
}
More information about the libc-commits
mailing list