Skip to content

Commit 32ae283

Browse files
committed
[WIP][libc] Add freelist malloc
1 parent 3106a23 commit 32ae283

File tree

11 files changed

+287
-154
lines changed

11 files changed

+287
-154
lines changed

libc/config/baremetal/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ set(TARGET_LIBC_ENTRYPOINTS
170170
libc.src.stdlib.ldiv
171171
libc.src.stdlib.llabs
172172
libc.src.stdlib.lldiv
173+
libc.src.stdlib.malloc
173174
libc.src.stdlib.qsort
174175
libc.src.stdlib.rand
175176
libc.src.stdlib.srand

libc/src/__support/threads/thread.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ class ThreadAtExitCallbackMgr {
115115
public:
116116
constexpr ThreadAtExitCallbackMgr()
117117
: mtx(/*timed=*/false, /*recursive=*/false, /*robust=*/false,
118-
/*pshared=*/false) {}
118+
/*pshared=*/false),
119+
callback_list() {}
119120

120121
int add_callback(AtExitCallback *callback, void *obj) {
121122
cpp::lock_guard lock(mtx);

libc/src/stdlib/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,14 @@ else()
418418
libc.src.string.memory_utils.inline_memcpy
419419
libc.src.string.memory_utils.inline_memset
420420
)
421-
add_entrypoint_external(
421+
add_entrypoint_object(
422422
malloc
423+
SRCS
424+
freelist_malloc.cpp
425+
HDRS
426+
malloc.h
427+
DEPENDS
428+
.freelist_heap
423429
)
424430
add_entrypoint_external(
425431
free

libc/src/stdlib/block.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ class Block {
245245
void mark_free() { info_.used = 0; }
246246

247247
/// Marks this block as the last one in the chain.
248-
void mark_last() { info_.last = 1; }
248+
constexpr void mark_last() { info_.last = 1; }
249249

250250
/// Clears the last bit from this block.
251251
void clear_last() { info_.last = 1; }
@@ -259,15 +259,15 @@ class Block {
259259
return check_status() == internal::BlockStatus::VALID;
260260
}
261261

262+
constexpr Block(size_t prev_outer_size, size_t outer_size);
263+
262264
private:
263265
/// Consumes the block and returns as a span of bytes.
264266
static ByteSpan as_bytes(Block *&&block);
265267

266268
/// Consumes the span of bytes and uses it to construct and return a block.
267269
static Block *as_block(size_t prev_outer_size, ByteSpan bytes);
268270

269-
Block(size_t prev_outer_size, size_t outer_size);
270-
271271
/// Returns a `BlockStatus` that is either VALID or indicates the reason why
272272
/// the block is invalid.
273273
///
@@ -442,7 +442,9 @@ Block<OffsetType, kAlign> *Block<OffsetType, kAlign>::prev() const {
442442
// Private template method implementations.
443443

444444
template <typename OffsetType, size_t kAlign>
445-
Block<OffsetType, kAlign>::Block(size_t prev_outer_size, size_t outer_size) {
445+
constexpr Block<OffsetType, kAlign>::Block(size_t prev_outer_size,
446+
size_t outer_size)
447+
: info_{} {
446448
prev_ = prev_outer_size / ALIGNMENT;
447449
next_ = outer_size / ALIGNMENT;
448450
info_.used = 0;

libc/src/stdlib/freelist.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,36 +69,44 @@ template <size_t NUM_BUCKETS = 6> class FreeList {
6969
/// Removes a chunk from this freelist.
7070
bool remove_chunk(cpp::span<cpp::byte> chunk);
7171

72-
private:
73-
// For a given size, find which index into chunks_ the node should be written
74-
// to.
75-
size_t find_chunk_ptr_for_size(size_t size, bool non_null) const;
72+
/// For a given size, find which index into chunks_ the node should be written
73+
/// to.
74+
constexpr size_t find_chunk_ptr_for_size(size_t size, bool non_null) const;
7675

7776
struct FreeListNode {
7877
FreeListNode *next;
7978
size_t size;
8079
};
8180

82-
public:
83-
explicit FreeList(cpp::array<size_t, NUM_BUCKETS> sizes)
81+
constexpr void set_freelist_node(FreeListNode *node,
82+
cpp::span<cpp::byte> chunk);
83+
84+
constexpr explicit FreeList(cpp::array<size_t, NUM_BUCKETS> sizes)
8485
: chunks_(NUM_BUCKETS + 1, 0), sizes_(sizes.begin(), sizes.end()) {}
8586

87+
private:
8688
FixedVector<FreeList::FreeListNode *, NUM_BUCKETS + 1> chunks_;
8789
FixedVector<size_t, NUM_BUCKETS> sizes_;
8890
};
8991

92+
template <size_t NUM_BUCKETS>
93+
constexpr void FreeList<NUM_BUCKETS>::set_freelist_node(FreeListNode *node,
94+
span<cpp::byte> chunk) {
95+
// Add it to the correct list.
96+
size_t chunk_ptr = find_chunk_ptr_for_size(chunk.size(), false);
97+
node->size = chunk.size();
98+
node->next = chunks_[chunk_ptr];
99+
chunks_[chunk_ptr] = node;
100+
}
101+
90102
template <size_t NUM_BUCKETS>
91103
bool FreeList<NUM_BUCKETS>::add_chunk(span<cpp::byte> chunk) {
92104
// Check that the size is enough to actually store what we need
93105
if (chunk.size() < sizeof(FreeListNode))
94106
return false;
95107

96-
// Add it to the correct list.
97-
size_t chunk_ptr = find_chunk_ptr_for_size(chunk.size(), false);
98-
99-
FreeListNode *node =
100-
::new (chunk.data()) FreeListNode{chunks_[chunk_ptr], chunk.size()};
101-
chunks_[chunk_ptr] = node;
108+
FreeListNode *node = ::new (chunk.data()) FreeListNode;
109+
set_freelist_node(node, chunk);
102110

103111
return true;
104112
}
@@ -163,8 +171,9 @@ bool FreeList<NUM_BUCKETS>::remove_chunk(span<cpp::byte> chunk) {
163171
}
164172

165173
template <size_t NUM_BUCKETS>
166-
size_t FreeList<NUM_BUCKETS>::find_chunk_ptr_for_size(size_t size,
167-
bool non_null) const {
174+
constexpr size_t
175+
FreeList<NUM_BUCKETS>::find_chunk_ptr_for_size(size_t size,
176+
bool non_null) const {
168177
size_t chunk_ptr = 0;
169178
for (chunk_ptr = 0u; chunk_ptr < sizes_.size(); chunk_ptr++) {
170179
if (sizes_[chunk_ptr] >= size &&

libc/src/stdlib/freelist_heap.h

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static constexpr cpp::array<size_t, 6> DEFAULT_BUCKETS{16, 32, 64,
3030
template <size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()> class FreeListHeap {
3131
public:
3232
using BlockType = Block<>;
33+
using FreeListType = FreeList<NUM_BUCKETS>;
3334

3435
struct HeapStats {
3536
size_t total_bytes;
@@ -39,35 +40,73 @@ template <size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()> class FreeListHeap {
3940
size_t total_allocate_calls;
4041
size_t total_free_calls;
4142
};
42-
FreeListHeap(span<cpp::byte> region);
43+
44+
FreeListHeap(span<cpp::byte> region)
45+
: FreeListHeap(&*region.begin(), &*region.end(), region.size()) {
46+
auto result = BlockType::init(region);
47+
BlockType *block = *result;
48+
freelist_.add_chunk(block_to_span(block));
49+
}
50+
51+
constexpr FreeListHeap(void *start, cpp::byte *end, size_t total_bytes)
52+
: block_region_start_(start), block_region_end_(end),
53+
freelist_(DEFAULT_BUCKETS), heap_stats_{} {
54+
heap_stats_.total_bytes = total_bytes;
55+
}
4356

4457
void *allocate(size_t size);
4558
void free(void *ptr);
4659
void *realloc(void *ptr, size_t size);
4760
void *calloc(size_t num, size_t size);
4861

4962
const HeapStats &heap_stats() const { return heap_stats_; }
63+
void reset_heap_stats() { heap_stats_ = {}; }
64+
65+
void *region_start() const { return block_region_start_; }
66+
size_t region_size() const {
67+
return reinterpret_cast<uintptr_t>(block_region_end_) -
68+
reinterpret_cast<uintptr_t>(block_region_start_);
69+
}
70+
71+
protected:
72+
constexpr void set_freelist_node(typename FreeListType::FreeListNode *node,
73+
cpp::span<cpp::byte> chunk) {
74+
freelist_.set_freelist_node(node, chunk);
75+
}
5076

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

56-
span<cpp::byte> region_;
57-
FreeList<NUM_BUCKETS> freelist_;
82+
bool is_valid_ptr(void *ptr) {
83+
return ptr >= block_region_start_ && ptr < block_region_end_;
84+
}
85+
86+
void *block_region_start_;
87+
void *block_region_end_;
88+
FreeListType freelist_;
5889
HeapStats heap_stats_;
5990
};
6091

61-
template <size_t NUM_BUCKETS>
62-
FreeListHeap<NUM_BUCKETS>::FreeListHeap(span<cpp::byte> region)
63-
: region_(region), freelist_(DEFAULT_BUCKETS), heap_stats_() {
64-
auto result = BlockType::init(region);
65-
BlockType *block = *result;
92+
template <size_t BUFF_SIZE, size_t NUM_BUCKETS = DEFAULT_BUCKETS.size()>
93+
struct FreeListHeapBuffer : public FreeListHeap<NUM_BUCKETS> {
94+
using parent = FreeListHeap<NUM_BUCKETS>;
95+
using FreeListNode = typename parent::FreeListType::FreeListNode;
6696

67-
freelist_.add_chunk(block_to_span(block));
97+
constexpr FreeListHeapBuffer()
98+
: FreeListHeap<NUM_BUCKETS>(&block, buffer + sizeof(buffer), BUFF_SIZE),
99+
block(0, BUFF_SIZE), node{}, buffer{} {
100+
block.mark_last();
68101

69-
heap_stats_.total_bytes = region.size();
70-
}
102+
cpp::span<cpp::byte> chunk(buffer, sizeof(buffer));
103+
parent::set_freelist_node(&node, chunk);
104+
}
105+
106+
typename parent::BlockType block;
107+
FreeListNode node;
108+
cpp::byte buffer[BUFF_SIZE - sizeof(block) - sizeof(node)];
109+
};
71110

72111
template <size_t NUM_BUCKETS>
73112
void *FreeListHeap<NUM_BUCKETS>::allocate(size_t size) {
@@ -97,7 +136,7 @@ void *FreeListHeap<NUM_BUCKETS>::allocate(size_t size) {
97136
template <size_t NUM_BUCKETS> void FreeListHeap<NUM_BUCKETS>::free(void *ptr) {
98137
cpp::byte *bytes = static_cast<cpp::byte *>(ptr);
99138

100-
LIBC_ASSERT(bytes >= region_.data() && bytes < region_.data() + region_.size() && "Invalid pointer");
139+
LIBC_ASSERT(is_valid_ptr(bytes) && "Invalid pointer");
101140

102141
BlockType *chunk_block = BlockType::from_usable_space(bytes);
103142

@@ -131,7 +170,7 @@ template <size_t NUM_BUCKETS> void FreeListHeap<NUM_BUCKETS>::free(void *ptr) {
131170
heap_stats_.total_free_calls += 1;
132171
}
133172

134-
// Follows contract of the C standard realloc() function
173+
// Follows constract of the C standard realloc() function
135174
// If ptr is free'd, will return nullptr.
136175
template <size_t NUM_BUCKETS>
137176
void *FreeListHeap<NUM_BUCKETS>::realloc(void *ptr, size_t size) {
@@ -146,7 +185,7 @@ void *FreeListHeap<NUM_BUCKETS>::realloc(void *ptr, size_t size) {
146185

147186
cpp::byte *bytes = static_cast<cpp::byte *>(ptr);
148187

149-
if (bytes < region_.data() || bytes >= region_.data() + region_.size())
188+
if (!is_valid_ptr(bytes))
150189
return nullptr;
151190

152191
BlockType *chunk_block = BlockType::from_usable_space(bytes);
@@ -177,6 +216,8 @@ void *FreeListHeap<NUM_BUCKETS>::calloc(size_t num, size_t size) {
177216
return ptr;
178217
}
179218

219+
extern FreeListHeap<> *freelist_heap;
220+
180221
} // namespace LIBC_NAMESPACE
181222

182223
#endif // LLVM_LIBC_SRC_STDLIB_FREELIST_HEAP_H

libc/src/stdlib/freelist_malloc.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===-- Implementation for freelist_malloc --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "freelist_heap.h"
10+
11+
#include <stddef.h>
12+
13+
namespace LIBC_NAMESPACE {
14+
15+
namespace {
16+
// TODO: We should probably have something akin to what scudo/sanitizer
17+
// allocators do where each platform defines this.
18+
constexpr size_t SIZE = 0x40000000ULL; // 1GB
19+
LIBC_CONSTINIT FreeListHeapBuffer<SIZE> freelist_heap_buffer;
20+
} // namespace
21+
22+
FreeListHeap<> *freelist_heap = &freelist_heap_buffer;
23+
24+
void *malloc(size_t size) { return freelist_heap->allocate(size); }
25+
26+
void free(void *ptr) { freelist_heap->free(ptr); }
27+
28+
void *calloc(size_t num, size_t size) {
29+
return freelist_heap->calloc(num, size);
30+
}
31+
32+
void *realloc(void *ptr, size_t size) {
33+
return freelist_heap->realloc(ptr, size);
34+
}
35+
36+
} // namespace LIBC_NAMESPACE

libc/test/src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ add_subdirectory(inttypes)
6161
if(${LIBC_TARGET_OS} STREQUAL "linux")
6262
add_subdirectory(fcntl)
6363
add_subdirectory(sched)
64-
add_subdirectory(sys)
64+
#add_subdirectory(sys)
6565
add_subdirectory(termios)
6666
add_subdirectory(unistd)
6767
endif()

libc/test/src/stdlib/CMakeLists.txt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ add_libc_test(
8888
DEPENDS
8989
libc.src.__support.CPP.span
9090
libc.src.stdlib.freelist_heap
91+
libc.src.stdlib.malloc
9192
libc.src.string.memcmp
9293
libc.src.string.memcpy
9394
)
@@ -437,19 +438,21 @@ if(LLVM_LIBC_FULL_BUILD)
437438
libc.src.stdlib.quick_exit
438439
)
439440

440-
# Only the GPU has an in-tree 'malloc' implementation.
441-
if(LIBC_TARGET_OS_IS_GPU)
442-
add_libc_test(
443-
malloc_test
444-
HERMETIC_TEST_ONLY
445-
SUITE
446-
libc-stdlib-tests
447-
SRCS
448-
malloc_test.cpp
449-
DEPENDS
450-
libc.include.stdlib
451-
libc.src.stdlib.malloc
452-
libc.src.stdlib.free
453-
)
454-
endif()
441+
add_libc_test(
442+
malloc_test
443+
SUITE
444+
libc-stdlib-tests
445+
SRCS
446+
block_test.cpp
447+
malloc_test.cpp
448+
freelist_malloc_test.cpp
449+
freelist_heap_test.cpp
450+
freelist_test.cpp
451+
DEPENDS
452+
libc.include.stdlib
453+
libc.src.stdlib.malloc
454+
libc.src.stdlib.free
455+
libc.src.string.memcmp
456+
libc.src.string.memcpy
457+
)
455458
endif()

0 commit comments

Comments
 (0)