Skip to content

Commit 347e2c1

Browse files
committed
Address nopsledder review comments
1 parent 847746c commit 347e2c1

File tree

10 files changed

+238
-143
lines changed

10 files changed

+238
-143
lines changed

libc/src/__support/CMakeLists.txt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,47 @@ add_header_library(
1414
libc.src.__support.CPP.type_traits
1515
)
1616

17-
add_header_library(
17+
add_object_library(
1818
freelist
1919
HDRS
2020
freelist.h
21+
SRCS
22+
freelist.cpp
2123
DEPENDS
24+
.block
2225
libc.src.__support.fixedvector
2326
libc.src.__support.CPP.array
2427
libc.src.__support.CPP.cstddef
2528
libc.src.__support.CPP.new
2629
libc.src.__support.CPP.span
2730
)
2831

32+
add_object_library(
33+
freetrie
34+
HDRS
35+
freetrie.h
36+
SRCS
37+
freetrie.cpp
38+
DEPENDS
39+
.block
40+
.freelist
41+
)
42+
43+
add_header_library(
44+
freestore
45+
HDRS
46+
freestore.h
47+
DEPENDS
48+
.freetrie
49+
)
50+
2951
add_header_library(
3052
freelist_heap
3153
HDRS
3254
freelist_heap.h
3355
DEPENDS
3456
.block
35-
.freelist
57+
.freestore
3658
libc.src.__support.CPP.cstddef
3759
libc.src.__support.CPP.array
3860
libc.src.__support.CPP.optional

libc/src/__support/freelist.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===-- Implementation for freelist ---------------------------------------===//
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.h"
10+
11+
namespace LIBC_NAMESPACE_DECL {
12+
13+
void FreeList::push(Node *node) {
14+
if (begin_) {
15+
LIBC_ASSERT(Block<>::from_usable_space(node)->outer_size() ==
16+
begin_->block()->outer_size() &&
17+
"freelist entries must have the same size");
18+
// Since the list is circular, insert the node immediately before begin_.
19+
node->prev = begin_->prev;
20+
node->next = begin_;
21+
begin_->prev->next = node;
22+
begin_->prev = node;
23+
} else {
24+
begin_ = node->prev = node->next = node;
25+
}
26+
}
27+
28+
void FreeList::remove(Node *node) {
29+
LIBC_ASSERT(begin_ && "cannot remove from empty list");
30+
if (node == node->next) {
31+
LIBC_ASSERT(node == begin_ &&
32+
"a self-referential node must be the only element");
33+
begin_ = nullptr;
34+
} else {
35+
node->prev->next = node->next;
36+
node->next->prev = node->prev;
37+
if (begin_ == node)
38+
begin_ = node->next;
39+
}
40+
}
41+
42+
} // namespace LIBC_NAMESPACE_DECL

libc/src/__support/freelist.h

Lines changed: 22 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
namespace LIBC_NAMESPACE_DECL {
1515

1616
/// A circularly-linked FIFO list storing free Blocks. All Blocks on a list
17-
/// are the same size.
17+
/// are the same size. The blocks are referenced by Nodes in the list; the list
18+
/// refers to these, but it does not own them.
1819
///
1920
/// Allocating free blocks in FIFO order maximizes the amount of time before a
2021
/// free block is reused. This in turn maximizes the number of opportunities for
@@ -25,12 +26,15 @@ class FreeList {
2526
class Node {
2627
public:
2728
/// @returns The block containing this node.
28-
Block<> *block() const {
29-
return const_cast<Block<> *>(Block<>::from_usable_space(this));
29+
LIBC_INLINE const Block<> *block() const {
30+
return Block<>::from_usable_space(this);
3031
}
3132

33+
/// @returns The block containing this node.
34+
LIBC_INLINE Block<> *block() { return Block<>::from_usable_space(this); }
35+
3236
/// @returns The inner size of blocks in the list containing this node.
33-
size_t size() const { return block()->inner_size(); }
37+
LIBC_INLINE size_t size() const { return block()->inner_size(); }
3438

3539
private:
3640
// Circularly linked pointers to adjacent nodes.
@@ -39,33 +43,39 @@ class FreeList {
3943
friend class FreeList;
4044
};
4145

42-
constexpr FreeList() : FreeList(nullptr) {}
43-
constexpr FreeList(Node *begin) : begin_(begin) {}
46+
LIBC_INLINE constexpr FreeList() : FreeList(nullptr) {}
47+
LIBC_INLINE constexpr FreeList(Node *begin) : begin_(begin) {}
4448

45-
bool empty() const { return !begin_; }
49+
LIBC_INLINE bool empty() const { return !begin_; }
4650

4751
/// @returns The inner size of blocks in the list.
48-
size_t size() const {
52+
LIBC_INLINE size_t size() const {
4953
LIBC_ASSERT(begin_ && "empty lists have no size");
5054
return begin_->size();
5155
}
5256

5357
/// @returns The first node in the list.
54-
Node *begin() { return begin_; }
58+
LIBC_INLINE Node *begin() { return begin_; }
5559

5660
/// @returns The first block in the list.
57-
Block<> *front() { return begin_->block(); }
61+
LIBC_INLINE Block<> *front() { return begin_->block(); }
5862

5963
/// Push a block to the back of the list.
6064
/// The block must be large enough to contain a node.
61-
void push(Block<> *block);
65+
LIBC_INLINE void push(Block<> *block) {
66+
LIBC_ASSERT(!block->used() &&
67+
"only free blocks can be placed on free lists");
68+
LIBC_ASSERT(block->inner_size_free() >= sizeof(FreeList) &&
69+
"block too small to accomodate free list node");
70+
push(new (block->usable_space()) Node);
71+
}
6272

6373
/// Push an already-constructed node to the back of the list.
6474
/// This allows pushing derived node types with additional data.
6575
void push(Node *node);
6676

6777
/// Pop the first node from the list.
68-
void pop();
78+
LIBC_INLINE void pop() { remove(begin_); }
6979

7080
/// Remove an arbitrary node from the list.
7181
void remove(Node *node);
@@ -74,44 +84,6 @@ class FreeList {
7484
Node *begin_;
7585
};
7686

77-
LIBC_INLINE void FreeList::push(Block<> *block) {
78-
LIBC_ASSERT(!block->used() && "only free blocks can be placed on free lists");
79-
LIBC_ASSERT(block->inner_size_free() >= sizeof(FreeList) &&
80-
"block too small to accomodate free list node");
81-
push(new (block->usable_space()) Node);
82-
}
83-
84-
LIBC_INLINE void FreeList::push(Node *node) {
85-
if (begin_) {
86-
LIBC_ASSERT(Block<>::from_usable_space(node)->outer_size() ==
87-
begin_->block()->outer_size() &&
88-
"freelist entries must have the same size");
89-
// Since the list is circular, insert the node immediately before begin_.
90-
node->prev = begin_->prev;
91-
node->next = begin_;
92-
begin_->prev->next = node;
93-
begin_->prev = node;
94-
} else {
95-
begin_ = node->prev = node->next = node;
96-
}
97-
}
98-
99-
LIBC_INLINE void FreeList::pop() { remove(begin_); }
100-
101-
LIBC_INLINE void FreeList::remove(Node *node) {
102-
LIBC_ASSERT(begin_ && "cannot remove from empty list");
103-
if (node == node->next) {
104-
LIBC_ASSERT(node == begin_ &&
105-
"a self-referential node must be the only element");
106-
begin_ = nullptr;
107-
} else {
108-
node->prev->next = node->next;
109-
node->next->prev = node->prev;
110-
if (begin_ == node)
111-
begin_ = node->next;
112-
}
113-
}
114-
11587
} // namespace LIBC_NAMESPACE_DECL
11688

11789
#endif // LLVM_LIBC_SRC___SUPPORT_FREELIST_H

libc/src/__support/freelist_heap.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ LIBC_INLINE constexpr bool IsPow2(size_t x) { return x && (x & (x - 1)) == 0; }
3232

3333
class FreeListHeap {
3434
public:
35-
using BlockType = Block<>;
36-
3735
constexpr FreeListHeap() : begin(&_end), end(&__llvm_libc_heap_limit) {}
3836

3937
constexpr FreeListHeap(span<cpp::byte> region)
@@ -90,8 +88,11 @@ LIBC_INLINE void *FreeListHeap::allocate_impl(size_t alignment, size_t size) {
9088
if (!is_initialized)
9189
init();
9290

93-
size_t request_size =
94-
alignment <= alignof(max_align_t) ? size : size + alignment - 1;
91+
size_t request_size = size;
92+
if (alignment > alignof(max_align_t)) {
93+
if (add_overflow(size, alignment - 1, size))
94+
return nullptr;
95+
}
9596

9697
Block<> *block = free_store.remove_best_fit(request_size);
9798
if (!block)
@@ -194,9 +195,12 @@ LIBC_INLINE void *FreeListHeap::realloc(void *ptr, size_t size) {
194195
}
195196

196197
LIBC_INLINE void *FreeListHeap::calloc(size_t num, size_t size) {
197-
void *ptr = allocate(num * size);
198+
size_t bytes;
199+
if (__builtin_mul_overflow(num, size, &bytes))
200+
return nullptr;
201+
void *ptr = allocate(bytes);
198202
if (ptr != nullptr)
199-
LIBC_NAMESPACE::inline_memset(ptr, 0, num * size);
203+
LIBC_NAMESPACE::inline_memset(ptr, 0, bytes);
200204
return ptr;
201205
}
202206

libc/src/__support/freestore.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ namespace LIBC_NAMESPACE_DECL {
1717
/// removed in logarithmic time.
1818
class FreeStore {
1919
public:
20+
FreeStore() = default;
21+
FreeStore(const FreeStore &other) = delete;
22+
FreeStore &operator=(const FreeStore &other) = delete;
23+
2024
/// Sets the range of possible block sizes. This can only be called when the
2125
/// trie is empty.
22-
void set_range(FreeTrie::SizeRange range) { large_trie.set_range(range); }
26+
LIBC_INLINE void set_range(FreeTrie::SizeRange range) {
27+
large_trie.set_range(range);
28+
}
2329

2430
/// Insert a free block. If the block is too small to be tracked, nothing
2531
/// happens.
@@ -42,10 +48,10 @@ class FreeStore {
4248
static constexpr size_t NUM_SMALL_SIZES =
4349
(MIN_LARGE_OUTER_SIZE - MIN_OUTER_SIZE) / ALIGNMENT;
4450

45-
static bool too_small(Block<> *block) {
51+
LIBC_INLINE static bool too_small(Block<> *block) {
4652
return block->outer_size() < MIN_OUTER_SIZE;
4753
}
48-
static bool is_small(Block<> *block) {
54+
LIBC_INLINE static bool is_small(Block<> *block) {
4955
return block->outer_size() < MIN_LARGE_OUTER_SIZE;
5056
}
5157

libc/src/__support/freetrie.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===-- Implementation for freetrie ---------------------------------------===//
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 "freetrie.h"
10+
11+
namespace LIBC_NAMESPACE_DECL {
12+
13+
void FreeTrie::remove(Node *node) {
14+
LIBC_ASSERT(!empty() && "cannot remove from empty trie");
15+
FreeList list = node;
16+
list.pop();
17+
Node *new_node = static_cast<Node *>(list.begin());
18+
if (!new_node) {
19+
// The freelist is empty. Replace the subtrie root with an arbitrary leaf.
20+
// This is legal because there is no relationship between the size of the
21+
// root and its children.
22+
Node *leaf = node;
23+
while (leaf->lower || leaf->upper)
24+
leaf = leaf->lower ? leaf->lower : leaf->upper;
25+
if (leaf == node) {
26+
// If the root is a leaf, then removing it empties the subtrie.
27+
replace_node(node, nullptr);
28+
return;
29+
}
30+
31+
replace_node(leaf, nullptr);
32+
new_node = leaf;
33+
}
34+
35+
// Copy the trie links to the new head.
36+
new_node->lower = node->lower;
37+
new_node->upper = node->upper;
38+
new_node->parent = node->parent;
39+
replace_node(node, new_node);
40+
return;
41+
}
42+
43+
void FreeTrie::replace_node(Node *node, Node *new_node) {
44+
if (node->parent) {
45+
Node *&parent_child =
46+
node->parent->lower == node ? node->parent->lower : node->parent->upper;
47+
LIBC_ASSERT(parent_child == node &&
48+
"no reference to child node found in parent");
49+
parent_child = new_node;
50+
} else {
51+
LIBC_ASSERT(root == node && "non-root node had no parent");
52+
root = new_node;
53+
}
54+
if (node->lower)
55+
node->lower->parent = new_node;
56+
if (node->upper)
57+
node->upper->parent = new_node;
58+
}
59+
60+
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)