mirror of
https://github.com/ProgramSnail/Lama.git
synced 2025-12-06 06:48:48 +00:00
Added tests + fixed bunch of bugs in GC implementation
This commit is contained in:
parent
3826c8dd32
commit
313997496d
9 changed files with 1577 additions and 1534 deletions
|
|
@ -1,15 +1,22 @@
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
all: gc_runtime.o gc.o runtime.o
|
all: gc_runtime.o gc.o runtime.o test.o
|
||||||
ar rc runtime.a gc_runtime.o runtime.o gc.o
|
ar rc runtime.a gc_runtime.o runtime.o gc.o
|
||||||
|
|
||||||
|
test.o: gc.o gc_runtime.o runtime.o virt_stack.o test_main.c test_util.s
|
||||||
|
$(CC) -o test.o -g2 -fstack-protector-all -m32 gc.o gc_runtime.o virt_stack.o runtime.o test_main.c test_util.s
|
||||||
|
|
||||||
|
virt_stack.o: virt_stack.h virt_stack.c
|
||||||
|
$(CC) -g2 -fstack-protector-all -m32 -c virt_stack.c
|
||||||
|
|
||||||
gc.o: gc.c gc.h
|
gc.o: gc.c gc.h
|
||||||
$(CC) -g -fstack-protector-all -m32 -c gc.c
|
$(CC) -g2 -fstack-protector-all -m32 -c gc.c
|
||||||
|
|
||||||
gc_runtime.o: gc_runtime.s
|
gc_runtime.o: gc_runtime.s
|
||||||
$(CC) -g -fstack-protector-all -m32 -c gc_runtime.s
|
$(CC) -g2 -fstack-protector-all -m32 -c gc_runtime.s
|
||||||
|
|
||||||
runtime.o: runtime.c runtime.h
|
runtime.o: runtime.c runtime.h
|
||||||
$(CC) -g -fstack-protector-all -m32 -c runtime.c
|
$(CC) -g2 -fstack-protector-all -m32 -c runtime.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) *.a *.o *~
|
$(RM) *.a *.o *~
|
||||||
|
|
|
||||||
251
runtime/gc.c
251
runtime/gc.c
|
|
@ -1,30 +1,46 @@
|
||||||
# define _GNU_SOURCE 1
|
# define _GNU_SOURCE 1
|
||||||
|
|
||||||
|
#include "gc.h"
|
||||||
|
#include "runtime_common.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "gc.h"
|
#include <assert.h>
|
||||||
#include "runtime_common.h"
|
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DEBUG_VERSION
|
#ifndef DEBUG_VERSION
|
||||||
static const size_t INIT_HEAP_SIZE = 1 << 18;
|
static const size_t INIT_HEAP_SIZE = 1 << 18;
|
||||||
#else
|
#else
|
||||||
static const size_t INIT_HEAP_SIZE = 8;
|
static const size_t INIT_HEAP_SIZE = 8;
|
||||||
#endif
|
#endif
|
||||||
static const size_t SIZE_T_CHARS = sizeof(size_t)/sizeof(char);
|
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
static const size_t cur_id = 1;
|
size_t cur_id = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static extra_roots_pool extra_roots;
|
static extra_roots_pool extra_roots;
|
||||||
|
|
||||||
extern size_t __gc_stack_top, __gc_stack_bottom;
|
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||||
|
#ifndef DEBUG_VERSION
|
||||||
|
extern const size_t __start_custom_data, __stop_custom_data;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
memory_chunk heap;
|
||||||
|
#else
|
||||||
static memory_chunk heap;
|
static memory_chunk heap;
|
||||||
|
#endif
|
||||||
|
|
||||||
void *alloc(size_t size) {
|
void *alloc(size_t size) {
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
++cur_id;
|
||||||
|
#endif
|
||||||
size = BYTES_TO_WORDS(size);
|
size = BYTES_TO_WORDS(size);
|
||||||
void *p = gc_alloc_on_existing_heap(size);
|
void *p = gc_alloc_on_existing_heap(size);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
|
|
@ -35,54 +51,78 @@ void* alloc(size_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *gc_alloc_on_existing_heap(size_t size) {
|
void *gc_alloc_on_existing_heap(size_t size) {
|
||||||
if (heap.current + size < heap.end) {
|
if (heap.current + size <= heap.end) {
|
||||||
void *p = (void *) heap.current;
|
void *p = (void *) heap.current;
|
||||||
heap.current += size;
|
heap.current += size;
|
||||||
|
memset(p, 0, size * sizeof(size_t));
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *gc_alloc(size_t size) {
|
void *gc_alloc(size_t size) {
|
||||||
// mark phase
|
mark_phase();
|
||||||
// TODO: add extra roots and static area scan
|
|
||||||
__gc_root_scan_stack();
|
|
||||||
|
|
||||||
// compact phase
|
compact_phase(size);
|
||||||
compact(size);
|
|
||||||
|
|
||||||
return gc_alloc_on_existing_heap(size);
|
return gc_alloc_on_existing_heap(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compact(size_t additional_size) {
|
void mark_phase(void) {
|
||||||
|
__gc_root_scan_stack();
|
||||||
|
scan_extra_roots();
|
||||||
|
#ifndef DEBUG_VERSION
|
||||||
|
scan_global_area();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void compact_phase(size_t additional_size) {
|
||||||
size_t live_size = compute_locations();
|
size_t live_size = compute_locations();
|
||||||
|
|
||||||
size_t next_heap_size = MAX(live_size * EXTRA_ROOM_HEAP_COEFFICIENT + additional_size, MINIMUM_HEAP_CAPACITY);
|
size_t next_heap_size = MAX(live_size * EXTRA_ROOM_HEAP_COEFFICIENT + additional_size, MINIMUM_HEAP_CAPACITY);
|
||||||
|
size_t next_heap_pseudo_size = MAX(next_heap_size, heap.size); // this is weird but here is why it happens:
|
||||||
|
// if we allocate too little heap right now, we may loose access to some alive objects
|
||||||
|
// however, after we physically relocate all of our objects we will shrink allocated memory if it is possible
|
||||||
|
|
||||||
memory_chunk new_memory;
|
memory_chunk old_heap = heap;
|
||||||
new_memory.begin = mremap(
|
heap.begin = mremap(
|
||||||
|
heap.begin,
|
||||||
|
WORDS_TO_BYTES(heap.size),
|
||||||
|
WORDS_TO_BYTES(next_heap_pseudo_size),
|
||||||
|
MREMAP_MAYMOVE
|
||||||
|
);
|
||||||
|
if (heap.begin == MAP_FAILED) {
|
||||||
|
perror("ERROR: compact_phase: mremap failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
heap.end = heap.begin + next_heap_pseudo_size;
|
||||||
|
heap.size = next_heap_pseudo_size;
|
||||||
|
heap.current = heap.begin + (old_heap.current - old_heap.begin);
|
||||||
|
|
||||||
|
update_references(&old_heap);
|
||||||
|
physically_relocate(&old_heap);
|
||||||
|
|
||||||
|
// shrink it if possible, otherwise this code won'test_small_tree_compaction do anything, in both cases references will remain valid
|
||||||
|
heap.begin = mremap(
|
||||||
heap.begin,
|
heap.begin,
|
||||||
WORDS_TO_BYTES(heap.size),
|
WORDS_TO_BYTES(heap.size),
|
||||||
WORDS_TO_BYTES(next_heap_size),
|
WORDS_TO_BYTES(next_heap_size),
|
||||||
MREMAP_MAYMOVE
|
0 // in this case we don't set MREMAP_MAYMOVE because it shouldn'test_small_tree_compaction move :)
|
||||||
);
|
);
|
||||||
if (new_memory.begin == MAP_FAILED) {
|
if (heap.begin == MAP_FAILED) {
|
||||||
perror ("ERROR: compact: mremap failed\n");
|
perror("ERROR: compact_phase: mremap failed\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
new_memory.end = new_memory.begin + next_heap_size;
|
heap.end = heap.begin + next_heap_size;
|
||||||
new_memory.size = next_heap_size;
|
heap.size = next_heap_size;
|
||||||
new_memory.current = new_memory.begin + live_size;
|
heap.current = heap.begin + live_size;
|
||||||
|
|
||||||
update_references(&new_memory);
|
|
||||||
physically_relocate(&new_memory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t compute_locations() {
|
size_t compute_locations() {
|
||||||
size_t *free_ptr = heap.begin;
|
size_t *free_ptr = heap.begin;
|
||||||
heap_iterator scan_iter = heap_begin_iterator();
|
heap_iterator scan_iter = heap_begin_iterator();
|
||||||
|
|
||||||
for (; heap_is_done_iterator(&scan_iter); heap_next_obj_iterator(&scan_iter)) {
|
for (; !heap_is_done_iterator(&scan_iter); heap_next_obj_iterator(&scan_iter)) {
|
||||||
void *header_ptr = scan_iter.current;
|
void *header_ptr = scan_iter.current;
|
||||||
void *obj_content = get_object_content_ptr(header_ptr);
|
void *obj_content = get_object_content_ptr(header_ptr);
|
||||||
size_t sz = BYTES_TO_WORDS(obj_size_header_ptr(header_ptr));
|
size_t sz = BYTES_TO_WORDS(obj_size_header_ptr(header_ptr));
|
||||||
|
|
@ -94,32 +134,66 @@ size_t compute_locations() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// it will return number of words
|
// it will return number of words
|
||||||
return scan_iter.current - heap.begin;
|
return free_ptr - heap.begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fix pointers on stack and in static area
|
void scan_and_fix_region(memory_chunk *old_heap, void *start, void *end) {
|
||||||
void update_references(memory_chunk *next_memory) {
|
for (size_t *ptr = (size_t *) start; ptr < (size_t *) end; ++ptr) {
|
||||||
|
size_t ptr_value = *ptr;
|
||||||
|
// this can't be expressed via is_valid_heap_pointer, because this pointer may point area corresponding to the old heap
|
||||||
|
if (is_valid_pointer((size_t *) ptr_value)
|
||||||
|
&& (size_t) old_heap->begin <= ptr_value
|
||||||
|
&& ptr_value < (size_t) old_heap->current
|
||||||
|
) {
|
||||||
|
void *obj_ptr = (void*) heap.begin + ((void *) ptr_value - (void *) old_heap->begin);
|
||||||
|
void *new_addr = (void*) heap.begin + ((void *) get_forward_address(obj_ptr) - (void *) old_heap->begin);
|
||||||
|
size_t content_offset = get_header_size(get_type_row_ptr(obj_ptr));
|
||||||
|
*(void **) ptr = new_addr + content_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_references(memory_chunk *old_heap) {
|
||||||
heap_iterator it = heap_begin_iterator();
|
heap_iterator it = heap_begin_iterator();
|
||||||
while (!heap_is_done_iterator(&it)) {
|
while (!heap_is_done_iterator(&it)) {
|
||||||
|
if (is_marked(get_object_content_ptr(it.current))) {
|
||||||
for (
|
for (
|
||||||
obj_field_iterator field_iter = ptr_field_begin_iterator(it.current);
|
obj_field_iterator field_iter = ptr_field_begin_iterator(it.current);
|
||||||
!field_is_done_iterator(&field_iter);
|
!field_is_done_iterator(&field_iter);
|
||||||
obj_next_ptr_field_iterator(&field_iter)
|
obj_next_ptr_field_iterator(&field_iter)
|
||||||
) {
|
) {
|
||||||
void *field_obj_content = *(void **) field_iter.cur_field; // TODO: create iterator method 'dereference', so that code would be a bit more readable
|
|
||||||
|
// this pointer should also be modified according to old_heap->begin
|
||||||
|
void *field_obj_content_addr = (void *) heap.begin + (*(void **) field_iter.cur_field - (void *) old_heap->begin); // TODO: vstack_create iterator method 'dereference', so that code would be a bit more readable
|
||||||
// important, we calculate new_addr very carefully here, because objects may relocate to another memory chunk
|
// important, we calculate new_addr very carefully here, because objects may relocate to another memory chunk
|
||||||
size_t *new_addr = next_memory->begin + ((size_t *) get_forward_address(field_obj_content) - heap.begin);
|
void *new_addr =
|
||||||
|
heap.begin + ((size_t *) get_forward_address(field_obj_content_addr) - (size_t *) old_heap->begin);
|
||||||
// update field reference to point to new_addr
|
// update field reference to point to new_addr
|
||||||
// since, we want fields to point to actual content, we need to add this extra content_offset
|
// since, we want fields to point to an actual content, we need to add this extra content_offset
|
||||||
// because forward_address itself is pointer to object header
|
// because forward_address itself is a pointer to the object's header
|
||||||
size_t content_offset = get_header_size(get_type_row_ptr(field_obj_content));
|
size_t content_offset = get_header_size(get_type_row_ptr(field_obj_content_addr));
|
||||||
|
if (!is_valid_heap_pointer((void *) (new_addr + content_offset))) {
|
||||||
|
fprintf(stderr, "ur: incorrect pointer assignment: on object with id %d", TO_DATA(get_object_content_ptr(it.current))->id);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
*(void **) field_iter.cur_field = new_addr + content_offset;
|
*(void **) field_iter.cur_field = new_addr + content_offset;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
heap_next_obj_iterator(&it);
|
heap_next_obj_iterator(&it);
|
||||||
}
|
}
|
||||||
|
// fix pointers from stack
|
||||||
|
scan_and_fix_region(old_heap, (void*) __gc_stack_top, (void*) __gc_stack_bottom);
|
||||||
|
|
||||||
|
// fix pointers from extra_roots
|
||||||
|
scan_and_fix_region(old_heap, (void*) extra_roots.roots, (size_t*) extra_roots.roots + extra_roots.current_free);
|
||||||
|
|
||||||
|
#ifndef DEBUG_VERSION
|
||||||
|
// fix pointers from static area
|
||||||
|
scan_and_fix_region(old_heap, (void*) &__start_custom_data, (void*) &__stop_custom_data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void physically_relocate(memory_chunk *next_memory) {
|
void physically_relocate(memory_chunk *old_heap) {
|
||||||
heap_iterator from_iter = heap_begin_iterator();
|
heap_iterator from_iter = heap_begin_iterator();
|
||||||
|
|
||||||
while (!heap_is_done_iterator(&from_iter)) {
|
while (!heap_is_done_iterator(&from_iter)) {
|
||||||
|
|
@ -127,16 +201,20 @@ void physically_relocate(memory_chunk *next_memory) {
|
||||||
if (is_marked(obj)) {
|
if (is_marked(obj)) {
|
||||||
// Move the object from its old location to its new location relative to
|
// Move the object from its old location to its new location relative to
|
||||||
// the heap's (possibly new) location, 'to' points to future object header
|
// the heap's (possibly new) location, 'to' points to future object header
|
||||||
void* to = next_memory->begin + ((size_t *) get_forward_address(obj) - heap.begin);
|
size_t *to = heap.begin + ((size_t *) get_forward_address(obj) - (size_t *) old_heap->begin);
|
||||||
memmove(to, from_iter.current, BYTES_TO_WORDS(obj_size_header_ptr(obj)));
|
memmove(to, from_iter.current, obj_size_header_ptr(from_iter.current));
|
||||||
unmark_object(to + ((size_t *) obj - from_iter.current));
|
unmark_object(get_object_content_ptr(to));
|
||||||
}
|
}
|
||||||
heap_next_obj_iterator(&from_iter);
|
heap_next_obj_iterator(&from_iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_valid_heap_pointer(const size_t *p) {
|
bool is_valid_heap_pointer(const size_t *p) {
|
||||||
return !UNBOXED(p) && (size_t) heap.begin <= (size_t) p && (size_t) p < (size_t) heap.end;
|
return !UNBOXED(p) && (size_t) heap.begin <= (size_t) p && (size_t) p < (size_t) heap.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid_pointer(const size_t *p) {
|
||||||
|
return !UNBOXED(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mark(void *obj) {
|
void mark(void *obj) {
|
||||||
|
|
@ -153,10 +231,26 @@ void mark(void *obj) {
|
||||||
!field_is_done_iterator(&ptr_field_it);
|
!field_is_done_iterator(&ptr_field_it);
|
||||||
obj_next_ptr_field_iterator(&ptr_field_it)
|
obj_next_ptr_field_iterator(&ptr_field_it)
|
||||||
) {
|
) {
|
||||||
mark(ptr_field_it.cur_field);
|
mark(* (void **) ptr_field_it.cur_field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scan_extra_roots(void) {
|
||||||
|
for (int i = 0; i < extra_roots.current_free; ++i) {
|
||||||
|
// this dereferencing is safe since runtime is pushing correct pointers into extra_roots
|
||||||
|
mark(*extra_roots.roots[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DEBUG_VERSION
|
||||||
|
void scan_global_area(void) {
|
||||||
|
// __start_custom_data is pointing to beginning of global area, thus all dereferencings are safe
|
||||||
|
for (const size_t *ptr = &__start_custom_data; ptr < &__stop_custom_data; ++ptr) {
|
||||||
|
mark(*(void **)ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void gc_test_and_mark_root(size_t **root) {
|
extern void gc_test_and_mark_root(size_t **root) {
|
||||||
mark((void *) *root);
|
mark((void *) *root);
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +272,19 @@ extern void __init (void) {
|
||||||
clear_extra_roots();
|
clear_extra_roots();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void __shutdown(void) {
|
||||||
|
munmap(heap.begin, heap.size);
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
cur_id = 0;
|
||||||
|
#endif
|
||||||
|
heap.begin = NULL;
|
||||||
|
heap.end = NULL;
|
||||||
|
heap.size = 0;
|
||||||
|
heap.current = NULL;
|
||||||
|
__gc_stack_top = 0;
|
||||||
|
__gc_stack_bottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void clear_extra_roots(void) {
|
void clear_extra_roots(void) {
|
||||||
extra_roots.current_free = 0;
|
extra_roots.current_free = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -207,18 +314,19 @@ void pop_extra_root (void ** p) {
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
|
|
||||||
void objects_snapshot(void *objects_ptr, size_t objects_cnt) {
|
size_t objects_snapshot(int *object_ids_buf, size_t object_ids_buf_size) {
|
||||||
size_t *ids_ptr = (size_t *) objects_ptr;
|
size_t *ids_ptr = (size_t *) object_ids_buf;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (
|
for (
|
||||||
heap_iterator it = heap_begin_iterator();
|
heap_iterator it = heap_begin_iterator();
|
||||||
!heap_is_done_iterator(&it) && i < objects_cnt;
|
!heap_is_done_iterator(&it) && i < object_ids_buf_size;
|
||||||
heap_next_obj_iterator(&it)
|
heap_next_obj_iterator(&it), ++i
|
||||||
) {
|
) {
|
||||||
void *header_ptr = it.current;
|
void *header_ptr = it.current;
|
||||||
data *d = TO_DATA(get_object_content_ptr(header_ptr));
|
data *d = TO_DATA(get_object_content_ptr(header_ptr));
|
||||||
ids_ptr[i] = d->id;
|
ids_ptr[i] = d->id;
|
||||||
}
|
}
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_stack(size_t stack_top, size_t stack_bottom) {
|
void set_stack(size_t stack_top, size_t stack_bottom) {
|
||||||
|
|
@ -241,7 +349,7 @@ size_t get_forward_address(void *obj) {
|
||||||
return GET_FORWARD_ADDRESS(d->forward_address);
|
return GET_FORWARD_ADDRESS(d->forward_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t set_forward_address(void *obj, size_t addr) {
|
void set_forward_address(void *obj, size_t addr) {
|
||||||
data *d = TO_DATA(obj);
|
data *d = TO_DATA(obj);
|
||||||
SET_FORWARD_ADDRESS(d->forward_address, addr);
|
SET_FORWARD_ADDRESS(d->forward_address, addr);
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +404,12 @@ lama_type get_type_header_ptr(void *ptr) {
|
||||||
case SEXP_TAG:
|
case SEXP_TAG:
|
||||||
return SEXP;
|
return SEXP;
|
||||||
default:
|
default:
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
fprintf(stderr, "ERROR: get_type_header_ptr: unknown object header, cur_id=%d", cur_id);
|
||||||
|
raise(SIGINT); // only for debug purposes
|
||||||
|
#else
|
||||||
perror("ERROR: get_type_header_ptr: unknown object header");
|
perror("ERROR: get_type_header_ptr: unknown object header");
|
||||||
|
#endif
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -319,6 +432,9 @@ size_t obj_size_header_ptr(void *ptr) {
|
||||||
return sexp_size(len);
|
return sexp_size(len);
|
||||||
default:
|
default:
|
||||||
perror("ERROR: obj_size_header_ptr: unknown object header");
|
perror("ERROR: obj_size_header_ptr: unknown object header");
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
raise(SIGINT); // only for debug purposes
|
||||||
|
#endif
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -336,18 +452,22 @@ size_t closure_size(size_t sz) {
|
||||||
return get_header_size(CLOSURE) + MEMBER_SIZE * sz;
|
return get_header_size(CLOSURE) + MEMBER_SIZE * sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sexp_size(size_t sz) {
|
size_t sexp_size(size_t members) {
|
||||||
return get_header_size(SEXP) + MEMBER_SIZE * sz;
|
return get_header_size(SEXP) + MEMBER_SIZE * members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
obj_field_iterator field_begin_iterator(void *obj) {
|
obj_field_iterator field_begin_iterator(void *obj) {
|
||||||
lama_type type = get_type_row_ptr(obj);
|
lama_type type = get_type_header_ptr(obj);
|
||||||
obj_field_iterator it = { .type=type, .obj_ptr=get_obj_header_ptr(obj, type), .cur_field=obj };
|
obj_field_iterator it = {.type=type, .obj_ptr=obj, .cur_field=get_object_content_ptr(obj)};
|
||||||
// since string doesn't have any actual fields we set cur_field to the end of object
|
// since string doesn't have any actual fields we set cur_field to the end of object
|
||||||
if (type == STRING) {
|
if (type == STRING) {
|
||||||
it.cur_field = get_end_of_obj(it.obj_ptr);
|
it.cur_field = get_end_of_obj(it.obj_ptr);
|
||||||
}
|
}
|
||||||
|
// skip first member which is basically pointer to the code
|
||||||
|
if (type == CLOSURE) {
|
||||||
|
it.cur_field += MEMBER_SIZE;
|
||||||
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,7 +477,7 @@ obj_field_iterator ptr_field_begin_iterator(void *obj) {
|
||||||
if (field_is_done_iterator(&it)) {
|
if (field_is_done_iterator(&it)) {
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
if (is_valid_heap_pointer(it.cur_field)) {
|
if (is_valid_pointer(*(size_t **) it.cur_field)) {
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
obj_next_ptr_field_iterator(&it);
|
obj_next_ptr_field_iterator(&it);
|
||||||
|
|
@ -371,7 +491,7 @@ void obj_next_field_iterator(obj_field_iterator *it) {
|
||||||
void obj_next_ptr_field_iterator(obj_field_iterator *it) {
|
void obj_next_ptr_field_iterator(obj_field_iterator *it) {
|
||||||
do {
|
do {
|
||||||
obj_next_field_iterator(it);
|
obj_next_field_iterator(it);
|
||||||
} while (!field_is_done_iterator(it) && !is_valid_heap_pointer(it->cur_field));
|
} while (!field_is_done_iterator(it) && !is_valid_pointer(*(size_t **) it->cur_field));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool field_is_done_iterator(obj_field_iterator *it) {
|
bool field_is_done_iterator(obj_field_iterator *it) {
|
||||||
|
|
@ -401,7 +521,42 @@ size_t get_header_size(lama_type type) {
|
||||||
return SEXP_ONLY_HEADER_SZ + DATA_HEADER_SZ;
|
return SEXP_ONLY_HEADER_SZ + DATA_HEADER_SZ;
|
||||||
default:
|
default:
|
||||||
perror("ERROR: get_header_size: unknown object type");
|
perror("ERROR: get_header_size: unknown object type");
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
raise(SIGINT); // only for debug purposes
|
||||||
|
#endif
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *alloc_string(int len) {
|
||||||
|
data *obj = alloc(string_size(len));
|
||||||
|
obj->data_header = STRING_TAG | (len << 3);
|
||||||
|
obj->id = cur_id;
|
||||||
|
obj->forward_address = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *alloc_array(int len) {
|
||||||
|
data *obj = alloc(array_size(len));
|
||||||
|
obj->data_header = ARRAY_TAG | (len << 3);
|
||||||
|
obj->id = cur_id;
|
||||||
|
obj->forward_address = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *alloc_sexp(int members) {
|
||||||
|
sexp *obj = alloc(sexp_size(members));
|
||||||
|
obj->sexp_header = obj->contents.data_header = SEXP_TAG | (members << 3);
|
||||||
|
obj->contents.id = cur_id;
|
||||||
|
obj->contents.forward_address = 0;
|
||||||
|
obj->tag = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *alloc_closure(int captured) {
|
||||||
|
data *obj = alloc(closure_size(captured));
|
||||||
|
obj->data_header = CLOSURE_TAG | (captured << 3);
|
||||||
|
obj->id = cur_id;
|
||||||
|
obj->forward_address = 0;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
|
||||||
58
runtime/gc.h
58
runtime/gc.h
|
|
@ -1,22 +1,26 @@
|
||||||
#ifndef __LAMA_GC__
|
#ifndef __LAMA_GC__
|
||||||
#define __LAMA_GC__
|
#define __LAMA_GC__
|
||||||
|
|
||||||
|
// this flag makes GC behavior a bit different for testing purposes.
|
||||||
|
#define DEBUG_VERSION
|
||||||
|
|
||||||
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
# define GET_MARK_BIT(x) (((int) (x)) & 1)
|
||||||
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
# define SET_MARK_BIT(x) (x = (((int) (x)) | 1))
|
||||||
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
# define RESET_MARK_BIT(x) (x = (((int) (x)) & (~1)))
|
||||||
# define GET_FORWARD_ADDRESS(x) (((int) (x)) & (~1)) // since last bit is used as mark-bit and due to correct alignment we can expect that last bit doesn't influence address (it should always be zero)
|
# define GET_FORWARD_ADDRESS(x) (((size_t) (x)) & (~1)) // since last bit is used as mark-bit and due to correct alignment we can expect that last bit doesn'test_small_tree_compaction influence address (it should always be zero)
|
||||||
# define SET_FORWARD_ADDRESS(x, addr) (x = (((int) (x)) | ((int) (addr))))
|
# define SET_FORWARD_ADDRESS(x, addr) (x = (GET_MARK_BIT(x) | ((int) (addr))))
|
||||||
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
# define EXTRA_ROOM_HEAP_COEFFICIENT 2 // TODO: tune this parameter
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
# define MINIMUM_HEAP_CAPACITY (1<<8) // TODO: tune this parameter
|
# define MINIMUM_HEAP_CAPACITY (1<<8) // TODO: tune this parameter
|
||||||
|
#else
|
||||||
|
# define MINIMUM_HEAP_CAPACITY (1<<8) // TODO: tune this parameter
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "runtime_common.h"
|
#include "runtime_common.h"
|
||||||
|
|
||||||
// this flag makes GC behavior a bit different for testing purposes.
|
|
||||||
#define DEBUG_VERSION
|
|
||||||
|
|
||||||
typedef enum { ARRAY, CLOSURE, STRING, SEXP } lama_type;
|
typedef enum { ARRAY, CLOSURE, STRING, SEXP } lama_type;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -53,27 +57,37 @@ void* gc_alloc(size_t);
|
||||||
// takes number of words as a parameter
|
// takes number of words as a parameter
|
||||||
void *gc_alloc_on_existing_heap(size_t);
|
void *gc_alloc_on_existing_heap(size_t);
|
||||||
|
|
||||||
void collect();
|
// specific for mark-and-compact_phase gc
|
||||||
|
|
||||||
// specific for mark-and-compact gc
|
|
||||||
void mark(void *obj);
|
void mark(void *obj);
|
||||||
|
void mark_phase(void);
|
||||||
|
// written in ASM, scans stack for pointers to the heap and starts marking process
|
||||||
|
extern void __gc_root_scan_stack(void); // TODO: write without ASM, since it is absolutely not necessary
|
||||||
|
// marks each pointer from extra roots
|
||||||
|
void scan_extra_roots(void);
|
||||||
|
#ifndef DEBUG_VERSION
|
||||||
|
// marks each valid pointer from global area
|
||||||
|
void scan_global_area(void);
|
||||||
|
#endif
|
||||||
// takes number of words that are required to be allocated somewhere on the heap
|
// takes number of words that are required to be allocated somewhere on the heap
|
||||||
void compact(size_t additional_size);
|
void compact_phase(size_t additional_size);
|
||||||
// specific for Lisp-2 algorithm
|
// specific for Lisp-2 algorithm
|
||||||
size_t compute_locations();
|
size_t compute_locations();
|
||||||
void update_references(memory_chunk *);
|
void update_references(memory_chunk *);
|
||||||
void physically_relocate(memory_chunk *);
|
void physically_relocate(memory_chunk *);
|
||||||
|
|
||||||
|
|
||||||
// written in ASM
|
// written in ASM
|
||||||
extern void __gc_init (void); // MANDATORY TO CALL BEFORE ANY INTERACTION WITH GC (apart from cases where we are working with virtual stack as happens in tests)
|
extern void __gc_init (void); // MANDATORY TO CALL BEFORE ANY INTERACTION WITH GC (apart from cases where we are working with virtual stack as happens in tests)
|
||||||
|
extern void __init (void); // should be called before interaction with GC in case of using in tests with virtual stack, otherwise it is automatically invoked by __gc_init
|
||||||
|
extern void __shutdown (void); // mostly useful for tests but basically you want to call this in case you want to deallocate all object allocated via GC
|
||||||
|
// written in ASM
|
||||||
extern void __pre_gc (void);
|
extern void __pre_gc (void);
|
||||||
|
// written in ASM
|
||||||
extern void __post_gc (void);
|
extern void __post_gc (void);
|
||||||
extern void __gc_root_scan_stack(void); // TODO: write without ASM, since it is absolutely not necessary
|
|
||||||
|
|
||||||
// invoked from ASM
|
// invoked from ASM
|
||||||
extern void gc_test_and_mark_root(size_t ** root);
|
extern void gc_test_and_mark_root(size_t ** root);
|
||||||
inline bool is_valid_heap_pointer(const size_t *);
|
inline bool is_valid_heap_pointer(const size_t *);
|
||||||
|
inline bool is_valid_pointer(const size_t *);
|
||||||
|
|
||||||
void clear_extra_roots (void);
|
void clear_extra_roots (void);
|
||||||
|
|
||||||
|
|
@ -86,8 +100,11 @@ void pop_extra_root (void ** p);
|
||||||
|
|
||||||
#ifdef DEBUG_VERSION
|
#ifdef DEBUG_VERSION
|
||||||
|
|
||||||
// test-only function, these pointer parameters are just a fancy way to return two values at a time
|
// makes a snapshot of current objects in heap (both alive and dead), writes these ids to object_ids_buf,
|
||||||
void objects_snapshot(void *objects_ptr, size_t objects_cnt);
|
// returns number of ids dumped
|
||||||
|
// object_ids_buf is pointer to area preallocated by user for dumping ids of objects in heap
|
||||||
|
// object_ids_buf_size is in WORDS, NOT BYTES
|
||||||
|
size_t objects_snapshot(int *object_ids_buf, size_t object_ids_buf_size);
|
||||||
|
|
||||||
// essential function to mock program stack
|
// essential function to mock program stack
|
||||||
void set_stack(size_t stack_top, size_t stack_bottom);
|
void set_stack(size_t stack_top, size_t stack_bottom);
|
||||||
|
|
@ -100,11 +117,15 @@ void set_extra_roots(size_t extra_roots_size, void** extra_roots_ptr);
|
||||||
|
|
||||||
/* Utility functions */
|
/* Utility functions */
|
||||||
|
|
||||||
|
// accepts pointer to the start of the region and to the end of the region
|
||||||
|
// scans it and if it meets a pointer, it should be modified in according to forward address
|
||||||
|
void scan_and_fix_region(memory_chunk *old_heap, void *start, void *end);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, returns forwarding address
|
// takes a pointer to an object content as an argument, returns forwarding address
|
||||||
size_t get_forward_address(void *obj);
|
size_t get_forward_address(void *obj);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, sets forwarding address to value 'addr'
|
// takes a pointer to an object content as an argument, sets forwarding address to value 'addr'
|
||||||
size_t set_forward_address(void *obj, size_t addr);
|
void set_forward_address(void *obj, size_t addr);
|
||||||
|
|
||||||
// takes a pointer to an object content as an argument, returns whether this object was marked as live
|
// takes a pointer to an object content as an argument, returns whether this object was marked as live
|
||||||
bool is_marked(void *obj);
|
bool is_marked(void *obj);
|
||||||
|
|
@ -139,8 +160,8 @@ size_t string_size(size_t len);
|
||||||
// TODO: ask if it is actually so? number of captured elements is actually sz-1 and 1 extra word is code ptr?
|
// TODO: ask if it is actually so? number of captured elements is actually sz-1 and 1 extra word is code ptr?
|
||||||
// returns number of bytes that are required to allocate closure with 'sz-1' captured values (header included)
|
// returns number of bytes that are required to allocate closure with 'sz-1' captured values (header included)
|
||||||
size_t closure_size(size_t sz);
|
size_t closure_size(size_t sz);
|
||||||
// returns number of bytes that are required to allocate s-expression with 'sz' fields (header included)
|
// returns number of bytes that are required to allocate s-expression with 'members' fields (header included)
|
||||||
size_t sexp_size(size_t sz);
|
size_t sexp_size(size_t members);
|
||||||
|
|
||||||
// returns an iterator over object fields, obj is ptr to object header
|
// returns an iterator over object fields, obj is ptr to object header
|
||||||
// (in case of s-exp, it is mandatory that obj ptr is very beginning of the object,
|
// (in case of s-exp, it is mandatory that obj ptr is very beginning of the object,
|
||||||
|
|
@ -161,4 +182,9 @@ void* get_obj_header_ptr(void *ptr, lama_type type);
|
||||||
void* get_object_content_ptr(void *header_ptr);
|
void* get_object_content_ptr(void *header_ptr);
|
||||||
void* get_end_of_obj(void *header_ptr);
|
void* get_end_of_obj(void *header_ptr);
|
||||||
|
|
||||||
|
void *alloc_string(int len);
|
||||||
|
void *alloc_array(int len);
|
||||||
|
void *alloc_sexp(int members);
|
||||||
|
void *alloc_closure(int captured);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -17,7 +17,8 @@ __gc_stack_top: .long 0
|
||||||
.extern gc_test_and_copy_root
|
.extern gc_test_and_copy_root
|
||||||
.text
|
.text
|
||||||
|
|
||||||
__gc_init: movl %ebp, __gc_stack_bottom
|
__gc_init:
|
||||||
|
movl %ebp, __gc_stack_bottom
|
||||||
addl $4, __gc_stack_bottom
|
addl $4, __gc_stack_bottom
|
||||||
call __init
|
call __init
|
||||||
ret
|
ret
|
||||||
|
|
@ -60,7 +61,9 @@ __gc_root_scan_stack:
|
||||||
pushl %ebx
|
pushl %ebx
|
||||||
pushl %edx
|
pushl %edx
|
||||||
movl __gc_stack_top, %eax
|
movl __gc_stack_top, %eax
|
||||||
jmp next
|
// jmp next
|
||||||
|
cmpl %eax, __gc_stack_bottom
|
||||||
|
jb returnn
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
movl (%eax), %ebx
|
movl (%eax), %ebx
|
||||||
|
|
@ -106,7 +109,7 @@ gc_run_t:
|
||||||
next:
|
next:
|
||||||
addl $4, %eax
|
addl $4, %eax
|
||||||
cmpl %eax, __gc_stack_bottom
|
cmpl %eax, __gc_stack_bottom
|
||||||
jne loop
|
jnb loop
|
||||||
returnn:
|
returnn:
|
||||||
movl $0, %eax
|
movl $0, %eax
|
||||||
popl %edx
|
popl %edx
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,6 @@
|
||||||
|
|
||||||
//# define DEBUG_PRINT 1
|
//# define DEBUG_PRINT 1
|
||||||
|
|
||||||
/* GC memory_chunk structure and data; declared here in order to allow debug print */
|
|
||||||
static memory_chunk from_space;
|
|
||||||
static memory_chunk to_space;
|
|
||||||
size_t *current;
|
|
||||||
/* end */
|
|
||||||
|
|
||||||
# ifdef __ENABLE_GC__
|
# ifdef __ENABLE_GC__
|
||||||
|
|
||||||
/* GC extern invariant for built-in functions */
|
/* GC extern invariant for built-in functions */
|
||||||
|
|
@ -66,7 +60,7 @@ void Lassert (void *f, char *s, ...) {
|
||||||
do if (!UNBOXED(x) && TAG(TO_DATA(x)->data_header) \
|
do if (!UNBOXED(x) && TAG(TO_DATA(x)->data_header) \
|
||||||
!= STRING_TAG) failure ("string value expected in %s\n", memo); while (0)
|
!= STRING_TAG) failure ("string value expected in %s\n", memo); while (0)
|
||||||
|
|
||||||
extern void* alloc (size_t);
|
//extern void* alloc (size_t);
|
||||||
extern void* Bsexp (int n, ...);
|
extern void* Bsexp (int n, ...);
|
||||||
extern int LtagHash (char*);
|
extern int LtagHash (char*);
|
||||||
|
|
||||||
|
|
@ -224,12 +218,8 @@ int Ls__Infix_37 (void *p, void *q) {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int Llength (void *p) {
|
extern int Llength (void *p) {
|
||||||
data *a = (data*) BOX (NULL);
|
|
||||||
|
|
||||||
ASSERT_BOXED(".length", p);
|
ASSERT_BOXED(".length", p);
|
||||||
|
return BOX(obj_size_row_ptr(p));
|
||||||
a = TO_DATA(p);
|
|
||||||
return BOX(LEN(a->data_header));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'";
|
static char* chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'";
|
||||||
|
|
@ -373,7 +363,6 @@ static void printValue (void *p) {
|
||||||
for (i = 0; i < LEN(a->data_header); i++) {
|
for (i = 0; i < LEN(a->data_header); i++) {
|
||||||
if (i) printValue ((void*)((int*) a->contents)[i]);
|
if (i) printValue ((void*)((int*) a->contents)[i]);
|
||||||
else printStringBuf ("0x%x", (void*)((int*) a->contents)[i]);
|
else printStringBuf ("0x%x", (void*)((int*) a->contents)[i]);
|
||||||
|
|
||||||
if (i != LEN(a->data_header) - 1) printStringBuf (", ");
|
if (i != LEN(a->data_header) - 1) printStringBuf (", ");
|
||||||
}
|
}
|
||||||
printStringBuf (">");
|
printStringBuf (">");
|
||||||
|
|
@ -397,9 +386,7 @@ static void printValue (void *p) {
|
||||||
|
|
||||||
if (strcmp (tag, "cons") == 0) {
|
if (strcmp (tag, "cons") == 0) {
|
||||||
data *b = a;
|
data *b = a;
|
||||||
|
|
||||||
printStringBuf ("{");
|
printStringBuf ("{");
|
||||||
|
|
||||||
while (LEN(a->data_header)) {
|
while (LEN(a->data_header)) {
|
||||||
printValue ((void*)((int*) b->contents)[0]);
|
printValue ((void*)((int*) b->contents)[0]);
|
||||||
b = (data*)((int*) b->contents)[1];
|
b = (data*)((int*) b->contents)[1];
|
||||||
|
|
@ -409,7 +396,6 @@ static void printValue (void *p) {
|
||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
|
|
||||||
printStringBuf ("}");
|
printStringBuf ("}");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -513,11 +499,9 @@ extern void* Lsubstring (void *subj, int p, int l) {
|
||||||
__pre_gc ();
|
__pre_gc ();
|
||||||
|
|
||||||
push_extra_root (&subj);
|
push_extra_root (&subj);
|
||||||
r = (data*) alloc (ll + 1 + sizeof (int));
|
r = (data*) alloc_string(ll);
|
||||||
pop_extra_root (&subj);
|
pop_extra_root (&subj);
|
||||||
|
|
||||||
r->data_header = STRING_TAG | (ll << 3);
|
|
||||||
|
|
||||||
strncpy (r->contents, (char*) subj + pp, ll);
|
strncpy (r->contents, (char*) subj + pp, ll);
|
||||||
|
|
||||||
__post_gc ();
|
__post_gc ();
|
||||||
|
|
@ -532,7 +516,7 @@ extern void* Lsubstring (void *subj, int p, int l) {
|
||||||
extern struct re_pattern_buffer *Lregexp (char *regexp) {
|
extern struct re_pattern_buffer *Lregexp (char *regexp) {
|
||||||
regex_t *b = (regex_t*) malloc (sizeof (regex_t));
|
regex_t *b = (regex_t*) malloc (sizeof (regex_t));
|
||||||
|
|
||||||
/* printf ("regexp: %s,\t%x\n", regexp, b); */
|
/* printf ("regexp: %s,\test_small_tree_compaction%x\n", regexp, b); */
|
||||||
|
|
||||||
memset (b, 0, sizeof (regex_t));
|
memset (b, 0, sizeof (regex_t));
|
||||||
|
|
||||||
|
|
@ -597,13 +581,21 @@ void *Lclone (void *p) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARRAY_TAG:
|
case ARRAY_TAG:
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
print_indent ();
|
||||||
|
printf ("Lclone: array &p=%p p=%p ebp=%p\n", &p, p, ebp); fflush (stdout);
|
||||||
|
#endif
|
||||||
|
obj = (data *) alloc_array(l);
|
||||||
|
memcpy(obj, TO_DATA(p), array_size(l));
|
||||||
|
res = (void *) obj->contents;
|
||||||
|
break;
|
||||||
case CLOSURE_TAG:
|
case CLOSURE_TAG:
|
||||||
#ifdef DEBUG_PRINT
|
#ifdef DEBUG_PRINT
|
||||||
print_indent ();
|
print_indent ();
|
||||||
printf ("Lclone: closure or array &p=%p p=%p ebp=%p\n", &p, p, ebp); fflush (stdout);
|
printf ("Lclone: closure &p=%p p=%p ebp=%p\n", &p, p, ebp); fflush (stdout);
|
||||||
#endif
|
#endif
|
||||||
obj = (data*) alloc (sizeof(int) * (l+1));
|
obj = (data *) alloc_closure(l);
|
||||||
memcpy (obj, TO_DATA(p), sizeof(int) * (l+1));
|
memcpy (obj, TO_DATA(p), closure_size(l));
|
||||||
res = (void*) (obj->contents);
|
res = (void*) (obj->contents);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -611,8 +603,8 @@ void *Lclone (void *p) {
|
||||||
#ifdef DEBUG_PRINT
|
#ifdef DEBUG_PRINT
|
||||||
print_indent (); printf ("Lclone: sexp\n"); fflush (stdout);
|
print_indent (); printf ("Lclone: sexp\n"); fflush (stdout);
|
||||||
#endif
|
#endif
|
||||||
sobj = (sexp*) alloc (sizeof(int) * (l+2));
|
sobj = (sexp*) alloc_sexp(l);
|
||||||
memcpy (sobj, TO_SEXP(p), sizeof(int) * (l+2));
|
memcpy (sobj, TO_SEXP(p), sexp_size(l));
|
||||||
res = (void*) sobj->contents.contents;
|
res = (void*) sobj->contents.contents;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -806,9 +798,7 @@ extern void* LmakeArray (int length) {
|
||||||
__pre_gc ();
|
__pre_gc ();
|
||||||
|
|
||||||
n = UNBOX(length);
|
n = UNBOX(length);
|
||||||
r = (data*) alloc (sizeof(int) * (n+1));
|
r = (data*) alloc_array(n);
|
||||||
|
|
||||||
r->data_header = ARRAY_TAG | (n << 3);
|
|
||||||
|
|
||||||
p = (int*) r->contents;
|
p = (int*) r->contents;
|
||||||
while (n--) *p++ = BOX(0);
|
while (n--) *p++ = BOX(0);
|
||||||
|
|
@ -826,9 +816,7 @@ extern void* LmakeString (int length) {
|
||||||
|
|
||||||
__pre_gc () ;
|
__pre_gc () ;
|
||||||
|
|
||||||
r = (data*) alloc (n + 1 + sizeof (int));
|
r = (data*) alloc_string(n); // '\0' in the end of the string is taken into account
|
||||||
|
|
||||||
r->data_header = STRING_TAG | (n << 3);
|
|
||||||
|
|
||||||
__post_gc();
|
__post_gc();
|
||||||
|
|
||||||
|
|
@ -837,7 +825,7 @@ extern void* LmakeString (int length) {
|
||||||
|
|
||||||
extern void* Bstring (void *p) {
|
extern void* Bstring (void *p) {
|
||||||
int n = strlen (p);
|
int n = strlen (p);
|
||||||
data *s = NULL;
|
void *s = NULL;
|
||||||
|
|
||||||
__pre_gc ();
|
__pre_gc ();
|
||||||
#ifdef DEBUG_PRINT
|
#ifdef DEBUG_PRINT
|
||||||
|
|
@ -852,7 +840,7 @@ extern void* Bstring (void *p) {
|
||||||
print_indent ();
|
print_indent ();
|
||||||
printf ("\tBstring: call strncpy: %p %p %p %i\n", &p, p, s, n); fflush(stdout);
|
printf ("\tBstring: call strncpy: %p %p %p %i\n", &p, p, s, n); fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
strncpy ((char*)s, p, n + 1);
|
strncpy ((char*)s, p, n + 1); // +1 because of '\0' in the end of C-strings
|
||||||
#ifdef DEBUG_PRINT
|
#ifdef DEBUG_PRINT
|
||||||
print_indent ();
|
print_indent ();
|
||||||
printf ("\tBstring: ends\n"); fflush(stdout);
|
printf ("\tBstring: ends\n"); fflush(stdout);
|
||||||
|
|
@ -921,9 +909,8 @@ extern void* Bclosure (int bn, void *entry, ...) {
|
||||||
push_extra_root ((void**)argss);
|
push_extra_root ((void**)argss);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = (data*) alloc (sizeof(int) * (n+2));
|
r = (data*) alloc_closure(n + 1);
|
||||||
|
|
||||||
r->data_header = CLOSURE_TAG | ((n + 1) << 3);
|
|
||||||
((void**) r->contents)[0] = entry;
|
((void**) r->contents)[0] = entry;
|
||||||
|
|
||||||
va_start(args, entry);
|
va_start(args, entry);
|
||||||
|
|
@ -963,9 +950,7 @@ extern void* Barray (int bn, ...) {
|
||||||
indent++; print_indent ();
|
indent++; print_indent ();
|
||||||
printf ("Barray: create n = %d\n", n); fflush(stdout);
|
printf ("Barray: create n = %d\n", n); fflush(stdout);
|
||||||
#endif
|
#endif
|
||||||
r = (data*) alloc (sizeof(int) * (n+1));
|
r = (data*) alloc_array(n);
|
||||||
|
|
||||||
r->data_header = ARRAY_TAG | (n << 3);
|
|
||||||
|
|
||||||
va_start(args, bn);
|
va_start(args, bn);
|
||||||
|
|
||||||
|
|
@ -983,6 +968,10 @@ extern void* Barray (int bn, ...) {
|
||||||
return r->contents;
|
return r->contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
extern memory_chunk heap;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern void* Bsexp (int bn, ...) {
|
extern void* Bsexp (int bn, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -998,17 +987,22 @@ extern void* Bsexp (int bn, ...) {
|
||||||
indent++; print_indent ();
|
indent++; print_indent ();
|
||||||
printf("Bsexp: allocate %zu!\n",sizeof(int) * (n+1)); fflush (stdout);
|
printf("Bsexp: allocate %zu!\n",sizeof(int) * (n+1)); fflush (stdout);
|
||||||
#endif
|
#endif
|
||||||
r = (sexp*) alloc (sizeof(int) * (n+1));
|
int fields_cnt = n - 1;
|
||||||
|
r = (sexp*) alloc_sexp(fields_cnt);
|
||||||
d = &(r->contents);
|
d = &(r->contents);
|
||||||
r->tag = 0;
|
r->tag = 0;
|
||||||
|
|
||||||
d->data_header = SEXP_TAG | ((n - 1) << 3);
|
|
||||||
|
|
||||||
va_start(args, bn);
|
va_start(args, bn);
|
||||||
|
|
||||||
for (i=0; i<n-1; i++) {
|
for (i=0; i<n-1; i++) {
|
||||||
ai = va_arg(args, int);
|
ai = va_arg(args, int);
|
||||||
|
|
||||||
|
#ifdef DEBUG_VERSION
|
||||||
|
if (!UNBOXED(ai)) {
|
||||||
|
assert(is_valid_heap_pointer((size_t *) ai));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
p = (size_t*) ai;
|
p = (size_t*) ai;
|
||||||
((int*)d->contents)[i] = ai;
|
((int*)d->contents)[i] = ai;
|
||||||
}
|
}
|
||||||
|
|
@ -1039,7 +1033,7 @@ extern int Btag (void *d, int t, int n) {
|
||||||
return BOX(TAG(r->data_header) == SEXP_TAG && TO_SEXP(d)->tag == UNBOX(t) && LEN(r->data_header) == UNBOX(n));
|
return BOX(TAG(r->data_header) == SEXP_TAG && TO_SEXP(d)->tag == UNBOX(t) && LEN(r->data_header) == UNBOX(n));
|
||||||
#else
|
#else
|
||||||
return BOX(TAG(r->data_header) == SEXP_TAG &&
|
return BOX(TAG(r->data_header) == SEXP_TAG &&
|
||||||
GET_SEXP_TAG(TO_SEXP(d)->data_header) == UNBOX(t) && LEN(r->data_header) == UNBOX(n));
|
GET_SEXP_TAG(TO_SEXP(d)->data_header) == UNBOX(test_small_tree_compaction) && LEN(r->data_header) == UNBOX(n));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1164,15 +1158,13 @@ extern void* /*Lstrcat*/ Li__Infix_4343 (void *a, void *b) {
|
||||||
|
|
||||||
push_extra_root (&a);
|
push_extra_root (&a);
|
||||||
push_extra_root (&b);
|
push_extra_root (&b);
|
||||||
d = (data *) alloc (sizeof(int) + LEN(da->data_header) + LEN(db->data_header) + 1);
|
d = alloc_string(LEN(da->data_header) + LEN(db->data_header));
|
||||||
pop_extra_root (&b);
|
pop_extra_root (&b);
|
||||||
pop_extra_root (&a);
|
pop_extra_root (&a);
|
||||||
|
|
||||||
da = TO_DATA(a);
|
da = TO_DATA(a);
|
||||||
db = TO_DATA(b);
|
db = TO_DATA(b);
|
||||||
|
|
||||||
d->data_header = STRING_TAG | ((LEN(da->data_header) + LEN(db->data_header)) << 3);
|
|
||||||
|
|
||||||
strncpy (d->contents , da->contents, LEN(da->data_header));
|
strncpy (d->contents , da->contents, LEN(da->data_header));
|
||||||
strncpy (d->contents + LEN(da->data_header), db->contents, LEN(db->data_header));
|
strncpy (d->contents + LEN(da->data_header), db->contents, LEN(db->data_header));
|
||||||
|
|
||||||
|
|
@ -1214,7 +1206,7 @@ extern void* LgetEnv (char *var) {
|
||||||
void *s;
|
void *s;
|
||||||
|
|
||||||
if (e == NULL)
|
if (e == NULL)
|
||||||
return BOX(0); // TODO add (void*) cast?
|
return (void*) BOX(0); // TODO add (void*) cast?
|
||||||
|
|
||||||
__pre_gc ();
|
__pre_gc ();
|
||||||
|
|
||||||
|
|
@ -1346,9 +1338,9 @@ extern void* Lfexists (char *fname) {
|
||||||
|
|
||||||
f = fopen (fname, "r");
|
f = fopen (fname, "r");
|
||||||
|
|
||||||
if (f) return BOX(1); // (void*) cast?
|
if (f) return (void*) BOX(1); // (void*) cast?
|
||||||
|
|
||||||
return BOX(0); // (void*) cast?
|
return (void*) BOX(0); // (void*) cast?
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void* Lfst (void *v) {
|
extern void* Lfst (void *v) {
|
||||||
|
|
@ -1457,520 +1449,3 @@ extern void LenableGC () {
|
||||||
extern void LdisableGC () {
|
extern void LdisableGC () {
|
||||||
enable_GC = 0;
|
enable_GC = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern const size_t __start_custom_data, __stop_custom_data;
|
|
||||||
|
|
||||||
# ifdef __ENABLE_GC__
|
|
||||||
|
|
||||||
extern void __gc_init ();
|
|
||||||
|
|
||||||
# else
|
|
||||||
|
|
||||||
# define __gc_init __gc_init_subst
|
|
||||||
void __gc_init_subst () {}
|
|
||||||
|
|
||||||
# endif
|
|
||||||
|
|
||||||
extern void __gc_root_scan_stack ();
|
|
||||||
|
|
||||||
/* ======================================== */
|
|
||||||
/* Mark-and-copy */
|
|
||||||
/* ======================================== */
|
|
||||||
|
|
||||||
//static size_t SPACE_SIZE = 16;
|
|
||||||
static size_t SPACE_SIZE = 256 * 1024 * 1024;
|
|
||||||
// static size_t SPACE_SIZE = 128;
|
|
||||||
// static size_t SPACE_SIZE = 1024 * 1024;
|
|
||||||
|
|
||||||
static int free_pool (memory_chunk * p) {
|
|
||||||
size_t *a = p->begin, b = p->size;
|
|
||||||
p->begin = NULL;
|
|
||||||
p->size = 0;
|
|
||||||
p->end = NULL;
|
|
||||||
p->current = NULL;
|
|
||||||
return munmap((void *)a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_to_space (int flag) {
|
|
||||||
size_t space_size = 0;
|
|
||||||
if (flag) SPACE_SIZE = SPACE_SIZE << 1;
|
|
||||||
space_size = SPACE_SIZE * sizeof(size_t);
|
|
||||||
to_space.begin = mmap (NULL, space_size, PROT_READ | PROT_WRITE,
|
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
|
||||||
if (to_space.begin == MAP_FAILED) {
|
|
||||||
perror ("EROOR: init_to_space: mmap failed\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
to_space.current = to_space.begin;
|
|
||||||
to_space.end = to_space.begin + SPACE_SIZE;
|
|
||||||
to_space.size = SPACE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gc_swap_spaces (void) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent++; print_indent ();
|
|
||||||
printf ("gc_swap_spaces\n"); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
free_pool (&from_space);
|
|
||||||
from_space.begin = to_space.begin;
|
|
||||||
from_space.current = current;
|
|
||||||
from_space.end = to_space.end;
|
|
||||||
from_space.size = to_space.size;
|
|
||||||
to_space.begin = NULL;
|
|
||||||
to_space.current = NULL;
|
|
||||||
to_space.end = NULL;
|
|
||||||
to_space.size = 0;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
# define IS_VALID_HEAP_POINTER(p)\
|
|
||||||
(!UNBOXED(p) && \
|
|
||||||
(size_t)from_space.begin <= (size_t)p && \
|
|
||||||
(size_t)from_space.end > (size_t)p)
|
|
||||||
|
|
||||||
# define IN_PASSIVE_SPACE(p) \
|
|
||||||
((size_t)to_space.begin <= (size_t)p && \
|
|
||||||
(size_t)to_space.end > (size_t)p)
|
|
||||||
|
|
||||||
# define IS_FORWARD_PTR(p) \
|
|
||||||
(!UNBOXED(p) && IN_PASSIVE_SPACE(p))
|
|
||||||
|
|
||||||
int is_valid_heap_pointer (void *p) {
|
|
||||||
return IS_VALID_HEAP_POINTER(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern size_t * gc_copy (size_t *obj);
|
|
||||||
|
|
||||||
static void copy_elements (size_t *where, size_t *from, int len) {
|
|
||||||
int i = 0;
|
|
||||||
void * p = NULL;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent++; print_indent ();
|
|
||||||
printf ("copy_elements: start; len = %d\n", len); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
size_t elem = from[i];
|
|
||||||
if (!IS_VALID_HEAP_POINTER(elem)) {
|
|
||||||
*where = elem;
|
|
||||||
where++;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("copy_elements: copy NON ptr: %zu %p \n", elem, elem); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("copy_elements: fix element: %p -> %p\n", elem, *where);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
p = gc_copy ((size_t*) elem);
|
|
||||||
*where = (size_t) p;
|
|
||||||
where ++;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("copy_elements: iteration end: where = %p, *where = %p, i = %d, \
|
|
||||||
len = %d\n", where, *where, i, len); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("\tcopy_elements: end\n"); fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static int extend_spaces (void) {
|
|
||||||
void *p = (void *) BOX (NULL);
|
|
||||||
size_t old_space_size = SPACE_SIZE * sizeof(size_t),
|
|
||||||
new_space_size = (SPACE_SIZE << 1) * sizeof(size_t);
|
|
||||||
p = mremap(to_space.begin, old_space_size, new_space_size, 0);
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent++; print_indent ();
|
|
||||||
#endif
|
|
||||||
if (p == MAP_FAILED) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("extend: extend_spaces: mremap failed\n"); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("extend: %p %p %p %p\n", p, to_space.begin, to_space.end, current);
|
|
||||||
fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
to_space.end += SPACE_SIZE;
|
|
||||||
SPACE_SIZE = SPACE_SIZE << 1;
|
|
||||||
to_space.size = SPACE_SIZE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern size_t * gc_copy (size_t *obj) {
|
|
||||||
data *d = TO_DATA(obj);
|
|
||||||
sexp *s = NULL;
|
|
||||||
size_t *copy = NULL;
|
|
||||||
int i = 0;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
int len1, len2, len3;
|
|
||||||
void * objj;
|
|
||||||
void * newobjj = (void*)current;
|
|
||||||
indent++; print_indent ();
|
|
||||||
printf ("gc_copy: %p cur = %p starts\n", obj, current);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!IS_VALID_HEAP_POINTER(obj)) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy: invalid ptr: %p\n", obj); fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IN_PASSIVE_SPACE(current) && current != to_space.end) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf("ERROR: gc_copy: out-of-space %p %p %p\n",
|
|
||||||
current, to_space.begin, to_space.end);
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
perror("ERROR: gc_copy: out-of-space\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IS_FORWARD_PTR(d->data_header)) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy: IS_FORWARD_PTR: return! %p -> %p\n", obj, (size_t *) d->data_header);
|
|
||||||
fflush(stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
return (size_t *) d->data_header;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy = current;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
objj = d;
|
|
||||||
#endif
|
|
||||||
switch (TAG(d->data_header)) {
|
|
||||||
case CLOSURE_TAG:
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy:closure_tag; len = %zu\n", LEN(d->data_header)); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
i = LEN(d->data_header);
|
|
||||||
// current += LEN(d->data_header) + 1;
|
|
||||||
// current += ((LEN(d->data_header) + 1) * sizeof(int) -1) / sizeof(size_t) + 1;
|
|
||||||
current += i+1;
|
|
||||||
*copy = d->data_header;
|
|
||||||
copy++;
|
|
||||||
d->data_header = (int) copy;
|
|
||||||
copy_elements (copy, obj, i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARRAY_TAG:
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy:array_tag; len = %zu\n", LEN(d->data_header)); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
current += ((LEN(d->data_header) + 1) * sizeof (int) - 1) / sizeof (size_t) + 1;
|
|
||||||
*copy = d->data_header;
|
|
||||||
copy++;
|
|
||||||
i = LEN(d->data_header);
|
|
||||||
d->data_header = (int) copy;
|
|
||||||
copy_elements (copy, obj, i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STRING_TAG:
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy:string_tag; len = %d\n", LEN(d->data_header) + 1); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
current += (LEN(d->data_header) + sizeof(int)) / sizeof(size_t) + 1;
|
|
||||||
*copy = d->data_header;
|
|
||||||
copy++;
|
|
||||||
d->data_header = (int) copy;
|
|
||||||
strcpy ((char*)©[0], (char*) obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SEXP_TAG :
|
|
||||||
s = TO_SEXP(obj);
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
objj = s;
|
|
||||||
len1 = LEN(s->contents.data_header);
|
|
||||||
len2 = LEN(s->data_header);
|
|
||||||
len3 = LEN(d->data_header);
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy:sexp_tag; len1 = %li, len2=%li, len3 = %li\n",
|
|
||||||
len1, len2, len3);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
i = LEN(s->contents.data_header);
|
|
||||||
current += i + 2;
|
|
||||||
*copy = s->tag;
|
|
||||||
copy++;
|
|
||||||
*copy = d->data_header;
|
|
||||||
copy++;
|
|
||||||
d->data_header = (int) copy;
|
|
||||||
copy_elements (copy, obj, i);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("ERROR: gc_copy: weird data_header: %p", TAG(d->data_header)); fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
perror ("ERROR: gc_copy: weird data_header");
|
|
||||||
exit (1);
|
|
||||||
return (obj);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_copy: %p(%p) -> %p (%p); new-current = %p\n",
|
|
||||||
obj, objj, copy, newobjj, current);
|
|
||||||
fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void gc_test_and_copy_root (size_t ** root) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent++;
|
|
||||||
#endif
|
|
||||||
if (IS_VALID_HEAP_POINTER(*root)) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_test_and_copy_root: root %p top=%p bot=%p *root %p \n", root, __gc_stack_top, __gc_stack_bottom, *root);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
*root = gc_copy (*root);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
else {
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc_test_and_copy_root: INVALID HEAP POINTER root %p *root %p\n", root, *root);
|
|
||||||
fflush (stdout);
|
|
||||||
}
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void gc_root_scan_data (void) {
|
|
||||||
size_t * p = (size_t*)&__start_custom_data;
|
|
||||||
while (p < (size_t*)&__stop_custom_data) {
|
|
||||||
gc_test_and_copy_root ((size_t**)p);
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* gc (size_t size) {
|
|
||||||
if (! enable_GC) {
|
|
||||||
Lfailure ("GC disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
current = to_space.begin;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: current:%p; to_space.b =%p; to_space.e =%p; \
|
|
||||||
f_space.b = %p; f_space.e = %p; __gc_stack_top=%p; __gc_stack_bottom=%p\n",
|
|
||||||
current, to_space.begin, to_space.end, from_space.begin, from_space.end,
|
|
||||||
__gc_stack_top, __gc_stack_bottom);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
gc_root_scan_data ();
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: data is scanned\n"); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
__gc_root_scan_stack ();
|
|
||||||
for (int i = 0; i < extra_roots.current_free; i++) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: extra_root № %i: %p %p\n", i, extra_roots.roots[i],
|
|
||||||
(size_t*) extra_roots.roots[i]);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
gc_test_and_copy_root ((size_t**)extra_roots.roots[i]);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: no more extra roots\n"); fflush (stdout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!IN_PASSIVE_SPACE(current)) {
|
|
||||||
printf ("gc: ASSERT: !IN_PASSIVE_SPACE(current) to_begin = %p to_end = %p \
|
|
||||||
current = %p\n", to_space.begin, to_space.end, current);
|
|
||||||
fflush (stdout);
|
|
||||||
perror ("ASSERT: !IN_PASSIVE_SPACE(current)\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (current + size >= to_space.end) {
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: pre-extend_spaces : %p %zu %p \n", current, size, to_space.end);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
if (extend_spaces ()) {
|
|
||||||
gc_swap_spaces ();
|
|
||||||
init_to_space (1);
|
|
||||||
return gc (size);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: post-extend_spaces: %p %zu %p \n", current, size, to_space.end);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
assert (IN_PASSIVE_SPACE(current));
|
|
||||||
assert (current + size < to_space.end);
|
|
||||||
|
|
||||||
gc_swap_spaces ();
|
|
||||||
from_space.current = current + size;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("gc: end: (allocate!) return %p; from_space.current %p; \
|
|
||||||
from_space.end %p \n\n",
|
|
||||||
current, from_space.current, from_space.end);
|
|
||||||
fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
return (void *) current;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
static void printFromSpace (void) {
|
|
||||||
size_t * cur = from_space.begin, *tmp = NULL;
|
|
||||||
data * d = NULL;
|
|
||||||
sexp * s = NULL;
|
|
||||||
size_t len = 0;
|
|
||||||
size_t elem_number = 0;
|
|
||||||
|
|
||||||
printf ("\nHEAP SNAPSHOT\n===================\n");
|
|
||||||
printf ("f_begin = %p, f_end = %p,\n", from_space.begin, from_space.end);
|
|
||||||
while (cur < from_space.current) {
|
|
||||||
printf ("data at %p", cur);
|
|
||||||
d = (data *) cur;
|
|
||||||
|
|
||||||
switch (TAG(d->data_header)) {
|
|
||||||
|
|
||||||
case STRING_TAG:
|
|
||||||
printf ("(=>%p): STRING\n\t%s; len = %i %zu\n",
|
|
||||||
d->contents, d->contents,
|
|
||||||
LEN(d->data_header), LEN(d->data_header) + 1 + sizeof(int));
|
|
||||||
fflush (stdout);
|
|
||||||
len = (LEN(d->data_header) + sizeof(int)) / sizeof(size_t) + 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLOSURE_TAG:
|
|
||||||
printf ("(=>%p): CLOSURE\n\t", d->contents);
|
|
||||||
len = LEN(d->data_header);
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
int elem = ((int*)d->contents)[i];
|
|
||||||
if (UNBOXED(elem)) printf ("%d ", elem);
|
|
||||||
else printf ("%p ", elem);
|
|
||||||
}
|
|
||||||
len += 1;
|
|
||||||
printf ("\n");
|
|
||||||
fflush (stdout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ARRAY_TAG:
|
|
||||||
printf ("(=>%p): ARRAY\n\t", d->contents);
|
|
||||||
len = LEN(d->data_header);
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
int elem = ((int*)d->contents)[i];
|
|
||||||
if (UNBOXED(elem)) printf ("%d ", elem);
|
|
||||||
else printf ("%p ", elem);
|
|
||||||
}
|
|
||||||
len += 1;
|
|
||||||
printf ("\n");
|
|
||||||
fflush (stdout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SEXP_TAG:
|
|
||||||
s = (sexp *) d;
|
|
||||||
d = (data *) &(s->contents);
|
|
||||||
char * data_header = de_hash (GET_SEXP_TAG(s->data_header));
|
|
||||||
printf ("(=>%p): SEXP\n\tdata_header(%s) ", s->contents.contents, data_header);
|
|
||||||
len = LEN(d->data_header);
|
|
||||||
tmp = (s->contents.contents);
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
int elem = ((int*)tmp)[i];
|
|
||||||
if (UNBOXED(elem)) printf ("%d ", UNBOX(elem));
|
|
||||||
else printf ("%p ", elem);
|
|
||||||
}
|
|
||||||
len += 2;
|
|
||||||
printf ("\n");
|
|
||||||
fflush (stdout);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0:
|
|
||||||
printf ("\nprintFromSpace: end: %zu elements\n===================\n\n",
|
|
||||||
elem_number);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf ("\nprintFromSpace: ERROR: bad data_header %d", TAG(d->data_header));
|
|
||||||
perror ("\nprintFromSpace: ERROR: bad data_header");
|
|
||||||
fflush (stdout);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
cur += len;
|
|
||||||
printf ("len = %zu, new cur = %p\n", len, cur);
|
|
||||||
elem_number++;
|
|
||||||
}
|
|
||||||
printf ("\nprintFromSpace: end: the whole space is printed:\
|
|
||||||
%zu elements\n===================\n\n", elem_number);
|
|
||||||
fflush (stdout);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __ENABLE_GC__
|
|
||||||
// alloc: allocates `size` bytes in heap
|
|
||||||
extern void * alloc (size_t size) {
|
|
||||||
void * p = (void*)BOX(NULL);
|
|
||||||
size = (size - 1) / sizeof(size_t) + 1; // convert bytes to words
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
indent++; print_indent ();
|
|
||||||
printf ("alloc: current: %p %zu words!", from_space.current, size);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
if (from_space.current + size < from_space.end) {
|
|
||||||
p = (void*) from_space.current;
|
|
||||||
from_space.current += size;
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf (";new current: %p \n", from_space.current); fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
#endif
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
init_to_space (0);
|
|
||||||
#ifdef DEBUG_PRINT
|
|
||||||
print_indent ();
|
|
||||||
printf ("alloc: call gc: %zu\n", size); fflush (stdout);
|
|
||||||
printFromSpace(); fflush (stdout);
|
|
||||||
p = gc (size);
|
|
||||||
print_indent ();
|
|
||||||
printf("alloc: gc END %p %p %p %p\n\n", from_space.begin,
|
|
||||||
from_space.end, from_space.current, p); fflush (stdout);
|
|
||||||
printFromSpace(); fflush (stdout);
|
|
||||||
indent--;
|
|
||||||
return p;
|
|
||||||
#else
|
|
||||||
return gc (size);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
|
|
|
||||||
259
runtime/test_main.c
Normal file
259
runtime/test_main.c
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "gc.h"
|
||||||
|
#include "runtime_common.h"
|
||||||
|
|
||||||
|
// function from runtime that maps string to int value
|
||||||
|
extern int LtagHash (char *s);
|
||||||
|
|
||||||
|
extern void* Bsexp (int n, ...);
|
||||||
|
extern void* Barray (int bn, ...);
|
||||||
|
extern void* Bstring (void*);
|
||||||
|
extern void* Bclosure (int bn, void *entry, ...);
|
||||||
|
|
||||||
|
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||||
|
|
||||||
|
void test_correct_structure_sizes(void) {
|
||||||
|
// something like induction base
|
||||||
|
assert((array_size(0) == get_header_size(ARRAY)));
|
||||||
|
assert((string_size(0) == get_header_size(STRING) + 1)); // +1 is because of '\0'
|
||||||
|
assert((sexp_size(0) == get_header_size(SEXP)));
|
||||||
|
assert((closure_size(0) == get_header_size(CLOSURE)));
|
||||||
|
|
||||||
|
// just check correctness for some small sizes
|
||||||
|
for (int k = 1; k < 20; ++k) {
|
||||||
|
assert((array_size(k) == get_header_size(ARRAY) + sizeof (int) * k));
|
||||||
|
assert((string_size(k) == get_header_size(STRING) + k + 1));
|
||||||
|
assert((sexp_size(k) == get_header_size(SEXP) + sizeof (int) * k));
|
||||||
|
assert((closure_size(k) == get_header_size(CLOSURE) + sizeof (int) * k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void no_gc_tests(void) {
|
||||||
|
test_correct_structure_sizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// unfortunately there is no generic function pointer that can hold pointer to function with arbitrary signature
|
||||||
|
extern size_t call_runtime_function(void *virt_stack_pointer, void *function_pointer, size_t num_args, ...);
|
||||||
|
|
||||||
|
#include "virt_stack.h"
|
||||||
|
|
||||||
|
virt_stack* init_test() {
|
||||||
|
__init();
|
||||||
|
virt_stack *st = vstack_create();
|
||||||
|
vstack_init(st);
|
||||||
|
__gc_stack_bottom = (size_t) vstack_top(st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_test(virt_stack *st) {
|
||||||
|
vstack_destruct(st);
|
||||||
|
__shutdown();
|
||||||
|
}
|
||||||
|
void force_gc_cycle(virt_stack *st) {
|
||||||
|
__gc_stack_top = (size_t) vstack_top(st);
|
||||||
|
gc_alloc(0);
|
||||||
|
__gc_stack_top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_string_alloc(void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
vstack_push(st, BOX(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bstring, 1, "abc"));
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_array_alloc(void) {
|
||||||
|
virt_stack* st = init_test();
|
||||||
|
|
||||||
|
// allocate array [ BOX(1) ] and push it onto the stack
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Barray, 2, BOX(1), BOX(1)));
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_sexp_alloc(void) {
|
||||||
|
virt_stack* st = init_test();
|
||||||
|
|
||||||
|
// allocate sexp with one boxed field and push it onto the stack
|
||||||
|
// calling runtime function Bsexp(BOX(2), BOX(1), LtagHash("test"))
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bsexp, 3, BOX(2), BOX(1), LtagHash("test")));
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_simple_closure_alloc(void) {
|
||||||
|
virt_stack* st = init_test();
|
||||||
|
|
||||||
|
// allocate closure with boxed captured value and push it onto the stack
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bclosure, 3, BOX(1), NULL, BOX(1)));
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_single_object_allocation_with_collection_virtual_stack(void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_garbage_is_reclaimed(void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
|
call_runtime_function(vstack_top(st), Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
|
force_gc_cycle(st);
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 0));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_alive_are_not_reclaimed(void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
|
||||||
|
|
||||||
|
force_gc_cycle(st);
|
||||||
|
|
||||||
|
const int N = 10;
|
||||||
|
int ids[N];
|
||||||
|
size_t alive = objects_snapshot(ids, N);
|
||||||
|
assert((alive == 1));
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_small_tree_compaction(void) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
// this one will increase heap size
|
||||||
|
call_runtime_function(vstack_top(st), Bstring, 1, "aaaaaaaaaaaaaaaaaaaaaa");
|
||||||
|
|
||||||
|
size_t l = call_runtime_function(vstack_top(st), Bstring, 1, "left-s");
|
||||||
|
size_t r = call_runtime_function(vstack_top(st), Bstring, 1, "right-s");
|
||||||
|
vstack_push(st, call_runtime_function(vstack_top(st), Bsexp, 4, BOX(3), (size_t)l, (size_t) r, LtagHash("tree")));
|
||||||
|
force_gc_cycle(st);
|
||||||
|
const int SZ = 10;
|
||||||
|
int ids[SZ];
|
||||||
|
size_t alive = objects_snapshot(ids, SZ);
|
||||||
|
assert((alive == 3));
|
||||||
|
|
||||||
|
// check that order is indeed preserved
|
||||||
|
for (int i = 0; i < alive - 1; ++i) {
|
||||||
|
assert((ids[i] < ids[i + 1]));
|
||||||
|
}
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern size_t cur_id;
|
||||||
|
|
||||||
|
size_t generate_random_obj_forest(virt_stack *st, int cnt, int seed) {
|
||||||
|
srand(seed);
|
||||||
|
int cur_sz = 0;
|
||||||
|
size_t alive = 0;
|
||||||
|
while (cnt) {
|
||||||
|
--cnt;
|
||||||
|
if (cur_sz == 0) {
|
||||||
|
vstack_push(st, BOX(1));
|
||||||
|
++cur_sz;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos[2] = {rand() % vstack_size(st), rand() % vstack_size(st)};
|
||||||
|
size_t field[2];
|
||||||
|
for (int t = 0; t < 2; ++t) {
|
||||||
|
field[t] = vstack_kth_from_start(st, pos[t]);
|
||||||
|
}
|
||||||
|
size_t obj;
|
||||||
|
|
||||||
|
if (rand() % 2) {
|
||||||
|
obj = call_runtime_function(vstack_top(st), Bsexp, 4, BOX(3), field[0], field[1], LtagHash("test"));
|
||||||
|
} else {
|
||||||
|
obj = BOX(1);
|
||||||
|
}
|
||||||
|
// whether object is stored on stack
|
||||||
|
if (rand() % 2 != 0) {
|
||||||
|
vstack_push(st, obj);
|
||||||
|
if ((obj & 1) == 0) {
|
||||||
|
++alive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++cur_sz;
|
||||||
|
}
|
||||||
|
force_gc_cycle(st);
|
||||||
|
return alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_stress_test_random_obj_forest(int seed) {
|
||||||
|
virt_stack *st = init_test();
|
||||||
|
|
||||||
|
const int SZ = 10000;
|
||||||
|
|
||||||
|
size_t expectedAlive = generate_random_obj_forest(st, SZ, seed);
|
||||||
|
|
||||||
|
int ids[SZ];
|
||||||
|
size_t alive = objects_snapshot(ids, SZ);
|
||||||
|
assert(alive == expectedAlive);
|
||||||
|
|
||||||
|
// check that order is indeed preserved
|
||||||
|
for (int i = 0; i < alive - 1; ++i) {
|
||||||
|
assert((ids[i] < ids[i + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_test(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
no_gc_tests();
|
||||||
|
|
||||||
|
test_simple_string_alloc();
|
||||||
|
test_simple_array_alloc();
|
||||||
|
test_simple_sexp_alloc();
|
||||||
|
test_simple_closure_alloc();
|
||||||
|
test_single_object_allocation_with_collection_virtual_stack();
|
||||||
|
test_garbage_is_reclaimed();
|
||||||
|
test_alive_are_not_reclaimed();
|
||||||
|
test_small_tree_compaction();
|
||||||
|
|
||||||
|
// stress test
|
||||||
|
for (int s = 0; s < 100; ++s) {
|
||||||
|
run_stress_test_random_obj_forest(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
runtime/test_util.s
Normal file
40
runtime/test_util.s
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# this is equivalent C-signature for this function
|
||||||
|
# size_t call_runtime_function(void *stack, void *func_ptr, int num_args, ...)
|
||||||
|
|
||||||
|
.globl call_runtime_function
|
||||||
|
.type call_runtime_function, @function
|
||||||
|
call_runtime_function:
|
||||||
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
|
||||||
|
# store old stack pointer
|
||||||
|
movl %esp, %edi
|
||||||
|
|
||||||
|
# move esp to point to the virtual stack
|
||||||
|
movl 8(%ebp), %esp
|
||||||
|
|
||||||
|
# push arguments onto the stack
|
||||||
|
movl 16(%ebp), %ecx # num_args
|
||||||
|
test %ecx, %ecx
|
||||||
|
jz f_call # in case function doesn't have any parameters
|
||||||
|
|
||||||
|
leal 16(%ebp), %eax # pointer to value BEFORE first argument
|
||||||
|
leal (%eax,%ecx,4), %edx # pointer to last argument (right-to-left)
|
||||||
|
|
||||||
|
push_args_loop:
|
||||||
|
pushl (%edx)
|
||||||
|
subl $4, %edx
|
||||||
|
subl $1, %ecx
|
||||||
|
jnz push_args_loop
|
||||||
|
|
||||||
|
# call the function
|
||||||
|
f_call:
|
||||||
|
movl 12(%ebp), %eax
|
||||||
|
call *%eax
|
||||||
|
|
||||||
|
# restore the old stack pointer
|
||||||
|
movl %edi, %esp
|
||||||
|
|
||||||
|
# pop the old frame pointer and return
|
||||||
|
popl %ebp # epilogue
|
||||||
|
ret
|
||||||
45
runtime/virt_stack.c
Normal file
45
runtime/virt_stack.c
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#include "virt_stack.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
virt_stack *vstack_create() {
|
||||||
|
return malloc(sizeof (virt_stack));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vstack_destruct(virt_stack *st) {
|
||||||
|
free(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vstack_init(virt_stack *st) {
|
||||||
|
st->cur = RUNTIME_VSTACK_SIZE;
|
||||||
|
st->buf[st->cur] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vstack_push(virt_stack *st, size_t value) {
|
||||||
|
if (st->cur == 0) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
--st->cur;
|
||||||
|
st->buf[st->cur] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t vstack_pop(virt_stack *st) {
|
||||||
|
if (st->cur == RUNTIME_VSTACK_SIZE) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
size_t value = st->buf[st->cur];
|
||||||
|
++st->cur;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* vstack_top(virt_stack *st) {
|
||||||
|
return st->buf + st->cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t vstack_size(virt_stack *st) {
|
||||||
|
return RUNTIME_VSTACK_SIZE - st->cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t vstack_kth_from_start(virt_stack *st, size_t k) {
|
||||||
|
assert(vstack_size(st) > k);
|
||||||
|
return st->buf[RUNTIME_VSTACK_SIZE - 1 - k];
|
||||||
|
}
|
||||||
33
runtime/virt_stack.h
Normal file
33
runtime/virt_stack.h
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// Created by egor on 24.04.23.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LAMA_RUNTIME_VIRT_STACK_H
|
||||||
|
#define LAMA_RUNTIME_VIRT_STACK_H
|
||||||
|
#define RUNTIME_VSTACK_SIZE 100000
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
struct {
|
||||||
|
size_t buf[RUNTIME_VSTACK_SIZE + 1];
|
||||||
|
size_t cur;
|
||||||
|
} typedef virt_stack;
|
||||||
|
|
||||||
|
virt_stack *vstack_create();
|
||||||
|
|
||||||
|
void vstack_destruct(virt_stack *st);
|
||||||
|
|
||||||
|
void vstack_init(virt_stack *st);
|
||||||
|
|
||||||
|
void vstack_push(virt_stack *st, size_t value);
|
||||||
|
|
||||||
|
size_t vstack_pop(virt_stack *st);
|
||||||
|
|
||||||
|
void* vstack_top(virt_stack *st);
|
||||||
|
|
||||||
|
size_t vstack_size(virt_stack *st);
|
||||||
|
|
||||||
|
size_t vstack_kth_from_start(virt_stack *st, size_t k);
|
||||||
|
|
||||||
|
#endif //LAMA_RUNTIME_VIRT_STACK_H
|
||||||
Loading…
Add table
Add a link
Reference in a new issue