Add structure type; allow GC to be disabled; unify object-moving code.
This commit is contained in:
parent
6829d550f5
commit
0867f66767
264
gc.c
264
gc.c
|
|
@ -13,9 +13,52 @@ gc_stats_t gc_stats;
|
||||||
/* Helper macros to reduce duplication */
|
/* Helper macros to reduce duplication */
|
||||||
#define VECTOR_BYTES(nelem) (sizeof(vector_t) + (sizeof(value_t) * (nelem)))
|
#define VECTOR_BYTES(nelem) (sizeof(vector_t) + (sizeof(value_t) * (nelem)))
|
||||||
#define BYTESTR_BYTES(size) (sizeof(byte_string_t) + (size))
|
#define BYTESTR_BYTES(size) (sizeof(byte_string_t) + (size))
|
||||||
|
#define STRUCT_BYTES(nslots) VECTOR_BYTES(nslots)
|
||||||
|
|
||||||
/* Alignment must ensure each object has enough room to hold a pair (BH . new_addr) */
|
/* Alignment must ensure each object has enough room to hold a forwarding object */
|
||||||
#define GC_ALIGNMENT ((size_t)(sizeof(pair_t)))
|
#define GC_ALIGNMENT ((size_t)(sizeof(object_t)))
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
static char *gc_ranges[2];
|
||||||
|
static size_t gc_min_size;
|
||||||
|
static size_t gc_max_size;
|
||||||
|
static size_t gc_soft_limit;
|
||||||
|
static bool gc_enabled;
|
||||||
|
|
||||||
|
static int gc_current_range;
|
||||||
|
static char *gc_free_ptr;
|
||||||
|
static char *gc_range_end;
|
||||||
|
|
||||||
|
static gc_root_t gc_root_list = {
|
||||||
|
.value = NIL,
|
||||||
|
.prev = &gc_root_list,
|
||||||
|
.next = &gc_root_list
|
||||||
|
};
|
||||||
|
|
||||||
|
void register_gc_root(gc_root_t *root, value_t v)
|
||||||
|
{
|
||||||
|
root->value = v;
|
||||||
|
root->next = &gc_root_list;
|
||||||
|
gc_root_list.prev->next = root;
|
||||||
|
root->prev = gc_root_list.prev;
|
||||||
|
gc_root_list.prev = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregister_gc_root(gc_root_t *root)
|
||||||
|
{
|
||||||
|
assert(root && root->prev && root->next); /* Uninitialized */
|
||||||
|
assert((root->prev != root) && (root->next != root)); /* Already removed */
|
||||||
|
|
||||||
|
/* Cut the given root out of the list */
|
||||||
|
root->prev->next = root->next;
|
||||||
|
root->next->prev = root->prev;
|
||||||
|
|
||||||
|
/* Remove dead references to root list; protects against double-removal */
|
||||||
|
root->prev = root->next = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
object_t *get_object(value_t v)
|
object_t *get_object(value_t v)
|
||||||
{
|
{
|
||||||
|
|
@ -124,6 +167,40 @@ byte_string_t *get_byte_string(value_t v)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_t make_struct(value_t type, size_t nslots)
|
||||||
|
{
|
||||||
|
gc_root_t type_root;
|
||||||
|
struct_t *s;
|
||||||
|
|
||||||
|
assert(nslots >= 1);
|
||||||
|
|
||||||
|
/* Ensure that there is always a slot for the type */
|
||||||
|
if (nslots < 1)
|
||||||
|
nslots = 1;
|
||||||
|
|
||||||
|
register_gc_root(&type_root, type);
|
||||||
|
|
||||||
|
s = (struct_t*)gc_alloc(STRUCT_BYTES(nslots));
|
||||||
|
s->tag = TYPE_TAG_VECTOR;
|
||||||
|
s->nslots = nslots;
|
||||||
|
s->slots[0] = type_root.value;
|
||||||
|
|
||||||
|
for (int i = 1; i < nslots; ++i)
|
||||||
|
s->slots[i] = NIL;
|
||||||
|
|
||||||
|
unregister_gc_root(&type_root);
|
||||||
|
|
||||||
|
return object_value(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_t *get_struct(value_t v)
|
||||||
|
{
|
||||||
|
if (is_struct(v))
|
||||||
|
return _get_struct(v);
|
||||||
|
else
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
intptr_t get_fixnum(value_t v)
|
intptr_t get_fixnum(value_t v)
|
||||||
{
|
{
|
||||||
if (is_fixnum(v))
|
if (is_fixnum(v))
|
||||||
|
|
@ -134,25 +211,11 @@ intptr_t get_fixnum(value_t v)
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
static char *gc_ranges[2];
|
|
||||||
static size_t gc_min_size;
|
|
||||||
static size_t gc_max_size;
|
|
||||||
static size_t gc_soft_limit;
|
|
||||||
|
|
||||||
static int gc_current_range;
|
|
||||||
static size_t gc_free_space;
|
|
||||||
static char *gc_free_ptr;
|
|
||||||
|
|
||||||
static gc_root_t gc_root_list = {
|
|
||||||
.value = NIL,
|
|
||||||
.prev = &gc_root_list,
|
|
||||||
.next = &gc_root_list
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline size_t gc_align(size_t nbytes) __attribute__ ((const));
|
static inline size_t gc_align(size_t nbytes) __attribute__ ((const));
|
||||||
static int gc_range_of(void *object) __attribute__ ((const));
|
static int gc_range_of(void *object) __attribute__ ((const));
|
||||||
static void transfer_object(value_t *value);
|
static void transfer_object(value_t *value);
|
||||||
static size_t transfer_children(object_t *object);
|
static size_t transfer_children(object_t *object);
|
||||||
|
static void _collect_garbage(size_t min_free);
|
||||||
|
|
||||||
static inline size_t gc_align(size_t nbytes)
|
static inline size_t gc_align(size_t nbytes)
|
||||||
{
|
{
|
||||||
|
|
@ -170,6 +233,11 @@ static int gc_range_of(void *object)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t gc_free_space(void)
|
||||||
|
{
|
||||||
|
return gc_range_end - gc_free_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
void gc_init(size_t min_size, size_t max_size)
|
void gc_init(size_t min_size, size_t max_size)
|
||||||
{
|
{
|
||||||
assert(min_size <= max_size);
|
assert(min_size <= max_size);
|
||||||
|
|
@ -185,70 +253,57 @@ void gc_init(size_t min_size, size_t max_size)
|
||||||
gc_min_size = min_size;
|
gc_min_size = min_size;
|
||||||
gc_max_size = max_size;
|
gc_max_size = max_size;
|
||||||
gc_soft_limit = gc_min_size;
|
gc_soft_limit = gc_min_size;
|
||||||
gc_free_space = gc_soft_limit;
|
gc_range_end = gc_free_ptr + gc_soft_limit;
|
||||||
|
|
||||||
gc_stats.collections = 0;
|
gc_stats.collections = 0;
|
||||||
gc_stats.total_ticks = 0;
|
gc_stats.total_ticks = 0;
|
||||||
gc_stats.high_water = 0;
|
gc_stats.high_water = 0;
|
||||||
|
|
||||||
|
gc_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_gc_root(gc_root_t *root, value_t v)
|
/* Preconditions: nbytes pre-aligned a la gc_align(), and space exists. */
|
||||||
|
static inline void *_gc_alloc(size_t nbytes)
|
||||||
{
|
{
|
||||||
root->value = v;
|
void *p = gc_free_ptr;
|
||||||
root->prev = &gc_root_list;
|
gc_free_ptr += nbytes;
|
||||||
root->next = gc_root_list.next;
|
return p;
|
||||||
root->next->prev = root;
|
|
||||||
gc_root_list.next = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unregister_gc_root(gc_root_t *root)
|
|
||||||
{
|
|
||||||
assert(root && root->prev && root->next); /* Uninitialized */
|
|
||||||
assert((root->prev != root) && (root->next != root)); /* Already removed */
|
|
||||||
|
|
||||||
/* Cut the given root out of the list */
|
|
||||||
root->prev->next = root->next;
|
|
||||||
root->next->prev = root->prev;
|
|
||||||
|
|
||||||
/* Remove dead references to root list; protects against double-removal */
|
|
||||||
root->prev = root->next = root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *gc_alloc(size_t nbytes)
|
void *gc_alloc(size_t nbytes)
|
||||||
{
|
{
|
||||||
nbytes = gc_align(nbytes);
|
nbytes = gc_align(nbytes);
|
||||||
|
|
||||||
if (nbytes > gc_free_space)
|
if (nbytes > gc_free_space())
|
||||||
collect_garbage(nbytes);
|
_collect_garbage(nbytes);
|
||||||
|
|
||||||
void *p = gc_free_ptr;
|
return _gc_alloc(nbytes);
|
||||||
gc_free_ptr += nbytes;
|
|
||||||
gc_free_space -= nbytes;
|
|
||||||
//debug(("Found %d bytes at %#.8p.\n", nbytes, p));
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Precondition: *value refers to an object (or pair). */
|
||||||
static void transfer_object(value_t *value)
|
static void transfer_object(value_t *value)
|
||||||
{
|
{
|
||||||
if (is_object(*value))
|
object_t *obj;
|
||||||
{
|
size_t nbytes;
|
||||||
object_t *obj = get_object(*value);
|
void *newobj;
|
||||||
value_t new_value;
|
value_t new_value;
|
||||||
|
|
||||||
assert(gc_range_of(obj) != gc_current_range);
|
assert(gc_range_of(obj) != gc_current_range);
|
||||||
|
assert(is_object(*value));
|
||||||
|
|
||||||
|
obj = _get_object(*value);
|
||||||
|
|
||||||
if (obj->tag == BROKEN_HEART)
|
if (obj->tag == BROKEN_HEART)
|
||||||
{
|
{
|
||||||
/* Object has already been moved; just update the reference */
|
/* Object has already been moved; just update the reference */
|
||||||
new_value = obj->payload.values[0];
|
*value = obj->forward;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t nbytes;
|
|
||||||
|
|
||||||
switch (obj->tag)
|
switch (obj->tag)
|
||||||
{
|
{
|
||||||
case TYPE_TAG_VECTOR:
|
case TYPE_TAG_VECTOR:
|
||||||
|
case TYPE_TAG_STRUCT:
|
||||||
nbytes = VECTOR_BYTES(((const vector_t*)obj)->size);
|
nbytes = VECTOR_BYTES(((const vector_t*)obj)->size);
|
||||||
break;
|
break;
|
||||||
case TYPE_TAG_BYTESTR:
|
case TYPE_TAG_BYTESTR:
|
||||||
|
|
@ -260,77 +315,73 @@ static void transfer_object(value_t *value)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
newobj = _gc_alloc(gc_align(nbytes));
|
||||||
void *newobj = gc_alloc(nbytes);
|
|
||||||
memcpy(newobj, obj, nbytes);
|
memcpy(newobj, obj, nbytes);
|
||||||
|
|
||||||
/* Keep the original tag bits (pair or object) */
|
/* Keep the original tag bits (pair or object) */
|
||||||
new_value = object_value(newobj) | (*value & 2);
|
new_value = object_value(newobj) | (*value & 2);
|
||||||
}
|
|
||||||
|
|
||||||
obj->tag = BROKEN_HEART;
|
obj->tag = BROKEN_HEART;
|
||||||
obj->payload.values[0] = new_value;
|
obj->forward = new_value;
|
||||||
}
|
|
||||||
|
|
||||||
*value = new_value;
|
*value = new_value;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t transfer_children(object_t *obj)
|
/* Also works on structs, which share the same layout */
|
||||||
|
static size_t transfer_vector(vector_t *vec)
|
||||||
{
|
{
|
||||||
switch (obj->tag)
|
|
||||||
{
|
|
||||||
case TYPE_TAG_VECTOR:
|
|
||||||
{
|
|
||||||
vector_t *vec = (vector_t*)obj;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < vec->size; ++i)
|
for (size_t i = 0; i < vec->size; ++i)
|
||||||
{
|
{
|
||||||
|
if (is_object(vec->elements[i]))
|
||||||
transfer_object(&vec->elements[i]);
|
transfer_object(&vec->elements[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return VECTOR_BYTES(vec->size);
|
return VECTOR_BYTES(vec->size);
|
||||||
}
|
}
|
||||||
case TYPE_TAG_BYTESTR:
|
|
||||||
|
static size_t transfer_pair(pair_t *p)
|
||||||
{
|
{
|
||||||
return BYTESTR_BYTES(((const byte_string_t*)obj)->size);
|
if (is_object(p->car))
|
||||||
}
|
|
||||||
case TYPE_TAG_BOX:
|
|
||||||
default: /* pair */
|
|
||||||
{
|
|
||||||
pair_t *p = (pair_t*)obj;
|
|
||||||
transfer_object(&p->car);
|
transfer_object(&p->car);
|
||||||
|
|
||||||
|
if (is_object(p->cdr))
|
||||||
transfer_object(&p->cdr);
|
transfer_object(&p->cdr);
|
||||||
|
|
||||||
return sizeof(pair_t);
|
return sizeof(pair_t);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void collect_garbage(size_t min_free)
|
static size_t transfer_children(object_t *obj)
|
||||||
{
|
{
|
||||||
static bool collecting = false;
|
switch (obj->tag)
|
||||||
|
{
|
||||||
|
case TYPE_TAG_VECTOR:
|
||||||
|
case TYPE_TAG_STRUCT:
|
||||||
|
return transfer_vector((vector_t*)obj);
|
||||||
|
case TYPE_TAG_BYTESTR:
|
||||||
|
return BYTESTR_BYTES(((const byte_string_t*)obj)->size);
|
||||||
|
case TYPE_TAG_BOX:
|
||||||
|
default: /* pair */
|
||||||
|
return transfer_pair((pair_t*)obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _collect_garbage(size_t min_free)
|
||||||
|
{
|
||||||
gc_root_t *root;
|
gc_root_t *root;
|
||||||
char *object_ptr;
|
char *object_ptr;
|
||||||
|
|
||||||
//debug(("Collecting garbage...\n"));
|
if (gc_enabled)
|
||||||
|
|
||||||
++gc_stats.collections;
|
|
||||||
gc_stats.total_ticks -= clock();
|
|
||||||
|
|
||||||
/* Recursive calls to collector should never occur */
|
|
||||||
if (collecting)
|
|
||||||
{
|
{
|
||||||
debug(("Ran out of memory while collecting garbage!\n"));
|
gc_stats.total_ticks -= clock();
|
||||||
abort();
|
++gc_stats.collections;
|
||||||
}
|
|
||||||
else
|
//debug(("Collecting garbage...\n"));
|
||||||
collecting = true;
|
|
||||||
|
|
||||||
/* Swap ranges; new "current" range is initially empty, old one is full */
|
/* Swap ranges; new "current" range is initially empty, old one is full */
|
||||||
gc_current_range = 1 - gc_current_range;
|
gc_current_range = 1 - gc_current_range;
|
||||||
gc_free_ptr = (char*)&gc_ranges[gc_current_range][0];
|
gc_free_ptr = gc_ranges[gc_current_range];
|
||||||
gc_free_space = gc_soft_limit;
|
gc_range_end = gc_free_ptr + gc_soft_limit;
|
||||||
object_ptr = gc_free_ptr;
|
object_ptr = gc_free_ptr;
|
||||||
|
|
||||||
/* Transfer GC roots (if necessary) */
|
/* Transfer GC roots (if necessary) */
|
||||||
|
|
@ -338,6 +389,7 @@ void collect_garbage(size_t min_free)
|
||||||
|
|
||||||
while (root != &gc_root_list)
|
while (root != &gc_root_list)
|
||||||
{
|
{
|
||||||
|
if (is_object(root->value))
|
||||||
transfer_object(&root->value);
|
transfer_object(&root->value);
|
||||||
root = root->next;
|
root = root->next;
|
||||||
}
|
}
|
||||||
|
|
@ -348,23 +400,33 @@ void collect_garbage(size_t min_free)
|
||||||
object_ptr += gc_align(transfer_children((object_t*)object_ptr));
|
object_ptr += gc_align(transfer_children((object_t*)object_ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
//debug(("Finished collection with %d bytes to spare (out of %d bytes).\n", gc_free_space, gc_soft_limit));
|
//debug(("Finished collection with %d bytes to spare (out of %d bytes).\n", gc_free_space(), gc_soft_limit));
|
||||||
|
|
||||||
|
gc_stats.total_ticks += clock();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t bytes_used = gc_soft_limit - gc_free_space;
|
size_t bytes_used = gc_free_ptr - gc_ranges[gc_current_range];
|
||||||
size_t min_limit = bytes_used + min_free;
|
size_t min_limit = bytes_used + min_free;
|
||||||
size_t new_limit = (5 * min_limit) / 3;
|
size_t new_limit = (5 * min_limit) / 3;
|
||||||
|
|
||||||
if (new_limit > gc_max_size)
|
if (new_limit > gc_max_size)
|
||||||
new_limit = gc_max_size;
|
new_limit = gc_max_size;
|
||||||
|
#if 0
|
||||||
else if (new_limit < gc_min_size)
|
else if (new_limit < gc_min_size)
|
||||||
new_limit = gc_min_size;
|
new_limit = gc_min_size;
|
||||||
|
|
||||||
gc_free_space = (gc_free_space + new_limit) - gc_soft_limit;
|
|
||||||
gc_soft_limit = new_limit;
|
gc_soft_limit = new_limit;
|
||||||
|
#else
|
||||||
|
if (new_limit > gc_soft_limit)
|
||||||
|
gc_soft_limit = new_limit;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gc_free_space < min_free)
|
/* Update end of range to reflect new limit */
|
||||||
|
gc_range_end = gc_ranges[gc_current_range] + gc_soft_limit;
|
||||||
|
|
||||||
|
if (gc_free_space() < min_free)
|
||||||
{
|
{
|
||||||
out_of_memory();
|
out_of_memory();
|
||||||
}
|
}
|
||||||
|
|
@ -373,11 +435,21 @@ void collect_garbage(size_t min_free)
|
||||||
{
|
{
|
||||||
gc_stats.high_water = gc_soft_limit;
|
gc_stats.high_water = gc_soft_limit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Done collecting. */
|
void collect_garbage(size_t min_free)
|
||||||
collecting = false;
|
{
|
||||||
|
bool was_enabled = gc_enabled;
|
||||||
|
gc_enabled = true;
|
||||||
|
_collect_garbage(min_free);
|
||||||
|
gc_enabled = was_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
gc_stats.total_ticks += clock();
|
bool set_gc_enabled(bool enable)
|
||||||
|
{
|
||||||
|
bool was_enabled = gc_enabled;
|
||||||
|
gc_enabled = enable;
|
||||||
|
return was_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim:set sw=2 expandtab: */
|
/* vim:set sw=2 expandtab: */
|
||||||
|
|
|
||||||
51
gc.h
51
gc.h
|
|
@ -30,42 +30,49 @@ typedef uintptr_t value_t;
|
||||||
#define TYPE_TAG_BOX SPECIAL_VALUE(1)
|
#define TYPE_TAG_BOX SPECIAL_VALUE(1)
|
||||||
#define TYPE_TAG_VECTOR SPECIAL_VALUE(2)
|
#define TYPE_TAG_VECTOR SPECIAL_VALUE(2)
|
||||||
#define TYPE_TAG_BYTESTR SPECIAL_VALUE(3)
|
#define TYPE_TAG_BYTESTR SPECIAL_VALUE(3)
|
||||||
|
#define TYPE_TAG_STRUCT SPECIAL_VALUE(4)
|
||||||
|
|
||||||
typedef struct object
|
typedef struct object
|
||||||
{
|
{
|
||||||
value_t tag;
|
value_t tag;
|
||||||
union {
|
value_t forward; /* only if tag == BROKEN_HEART */
|
||||||
value_t values[0];
|
|
||||||
char bytes[0];
|
|
||||||
} payload;
|
|
||||||
} object_t;
|
} object_t;
|
||||||
|
|
||||||
typedef struct box
|
/* CAR is anything *other* than a valid type tag or BROKEN_HEART. */
|
||||||
{
|
|
||||||
value_t tag;
|
|
||||||
value_t value;
|
|
||||||
} box_t;
|
|
||||||
|
|
||||||
typedef struct pair
|
typedef struct pair
|
||||||
{
|
{
|
||||||
value_t car;
|
value_t car;
|
||||||
value_t cdr;
|
value_t cdr;
|
||||||
} pair_t;
|
} pair_t;
|
||||||
|
|
||||||
|
typedef struct box
|
||||||
|
{
|
||||||
|
value_t tag; /* TYPE_TAG_BOX */
|
||||||
|
value_t value;
|
||||||
|
} box_t;
|
||||||
|
|
||||||
typedef struct vector
|
typedef struct vector
|
||||||
{
|
{
|
||||||
value_t tag;
|
value_t tag; /* TYPE_TAG_VECTOR */
|
||||||
size_t size;
|
size_t size;
|
||||||
value_t elements[0];
|
value_t elements[0];
|
||||||
} vector_t;
|
} vector_t;
|
||||||
|
|
||||||
typedef struct byte_string
|
typedef struct byte_string
|
||||||
{
|
{
|
||||||
value_t tag;
|
value_t tag; /* TYPE_TAG_BYTESTR */
|
||||||
size_t size;
|
size_t size;
|
||||||
uint8_t bytes[0];
|
uint8_t bytes[0];
|
||||||
} byte_string_t;
|
} byte_string_t;
|
||||||
|
|
||||||
|
/* Equivalent to vector_t */
|
||||||
|
typedef struct structure
|
||||||
|
{
|
||||||
|
value_t tag; /* TYPE_TAG_STRUCT */
|
||||||
|
size_t nslots; /* Includes slots[0], the struct subtype */
|
||||||
|
value_t slots[0];
|
||||||
|
} struct_t;
|
||||||
|
|
||||||
typedef struct gc_root
|
typedef struct gc_root
|
||||||
{
|
{
|
||||||
value_t value;
|
value_t value;
|
||||||
|
|
@ -96,6 +103,10 @@ vector_t *get_vector(value_t v);
|
||||||
value_t make_byte_string(size_t size, int default_value);
|
value_t make_byte_string(size_t size, int default_value);
|
||||||
byte_string_t *get_byte_string(value_t v);
|
byte_string_t *get_byte_string(value_t v);
|
||||||
|
|
||||||
|
/* Precondition: slots >= 1; this includes the struct type tag, slots[0]. */
|
||||||
|
value_t make_struct(value_t type, size_t slots);
|
||||||
|
struct_t *get_struct(value_t v);
|
||||||
|
|
||||||
intptr_t get_fixnum(value_t v);
|
intptr_t get_fixnum(value_t v);
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
@ -176,6 +187,21 @@ static inline size_t byte_string_size(value_t v)
|
||||||
return get_byte_string(v)->size;
|
return get_byte_string(v)->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_struct(value_t v)
|
||||||
|
{
|
||||||
|
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_STRUCT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct_t *_get_struct(value_t v)
|
||||||
|
{
|
||||||
|
return (struct_t*)_get_object(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t struct_type(value_t v)
|
||||||
|
{
|
||||||
|
return get_struct(v)->slots[0];
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool is_fixnum(value_t v)
|
static inline bool is_fixnum(value_t v)
|
||||||
{
|
{
|
||||||
return (v & 1) != 0;
|
return (v & 1) != 0;
|
||||||
|
|
@ -196,6 +222,7 @@ void register_gc_root(gc_root_t *root, value_t v);
|
||||||
void unregister_gc_root(gc_root_t *root);
|
void unregister_gc_root(gc_root_t *root);
|
||||||
void *gc_alloc(size_t nbytes);
|
void *gc_alloc(size_t nbytes);
|
||||||
void collect_garbage(size_t min_free);
|
void collect_garbage(size_t min_free);
|
||||||
|
bool set_gc_enabled(bool enable);
|
||||||
|
|
||||||
/* To be provided by the main application */
|
/* To be provided by the main application */
|
||||||
void out_of_memory(void) __attribute__ ((noreturn));
|
void out_of_memory(void) __attribute__ ((noreturn));
|
||||||
|
|
|
||||||
24
gc_test.c
24
gc_test.c
|
|
@ -28,7 +28,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
int r = rand() & 0xffff;
|
int r = rand() & 0x3fff;
|
||||||
|
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
list_root.value = make_fixnum(rand());
|
list_root.value = make_fixnum(rand());
|
||||||
|
|
@ -48,29 +48,29 @@ int main(int argc, char **argv)
|
||||||
case 3:
|
case 3:
|
||||||
list_root.value = cons(list_root.value, cons(make_fixnum(-1), NIL));
|
list_root.value = cons(list_root.value, cons(make_fixnum(-1), NIL));
|
||||||
get_pair(get_pair(list_root.value)->cdr)->cdr = list_root.value;
|
get_pair(get_pair(list_root.value)->cdr)->cdr = list_root.value;
|
||||||
++count;
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
case 7:
|
case 7:
|
||||||
{
|
{
|
||||||
value_t vec = make_vector(4, NIL);
|
value_t s = make_struct(NIL, 5);
|
||||||
_get_vector(vec)->elements[r & 3] = list_root.value;
|
_get_struct(s)->slots[1+(r & 3)] = list_root.value;
|
||||||
list_root.value = vec;
|
list_root.value = s;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
++count;
|
|
||||||
|
|
||||||
if (count >= 10000000)
|
if (++count >= 10000000)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%0.3f usec / GC; max. limit was %u bytes; %0.3f seconds spent in %d GCs.\n",
|
const double total_time = gc_stats.total_ticks / (double)CLOCKS_PER_SEC;
|
||||||
(1000000 * (gc_stats.total_ticks / (double)CLOCKS_PER_SEC)) / gc_stats.collections,
|
|
||||||
gc_stats.high_water,
|
fprintf(stderr, "%0.3f sec / %d GCs => %0.3f usec/GC; peak was %u bytes.\n",
|
||||||
gc_stats.total_ticks / (double)CLOCKS_PER_SEC,
|
total_time,
|
||||||
gc_stats.collections);
|
gc_stats.collections,
|
||||||
|
(1000000 * total_time) / gc_stats.collections,
|
||||||
|
gc_stats.high_water);
|
||||||
|
|
||||||
gc_stats.collections = 0;
|
gc_stats.collections = 0;
|
||||||
gc_stats.total_ticks = 0;
|
gc_stats.total_ticks = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue