-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathpz_gc_layout_bop.h
252 lines (198 loc) · 5.59 KB
/
pz_gc_layout_bop.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/*
* Plasma garbage collector memory layout - bop allocation.
* vim: ts=4 sw=4 et
*
* Copyright (C) Plasma Team
* Distributed under the terms of the MIT license, see ../LICENSE.code
*/
#ifndef PZ_GC_LAYOUT_BOP_H
#define PZ_GC_LAYOUT_BOP_H
namespace pz {
/*
* A cell in the "bag of pages" storage class.
*/
class CellPtrBOP : public CellPtr
{
private:
Block * m_block;
unsigned m_index;
constexpr CellPtrBOP() : m_block(nullptr), m_index(0) {}
int * free_list_data()
{
return reinterpret_cast<int *>(m_ptr);
}
public:
inline explicit CellPtrBOP(Block * block, unsigned index, void * ptr);
inline explicit CellPtrBOP(Block * block, unsigned index);
Block * block() const
{
return m_block;
}
unsigned index() const
{
return m_index;
}
void set_next_in_list(int next)
{
*free_list_data() = next;
}
int next_in_list()
{
return *free_list_data();
}
static constexpr CellPtrBOP Invalid()
{
return CellPtrBOP();
}
constexpr static uintptr_t Bits_Allocated = 0x01;
constexpr static uintptr_t Bits_Marked = 0x02;
inline bool is_allocated() const;
inline bool is_marked() const;
inline void allocate();
inline void unallocate();
inline void mark();
inline void unmark();
};
/*
* Blocks
*/
class Block
{
private:
struct Header {
const static size_t Block_Empty = 0;
size_t block_type_or_size;
const static int Empty_Free_List = -1;
int free_list;
// Really a bytemap.
uint8_t bitmap[GC_Cells_Per_Block];
explicit Header(size_t cell_size_)
: block_type_or_size(cell_size_)
, free_list(Empty_Free_List)
{
assert(cell_size_ >= GC_Min_Cell_Size);
}
Header() {}
};
Header m_header;
public:
static constexpr size_t Header_Bytes =
RoundUp<size_t>(sizeof(m_header), WORDSIZE_BYTES);
static constexpr size_t Payload_Bytes = GC_Block_Size - Header_Bytes;
static constexpr size_t Max_Cell_Size = Payload_Bytes / WORDSIZE_BYTES;
private:
alignas(WORDSIZE_BYTES) uint8_t m_bytes[Payload_Bytes];
public:
explicit Block(const Options & options, size_t cell_size_);
// This constructor won't touch any memory and can be used to construct
// uninitialised Blocks within Chunks.
Block() {}
Block(const Block &) = delete;
void operator=(const Block &) = delete;
// Size in words.
size_t size() const
{
assert(is_in_use());
return m_header.block_type_or_size;
}
unsigned num_cells() const
{
unsigned num = Payload_Bytes / (size() * WORDSIZE_BYTES);
assert(num <= GC_Cells_Per_Block);
return num;
}
bool is_in_payload(const void * ptr) const
{
return ptr >= m_bytes && ptr < &m_bytes[Payload_Bytes];
}
inline bool is_valid_address(const void * ptr) const;
/*
* Must also work for interior pointers.
*/
inline unsigned index_of(const void * ptr) const;
inline void ** index_to_pointer(unsigned index);
private:
/*
* TODO: Can the const and non-const versions somehow share an
* implementation? Would that actually save any code lines?
*/
const uint8_t * cell_bits(unsigned index) const
{
assert(index < num_cells());
return &(m_header.bitmap[index]);
}
uint8_t * cell_bits(unsigned index)
{
assert(index < num_cells());
return &(m_header.bitmap[index]);
}
friend CellPtrBOP;
public:
bool is_full() const
{
assert(is_in_use());
return m_header.free_list == Header::Empty_Free_List;
}
bool is_in_use() const
{
return m_header.block_type_or_size != Header::Block_Empty;
}
unsigned num_allocated();
size_t usage();
// Returns true if the entire block is empty and may be reclaimed.
bool sweep(const Options & options);
void make_unused();
CellPtrBOP allocate_cell();
#ifdef PZ_DEV
void print_usage_stats() const;
void check();
private:
bool is_in_free_list(CellPtrBOP & cell);
// Calculate the number of free cells via the free list length.
unsigned num_free();
#endif
};
static_assert(sizeof(Block) == GC_Block_Size,
"sizeof(Block) must match specified block size");
/*
* ChunkBOP is a chunk containing BIBOP style blocks of cells.
*/
class ChunkBOP : public Chunk
{
private:
uint32_t m_wilderness;
alignas(GC_Block_Size) Block m_blocks[GC_Block_Per_Chunk];
public:
ChunkBOP(Heap * heap) : Chunk(heap, CT_BOP), m_wilderness(0) {}
/*
* Get an unused block.
*
* The caller must initialise the block, this is require to ensure that
* it is properly marked as allocated.
*/
Block * allocate_block();
/*
* The size of the allocated portion of this Chunk.
*/
size_t usage();
bool is_empty() const;
/*
* If this pointer lies within the allocated part of this chunk then
* return its block.
*/
inline Block * ptr_to_block(void * ptr);
/*
* Get an block for the given size that is not full (we want to
* allocate a cell of this size).
*/
Block * get_block_for_allocation(size_t size_in_words);
void sweep(const Options & options);
#ifdef PZ_DEV
void print_usage_stats() const;
void check();
#endif
};
static_assert(sizeof(ChunkBOP) == GC_Chunk_Size,
"sizeof(ChunkBOP) must match specified chunk size");
} // namespace pz
#endif // ! PZ_GC_LAYOUT_BOP_H