1 //===-- Interface for freelist_heap ---------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
10 #define LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H
16 #include "src/__support/CPP/optional.h"
17 #include "src/__support/CPP/span.h"
18 #include "src/__support/libc_assert.h"
19 #include "src/__support/macros/config.h"
20 #include "src/string/memory_utils/inline_memcpy.h"
21 #include "src/string/memory_utils/inline_memset.h"
23 namespace LIBC_NAMESPACE_DECL
{
25 extern "C" cpp::byte _end
;
26 extern "C" cpp::byte __llvm_libc_heap_limit
;
31 inline constexpr bool IsPow2(size_t x
) { return x
&& (x
& (x
- 1)) == 0; }
33 static constexpr cpp::array
<size_t, 6> DEFAULT_BUCKETS
{16, 32, 64,
36 template <size_t NUM_BUCKETS
= DEFAULT_BUCKETS
.size()> class FreeListHeap
{
38 using BlockType
= Block
<>;
39 using FreeListType
= FreeList
<NUM_BUCKETS
>;
41 static constexpr size_t MIN_ALIGNMENT
=
42 cpp::max(BlockType::ALIGNMENT
, alignof(max_align_t
));
44 constexpr FreeListHeap() : begin_(&_end
), end_(&__llvm_libc_heap_limit
) {}
46 constexpr FreeListHeap(span
<cpp::byte
> region
)
47 : begin_(region
.begin()), end_(region
.end()) {}
49 void *allocate(size_t size
);
50 void *aligned_allocate(size_t alignment
, size_t size
);
51 // NOTE: All pointers passed to free must come from one of the other
52 // allocation functions: `allocate`, `aligned_allocate`, `realloc`, `calloc`.
54 void *realloc(void *ptr
, size_t size
);
55 void *calloc(size_t num
, size_t size
);
57 cpp::span
<cpp::byte
> region() const { return {begin_
, end_
}; }
62 void *allocate_impl(size_t alignment
, size_t size
);
64 span
<cpp::byte
> block_to_span(BlockType
*block
) {
65 return span
<cpp::byte
>(block
->usable_space(), block
->inner_size());
68 bool is_valid_ptr(void *ptr
) { return ptr
>= begin_
&& ptr
< end_
; }
70 bool is_initialized_
= false;
73 FreeListType freelist_
{DEFAULT_BUCKETS
};
76 template <size_t BUFF_SIZE
, size_t NUM_BUCKETS
= DEFAULT_BUCKETS
.size()>
77 class FreeListHeapBuffer
: public FreeListHeap
<NUM_BUCKETS
> {
78 using parent
= FreeListHeap
<NUM_BUCKETS
>;
79 using FreeListNode
= typename
parent::FreeListType::FreeListNode
;
82 constexpr FreeListHeapBuffer()
83 : FreeListHeap
<NUM_BUCKETS
>{buffer
}, buffer
{} {}
86 cpp::byte buffer
[BUFF_SIZE
];
89 template <size_t NUM_BUCKETS
> void FreeListHeap
<NUM_BUCKETS
>::init() {
90 LIBC_ASSERT(!is_initialized_
&& "duplicate initialization");
91 auto result
= BlockType::init(region());
92 BlockType
*block
= *result
;
93 freelist_
.add_chunk(block_to_span(block
));
94 is_initialized_
= true;
97 template <size_t NUM_BUCKETS
>
98 void *FreeListHeap
<NUM_BUCKETS
>::allocate_impl(size_t alignment
, size_t size
) {
102 if (!is_initialized_
)
105 // Find a chunk in the freelist. Split it if needed, then return.
107 freelist_
.find_chunk_if([alignment
, size
](span
<cpp::byte
> chunk
) {
108 BlockType
*block
= BlockType::from_usable_space(chunk
.data());
109 return block
->can_allocate(alignment
, size
);
112 if (chunk
.data() == nullptr)
114 freelist_
.remove_chunk(chunk
);
116 BlockType
*chunk_block
= BlockType::from_usable_space(chunk
.data());
117 LIBC_ASSERT(!chunk_block
->used());
119 // Split that chunk. If there's a leftover chunk, add it to the freelist
120 auto block_info
= BlockType::allocate(chunk_block
, alignment
, size
);
122 freelist_
.add_chunk(block_to_span(block_info
.next
));
124 freelist_
.add_chunk(block_to_span(block_info
.prev
));
125 chunk_block
= block_info
.block
;
127 chunk_block
->mark_used();
129 return chunk_block
->usable_space();
132 template <size_t NUM_BUCKETS
>
133 void *FreeListHeap
<NUM_BUCKETS
>::allocate(size_t size
) {
134 return allocate_impl(MIN_ALIGNMENT
, size
);
137 template <size_t NUM_BUCKETS
>
138 void *FreeListHeap
<NUM_BUCKETS
>::aligned_allocate(size_t alignment
,
140 // The alignment must be an integral power of two.
141 if (!IsPow2(alignment
))
144 // The size parameter must be an integral multiple of alignment.
145 if (size
% alignment
!= 0)
148 return allocate_impl(alignment
, size
);
151 template <size_t NUM_BUCKETS
> void FreeListHeap
<NUM_BUCKETS
>::free(void *ptr
) {
152 cpp::byte
*bytes
= static_cast<cpp::byte
*>(ptr
);
154 LIBC_ASSERT(is_valid_ptr(bytes
) && "Invalid pointer");
156 BlockType
*chunk_block
= BlockType::from_usable_space(bytes
);
157 LIBC_ASSERT(chunk_block
->next() && "sentinel last block cannot be freed");
158 LIBC_ASSERT(chunk_block
->used() && "The block is not in-use");
159 chunk_block
->mark_free();
161 // Can we combine with the left or right blocks?
162 BlockType
*prev_free
= chunk_block
->prev_free();
163 BlockType
*next
= chunk_block
->next();
165 if (prev_free
!= nullptr) {
166 // Remove from freelist and merge
167 freelist_
.remove_chunk(block_to_span(prev_free
));
168 chunk_block
= prev_free
;
169 chunk_block
->merge_next();
172 freelist_
.remove_chunk(block_to_span(next
));
173 chunk_block
->merge_next();
175 // Add back to the freelist
176 freelist_
.add_chunk(block_to_span(chunk_block
));
179 // Follows constract of the C standard realloc() function
180 // If ptr is free'd, will return nullptr.
181 template <size_t NUM_BUCKETS
>
182 void *FreeListHeap
<NUM_BUCKETS
>::realloc(void *ptr
, size_t size
) {
188 // If the pointer is nullptr, allocate a new memory.
190 return allocate(size
);
192 cpp::byte
*bytes
= static_cast<cpp::byte
*>(ptr
);
194 if (!is_valid_ptr(bytes
))
197 BlockType
*chunk_block
= BlockType::from_usable_space(bytes
);
198 if (!chunk_block
->used())
200 size_t old_size
= chunk_block
->inner_size();
202 // Do nothing and return ptr if the required memory size is smaller than
204 if (old_size
>= size
)
207 void *new_ptr
= allocate(size
);
208 // Don't invalidate ptr if allocate(size) fails to initilize the memory.
209 if (new_ptr
== nullptr)
211 LIBC_NAMESPACE::inline_memcpy(new_ptr
, ptr
, old_size
);
217 template <size_t NUM_BUCKETS
>
218 void *FreeListHeap
<NUM_BUCKETS
>::calloc(size_t num
, size_t size
) {
219 void *ptr
= allocate(num
* size
);
221 LIBC_NAMESPACE::inline_memset(ptr
, 0, num
* size
);
225 extern FreeListHeap
<> *freelist_heap
;
227 } // namespace LIBC_NAMESPACE_DECL
229 #endif // LLVM_LIBC_SRC___SUPPORT_FREELIST_HEAP_H