summaryrefslogtreecommitdiffstats
path: root/bytecode/heap.c
diff options
context:
space:
mode:
Diffstat (limited to 'bytecode/heap.c')
-rw-r--r--bytecode/heap.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/bytecode/heap.c b/bytecode/heap.c
new file mode 100644
index 0000000..863bf0c
--- /dev/null
+++ b/bytecode/heap.c
@@ -0,0 +1,114 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "encoding.h"
+#include "heap.h"
+#include "panic.h"
+#include "value.h"
+
+struct heap new_heap(struct val_slice gc_roots)
+{
+ return (struct heap) {
+ .buf = NULL,
+ .active = { .size = 0 },
+ .standby = { .size = 0 },
+ .free_ptr = 0,
+ .last_free = 0,
+ .gc_roots = gc_roots,
+ };
+}
+
+static bool forwarded(struct heap *h, value *p)
+{
+ if (!is_pointer(*p)) {
+ return false;
+ }
+ value *q = to_pointer(*p);
+ return h->active.buf <= q && q <= h->active.buf + h->active.size;
+}
+
+static size_t alloc_size(value *p)
+{
+ return p[-1] >> 1;
+}
+
+static value *simple_alloc(struct heap *h, int64_t size)
+{
+ h->active.buf[h->free_ptr] = size << 1;
+ value *p = h->active.buf + h->free_ptr + 1;
+ h->free_ptr += size;
+ return p;
+}
+
+static value process_gc_value(struct heap *h, value val)
+{
+ if (!is_pointer(val)) {
+ return val;
+ }
+ value *p = to_pointer(val);
+ if (forwarded(h, p)) {
+ return *p;
+ }
+
+ size_t size = alloc_size(p);
+
+ // Allocate space in the new buffer.
+ value *q = simple_alloc(h, size);
+
+ // Copy values, recursively modifying pointers.
+ for (size_t i = 0; i < size; i++) {
+ q[i] = process_gc_value(h, p[i]);
+ }
+
+ // Write forwarding pointer.
+ p[0] = from_pointer(q);
+
+ return from_pointer(q);
+}
+
+static void collect_garbage(struct heap *h)
+{
+ if (h->free_ptr < 2 * h->last_free) {
+ return;
+ }
+
+ // Swap the heaps.
+ struct val_slice temp = h->active;
+ h->active = h->standby;
+ h->standby = temp;
+
+ h->free_ptr = 0;
+
+ for (size_t i = 0; i < h->gc_roots.size; i++ ) {
+ h->gc_roots.buf[i] = process_gc_value(h, h->gc_roots.buf[i]);
+ }
+}
+
+static void more_space(struct heap *h, int64_t size_hint)
+{
+ size_t new_size = h->active.size * 2 + size_hint;
+ value *new_buf = calloc(2 * new_size, sizeof(value));
+ if (!new_buf) {
+ panicf("Out of memory!\n");
+ }
+ memcpy(new_buf, h->active.buf, h->active.size * sizeof(value));
+ free(h->buf);
+
+ h->buf = new_buf;
+ h->active = (struct val_slice) { .size = new_size, .buf = new_buf };
+ h->standby = (struct val_slice) { .size = new_size, .buf = new_buf + new_size };
+}
+
+value *alloc(struct heap *h, int64_t size)
+{
+ if (size <= 0) {
+ panicf("Alloc of zero size!\n");
+ }
+ collect_garbage(h);
+
+ if (h->free_ptr + size >= h->active.size) {
+ more_space(h, size);
+ }
+ return simple_alloc(h, size);
+}