Correctly use finalizers to clean up memory in interpreter

parent dd71c975
Pipeline #11626 passed with stage
in 8 minutes and 8 seconds
......@@ -5,8 +5,8 @@ from symbols_in_program import :: Symbol
:: Finalizer
:: *CoercionEnvironment = E.host_reference:
{ ce_references :: ![host_reference]
:: *CoercionEnvironment =
{ ce_dummy :: !() //* Ensures that the rest of the record is in one memory block
, ce_code_segment :: !Pointer
, ce_code_size :: !Int
, ce_data_segment :: !Pointer
......@@ -21,4 +21,4 @@ from symbols_in_program import :: Symbol
, ce_hp :: !Pointer
}
coerce :: *CoercionEnvironment !Finalizer !Pointer -> .a
coerce :: *CoercionEnvironment !Finalizer -> .a
......@@ -31,11 +31,10 @@ import code from "interpret.a"
// Example: get an infinite list of primes from a bytecode file and take only
// the first 100 elements.
Start :: *World -> [Int]
Start :: *World -> Int
Start w
# (primes,w) = get_expression "../test/infprimes.bc" w
= /*reverse*/ (take 5000 primes)
//= sum (reverse (take 5000 primes) ++ reverse (take 5000 primes)) // 228910518
= last (reverse (take 15000 primes))
// Example: get a function from a bytecode file and apply it
Start w
......@@ -76,7 +75,7 @@ get_expression filename w
# start_node = hp
# hp = hp + IF_INT_64_OR_32 24 12
#! ce =
{ ce_references = [start_node]
{ ce_dummy = ()
, ce_code_segment = code_segment
, ce_code_size = csize
, ce_data_segment = data_segment
......@@ -90,15 +89,22 @@ get_expression filename w
, ce_csp = csp
, ce_hp = hp
}
= (coerce ce (Finalizer 0 0 0) start_node, w)
= (coerce ce (Finalizer 0 0 start_node), w)
// Obviously, this is not a "valid" finalizer in the sense that it can be
// called from the garbage collector. But that's okay, because we don't add
// it to the finalizer_list anyway. This is just to ensure that the first
// call to `coerce` gets the right argument.
// On purpose unique: this ensures there is only one CoercionEnvironment, ever.
// This is needed to ensure that the heap address gets shared by all coercings.
// Also on purpose lazy: this ensures it is passed on the A-stack, so that we
// can easily pass it to C.
coerce :: *CoercionEnvironment !Finalizer !Pointer -> .a
coerce ce _ p = code {
updatepop_a 0 1
coerce :: *CoercionEnvironment !Finalizer -> .a
coerce ce fin = code {
push_a 1 | Get finalizer
repl_r_args 0 3 | Get arguments from finalizer
updatepop_a 0 1 | Remove finalizer
pop_b 2 | Keep only node argument in finalizer
.d 1 1 i
jsr _copy_node_asm
.o 1 0
......
......@@ -51,9 +51,10 @@ SRC_INTERPRET:=\
parse.c\
traps.c\
util.c
OBJ_INTERPRET:=$(subst .c,.o,$(SRC_INTERPRET)) copy_node.o
OBJ_INTERPRET:=$(subst .c,.o,$(SRC_INTERPRET)) copy_node.o finalizers.o
DEP_INTERPRET:=$(subst .c,.h,$(SRC_INTERPRET))\
copy_node.h\
finalizers.h\
gc/util.h\
interpret_instructions.h\
settings.h
......
......@@ -6,13 +6,12 @@
#include "bytecode.h"
#include "copy_node.h"
#include "finalizers.h"
#include "gc.h"
#include "interpret.h"
#include "util.h"
extern void *e__CodeSharing__ncoerce;
extern void *e____system__kFinalizer;
extern BC_WORD *finalizer_list;
extern void *dINT;
/* This does not contain the ce_symbols from the CoercionEnvironment type. This
......@@ -34,70 +33,18 @@ struct coercion_environment {
BC_WORD *hp;
};
struct host_references *host_references = NULL;
BC_WORD *add_host_reference(BC_WORD *heap, BC_WORD *reference) {
/* TODO check if we need garbage collection */
heap[0] = (BC_WORD) &__Cons+2+IF_INT_64_OR_32(16,8);
heap[1] = (BC_WORD) reference;
heap[2] = (BC_WORD) host_references;
host_references = (struct host_references*) heap;
#if DEBUG_CLEAN_LINKS > 1
fprintf(stderr,"\tAdded host reference: %p -> %p\n", reference, (void*)reference[1]);
#endif
return heap + 3;
}
void remove_one_host_reference(BC_WORD *node) {
struct host_references *prev = NULL;
struct host_references *hrs = host_references;
while (hrs->hr_descriptor != (void*)((BC_WORD)&__Nil+2)) {
if (hrs->hr_reference[1] == node) {
if (prev == NULL)
host_references = hrs->hr_rest;
else
prev->hr_rest = hrs->hr_rest;
return;
}
prev = hrs;
hrs = hrs->hr_rest;
}
}
void interpreter_finalizer(void *coerce_node) {
fprintf(stderr,"\t\tFinalizing %p\n",coerce_node);
fprintf(stderr,"\t\t\t%p %p\n",&e__CodeSharing__ncoerce,((BC_WORD*)coerce_node)[0]);
struct coercion_environment *ce = ((struct coercion_environment**)coerce_node)[1];
host_references = ((struct host_references**)ce)[1];
BC_WORD **node = ((BC_WORD***)coerce_node)[3];
remove_one_host_reference(node[1]);
((struct host_references**)ce)[1] = host_references;
void interpreter_finalizer(BC_WORD coerce_node) {
}
BC_WORD *make_coerce_node(BC_WORD *heap, void *coercion_environment, BC_WORD node) {
heap[ 0] = (BC_WORD) &e__CodeSharing__ncoerce;
heap[ 1] = (BC_WORD) coercion_environment;
heap[ 2] = (BC_WORD) &heap[4];
heap[ 3] = (BC_WORD) &heap[9];
heap[ 4] = (BC_WORD) &e____system__kFinalizer+2;
heap[ 5] = (BC_WORD) finalizer_list;
heap[ 6] = (BC_WORD) &heap[7];
heap[ 7] = (BC_WORD) &interpreter_finalizer;
heap[ 8] = (BC_WORD) heap;
finalizer_list = &heap[4];
fprintf(stderr,"\t\tFinalizer %p %p\n",heap,(void*)heap[0]);
heap[ 9] = (BC_WORD) &dINT+2;
heap[10] = node;
return add_host_reference(heap+11, &heap[9]);
heap[ 2] = (BC_WORD) &heap[3];
return build_finalizer(heap+3, interpreter_finalizer, node);
}
BC_WORD copy_interpreter_to_host(BC_WORD *host_heap, size_t host_heap_free, void *coercion_environment, BC_WORD *node) {
struct coercion_environment *ce = (struct coercion_environment*)(((BC_WORD**)coercion_environment)[2]);
host_references = ((struct host_references**)coercion_environment)[1];
BC_WORD *org_host_heap = host_heap;
......@@ -153,9 +100,11 @@ BC_WORD copy_interpreter_to_host(BC_WORD *host_heap, size_t host_heap_free, void
fprintf(stderr, "\tcoercing (arities %d / %d)...\n", a_arity, b_arity);
#endif
if ((a_arity < 3 && host_heap_free < 3 + 14 * a_arity)
|| (a_arity >= 3 && host_heap_free < a_arity + 2 + 14 * a_arity)) {
fprintf(stderr,"Not enough memory (%ld, %d)\n", host_heap_free, a_arity);
if ((a_arity < 3 && host_heap_free < 3 + (3+FINALIZER_SIZE_ON_HEAP) * a_arity)
|| (a_arity >= 3 && host_heap_free < a_arity + 2 + (3+FINALIZER_SIZE_ON_HEAP) * a_arity)) {
#if DEBUG_CLEAN_LINKS > 1
fprintf(stderr,"\tnot enough memory (%ld, %d)\n", host_heap_free, a_arity);
#endif
return -2;
}
......@@ -173,8 +122,6 @@ BC_WORD copy_interpreter_to_host(BC_WORD *host_heap, size_t host_heap_free, void
return -5;
}
remove_one_host_reference(node);
((struct host_references**)coercion_environment)[1] = host_references;
BC_WORD *host_node = host_heap;
host_node[0] = (BC_WORD)(((BC_WORD*)((BC_WORD)host_address+2))+a_arity); /* TODO check */
......@@ -203,8 +150,6 @@ BC_WORD copy_interpreter_to_host(BC_WORD *host_heap, size_t host_heap_free, void
}
}
((struct host_references**)coercion_environment)[1] = host_references;
#if DEBUG_CLEAN_LINKS > 1
fprintf(stderr, "\tReturning\n");
#endif
......
......@@ -2,13 +2,4 @@
#include "bytecode.h"
struct host_references {
void *hr_descriptor;
BC_WORD **hr_reference;
struct host_references *hr_rest;
};
extern void *__Cons;
extern void *__Nil;
extern struct host_references *host_references;
void interpreter_finalizer(BC_WORD coerce_node);
#include "finalizers.h"
extern struct finalizers *finalizer_list;
extern void *__Nil;
extern void *e____system__kFinalizer;
struct finalizers *next_finalizer(struct finalizers *now) {
now = now == NULL ? finalizer_list : now->next;
return ((BC_WORD) now == (BC_WORD) &__Nil-8) ? NULL : now;
}
BC_WORD *build_finalizer(BC_WORD *heap, void (*fun)(BC_WORD), BC_WORD arg) {
heap[0] = (BC_WORD) &e____system__kFinalizer+2;
heap[1] = (BC_WORD) finalizer_list;
heap[2] = (BC_WORD) &heap[3];
heap[3] = (BC_WORD) fun;
heap[4] = arg;
finalizer_list = (struct finalizers*) &heap[0];
return heap+5;
}
#pragma once
#include "bytecode.h"
#define FINALIZER_SIZE_ON_HEAP 5
struct finalizer {
void (*fun)(BC_WORD);
BC_WORD arg;
};
struct finalizers {
void *descriptor;
struct finalizers *next;
struct finalizer *cur;
};
struct finalizers *next_finalizer(struct finalizers *);
BC_WORD *build_finalizer(BC_WORD *heap, void (*fun)(BC_WORD), BC_WORD arg);
......@@ -10,6 +10,7 @@
#ifdef LINK_CLEAN_RUNTIME
# include "../copy_node.h"
# include "../finalizers.h"
#endif
int in_first_semispace = 1;
......@@ -51,15 +52,14 @@ BC_WORD *collect_copy(BC_WORD *stack, BC_WORD *asp, BC_WORD *heap, size_t heap_s
# if (DEBUG_GARBAGE_COLLECTOR > 1)
fprintf(stderr, "Pass 1b: reverse pointers from the host\n");
# endif
struct host_references *hrs = host_references;
while (hrs->hr_descriptor != (void*)((BC_WORD)&__Nil+2)) {
struct finalizers *finalizers = NULL;
while ((finalizers = next_finalizer(finalizers)) != NULL) {
# if (DEBUG_GARBAGE_COLLECTOR > 2)
fprintf(stderr, "\t%p -> %p\n", (void*)hrs->hr_reference[1], (void*)*hrs->hr_reference[1]);
fprintf(stderr, "\t%p -> %p\n", (void*)finalizers->cur->arg, ((void**)finalizers->cur->arg)[1]);
# endif
BC_WORD *temp = (BC_WORD*) hrs->hr_reference[1];
hrs->hr_reference[1] = (BC_WORD*) *temp;
*temp = (BC_WORD) (&hrs->hr_reference[1]) | 1;
hrs = hrs->hr_rest;
BC_WORD *temp = (BC_WORD*) finalizers->cur->arg;
finalizers->cur->arg = *temp;
*temp = (BC_WORD) (&finalizers->cur->arg) | 1;
}
#endif
......
......@@ -7,6 +7,7 @@
#ifdef LINK_CLEAN_RUNTIME
# include "../copy_node.h"
# include "../finalizers.h"
#endif
#define GREY_NODES_INITIAL 100
......@@ -149,11 +150,9 @@ void mark_a_stack(BC_WORD *stack, BC_WORD *asp, BC_WORD *heap, size_t heap_size,
#ifdef LINK_CLEAN_RUNTIME
void mark_host_references(BC_WORD *heap, size_t heap_size, struct nodes_set *set) {
struct host_references *hrs = host_references;
while (hrs->hr_descriptor != (void*)((BC_WORD)&__Nil+2)) {
add_grey_node(set, (BC_WORD*) hrs->hr_reference[1], heap, heap_size);
hrs = hrs->hr_rest;
}
struct finalizers *finalizers = NULL;
while ((finalizers = next_finalizer(finalizers)) != NULL)
add_grey_node(set, (BC_WORD*) finalizers->cur->arg, heap, heap_size);
}
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment