VM: move the stack scrubbing logic to call_frame_slot_visitor to avoid visiting the callstack twice

primitive_minor_gc() iterates the stack twice, first to scrub stack
locations, then to trace overinitialized ones and gc roots. By running
visit_callstack() before visit_stack_elements() you only need to do it
once.
db4
Björn Lindqvist 2014-08-28 17:32:36 +02:00 committed by Doug Coleman
parent 8fb8313251
commit 9836d6a1d0
4 changed files with 56 additions and 87 deletions

View File

@ -10,7 +10,7 @@ ARTICLE: "compiler.cfg.stacks.vacant" "Uninitialized/overinitialized stack locat
"##replace ... D 0"
"##replace ... D 1"
}
"The GC check runs before stack locations 0 and 1 have been initialized, and so the GC needs to scrub them so that they don't get traced. This is achieved by computing uninitialized locations with a dataflow analysis, and recording the information in GC maps. The scrub_contexts() method on vm/gc.cpp reads this information from GC maps and performs the scrubbing." ;
"The GC check runs before stack locations 0 and 1 have been initialized, and so the GC needs to scrub them so that they don't get traced. This is achieved by computing uninitialized locations with a dataflow analysis, and recording the information in GC maps. The call_frame_slot_visitor object in vm/slot_visitor.hpp reads this information from GC maps and performs the scrubbing." ;
HELP: initial-state
{ $description "Initially the stack bottom is at 0 for both the data and retain stacks and no replaces have been registered." } ;

View File

@ -49,36 +49,6 @@ void context::fix_stacks() {
reset_retainstack();
}
void context::scrub_stacks(gc_info* info, cell index) {
uint8_t* bitmap = info->gc_info_bitmap();
{
cell base = info->callsite_scrub_d(index);
for (cell loc = 0; loc < info->scrub_d_count; loc++) {
if (bitmap_p(bitmap, base + loc)) {
#ifdef DEBUG_GC_MAPS
std::cout << "scrubbing datastack location " << loc << std::endl;
#endif
*((cell*)datastack - loc) = 0;
}
}
}
{
cell base = info->callsite_scrub_r(index);
for (cell loc = 0; loc < info->scrub_r_count; loc++) {
if (bitmap_p(bitmap, base + loc)) {
#ifdef DEBUG_GC_MAPS
std::cout << "scrubbing retainstack location " << loc << std::endl;
#endif
*((cell*)retainstack - loc) = 0;
}
}
}
}
context::~context() {
delete datastack_seg;
delete retainstack_seg;

View File

@ -186,47 +186,7 @@ void factor_vm::gc(gc_op op, cell requested_size, bool trace_contexts_p) {
FACTOR_ASSERT(!data->high_fragmentation_p());
}
/* primitive_minor_gc() is invoked by inline GC checks, and it needs to fill in
uninitialized stack locations before actually calling the GC. See the
comment in compiler.cfg.stacks.uninitialized for details. */
struct call_frame_scrubber {
factor_vm* parent;
context* ctx;
call_frame_scrubber(factor_vm* parent, context* ctx)
: parent(parent), ctx(ctx) {}
void operator()(void* frame_top, cell frame_size, code_block* owner,
void* addr) {
cell return_address = owner->offset(addr);
gc_info* info = owner->block_gc_info();
FACTOR_ASSERT(return_address < owner->size());
cell index = info->return_address_index(return_address);
if (index != (cell)-1)
ctx->scrub_stacks(info, index);
}
};
void factor_vm::scrub_context(context* ctx) {
call_frame_scrubber scrubber(this, ctx);
iterate_callstack(ctx, scrubber);
}
void factor_vm::scrub_contexts() {
std::set<context*>::const_iterator begin = active_contexts.begin();
std::set<context*>::const_iterator end = active_contexts.end();
while (begin != end) {
scrub_context(*begin);
begin++;
}
}
void factor_vm::primitive_minor_gc() {
scrub_contexts();
gc(collect_nursery_op, 0, /* requested size */
true /* trace contexts? */);
}

View File

@ -125,6 +125,7 @@ template <typename Fixup> struct slot_visitor {
void visit_roots();
void visit_callstack_object(callstack* stack);
void visit_callstack(context* ctx);
void visit_context(context *ctx);
void visit_contexts();
void visit_code_block_objects(code_block* compiled);
void visit_embedded_literals(code_block* compiled);
@ -261,6 +262,16 @@ template <typename Fixup> void slot_visitor<Fixup>::visit_roots() {
parent->special_objects + special_object_count);
}
/* primitive_minor_gc() is invoked by inline GC checks, and it needs to fill in
uninitialized stack locations before actually calling the GC. See the
documentation in compiler.cfg.stacks.vacant for details.
So for each call frame:
- scrub some uninitialized locations
- trace some overinitialized locations
- trace roots in spill slots
*/
template <typename Fixup> struct call_frame_slot_visitor {
factor_vm* parent;
slot_visitor<Fixup>* visitor;
@ -299,20 +310,28 @@ template <typename Fixup> struct call_frame_slot_visitor {
cell* stack_pointer = (cell*)frame_top;
uint8_t* bitmap = info->gc_info_bitmap();
/* Subtract old value of base pointer from every derived pointer. */
for (cell spill_slot = 0; spill_slot < info->derived_root_count;
spill_slot++) {
uint32_t base_pointer = info->lookup_base_pointer(callsite, spill_slot);
if (base_pointer != (uint32_t)-1) {
/* Scrub vacant stack locations. */
cell callsite_scrub_d = info->callsite_scrub_d(callsite);
for (cell loc = 0; loc < info->scrub_d_count; loc++) {
if (bitmap_p(bitmap, callsite_scrub_d + loc)) {
#ifdef DEBUG_GC_MAPS
std::cout << "visiting derived root " << spill_slot
<< " with base pointer " << base_pointer << std::endl;
std::cout << "scrubbing datastack location " << loc << std::endl;
#endif
stack_pointer[spill_slot] -= stack_pointer[base_pointer];
*((cell*)ctx->datastack - loc) = 0;
}
}
/* Trace all overinitialized stack locations. */
cell callsite_scrub_r = info->callsite_scrub_r(callsite);
for (cell loc = 0; loc < info->scrub_r_count; loc++) {
if (bitmap_p(bitmap, callsite_scrub_r + loc)) {
#ifdef DEBUG_GC_MAPS
std::cout << "scrubbing retainstack location " << loc << std::endl;
#endif
*((cell*)ctx->retainstack - loc) = 0;
}
}
/* Trace overinitialized stack locations. */
cell callsite_check_d = info->callsite_check_d(callsite);
for (uint32_t loc = 0; loc < info->check_d_count; loc++) {
if (bitmap_p(bitmap, callsite_check_d + loc)) {
@ -335,6 +354,19 @@ template <typename Fixup> struct call_frame_slot_visitor {
}
}
/* Subtract old value of base pointer from every derived pointer. */
for (cell spill_slot = 0; spill_slot < info->derived_root_count;
spill_slot++) {
uint32_t base_pointer = info->lookup_base_pointer(callsite, spill_slot);
if (base_pointer != (uint32_t)-1) {
#ifdef DEBUG_GC_MAPS
std::cout << "visiting derived root " << spill_slot
<< " with base pointer " << base_pointer << std::endl;
#endif
stack_pointer[spill_slot] -= stack_pointer[base_pointer];
}
}
/* Update all GC roots, including base pointers. */
cell callsite_gc_roots = info->callsite_gc_roots(callsite);
@ -370,17 +402,24 @@ void slot_visitor<Fixup>::visit_callstack(context* ctx) {
parent->iterate_callstack(ctx, call_frame_visitor, fixup);
}
template <typename Fixup>
void slot_visitor<Fixup>::visit_context(context* ctx) {
/* Callstack is visited first because it scrubs the data and retain
stacks. */
visit_callstack(ctx);
visit_stack_elements(ctx->datastack_seg, (cell*)ctx->datastack);
visit_stack_elements(ctx->retainstack_seg, (cell*)ctx->retainstack);
visit_object_array(ctx->context_objects,
ctx->context_objects + context_object_count);
}
template <typename Fixup> void slot_visitor<Fixup>::visit_contexts() {
std::set<context*>::const_iterator begin = parent->active_contexts.begin();
std::set<context*>::const_iterator end = parent->active_contexts.end();
while (begin != end) {
context* ctx = *begin;
visit_stack_elements(ctx->datastack_seg, (cell*)ctx->datastack);
visit_stack_elements(ctx->retainstack_seg, (cell*)ctx->retainstack);
visit_object_array(ctx->context_objects,
ctx->context_objects + context_object_count);
visit_callstack(ctx);
visit_context(*begin);
begin++;
}
}