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] Lazily initialize freelist malloc using symbols #99254

Merged
merged 9 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions libc/config/baremetal/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
"value": false
}
},
"malloc": {
"LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE": {
"value": 102400
}
},
"qsort": {
"LIBC_CONF_QSORT_IMPL": {
"value": "LIBC_QSORT_HEAP_SORT"
Expand Down
6 changes: 0 additions & 6 deletions libc/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,6 @@
"doc": "Default number of spins before blocking if a rwlock is in contention (default to 100)."
}
},
"malloc": {
"LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE": {
"value": 1073741824,
"doc": "Default size for the constinit freelist buffer used for the freelist malloc implementation (default 1o 1GB)."
}
},
"unistd": {
"LIBC_CONF_ENABLE_TID_CACHE": {
"value": true,
Expand Down
2 changes: 0 additions & 2 deletions libc/docs/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ to learn about the defaults for your platform and target.
- ``LIBC_CONF_KEEP_FRAME_POINTER``: Keep frame pointer in functions for better debugging experience.
* **"errno" options**
- ``LIBC_CONF_ERRNO_MODE``: The implementation used for errno, acceptable values are LIBC_ERRNO_MODE_DEFAULT, LIBC_ERRNO_MODE_UNDEFINED, LIBC_ERRNO_MODE_THREAD_LOCAL, LIBC_ERRNO_MODE_SHARED, LIBC_ERRNO_MODE_EXTERNAL, and LIBC_ERRNO_MODE_SYSTEM.
* **"malloc" options**
- ``LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE``: Default size for the constinit freelist buffer used for the freelist malloc implementation (default 1o 1GB).
* **"math" options**
- ``LIBC_CONF_MATH_OPTIMIZATIONS``: Configures optimizations for math functions. Values accepted are LIBC_MATH_SKIP_ACCURATE_PASS, LIBC_MATH_SMALL_TABLES, LIBC_MATH_NO_ERRNO, LIBC_MATH_NO_EXCEPT, and LIBC_MATH_FAST.
* **"printf" options**
Expand Down
73 changes: 32 additions & 41 deletions libc/src/__support/freelist_heap.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@

namespace LIBC_NAMESPACE_DECL {

extern "C" cpp::byte _end;
mysterymath marked this conversation as resolved.
Show resolved Hide resolved
extern "C" cpp::byte __llvm_libc_heap_limit;

using cpp::optional;
using cpp::span;

Expand All @@ -47,18 +50,10 @@ template <size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()> class FreeListHeap {
size_t total_free_calls;
};

FreeListHeap(span<cpp::byte> region)
: FreeListHeap(&*region.begin(), &*region.end(), region.size()) {
auto result = BlockType::init(region);
BlockType *block = *result;
freelist_.add_chunk(block_to_span(block));
}
constexpr FreeListHeap() : begin_(&_end), end_(&__llvm_libc_heap_limit) {}

constexpr FreeListHeap(void *start, cpp::byte *end, size_t total_bytes)
: block_region_start_(start), block_region_end_(end),
freelist_(DEFAULT_BUCKETS), heap_stats_{} {
heap_stats_.total_bytes = total_bytes;
}
constexpr FreeListHeap(span<cpp::byte> region)
: begin_(region.begin()), end_(region.end()) {}

void *allocate(size_t size);
void *aligned_allocate(size_t alignment, size_t size);
Expand All @@ -69,61 +64,57 @@ template <size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()> class FreeListHeap {
void *calloc(size_t num, size_t size);

const HeapStats &heap_stats() const { return heap_stats_; }
void reset_heap_stats() { heap_stats_ = {}; }

void *region_start() const { return block_region_start_; }
size_t region_size() const {
return reinterpret_cast<uintptr_t>(block_region_end_) -
reinterpret_cast<uintptr_t>(block_region_start_);
}
cpp::span<cpp::byte> region() const { return {begin_, end_}; }

protected:
constexpr void set_freelist_node(typename FreeListType::FreeListNode &node,
cpp::span<cpp::byte> chunk) {
freelist_.set_freelist_node(node, chunk);
}
private:
void init();

void *allocate_impl(size_t alignment, size_t size);

private:
span<cpp::byte> block_to_span(BlockType *block) {
return span<cpp::byte>(block->usable_space(), block->inner_size());
}

bool is_valid_ptr(void *ptr) {
return ptr >= block_region_start_ && ptr < block_region_end_;
}
bool is_valid_ptr(void *ptr) { return ptr >= begin_ && ptr < end_; }

void *block_region_start_;
void *block_region_end_;
FreeListType freelist_;
HeapStats heap_stats_;
bool is_initialized_ = false;
cpp::byte *begin_;
cpp::byte *end_;
FreeListType freelist_{DEFAULT_BUCKETS};
HeapStats heap_stats_{};
};

template <size_t BUFF_SIZE, size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()>
struct FreeListHeapBuffer : public FreeListHeap<NUM_BUCKETS> {
class FreeListHeapBuffer : public FreeListHeap<NUM_BUCKETS> {
using parent = FreeListHeap<NUM_BUCKETS>;
using FreeListNode = typename parent::FreeListType::FreeListNode;

public:
constexpr FreeListHeapBuffer()
: FreeListHeap<NUM_BUCKETS>(&block, buffer + sizeof(buffer), BUFF_SIZE),
block(0, BUFF_SIZE), node{}, buffer{} {
block.mark_last();

cpp::span<cpp::byte> chunk(buffer, sizeof(buffer));
parent::set_freelist_node(node, chunk);
}
: FreeListHeap<NUM_BUCKETS>{buffer}, buffer{} {}

typename parent::BlockType block;
FreeListNode node;
cpp::byte buffer[BUFF_SIZE - sizeof(block) - sizeof(node)];
private:
cpp::byte buffer[BUFF_SIZE];
};

template <size_t NUM_BUCKETS> void FreeListHeap<NUM_BUCKETS>::init() {
LIBC_ASSERT(!is_initialized_ && "duplicate initialization");
heap_stats_.total_bytes = region().size();
auto result = BlockType::init(region());
BlockType *block = *result;
freelist_.add_chunk(block_to_span(block));
is_initialized_ = true;
}

template <size_t NUM_BUCKETS>
void *FreeListHeap<NUM_BUCKETS>::allocate_impl(size_t alignment, size_t size) {
if (size == 0)
return nullptr;

if (!is_initialized_)
init();

// Find a chunk in the freelist. Split it if needed, then return.
auto chunk =
freelist_.find_chunk_if([alignment, size](span<cpp::byte> chunk) {
Expand Down
2 changes: 0 additions & 2 deletions libc/src/stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,6 @@ if(NOT LIBC_TARGET_OS_IS_GPU)
malloc.h
DEPENDS
libc.src.__support.freelist_heap
COMPILE_OPTIONS
-DLIBC_FREELIST_MALLOC_SIZE=${LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE}
)
get_target_property(freelist_malloc_is_skipped libc.src.stdlib.freelist_malloc "SKIPPED")
if(LIBC_TARGET_OS_IS_BAREMETAL AND NOT freelist_malloc_is_skipped)
Expand Down
13 changes: 2 additions & 11 deletions libc/src/stdlib/freelist_malloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,8 @@

namespace LIBC_NAMESPACE_DECL {

namespace {
#ifdef LIBC_FREELIST_MALLOC_SIZE
// This is set via the LIBC_CONF_FREELIST_MALLOC_BUFFER_SIZE configuration.
constexpr size_t SIZE = LIBC_FREELIST_MALLOC_SIZE;
#else
#error "LIBC_FREELIST_MALLOC_SIZE was not defined for this build."
#endif
LIBC_CONSTINIT FreeListHeapBuffer<SIZE> freelist_heap_buffer;
} // namespace

FreeListHeap<> *freelist_heap = &freelist_heap_buffer;
static LIBC_CONSTINIT FreeListHeap<> freelist_heap_symbols;
FreeListHeap<> *freelist_heap = &freelist_heap_symbols;

LLVM_LIBC_FUNCTION(void *, malloc, (size_t size)) {
return freelist_heap->allocate(size);
Expand Down
1 change: 1 addition & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ if(LLVM_LIBC_FULL_BUILD)
SUITE
libc-support-tests
SRCS
fake_heap.s
freelist_heap_test.cpp
freelist_malloc_test.cpp
DEPENDS
Expand Down
15 changes: 15 additions & 0 deletions libc/test/src/__support/fake_heap.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//===-- Test fake definition for heap symbols -----------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

.globl _end, __llvm_libc_heap_limit

.bss
_end:
.fill 1024
__llvm_libc_heap_limit:

7 changes: 6 additions & 1 deletion libc/test/src/__support/freelist_heap_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,19 @@ using LIBC_NAMESPACE::freelist_heap;
#define TEST_FOR_EACH_ALLOCATOR(TestCase, BufferSize) \
class LlvmLibcFreeListHeapTest##TestCase : public testing::Test { \
public: \
FreeListHeapBuffer<BufferSize> fake_global_buffer; \
void SetUp() override { \
freelist_heap = \
new (&fake_global_buffer) FreeListHeapBuffer<BufferSize>; \
} \
mysterymath marked this conversation as resolved.
Show resolved Hide resolved
void RunTest(FreeListHeap<> &allocator, [[maybe_unused]] size_t N); \
}; \
TEST_F(LlvmLibcFreeListHeapTest##TestCase, TestCase) { \
alignas(FreeListHeap<>::BlockType) \
cpp::byte buf[BufferSize] = {cpp::byte(0)}; \
FreeListHeap<> allocator(buf); \
RunTest(allocator, BufferSize); \
RunTest(*freelist_heap, freelist_heap->region_size()); \
RunTest(*freelist_heap, freelist_heap->region().size()); \
} \
void LlvmLibcFreeListHeapTest##TestCase::RunTest(FreeListHeap<> &allocator, \
size_t N)
Expand Down
4 changes: 1 addition & 3 deletions libc/test/src/__support/freelist_malloc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::freelist_heap;
using LIBC_NAMESPACE::FreeListHeapBuffer;

TEST(LlvmLibcFreeListMalloc, MallocStats) {
constexpr size_t kAllocSize = 256;
constexpr size_t kCallocNum = 4;
constexpr size_t kCallocSize = 64;

freelist_heap->reset_heap_stats(); // Do this because other tests might've
// called the same global allocator.

void *ptr1 = LIBC_NAMESPACE::malloc(kAllocSize);

const auto &freelist_heap_stats = freelist_heap->heap_stats();
Expand Down
Loading