From d1483a0c15cc202e9f49c39d2719b6c903132fd4 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Thu, 9 Jan 2025 20:12:36 -0600 Subject: [PATCH] gc: automatically run garbage collection if memory allocation fails --- Makefile | 2 +- c/backtrace.c | 64 ++++++++++++++++++-------------- c/backtrace.h | 9 +---- c/class_resolver.c | 13 ++++--- c/class_resolver.h | 5 --- c/execute.c | 19 +++++----- c/execute_helper.h | 5 +++ c/frame.c | 2 +- c/gc.c | 11 +++++- c/gc.h | 1 + c/memory_allocator.c | 63 ++++++++++++++++--------------- c/native_types.h | 33 ---------------- c/native_types_allocate.h | 48 ++++++++++++++++++++++++ classes/java/lang/Backtrace.java | 6 +++ classes/test/TestGC.java | 14 ++++++- 15 files changed, 173 insertions(+), 122 deletions(-) create mode 100644 c/native_types_allocate.h create mode 100644 classes/java/lang/Backtrace.java diff --git a/Makefile b/Makefile index da83501..04e6d61 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CC ?= gcc ARCH = -m32 CFLAGS += -Wall -Werror -Wfatal-errors -Wno-error=unused-variable -fstack-protector -std=c2x -g CFLAGS += -DDEBUG -CFLAGS += -DDEBUG_PRINT +#CFLAGS += -DDEBUG_PRINT LDFLAGS = -lm OPT ?= -O0 DEPFLAGS = -MMD -MP diff --git a/c/backtrace.c b/c/backtrace.c index 46f0686..881e5f5 100644 --- a/c/backtrace.c +++ b/c/backtrace.c @@ -3,30 +3,35 @@ #include "debug.h" #include "find_attribute.h" #include "memory_allocator.h" +#include "native_types_allocate.h" -struct backtrace * backtrace_allocate(struct vm * vm) +struct objectref * backtrace_allocate(struct vm * vm) { - struct class_entry * object_class_entry = class_resolver_lookup_class(vm->class_hash_table.length, - vm->class_hash_table.entry, - (const uint8_t *)"java/lang/Object", - 16); - debugf("object class entry: %p\n", object_class_entry); + struct class_entry * class_entry = class_resolver_lookup_class(vm->class_hash_table.length, + vm->class_hash_table.entry, + (const uint8_t *)"java/lang/Backtrace", + 19); + debugf("backtrace class entry: %p\n", class_entry); - int num_fields = (sizeof (struct backtrace)) / (sizeof (void *)); - struct objectref * objectref = obj_allocate(num_fields); - objectref->class_entry = object_class_entry; - struct backtrace * backtrace = (struct backtrace *)&objectref->aref[0]; - backtrace->num_entries = vm->frame_stack.ix; - int backtrace_entries_size = (sizeof (struct backtrace_entry)) * backtrace->num_entries; - backtrace->entry = memory_allocate(backtrace_entries_size); + assert(class_entry->instance_fields_count >= 1); + struct objectref * objectref = obj_allocate(vm, class_entry->instance_fields_count); + objectref->class_entry = class_entry; - for (int i = (vm->frame_stack.ix - 1); i >= 0; i--) { + int num_entries = vm->frame_stack.ix; + struct arrayref * arrayref = prim_array_allocate(vm, (sizeof (struct backtrace_entry)), num_entries); + arrayref->length = num_entries; + arrayref->class_entry = nullptr; + + struct backtrace_entry * backtrace_entry = (struct backtrace_entry *)&arrayref->u32[0]; + objectref->aref[0] = arrayref; + + for (int i = (num_entries - 1); i >= 0; i--) { struct frame * frame = &vm->frame_stack.frame[i]; - backtrace->entry[i].class_file = frame->class_entry->class_file; - backtrace->entry[i].method_info = frame->method_info; - backtrace->entry[i].pc = frame->pc; + backtrace_entry[i].class_file = frame->class_entry->class_file; + backtrace_entry[i].method_info = frame->method_info; + backtrace_entry[i].pc = frame->pc; } - return backtrace; + return objectref; } static int get_line_number(struct class_file * class_file, struct method_info * method_info, int pc) @@ -57,20 +62,25 @@ static int get_line_number(struct class_file * class_file, struct method_info * return line_number; } -void backtrace_print(struct backtrace * backtrace) +void backtrace_print(struct objectref * objectref) { + debugf("backtrace objectref class entry: %p\n", objectref->class_entry); + + struct arrayref * arrayref = objectref->aref[0]; + struct backtrace_entry * backtrace_entry = (struct backtrace_entry *)&arrayref->u32[0]; + prints("backtrace:\n"); - for (int i = (backtrace->num_entries - 1); i >= 0; i--) { - struct backtrace_entry * backtrace_entry = &backtrace->entry[i]; + for (int i = (arrayref->length - 1); i >= 0; i--) { + struct backtrace_entry * entry = &backtrace_entry[i]; prints(" class: "); - print__class_file__class_name(backtrace_entry->class_file); + print__class_file__class_name(entry->class_file); prints(" method: "); - print__method_info__method_name(backtrace_entry->class_file, backtrace_entry->method_info); + print__method_info__method_name(entry->class_file, entry->method_info); printc('\n'); - printf(" pc: %3d ", backtrace_entry->pc); - int line_number = get_line_number(backtrace_entry->class_file, - backtrace_entry->method_info, - backtrace_entry->pc); + printf(" pc: %3d ", entry->pc); + int line_number = get_line_number(entry->class_file, + entry->method_info, + entry->pc); printf(" line_number: %3d\n", line_number); } } diff --git a/c/backtrace.h b/c/backtrace.h index 9dbb88a..2fa4967 100644 --- a/c/backtrace.h +++ b/c/backtrace.h @@ -8,11 +8,6 @@ struct backtrace_entry { int pc; }; -struct backtrace { - int num_entries; - struct backtrace_entry * entry; -}; +struct objectref * backtrace_allocate(struct vm * vm); -struct backtrace * backtrace_allocate(struct vm * vm); - -void backtrace_print(struct backtrace * backtrace); +void backtrace_print(struct objectref * objectref); diff --git a/c/class_resolver.c b/c/class_resolver.c index b546ca8..d8f613d 100644 --- a/c/class_resolver.c +++ b/c/class_resolver.c @@ -13,6 +13,8 @@ #include "fatal.h" #include "parse_type.h" #include "find_attribute.h" +#include "frame.h" +#include "native_types_allocate.h" static int field_info_field_size(struct class_file * class_file, struct field_info * field_info) { @@ -587,8 +589,7 @@ struct method_entry class_resolver_lookup_method_from_method_name_method_descrip } } -struct objectref * class_resolver_lookup_string(int class_hash_table_length, - struct hash_table_entry * class_hash_table, +struct objectref * class_resolver_lookup_string(struct vm * vm, struct class_entry * class_entry, const int string_index) { @@ -604,14 +605,14 @@ struct objectref * class_resolver_lookup_string(int class_hash_table_length, struct constant * utf8_constant = &class_entry->class_file->constant_pool[string_constant->string.string_index - 1]; assert(utf8_constant->tag == CONSTANT_Utf8); - struct class_entry * string_class_entry = class_resolver_lookup_class(class_hash_table_length, - class_hash_table, + struct class_entry * string_class_entry = class_resolver_lookup_class(vm->class_hash_table.length, + vm->class_hash_table.entry, (const uint8_t *)"java/lang/String", 16); debugf("string class entry: %p\n", string_class_entry); int32_t count = utf8_constant->utf8.length; - struct arrayref * arrayref = prim_array_allocate(1, count); + struct arrayref * arrayref = prim_array_allocate(vm, 1, count); assert(arrayref != nullptr); arrayref->class_entry = nullptr; // byte[] arrayref->length = utf8_constant->utf8.length; @@ -621,7 +622,7 @@ struct objectref * class_resolver_lookup_string(int class_hash_table_length, assert(string_class_entry != nullptr); int fields_count = string_class_entry->instance_fields_count; - struct objectref * objectref = obj_allocate(fields_count); + struct objectref * objectref = obj_allocate(vm, fields_count); assert(objectref != nullptr); objectref->class_entry = string_class_entry; for (int i = 0; i < fields_count; i++) { diff --git a/c/class_resolver.h b/c/class_resolver.h index bd798e3..a25de62 100644 --- a/c/class_resolver.h +++ b/c/class_resolver.h @@ -86,11 +86,6 @@ struct field_entry * class_resolver_lookup_field_from_fieldref_index(int fields_ struct class_entry * class_entry, int fieldref_index); -struct objectref * class_resolver_lookup_string(int class_hash_table_length, - struct hash_table_entry * class_hash_table, - struct class_entry * class_entry, - const int string_index); - bool class_resolver_instanceof(int class_hash_table_length, struct hash_table_entry * class_hash_table, struct class_entry * origin_class_entry, diff --git a/c/execute.c b/c/execute.c index 13f6056..915199d 100644 --- a/c/execute.c +++ b/c/execute.c @@ -2,12 +2,13 @@ #include "memory_allocator.h" #include "bswap.h" #include "class_resolver.h" -#include "execute_helper.h" #include "printf.h" #include "field_size.h" #include "debug.h" #include "native_types.h" +#include "native_types_allocate.h" #include "parse_type.h" +#include "execute_helper.h" void op_aaload(struct vm * vm) { @@ -71,7 +72,7 @@ void op_anewarray(struct vm * vm, uint32_t index) index); int32_t count = operand_stack_pop_u32(vm->current_frame); - struct arrayref * arrayref = ref_array_allocate(count); + struct arrayref * arrayref = ref_array_allocate(vm, count); assert(arrayref != nullptr); arrayref->length = count; arrayref->class_entry = class_entry; @@ -1424,8 +1425,7 @@ void op_ldc(struct vm * vm, uint32_t index) int32_t value = constant->integer.bytes; operand_stack_push_u32(vm->current_frame, value); } else if (constant->tag == CONSTANT_String) { - struct objectref * objectref = class_resolver_lookup_string(vm->class_hash_table.length, - vm->class_hash_table.entry, + struct objectref * objectref = class_resolver_lookup_string(vm, vm->current_frame->class_entry, index); operand_stack_push_ref(vm->current_frame, objectref); @@ -1451,8 +1451,7 @@ void op_ldc_w(struct vm * vm, uint32_t index) int32_t value = constant->integer.bytes; operand_stack_push_u32(vm->current_frame, value); } else if (constant->tag == CONSTANT_String) { - struct objectref * objectref = class_resolver_lookup_string(vm->class_hash_table.length, - vm->class_hash_table.entry, + struct objectref * objectref = class_resolver_lookup_string(vm, vm->current_frame->class_entry, index); operand_stack_push_ref(vm->current_frame, objectref); @@ -1660,10 +1659,10 @@ static struct arrayref * _multiarray(struct vm * vm, int32_t * dims, int num_dim int32_t element_size; if (*type == 'L' || *type == '[') { element_size = (sizeof (void *)); - arrayref = ref_array_allocate(count); + arrayref = ref_array_allocate(vm, count); } else { element_size = field_size_array(*type); - arrayref = prim_array_allocate(element_size, count); + arrayref = prim_array_allocate(vm, element_size, count); } assert(arrayref != nullptr); arrayref->length = count; @@ -1733,7 +1732,7 @@ void op_new(struct vm * vm, uint32_t index) reference to the instance, is pushed onto the operand stack. */ int fields_count = class_entry->instance_fields_count; - struct objectref * objectref = obj_allocate(fields_count); + struct objectref * objectref = obj_allocate(vm, fields_count); assert(objectref != nullptr); objectref->class_entry = class_entry; for (int i = 0; i < fields_count; i++) { @@ -1748,7 +1747,7 @@ void op_newarray(struct vm * vm, uint32_t atype) { int32_t count = operand_stack_pop_u32(vm->current_frame); int32_t element_size = array_element_size(atype); - struct arrayref * arrayref = prim_array_allocate(element_size, count); + struct arrayref * arrayref = prim_array_allocate(vm, element_size, count); assert(arrayref != nullptr); arrayref->length = count; arrayref->class_entry = nullptr; diff --git a/c/execute_helper.h b/c/execute_helper.h index 929a7a9..6f9107e 100644 --- a/c/execute_helper.h +++ b/c/execute_helper.h @@ -1,3 +1,8 @@ +// declared here due to circular frame.h include in class_resolver.h +struct objectref * class_resolver_lookup_string(struct vm * vm, + struct class_entry * class_entry, + const int string_index); + static inline void class_entry_field_entry_from_constant_index(struct vm * vm, int32_t index, struct class_entry ** class_entry, diff --git a/c/frame.c b/c/frame.c index 9d99e9e..2488a9b 100644 --- a/c/frame.c +++ b/c/frame.c @@ -548,7 +548,7 @@ void vm_exception(struct vm * vm, struct objectref * objectref) } } assert(objectref->oref[0] != 0); - backtrace_print((struct backtrace *)objectref->oref[0]); + backtrace_print(objectref->oref[0]); } static void print_vm_stack(struct vm * vm) diff --git a/c/gc.c b/c/gc.c index cd217be..2c95233 100644 --- a/c/gc.c +++ b/c/gc.c @@ -145,7 +145,7 @@ void gc_mark(struct vm * vm) static void sweep_address(void * address) { struct tag * tag = (struct tag *)address; - printf("%p mark: %d\n", address, tag->mark); + //printf("%p mark: %d\n", address, tag->mark); int mark = tag->mark; tag->mark = 0; if (mark == 0) { @@ -157,3 +157,12 @@ void gc_sweep() { memory_iterate_allocated(sweep_address); } + +void memory_print_free_list(); + +void gc_run(struct vm * vm) +{ + gc_mark(vm); + gc_sweep(); + //memory_print_free_list(); +} diff --git a/c/gc.h b/c/gc.h index 9341ea7..985c3ab 100644 --- a/c/gc.h +++ b/c/gc.h @@ -4,3 +4,4 @@ void gc_mark(struct vm * vm); void gc_sweep(); +void gc_run(struct vm * vm); diff --git a/c/memory_allocator.c b/c/memory_allocator.c index d61e88f..de57e67 100644 --- a/c/memory_allocator.c +++ b/c/memory_allocator.c @@ -6,18 +6,38 @@ #define block_power (5UL) #define block_size (1UL << block_power) -static uint8_t memory[0x200]; +static uint8_t memory[0x400]; //static uint8_t memory[0x100000]; -#define free_list_length ((sizeof (memory)) / block_size) -static uint8_t free_list[free_list_length]; +static uint8_t free_list[((sizeof (memory)) / block_size)]; +const int free_list_length = (sizeof (free_list)); static uint32_t free_ix; +static_assert(((sizeof (free_list)) & (~((sizeof (free_list)) - 1))) == (sizeof (free_list))); + enum allocation_state { ALLOCATED = 0b0001, START = 0b0010, END = 0b0100, }; +void memory_print_free_list() +{ + for (int i = 0; i < free_list_length; i++) { + printf("%d ", free_list[i]); + } + printf("\n"); + for (int i = 0; i < free_list_length; i++) { + char c = (free_ix == i) ? '^' : ' '; + printf("%c ", c); + } + printf("\n"); + for (int i = 0; i < free_list_length; i++) { + char c = memory_is_allocated((void *)((i << block_power) + memory)) ? 'X' : ' '; + printf("%c ", c); + } + printf("\n"); +} + void memory_reset_free_list() { for (int i = 0; i < (sizeof (free_list)); i++) { @@ -70,14 +90,16 @@ void * memory_allocate(uint32_t size) void * mem = &memory[free_ix << block_power]; free_ix = (free_ix + blocks) & (free_list_length - 1); - printf("memory allocate: %p\n", mem); + debugf("memory allocate: %p\n", mem); + //memory_print_free_list(); return mem; } void memory_free(void * p) { - printf("memory free: %p\n", p); + debugf("memory free: %p\n", p); + //memory_print_free_list(); uint8_t * buf = (uint8_t *)p; assert(buf >= memory); @@ -119,25 +141,6 @@ void memory_iterate_allocated(memory_iterate_func_t func) } #if 0 -static void print_free_list() -{ - for (int i = 0; i < free_list_length; i++) { - printf("%d ", free_list[i]); - } - printf("\n"); - for (int i = 0; i < free_list_length; i++) { - char c = (free_ix == i) ? '^' : ' '; - printf("%c ", c); - } - printf("\n"); - for (int i = 0; i < free_list_length; i++) { - char c = memory_is_allocated((void *)((i << block_power) + memory)) ? 'X' : ' '; - printf("%c ", c); - } - printf("\n"); - -} - int main() { memory_reset_free_list(); @@ -151,33 +154,33 @@ int main() printf("p3 %p\n", p3); void * p4 = memory_allocate(90); printf("p4 %p\n", p4); - print_free_list(); + memory_print_free_list(); memory_free(p2); memory_free(p1); void * p5 = memory_allocate(256); printf("p5 %p\n", p5); - print_free_list(); + memory_print_free_list(); void * p6 = memory_allocate(128); printf("p6 %p\n", p6); memory_free(p4); - print_free_list(); + memory_print_free_list(); void * p7 = memory_allocate(128); printf("p7 %p\n", p7); - print_free_list(); + memory_print_free_list(); void * p8 = memory_allocate(128); printf("p8 %p\n", p8); - print_free_list(); + memory_print_free_list(); void * p9 = memory_allocate(128); printf("p9 %p\n", p9); - print_free_list(); + memory_print_free_list(); } #endif diff --git a/c/native_types.h b/c/native_types.h index 82b2e8b..c861d42 100644 --- a/c/native_types.h +++ b/c/native_types.h @@ -85,36 +85,3 @@ static inline int array_element_size(int atype) break; } } - -static inline struct arrayref * prim_array_allocate(int element_size, int count) -{ - int32_t size = count * element_size + (sizeof (struct arrayref)); - struct arrayref * arrayref = memory_allocate(size); - if (arrayref != nullptr) { - arrayref->tag.type = TAG_TYPE_PRIM_ARRAY; - arrayref->tag.mark = 0; - } - return arrayref; -} - -static inline struct arrayref * ref_array_allocate(int count) -{ - int32_t size = count * (sizeof (void *)) + (sizeof (struct arrayref)); - struct arrayref * arrayref = memory_allocate(size); - if (arrayref != nullptr) { - arrayref->tag.type = TAG_TYPE_REF_ARRAY; - arrayref->tag.mark = 0; - } - return arrayref; -} - -static inline struct objectref * obj_allocate(int num_fields) -{ - int32_t size = num_fields * (sizeof (void *)) + (sizeof (struct objectref)); - struct objectref * objectref = memory_allocate(size); - if (objectref != nullptr) { - objectref->tag.type = TAG_TYPE_OBJECT; - objectref->tag.mark = 0; - } - return objectref; -} diff --git a/c/native_types_allocate.h b/c/native_types_allocate.h new file mode 100644 index 0000000..da9b99a --- /dev/null +++ b/c/native_types_allocate.h @@ -0,0 +1,48 @@ +#pragma once + +#include "native_types.h" +#include "frame.h" +#include "gc.h" + +static inline void * gc_memory_allocate(struct vm * vm, int size) +{ + void * mem = memory_allocate(size); + if (mem == nullptr) { + gc_run(vm); + mem = memory_allocate(size); + } + return mem; +} + +static inline struct arrayref * prim_array_allocate(struct vm * vm, int element_size, int count) +{ + int32_t size = count * element_size + (sizeof (struct arrayref)); + struct arrayref * arrayref = gc_memory_allocate(vm, size); + if (arrayref != nullptr) { + arrayref->tag.type = TAG_TYPE_PRIM_ARRAY; + arrayref->tag.mark = 0; + } + return arrayref; +} + +static inline struct arrayref * ref_array_allocate(struct vm * vm, int count) +{ + int32_t size = count * (sizeof (void *)) + (sizeof (struct arrayref)); + struct arrayref * arrayref = gc_memory_allocate(vm, size); + if (arrayref != nullptr) { + arrayref->tag.type = TAG_TYPE_REF_ARRAY; + arrayref->tag.mark = 0; + } + return arrayref; +} + +static inline struct objectref * obj_allocate(struct vm * vm, int num_fields) +{ + int32_t size = num_fields * (sizeof (void *)) + (sizeof (struct objectref)); + struct objectref * objectref = gc_memory_allocate(vm, size); + if (objectref != nullptr) { + objectref->tag.type = TAG_TYPE_OBJECT; + objectref->tag.mark = 0; + } + return objectref; +} diff --git a/classes/java/lang/Backtrace.java b/classes/java/lang/Backtrace.java new file mode 100644 index 0000000..609c2fe --- /dev/null +++ b/classes/java/lang/Backtrace.java @@ -0,0 +1,6 @@ +class Backtrace { + int[] backtrace_entry; // is actually (struct backtrace_entry) + + private Backtrace() { + } +} diff --git a/classes/test/TestGC.java b/classes/test/TestGC.java index bf79e23..c5aed2a 100644 --- a/classes/test/TestGC.java +++ b/classes/test/TestGC.java @@ -4,7 +4,7 @@ class TestGC { static TestGC static_tgc; TestGC instance_tgc; - void main() { + static void test() { System.out.println("static new TestGC"); TestGC tgc = new TestGC(); static_tgc = tgc; @@ -13,4 +13,16 @@ class TestGC { System.out.println("stack new TestGC"); TestGC stack = new TestGC(); } + + static void test2() { + for (int i = 0; i < 100; i++) { + System.out.print("new TestGC: "); + System.out.println(i); + TestGC gc = new TestGC(); + } + } + + public static void main() { + test2(); + } }