#define _GNU_SOURCE #include "../gc.h" #include "../platform.h" #include "../util.h" #include #include #include #include #include #include #include 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); }