-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpogmalloc.c
273 lines (216 loc) · 9.22 KB
/
pogmalloc.c
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
#include <memory.h>
#include "pogmalloc.h"
void pog_init(uintptr_t* heap_start, size_t heap_size,
pog_chunk* alloced_chunks_start, size_t alloced_chunks_size,
pog_chunk* freed_chunks_start, size_t freed_chunks_size,
pog_chunk* tmp_chunks_start, size_t tmp_chunks_size,
expand_function_t expand_function) {
metadata = (pog_metadata) {
.start = heap_start,
.end = (uintptr_t *) heap_start[heap_size - 1],
.expand_function = (void*)expand_function
};
alloced_chunks_list = (pog_chunk_list) {
.chunks = alloced_chunks_start,
.curr_size = 0,
.max_size = alloced_chunks_size
};
freed_chunks_list = (pog_chunk_list) {
.chunks = freed_chunks_start,
.curr_size = 0,
.max_size = freed_chunks_size
};
tmp_chunks_list = (pog_chunk_list) {
.chunks = tmp_chunks_start,
.curr_size = 0,
.max_size = tmp_chunks_size
};
pog_chunk_insert(&freed_chunks_list, (pog_chunk) {
.start = heap_start,
.size = heap_size
});
}
void *pog_malloc(size_t size_bytes) {
if (size_bytes == 0) {
return NULL;
}
const size_t size_words = (size_bytes + (sizeof(uintptr_t) - 1)) / sizeof(uintptr_t);
DEBUG("trying to allocate %zu bytes (%zu word%s) of memory\n", size_bytes, size_words, size_words > 1 ? "s" : "");
size_t first_free_chunk_idx = pog_chunk_first_free(&freed_chunks_list, size_words);
if (first_free_chunk_idx == (size_t) -1) {
TRACE("no best fit found, trying to squash freed chunks\n", NULL);
//If no best fit was found, try to compress freed chunks and try again
pog_squash();
first_free_chunk_idx = pog_chunk_first_free(&freed_chunks_list, size_words);
}
if (first_free_chunk_idx == (size_t) -1) {
TRACE("no best fit found, trying to expand heap space\n", NULL);
size_t alloced_max_size_before = alloced_chunks_list.max_size;
size_t freed_max_size_before = freed_chunks_list.max_size;
//If still no chunks was found, expand the heap space
expand_function_t fn = (expand_function_t) metadata.expand_function;
if(fn(size_words, &alloced_chunks_list.max_size, &freed_chunks_list.max_size) != 0) {
//Something went wrong while expanding the heap space :(
exit(1);
}
//If the expansion was successful, we also expect the freed size to have increased
//(increasing the size of alloced chunks is optional
assert(alloced_chunks_list.max_size >= alloced_max_size_before);
assert(freed_chunks_list.max_size > freed_max_size_before);
pog_chunk_insert(&freed_chunks_list, (pog_chunk) {
.start = (uintptr_t *) (freed_chunks_list.chunks + freed_chunks_list.max_size),
.size = size_words
});
first_free_chunk_idx = pog_chunk_first_free(&freed_chunks_list, size_words);
}
assert(first_free_chunk_idx != (size_t) -1);
pog_chunk first_free_chunk = freed_chunks_list.chunks[first_free_chunk_idx];
TRACE("found best fit at %p of %zu word%s\n", first_free_chunk.start, first_free_chunk.size, first_free_chunk.size > 1 ? "s" : "");
if (first_free_chunk.size > size_words) {
TRACE("splitting into two chunks of %zu word%s and %zu word%s\n",
size_words, size_words > 1 ? "s" : "",
first_free_chunk.size - size_words, (first_free_chunk.size - size_words) > 1 ? "s" : "");
//remove old chunk
pog_chunk_remove(&freed_chunks_list, first_free_chunk_idx);
//add new, smaller chunk to freed chunks
pog_chunk new_chunk = (pog_chunk) {
.size = first_free_chunk.size - size_words,
.start = first_free_chunk.start + size_words
};
pog_chunk_insert(&freed_chunks_list, new_chunk);
//add chunk to alloced chunks
pog_chunk alloced_chunk = (pog_chunk) {
.size = size_words,
.start = first_free_chunk.start
};
pog_chunk_insert(&alloced_chunks_list, alloced_chunk);
return alloced_chunk.start;
} else {
//remove chunk from freed chunks
pog_chunk_remove(&freed_chunks_list, first_free_chunk_idx);
//insert chunk into alloced
pog_chunk_insert(&alloced_chunks_list, first_free_chunk);
return first_free_chunk.start;
}
}
void pog_free(void *ptr) {
//get the memory chunk of the ptr
size_t idx = pog_chunk_by_ptr(&alloced_chunks_list, ptr);
assert(idx != (size_t) -1);
DEBUG("freeing %p\n", ptr);
pog_chunk freed_chunk = alloced_chunks_list.chunks[idx];
for (size_t i = 0; i < freed_chunk.size; ++i) {
freed_chunk.start[i] = 0;
}
//move the memory chunk to the freed list
pog_chunk_remove(&alloced_chunks_list, idx);
pog_chunk_insert(&freed_chunks_list, freed_chunk);
}
void pog_squash() {
DEBUG("squashing freed chunks\n", NULL);
pog_chunk_squash(&tmp_chunks_list, &freed_chunks_list);
//Override freed chunks with tmp chunks
freed_chunks_list.curr_size = 0;
for (size_t i = 0; i < tmp_chunks_list.curr_size; ++i) {
pog_chunk_insert(&freed_chunks_list, tmp_chunks_list.chunks[i]);
}
//freed_chunks_list = tmp_chunks_list;
}
void* pog_realloc(void* ptr, size_t size_bytes) {
size_t idx = pog_chunk_by_ptr(&alloced_chunks_list, ptr);
assert(idx != (size_t) -1);
const size_t size_words = (size_bytes + (sizeof(uintptr_t) - 1)) / sizeof(uintptr_t);
DEBUG("reallocating %p to %zu bytes (%zu word%s) of memory\n", ptr, size_bytes, size_words, size_words > 1 ? "s" : "");
if (size_words == alloced_chunks_list.chunks[idx].size) {
return ptr;
}
size_t copy_amount = size_words > alloced_chunks_list.chunks[idx].size ?
alloced_chunks_list.chunks[idx].size * (size_t) sizeof(uintptr_t) :
size_bytes;
void* new_ptr = pog_malloc(size_bytes);
memcpy(new_ptr, alloced_chunks_list.chunks[idx].start, copy_amount);
pog_free(ptr);
return new_ptr;
}
#if FEATURE_DEBUG
void pog_debug() {
printf("--------------------------------------------\n");
pog_chunk_debug(alloced_chunks_list, "Alloced");
printf("--------------------------------------------\n");
pog_chunk_debug(freed_chunks_list, "Freed");
printf("--------------------------------------------\n");
}
#else
void pog_debug() {
assert(0 && "FEATURE_DEBUG is not enabled");
}
#endif
#if FEATURE_GC
void pog_gc_mark_region(const uintptr_t* start, const uintptr_t* end);
int pog_gc_region_find(pog_chunk chunk, const uintptr_t* p) {
if (chunk.start <= p && p < chunk.start + chunk.size) {
size_t idx = pog_chunk_by_ptr(&tmp_chunks_list, chunk.start);
if (idx != (size_t) -1) {
pog_chunk_remove(&tmp_chunks_list, idx);
pog_gc_mark_region(chunk.start, chunk.start + chunk.size);
}
}
}
void pog_gc_mark_region(const uintptr_t* start, const uintptr_t* end) {
for (;start < end; start += 1) {
const uintptr_t *p = (const uintptr_t *) *start;
for (size_t i = 0; i < alloced_chunks_list.curr_size; ++i) {
pog_chunk chunk = alloced_chunks_list.chunks[i];
pog_gc_region_find(chunk, p);
}
}
}
void pog_gc_mark_static_region(const uintptr_t* start, const uintptr_t* end) {
for (;start < end; start += 1) {
const uintptr_t *p = (const uintptr_t *) *start;
if (p == NULL)
continue;
for (size_t i = 0; i < alloced_chunks_list.curr_size; ++i) {
pog_chunk chunk = alloced_chunks_list.chunks[i];
pog_gc_region_find(chunk, (const uintptr_t *) *p);
}
}
}
void pog_gc_collect() {
const uintptr_t *stack_start = (const uintptr_t*)__builtin_frame_address(0);
tmp_chunks_list.curr_size = 0;
for (size_t i = 0; i < alloced_chunks_list.curr_size; i++) {
pog_chunk_insert(&tmp_chunks_list, alloced_chunks_list.chunks[i]);
}
//mark unused pointers on the heap and stack
pog_gc_mark_region(stack_start, stack_base + 1);
//you don't need to have static pointers marked
if (static_mem_ptrs != NULL) {
//mark unused pointers in static memory
pog_gc_mark_region((const uintptr_t *) static_mem_ptrs, (const uintptr_t *) (static_mem_ptrs + static_mem_curr_size));
//in C, the line above works for static pointers, but in C++ it doesn't for some reason,
//and we have to deref the ptr again 🤷
pog_gc_mark_static_region((const uintptr_t *) static_mem_ptrs, (const uintptr_t *) (static_mem_ptrs + static_mem_curr_size));
}
for (size_t i = 0; i < tmp_chunks_list.curr_size; i++) {
DEBUG("%p marked unused\n", tmp_chunks_list.chunks[i].start);
pog_free(tmp_chunks_list.chunks[i].start);
}
}
void pog_gc_static_init(uintptr_t** static_mem_start) {
static_mem_ptrs = static_mem_start;
}
void pog_gc_mark_static(void* mem) {
assert(static_mem_curr_size < static_mem_max_size);
static_mem_ptrs[static_mem_curr_size] = mem;
static_mem_curr_size++;
DEBUG("marked %p as static memory\n", mem);
}
#else
void pog_gc_collect() {
assert(0 && "FEATURE_GC is not enabled");
}
void pog_gc_mark_static(void *mem) {
assert(0 && "FEATURE_GC is not enabled");
}
#endif