2019-10-18 09:05:08 -04:00
|
|
|
#include "master.h"
|
2004-07-16 02:26:21 -04:00
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* If memory allocation fails, bail out */
|
2006-07-07 00:07:18 -04:00
|
|
|
void *safe_malloc(size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ptr = malloc(size);
|
2019-10-18 09:05:06 -04:00
|
|
|
if(!ptr) fatal_error("Out of memory in safe_malloc", 0);
|
2006-07-07 00:07:18 -04:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
CELL init_zone(F_ZONE *z, CELL size, CELL start)
|
|
|
|
|
{
|
|
|
|
|
z->size = size;
|
|
|
|
|
z->start = z->here = start;
|
|
|
|
|
z->end = start + size;
|
|
|
|
|
return z->end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void init_cards_offset(void)
|
|
|
|
|
{
|
|
|
|
|
cards_offset = (CELL)data_heap->cards
|
|
|
|
|
- (data_heap->segment->start >> CARD_BITS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* the data heap layout is important:
|
|
|
|
|
- two semispaces: tenured and prior
|
|
|
|
|
- younger generations follow
|
|
|
|
|
this is so that we can easily check if a pointer is in some generation or a
|
|
|
|
|
younger one */
|
|
|
|
|
F_DATA_HEAP *alloc_data_heap(CELL gens, CELL young_size, CELL aging_size)
|
|
|
|
|
{
|
|
|
|
|
young_size = align_page(young_size);
|
|
|
|
|
aging_size = align_page(aging_size);
|
|
|
|
|
|
|
|
|
|
F_DATA_HEAP *data_heap = safe_malloc(sizeof(F_DATA_HEAP));
|
|
|
|
|
data_heap->young_size = young_size;
|
|
|
|
|
data_heap->aging_size = aging_size;
|
|
|
|
|
data_heap->gen_count = gens;
|
|
|
|
|
|
|
|
|
|
CELL total_size = (gens - 1) * young_size + 2 * aging_size;
|
|
|
|
|
data_heap->segment = alloc_segment(total_size);
|
|
|
|
|
|
|
|
|
|
data_heap->generations = safe_malloc(sizeof(F_ZONE) * gens);
|
|
|
|
|
|
|
|
|
|
CELL cards_size = total_size / CARD_SIZE;
|
|
|
|
|
data_heap->cards = safe_malloc(cards_size);
|
|
|
|
|
data_heap->cards_end = data_heap->cards + cards_size;
|
|
|
|
|
|
|
|
|
|
CELL alloter = data_heap->segment->start;
|
|
|
|
|
|
|
|
|
|
alloter = init_zone(&tenured,aging_size,alloter);
|
|
|
|
|
alloter = init_zone(&data_heap->prior,aging_size,alloter);
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for(i = gens - 2; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
alloter = init_zone(&data_heap->generations[i],
|
|
|
|
|
young_size,alloter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(alloter != data_heap->segment->end)
|
|
|
|
|
critical_error("Bug in alloc_data_heap",alloter);
|
|
|
|
|
|
|
|
|
|
return data_heap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
F_DATA_HEAP *grow_data_heap(F_DATA_HEAP *data_heap, CELL requested_bytes)
|
|
|
|
|
{
|
|
|
|
|
CELL new_young_size = (data_heap->young_size * 2) + requested_bytes;
|
|
|
|
|
CELL new_aging_size = (data_heap->aging_size * 2) + requested_bytes;
|
|
|
|
|
|
|
|
|
|
return alloc_data_heap(data_heap->gen_count,
|
|
|
|
|
new_young_size,
|
|
|
|
|
new_aging_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dealloc_data_heap(F_DATA_HEAP *data_heap)
|
|
|
|
|
{
|
|
|
|
|
dealloc_segment(data_heap->segment);
|
|
|
|
|
free(data_heap->generations);
|
|
|
|
|
free(data_heap->cards);
|
|
|
|
|
free(data_heap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Every card stores the offset of the first object in that card, which must be
|
|
|
|
|
cleared when a generation has been cleared */
|
|
|
|
|
void clear_cards(CELL from, CELL to)
|
|
|
|
|
{
|
|
|
|
|
/* NOTE: reverse order due to heap layout. */
|
|
|
|
|
F_CARD *last_card = ADDR_TO_CARD(data_heap->generations[from].end);
|
|
|
|
|
F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[to].start);
|
|
|
|
|
for(; ptr < last_card; ptr++)
|
|
|
|
|
clear_card(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_data_heap(F_DATA_HEAP *data_heap_)
|
|
|
|
|
{
|
|
|
|
|
data_heap = data_heap_;
|
|
|
|
|
nursery = &data_heap->generations[NURSERY];
|
|
|
|
|
init_cards_offset();
|
|
|
|
|
clear_cards(NURSERY,TENURED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void init_data_heap(CELL gens,
|
|
|
|
|
CELL young_size,
|
|
|
|
|
CELL aging_size,
|
|
|
|
|
bool secure_gc_)
|
|
|
|
|
{
|
|
|
|
|
set_data_heap(alloc_data_heap(gens,young_size,aging_size));
|
|
|
|
|
|
|
|
|
|
extra_roots_region = alloc_segment(getpagesize());
|
|
|
|
|
extra_roots = extra_roots_region->start - CELLS;
|
|
|
|
|
|
|
|
|
|
gc_time = 0;
|
|
|
|
|
minor_collections = 0;
|
|
|
|
|
cards_scanned = 0;
|
|
|
|
|
secure_gc = secure_gc_;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Size of the object pointed to by a tagged pointer */
|
2006-05-18 01:08:09 -04:00
|
|
|
CELL object_size(CELL tagged)
|
2005-05-10 22:30:58 -04:00
|
|
|
{
|
2006-07-31 17:36:15 -04:00
|
|
|
if(tagged == F || TAG(tagged) == FIXNUM_TYPE)
|
2006-05-18 01:08:09 -04:00
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return untagged_object_size(UNTAG(tagged));
|
2004-12-10 21:39:45 -05:00
|
|
|
}
|
2005-05-10 22:30:58 -04:00
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Size of the object pointed to by an untagged pointer */
|
2005-05-13 00:09:49 -04:00
|
|
|
CELL untagged_object_size(CELL pointer)
|
2004-08-12 01:07:22 -04:00
|
|
|
{
|
2006-07-31 17:36:15 -04:00
|
|
|
return align8(unaligned_object_size(pointer));
|
|
|
|
|
}
|
2004-08-12 01:07:22 -04:00
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Size of the data area of an object pointed to by an untagged pointer */
|
2006-07-31 17:36:15 -04:00
|
|
|
CELL unaligned_object_size(CELL pointer)
|
|
|
|
|
{
|
2005-05-13 00:09:49 -04:00
|
|
|
switch(untag_header(get(pointer)))
|
|
|
|
|
{
|
|
|
|
|
case WORD_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_WORD);
|
2005-05-13 00:09:49 -04:00
|
|
|
case ARRAY_TYPE:
|
|
|
|
|
case TUPLE_TYPE:
|
|
|
|
|
case BIGNUM_TYPE:
|
2006-05-15 18:15:35 -04:00
|
|
|
case QUOTATION_TYPE:
|
2019-10-18 09:05:06 -04:00
|
|
|
return array_size(array_capacity((F_ARRAY*)pointer));
|
|
|
|
|
case BYTE_ARRAY_TYPE:
|
|
|
|
|
return byte_array_size(
|
|
|
|
|
byte_array_capacity((F_BYTE_ARRAY*)pointer));
|
2019-10-18 09:05:08 -04:00
|
|
|
case BIT_ARRAY_TYPE:
|
|
|
|
|
return bit_array_size(
|
|
|
|
|
bit_array_capacity((F_BIT_ARRAY*)pointer));
|
2005-05-13 00:09:49 -04:00
|
|
|
case HASHTABLE_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_HASHTABLE);
|
2005-05-13 00:09:49 -04:00
|
|
|
case VECTOR_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_VECTOR);
|
2005-05-13 00:09:49 -04:00
|
|
|
case STRING_TYPE:
|
2019-10-18 09:05:06 -04:00
|
|
|
return string_size(string_capacity((F_STRING*)pointer));
|
2005-05-13 00:09:49 -04:00
|
|
|
case SBUF_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_SBUF);
|
2006-05-15 18:00:37 -04:00
|
|
|
case RATIO_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_RATIO);
|
2005-05-13 00:09:49 -04:00
|
|
|
case FLOAT_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_FLOAT);
|
2006-05-15 18:00:37 -04:00
|
|
|
case COMPLEX_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_COMPLEX);
|
2005-05-13 00:09:49 -04:00
|
|
|
case DLL_TYPE:
|
2006-10-31 23:20:34 -05:00
|
|
|
return sizeof(F_DLL);
|
2005-05-13 00:09:49 -04:00
|
|
|
case ALIEN_TYPE:
|
2006-10-31 23:20:34 -05:00
|
|
|
return sizeof(F_ALIEN);
|
2005-08-03 23:56:28 -04:00
|
|
|
case WRAPPER_TYPE:
|
2006-07-31 17:36:15 -04:00
|
|
|
return sizeof(F_WRAPPER);
|
2005-05-13 00:09:49 -04:00
|
|
|
default:
|
|
|
|
|
critical_error("Cannot determine untagged_object_size",pointer);
|
2006-07-31 17:36:15 -04:00
|
|
|
return -1; /* can't happen */
|
2005-05-13 00:09:49 -04:00
|
|
|
}
|
2006-07-31 17:36:15 -04:00
|
|
|
}
|
2004-08-12 23:40:28 -04:00
|
|
|
|
2006-09-26 18:44:18 -04:00
|
|
|
void primitive_size(void)
|
|
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
box_unsigned_cell(object_size(dpop()));
|
2006-09-26 18:44:18 -04:00
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Push memory usage statistics in data heap */
|
2006-09-26 01:08:05 -04:00
|
|
|
void primitive_data_room(void)
|
2005-05-13 00:09:49 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
F_ARRAY *a = allot_array(ARRAY_TYPE,data_heap->gen_count * 2,F);
|
2005-05-13 00:09:49 -04:00
|
|
|
int gen;
|
2006-10-31 00:52:02 -05:00
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
dpush(tag_fixnum((data_heap->cards_end - data_heap->cards) >> 10));
|
|
|
|
|
dpush(tag_fixnum((data_heap->prior.size) >> 10));
|
2006-11-01 14:29:58 -05:00
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
for(gen = 0; gen < data_heap->gen_count; gen++)
|
2005-05-13 00:09:49 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
F_ZONE *z = &data_heap->generations[gen];
|
|
|
|
|
set_array_nth(a,gen * 2,tag_fixnum((z->end - z->here) >> 10));
|
|
|
|
|
set_array_nth(a,gen * 2 + 1,tag_fixnum((z->size) >> 10));
|
2005-05-13 00:09:49 -04:00
|
|
|
}
|
2006-10-31 00:52:02 -05:00
|
|
|
|
2006-05-15 00:03:55 -04:00
|
|
|
dpush(tag_object(a));
|
2005-05-13 00:09:49 -04:00
|
|
|
}
|
|
|
|
|
|
2006-05-18 01:08:09 -04:00
|
|
|
/* Disables GC and activates next-object ( -- obj ) primitive */
|
2005-02-18 20:37:01 -05:00
|
|
|
void primitive_begin_scan(void)
|
|
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
primitive_data_gc();
|
|
|
|
|
heap_scan_ptr = tenured.start;
|
2006-10-31 00:52:02 -05:00
|
|
|
gc_off = true;
|
2005-02-18 20:37:01 -05:00
|
|
|
}
|
|
|
|
|
|
2006-05-18 01:08:09 -04:00
|
|
|
/* Push object at heap scan cursor and advance; pushes f when done */
|
2005-02-18 20:37:01 -05:00
|
|
|
void primitive_next_object(void)
|
|
|
|
|
{
|
|
|
|
|
CELL value = get(heap_scan_ptr);
|
|
|
|
|
CELL obj = heap_scan_ptr;
|
2006-05-17 14:55:46 -04:00
|
|
|
CELL type;
|
2005-02-18 20:37:01 -05:00
|
|
|
|
2006-10-31 00:52:02 -05:00
|
|
|
if(!gc_off)
|
2019-10-18 09:05:04 -04:00
|
|
|
simple_error(ERROR_HEAP_SCAN,F,F);
|
2005-02-18 20:37:01 -05:00
|
|
|
|
2005-05-13 00:09:49 -04:00
|
|
|
if(heap_scan_ptr >= tenured.here)
|
2005-02-18 20:37:01 -05:00
|
|
|
{
|
|
|
|
|
dpush(F);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-05-17 14:55:46 -04:00
|
|
|
type = untag_header(value);
|
2006-05-18 01:08:09 -04:00
|
|
|
heap_scan_ptr += untagged_object_size(heap_scan_ptr);
|
2005-02-18 20:37:01 -05:00
|
|
|
|
2006-05-17 19:05:44 -04:00
|
|
|
if(type <= HEADER_TYPE)
|
2005-02-18 20:37:01 -05:00
|
|
|
dpush(RETAG(obj,type));
|
|
|
|
|
else
|
|
|
|
|
dpush(RETAG(obj,OBJECT_TYPE));
|
|
|
|
|
}
|
|
|
|
|
|
2006-05-18 01:08:09 -04:00
|
|
|
/* Re-enables GC */
|
2005-02-18 20:37:01 -05:00
|
|
|
void primitive_end_scan(void)
|
|
|
|
|
{
|
2006-10-31 00:52:02 -05:00
|
|
|
gc_off = false;
|
2004-09-06 22:39:12 -04:00
|
|
|
}
|
2006-07-07 00:07:18 -04:00
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Scan all the objects in the card */
|
2006-10-31 23:20:34 -05:00
|
|
|
INLINE void collect_card(F_CARD *ptr, CELL here)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2006-10-31 23:20:34 -05:00
|
|
|
F_CARD c = *ptr;
|
2006-07-07 00:07:18 -04:00
|
|
|
CELL offset = (c & CARD_BASE_MASK);
|
|
|
|
|
CELL card_scan = (CELL)CARD_TO_ADDR(ptr) + offset;
|
|
|
|
|
CELL card_end = (CELL)CARD_TO_ADDR(ptr + 1);
|
|
|
|
|
|
|
|
|
|
if(offset == 0x7f)
|
|
|
|
|
{
|
|
|
|
|
if(c == 0xff)
|
|
|
|
|
critical_error("bad card",(CELL)ptr);
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(card_scan < card_end && card_scan < here)
|
|
|
|
|
card_scan = collect_next(card_scan);
|
|
|
|
|
|
|
|
|
|
cards_scanned++;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Copy all newspace objects referenced from marked cards to the destination */
|
2006-07-07 00:07:18 -04:00
|
|
|
INLINE void collect_gen_cards(CELL gen)
|
|
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[gen].start);
|
|
|
|
|
CELL here = data_heap->generations[gen].here;
|
|
|
|
|
F_CARD *last_card = ADDR_TO_CARD(here - 1);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
for(; ptr <= last_card; ptr++)
|
|
|
|
|
{
|
|
|
|
|
if(card_marked(*ptr))
|
|
|
|
|
collect_card(ptr,here);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* After all old->new forward references have been copied over, we must unmark
|
|
|
|
|
the cards */
|
2006-07-07 00:07:18 -04:00
|
|
|
void unmark_cards(CELL from, CELL to)
|
|
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
F_CARD *ptr = ADDR_TO_CARD(data_heap->generations[from].start);
|
|
|
|
|
CELL here = data_heap->generations[to].here;
|
|
|
|
|
F_CARD *last_card = ADDR_TO_CARD(here - 1);
|
|
|
|
|
|
2006-07-07 00:07:18 -04:00
|
|
|
for(; ptr <= last_card; ptr++)
|
|
|
|
|
unmark_card(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Scan cards in all generations older than the one being collected, copying
|
|
|
|
|
old->new references */
|
2006-07-07 00:07:18 -04:00
|
|
|
void collect_cards(CELL gen)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2019-10-18 09:05:06 -04:00
|
|
|
for(i = gen + 1; i < data_heap->gen_count; i++)
|
2006-07-07 00:07:18 -04:00
|
|
|
collect_gen_cards(i);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
void collect_callframe(F_INTERP_FRAME *callframe)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
callframe->scan -= callframe->quot;
|
|
|
|
|
callframe->end -= callframe->quot;
|
|
|
|
|
copy_handle(&callframe->quot);
|
|
|
|
|
callframe->scan += callframe->quot;
|
|
|
|
|
callframe->end += callframe->quot;
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Copy all tagged pointers in a range of memory */
|
2006-11-02 18:29:11 -05:00
|
|
|
void collect_stack(F_SEGMENT *region, CELL top)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
|
|
|
|
CELL bottom = region->start;
|
|
|
|
|
CELL ptr;
|
|
|
|
|
|
|
|
|
|
for(ptr = bottom; ptr <= top; ptr += CELLS)
|
|
|
|
|
copy_handle((CELL*)ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* The callstack has a special format */
|
2019-10-18 09:05:06 -04:00
|
|
|
void collect_callstack(F_SEGMENT *seg, F_INTERP_FRAME *top)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
F_INTERP_FRAME *bottom = (F_INTERP_FRAME *)seg->start;
|
|
|
|
|
while(bottom < top)
|
|
|
|
|
{
|
|
|
|
|
collect_callframe(bottom);
|
|
|
|
|
bottom++;
|
|
|
|
|
}
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Copy roots over at the start of GC, namely various constants, stacks,
|
|
|
|
|
the user environment and extra roots registered with REGISTER_ROOT */
|
2006-07-07 00:07:18 -04:00
|
|
|
void collect_roots(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2019-10-18 09:05:04 -04:00
|
|
|
F_CONTEXT *stacks;
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
copy_handle(&T);
|
|
|
|
|
copy_handle(&bignum_zero);
|
|
|
|
|
copy_handle(&bignum_pos_one);
|
|
|
|
|
copy_handle(&bignum_neg_one);
|
2019-10-18 09:05:06 -04:00
|
|
|
|
|
|
|
|
collect_callframe(&callframe);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
2006-11-02 18:29:11 -05:00
|
|
|
collect_stack(extra_roots_region,extra_roots);
|
2006-11-01 00:20:49 -05:00
|
|
|
|
2006-07-07 00:07:18 -04:00
|
|
|
save_stacks();
|
|
|
|
|
stacks = stack_chain;
|
|
|
|
|
|
|
|
|
|
while(stacks)
|
|
|
|
|
{
|
|
|
|
|
collect_stack(stacks->data_region,stacks->data);
|
|
|
|
|
collect_stack(stacks->retain_region,stacks->retain);
|
|
|
|
|
collect_callstack(stacks->call_region,stacks->call);
|
|
|
|
|
|
|
|
|
|
if(stacks->next != NULL)
|
2019-10-18 09:05:06 -04:00
|
|
|
collect_callframe(&stacks->callframe);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
2019-10-18 09:05:04 -04:00
|
|
|
copy_handle(&stacks->catchstack_save);
|
|
|
|
|
copy_handle(&stacks->current_callback_save);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
stacks = stacks->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < USER_ENV; i++)
|
|
|
|
|
copy_handle(&userenv[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Given a pointer to oldspace, copy it to newspace */
|
2006-07-07 00:07:18 -04:00
|
|
|
INLINE void *copy_untagged_object(void *pointer, CELL size)
|
|
|
|
|
{
|
|
|
|
|
void *newpointer;
|
2019-10-18 09:05:06 -04:00
|
|
|
if(newspace->here + size >= newspace->end)
|
2006-07-07 00:07:18 -04:00
|
|
|
longjmp(gc_jmp,1);
|
2019-10-18 09:05:06 -04:00
|
|
|
allot_barrier(newspace->here);
|
2006-07-07 00:07:18 -04:00
|
|
|
newpointer = allot_zone(newspace,size);
|
|
|
|
|
memcpy(newpointer,pointer,size);
|
|
|
|
|
return newpointer;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:08 -04:00
|
|
|
INLINE void forward_object(CELL pointer, CELL newpointer)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
|
|
|
|
put(UNTAG(pointer),RETAG(newpointer,GC_COLLECTED));
|
2019-10-18 09:05:08 -04:00
|
|
|
}
|
2006-07-07 00:07:18 -04:00
|
|
|
|
2019-10-18 09:05:08 -04:00
|
|
|
INLINE CELL copy_object_impl(CELL pointer)
|
|
|
|
|
{
|
|
|
|
|
CELL newpointer = (CELL)copy_untagged_object(
|
|
|
|
|
(void*)UNTAG(pointer),
|
|
|
|
|
object_size(pointer));
|
|
|
|
|
forward_object(pointer,newpointer);
|
2006-07-07 00:07:18 -04:00
|
|
|
return newpointer;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Follow a chain of forwarding pointers */
|
2006-07-07 00:07:18 -04:00
|
|
|
CELL resolve_forwarding(CELL untagged, CELL tag)
|
|
|
|
|
{
|
|
|
|
|
CELL header = get(untagged);
|
|
|
|
|
/* another forwarding pointer */
|
|
|
|
|
if(TAG(header) == GC_COLLECTED)
|
|
|
|
|
return resolve_forwarding(UNTAG(header),tag);
|
|
|
|
|
/* we've found the destination */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
CELL pointer = RETAG(untagged,tag);
|
|
|
|
|
if(should_copy(untagged))
|
|
|
|
|
pointer = RETAG(copy_object_impl(pointer),tag);
|
|
|
|
|
return pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Given a pointer to a tagged pointer to oldspace, copy it to newspace.
|
2006-07-07 00:07:18 -04:00
|
|
|
If the object has already been copied, return the forwarding
|
|
|
|
|
pointer address without copying anything; otherwise, install
|
2006-11-01 00:20:49 -05:00
|
|
|
a new forwarding pointer. */
|
2006-07-07 00:07:18 -04:00
|
|
|
CELL copy_object(CELL pointer)
|
|
|
|
|
{
|
|
|
|
|
CELL tag;
|
|
|
|
|
CELL header;
|
|
|
|
|
|
|
|
|
|
if(pointer == F)
|
|
|
|
|
return F;
|
|
|
|
|
|
|
|
|
|
tag = TAG(pointer);
|
|
|
|
|
|
|
|
|
|
if(tag == FIXNUM_TYPE)
|
|
|
|
|
return pointer;
|
|
|
|
|
|
|
|
|
|
header = get(UNTAG(pointer));
|
|
|
|
|
if(TAG(header) == GC_COLLECTED)
|
|
|
|
|
return resolve_forwarding(UNTAG(header),tag);
|
|
|
|
|
else
|
|
|
|
|
return RETAG(copy_object_impl(pointer),tag);
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-27 03:11:18 -04:00
|
|
|
/* The number of cells from the start of the object which should be scanned by
|
|
|
|
|
the GC. Some types have a binary payload at the end (string, word, DLL) which
|
|
|
|
|
we ignore. */
|
|
|
|
|
CELL binary_payload_start(CELL pointer)
|
|
|
|
|
{
|
|
|
|
|
switch(untag_header(get(pointer)))
|
|
|
|
|
{
|
|
|
|
|
/* these objects do not refer to other objects at all */
|
|
|
|
|
case STRING_TYPE:
|
|
|
|
|
case FLOAT_TYPE:
|
|
|
|
|
case BYTE_ARRAY_TYPE:
|
2019-10-18 09:05:08 -04:00
|
|
|
case BIT_ARRAY_TYPE:
|
2006-09-27 03:11:18 -04:00
|
|
|
case BIGNUM_TYPE:
|
|
|
|
|
return 0;
|
|
|
|
|
/* these objects have some binary data at the end */
|
|
|
|
|
case WORD_TYPE:
|
|
|
|
|
return sizeof(F_WORD) - CELLS;
|
|
|
|
|
case ALIEN_TYPE:
|
|
|
|
|
case DLL_TYPE:
|
|
|
|
|
return CELLS * 2;
|
|
|
|
|
/* everything else consists entirely of pointers */
|
|
|
|
|
default:
|
|
|
|
|
return unaligned_object_size(pointer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Every object has a regular representation in the runtime, which makes GC
|
|
|
|
|
much simpler. Every slot of the object until binary_payload_start is a pointer
|
|
|
|
|
to some other object. */
|
|
|
|
|
INLINE void collect_object(CELL start)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2006-09-27 03:11:18 -04:00
|
|
|
CELL scan = start;
|
2006-07-31 17:36:15 -04:00
|
|
|
CELL payload_start = binary_payload_start(scan);
|
|
|
|
|
CELL end = scan + payload_start;
|
|
|
|
|
|
|
|
|
|
scan += CELLS;
|
|
|
|
|
|
|
|
|
|
while(scan < end)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2006-07-31 17:36:15 -04:00
|
|
|
copy_handle((CELL*)scan);
|
|
|
|
|
scan += CELLS;
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
2006-09-27 03:11:18 -04:00
|
|
|
|
|
|
|
|
/* It is odd to put this hook here, but this is the only special case
|
|
|
|
|
made for any type of object by the GC. If code GC is being performed,
|
|
|
|
|
compiled code blocks referenced by this word must be marked. */
|
|
|
|
|
if(collecting_code && object_type(start) == WORD_TYPE)
|
|
|
|
|
{
|
|
|
|
|
F_WORD *word = (F_WORD *)start;
|
|
|
|
|
if(word->compiledp != F)
|
|
|
|
|
recursive_mark(word->xt);
|
|
|
|
|
}
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CELL collect_next(CELL scan)
|
|
|
|
|
{
|
|
|
|
|
CELL size = untagged_object_size(scan);
|
|
|
|
|
collect_object(scan);
|
|
|
|
|
return scan + size;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
INLINE void reset_generation(CELL i)
|
|
|
|
|
{
|
|
|
|
|
F_ZONE *z = &data_heap->generations[i];
|
|
|
|
|
z->here = z->start;
|
|
|
|
|
if(secure_gc)
|
|
|
|
|
memset((void*)z->start,69,z->size);
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* After garbage collection, any generations which are now empty need to have
|
|
|
|
|
their allocation pointers and cards reset. */
|
2006-07-07 00:07:18 -04:00
|
|
|
void reset_generations(CELL from, CELL to)
|
|
|
|
|
{
|
|
|
|
|
CELL i;
|
2019-10-18 09:05:06 -04:00
|
|
|
for(i = from; i <= to; i++) reset_generation(i);
|
2006-07-07 00:07:18 -04:00
|
|
|
clear_cards(from,to);
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Prepare to start copying reachable objects into an unused zone */
|
2019-10-18 09:05:06 -04:00
|
|
|
void begin_gc(CELL gen,
|
|
|
|
|
bool code_gc,
|
|
|
|
|
bool growing_data_heap_,
|
|
|
|
|
CELL requested_bytes)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2006-09-26 19:00:41 -04:00
|
|
|
collecting_code = code_gc;
|
2019-10-18 09:05:06 -04:00
|
|
|
growing_data_heap = growing_data_heap_;
|
|
|
|
|
collecting_gen = gen;
|
2006-07-07 00:07:18 -04:00
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
if(growing_data_heap)
|
|
|
|
|
{
|
|
|
|
|
if(gen != TENURED)
|
|
|
|
|
critical_error("Invalid parameters to begin_gc",0);
|
|
|
|
|
|
|
|
|
|
old_data_heap = data_heap;
|
|
|
|
|
set_data_heap(grow_data_heap(old_data_heap,requested_bytes));
|
|
|
|
|
newspace = &data_heap->generations[gen];
|
|
|
|
|
}
|
|
|
|
|
else if(gen == TENURED)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
|
|
|
|
/* when collecting the oldest generation, rotate it
|
|
|
|
|
with the semispace */
|
2019-10-18 09:05:06 -04:00
|
|
|
F_ZONE z = data_heap->generations[gen];
|
|
|
|
|
data_heap->generations[gen] = data_heap->prior;
|
|
|
|
|
data_heap->prior = z;
|
|
|
|
|
reset_generation(gen);
|
|
|
|
|
newspace = &data_heap->generations[gen];
|
2006-07-07 00:07:18 -04:00
|
|
|
clear_cards(TENURED,TENURED);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* when collecting a younger generation, we copy
|
|
|
|
|
reachable objects to the next oldest generation,
|
|
|
|
|
so we set the newspace so the next generation. */
|
2019-10-18 09:05:06 -04:00
|
|
|
newspace = &data_heap->generations[gen + 1];
|
|
|
|
|
collecting_gen_start = data_heap->generations[gen].start;
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
void major_gc_message(void)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
fprintf(stderr,"*** %s GC (%ld minor, %ld cards)\n",
|
|
|
|
|
collecting_code ? "Code and data" : "Data",
|
|
|
|
|
minor_collections,cards_scanned);
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
minor_collections = 0;
|
|
|
|
|
cards_scanned = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void end_gc(void)
|
|
|
|
|
{
|
|
|
|
|
if(growing_data_heap)
|
|
|
|
|
{
|
|
|
|
|
dealloc_data_heap(old_data_heap);
|
|
|
|
|
old_data_heap = NULL;
|
|
|
|
|
growing_data_heap = false;
|
|
|
|
|
|
|
|
|
|
fprintf(stderr,"*** Data heap resized to %lu bytes\n",
|
|
|
|
|
data_heap->segment->size);
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-26 19:00:41 -04:00
|
|
|
if(collecting_gen == TENURED)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
|
|
|
|
/* we did a full collection; no more
|
|
|
|
|
old-to-new pointers remain since everything
|
|
|
|
|
is in tenured space */
|
|
|
|
|
unmark_cards(TENURED,TENURED);
|
|
|
|
|
/* all generations except tenured space are
|
|
|
|
|
now empty */
|
|
|
|
|
reset_generations(NURSERY,TENURED - 1);
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
major_gc_message();
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* we collected a younger generation. so the
|
|
|
|
|
next-oldest generation no longer has any
|
|
|
|
|
pointers into the younger generation (the
|
|
|
|
|
younger generation is empty!) */
|
2006-09-26 19:00:41 -04:00
|
|
|
unmark_cards(collecting_gen + 1,collecting_gen + 1);
|
2006-07-07 00:07:18 -04:00
|
|
|
/* all generations up to and including the one
|
|
|
|
|
collected are now empty */
|
2006-09-26 19:00:41 -04:00
|
|
|
reset_generations(NURSERY,collecting_gen);
|
2006-11-01 00:20:49 -05:00
|
|
|
|
2006-07-07 00:07:18 -04:00
|
|
|
minor_collections++;
|
|
|
|
|
}
|
2006-09-26 19:00:41 -04:00
|
|
|
|
|
|
|
|
if(collecting_code)
|
|
|
|
|
{
|
|
|
|
|
/* now that all reachable code blocks have been marked,
|
|
|
|
|
deallocate the rest */
|
2019-10-18 09:05:06 -04:00
|
|
|
free_unmarked(&code_heap);
|
2006-09-26 19:00:41 -04:00
|
|
|
}
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
/* Collect gen and all younger generations.
|
|
|
|
|
If growing_data_heap_ is true, we must grow the data heap to such a size that
|
|
|
|
|
an allocation of requested_bytes won't fail */
|
|
|
|
|
void garbage_collection(volatile CELL gen,
|
|
|
|
|
bool code_gc,
|
|
|
|
|
volatile bool growing_data_heap,
|
|
|
|
|
CELL requested_bytes)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
|
|
|
|
s64 start = current_millis();
|
|
|
|
|
CELL scan;
|
|
|
|
|
|
2006-10-31 00:52:02 -05:00
|
|
|
if(gc_off)
|
|
|
|
|
critical_error("GC disabled",gen);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
/* we come back here if a generation is full */
|
|
|
|
|
if(setjmp(gc_jmp))
|
|
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
/* We have no older generations we can try collecting, so we
|
|
|
|
|
resort to growing the data heap */
|
2006-07-07 00:07:18 -04:00
|
|
|
if(gen == TENURED)
|
2019-10-18 09:05:06 -04:00
|
|
|
growing_data_heap = true;
|
|
|
|
|
/* Collect the next oldest generation */
|
2006-07-07 00:07:18 -04:00
|
|
|
else
|
|
|
|
|
gen++;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:05:06 -04:00
|
|
|
begin_gc(gen,code_gc,growing_data_heap,requested_bytes);
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
/* initialize chase pointer */
|
|
|
|
|
scan = newspace->here;
|
|
|
|
|
|
|
|
|
|
/* collect objects referenced from stacks and environment */
|
|
|
|
|
collect_roots();
|
|
|
|
|
|
|
|
|
|
/* collect objects referenced from older generations */
|
|
|
|
|
collect_cards(gen);
|
|
|
|
|
|
2006-09-26 19:00:41 -04:00
|
|
|
if(!code_gc)
|
|
|
|
|
{
|
|
|
|
|
/* if we are doing code GC, then we will copy over literals
|
|
|
|
|
from any code block which gets marked as live. if we are not
|
|
|
|
|
doing code GC, just consider all literals as roots. */
|
|
|
|
|
collect_literals();
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-07 00:07:18 -04:00
|
|
|
while(scan < newspace->here)
|
|
|
|
|
scan = collect_next(scan);
|
|
|
|
|
|
2006-09-26 19:00:41 -04:00
|
|
|
end_gc();
|
2006-07-07 00:07:18 -04:00
|
|
|
|
|
|
|
|
gc_time += (current_millis() - start);
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-26 19:00:41 -04:00
|
|
|
void primitive_data_gc(void)
|
2006-07-07 00:07:18 -04:00
|
|
|
{
|
2019-10-18 09:05:06 -04:00
|
|
|
garbage_collection(TENURED,false,false,0);
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
|
|
|
|
|
2006-11-01 00:20:49 -05:00
|
|
|
/* Push total time spent on GC */
|
2006-07-07 00:07:18 -04:00
|
|
|
void primitive_gc_time(void)
|
|
|
|
|
{
|
2006-10-31 01:04:02 -05:00
|
|
|
box_unsigned_8(gc_time);
|
2006-07-07 00:07:18 -04:00
|
|
|
}
|
2006-11-07 00:22:34 -05:00
|
|
|
|
|
|
|
|
void simple_gc(void)
|
|
|
|
|
{
|
|
|
|
|
maybe_gc(0);
|
|
|
|
|
}
|
2019-10-18 09:05:08 -04:00
|
|
|
|
|
|
|
|
void primitive_become(void)
|
|
|
|
|
{
|
|
|
|
|
F_VECTOR *new_object_vector = untag_vector(dpop());
|
|
|
|
|
F_ARRAY *new_objects = untag_array_fast(new_object_vector->array);
|
|
|
|
|
F_VECTOR *old_object_vector = untag_vector(dpop());
|
|
|
|
|
F_ARRAY *old_objects = untag_array_fast(old_object_vector->array);
|
|
|
|
|
|
|
|
|
|
CELL capacity = untag_fixnum_fast(new_object_vector->top);
|
|
|
|
|
CELL i;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < capacity; i++)
|
|
|
|
|
{
|
|
|
|
|
CELL old_obj = get(AREF(old_objects,i));
|
|
|
|
|
CELL new_obj = get(AREF(new_objects,i));
|
|
|
|
|
|
|
|
|
|
forward_object(old_obj,new_obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
primitive_data_gc();
|
|
|
|
|
}
|