VM: Refactor code_blocks to Factor style

db4
Erik Charlebois 2013-05-11 21:50:21 -04:00
parent 2e20733ade
commit d2fe86eb7e
3 changed files with 561 additions and 649 deletions

View File

@ -1,5 +1,4 @@
namespace factor namespace factor {
{
/* Code block visitors iterate over sets of code blocks, applying a functor to /* Code block visitors iterate over sets of code blocks, applying a functor to
each one. The functor returns a new code_block pointer, which may or may not each one. The functor returns a new code_block pointer, which may or may not
@ -12,130 +11,114 @@ Iteration is driven by visit_*() methods. Some of them define GC roots:
- visit_context_code_blocks() - visit_context_code_blocks()
- visit_callback_code_blocks() */ - visit_callback_code_blocks() */
template<typename Fixup> struct code_block_visitor { template <typename Fixup> struct code_block_visitor {
factor_vm *parent; factor_vm* parent;
Fixup fixup; Fixup fixup;
explicit code_block_visitor(factor_vm *parent_, Fixup fixup_) : explicit code_block_visitor(factor_vm* parent_, Fixup fixup_)
parent(parent_), fixup(fixup_) {} : parent(parent_), fixup(fixup_) {}
code_block *visit_code_block(code_block *compiled); code_block* visit_code_block(code_block* compiled);
void visit_object_code_block(object *obj); void visit_object_code_block(object* obj);
void visit_embedded_code_pointers(code_block *compiled); void visit_embedded_code_pointers(code_block* compiled);
void visit_context_code_blocks(); void visit_context_code_blocks();
void visit_uninitialized_code_blocks(); void visit_uninitialized_code_blocks();
void visit_code_roots(); void visit_code_roots();
}; };
template<typename Fixup> template <typename Fixup>
code_block *code_block_visitor<Fixup>::visit_code_block(code_block *compiled) code_block* code_block_visitor<Fixup>::visit_code_block(code_block* compiled) {
{ return fixup.fixup_code(compiled);
return fixup.fixup_code(compiled);
} }
template<typename Fixup> template <typename Fixup> struct call_frame_code_block_visitor {
struct call_frame_code_block_visitor { factor_vm* parent;
factor_vm *parent; Fixup fixup;
Fixup fixup;
explicit call_frame_code_block_visitor(factor_vm *parent_, Fixup fixup_) : explicit call_frame_code_block_visitor(factor_vm* parent_, Fixup fixup_)
parent(parent_), fixup(fixup_) {} : parent(parent_), fixup(fixup_) {}
void operator()(void *frame_top, cell frame_size, code_block *owner, void *addr) void operator()(void* frame_top, cell frame_size, code_block* owner,
{ void* addr) {
code_block *compiled = Fixup::translated_code_block_map code_block* compiled =
? owner Fixup::translated_code_block_map ? owner : fixup.fixup_code(owner);
: fixup.fixup_code(owner); void* fixed_addr = compiled->address_for_offset(owner->offset(addr));
void *fixed_addr = compiled->address_for_offset(owner->offset(addr)); set_frame_return_address(frame_top, fixed_addr);
set_frame_return_address(frame_top, fixed_addr); }
}
}; };
template<typename Fixup> template <typename Fixup>
void code_block_visitor<Fixup>::visit_object_code_block(object *obj) void code_block_visitor<Fixup>::visit_object_code_block(object* obj) {
{ switch (obj->type()) {
switch(obj->type()) case WORD_TYPE: {
{ word* w = (word*)obj;
case WORD_TYPE: if (w->entry_point)
{ w->entry_point = visit_code_block(w->code())->entry_point();
word *w = (word *)obj; break;
if(w->entry_point) }
w->entry_point = visit_code_block(w->code())->entry_point(); case QUOTATION_TYPE: {
break; quotation* q = (quotation*)obj;
} if (q->entry_point)
case QUOTATION_TYPE: q->entry_point = visit_code_block(q->code())->entry_point();
{ break;
quotation *q = (quotation *)obj; }
if(q->entry_point) case CALLSTACK_TYPE: {
q->entry_point = visit_code_block(q->code())->entry_point(); callstack* stack = (callstack*)obj;
break; call_frame_code_block_visitor<Fixup> call_frame_visitor(parent, fixup);
} parent->iterate_callstack_object(stack, call_frame_visitor, fixup);
case CALLSTACK_TYPE: break;
{ }
callstack *stack = (callstack *)obj; }
call_frame_code_block_visitor<Fixup> call_frame_visitor(parent,fixup);
parent->iterate_callstack_object(stack,call_frame_visitor,fixup);
break;
}
}
} }
template<typename Fixup> template <typename Fixup> struct embedded_code_pointers_visitor {
struct embedded_code_pointers_visitor { Fixup fixup;
Fixup fixup;
explicit embedded_code_pointers_visitor(Fixup fixup_) : fixup(fixup_) {} explicit embedded_code_pointers_visitor(Fixup fixup_) : fixup(fixup_) {}
void operator()(instruction_operand op) void operator()(instruction_operand op) {
{ relocation_type type = op.rel_type();
relocation_type type = op.rel_type(); if (type == RT_ENTRY_POINT || type == RT_ENTRY_POINT_PIC ||
if(type == RT_ENTRY_POINT type == RT_ENTRY_POINT_PIC_TAIL)
|| type == RT_ENTRY_POINT_PIC op.store_code_block(fixup.fixup_code(op.load_code_block()));
|| type == RT_ENTRY_POINT_PIC_TAIL) }
op.store_code_block(fixup.fixup_code(op.load_code_block()));
}
}; };
template<typename Fixup> template <typename Fixup>
void code_block_visitor<Fixup>::visit_embedded_code_pointers(code_block *compiled) void code_block_visitor<Fixup>::visit_embedded_code_pointers(
{ code_block* compiled) {
if(!parent->code->uninitialized_p(compiled)) if (!parent->code->uninitialized_p(compiled)) {
{ embedded_code_pointers_visitor<Fixup> operand_visitor(fixup);
embedded_code_pointers_visitor<Fixup> operand_visitor(fixup); compiled->each_instruction_operand(operand_visitor);
compiled->each_instruction_operand(operand_visitor); }
}
} }
template<typename Fixup> template <typename Fixup>
void code_block_visitor<Fixup>::visit_context_code_blocks() void code_block_visitor<Fixup>::visit_context_code_blocks() {
{ call_frame_code_block_visitor<Fixup> call_frame_visitor(parent, fixup);
call_frame_code_block_visitor<Fixup> call_frame_visitor(parent,fixup); parent->iterate_active_callstacks(call_frame_visitor, fixup);
parent->iterate_active_callstacks(call_frame_visitor,fixup);
} }
template<typename Fixup> template <typename Fixup>
void code_block_visitor<Fixup>::visit_uninitialized_code_blocks() void code_block_visitor<Fixup>::visit_uninitialized_code_blocks() {
{ std::map<code_block*, cell>* uninitialized_blocks =
std::map<code_block *, cell> *uninitialized_blocks = &parent->code->uninitialized_blocks; &parent->code->uninitialized_blocks;
std::map<code_block *, cell>::const_iterator iter = uninitialized_blocks->begin(); std::map<code_block*, cell>::const_iterator iter =
std::map<code_block *, cell>::const_iterator end = uninitialized_blocks->end(); uninitialized_blocks->begin();
std::map<code_block*, cell>::const_iterator end = uninitialized_blocks->end();
std::map<code_block *, cell> new_uninitialized_blocks; std::map<code_block*, cell> new_uninitialized_blocks;
for(; iter != end; iter++) for (; iter != end; iter++) {
{ new_uninitialized_blocks.insert(
new_uninitialized_blocks.insert(std::make_pair( std::make_pair(fixup.fixup_code(iter->first), iter->second));
fixup.fixup_code(iter->first), }
iter->second));
}
parent->code->uninitialized_blocks = new_uninitialized_blocks; parent->code->uninitialized_blocks = new_uninitialized_blocks;
} }
template<typename Fixup> template <typename Fixup> void code_block_visitor<Fixup>::visit_code_roots() {
void code_block_visitor<Fixup>::visit_code_roots() visit_uninitialized_code_blocks();
{
visit_uninitialized_code_blocks();
} }
} }

View File

@ -1,530 +1,495 @@
#include "master.hpp" #include "master.hpp"
namespace factor namespace factor {
{
cell code_block::owner_quot() const cell code_block::owner_quot() const {
{ tagged<object> executing(owner);
tagged<object> executing(owner); if (!optimized_p() && executing->type() == WORD_TYPE)
if (!optimized_p() && executing->type() == WORD_TYPE) executing = executing.as<word>()->def;
executing = executing.as<word>()->def; return executing.value();
return executing.value();
} }
cell code_block::scan(factor_vm *vm, void *addr) const cell code_block::scan(factor_vm* vm, void* addr) const {
{ switch (type()) {
switch(type()) case code_block_unoptimized: {
{ tagged<object> obj(owner);
case code_block_unoptimized: if (obj.type_p(WORD_TYPE))
{ obj = obj.as<word>()->def;
tagged<object> obj(owner);
if(obj.type_p(WORD_TYPE))
obj = obj.as<word>()->def;
if(obj.type_p(QUOTATION_TYPE)) if (obj.type_p(QUOTATION_TYPE))
return tag_fixnum(vm->quot_code_offset_to_scan(obj.value(),offset(addr))); return tag_fixnum(
else vm->quot_code_offset_to_scan(obj.value(), offset(addr)));
return false_object; else
} return false_object;
case code_block_optimized: }
case code_block_pic: case code_block_optimized:
return false_object; case code_block_pic:
default: return false_object;
critical_error("Bad frame type",type()); default:
return false_object; critical_error("Bad frame type", type());
} return false_object;
}
} }
cell factor_vm::compute_entry_point_address(cell obj) cell factor_vm::compute_entry_point_address(cell obj) {
{ switch (tagged<object>(obj).type()) {
switch(tagged<object>(obj).type()) case WORD_TYPE:
{ return (cell) untag<word>(obj)->entry_point;
case WORD_TYPE: case QUOTATION_TYPE:
return (cell)untag<word>(obj)->entry_point; return (cell) untag<quotation>(obj)->entry_point;
case QUOTATION_TYPE: default:
return (cell)untag<quotation>(obj)->entry_point; critical_error("Expected word or quotation", obj);
default: return 0;
critical_error("Expected word or quotation",obj); }
return 0;
}
} }
cell factor_vm::compute_entry_point_pic_address(word *w, cell tagged_quot) cell factor_vm::compute_entry_point_pic_address(word* w, cell tagged_quot) {
{ if (!to_boolean(tagged_quot) || max_pic_size == 0)
if(!to_boolean(tagged_quot) || max_pic_size == 0) return (cell) w->entry_point;
return (cell)w->entry_point; else {
else quotation* quot = untag<quotation>(tagged_quot);
{ if (quot_compiled_p(quot))
quotation *quot = untag<quotation>(tagged_quot); return (cell) quot->entry_point;
if(quot_compiled_p(quot)) else
return (cell)quot->entry_point; return (cell) w->entry_point;
else }
return (cell)w->entry_point;
}
} }
cell factor_vm::compute_entry_point_pic_address(cell w_) cell factor_vm::compute_entry_point_pic_address(cell w_) {
{ tagged<word> w(w_);
tagged<word> w(w_); return compute_entry_point_pic_address(w.untagged(), w->pic_def);
return compute_entry_point_pic_address(w.untagged(),w->pic_def);
} }
cell factor_vm::compute_entry_point_pic_tail_address(cell w_) cell factor_vm::compute_entry_point_pic_tail_address(cell w_) {
{ tagged<word> w(w_);
tagged<word> w(w_); return compute_entry_point_pic_address(w.untagged(), w->pic_tail_def);
return compute_entry_point_pic_address(w.untagged(),w->pic_tail_def);
} }
cell factor_vm::code_block_owner(code_block *compiled) cell factor_vm::code_block_owner(code_block* compiled) {
{ tagged<object> owner(compiled->owner);
tagged<object> owner(compiled->owner);
/* Cold generic word call sites point to quotations that call the /* Cold generic word call sites point to quotations that call the
inline-cache-miss and inline-cache-miss-tail primitives. */ inline-cache-miss and inline-cache-miss-tail primitives. */
if(owner.type_p(QUOTATION_TYPE)) if (owner.type_p(QUOTATION_TYPE)) {
{ tagged<quotation> quot(owner.as<quotation>());
tagged<quotation> quot(owner.as<quotation>()); tagged<array> elements(quot->array);
tagged<array> elements(quot->array);
#ifdef FACTOR_DEBUG #ifdef FACTOR_DEBUG
FACTOR_ASSERT(array_capacity(elements.untagged()) == 5); FACTOR_ASSERT(array_capacity(elements.untagged()) == 5);
FACTOR_ASSERT(array_nth(elements.untagged(),4) == special_objects[PIC_MISS_WORD] FACTOR_ASSERT(array_nth(elements.untagged(), 4) ==
|| array_nth(elements.untagged(),4) == special_objects[PIC_MISS_TAIL_WORD]); special_objects[PIC_MISS_WORD] ||
array_nth(elements.untagged(), 4) ==
special_objects[PIC_MISS_TAIL_WORD]);
#endif #endif
tagged<wrapper> word_wrapper(array_nth(elements.untagged(),0)); tagged<wrapper> word_wrapper(array_nth(elements.untagged(), 0));
return word_wrapper->object; return word_wrapper->object;
} } else
else return compiled->owner;
return compiled->owner;
} }
struct update_word_references_relocation_visitor { struct update_word_references_relocation_visitor {
factor_vm *parent; factor_vm* parent;
bool reset_inline_caches; bool reset_inline_caches;
update_word_references_relocation_visitor( update_word_references_relocation_visitor(factor_vm* parent_,
factor_vm *parent_, bool reset_inline_caches_)
bool reset_inline_caches_) : : parent(parent_), reset_inline_caches(reset_inline_caches_) {}
parent(parent_),
reset_inline_caches(reset_inline_caches_) {}
void operator()(instruction_operand op) void operator()(instruction_operand op) {
{ switch (op.rel_type()) {
switch(op.rel_type()) case RT_ENTRY_POINT: {
{ code_block* compiled = op.load_code_block();
case RT_ENTRY_POINT: cell owner = compiled->owner;
{ if (to_boolean(owner))
code_block *compiled = op.load_code_block(); op.store_value(parent->compute_entry_point_address(owner));
cell owner = compiled->owner; break;
if(to_boolean(owner)) }
op.store_value(parent->compute_entry_point_address(owner)); case RT_ENTRY_POINT_PIC: {
break; code_block* compiled = op.load_code_block();
} if (reset_inline_caches || !compiled->pic_p()) {
case RT_ENTRY_POINT_PIC: cell owner = parent->code_block_owner(compiled);
{ if (to_boolean(owner))
code_block *compiled = op.load_code_block(); op.store_value(parent->compute_entry_point_pic_address(owner));
if(reset_inline_caches || !compiled->pic_p()) }
{ break;
cell owner = parent->code_block_owner(compiled); }
if(to_boolean(owner)) case RT_ENTRY_POINT_PIC_TAIL: {
op.store_value(parent->compute_entry_point_pic_address(owner)); code_block* compiled = op.load_code_block();
} if (reset_inline_caches || !compiled->pic_p()) {
break; cell owner = parent->code_block_owner(compiled);
} if (to_boolean(owner))
case RT_ENTRY_POINT_PIC_TAIL: op.store_value(parent->compute_entry_point_pic_tail_address(owner));
{ }
code_block *compiled = op.load_code_block(); break;
if(reset_inline_caches || !compiled->pic_p()) }
{ default:
cell owner = parent->code_block_owner(compiled); break;
if(to_boolean(owner)) }
op.store_value(parent->compute_entry_point_pic_tail_address(owner)); }
}
break;
}
default:
break;
}
}
}; };
/* Relocate new code blocks completely; updating references to literals, /* Relocate new code blocks completely; updating references to literals,
dlsyms, and words. For all other words in the code heap, we only need dlsyms, and words. For all other words in the code heap, we only need
to update references to other words, without worrying about literals to update references to other words, without worrying about literals
or dlsyms. */ or dlsyms. */
void factor_vm::update_word_references(code_block *compiled, bool reset_inline_caches) void factor_vm::update_word_references(code_block* compiled,
{ bool reset_inline_caches) {
if(code->uninitialized_p(compiled)) if (code->uninitialized_p(compiled))
initialize_code_block(compiled); initialize_code_block(compiled);
/* update_word_references() is always applied to every block in /* update_word_references() is always applied to every block in
the code heap. Since it resets all call sites to point to the code heap. Since it resets all call sites to point to
their canonical entry point (cold entry point for non-tail calls, their canonical entry point (cold entry point for non-tail calls,
standard entry point for tail calls), it means that no PICs standard entry point for tail calls), it means that no PICs
are referenced after this is done. So instead of polluting are referenced after this is done. So instead of polluting
the code heap with dead PICs that will be freed on the next the code heap with dead PICs that will be freed on the next
GC, we add them to the free list immediately. */ GC, we add them to the free list immediately. */
else if(reset_inline_caches && compiled->pic_p()) else if (reset_inline_caches && compiled->pic_p())
code->free(compiled); code->free(compiled);
else else {
{ update_word_references_relocation_visitor visitor(this,
update_word_references_relocation_visitor visitor(this,reset_inline_caches); reset_inline_caches);
compiled->each_instruction_operand(visitor); compiled->each_instruction_operand(visitor);
compiled->flush_icache(); compiled->flush_icache();
} }
} }
/* Look up an external library symbol referenced by a compiled code block */ /* Look up an external library symbol referenced by a compiled code block */
cell factor_vm::compute_dlsym_address(array *parameters, cell index) cell factor_vm::compute_dlsym_address(array* parameters, cell index) {
{ cell symbol = array_nth(parameters, index);
cell symbol = array_nth(parameters,index); cell library = array_nth(parameters, index + 1);
cell library = array_nth(parameters,index + 1);
dll *d = (to_boolean(library) ? untag<dll>(library) : NULL); dll* d = (to_boolean(library) ? untag<dll>(library) : NULL);
void* undefined_symbol = (void*)factor::undefined_symbol; void* undefined_symbol = (void*)factor::undefined_symbol;
undefined_symbol = FUNCTION_CODE_POINTER(undefined_symbol); undefined_symbol = FUNCTION_CODE_POINTER(undefined_symbol);
if(d != NULL && !d->handle) if (d != NULL && !d->handle)
return (cell)undefined_symbol; return (cell) undefined_symbol;
switch(tagged<object>(symbol).type()) switch (tagged<object>(symbol).type()) {
{ case BYTE_ARRAY_TYPE: {
case BYTE_ARRAY_TYPE: symbol_char* name = alien_offset(symbol);
{ void* sym = ffi_dlsym(d, name);
symbol_char *name = alien_offset(symbol);
void *sym = ffi_dlsym(d,name);
if(sym) if (sym)
return (cell)sym; return (cell) sym;
else else
return (cell)undefined_symbol; return (cell) undefined_symbol;
} }
case ARRAY_TYPE: case ARRAY_TYPE: {
{ array* names = untag<array>(symbol);
array *names = untag<array>(symbol); for (cell i = 0; i < array_capacity(names); i++) {
for(cell i = 0; i < array_capacity(names); i++) symbol_char* name = alien_offset(array_nth(names, i));
{ void* sym = ffi_dlsym(d, name);
symbol_char *name = alien_offset(array_nth(names,i));
void *sym = ffi_dlsym(d,name);
if(sym) if (sym)
return (cell)sym; return (cell) sym;
} }
return (cell)undefined_symbol; return (cell) undefined_symbol;
} }
default: default:
critical_error("Bad symbol specifier",symbol); critical_error("Bad symbol specifier", symbol);
return (cell)undefined_symbol; return (cell) undefined_symbol;
} }
} }
#ifdef FACTOR_PPC #ifdef FACTOR_PPC
cell factor_vm::compute_dlsym_toc_address(array *parameters, cell index) cell factor_vm::compute_dlsym_toc_address(array* parameters, cell index) {
{ cell symbol = array_nth(parameters, index);
cell symbol = array_nth(parameters,index); cell library = array_nth(parameters, index + 1);
cell library = array_nth(parameters,index + 1);
dll *d = (to_boolean(library) ? untag<dll>(library) : NULL); dll* d = (to_boolean(library) ? untag<dll>(library) : NULL);
void* undefined_toc = (void*)factor::undefined_symbol; void* undefined_toc = (void*)factor::undefined_symbol;
undefined_toc = FUNCTION_TOC_POINTER(undefined_toc); undefined_toc = FUNCTION_TOC_POINTER(undefined_toc);
if(d != NULL && !d->handle) if (d != NULL && !d->handle)
return (cell)undefined_toc; return (cell) undefined_toc;
switch(tagged<object>(symbol).type()) switch (tagged<object>(symbol).type()) {
{ case BYTE_ARRAY_TYPE: {
case BYTE_ARRAY_TYPE: symbol_char* name = alien_offset(symbol);
{ void* toc = ffi_dlsym_toc(d, name);
symbol_char *name = alien_offset(symbol); if (toc)
void* toc = ffi_dlsym_toc(d,name); return (cell) toc;
if(toc) else
return (cell)toc; return (cell) undefined_toc;
else }
return (cell)undefined_toc; case ARRAY_TYPE: {
} array* names = untag<array>(symbol);
case ARRAY_TYPE: for (cell i = 0; i < array_capacity(names); i++) {
{ symbol_char* name = alien_offset(array_nth(names, i));
array *names = untag<array>(symbol); void* toc = ffi_dlsym_toc(d, name);
for(cell i = 0; i < array_capacity(names); i++)
{
symbol_char *name = alien_offset(array_nth(names,i));
void *toc = ffi_dlsym_toc(d,name);
if(toc) if (toc)
return (cell)toc; return (cell) toc;
} }
return (cell)undefined_toc; return (cell) undefined_toc;
} }
default: default:
critical_error("Bad symbol specifier",symbol); critical_error("Bad symbol specifier", symbol);
return (cell)undefined_toc; return (cell) undefined_toc;
} }
} }
#endif #endif
cell factor_vm::compute_vm_address(cell arg) cell factor_vm::compute_vm_address(cell arg) {
{ return (cell) this + untag_fixnum(arg);
return (cell)this + untag_fixnum(arg);
} }
void factor_vm::store_external_address(instruction_operand op) void factor_vm::store_external_address(instruction_operand op) {
{ code_block* compiled = op.compiled;
code_block *compiled = op.compiled; array* parameters =
array *parameters = (to_boolean(compiled->parameters) ? untag<array>(compiled->parameters) : NULL); (to_boolean(compiled->parameters) ? untag<array>(compiled->parameters)
cell index = op.index; : NULL);
cell index = op.index;
switch(op.rel_type()) switch (op.rel_type()) {
{ case RT_DLSYM:
case RT_DLSYM: op.store_value(compute_dlsym_address(parameters, index));
op.store_value(compute_dlsym_address(parameters,index)); break;
break; case RT_THIS:
case RT_THIS: op.store_value((cell) compiled->entry_point());
op.store_value((cell)compiled->entry_point()); break;
break; case RT_MEGAMORPHIC_CACHE_HITS:
case RT_MEGAMORPHIC_CACHE_HITS: op.store_value((cell) & dispatch_stats.megamorphic_cache_hits);
op.store_value((cell)&dispatch_stats.megamorphic_cache_hits); break;
break; case RT_VM:
case RT_VM: op.store_value(compute_vm_address(array_nth(parameters, index)));
op.store_value(compute_vm_address(array_nth(parameters,index))); break;
break; case RT_CARDS_OFFSET:
case RT_CARDS_OFFSET: op.store_value(cards_offset);
op.store_value(cards_offset); break;
break; case RT_DECKS_OFFSET:
case RT_DECKS_OFFSET: op.store_value(decks_offset);
op.store_value(decks_offset); break;
break;
#ifdef WINDOWS #ifdef WINDOWS
case RT_EXCEPTION_HANDLER: case RT_EXCEPTION_HANDLER:
op.store_value((cell)&factor::exception_handler); op.store_value((cell) & factor::exception_handler);
break; break;
#endif #endif
#ifdef FACTOR_PPC #ifdef FACTOR_PPC
case RT_DLSYM_TOC: case RT_DLSYM_TOC:
op.store_value(compute_dlsym_toc_address(parameters,index)); op.store_value(compute_dlsym_toc_address(parameters, index));
break; break;
#endif #endif
case RT_INLINE_CACHE_MISS: case RT_INLINE_CACHE_MISS:
op.store_value((cell)&factor::inline_cache_miss); op.store_value((cell) & factor::inline_cache_miss);
break; break;
case RT_SAFEPOINT: case RT_SAFEPOINT:
op.store_value((cell)code->safepoint_page); op.store_value((cell) code->safepoint_page);
break; break;
default: default:
critical_error("Bad rel type in store_external_address()",op.rel_type()); critical_error("Bad rel type in store_external_address()", op.rel_type());
break; break;
} }
} }
cell factor_vm::compute_here_address(cell arg, cell offset, code_block *compiled) cell factor_vm::compute_here_address(cell arg, cell offset,
{ code_block* compiled) {
fixnum n = untag_fixnum(arg); fixnum n = untag_fixnum(arg);
if(n >= 0) if (n >= 0)
return (cell)compiled->entry_point() + offset + n; return (cell) compiled->entry_point() + offset + n;
else else
return (cell)compiled->entry_point() - n; return (cell) compiled->entry_point() - n;
} }
struct initial_code_block_visitor { struct initial_code_block_visitor {
factor_vm *parent; factor_vm* parent;
cell literals; cell literals;
cell literal_index; cell literal_index;
explicit initial_code_block_visitor(factor_vm *parent_, cell literals_) explicit initial_code_block_visitor(factor_vm* parent_, cell literals_)
: parent(parent_), literals(literals_), literal_index(0) {} : parent(parent_), literals(literals_), literal_index(0) {}
cell next_literal() cell next_literal() {
{ return array_nth(untag<array>(literals), literal_index++);
return array_nth(untag<array>(literals),literal_index++); }
}
void operator()(instruction_operand op) void operator()(instruction_operand op) {
{ switch (op.rel_type()) {
switch(op.rel_type()) case RT_LITERAL:
{ op.store_value(next_literal());
case RT_LITERAL: break;
op.store_value(next_literal()); case RT_ENTRY_POINT:
break; op.store_value(parent->compute_entry_point_address(next_literal()));
case RT_ENTRY_POINT: break;
op.store_value(parent->compute_entry_point_address(next_literal())); case RT_ENTRY_POINT_PIC:
break; op.store_value(parent->compute_entry_point_pic_address(next_literal()));
case RT_ENTRY_POINT_PIC: break;
op.store_value(parent->compute_entry_point_pic_address(next_literal())); case RT_ENTRY_POINT_PIC_TAIL:
break; op.store_value(
case RT_ENTRY_POINT_PIC_TAIL: parent->compute_entry_point_pic_tail_address(next_literal()));
op.store_value(parent->compute_entry_point_pic_tail_address(next_literal())); break;
break; case RT_HERE:
case RT_HERE: op.store_value(parent->compute_here_address(
op.store_value(parent->compute_here_address(next_literal(),op.rel_offset(),op.compiled)); next_literal(), op.rel_offset(), op.compiled));
break; break;
case RT_UNTAGGED: case RT_UNTAGGED:
op.store_value(untag_fixnum(next_literal())); op.store_value(untag_fixnum(next_literal()));
break; break;
default: default:
parent->store_external_address(op); parent->store_external_address(op);
break; break;
} }
} }
}; };
/* Perform all fixups on a code block */ /* Perform all fixups on a code block */
void factor_vm::initialize_code_block(code_block *compiled, cell literals) void factor_vm::initialize_code_block(code_block* compiled, cell literals) {
{ initial_code_block_visitor visitor(this, literals);
initial_code_block_visitor visitor(this,literals); compiled->each_instruction_operand(visitor);
compiled->each_instruction_operand(visitor); compiled->flush_icache();
compiled->flush_icache();
/* next time we do a minor GC, we have to trace this code block, since /* next time we do a minor GC, we have to trace this code block, since
the newly-installed instruction operands might point to literals in the newly-installed instruction operands might point to literals in
nursery or aging */ nursery or aging */
code->write_barrier(compiled); code->write_barrier(compiled);
} }
void factor_vm::initialize_code_block(code_block *compiled) void factor_vm::initialize_code_block(code_block* compiled) {
{ std::map<code_block*, cell>::iterator iter =
std::map<code_block *,cell>::iterator iter = code->uninitialized_blocks.find(compiled); code->uninitialized_blocks.find(compiled);
initialize_code_block(compiled,iter->second); initialize_code_block(compiled, iter->second);
code->uninitialized_blocks.erase(iter); code->uninitialized_blocks.erase(iter);
} }
/* Fixup labels. This is done at compile time, not image load time */ /* Fixup labels. This is done at compile time, not image load time */
void factor_vm::fixup_labels(array *labels, code_block *compiled) void factor_vm::fixup_labels(array* labels, code_block* compiled) {
{ cell size = array_capacity(labels);
cell size = array_capacity(labels);
for(cell i = 0; i < size; i += 3) for (cell i = 0; i < size; i += 3) {
{ relocation_class rel_class =
relocation_class rel_class = (relocation_class)untag_fixnum(array_nth(labels,i)); (relocation_class) untag_fixnum(array_nth(labels, i));
cell offset = untag_fixnum(array_nth(labels,i + 1)); cell offset = untag_fixnum(array_nth(labels, i + 1));
cell target = untag_fixnum(array_nth(labels,i + 2)); cell target = untag_fixnum(array_nth(labels, i + 2));
relocation_entry new_entry(RT_HERE,rel_class,offset); relocation_entry new_entry(RT_HERE, rel_class, offset);
instruction_operand op(new_entry,compiled,0); instruction_operand op(new_entry, compiled, 0);
op.store_value(target + (cell)compiled->entry_point()); op.store_value(target + (cell) compiled->entry_point());
} }
} }
/* Might GC */ /* Might GC */
/* Allocates memory */ /* Allocates memory */
code_block *factor_vm::allot_code_block(cell size, code_block_type type) code_block* factor_vm::allot_code_block(cell size, code_block_type type) {
{ code_block* block = code->allocator->allot(size + sizeof(code_block));
code_block *block = code->allocator->allot(size + sizeof(code_block));
/* If allocation failed, do a full GC and compact the code heap. /* If allocation failed, do a full GC and compact the code heap.
A full GC that occurs as a result of the data heap filling up does not A full GC that occurs as a result of the data heap filling up does not
trigger a compaction. This setup ensures that most GCs do not compact trigger a compaction. This setup ensures that most GCs do not compact
the code heap, but if the code fills up, it probably means it will be the code heap, but if the code fills up, it probably means it will be
fragmented after GC anyway, so its best to compact. */ fragmented after GC anyway, so its best to compact. */
if(block == NULL) if (block == NULL) {
{ primitive_compact_gc();
primitive_compact_gc(); block = code->allocator->allot(size + sizeof(code_block));
block = code->allocator->allot(size + sizeof(code_block));
/* Insufficient room even after code GC, give up */ /* Insufficient room even after code GC, give up */
if(block == NULL) if (block == NULL) {
{ std::cout << "Code heap used: " << code->allocator->occupied_space()
std::cout << "Code heap used: " << code->allocator->occupied_space() << "\n"; << "\n";
std::cout << "Code heap free: " << code->allocator->free_space() << "\n"; std::cout << "Code heap free: " << code->allocator->free_space() << "\n";
fatal_error("Out of memory in add-compiled-block",0); fatal_error("Out of memory in add-compiled-block", 0);
} }
} }
block->set_type(type); block->set_type(type);
return block; return block;
} }
/* Might GC */ /* Might GC */
/* Allocates memory */ /* Allocates memory */
code_block *factor_vm::add_code_block(code_block_type type, cell code_, cell labels_, code_block* factor_vm::add_code_block(code_block_type type, cell code_,
cell owner_, cell relocation_, cell parameters_, cell literals_, cell labels_, cell owner_,
cell frame_size_untagged) cell relocation_, cell parameters_,
{ cell literals_,
data_root<byte_array> code(code_,this); cell frame_size_untagged) {
data_root<object> labels(labels_,this); data_root<byte_array> code(code_, this);
data_root<object> owner(owner_,this); data_root<object> labels(labels_, this);
data_root<byte_array> relocation(relocation_,this); data_root<object> owner(owner_, this);
data_root<array> parameters(parameters_,this); data_root<byte_array> relocation(relocation_, this);
data_root<array> literals(literals_,this); data_root<array> parameters(parameters_, this);
data_root<array> literals(literals_, this);
cell code_length = array_capacity(code.untagged()); cell code_length = array_capacity(code.untagged());
code_block *compiled = allot_code_block(code_length,type); code_block* compiled = allot_code_block(code_length, type);
compiled->owner = owner.value(); compiled->owner = owner.value();
/* slight space optimization */ /* slight space optimization */
if(relocation.type() == BYTE_ARRAY_TYPE && array_capacity(relocation.untagged()) == 0) if (relocation.type() == BYTE_ARRAY_TYPE &&
compiled->relocation = false_object; array_capacity(relocation.untagged()) == 0)
else compiled->relocation = false_object;
compiled->relocation = relocation.value(); else
compiled->relocation = relocation.value();
if(parameters.type() == ARRAY_TYPE && array_capacity(parameters.untagged()) == 0) if (parameters.type() == ARRAY_TYPE &&
compiled->parameters = false_object; array_capacity(parameters.untagged()) == 0)
else compiled->parameters = false_object;
compiled->parameters = parameters.value(); else
compiled->parameters = parameters.value();
/* code */ /* code */
memcpy(compiled + 1,code.untagged() + 1,code_length); memcpy(compiled + 1, code.untagged() + 1, code_length);
/* fixup labels */ /* fixup labels */
if(to_boolean(labels.value())) if (to_boolean(labels.value()))
fixup_labels(labels.as<array>().untagged(),compiled); fixup_labels(labels.as<array>().untagged(), compiled);
compiled->set_stack_frame_size(frame_size_untagged); compiled->set_stack_frame_size(frame_size_untagged);
/* Once we are ready, fill in literal and word references in this code /* Once we are ready, fill in literal and word references in this code
block's instruction operands. In most cases this is done right after this block's instruction operands. In most cases this is done right after this
method returns, except when compiling words with the non-optimizing method returns, except when compiling words with the non-optimizing
compiler at the beginning of bootstrap */ compiler at the beginning of bootstrap */
this->code->uninitialized_blocks.insert(std::make_pair(compiled,literals.value())); this->code->uninitialized_blocks
this->code->all_blocks.insert((cell)compiled); .insert(std::make_pair(compiled, literals.value()));
this->code->all_blocks.insert((cell) compiled);
/* next time we do a minor GC, we have to trace this code block, since /* next time we do a minor GC, we have to trace this code block, since
the fields of the code_block struct might point into nursery or aging */ the fields of the code_block struct might point into nursery or aging */
this->code->write_barrier(compiled); this->code->write_barrier(compiled);
return compiled; return compiled;
} }
/* Find the RT_DLSYM relocation nearest to the given return address. */ /* Find the RT_DLSYM relocation nearest to the given return address. */
struct find_symbol_at_address_visitor { struct find_symbol_at_address_visitor {
factor_vm *parent; factor_vm* parent;
cell return_address; cell return_address;
cell symbol; cell symbol;
cell library; cell library;
find_symbol_at_address_visitor(factor_vm *parent_, cell return_address_) : find_symbol_at_address_visitor(factor_vm* parent_, cell return_address_)
parent(parent_), return_address(return_address_), : parent(parent_),
symbol(false_object), library(false_object) { } return_address(return_address_),
symbol(false_object),
library(false_object) {}
void operator()(instruction_operand op) void operator()(instruction_operand op) {
{ if (op.rel_type() == RT_DLSYM && op.pointer <= return_address) {
if(op.rel_type() == RT_DLSYM && op.pointer <= return_address) code_block* compiled = op.compiled;
{ array* parameters = untag<array>(compiled->parameters);
code_block *compiled = op.compiled; cell index = op.index;
array *parameters = untag<array>(compiled->parameters); symbol = array_nth(parameters, index);
cell index = op.index; library = array_nth(parameters, index + 1);
symbol = array_nth(parameters,index); }
library = array_nth(parameters,index + 1); }
}
}
}; };
/* References to undefined symbols are patched up to call this function on /* References to undefined symbols are patched up to call this function on
image load. It finds the symbol and library, and throws an error. */ image load. It finds the symbol and library, and throws an error. */
void factor_vm::undefined_symbol() void factor_vm::undefined_symbol() {
{ void* frame = ctx->callstack_top;
void *frame = ctx->callstack_top; void* return_address = frame_return_address(frame);
void *return_address = frame_return_address(frame); code_block* compiled = code->code_block_for_address((cell) return_address);
code_block *compiled = code->code_block_for_address((cell)return_address); find_symbol_at_address_visitor visitor(this, (cell) return_address);
find_symbol_at_address_visitor visitor(this, (cell)return_address); compiled->each_instruction_operand(visitor);
compiled->each_instruction_operand(visitor); if (!to_boolean(visitor.symbol))
if (!to_boolean(visitor.symbol)) critical_error("Can't find RT_DLSYM at return address",
critical_error("Can't find RT_DLSYM at return address", (cell)return_address); (cell) return_address);
else else
general_error(ERROR_UNDEFINED_SYMBOL,visitor.symbol,visitor.library); general_error(ERROR_UNDEFINED_SYMBOL, visitor.symbol, visitor.library);
} }
void undefined_symbol() void undefined_symbol() { return current_vm()->undefined_symbol(); }
{
return current_vm()->undefined_symbol();
}
} }

View File

@ -1,153 +1,117 @@
namespace factor namespace factor {
{
/* The compiled code heap is structured into blocks. */ /* The compiled code heap is structured into blocks. */
struct code_block struct code_block {
{ // header format (bits indexed with least significant as zero):
// header format (bits indexed with least significant as zero): // bit 0 : free?
// bit 0 : free? // bits 1-2: type (as a code_block_type)
// bits 1-2: type (as a code_block_type) // if not free:
// if not free: // bits 3-23: code size / 8
// bits 3-23: code size / 8 // bits 24-31: stack frame size / 16
// bits 24-31: stack frame size / 16 // if free:
// if free: // bits 3-end: code size / 8
// bits 3-end: code size / 8 cell header;
cell header; cell owner; /* tagged pointer to word, quotation or f */
cell owner; /* tagged pointer to word, quotation or f */ cell parameters; /* tagged pointer to array or f */
cell parameters; /* tagged pointer to array or f */ cell relocation; /* tagged pointer to byte-array or f */
cell relocation; /* tagged pointer to byte-array or f */
bool free_p() const bool free_p() const { return (header & 1) == 1; }
{
return (header & 1) == 1;
}
code_block_type type() const code_block_type type() const {
{ return (code_block_type)((header >> 1) & 0x3);
return (code_block_type)((header >> 1) & 0x3); }
}
void set_type(code_block_type type) void set_type(code_block_type type) {
{ header = ((header & ~0x7) | (type << 1));
header = ((header & ~0x7) | (type << 1)); }
}
bool pic_p() const bool pic_p() const { return type() == code_block_pic; }
{
return type() == code_block_pic;
}
bool optimized_p() const bool optimized_p() const { return type() == code_block_optimized; }
{
return type() == code_block_optimized;
}
cell size() const cell size() const {
{ cell size;
cell size; if (free_p())
if (free_p()) size = header & ~7;
size = header & ~7; else
else size = header & 0xFFFFF8;
size = header & 0xFFFFF8; FACTOR_ASSERT(size > 0);
FACTOR_ASSERT(size > 0); return size;
return size; }
}
cell stack_frame_size() const cell stack_frame_size() const {
{ if (free_p())
if (free_p()) return 0;
return 0; else
else return (header >> 20) & 0xFF0;
return (header >> 20) & 0xFF0; }
}
cell stack_frame_size_for_address(cell addr) const cell stack_frame_size_for_address(cell addr) const {
{ cell natural_frame_size = stack_frame_size();
cell natural_frame_size = stack_frame_size(); /* The first instruction in a code block is the prolog safepoint,
/* The first instruction in a code block is the prolog safepoint, and a leaf procedure code block will record a frame size of zero.
and a leaf procedure code block will record a frame size of zero. If we're seeing a stack frame in either of these cases, it's a
If we're seeing a stack frame in either of these cases, it's a fake "leaf frame" set up by the signal handler. */
fake "leaf frame" set up by the signal handler. */ if (natural_frame_size == 0 || (void*)addr == entry_point())
if (natural_frame_size == 0 || (void*)addr == entry_point()) return LEAF_FRAME_SIZE;
return LEAF_FRAME_SIZE; else
else return natural_frame_size;
return natural_frame_size; }
}
void set_stack_frame_size(cell frame_size) void set_stack_frame_size(cell frame_size) {
{ FACTOR_ASSERT(size() < 0xFFFFFF);
FACTOR_ASSERT(size() < 0xFFFFFF); FACTOR_ASSERT(!free_p());
FACTOR_ASSERT(!free_p()); FACTOR_ASSERT(frame_size % 16 == 0);
FACTOR_ASSERT(frame_size % 16 == 0); FACTOR_ASSERT(frame_size <= 0xFF0);
FACTOR_ASSERT(frame_size <= 0xFF0); header = (header & 0xFFFFFF) | (frame_size << 20);
header = (header & 0xFFFFFF) | (frame_size << 20); }
}
template<typename Fixup> cell size(Fixup fixup) const template <typename Fixup> cell size(Fixup fixup) const { return size(); }
{
return size();
}
void *entry_point() const void* entry_point() const { return (void*)(this + 1); }
{
return (void *)(this + 1);
}
/* GC info is stored at the end of the block */ /* GC info is stored at the end of the block */
gc_info *block_gc_info() const gc_info* block_gc_info() const {
{ return (gc_info*)((u8*)this + size() - sizeof(gc_info));
return (gc_info *)((u8 *)this + size() - sizeof(gc_info)); }
}
void flush_icache() void flush_icache() { factor::flush_icache((cell) this, size()); }
{
factor::flush_icache((cell)this,size());
}
template<typename Iterator> void each_instruction_operand(Iterator &iter) template <typename Iterator> void each_instruction_operand(Iterator& iter) {
{ if (to_boolean(relocation)) {
if(to_boolean(relocation)) byte_array* rels = (byte_array*)UNTAG(relocation);
{
byte_array *rels = (byte_array *)UNTAG(relocation);
cell index = 0; cell index = 0;
cell length = (rels->capacity >> TAG_BITS) / sizeof(relocation_entry); cell length = (rels->capacity >> TAG_BITS) / sizeof(relocation_entry);
for(cell i = 0; i < length; i++) for (cell i = 0; i < length; i++) {
{ relocation_entry rel = rels->data<relocation_entry>()[i];
relocation_entry rel = rels->data<relocation_entry>()[i]; iter(instruction_operand(rel, this, index));
iter(instruction_operand(rel,this,index)); index += rel.number_of_parameters();
index += rel.number_of_parameters(); }
} }
} }
}
cell offset(void *addr) const cell offset(void* addr) const { return (char*)addr - (char*)entry_point(); }
{
return (char*)addr - (char*)entry_point();
}
void *address_for_offset(cell offset) const void* address_for_offset(cell offset) const {
{ return (void*)((char*)entry_point() + offset);
return (void*)((char*)entry_point() + offset); }
}
cell scan(factor_vm *vm, void *addr) const; cell scan(factor_vm* vm, void* addr) const;
cell owner_quot() const; cell owner_quot() const;
}; };
VM_C_API void undefined_symbol(void); VM_C_API void undefined_symbol(void);
inline code_block *word::code() const { inline code_block* word::code() const {
FACTOR_ASSERT(entry_point != NULL); FACTOR_ASSERT(entry_point != NULL);
return (code_block*)entry_point - 1; return (code_block*)entry_point - 1;
} }
inline code_block *quotation::code() const { inline code_block* quotation::code() const {
FACTOR_ASSERT(entry_point != NULL); FACTOR_ASSERT(entry_point != NULL);
return (code_block*)entry_point - 1; return (code_block*)entry_point - 1;
} }
} }