summaryrefslogtreecommitdiff
path: root/src/platform/linux.c
diff options
context:
space:
mode:
authorNathan Ringo <nathan@remexre.com>2024-11-27 20:28:40 -0600
committerNathan Ringo <nathan@remexre.com>2024-11-27 20:28:40 -0600
commitb252d0de46cf12b8e2521b3eb42da9acc41a4cc1 (patch)
tree456bfed5547745edacebe8c89194c26f03a25908 /src/platform/linux.c
parent62e41dcb40d0450d493a804e7f0ac3e32f35aabf (diff)
new simpler GC
Diffstat (limited to 'src/platform/linux.c')
-rw-r--r--src/platform/linux.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/platform/linux.c b/src/platform/linux.c
index e69de29..3305869 100644
--- a/src/platform/linux.c
+++ b/src/platform/linux.c
@@ -0,0 +1,158 @@
+#define _GNU_SOURCE
+#include "../gc.h"
+#include "../platform.h"
+#include "../util.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ // As a test of the garbage collector, we're doing a bunch of random
+ // mutations.
+ gc_init();
+ srand(0);
+ gc_debug();
+
+ const struct value ZERO = {.bits = (0 << 2) | TAG_FIXNUM};
+
+ struct object *values = gc_alloc(64, 0);
+ struct object *value_slot_counts = gc_alloc(64, 0);
+ gc_root_push(&values);
+ gc_root_push(&value_slot_counts);
+
+ for (size_t i = 0; i < 1000000; i++) {
+ if ((rand() & 0b11) == 0) {
+ size_t value_slot_count = 3;
+ if (!value_slot_count)
+ value_slot_count = 1;
+ size_t untraced_slot_count = rand() & 255;
+ struct value value =
+ alloc_builtin_object(ZERO, value_slot_count, untraced_slot_count);
+ size_t i = rand() % 63;
+ gc_write_value_slot(values, i, value);
+ gc_write_value_slot(value_slot_counts, i,
+ integer_of_int(value_slot_count));
+ } else {
+ size_t i = rand() & 63;
+ struct value value = gc_read_value_slot(values, i);
+ struct value value_slot_count_value =
+ gc_read_value_slot(value_slot_counts, i);
+ if (!value.bits)
+ continue;
+ assume(get_tag(value) == TAG_BUILTIN_OBJECT);
+ assume(get_tag(value_slot_count_value) == TAG_FIXNUM);
+ struct object *obj = untag_ptr(value);
+ intptr_t value_slot_count = untag_fixnum(value_slot_count_value);
+ size_t j = rand() % value_slot_count;
+ size_t k = rand() % 63;
+ gc_write_value_slot(obj, j, gc_read_value_slot(values, k));
+ }
+ }
+
+ gc_debug();
+ return 0;
+}
+
+void panic_begin(void) {}
+noreturn void panic_end(void) { abort(); }
+
+static size_t read_cache_size(const char *path) {
+ int fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ fprintf(stderr, "open %s: %s\n", path, strerror(errno)), abort();
+
+ char buf[4096];
+ size_t len = 0;
+ for (;;) {
+ ssize_t ret = read(fd, &buf[len], sizeof(buf) - len);
+ if (ret == 0)
+ break;
+ else if (ret < 0 && errno == EINTR)
+ continue;
+ else if (ret < 0)
+ fprintf(stderr, "read %s: %s\n", path, strerror(errno)), abort();
+ else
+ len += ret;
+ }
+
+ if (close(fd))
+ fprintf(stderr, "close %s: %s\n", path, strerror(errno)), abort();
+
+ size_t i = 0, out = 0;
+ while (i < len) {
+ char ch = buf[i];
+ if ('0' <= ch && ch <= '9') {
+ out = 10 * out + (ch - '0');
+ i++;
+ } else {
+ break;
+ }
+ }
+ if (i < len) {
+ char ch = buf[i];
+ if (ch == 'K') {
+ out *= 1024;
+ i++;
+ } else if (ch == 'M') {
+ out *= 1024 * 1024;
+ i++;
+ }
+ }
+ if (i < len && buf[i] == '\n')
+ i++;
+ if (i != len) {
+ fprintf(stderr, "invalid cache size in %s: [", path);
+ for (size_t j = 0; j < len; j++) {
+ fprintf(stderr, "%s", i == j ? "|" : j ? " " : "");
+ char ch = buf[j];
+ if (' ' <= ch && ch <= '~' && ch != '\'' && ch != '\\')
+ fprintf(stderr, "'%c'", ch);
+ else if (ch == '\n')
+ fprintf(stderr, "'\\n'");
+ else if (ch == '\'')
+ fprintf(stderr, "\"'\"");
+ else if (ch == '\\')
+ fprintf(stderr, "'\\\\'");
+ else
+ fprintf(stderr, "%02hhx", buf[j]);
+ }
+ fprintf(stderr, "]\n");
+ abort();
+ }
+
+ return out;
+}
+
+size_t get_l1d_size(void) {
+ return read_cache_size("/sys/devices/system/cpu/cpu0/cache/index0/size");
+}
+
+size_t get_l3_size(void) {
+ return read_cache_size("/sys/devices/system/cpu/cpu0/cache/index2/size");
+}
+
+uintptr_t alloc_gc_region(size_t size) {
+ static uintptr_t base_ptr = 0x10000;
+ static size_t page_size = 0;
+ if (!page_size)
+ page_size = sysconf(_SC_PAGE_SIZE);
+
+ void *ptr = mmap((void *)base_ptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ptr == MAP_FAILED)
+ perror("alloc_gc_region"), exit(111);
+ base_ptr =
+ (((uintptr_t)ptr + size + page_size - 1) & ~(page_size - 1)) + page_size;
+ return (uintptr_t)ptr;
+}
+
+void clear_gc_region(uintptr_t addr, size_t size) {
+ void *new_ptr = mmap((void *)addr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (new_ptr == MAP_FAILED)
+ perror("clear_gc_region"), exit(111);
+}