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

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Tue Jun 2 15:49:08 PDT 2026


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

>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 1/3] [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));
 }

>From 417c80a6a6b42e29d907a3eb107d6dbecf983d44 Mon Sep 17 00:00:00 2001
From: Yifan Zhu <yfzhu at google.com>
Date: Tue, 2 Jun 2026 14:07:03 -0700
Subject: [PATCH 2/3] cleanup

---
 libc/src/__support/CMakeLists.txt  | 13 ++++++
 libc/src/__support/freelist_heap.h | 71 +++++++++++++++++++++++-------
 libc/src/__support/freestore.h     | 49 +++++++++++++++++++++
 libc/src/__support/freetrie.cpp    |  2 +-
 libc/src/__support/freetrie.h      | 24 +++++-----
 5 files changed, 129 insertions(+), 30 deletions(-)
 create mode 100644 libc/src/__support/freestore.h

diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index 89c4379381956..9a617f95e2d53 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -40,6 +40,17 @@ add_object_library(
     libc.src.__support.CPP.span
 )
 
+add_header_library(
+  freestore
+  HDRS
+    freestore.h
+  DEPENDS
+    .block
+    .freelist
+    libc.hdr.types.size_t
+    libc.src.__support.macros.config
+)
+
 add_object_library(
   freetrie
   HDRS
@@ -49,6 +60,7 @@ add_object_library(
   DEPENDS
     .block
     .freelist
+    .freestore
     libc.src.__support.CPP.array
 )
 
@@ -65,6 +77,7 @@ add_object_library(
     .block
     .freelist
     .freetrie
+    .freestore
     libc.src.__support.CPP.cstddef
     libc.src.__support.CPP.array
     libc.src.__support.CPP.optional
diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index bc992dd71da6d..218dfa1db4308 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -16,10 +16,11 @@
 
 #include <stddef.h>
 
-#include "block.h"
-#include "freetrie.h"
 #include "src/__support/CPP/optional.h"
 #include "src/__support/CPP/span.h"
+#include "src/__support/block.h"
+#include "src/__support/freestore.h"
+#include "src/__support/freetrie.h"
 #include "src/__support/libc_assert.h"
 #include "src/__support/macros/config.h"
 #include "src/__support/math_extras.h"
@@ -36,11 +37,19 @@ using cpp::span;
 
 LIBC_INLINE constexpr bool IsPow2(size_t x) { return x && (x & (x - 1)) == 0; }
 
-class FreeListHeap {
+template <typename T> LIBC_INLINE void init_free_store(T &, size_t) {}
+
+template <>
+LIBC_INLINE void init_free_store<TrieFreeStore>(TrieFreeStore &store,
+                                                size_t size) {
+  store.set_range({0, cpp::bit_ceil(size)});
+}
+
+template <typename FreeStoreType = TrieFreeStore> class FreeListHeapImpl {
 public:
-  constexpr FreeListHeap() : begin(&_end), end(&__llvm_libc_heap_limit) {}
+  constexpr FreeListHeapImpl() : begin(&_end), end(&__llvm_libc_heap_limit) {}
 
-  constexpr FreeListHeap(span<cpp::byte> region)
+  constexpr FreeListHeapImpl(span<cpp::byte> region)
       : begin(region.begin()), end(region.end()) {}
 
   void *allocate(size_t size);
@@ -67,18 +76,38 @@ class FreeListHeap {
   cpp::byte *begin;
   cpp::byte *end;
   bool is_initialized = false;
-  TrieFreeStore free_store;
+  FreeStoreType free_store;
+};
+
+// Deduction guide for FreeListHeapImpl to allow bracket-less instantiation
+FreeListHeapImpl(span<cpp::byte>) -> FreeListHeapImpl<TrieFreeStore>;
+
+using FreeListHeap = FreeListHeapImpl<TrieFreeStore>;
+
+template <size_t BUFF_SIZE, typename FreeStoreType = TrieFreeStore>
+class FreeListHeapBuffer : public FreeListHeapImpl<FreeStoreType> {
+public:
+  constexpr FreeListHeapBuffer()
+      : FreeListHeapImpl<FreeStoreType>{buffer}, buffer{} {}
+
+private:
+  cpp::byte buffer[BUFF_SIZE];
 };
 
-template <size_t BUFF_SIZE> class FreeListHeapBuffer : public FreeListHeap {
+// Specialization for the default FreeStore to allow conversion to FreeListHeap*
+template <size_t BUFF_SIZE>
+class FreeListHeapBuffer<BUFF_SIZE, TrieFreeStore>
+    : public FreeListHeapImpl<TrieFreeStore> {
 public:
-  constexpr FreeListHeapBuffer() : FreeListHeap{buffer}, buffer{} {}
+  constexpr FreeListHeapBuffer()
+      : FreeListHeapImpl<TrieFreeStore>{buffer}, buffer{} {}
 
 private:
   cpp::byte buffer[BUFF_SIZE];
 };
 
-LIBC_INLINE void FreeListHeap::init() {
+template <typename FreeStoreType>
+LIBC_INLINE void FreeListHeapImpl<FreeStoreType>::init() {
   LIBC_ASSERT(!is_initialized && "duplicate initialization");
   auto result = Block::init(region());
   Block *block = *result;
@@ -87,7 +116,9 @@ LIBC_INLINE void FreeListHeap::init() {
   is_initialized = true;
 }
 
-LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
+template <typename FreeStoreType>
+LIBC_INLINE void *
+FreeListHeapImpl<FreeStoreType>::allocate_impl(size_t alignment, size_t size) {
   if (size == 0)
     return nullptr;
 
@@ -112,12 +143,15 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
   return block_info.block->usable_space();
 }
 
-LIBC_INLINE void *FreeListHeap::allocate(size_t size) {
+template <typename FreeStoreType>
+LIBC_INLINE void *FreeListHeapImpl<FreeStoreType>::allocate(size_t size) {
   return allocate_impl(Block::MIN_ALIGN, size);
 }
 
-LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
-                                                 size_t size) {
+template <typename FreeStoreType>
+LIBC_INLINE void *
+FreeListHeapImpl<FreeStoreType>::aligned_allocate(size_t alignment,
+                                                  size_t size) {
   // The alignment must be an integral power of two.
   if (!IsPow2(alignment))
     return nullptr;
@@ -132,7 +166,8 @@ LIBC_INLINE void *FreeListHeap::aligned_allocate(size_t alignment,
   return allocate_impl(alignment, size);
 }
 
-LIBC_INLINE void FreeListHeap::free(void *ptr) {
+template <typename FreeStoreType>
+LIBC_INLINE void FreeListHeapImpl<FreeStoreType>::free(void *ptr) {
   if (ptr == nullptr)
     return;
 
@@ -165,7 +200,9 @@ LIBC_INLINE void FreeListHeap::free(void *ptr) {
 
 // Follows constract of the C standard realloc() function
 // If ptr is free'd, will return nullptr.
-LIBC_INLINE void *FreeListHeap::realloc(void *ptr, size_t size) {
+template <typename FreeStoreType>
+LIBC_INLINE void *FreeListHeapImpl<FreeStoreType>::realloc(void *ptr,
+                                                           size_t size) {
   if (size == 0) {
     free(ptr);
     return nullptr;
@@ -200,7 +237,9 @@ LIBC_INLINE void *FreeListHeap::realloc(void *ptr, size_t size) {
   return new_ptr;
 }
 
-LIBC_INLINE void *FreeListHeap::calloc(size_t num, size_t size) {
+template <typename FreeStoreType>
+LIBC_INLINE void *FreeListHeapImpl<FreeStoreType>::calloc(size_t num,
+                                                          size_t size) {
   size_t bytes;
   if (__builtin_mul_overflow(num, size, &bytes))
     return nullptr;
diff --git a/libc/src/__support/freestore.h b/libc/src/__support/freestore.h
new file mode 100644
index 0000000000000..97a7f7f3270e4
--- /dev/null
+++ b/libc/src/__support/freestore.h
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 the FreeStore template class.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
+#define LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
+
+#include "hdr/types/size_t.h"
+#include "src/__support/block.h"
+#include "src/__support/freelist.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+template <typename T> class FreeStore {
+protected:
+  static constexpr size_t MIN_OUTER_SIZE =
+      align_up(sizeof(Block) + sizeof(FreeList::Node), Block::MIN_ALIGN);
+
+  LIBC_INLINE static bool too_small(Block *block) {
+    return block->outer_size() < MIN_OUTER_SIZE;
+  }
+
+public:
+  LIBC_INLINE void insert(Block *block) {
+    static_cast<T *>(this)->insert_impl(block);
+  }
+
+  LIBC_INLINE void remove(Block *block) {
+    static_cast<T *>(this)->remove_impl(block);
+  }
+
+  LIBC_INLINE Block *find_and_remove_fit(size_t size) {
+    return static_cast<T *>(this)->find_and_remove_fit_impl(size);
+  }
+};
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FREESTORE_H
diff --git a/libc/src/__support/freetrie.cpp b/libc/src/__support/freetrie.cpp
index e76efe717f215..bb7a53a7635d9 100644
--- a/libc/src/__support/freetrie.cpp
+++ b/libc/src/__support/freetrie.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "freetrie.h"
+#include "src/__support/freetrie.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
diff --git a/libc/src/__support/freetrie.h b/libc/src/__support/freetrie.h
index ce8b80d280ae3..cff995d48f376 100644
--- a/libc/src/__support/freetrie.h
+++ b/libc/src/__support/freetrie.h
@@ -14,8 +14,11 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
 #define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
 
-#include "freelist.h"
+#include "hdr/types/size_t.h"
 #include "src/__support/CPP/array.h"
+#include "src/__support/freelist.h"
+#include "src/__support/freestore.h"
+#include "src/__support/macros/attributes.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -125,7 +128,7 @@ class FreeTrie {
 
 /// A best-fit store of variously-sized free blocks. Blocks can be inserted and
 /// removed in logarithmic time.
-class TrieFreeStore {
+class TrieFreeStore : public FreeStore<TrieFreeStore> {
 public:
   TrieFreeStore() = default;
   TrieFreeStore(const TrieFreeStore &other) = delete;
@@ -139,27 +142,22 @@ class TrieFreeStore {
 
   /// Insert a free block. If the block is too small to be tracked, nothing
   /// happens.
-  void insert(Block *block);
+  void insert_impl(Block *block);
 
   /// Remove a free block. If the block is too small to be tracked, nothing
   /// happens.
-  void remove(Block *block);
+  void remove_impl(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);
+  Block *find_and_remove_fit_impl(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;
   }
@@ -286,7 +284,7 @@ LIBC_INLINE FreeTrie::Node *FreeTrie::find_best_fit(size_t size) {
   }
 }
 
-LIBC_INLINE void TrieFreeStore::insert(Block *block) {
+LIBC_INLINE void TrieFreeStore::insert_impl(Block *block) {
   if (too_small(block))
     return;
   if (is_small(block))
@@ -295,7 +293,7 @@ LIBC_INLINE void TrieFreeStore::insert(Block *block) {
     large_trie.push(block);
 }
 
-LIBC_INLINE void TrieFreeStore::remove(Block *block) {
+LIBC_INLINE void TrieFreeStore::remove_impl(Block *block) {
   if (too_small(block))
     return;
   if (is_small(block)) {
@@ -307,7 +305,7 @@ LIBC_INLINE void TrieFreeStore::remove(Block *block) {
   }
 }
 
-LIBC_INLINE Block *TrieFreeStore::find_and_remove_fit(size_t size) {
+LIBC_INLINE Block *TrieFreeStore::find_and_remove_fit_impl(size_t size) {
   if (FreeList *list = find_best_small_fit(size)) {
     Block *block = list->front();
     list->pop();

>From dc773380aa37752bf4ab712878496944eac745f1 Mon Sep 17 00:00:00 2001
From: Yifan Zhu <yfzhu at google.com>
Date: Tue, 2 Jun 2026 15:48:53 -0700
Subject: [PATCH 3/3] remove

---
 libc/src/__support/freelist_heap.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/libc/src/__support/freelist_heap.h b/libc/src/__support/freelist_heap.h
index 218dfa1db4308..74e3777712941 100644
--- a/libc/src/__support/freelist_heap.h
+++ b/libc/src/__support/freelist_heap.h
@@ -37,14 +37,6 @@ using cpp::span;
 
 LIBC_INLINE constexpr bool IsPow2(size_t x) { return x && (x & (x - 1)) == 0; }
 
-template <typename T> LIBC_INLINE void init_free_store(T &, size_t) {}
-
-template <>
-LIBC_INLINE void init_free_store<TrieFreeStore>(TrieFreeStore &store,
-                                                size_t size) {
-  store.set_range({0, cpp::bit_ceil(size)});
-}
-
 template <typename FreeStoreType = TrieFreeStore> class FreeListHeapImpl {
 public:
   constexpr FreeListHeapImpl() : begin(&_end), end(&__llvm_libc_heap_limit) {}



More information about the libc-commits mailing list