-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpages.c
221 lines (206 loc) · 7.15 KB
/
pages.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
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "pages.h"
// Oldspace
page_t oldspace = NULL;
int last_freed = 0;
int last_pinned = 0;
// Newspace
page_t last_page = NULL;
obj_t next_cons = NULL;
int new_pages = 0;
char newspace_bit = 1;
#define CEIL(x, y) (((x) + (y) - 1) / (y))
void allocate_page(void) {
page_t new_page = memalign(PAGE_SIZE, PAGE_SIZE);
if (!new_page) {
perror("Failed to allocate a page");
abort();
}
/* Compute the page's bitmap and data sizes */
uint32_t remaining_words = (PAGE_SIZE - ((intptr_t)new_page->rest - (intptr_t)new_page)) / WORD_BYTES;
allocations_t allocation = page_allocation_sizes(remaining_words);
/* Initialize allocation markers */
memset(new_page->rest, 0, allocation.bitmap_size * WORD_BYTES);
intptr_t data_offset = allocation.bitmap_size * WORD_BYTES;
new_page->data = (obj_t)(new_page->rest + data_offset);
new_page->allocated = new_page->data;
new_page->size = PAGE_SIZE;
new_page->pinned = false;
new_page->newspace = true;
/* Link this page into the list of pages. */
new_page->next_page = NULL;
new_page->previous_page = last_page;
if (last_page != NULL)
last_page->next_page = new_page;
last_page = new_page;
/* Set up global information */
next_cons = new_page->data;
new_pages++;
}
obj_t end_of_page(page_t page) {
return (obj_t)((intptr_t)page + page->size);
}
_Bool in_page(obj_t pointer, page_t page) {
return pointer >= page->data &&
pointer < page->allocated;
}
// Lower and upper bounds for oldspace pages
void* low = NULL;
void* high = NULL;
// A cache of frequently retrieved pages, keyed by page
struct page_entry {
void* page;
};
struct page_entry page_cache[CACHE_SIZE];
// A cache of bogus pointers, keyed by pointer >> 3
struct page_entry negative_cache[CACHE_SIZE];
void clear_page_cache(void) {
for (int i = 0; i < CACHE_SIZE; i++) {
struct page_entry entry = {NULL};
page_cache[i] = entry;
negative_cache[i] = entry;
}
}
void set_interval(void) {
low = (obj_t)((intptr_t)~1);
high = (obj_t)((intptr_t)0);
for (page_t page = oldspace; page != NULL; page = page->previous_page) {
void* start = page->data;
low = start < low ? start : low;
void* end = page->allocated;
high = end > high ? end : high;
}
}
cons_t forwarding(cons_t c) { return (cons_t)((intptr_t)(c->forward) & ~(intptr_t)1); }
_Bool in_newspace(cons_t c) { return ((intptr_t)c->forward & (intptr_t)1) == newspace_bit; }
void set_in_newspace(cons_t c, _Bool newspace) {
intptr_t base = (intptr_t)(c->forward) & ~(intptr_t)(1);
c->forward = (cons_t)(base | (newspace == newspace_bit));
}
obj_t car(cons_t c) { return c->car; }
obj_t cdr(cons_t c) { return c->cdr; }
_Bool forwarded(cons_t c) { return forwarding(c) != c; }
object_location_t start_of_object_in_page(obj_t pointer, page_t page) {
return (object_location_t){closest_previous_bit((allocation_bitmap_t)page->rest,
pointer,
page->data),
page};
}
size_t hash_page(void* x) { return ((intptr_t)x / PAGE_SIZE) % CACHE_SIZE; }
size_t hash_pointer(void* x) { return ((intptr_t)(x) >> 3) % CACHE_SIZE; }
object_location_t in_heap(obj_t pointer) {
/* Is this pointer even in bounds? */
if ((void*)pointer > high || (void*)pointer < low)
return (object_location_t){NULL, NULL}; // Quite obviously not in a page.
#ifndef GC_MOLASSES_SIMULATOR
/* Try the cached page */
struct page_entry cached_page = page_cache[hash_page(pointer)];
if (cached_page.page != NULL && in_page(pointer, cached_page.page))
return start_of_object_in_page(pointer, cached_page.page);
/* Test if this pointer is certainly out */
cached_page = negative_cache[hash_pointer(pointer)];
if (cached_page.page == (void*)((intptr_t)pointer >> 3))
return (object_location_t){NULL, NULL};
#endif
/* Now scan the pages. */
for (page_t the_page = oldspace; the_page != NULL; the_page = the_page->previous_page)
if (pointer >= (obj_t)the_page->data && pointer < end_of_page(the_page)) {
/* If we have a hit, put it in the cache for later. */
struct page_entry entry = {the_page};
page_cache[hash_page(the_page)] = entry;
if (in_page(pointer, the_page))
return start_of_object_in_page(pointer, the_page);
else
/* It won't be in another page then. */
break;
}
struct page_entry entry = {(void*)((intptr_t)pointer >> 3)};
negative_cache[hash_pointer(pointer)] = entry;
return (object_location_t){NULL, NULL};
}
cons_t make_cons(obj_t car, obj_t cdr) {
again:
if (last_page != NULL &&
(next_cons + sizeof(struct cons)) < end_of_page(last_page)) {
cons_t this = (cons_t)next_cons;
set_allocation_bit((allocation_bitmap_t)last_page->rest, next_cons, last_page->data);
next_cons += sizeof(struct cons);
this->car = car;
this->cdr = cdr;
this->forward = this;
set_in_newspace(this, true);
last_page->allocated += sizeof(struct cons);
return this;
} else {
allocate_page();
goto again;
}
}
void flip(void) {
#ifdef GC_REPORT_STATUS
puts("gc: Flipping...");
#endif
clear_page_cache();
/* clear up oldspace */
int freed_pages = 0, pinned_pages = 0;
page_t page = oldspace;
/* Flip the newspace bit to have all new objects look old. */
newspace_bit = 1 - newspace_bit;
while (page != NULL) {
page_t next_page;
next_page = page->previous_page;
if (page->pinned) {
/* Move this page to newspace. */
page->next_page = NULL;
page->previous_page = last_page;
page->pinned = false;
page->newspace = true;
last_page->next_page = page;
last_page = page;
pinned_pages++;
/* Pinned objects are still old. */
for (cons_t object = (cons_t)page->data; object < (cons_t)page->allocated; object++) {
set_in_newspace(object, false);
}
} else {
/* memset(page, 0xAA, page->size); */
free(page);
freed_pages++;
}
page = next_page;
}
#ifdef GC_REPORT_STATUS
printf("gc: %d pages freed, %d pages still pinned, %d pages to evacuate\n", freed_pages, pinned_pages, new_pages);
#endif
/* set up the next oldspace and newspace */
oldspace = last_page;
for (page_t the_page = oldspace; the_page != NULL; the_page = the_page->previous_page)
the_page->newspace = false;
last_page = NULL;
last_freed = freed_pages;
last_pinned = pinned_pages;
new_pages = 0;
allocate_page();
set_interval();
}
// Get the room (memory usage)
room_t room(void) {
long oldspace_pages = 0, oldspace_bytes = 0,
newspace_pages = 0, newspace_bytes = 0;
for (page_t page = oldspace; page != NULL; page = page->previous_page) {
oldspace_pages++;
oldspace_bytes += (intptr_t)page->allocated - (intptr_t)page->data;
}
for (page_t page = last_page; page != NULL; page = page->previous_page) {
newspace_pages++;
intptr_t this_size = (intptr_t)page->allocated - (intptr_t)page->data;
newspace_bytes += this_size;
}
room_t the_room = {oldspace_pages, oldspace_bytes,
newspace_pages, newspace_bytes,
last_freed, last_pinned};
return the_room;
}