mirror of
https://github.com/ProgramSnail/Lama.git
synced 2025-12-06 14:58:50 +00:00
Got rid of ASM for runtime pre_gc, post_gc and stack scan
This commit is contained in:
parent
8b073cbd48
commit
b5a0b81d11
5 changed files with 35 additions and 156 deletions
|
|
@ -6,21 +6,21 @@ UNIT_TESTS_FLAGS=$(TEST_FLAGS)
|
|||
INVARIANTS_CHECK_FLAGS=$(TEST_FLAGS) -DFULL_INVARIANT_CHECKS
|
||||
|
||||
# this target is the most important one, its' artefacts should be used as a runtime of Lama
|
||||
all: gc_runtime.o gc.o runtime.o
|
||||
ar rc runtime.a gc_runtime.o runtime.o gc.o
|
||||
all: gc.o runtime.o
|
||||
ar rc runtime.a runtime.o gc.o
|
||||
|
||||
# this is a target that runs unit tests, scenarios are written in a single file `test_main.c`
|
||||
unit_tests.o: gc.c gc.h gc_runtime.s runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o unit_tests.o $(UNIT_TESTS_FLAGS) gc.c gc_runtime.s virt_stack.c runtime.c test_main.c test_util.s
|
||||
unit_tests.o: gc.c gc.h runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o unit_tests.o $(UNIT_TESTS_FLAGS) gc.c virt_stack.c runtime.c test_main.c test_util.s
|
||||
|
||||
# this target also runs unit tests but with additional expensive checks of GC invariants which aren't used in production version
|
||||
invariants_check.o: gc.c gc.h gc_runtime.s runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o invariants_check.o $(INVARIANTS_CHECK_FLAGS) gc.c gc_runtime.s virt_stack.c runtime.c test_main.c test_util.s
|
||||
invariants_check.o: gc.c gc.h runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o invariants_check.o $(INVARIANTS_CHECK_FLAGS) gc.c virt_stack.c runtime.c test_main.c test_util.s
|
||||
|
||||
# this target also runs unit tests but with additional expensive checks of GC invariants which aren't used in production version
|
||||
# additionally, it prints debug information
|
||||
invariants_check_debug_print.o: gc.c gc.h gc_runtime.s runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o invariants_check_debug_print.o $(INVARIANTS_CHECK_FLAGS) -DDEBUG_PRINT gc.c gc_runtime.s virt_stack.c runtime.c test_main.c test_util.s
|
||||
invariants_check_debug_print.o: gc.c gc.h runtime.c runtime.h runtime_common.h virt_stack.c virt_stack.h test_main.c test_util.s
|
||||
$(CC) -o invariants_check_debug_print.o $(INVARIANTS_CHECK_FLAGS) -DDEBUG_PRINT gc.c virt_stack.c runtime.c test_main.c test_util.s
|
||||
|
||||
virt_stack.o: virt_stack.h virt_stack.c
|
||||
$(CC) $(PROD_FLAGS) -c virt_stack.c
|
||||
|
|
@ -28,9 +28,6 @@ virt_stack.o: virt_stack.h virt_stack.c
|
|||
gc.o: gc.c gc.h
|
||||
$(CC) -rdynamic $(PROD_FLAGS) -c gc.c
|
||||
|
||||
gc_runtime.o: gc_runtime.s
|
||||
$(CC) $(PROD_FLAGS) -c gc_runtime.s
|
||||
|
||||
runtime.o: runtime.c runtime.h
|
||||
$(CC) $(PROD_FLAGS) -c runtime.c
|
||||
|
||||
|
|
|
|||
26
runtime/gc.c
26
runtime/gc.c
|
|
@ -22,7 +22,7 @@ size_t cur_id = 0;
|
|||
|
||||
static extra_roots_pool extra_roots;
|
||||
|
||||
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||
size_t __gc_stack_top = 0, __gc_stack_bottom = 0;
|
||||
#ifdef LAMA_ENV
|
||||
extern const size_t __start_custom_data, __stop_custom_data;
|
||||
#endif
|
||||
|
|
@ -39,7 +39,7 @@ void dump_heap ();
|
|||
|
||||
void handler (int sig) {
|
||||
void *array[10];
|
||||
size_t size;
|
||||
int size;
|
||||
|
||||
// get void*'s for all entries on the stack
|
||||
size = backtrace(array, 10);
|
||||
|
|
@ -59,9 +59,8 @@ void *alloc (size_t size) {
|
|||
#endif
|
||||
void *p = gc_alloc_on_existing_heap(size);
|
||||
if (!p) {
|
||||
// not enough place in heap, need to perform GC cycle
|
||||
// not enough place in the heap, need to perform GC cycle
|
||||
p = gc_alloc(size);
|
||||
// return gc_alloc(size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
|
@ -223,17 +222,23 @@ void *gc_alloc (size_t size) {
|
|||
return gc_alloc_on_existing_heap(size);
|
||||
}
|
||||
|
||||
static void gc_root_scan_stack () {
|
||||
for (size_t *p = (size_t *)(__gc_stack_top + 4); p < (size_t *)__gc_stack_bottom; ++p) {
|
||||
gc_test_and_mark_root((size_t **)p);
|
||||
}
|
||||
}
|
||||
|
||||
void mark_phase (void) {
|
||||
#if defined(DEBUG_VERSION) && defined(DEBUG_PRINT)
|
||||
fprintf(stderr, "marking has started\n");
|
||||
fprintf(stderr,
|
||||
"__gc_root_scan_stack has started: gc_top=%p bot=%p\n",
|
||||
"gc_root_scan_stack has started: gc_top=%p bot=%p\n",
|
||||
(void *)__gc_stack_top,
|
||||
(void *)__gc_stack_bottom);
|
||||
#endif
|
||||
__gc_root_scan_stack();
|
||||
gc_root_scan_stack();
|
||||
#if defined(DEBUG_VERSION) && defined(DEBUG_PRINT)
|
||||
fprintf(stderr, "__gc_root_scan_stack has finished\n");
|
||||
fprintf(stderr, "gc_root_scan_stack has finished\n");
|
||||
fprintf(stderr, "scan_extra_roots has started\n");
|
||||
#endif
|
||||
scan_extra_roots();
|
||||
|
|
@ -554,7 +559,12 @@ extern void gc_test_and_mark_root (size_t **root) {
|
|||
mark((void *)*root);
|
||||
}
|
||||
|
||||
extern void __init (void) {
|
||||
void __gc_init (void) {
|
||||
__gc_stack_bottom = (size_t)__builtin_frame_address(1) + 4;
|
||||
__init();
|
||||
}
|
||||
|
||||
void __init (void) {
|
||||
signal(SIGSEGV, handler);
|
||||
size_t space_size = INIT_HEAP_SIZE * sizeof(size_t);
|
||||
|
||||
|
|
|
|||
14
runtime/gc.h
14
runtime/gc.h
|
|
@ -58,8 +58,6 @@ void *gc_alloc_on_existing_heap(size_t);
|
|||
// specific for mark-and-compact_phase gc
|
||||
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);
|
||||
// marks each pointer from extra roots
|
||||
void scan_extra_roots (void);
|
||||
#ifdef LAMA_ENV
|
||||
|
|
@ -100,24 +98,16 @@ void pop_extra_root (void **p);
|
|||
// ============================================================================
|
||||
// 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);
|
||||
void __gc_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 __init (void);
|
||||
void __init (void);
|
||||
|
||||
// mostly useful for tests but basically you want to call this in case you want
|
||||
// to deallocate all object allocated via GC
|
||||
extern void __shutdown (void);
|
||||
|
||||
// Next two functions sets and unsets `__gc_stack_top`
|
||||
// The first (`__pre_gc`) should be called in the very beginning of any runtime
|
||||
// function during the execution of which garbage collection can be initiated.
|
||||
// The last one is a `companion function` which has to be called at the very
|
||||
// end of any function that previously called `__pre_gc`
|
||||
extern void __pre_gc (void);
|
||||
extern void __post_gc (void);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// invoked from GASM: see gc_runtime.s
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
.data
|
||||
__gc_stack_bottom: .long 0
|
||||
__gc_stack_top: .long 0
|
||||
|
||||
.globl __pre_gc
|
||||
.globl __post_gc
|
||||
.globl __gc_init
|
||||
.globl __gc_root_scan_stack
|
||||
.globl __gc_stack_top
|
||||
.globl __gc_stack_bottom
|
||||
.extern __init
|
||||
.extern gc_test_and_mark_root
|
||||
|
||||
.text
|
||||
|
||||
__gc_init:
|
||||
movl %ebp, __gc_stack_bottom
|
||||
addl $4, __gc_stack_bottom
|
||||
call __init
|
||||
ret
|
||||
|
||||
// if __gc_stack_top is equal to 0
|
||||
// then set __gc_stack_top to %ebp
|
||||
// else return
|
||||
__pre_gc:
|
||||
pushl %eax
|
||||
movl __gc_stack_top, %eax
|
||||
cmpl $0, %eax
|
||||
jne __pre_gc_2
|
||||
movl %ebp, %eax
|
||||
// addl $8, %eax
|
||||
movl %eax, __gc_stack_top
|
||||
__pre_gc_2:
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
// if __gc_stack_top has been set by the caller
|
||||
// (i.e. it is equal to its %ebp)
|
||||
// then set __gc_stack_top to 0
|
||||
// else return
|
||||
__post_gc:
|
||||
pushl %eax
|
||||
movl __gc_stack_top, %eax
|
||||
cmpl %eax, %ebp
|
||||
jnz __post_gc2
|
||||
movl $0, __gc_stack_top
|
||||
__post_gc2:
|
||||
popl %eax
|
||||
ret
|
||||
|
||||
// Scan stack for roots
|
||||
// strting from __gc_stack_top
|
||||
// till __gc_stack_bottom
|
||||
__gc_root_scan_stack:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
pushl %ebx
|
||||
pushl %edx
|
||||
movl __gc_stack_top, %eax
|
||||
jmp next
|
||||
|
||||
loop:
|
||||
movl (%eax), %ebx
|
||||
|
||||
// check that it is not a pointer to code section
|
||||
// i.e. the following is not true:
|
||||
// __executable_start <= (%eax) <= __etext
|
||||
check11:
|
||||
leal __executable_start, %edx
|
||||
cmpl %ebx, %edx
|
||||
jna check12
|
||||
jmp check21
|
||||
|
||||
check12:
|
||||
leal __etext, %edx
|
||||
cmpl %ebx, %edx
|
||||
jnb next
|
||||
|
||||
// check that it is not a pointer into the program stack
|
||||
// i.e. the following is not true:
|
||||
// __gc_stack_bottom <= (%eax) <= __gc_stack_top
|
||||
check21:
|
||||
cmpl %ebx, __gc_stack_top
|
||||
jna check22
|
||||
jmp loop2
|
||||
|
||||
check22:
|
||||
cmpl %ebx, __gc_stack_bottom
|
||||
jnb next
|
||||
|
||||
// check if it a valid pointer
|
||||
// i.e. the lastest bit is set to zero
|
||||
loop2:
|
||||
andl $0x00000001, %ebx
|
||||
jnz next
|
||||
gc_run_t:
|
||||
pushl %eax
|
||||
pushl %eax
|
||||
call gc_test_and_mark_root
|
||||
addl $4, %esp
|
||||
popl %eax
|
||||
|
||||
next:
|
||||
addl $4, %eax
|
||||
cmpl %eax, __gc_stack_bottom
|
||||
jne loop
|
||||
returnn:
|
||||
movl $0, %eax
|
||||
popl %edx
|
||||
popl %ebx
|
||||
movl %ebp, %esp
|
||||
popl %ebp
|
||||
ret
|
||||
|
|
@ -30,23 +30,18 @@ void __post_gc_subst () { }
|
|||
#endif
|
||||
/* end */
|
||||
|
||||
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||
|
||||
#define PRE_GC() \
|
||||
bool flag = true; \
|
||||
if (__gc_stack_top == 0) { flag = false; } \
|
||||
__pre_gc(); \
|
||||
bool flag = false; \
|
||||
flag = __gc_stack_top == 0; \
|
||||
if (flag) { __gc_stack_top = (size_t)__builtin_frame_address(0); } \
|
||||
assert(__gc_stack_top != 0); \
|
||||
assert(__builtin_frame_address(0) <= (void *)__gc_stack_top);
|
||||
|
||||
#define POST_GC() \
|
||||
assert(__builtin_frame_address(0) <= (void *)__gc_stack_top); \
|
||||
__post_gc(); \
|
||||
\
|
||||
if (!flag && __gc_stack_top != 0) { \
|
||||
fprintf(stderr, "Moving stack???\n"); \
|
||||
assert(false); \
|
||||
}
|
||||
|
||||
extern size_t __gc_stack_top, __gc_stack_bottom;
|
||||
if (flag) { __gc_stack_top = 0; }
|
||||
|
||||
static void vfailure (char *s, va_list args) {
|
||||
fprintf(stderr, "*** FAILURE: ");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue