diff --git a/debugger.c b/debugger.c index 770693a..a222869 100644 --- a/debugger.c +++ b/debugger.c @@ -17,9 +17,10 @@ #include #include -#include "vm.h" +#include "heap.h" #include "page.h" #include "utfsjis.h" +#include "vm.h" struct variable { enum ain_data_type data_type; @@ -49,7 +50,7 @@ static struct page *get_page(int pageno) { if (!page_index_valid(pageno)) return NULL; - return vm_get_page(pageno); + return heap_get_page(pageno); } static struct variable *get_global_by_name(const char *name) diff --git a/heap.c b/heap.c new file mode 100644 index 0000000..c861d65 --- /dev/null +++ b/heap.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include "page.h" +#include "heap.h" +#include "vm.h" + +#define INITIAL_HEAP_SIZE 4096 +#define HEAP_ALLOC_STEP 4096 + +struct vm_pointer *heap; +static size_t heap_size; + +// Heap free list +// This is a list of unused indices into the 'heap' array. +static int32_t *heap_free_stack; +static size_t heap_free_ptr = 0; + +static const char *vm_ptrtype_strtab[] = { + [VM_PAGE] = "VM_PAGE", + [VM_STRING] = "VM_STRING", +}; + +static const char *vm_ptrtype_string(enum vm_pointer_type type) { + if (type < NR_VM_POINTER_TYPES) + return vm_ptrtype_strtab[type]; + return "INVALID POINTER TYPE"; +} + +void heap_init(void) +{ + heap_size = INITIAL_HEAP_SIZE; + heap = xmalloc(INITIAL_HEAP_SIZE * sizeof(struct vm_pointer)); + + heap_free_stack = xmalloc(INITIAL_HEAP_SIZE * sizeof(int32_t)); + for (size_t i = 0; i < INITIAL_HEAP_SIZE; i++) { + heap_free_stack[i] = i; + } + heap_free_ptr = 1; // global page at index 0 +} + +int32_t heap_alloc_slot(enum vm_pointer_type type) +{ + // grow heap if needed + if (heap_free_ptr >= heap_size) { + heap = xrealloc(heap, sizeof(struct vm_pointer) * (heap_size+HEAP_ALLOC_STEP)); + heap_free_stack = xrealloc(heap_free_stack, sizeof(int32_t) * (heap_size+HEAP_ALLOC_STEP)); + for (size_t i = heap_size; i < heap_size+HEAP_ALLOC_STEP; i++) { + heap_free_stack[i] = i; + } + heap_size += HEAP_ALLOC_STEP; + } + + int32_t slot = heap_free_stack[heap_free_ptr++]; + heap[slot].ref = 1; + heap[slot].type = type; +#ifdef DEBUG_HEAP + heap[slot].alloc_addr = instr_ptr; + heap[slot].ref_addr = 0; +#endif + return slot; +} + +static void heap_free_slot(int32_t slot) +{ + heap_free_stack[--heap_free_ptr] = slot; +} + +void heap_ref(int32_t slot) +{ + if (slot == -1) + return; + heap[slot].ref++; +#ifdef DEBUG_HEAP + heap[slot].ref_addr = instr_ptr; +#endif +} + +void heap_unref(int slot) +{ + if (heap[slot].ref <= 0) { +#ifdef DEBUG_HEAP + VM_ERROR("double free of slot %d (%s)\nOriginally allocated at %X\nOriginally freed at %X", + slot, vm_ptrtype_string(heap[slot].type), heap[slot].alloc_addr, heap[slot].free_addr); +#endif + VM_ERROR("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type)); + } + if (--heap[slot].ref <= 0) { +#ifdef DEBUG_HEAP + heap[slot].free_addr = instr_ptr; +#endif + switch (heap[slot].type) { + case VM_PAGE: + if (heap[slot].page) { + delete_page(heap[slot].page); + free_page(heap[slot].page); + } + break; + case VM_STRING: + free_string(heap[slot].s); + break; + } + heap_free_slot(slot); + } +} + +bool heap_index_valid(int index) +{ + return index >= 0 && (size_t)index < heap_size && heap[index].ref > 0; +} + +bool page_index_valid(int index) +{ + return heap_index_valid(index) && heap[index].type == VM_PAGE; +} + +struct page *heap_get_page(int index) +{ + if (!page_index_valid(index)) + VM_ERROR("Invalid page index: %d", index); + return heap[index].page; +} + +void heap_set_page(int slot, struct page *page) +{ + heap[slot].page = page; +} + diff --git a/heap.h b/heap.h new file mode 100644 index 0000000..c0f730d --- /dev/null +++ b/heap.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef SYSTEM4_HEAP_H +#define SYSTEM4_HEAP_H + +#include + +struct string; +struct page; + +enum vm_pointer_type { + VM_PAGE, + VM_STRING +}; +#define NR_VM_POINTER_TYPES (VM_STRING+1) + +// Heap-backed objects. Reference counted. +struct vm_pointer { + int ref; + enum vm_pointer_type type; + union { + struct string *s; + struct page *page; + }; +#ifdef DEBUG_HEAP + size_t alloc_addr; + size_t ref_addr; + size_t free_addr; +#endif +}; + +struct vm_pointer *heap; + +void heap_init(void); +int32_t heap_alloc_slot(enum vm_pointer_type type); +void heap_ref(int slot); +void heap_unref(int slot); + +struct page *heap_get_page(int index); +void heap_set_page(int slot, struct page *page); + +#endif /* SYSTEM4_HEAP_H */ diff --git a/hll/File.c b/hll/File.c index db25514..878a9e7 100644 --- a/hll/File.c +++ b/hll/File.c @@ -21,6 +21,7 @@ #include #include "hll.h" #include "../file.h" +#include "../heap.h" #include "../savedata.h" #include "../system4.h" @@ -73,7 +74,7 @@ hll_defun(Close, args) //int Read(ref struct pIVMStruct) hll_defun(Read, args) { - struct page *page = vm_get_page(args[0].i); + struct page *page = heap_get_page(args[0].i); if (page->type != STRUCT_PAGE) { VM_ERROR("File.Read of non-struct"); } diff --git a/hll/hll.h b/hll/hll.h index 86c248a..ff9e31a 100644 --- a/hll/hll.h +++ b/hll/hll.h @@ -22,6 +22,7 @@ */ #include "../vm.h" +#include "../heap.h" #include "../page.h" #include "../system4.h" diff --git a/meson.build b/meson.build index 41b37f0..c2cf9a3 100644 --- a/meson.build +++ b/meson.build @@ -13,6 +13,7 @@ xsystem4 = ['ald.c', 'cg.c', 'draw.c', 'file.c', + 'heap.c', 'ini.c', 'input.c', 'page.c', diff --git a/page.c b/page.c index 149ccf6..8a68199 100644 --- a/page.c +++ b/page.c @@ -16,9 +16,10 @@ #include "system4.h" #include "ain.h" +#include "heap.h" +#include "page.h" #include "vm.h" #include "vm_string.h" -#include "page.h" #define NR_CACHES 8 #define CACHE_SIZE 64 diff --git a/page.h b/page.h index 591f43f..f51000b 100644 --- a/page.h +++ b/page.h @@ -19,8 +19,8 @@ #include #include -#include "vm.h" #include "ain.h" +#include "vm.h" struct ain_variable; enum ain_data_type; @@ -61,16 +61,6 @@ struct page { union vm_value values[]; }; -static inline union vm_value page_get(int pageno, int varno) -{ - return heap[pageno].page->values[varno]; -} - -static inline void page_set(int pageno, int varno, union vm_value v) -{ - heap[pageno].page->values[varno] = v; -} - // variables union vm_value variable_initval(enum ain_data_type type); void variable_fini(union vm_value v, enum ain_data_type type); diff --git a/savedata.c b/savedata.c index f26caab..ecf5ae8 100644 --- a/savedata.c +++ b/savedata.c @@ -18,11 +18,12 @@ #include #include #include "system4.h" -#include "savedata.h" #include "ain.h" -#include "page.h" -#include "vm.h" #include "cJSON.h" +#include "heap.h" +#include "page.h" +#include "savedata.h" +#include "vm.h" static int current_global; @@ -44,7 +45,7 @@ static char *savedir_path(const char *filename) static cJSON *page_to_json(int index) { - struct page *page = vm_get_page(index); + struct page *page = heap_get_page(index); if (!page) return cJSON_CreateNull(); diff --git a/string.c b/string.c index d4540ad..65689d5 100644 --- a/string.c +++ b/string.c @@ -18,10 +18,11 @@ #include #include #include +#include "heap.h" #include "system4.h" +#include "utfsjis.h" #include "vm.h" #include "vm_string.h" -#include "utfsjis.h" struct string EMPTY_STRING = { .cow = true, diff --git a/vm.c b/vm.c index 350103b..326094b 100644 --- a/vm.c +++ b/vm.c @@ -26,6 +26,7 @@ #include "page.h" #include "ain.h" #include "file.h" +#include "heap.h" #include "instructions.h" #include "little_endian.h" #include "savedata.h" @@ -33,8 +34,6 @@ #include "debugger.h" #define INITIAL_STACK_SIZE 4096 -#define INITIAL_HEAP_SIZE 4096 -#define HEAP_ALLOC_STEP 4096 #define HLL_MAX_ARGS 64 // When the IP is set to VM_RETURN, the VM halts @@ -57,16 +56,6 @@ union vm_value *stack = NULL; // the stack int32_t stack_ptr = 0; // pointer to the top of the stack static size_t stack_size; // current size of the stack -// The heap -// An array of pointers to heap-allocated objects, plus reference counts. -struct vm_pointer *heap; -static size_t heap_size; - -// Heap free list -// This is a list of unused indices into the 'heap' array. -static int32_t *heap_free_stack; -static size_t heap_free_ptr = 0; - // Stack of function call frames static struct function_call call_stack[4096]; static int32_t call_stack_ptr = 0; // 0 = imaginary frame before main() @@ -102,87 +91,6 @@ static const char *current_instruction_name(void) return "UNKNOWN OPCODE"; } -int32_t heap_alloc_slot(enum vm_pointer_type type) -{ - // grow heap if needed - if (heap_free_ptr >= heap_size) { - heap = xrealloc(heap, sizeof(struct vm_pointer) * (heap_size+HEAP_ALLOC_STEP)); - heap_free_stack = xrealloc(heap_free_stack, sizeof(int32_t) * (heap_size+HEAP_ALLOC_STEP)); - for (size_t i = heap_size; i < heap_size+HEAP_ALLOC_STEP; i++) { - heap_free_stack[i] = i; - } - heap_size += HEAP_ALLOC_STEP; - } - - int32_t slot = heap_free_stack[heap_free_ptr++]; - heap[slot].ref = 1; - heap[slot].type = type; -#ifdef DEBUG_HEAP - heap[slot].alloc_addr = instr_ptr; - heap[slot].ref_addr = 0; -#endif - return slot; -} - -static void heap_free_slot(int32_t slot) -{ - heap_free_stack[--heap_free_ptr] = slot; -} - -void heap_ref(int32_t slot) -{ - if (slot == -1) - return; - heap[slot].ref++; -#ifdef DEBUG_HEAP - heap[slot].ref_addr = instr_ptr; -#endif -} - -static const char *vm_ptrtype_strtab[] = { - [VM_PAGE] = "VM_PAGE", - [VM_STRING] = "VM_STRING", -}; - -static const char *vm_ptrtype_string(enum vm_pointer_type type) { - if (type < NR_VM_POINTER_TYPES) - return vm_ptrtype_strtab[type]; - return "INVALID POINTER TYPE"; -} - -void heap_unref(int slot) -{ - if (heap[slot].ref <= 0) { -#ifdef DEBUG_HEAP - VM_ERROR("double free of slot %d (%s)\nOriginally allocd at %X\nOriginally freed at %X", - slot, vm_ptrtype_string(heap[slot].type), heap[slot].alloc_addr, heap[slot].free_addr); -#endif - VM_ERROR("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type)); - } - if (--heap[slot].ref <= 0) { -#ifdef DEBUG_HEAP - heap[slot].free_addr = instr_ptr; -#endif - switch (heap[slot].type) { - case VM_PAGE: - if (heap[slot].page) { - delete_page(heap[slot].page); - free_page(heap[slot].page); - } - break; - case VM_STRING: - free_string(heap[slot].s); - break; - } - heap_free_slot(slot); - } -} - -void heap_set_page(int slot, struct page *page) -{ - heap[slot].page = page; -} - static int local_page_slot(void) { return call_stack[call_stack_ptr-1].page_slot; @@ -238,18 +146,6 @@ static union vm_value *struct_page(void) return heap[struct_page_slot()].page->values; } -bool page_index_valid(int index) -{ - return index >= 0 && (size_t)index < heap_size && heap[index].ref > 0 && heap[index].type == VM_PAGE; -} - -struct page *vm_get_page(int index) -{ - if (!page_index_valid(index)) - VM_ERROR("Invalid page index: %d", index); - return heap[index].page; -} - static union vm_value stack_peek(int n) { return stack[stack_ptr - (1 + n)]; @@ -271,7 +167,7 @@ static union vm_value *stack_pop_var(void) { int32_t page_index = stack_pop().i; int32_t heap_index = stack_pop().i; - if (heap_index < 0 || (size_t)heap_index >= heap_size) + if (!heap_index_valid(heap_index)) VM_ERROR("Out of bounds heap index: %d/%d", heap_index, page_index); if (!heap[heap_index].page || page_index >= heap[heap_index].page->nr_vars) VM_ERROR("Out of bounds page index: %d/%d", heap_index, page_index); @@ -1705,14 +1601,7 @@ void vm_execute_ain(struct ain *program) stack = xmalloc(INITIAL_STACK_SIZE * sizeof(union vm_value)); stack_ptr = 0; - heap_size = INITIAL_HEAP_SIZE; - heap = xmalloc(INITIAL_HEAP_SIZE * sizeof(struct vm_pointer)); - - heap_free_stack = xmalloc(INITIAL_HEAP_SIZE * sizeof(int32_t)); - for (size_t i = 0; i < INITIAL_HEAP_SIZE; i++) { - heap_free_stack[i] = i; - } - heap_free_ptr = 1; // global page at index 0 + heap_init(); ain = program; diff --git a/vm.h b/vm.h index 59e1469..f7b7b61 100644 --- a/vm.h +++ b/vm.h @@ -32,32 +32,10 @@ union vm_value { float *fref; }; -enum vm_pointer_type { - VM_PAGE, - VM_STRING -}; - -#define NR_VM_POINTER_TYPES (VM_STRING+1) - struct string; struct page; enum ain_data_type; -// Heap-backed objects. Reference counted. -struct vm_pointer { - int ref; - enum vm_pointer_type type; - union { - struct string *s; - struct page *page; - }; -#ifdef DEBUG_HEAP - size_t alloc_addr; - size_t ref_addr; - size_t free_addr; -#endif -}; - struct hll_function { char *name; union vm_value (*fun)(union vm_value *_args); @@ -70,15 +48,10 @@ struct library { struct ain; -struct vm_pointer *heap; union vm_value *stack; int32_t stack_ptr; struct ain *ain; -int32_t heap_alloc_slot(enum vm_pointer_type type); -void heap_ref(int slot); -void heap_unref(int slot); - static inline union vm_value _vm_id(union vm_value v) { return v; @@ -126,12 +99,10 @@ static inline void stack_push_value(union vm_value v) #define stack_push(v) (stack_push_value(vm_value_cast(v))) union vm_value stack_pop(void); -void heap_set_page(int slot, struct page *page); - union vm_value global_get(int varno); void global_set(int varno, union vm_value val); +bool heap_index_valid(int index); bool page_index_valid(int index); -struct page *vm_get_page(int index); struct page *local_page(void); int vm_string_ref(struct string *s);