Skip to content

Commit

Permalink
GC uses bitmap to register objects during GC, allowing safe callstack…
Browse files Browse the repository at this point in the history
… scanning. 8 byte alignment enforced. Version bump
  • Loading branch information
reginaldford committed Oct 18, 2024
1 parent 734a340 commit e9bbf46
Show file tree
Hide file tree
Showing 21 changed files with 247 additions and 213 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CC_DEBUG := clang
CC_PROF := clang
CC_UNIFIED := zig cc
# CC_UNIFIED := zig cc -target x86_64-windows-gnu #doesnt work
CFLAGS := -O2 -fno-omit-frame-pointer -fno-optimize-sibling-calls
CFLAGS := -O3
CFLAGS_DEBUG := -g
CFLAGS_PROF := -fprofile-instr-generate -fcoverage-mapping
BUILD_DIR := build
Expand Down
2 changes: 1 addition & 1 deletion scripts/benchmarks/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#!/usr/local/bin/python
import time

num_to_fib=36;
num_to_fib=34;

def fibonacci_recursive(n):
if n <= 1:
Expand Down
2 changes: 1 addition & 1 deletion sms_src/examples/benchmark.sms
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/local/bin/sms -m8
#!/usr/local/bin/sms -m1g
#Use this to benchmark something
{
let _errHandler = (x) => {
Expand Down
56 changes: 35 additions & 21 deletions src/kernel_test/chapter_0.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,40 @@
// Just report the size of structures
int chapter_0(int test) {
int current_test = 0;
TEST(printf("sizeof %s is %d\n", "enum", (int)sizeof(enum sm_object_type)));
TEST(printf("sizeof %s is %d\n", "long", (int)sizeof(long)));
TEST(printf("sizeof %s is %d\n", "long long", (int)sizeof(long long)));
TEST(printf("sizeof %s is %d\n", "f64", (int)sizeof(f64)));
TEST(printf("sizeof %s is %d\n", "int", (int)sizeof(int)));
TEST(printf("sizeof %s is %d\n", "short", (int)sizeof(int)));
TEST(printf("sizeof %s is %d\n", "sm_object*", (int)sizeof(sm_object *)));
TEST(printf("sizeof %s is %d\n", "sm_object", (int)sizeof(sm_object)));
TEST(printf("sizeof %s is %d\n", "sm_string", (int)sizeof(sm_string)));
TEST(printf("sizeof %s is %d\n", "sm_cx", (int)sizeof(sm_cx)));
TEST(printf("sizeof %s is %d\n", "sm_f64", (int)sizeof(sm_f64)));
TEST(printf("sizeof %s is %d\n", "sm_pointer", (int)sizeof(sm_pointer)));
TEST(printf("sizeof %s is %d\n", "sm_symbol", (int)sizeof(sm_symbol)));
TEST(printf("sizeof %s is %d\n", "sm_expr", (int)sizeof(sm_expr)));
TEST(printf("sizeof %s is %d\n", "sm_fun", (int)sizeof(sm_fun)));
TEST(printf("sizeof %s is %d\n", "sm_fun_param", (int)sizeof(sm_fun_param)));
TEST(printf("sizeof %s is %d\n", "sm_fun_param_obj", (int)sizeof(sm_fun_param_obj)));
TEST(printf("sizeof %s is %d\n", "sm_error", (int)sizeof(sm_error)));
TEST(printf("sizeof %s is %d\n", "sm_space", (int)sizeof(sm_space)));
TEST(printf("sizeof %s is %d\n", "sm_return", (int)sizeof(sm_return)));
TEST(printf("sizeof %s is %d\n", "sm_self", (int)sizeof(sm_self)));
// C types
TEST(printf("sizeof %s is %zu\n", "int", sizeof(int)));
TEST(printf("sizeof %s is %zu\n", "long long", sizeof(long long)));
TEST(printf("sizeof %s is %zu\n", "long", sizeof(long)));
TEST(printf("sizeof %s is %zu\n", "short", sizeof(short)));
TEST(printf("sizeof %s is %zu\n", "size_t", sizeof(size_t)));
// SMS types
TEST(printf("sizeof %s is %zu\n", "sm_array", sizeof(sm_array)));
TEST(printf("sizeof %s is %zu\n", "sm_bc_block", sizeof(sm_bc_block)));
TEST(printf("sizeof %s is %zu\n", "sm_bstack", sizeof(sm_bstack)));
TEST(printf("sizeof %s is %zu\n", "sm_cx", sizeof(sm_cx)));
TEST(printf("sizeof %s is %zu\n", "sm_env", sizeof(sm_env)));
TEST(printf("sizeof %s is %zu\n", "sm_error", sizeof(sm_error)));
TEST(printf("sizeof %s is %zu\n", "sm_expr", sizeof(sm_expr)));
TEST(printf("sizeof %s is %zu\n", "sm_f64", sizeof(sm_f64)));
TEST(printf("sizeof %s is %zu\n", "sm_fun", sizeof(sm_fun)));
TEST(printf("sizeof %s is %zu\n", "sm_fun_param", sizeof(sm_fun_param)));
TEST(printf("sizeof %s is %zu\n", "sm_fun_param_obj", sizeof(sm_fun_param_obj)));
TEST(printf("sizeof %s is %zu\n", "sm_heap", sizeof(sm_heap)));
TEST(printf("sizeof %s is %zu\n", "sm_heap_set", sizeof(sm_heap_set)));
TEST(printf("sizeof %s is %zu\n", "sm_img", sizeof(sm_img)));
TEST(printf("sizeof %s is %zu\n", "sm_local", sizeof(sm_local)));
TEST(printf("sizeof %s is %zu\n", "sm_meta", sizeof(sm_meta)));
TEST(printf("sizeof %s is %zu\n", "sm_node", sizeof(sm_node)));
TEST(printf("sizeof %s is %zu\n", "sm_object", sizeof(sm_object)));
TEST(printf("sizeof %s is %zu\n", "sm_parse_result", sizeof(sm_parse_result)));
TEST(printf("sizeof %s is %zu\n", "sm_pointer", sizeof(sm_pointer)));
TEST(printf("sizeof %s is %zu\n", "sm_return", sizeof(sm_return)));
TEST(printf("sizeof %s is %zu\n", "sm_self", sizeof(sm_self)));
TEST(printf("sizeof %s is %zu\n", "sm_space", sizeof(sm_space)));
TEST(printf("sizeof %s is %zu\n", "sm_stack", sizeof(sm_stack)));
TEST(printf("sizeof %s is %zu\n", "sm_stack_obj", sizeof(sm_stack_obj)));
TEST(printf("sizeof %s is %zu\n", "sm_string", sizeof(sm_string)));
TEST(printf("sizeof %s is %zu\n", "sm_symbol", sizeof(sm_symbol)));
TEST(printf("sizeof %s is %zu\n", "sm_ui8", sizeof(sm_ui8)));
return 0;
}
5 changes: 1 addition & 4 deletions src/kernel_test/chapter_1.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ int chapter_1(int test) {
for (int i = 0; i < 100; i++)
sm_new_cx(NULL);
printf("Running GC.\n");
if (!sms_other_heap)
sms_other_heap = sm_new_heap(sms_heap->capacity);
sm_garbage_collect(sms_heap, sms_other_heap);
sm_swap_heaps(&sms_heap, &sms_other_heap);
sm_garbage_collect();
sm_string *s = sm_new_string(0, "");
sm_parse_result pr = sm_parse_cstr(&(s->content), 0);
printf("Evaluating empty string.\n");
Expand Down
6 changes: 1 addition & 5 deletions src/kernel_test/chapter_3.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ int chapter_3(int test) {
sm_cx_let(test_cx, sm_new_symbol("ab", 2), (sm_object *)sm_new_f64(9));

sm_object *a = sm_cx_get(test_cx, sm_new_symbol("a", 1));
if (!sms_other_heap)
sms_other_heap = sm_new_heap(sms_heap->capacity);
sm_garbage_collect(sms_heap, sms_other_heap);
sm_swap_heaps(&sms_heap, &sms_other_heap);

sm_garbage_collect();
sm_cx_rm(test_cx, sm_new_symbol("b", 1));

return num_fails;
Expand Down
14 changes: 5 additions & 9 deletions src/main/engine/sm_ast_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,9 @@ inline sm_object *sm_engine_eval(sm_object *input, sm_cx *current_cx, sm_expr *s
img.sms_symbol_name_heap = *sms_symbol_name_heap; // Copy the symbol name heap

// Perform garbage collection to compact the sms_heap
sm_heap *compacted_heap = sm_new_heap(sms_heap->capacity); // Create a new heap for GC
sm_garbage_collect(sms_heap, compacted_heap); // Compact the main heap into the new one
sm_heap *compacted_heap = sm_new_heap(sms_heap->capacity, true); // Create a new heap for GC
// TODO: sm_heap_compact_to(from,to)
// sm_garbage_collect(sms_heap, compacted_heap); // Compact the main heap into the new one

// Open the file for writing
FILE *file = fopen(fname_cstr, "wb");
Expand Down Expand Up @@ -249,14 +250,9 @@ inline sm_object *sm_engine_eval(sm_object *input, sm_cx *current_cx, sm_expr *s
// Return sms_true to indicate success
return (sm_object *)sms_true;
}


case SM_GC_EXPR: {
// This fails because it changes all of the pointers before the function returns.
// sm_garbage_collect();
sm_symbol *title = sm_new_symbol("notImplemented", 14);
sm_string *message = sm_new_string(33, "_gc() command is not implemented yet");
return ((sm_object *)sm_new_error_from_expr(title, message, sme, NULL));
sm_garbage_collect();
return (sm_object *)sms_true;
break;
}
case SM_SLEEP_EXPR: {
Expand Down
106 changes: 39 additions & 67 deletions src/main/memory/sm_gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,8 @@ extern void **memory_marker1;
extern void **memory_marker2;
extern bool evaluating;

bool sm_is_sensible_object(sm_object *obj, sm_heap *location) {
if (sm_is_within_heap(obj, location) && sm_sizeof(obj) &&
((intptr_t)obj) % (sizeof(size_t) / 2) == 0) {
return true;
}
return false;
}


// Copy the object
sm_object *sm_copy(sm_object *obj) {
// if (sm_is_within_heap(obj,sms_heap))
if (obj->my_type != SM_SYMBOL_TYPE)
return sm_realloc(obj, sm_sizeof(obj));
else
Expand Down Expand Up @@ -48,9 +38,10 @@ sm_object *sm_move_to_new_heap(sm_heap *dest, sm_object *obj) {

// If obj is an sm_pointer, the object was already moved to the new heap
// Else, copy the object to the new heap and leave an sm_pointer
// Expects sm_heap_scan to be used on this to have populated map
sm_object *sm_meet_object(sm_heap *source, sm_heap *dest, sm_object *obj) {
// Only gc objects from sms_other_heap, which used to be sms_heap
if (sm_is_sensible_object(obj, source)) {
if (sm_heap_has_object(source, obj)) {
uint32_t obj_type = obj->my_type;
if (obj_type == SM_POINTER_TYPE)
return (sm_object *)(((uint64_t)dest) + (uint64_t)((sm_pointer *)obj)->address);
Expand All @@ -69,12 +60,13 @@ void sm_inflate_heap(sm_heap *from, sm_heap *to) {
while (scan_cursor < to->storage + to->used) {
sm_object *current_obj = (sm_object *)scan_cursor;
// Check sizeof, avoid an infinite loop
if (!sm_sizeof(current_obj)) {
size_t obj_size = sm_sizeof(current_obj);
if (!obj_size) {
fprintf(stderr, "Error: Cannot determine object size.\n");
exit(1);
}
// scan_cursor is not referred to for the rest of the loop
scan_cursor += sm_sizeof(current_obj);
scan_cursor += obj_size;
switch (current_obj->my_type) {
case SM_CX_TYPE: {
sm_cx *cx = (sm_cx *)current_obj;
Expand Down Expand Up @@ -172,68 +164,48 @@ void sm_inflate_heap(sm_heap *from, sm_heap *to) {
}

// Copying GC
void sm_garbage_collect(sm_heap *from_heap, sm_heap *to_heap) {
if (from_heap->used != 0) {
// Build "to" heap if necessary, same size as current
if (to_heap == NULL)
to_heap = sm_new_heap(from_heap->capacity);
// For when we recycle a heap...
to_heap->used = 0;
// We need to clear the memory in case of gc during building an object
memset(to_heap->storage, 0, to_heap->capacity);

if (evaluating) {
// Fix c callstack ptrs
memory_marker2 = __builtin_frame_address(0);
void **lowerPtr = memory_marker1 < memory_marker2 ? memory_marker1 : memory_marker2;
void **higherPtr = memory_marker1 < memory_marker2 ? memory_marker2 : memory_marker1;
// Alignment
lowerPtr -= ((intptr_t)lowerPtr) % sizeof(size_t);
for (void **ptr = lowerPtr; ptr < higherPtr; ptr++) {
// We are updating pointers in the c callstack. Watch for false alerts.
if (sm_is_sensible_object((sm_object *)*ptr, from_heap))
*ptr = (void *)sm_meet_object(from_heap, to_heap, (sm_object *)*ptr);
}
}

void sm_garbage_collect() {
if (sms_heap->used != 0) {
sm_heap_scan(sms_heap);
// Build "to" heap if necessary, same size as current
if (sms_other_heap == NULL)
sms_other_heap = sm_new_heap(sms_heap->capacity, true);
// Clear for when we recycle a heap
sm_heap_clear(sms_other_heap);
// Copy root (global context)
*sm_global_lex_stack(NULL)->top =
sm_meet_object(from_heap, to_heap, (sm_object *)*sm_global_lex_stack(NULL)->top);

// Treat parser output as root
sm_meet_object(sms_heap, sms_other_heap, (sm_object *)*sm_global_lex_stack(NULL)->top);
// Treat parser output as root or set parser output to false
if (evaluating)
sm_global_parser_output(sm_meet_object(from_heap, to_heap, sm_global_parser_output(NULL)));
// Or set parser output to false
sm_global_parser_output(
sm_meet_object(sms_heap, sms_other_heap, sm_global_parser_output(NULL)));
else
sm_global_parser_output((sm_object *)sms_false);

// Fix c callstack ptrs if evaluating
if (evaluating) {
memory_marker2 = __builtin_frame_address(0);
// Fill in the heap bitmap
void **lowerPtr = memory_marker1 < memory_marker2 ? memory_marker1 : memory_marker2;
void **higherPtr = memory_marker1 < memory_marker2 ? memory_marker2 : memory_marker1;
// Scan callstack for valid object ptrs
for (void **ptr = lowerPtr; ptr < higherPtr; ptr++)
if (sm_heap_has_object(sms_heap, *ptr))
*ptr = (void *)sm_meet_object(sms_heap, sms_other_heap, (sm_object *)*ptr);
}
// Inflate
sm_inflate_heap(from_heap, to_heap);

sm_inflate_heap(sms_heap, sms_other_heap);
// For tracking purposes
sm_gc_count(1);
}
// Report memory stat
FILE *output = sm_global_environment(NULL) && sm_global_environment(NULL)->quiet_mode == false
? stdout
: fopen("/dev/null", "w");
if (output) {
if (!evaluating)
putc('\n', output);
fprintf(output, "%s", sm_terminal_fg_color(SM_TERM_B_BLACK));
putc('(', output);
if (output == stdout)
sm_print_fancy_bytelength((long long)to_heap->used);
fprintf(output, " / ");
if (output == stdout)
sm_print_fancy_bytelength((long long)to_heap->capacity);
putc(')', output);
fprintf(output, "%s", sm_terminal_reset());
if (evaluating)
putc('\n', output);

if (output != stdout) {
fclose(output); // Close /dev/null if it was opened
// swap global heap ptrs
sm_swap_heaps(&sms_heap, &sms_other_heap);
// Report memory stat
if (sm_global_environment(NULL) && sm_global_environment(NULL)->quiet_mode == false) {
char used_str[16];
char capacity_str[16];
sm_sprint_fancy_bytelength(used_str, (uint64_t)sms_heap->used);
sm_sprint_fancy_bytelength(capacity_str, (uint64_t)sms_heap->capacity);
printf("\n%s(%s / %s)%s\n", sm_terminal_fg_color(SM_TERM_B_BLACK), used_str, capacity_str,
sm_terminal_reset());
}
}
}
2 changes: 1 addition & 1 deletion src/main/memory/sm_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sm_object *sm_copy(sm_object *obj);
/// Deep Copy the object
sm_object *sm_deep_copy(sm_object *obj);
/// Copies all objects linked to _scratch to new space. Uses "stop and copy" method.
void sm_garbage_collect(sm_heap *from, sm_heap *to);
void sm_garbage_collect();
/// Copy the object to the new heap, mutating the original object to an sm_pointer.
sm_object *sm_move_to_new_heap(sm_heap *dest, sm_object *obj);
/// Completes the sm_garbage_collect() process by 'inflating' the existing objects in the heap.
Expand Down
Loading

0 comments on commit e9bbf46

Please sign in to comment.