vm: mark sweep gc for tenured space work in progress
parent
f0816d72f1
commit
814f6371d6
|
@ -25,7 +25,7 @@ void factor_vm::collect_aging()
|
|||
collector.trace_cards(data->tenured,
|
||||
card_points_to_aging,
|
||||
simple_unmarker(card_mark_mask));
|
||||
collector.cheneys_algorithm();
|
||||
collector.tenure_reachable_objects();
|
||||
}
|
||||
{
|
||||
/* If collection fails here, do a to_tenured collection. */
|
||||
|
|
|
@ -15,6 +15,10 @@ struct aging_policy {
|
|||
{
|
||||
return !(aging->contains_p(untagged) || tenured->contains_p(untagged));
|
||||
}
|
||||
|
||||
void promoted_object(object *obj) {}
|
||||
|
||||
void visited_object(object *obj) {}
|
||||
};
|
||||
|
||||
struct aging_collector : copying_collector<aging_space,aging_policy> {
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
struct aging_space : bump_allocator {
|
||||
struct aging_space : bump_allocator<object> {
|
||||
object_start_map starts;
|
||||
|
||||
aging_space(cell size, cell start) :
|
||||
bump_allocator(size,start), starts(size,start) {}
|
||||
bump_allocator<object>(size,start), starts(size,start) {}
|
||||
|
||||
object *allot(cell size)
|
||||
{
|
||||
if(here + size > end) return NULL;
|
||||
|
||||
object *obj = bump_allocator::allot(size);
|
||||
object *obj = bump_allocator<object>::allot(size);
|
||||
starts.record_object_start_offset(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
cell next_object_after(cell scan)
|
||||
{
|
||||
cell size = ((object *)scan)->size();
|
||||
if(scan + size < here)
|
||||
return scan + size;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
struct bump_allocator {
|
||||
template<typename Block> struct bump_allocator {
|
||||
/* offset of 'here' and 'end' is hardcoded in compiler backends */
|
||||
cell here;
|
||||
cell start;
|
||||
|
@ -9,27 +9,18 @@ struct bump_allocator {
|
|||
cell size;
|
||||
|
||||
bump_allocator(cell size_, cell start_) :
|
||||
here(0), start(start_), end(start_ + size_), size(size_) {}
|
||||
here(start_), start(start_), end(start_ + size_), size(size_) {}
|
||||
|
||||
inline bool contains_p(object *pointer)
|
||||
inline bool contains_p(Block *block)
|
||||
{
|
||||
return ((cell)pointer - start) < size;
|
||||
return ((cell)block - start) < size;
|
||||
}
|
||||
|
||||
inline object *allot(cell size)
|
||||
inline Block *allot(cell size)
|
||||
{
|
||||
cell h = here;
|
||||
here = h + align(size,data_alignment);
|
||||
return (object *)h;
|
||||
}
|
||||
|
||||
cell next_allocated_block_after(cell scan)
|
||||
{
|
||||
cell size = ((object *)scan)->size();
|
||||
if(scan + size < here)
|
||||
return scan + size;
|
||||
else
|
||||
return 0;
|
||||
return (Block *)h;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ code_heap::code_heap(cell size)
|
|||
if(size > (1L << (sizeof(cell) * 8 - 6))) fatal_error("Heap too large",size);
|
||||
seg = new segment(align_page(size),true);
|
||||
if(!seg) fatal_error("Out of memory in heap allocator",size);
|
||||
allocator = new free_list_allocator<heap_block>(seg->start,size);
|
||||
allocator = new free_list_allocator<heap_block>(size,seg->start);
|
||||
}
|
||||
|
||||
code_heap::~code_heap()
|
||||
|
|
143
vm/collector.hpp
143
vm/collector.hpp
|
@ -40,7 +40,10 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
|
||||
object *untagged = parent->untag<object>(pointer);
|
||||
if(!policy.should_copy_p(untagged))
|
||||
{
|
||||
policy.visited_object(untagged);
|
||||
return;
|
||||
}
|
||||
|
||||
object *forwarding = resolve_forwarding(untagged);
|
||||
|
||||
|
@ -49,7 +52,10 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
else if(policy.should_copy_p(forwarding))
|
||||
untagged = promote_object(forwarding);
|
||||
else
|
||||
{
|
||||
untagged = forwarding;
|
||||
policy.visited_object(untagged);
|
||||
}
|
||||
|
||||
*handle = RETAG(untagged,TAG(pointer));
|
||||
}
|
||||
|
@ -79,6 +85,8 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
stats->object_count++;
|
||||
stats->bytes_copied += size;
|
||||
|
||||
policy.promoted_object(newpointer);
|
||||
|
||||
return newpointer;
|
||||
}
|
||||
|
||||
|
@ -145,6 +153,141 @@ template<typename TargetGeneration, typename Policy> struct collector {
|
|||
ctx = ctx->next;
|
||||
}
|
||||
}
|
||||
|
||||
inline cell first_card_in_deck(cell deck)
|
||||
{
|
||||
return deck << (deck_bits - card_bits);
|
||||
}
|
||||
|
||||
inline cell last_card_in_deck(cell deck)
|
||||
{
|
||||
return first_card_in_deck(deck + 1);
|
||||
}
|
||||
|
||||
inline cell card_deck_for_address(cell a)
|
||||
{
|
||||
return addr_to_deck(a - this->data->start);
|
||||
}
|
||||
|
||||
inline cell card_start_address(cell card)
|
||||
{
|
||||
return (card << card_bits) + this->data->start;
|
||||
}
|
||||
|
||||
inline cell card_end_address(cell card)
|
||||
{
|
||||
return ((card + 1) << card_bits) + this->data->start;
|
||||
}
|
||||
|
||||
void trace_partial_objects(cell start, cell end, cell card_start, cell card_end)
|
||||
{
|
||||
if(card_start < end)
|
||||
{
|
||||
start += sizeof(cell);
|
||||
|
||||
if(start < card_start) start = card_start;
|
||||
if(end > card_end) end = card_end;
|
||||
|
||||
cell *slot_ptr = (cell *)start;
|
||||
cell *end_ptr = (cell *)end;
|
||||
|
||||
if(slot_ptr != end_ptr)
|
||||
{
|
||||
for(; slot_ptr < end_ptr; slot_ptr++)
|
||||
this->trace_handle(slot_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SourceGeneration, typename Unmarker>
|
||||
void trace_cards(SourceGeneration *gen, card mask, Unmarker unmarker)
|
||||
{
|
||||
u64 start_time = current_micros();
|
||||
|
||||
card_deck *decks = this->data->decks;
|
||||
card_deck *cards = this->data->cards;
|
||||
|
||||
cell gen_start_card = addr_to_card(gen->start - this->data->start);
|
||||
|
||||
cell first_deck = card_deck_for_address(gen->start);
|
||||
cell last_deck = card_deck_for_address(gen->end);
|
||||
|
||||
cell start = 0, binary_start = 0, end = 0;
|
||||
|
||||
for(cell deck_index = first_deck; deck_index < last_deck; deck_index++)
|
||||
{
|
||||
if(decks[deck_index] & mask)
|
||||
{
|
||||
this->parent->gc_stats.decks_scanned++;
|
||||
|
||||
cell first_card = first_card_in_deck(deck_index);
|
||||
cell last_card = last_card_in_deck(deck_index);
|
||||
|
||||
for(cell card_index = first_card; card_index < last_card; card_index++)
|
||||
{
|
||||
if(cards[card_index] & mask)
|
||||
{
|
||||
this->parent->gc_stats.cards_scanned++;
|
||||
|
||||
if(end < card_start_address(card_index))
|
||||
{
|
||||
start = gen->starts.find_object_containing_card(card_index - gen_start_card);
|
||||
binary_start = start + this->parent->binary_payload_start((object *)start);
|
||||
end = start + ((object *)start)->size();
|
||||
}
|
||||
|
||||
#ifdef FACTOR_DEBUG
|
||||
assert(addr_to_card(start - this->data->start) <= card_index);
|
||||
assert(start < card_end_address(card_index));
|
||||
#endif
|
||||
|
||||
scan_next_object: {
|
||||
trace_partial_objects(
|
||||
start,
|
||||
binary_start,
|
||||
card_start_address(card_index),
|
||||
card_end_address(card_index));
|
||||
if(end < card_end_address(card_index))
|
||||
{
|
||||
start = gen->next_object_after(start);
|
||||
if(start)
|
||||
{
|
||||
binary_start = start + this->parent->binary_payload_start((object *)start);
|
||||
end = start + ((object *)start)->size();
|
||||
goto scan_next_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unmarker(&cards[card_index]);
|
||||
|
||||
if(!start) goto end;
|
||||
}
|
||||
}
|
||||
|
||||
unmarker(&decks[deck_index]);
|
||||
}
|
||||
}
|
||||
|
||||
end: this->parent->gc_stats.card_scan_time += (current_micros() - start_time);
|
||||
}
|
||||
|
||||
/* Trace all literals referenced from a code block. Only for aging and nursery collections */
|
||||
void trace_literal_references(code_block *compiled)
|
||||
{
|
||||
this->trace_handle(&compiled->owner);
|
||||
this->trace_handle(&compiled->literals);
|
||||
this->trace_handle(&compiled->relocation);
|
||||
this->parent->gc_stats.code_blocks_scanned++;
|
||||
}
|
||||
|
||||
void trace_code_heap_roots(std::set<code_block *> *remembered_set)
|
||||
{
|
||||
std::set<code_block *>::const_iterator iter = remembered_set->begin();
|
||||
std::set<code_block *>::const_iterator end = remembered_set->end();
|
||||
|
||||
for(; iter != end; iter++) trace_literal_references(*iter);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,147 +18,12 @@ struct copying_collector : collector<TargetGeneration,Policy> {
|
|||
explicit copying_collector(factor_vm *parent_, generation_statistics *stats_, TargetGeneration *target_, Policy policy_) :
|
||||
collector<TargetGeneration,Policy>(parent_,stats_,target_,policy_), scan(target_->here) {}
|
||||
|
||||
inline cell first_card_in_deck(cell deck)
|
||||
{
|
||||
return deck << (deck_bits - card_bits);
|
||||
}
|
||||
|
||||
inline cell last_card_in_deck(cell deck)
|
||||
{
|
||||
return first_card_in_deck(deck + 1);
|
||||
}
|
||||
|
||||
inline cell card_deck_for_address(cell a)
|
||||
{
|
||||
return addr_to_deck(a - this->data->start);
|
||||
}
|
||||
|
||||
inline cell card_start_address(cell card)
|
||||
{
|
||||
return (card << card_bits) + this->data->start;
|
||||
}
|
||||
|
||||
inline cell card_end_address(cell card)
|
||||
{
|
||||
return ((card + 1) << card_bits) + this->data->start;
|
||||
}
|
||||
|
||||
void trace_partial_objects(cell start, cell end, cell card_start, cell card_end)
|
||||
{
|
||||
if(card_start < end)
|
||||
{
|
||||
start += sizeof(cell);
|
||||
|
||||
if(start < card_start) start = card_start;
|
||||
if(end > card_end) end = card_end;
|
||||
|
||||
cell *slot_ptr = (cell *)start;
|
||||
cell *end_ptr = (cell *)end;
|
||||
|
||||
if(slot_ptr != end_ptr)
|
||||
{
|
||||
for(; slot_ptr < end_ptr; slot_ptr++)
|
||||
this->trace_handle(slot_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SourceGeneration, typename Unmarker>
|
||||
void trace_cards(SourceGeneration *gen, card mask, Unmarker unmarker)
|
||||
{
|
||||
u64 start_time = current_micros();
|
||||
|
||||
card_deck *decks = this->data->decks;
|
||||
card_deck *cards = this->data->cards;
|
||||
|
||||
cell gen_start_card = addr_to_card(gen->start - this->data->start);
|
||||
|
||||
cell first_deck = card_deck_for_address(gen->start);
|
||||
cell last_deck = card_deck_for_address(gen->end);
|
||||
|
||||
cell start = 0, binary_start = 0, end = 0;
|
||||
|
||||
for(cell deck_index = first_deck; deck_index < last_deck; deck_index++)
|
||||
{
|
||||
if(decks[deck_index] & mask)
|
||||
{
|
||||
this->parent->gc_stats.decks_scanned++;
|
||||
|
||||
cell first_card = first_card_in_deck(deck_index);
|
||||
cell last_card = last_card_in_deck(deck_index);
|
||||
|
||||
for(cell card_index = first_card; card_index < last_card; card_index++)
|
||||
{
|
||||
if(cards[card_index] & mask)
|
||||
{
|
||||
this->parent->gc_stats.cards_scanned++;
|
||||
|
||||
if(end < card_start_address(card_index))
|
||||
{
|
||||
start = gen->starts.find_object_containing_card(card_index - gen_start_card);
|
||||
binary_start = start + this->parent->binary_payload_start((object *)start);
|
||||
end = start + ((object *)start)->size();
|
||||
}
|
||||
|
||||
#ifdef FACTOR_DEBUG
|
||||
assert(addr_to_card(start - this->data->start) <= card_index);
|
||||
assert(start < card_end_address(card_index));
|
||||
#endif
|
||||
|
||||
scan_next_object: {
|
||||
trace_partial_objects(
|
||||
start,
|
||||
binary_start,
|
||||
card_start_address(card_index),
|
||||
card_end_address(card_index));
|
||||
if(end < card_end_address(card_index))
|
||||
{
|
||||
start = gen->next_allocated_block_after(start);
|
||||
if(start)
|
||||
{
|
||||
binary_start = start + this->parent->binary_payload_start((object *)start);
|
||||
end = start + ((object *)start)->size();
|
||||
goto scan_next_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unmarker(&cards[card_index]);
|
||||
|
||||
if(!start) goto end;
|
||||
}
|
||||
}
|
||||
|
||||
unmarker(&decks[deck_index]);
|
||||
}
|
||||
}
|
||||
|
||||
end: this->parent->gc_stats.card_scan_time += (current_micros() - start_time);
|
||||
}
|
||||
|
||||
/* Trace all literals referenced from a code block. Only for aging and nursery collections */
|
||||
void trace_literal_references(code_block *compiled)
|
||||
{
|
||||
this->trace_handle(&compiled->owner);
|
||||
this->trace_handle(&compiled->literals);
|
||||
this->trace_handle(&compiled->relocation);
|
||||
this->parent->gc_stats.code_blocks_scanned++;
|
||||
}
|
||||
|
||||
void trace_code_heap_roots(std::set<code_block *> *remembered_set)
|
||||
{
|
||||
std::set<code_block *>::const_iterator iter = remembered_set->begin();
|
||||
std::set<code_block *>::const_iterator end = remembered_set->end();
|
||||
|
||||
for(; iter != end; iter++) trace_literal_references(*iter);
|
||||
}
|
||||
|
||||
void cheneys_algorithm()
|
||||
{
|
||||
while(scan && scan < this->target->here)
|
||||
{
|
||||
this->trace_slots((object *)scan);
|
||||
scan = this->target->next_allocated_block_after(scan);
|
||||
scan = this->target->next_object_after(scan);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@ data_heap::data_heap(cell young_size_, cell aging_size_, cell tenured_size_)
|
|||
aging_size = aging_size_;
|
||||
tenured_size = tenured_size_;
|
||||
|
||||
cell total_size = young_size + 2 * aging_size + 2 * tenured_size;
|
||||
cell total_size = young_size + 2 * aging_size + tenured_size;
|
||||
|
||||
total_size += deck_size;
|
||||
|
||||
|
@ -29,20 +29,21 @@ data_heap::data_heap(cell young_size_, cell aging_size_, cell tenured_size_)
|
|||
|
||||
cards = new card[cards_size];
|
||||
cards_end = cards + cards_size;
|
||||
memset(cards,0,cards_size);
|
||||
|
||||
cell decks_size = addr_to_deck(total_size);
|
||||
decks = new card_deck[decks_size];
|
||||
decks_end = decks + decks_size;
|
||||
memset(decks,0,decks_size);
|
||||
|
||||
start = align(seg->start,deck_size);
|
||||
|
||||
tenured = new tenured_space(tenured_size,start);
|
||||
tenured_semispace = new tenured_space(tenured_size,tenured->end);
|
||||
|
||||
aging = new aging_space(aging_size,tenured_semispace->end);
|
||||
aging = new aging_space(aging_size,tenured->end);
|
||||
aging_semispace = new aging_space(aging_size,aging->end);
|
||||
|
||||
nursery = new bump_allocator(young_size,aging_semispace->end);
|
||||
nursery = new nursery_space(young_size,aging_semispace->end);
|
||||
|
||||
assert(seg->end - nursery->end <= deck_size);
|
||||
}
|
||||
|
@ -54,7 +55,6 @@ data_heap::~data_heap()
|
|||
delete aging;
|
||||
delete aging_semispace;
|
||||
delete tenured;
|
||||
delete tenured_semispace;
|
||||
delete[] cards;
|
||||
delete[] decks;
|
||||
}
|
||||
|
@ -71,8 +71,6 @@ void factor_vm::set_data_heap(data_heap *data_)
|
|||
nursery = *data->nursery;
|
||||
nursery.here = nursery.start;
|
||||
init_card_decks();
|
||||
data->reset_generation(data->aging);
|
||||
data->reset_generation(data->tenured);
|
||||
}
|
||||
|
||||
void factor_vm::init_data_heap(cell young_size, cell aging_size, cell tenured_size)
|
||||
|
@ -185,8 +183,11 @@ void factor_vm::primitive_data_room()
|
|||
a.add(tag_fixnum((data->aging->end - data->aging->here) >> 10));
|
||||
a.add(tag_fixnum((data->aging->size) >> 10));
|
||||
|
||||
a.add(tag_fixnum((data->tenured->end - data->tenured->here) >> 10));
|
||||
a.add(tag_fixnum((data->tenured->size) >> 10));
|
||||
//XXX
|
||||
cell used, total_free, max_free;
|
||||
data->tenured->usage(&used,&total_free,&max_free);
|
||||
a.add(tag_fixnum(total_free >> 10));
|
||||
a.add(tag_fixnum(used >> 10));
|
||||
|
||||
a.trim();
|
||||
dpush(a.elements.value());
|
||||
|
@ -195,7 +196,7 @@ void factor_vm::primitive_data_room()
|
|||
/* Disables GC and activates next-object ( -- obj ) primitive */
|
||||
void factor_vm::begin_scan()
|
||||
{
|
||||
heap_scan_ptr = data->tenured->start;
|
||||
heap_scan_ptr = data->tenured->first_object();
|
||||
gc_off = true;
|
||||
}
|
||||
|
||||
|
@ -214,12 +215,14 @@ cell factor_vm::next_object()
|
|||
if(!gc_off)
|
||||
general_error(ERROR_HEAP_SCAN,false_object,false_object,NULL);
|
||||
|
||||
if(heap_scan_ptr >= data->tenured->here)
|
||||
if(heap_scan_ptr)
|
||||
{
|
||||
cell current = heap_scan_ptr;
|
||||
heap_scan_ptr = data->tenured->next_object_after(heap_scan_ptr);
|
||||
return tag_dynamic((object *)current);
|
||||
}
|
||||
else
|
||||
return false_object;
|
||||
|
||||
object *obj = (object *)heap_scan_ptr;
|
||||
heap_scan_ptr += obj->size();
|
||||
return tag_dynamic(obj);
|
||||
}
|
||||
|
||||
/* Push object at heap scan cursor and advance; pushes f when done */
|
||||
|
|
|
@ -10,11 +10,10 @@ struct data_heap {
|
|||
|
||||
segment *seg;
|
||||
|
||||
bump_allocator *nursery;
|
||||
nursery_space *nursery;
|
||||
aging_space *aging;
|
||||
aging_space *aging_semispace;
|
||||
tenured_space *tenured;
|
||||
tenured_space *tenured_semispace;
|
||||
|
||||
card *cards;
|
||||
card *cards_end;
|
||||
|
@ -49,7 +48,6 @@ their allocation pointers and cards reset. */
|
|||
template<typename Generation> void data_heap::reset_generation(Generation *gen)
|
||||
{
|
||||
gen->here = gen->start;
|
||||
|
||||
clear_cards(gen);
|
||||
clear_decks(gen);
|
||||
gen->starts.clear_object_start_offsets();
|
||||
|
|
16
vm/debug.cpp
16
vm/debug.cpp
|
@ -209,19 +209,21 @@ void factor_vm::dump_memory(cell from, cell to)
|
|||
dump_cell(from);
|
||||
}
|
||||
|
||||
void factor_vm::dump_zone(const char *name, bump_allocator *z)
|
||||
template<typename Generation>
|
||||
void factor_vm::dump_generation(const char *name, Generation *gen)
|
||||
{
|
||||
print_string(name); print_string(": ");
|
||||
print_string("Start="); print_cell(z->start);
|
||||
print_string(", size="); print_cell(z->size);
|
||||
print_string(", here="); print_cell(z->here - z->start); nl();
|
||||
print_string("Start="); print_cell(gen->start);
|
||||
print_string(", size="); print_cell(gen->size);
|
||||
print_string(", end="); print_cell(gen->end);
|
||||
nl();
|
||||
}
|
||||
|
||||
void factor_vm::dump_generations()
|
||||
{
|
||||
dump_zone("Nursery",&nursery);
|
||||
dump_zone("Aging",data->aging);
|
||||
dump_zone("Tenured",data->tenured);
|
||||
dump_generation("Nursery",&nursery);
|
||||
dump_generation("Aging",data->aging);
|
||||
dump_generation("Tenured",data->tenured);
|
||||
|
||||
print_string("Cards: base=");
|
||||
print_cell((cell)data->cards);
|
||||
|
|
|
@ -9,29 +9,17 @@ struct free_list {
|
|||
};
|
||||
|
||||
template<typename Block> struct free_list_allocator {
|
||||
cell start;
|
||||
cell size;
|
||||
cell start;
|
||||
cell end;
|
||||
free_list free_blocks;
|
||||
mark_bits<Block> state;
|
||||
|
||||
explicit free_list_allocator(cell start, cell size);
|
||||
|
||||
inline Block *first_block()
|
||||
{
|
||||
return (Block *)start;
|
||||
}
|
||||
|
||||
inline Block *last_block()
|
||||
{
|
||||
return (Block *)end;
|
||||
}
|
||||
|
||||
Block *next_block_after(heap_block *block)
|
||||
{
|
||||
return (Block *)((cell)block + block->size());
|
||||
}
|
||||
|
||||
explicit free_list_allocator(cell size, cell start);
|
||||
bool contains_p(Block *block);
|
||||
Block *first_block();
|
||||
Block *last_block();
|
||||
Block *next_block_after(Block *block);
|
||||
void clear_free_list();
|
||||
void add_to_free_list(free_heap_block *block);
|
||||
void build_free_list(cell size);
|
||||
|
@ -42,35 +30,42 @@ template<typename Block> struct free_list_allocator {
|
|||
void free(Block *block);
|
||||
void usage(cell *used, cell *total_free, cell *max_free);
|
||||
cell occupied();
|
||||
|
||||
void sweep();
|
||||
template<typename Iterator> void sweep(Iterator &iter);
|
||||
template<typename Iterator> void compact(Iterator &iter);
|
||||
|
||||
template<typename Iterator> void iterate(Iterator &iter)
|
||||
{
|
||||
Block *scan = first_block();
|
||||
Block *end = last_block();
|
||||
|
||||
while(scan != end)
|
||||
{
|
||||
cell size = scan->size();
|
||||
Block *next = (Block *)((cell)scan + size);
|
||||
if(!scan->free_p()) iter(scan,size);
|
||||
scan = next;
|
||||
}
|
||||
}
|
||||
template<typename Iterator> void iterate(Iterator &iter);
|
||||
};
|
||||
|
||||
template<typename Block>
|
||||
free_list_allocator<Block>::free_list_allocator(cell size_, cell start_) :
|
||||
size(size_), start(start_), end(start_ + size_), state(mark_bits<Block>(size_,start_))
|
||||
{
|
||||
clear_free_list();
|
||||
}
|
||||
|
||||
template<typename Block> void free_list_allocator<Block>::clear_free_list()
|
||||
{
|
||||
memset(&free_blocks,0,sizeof(free_list));
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
free_list_allocator<Block>::free_list_allocator(cell start_, cell size_) :
|
||||
start(start_), size(size_), end(start_ + size_), state(mark_bits<Block>(start_,size_))
|
||||
template<typename Block> bool free_list_allocator<Block>::contains_p(Block *block)
|
||||
{
|
||||
clear_free_list();
|
||||
return ((cell)block - start) < size;
|
||||
}
|
||||
|
||||
template<typename Block> Block *free_list_allocator<Block>::first_block()
|
||||
{
|
||||
return (Block *)start;
|
||||
}
|
||||
|
||||
template<typename Block> Block *free_list_allocator<Block>::last_block()
|
||||
{
|
||||
return (Block *)end;
|
||||
}
|
||||
|
||||
template<typename Block> Block *free_list_allocator<Block>::next_block_after(Block *block)
|
||||
{
|
||||
return (Block *)((cell)block + block->size());
|
||||
}
|
||||
|
||||
template<typename Block> void free_list_allocator<Block>::add_to_free_list(free_heap_block *block)
|
||||
|
@ -234,8 +229,56 @@ template<typename Block> cell free_list_allocator<Block>::occupied()
|
|||
return size;
|
||||
}
|
||||
|
||||
/* After code GC, all live code blocks are marked, so any
|
||||
which are not marked can be reclaimed. */
|
||||
template<typename Block>
|
||||
void free_list_allocator<Block>::sweep()
|
||||
{
|
||||
this->clear_free_list();
|
||||
|
||||
Block *prev = NULL;
|
||||
Block *scan = this->first_block();
|
||||
Block *end = this->last_block();
|
||||
|
||||
while(scan != end)
|
||||
{
|
||||
cell size = scan->size();
|
||||
|
||||
if(scan->free_p())
|
||||
{
|
||||
if(prev && prev->free_p())
|
||||
{
|
||||
free_heap_block *free_prev = (free_heap_block *)prev;
|
||||
free_prev->set_size(free_prev->size() + size);
|
||||
}
|
||||
else
|
||||
prev = scan;
|
||||
}
|
||||
else if(this->state.marked_p(scan))
|
||||
{
|
||||
if(prev && prev->free_p())
|
||||
this->add_to_free_list((free_heap_block *)prev);
|
||||
prev = scan;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(prev && prev->free_p())
|
||||
{
|
||||
free_heap_block *free_prev = (free_heap_block *)prev;
|
||||
free_prev->set_size(free_prev->size() + size);
|
||||
}
|
||||
else
|
||||
{
|
||||
((free_heap_block *)scan)->set_free();
|
||||
prev = scan;
|
||||
}
|
||||
}
|
||||
|
||||
scan = (Block *)((cell)scan + size);
|
||||
}
|
||||
|
||||
if(prev && prev->free_p())
|
||||
this->add_to_free_list((free_heap_block *)prev);
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
template<typename Iterator>
|
||||
void free_list_allocator<Block>::sweep(Iterator &iter)
|
||||
|
@ -302,4 +345,20 @@ void free_list_allocator<Block>::compact(Iterator &iter)
|
|||
this->build_free_list((cell)compactor.address - this->start);
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
template<typename Iterator>
|
||||
void free_list_allocator<Block>::iterate(Iterator &iter)
|
||||
{
|
||||
Block *scan = first_block();
|
||||
Block *end = last_block();
|
||||
|
||||
while(scan != end)
|
||||
{
|
||||
cell size = scan->size();
|
||||
Block *next = (Block *)((cell)scan + size);
|
||||
if(!scan->free_p()) iter(scan,size);
|
||||
scan = next;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace factor
|
|||
{
|
||||
|
||||
full_collector::full_collector(factor_vm *parent_) :
|
||||
copying_collector<tenured_space,full_policy>(
|
||||
collector<tenured_space,full_policy>(
|
||||
parent_,
|
||||
&parent_->gc_stats.full_stats,
|
||||
parent_->data->tenured,
|
||||
|
@ -89,18 +89,22 @@ void full_collector::trace_literal_references(code_block *compiled)
|
|||
collections */
|
||||
void full_collector::mark_code_block(code_block *compiled)
|
||||
{
|
||||
this->code->set_marked_p(compiled);
|
||||
trace_literal_references(compiled);
|
||||
if(!this->code->marked_p(compiled))
|
||||
{
|
||||
this->code->set_marked_p(compiled);
|
||||
trace_literal_references(compiled);
|
||||
}
|
||||
}
|
||||
|
||||
void full_collector::cheneys_algorithm()
|
||||
void full_collector::mark_reachable_objects()
|
||||
{
|
||||
while(scan && scan < target->here)
|
||||
std::vector<object *> *mark_stack = &this->target->mark_stack;
|
||||
while(!mark_stack->empty())
|
||||
{
|
||||
object *obj = (object *)scan;
|
||||
object *obj = mark_stack->back();
|
||||
mark_stack->pop_back();
|
||||
this->trace_slots(obj);
|
||||
this->mark_object_code_block(obj);
|
||||
scan = target->next_allocated_block_after(scan);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,6 +113,7 @@ void factor_vm::collect_full_impl(bool trace_contexts_p)
|
|||
full_collector collector(this);
|
||||
|
||||
code->clear_mark_bits();
|
||||
data->tenured->clear_mark_bits();
|
||||
|
||||
collector.trace_roots();
|
||||
if(trace_contexts_p)
|
||||
|
@ -118,8 +123,9 @@ void factor_vm::collect_full_impl(bool trace_contexts_p)
|
|||
collector.trace_callbacks();
|
||||
}
|
||||
|
||||
collector.cheneys_algorithm();
|
||||
collector.mark_reachable_objects();
|
||||
|
||||
data->tenured->sweep();
|
||||
data->reset_generation(data->aging);
|
||||
nursery.here = nursery.start;
|
||||
}
|
||||
|
@ -144,9 +150,6 @@ void factor_vm::collect_growing_heap(cell requested_bytes,
|
|||
|
||||
void factor_vm::collect_full(bool trace_contexts_p, bool compact_code_heap_p)
|
||||
{
|
||||
/* Copy all live objects to the tenured semispace. */
|
||||
std::swap(data->tenured,data->tenured_semispace);
|
||||
data->reset_generation(data->tenured);
|
||||
collect_full_impl(trace_contexts_p);
|
||||
|
||||
if(compact_code_heap_p)
|
||||
|
|
|
@ -11,9 +11,20 @@ struct full_policy {
|
|||
{
|
||||
return !tenured->contains_p(untagged);
|
||||
}
|
||||
|
||||
void promoted_object(object *obj)
|
||||
{
|
||||
tenured->mark_and_push(obj);
|
||||
}
|
||||
|
||||
void visited_object(object *obj)
|
||||
{
|
||||
if(!tenured->marked_p(obj))
|
||||
tenured->mark_and_push(obj);
|
||||
}
|
||||
};
|
||||
|
||||
struct full_collector : copying_collector<tenured_space,full_policy> {
|
||||
struct full_collector : collector<tenured_space,full_policy> {
|
||||
bool trace_contexts_p;
|
||||
|
||||
full_collector(factor_vm *parent_);
|
||||
|
@ -22,7 +33,7 @@ struct full_collector : copying_collector<tenured_space,full_policy> {
|
|||
void trace_callbacks();
|
||||
void trace_literal_references(code_block *compiled);
|
||||
void mark_code_block(code_block *compiled);
|
||||
void cheneys_algorithm();
|
||||
void mark_reachable_objects();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -235,11 +235,13 @@ object *factor_vm::allot_object(header header, cell size)
|
|||
else
|
||||
{
|
||||
/* If tenured space does not have enough room, collect */
|
||||
if(data->tenured->here + size > data->tenured->end)
|
||||
//XXX
|
||||
//if(data->tenured->here + size > data->tenured->end)
|
||||
primitive_full_gc();
|
||||
|
||||
/* If it still won't fit, grow the heap */
|
||||
if(data->tenured->here + size > data->tenured->end)
|
||||
//XXX
|
||||
//if(data->tenured->here + size > data->tenured->end)
|
||||
{
|
||||
gc(collect_growing_heap_op,
|
||||
size, /* requested size */
|
||||
|
|
|
@ -39,7 +39,7 @@ void factor_vm::load_data_heap(FILE *file, image_header *h, vm_parameters *p)
|
|||
fatal_error("load_data_heap failed",0);
|
||||
}
|
||||
|
||||
data->tenured->here = data->tenured->start + h->data_size;
|
||||
data->tenured->build_free_list(h->data_size);
|
||||
}
|
||||
|
||||
void factor_vm::load_code_heap(FILE *file, image_header *h, vm_parameters *p)
|
||||
|
@ -203,7 +203,7 @@ void factor_vm::relocate_data(cell data_relocation_base, cell code_relocation_ba
|
|||
{
|
||||
relocate_object((object *)obj,data_relocation_base,code_relocation_base);
|
||||
data->tenured->starts.record_object_start_offset((object *)obj);
|
||||
obj = data->tenured->next_allocated_block_after(obj);
|
||||
obj = data->tenured->next_object_after(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ bool factor_vm::save_image(const vm_char *filename)
|
|||
h.magic = image_magic;
|
||||
h.version = image_version;
|
||||
h.data_relocation_base = data->tenured->start;
|
||||
h.data_size = data->tenured->here - data->tenured->start;
|
||||
h.data_size = data->tenured->occupied();
|
||||
h.code_relocation_base = code->seg->start;
|
||||
h.code_size = code->allocator->occupied();
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace factor
|
|||
#include "free_list_allocator.hpp"
|
||||
#include "write_barrier.hpp"
|
||||
#include "object_start_map.hpp"
|
||||
#include "nursery_space.hpp"
|
||||
#include "aging_space.hpp"
|
||||
#include "tenured_space.hpp"
|
||||
#include "data_heap.hpp"
|
||||
|
|
|
@ -6,10 +6,14 @@ struct nursery_policy {
|
|||
|
||||
nursery_policy(factor_vm *parent_) : parent(parent_) {}
|
||||
|
||||
bool should_copy_p(object *untagged)
|
||||
bool should_copy_p(object *obj)
|
||||
{
|
||||
return parent->nursery.contains_p(untagged);
|
||||
return parent->nursery.contains_p(obj);
|
||||
}
|
||||
|
||||
void promoted_object(object *obj) {}
|
||||
|
||||
void visited_object(object *obj) {}
|
||||
};
|
||||
|
||||
struct nursery_collector : copying_collector<aging_space,nursery_policy> {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
struct nursery_space : bump_allocator<object>
|
||||
{
|
||||
nursery_space(cell size, cell start) : bump_allocator<object>(size,start) {}
|
||||
};
|
||||
|
||||
}
|
|
@ -1,19 +1,65 @@
|
|||
namespace factor
|
||||
{
|
||||
|
||||
struct tenured_space : bump_allocator {
|
||||
struct tenured_space : free_list_allocator<object> {
|
||||
object_start_map starts;
|
||||
std::vector<object *> mark_stack;
|
||||
|
||||
tenured_space(cell size, cell start) :
|
||||
bump_allocator(size,start), starts(size,start) {}
|
||||
free_list_allocator<object>(size,start), starts(size,start) {}
|
||||
|
||||
object *allot(cell size)
|
||||
{
|
||||
if(here + size > end) return NULL;
|
||||
object *obj = free_list_allocator<object>::allot(size);
|
||||
if(obj)
|
||||
{
|
||||
starts.record_object_start_offset(obj);
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
object *obj = bump_allocator::allot(size);
|
||||
starts.record_object_start_offset(obj);
|
||||
return obj;
|
||||
object *first_allocated_block_after(object *block)
|
||||
{
|
||||
while(block != this->last_block() && block->free_p())
|
||||
{
|
||||
free_heap_block *free_block = (free_heap_block *)block;
|
||||
block = (object *)((cell)free_block + free_block->size());
|
||||
}
|
||||
|
||||
if(block == this->last_block())
|
||||
return NULL;
|
||||
else
|
||||
return block;
|
||||
}
|
||||
|
||||
cell first_object()
|
||||
{
|
||||
return (cell)first_allocated_block_after(this->first_block());
|
||||
}
|
||||
|
||||
cell next_object_after(cell scan)
|
||||
{
|
||||
cell size = ((object *)scan)->size();
|
||||
object *next = (object *)(scan + size);
|
||||
return (cell)first_allocated_block_after(next);
|
||||
}
|
||||
|
||||
void clear_mark_bits()
|
||||
{
|
||||
state.clear_mark_bits();
|
||||
}
|
||||
|
||||
bool marked_p(object *obj)
|
||||
{
|
||||
return this->state.marked_p(obj);
|
||||
}
|
||||
|
||||
void mark_and_push(object *obj)
|
||||
{
|
||||
this->state.set_marked_p(obj);
|
||||
this->mark_stack.push_back(obj);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,12 +4,23 @@ namespace factor
|
|||
{
|
||||
|
||||
to_tenured_collector::to_tenured_collector(factor_vm *myvm_) :
|
||||
copying_collector<tenured_space,to_tenured_policy>(
|
||||
collector<tenured_space,to_tenured_policy>(
|
||||
myvm_,
|
||||
&myvm_->gc_stats.aging_stats,
|
||||
myvm_->data->tenured,
|
||||
to_tenured_policy(myvm_)) {}
|
||||
|
||||
void to_tenured_collector::tenure_reachable_objects()
|
||||
{
|
||||
std::vector<object *> *mark_stack = &this->target->mark_stack;
|
||||
while(!mark_stack->empty())
|
||||
{
|
||||
object *obj = mark_stack->back();
|
||||
mark_stack->pop_back();
|
||||
this->trace_slots(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void factor_vm::collect_to_tenured()
|
||||
{
|
||||
/* Copy live objects from aging space to tenured space. */
|
||||
|
@ -21,7 +32,7 @@ void factor_vm::collect_to_tenured()
|
|||
card_points_to_aging,
|
||||
dummy_unmarker());
|
||||
collector.trace_code_heap_roots(&code->points_to_aging);
|
||||
collector.cheneys_algorithm();
|
||||
collector.tenure_reachable_objects();
|
||||
update_code_heap_for_minor_gc(&code->points_to_aging);
|
||||
|
||||
nursery.here = nursery.start;
|
||||
|
|
|
@ -11,10 +11,18 @@ struct to_tenured_policy {
|
|||
{
|
||||
return !tenured->contains_p(untagged);
|
||||
}
|
||||
|
||||
void promoted_object(object *obj)
|
||||
{
|
||||
tenured->mark_stack.push_back(obj);
|
||||
}
|
||||
|
||||
void visited_object(object *obj) {}
|
||||
};
|
||||
|
||||
struct to_tenured_collector : copying_collector<tenured_space,to_tenured_policy> {
|
||||
struct to_tenured_collector : collector<tenured_space,to_tenured_policy> {
|
||||
to_tenured_collector(factor_vm *myvm_);
|
||||
void tenure_reachable_objects();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ struct factor_vm
|
|||
context *ctx;
|
||||
|
||||
/* New objects are allocated here */
|
||||
bump_allocator nursery;
|
||||
nursery_space nursery;
|
||||
|
||||
/* Add this to a shifted address to compute write barrier offsets */
|
||||
cell cards_offset;
|
||||
|
@ -308,7 +308,7 @@ struct factor_vm
|
|||
void print_callstack();
|
||||
void dump_cell(cell x);
|
||||
void dump_memory(cell from, cell to);
|
||||
void dump_zone(const char *name, bump_allocator *z);
|
||||
template<typename Generation> void dump_generation(const char *name, Generation *gen);
|
||||
void dump_generations();
|
||||
void dump_objects(cell type);
|
||||
void find_data_references_step(cell *scan);
|
||||
|
|
Loading…
Reference in New Issue