factor/vm/code_block.cpp

491 lines
12 KiB
C++

#include "master.hpp"
void flush_icache_for(F_CODE_BLOCK *block)
{
flush_icache((CELL)block,block->block.size);
}
void iterate_relocations(F_CODE_BLOCK *compiled, RELOCATION_ITERATOR iter)
{
if(compiled->relocation != F)
{
F_BYTE_ARRAY *relocation = untag<F_BYTE_ARRAY>(compiled->relocation);
CELL index = stack_traces_p() ? 1 : 0;
F_REL *rel = (F_REL *)(relocation + 1);
F_REL *rel_end = (F_REL *)((char *)rel + array_capacity(relocation));
while(rel < rel_end)
{
iter(*rel,index,compiled);
switch(REL_TYPE(*rel))
{
case RT_PRIMITIVE:
case RT_XT:
case RT_XT_DIRECT:
case RT_IMMEDIATE:
case RT_HERE:
case RT_UNTAGGED:
index++;
break;
case RT_DLSYM:
index += 2;
break;
case RT_THIS:
case RT_STACK_CHAIN:
break;
default:
critical_error("Bad rel type",*rel);
return; /* Can't happen */
}
rel++;
}
}
}
/* Store a 32-bit value into a PowerPC LIS/ORI sequence */
INLINE void store_address_2_2(CELL cell, CELL value)
{
put(cell - CELLS,((get(cell - CELLS) & ~0xffff) | ((value >> 16) & 0xffff)));
put(cell,((get(cell) & ~0xffff) | (value & 0xffff)));
}
/* Store a value into a bitfield of a PowerPC instruction */
INLINE void store_address_masked(CELL cell, F_FIXNUM value, CELL mask, F_FIXNUM shift)
{
/* This is unaccurate but good enough */
F_FIXNUM test = (F_FIXNUM)mask >> 1;
if(value <= -test || value >= test)
critical_error("Value does not fit inside relocation",0);
u32 original = *(u32*)cell;
original &= ~mask;
*(u32*)cell = (original | ((value >> shift) & mask));
}
/* Perform a fixup on a code block */
void store_address_in_code_block(CELL klass, CELL offset, F_FIXNUM absolute_value)
{
F_FIXNUM relative_value = absolute_value - offset;
switch(klass)
{
case RC_ABSOLUTE_CELL:
put(offset,absolute_value);
break;
case RC_ABSOLUTE:
*(u32*)offset = absolute_value;
break;
case RC_RELATIVE:
*(u32*)offset = relative_value - sizeof(u32);
break;
case RC_ABSOLUTE_PPC_2_2:
store_address_2_2(offset,absolute_value);
break;
case RC_RELATIVE_PPC_2:
store_address_masked(offset,relative_value,REL_RELATIVE_PPC_2_MASK,0);
break;
case RC_RELATIVE_PPC_3:
store_address_masked(offset,relative_value,REL_RELATIVE_PPC_3_MASK,0);
break;
case RC_RELATIVE_ARM_3:
store_address_masked(offset,relative_value - CELLS * 2,
REL_RELATIVE_ARM_3_MASK,2);
break;
case RC_INDIRECT_ARM:
store_address_masked(offset,relative_value - CELLS,
REL_INDIRECT_ARM_MASK,0);
break;
case RC_INDIRECT_ARM_PC:
store_address_masked(offset,relative_value - CELLS * 2,
REL_INDIRECT_ARM_MASK,0);
break;
default:
critical_error("Bad rel class",klass);
break;
}
}
void update_literal_references_step(F_REL rel, CELL index, F_CODE_BLOCK *compiled)
{
if(REL_TYPE(rel) == RT_IMMEDIATE)
{
CELL offset = REL_OFFSET(rel) + (CELL)(compiled + 1);
F_ARRAY *literals = untag<F_ARRAY>(compiled->literals);
F_FIXNUM absolute_value = array_nth(literals,index);
store_address_in_code_block(REL_CLASS(rel),offset,absolute_value);
}
}
/* Update pointers to literals from compiled code. */
void update_literal_references(F_CODE_BLOCK *compiled)
{
iterate_relocations(compiled,update_literal_references_step);
flush_icache_for(compiled);
}
/* Copy all literals referenced from a code block to newspace. Only for
aging and nursery collections */
void copy_literal_references(F_CODE_BLOCK *compiled)
{
if(collecting_gen >= compiled->block.last_scan)
{
if(collecting_accumulation_gen_p())
compiled->block.last_scan = collecting_gen;
else
compiled->block.last_scan = collecting_gen + 1;
/* initialize chase pointer */
CELL scan = newspace->here;
copy_handle(&compiled->literals);
copy_handle(&compiled->relocation);
/* do some tracing so that all reachable literals are now
at their final address */
copy_reachable_objects(scan,&newspace->here);
update_literal_references(compiled);
}
}
CELL object_xt(CELL obj)
{
if(TAG(obj) == QUOTATION_TYPE)
{
F_QUOTATION *quot = untag<F_QUOTATION>(obj);
return (CELL)quot->xt;
}
else
{
F_WORD *word = untag<F_WORD>(obj);
return (CELL)word->xt;
}
}
CELL word_direct_xt(CELL obj)
{
F_WORD *word = untag<F_WORD>(obj);
CELL quot = word->direct_entry_def;
if(quot == F || max_pic_size == 0)
return (CELL)word->xt;
else
{
F_QUOTATION *untagged = untag<F_QUOTATION>(quot);
if(untagged->compiledp == F)
return (CELL)word->xt;
else
return (CELL)untagged->xt;
}
}
void update_word_references_step(F_REL rel, CELL index, F_CODE_BLOCK *compiled)
{
F_RELTYPE type = REL_TYPE(rel);
if(type == RT_XT || type == RT_XT_DIRECT)
{
CELL offset = REL_OFFSET(rel) + (CELL)(compiled + 1);
F_ARRAY *literals = untag<F_ARRAY>(compiled->literals);
CELL obj = array_nth(literals,index);
CELL xt;
if(type == RT_XT)
xt = object_xt(obj);
else
xt = word_direct_xt(obj);
store_address_in_code_block(REL_CLASS(rel),offset,xt);
}
}
/* Relocate new code blocks completely; updating references to literals,
dlsyms, and words. For all other words in the code heap, we only need
to update references to other words, without worrying about literals
or dlsyms. */
void update_word_references(F_CODE_BLOCK *compiled)
{
if(compiled->block.needs_fixup)
relocate_code_block(compiled);
/* update_word_references() is always applied to every block in
the code heap. Since it resets all call sites to point to
their canonical XT (cold entry point for non-tail calls,
standard entry point for tail calls), it means that no PICs
are referenced after this is done. So instead of polluting
the code heap with dead PICs that will be freed on the next
GC, we add them to the free list immediately. */
else if(compiled->block.type == PIC_TYPE)
{
fflush(stdout);
heap_free(&code_heap,&compiled->block);
}
else
{
iterate_relocations(compiled,update_word_references_step);
flush_icache_for(compiled);
}
}
void update_literal_and_word_references(F_CODE_BLOCK *compiled)
{
update_literal_references(compiled);
update_word_references(compiled);
}
INLINE void check_code_address(CELL address)
{
#ifdef FACTOR_DEBUG
assert(address >= code_heap.segment->start && address < code_heap.segment->end);
#endif
}
/* Update references to words. This is done after a new code block
is added to the heap. */
/* Mark all literals referenced from a word XT. Only for tenured
collections */
void mark_code_block(F_CODE_BLOCK *compiled)
{
check_code_address((CELL)compiled);
mark_block(&compiled->block);
copy_handle(&compiled->literals);
copy_handle(&compiled->relocation);
}
void mark_stack_frame_step(F_STACK_FRAME *frame)
{
mark_code_block(frame_code(frame));
}
/* Mark code blocks executing in currently active stack frames. */
void mark_active_blocks(F_CONTEXT *stacks)
{
if(collecting_gen == TENURED)
{
CELL top = (CELL)stacks->callstack_top;
CELL bottom = (CELL)stacks->callstack_bottom;
iterate_callstack(top,bottom,mark_stack_frame_step);
}
}
void mark_object_code_block(CELL scan)
{
F_WORD *word;
F_QUOTATION *quot;
F_CALLSTACK *stack;
switch(hi_tag(scan))
{
case WORD_TYPE:
word = (F_WORD *)scan;
if(word->code)
mark_code_block(word->code);
if(word->profiling)
mark_code_block(word->profiling);
break;
case QUOTATION_TYPE:
quot = (F_QUOTATION *)scan;
if(quot->compiledp != F)
mark_code_block(quot->code);
break;
case CALLSTACK_TYPE:
stack = (F_CALLSTACK *)scan;
iterate_callstack_object(stack,mark_stack_frame_step);
break;
}
}
/* References to undefined symbols are patched up to call this function on
image load */
void undefined_symbol(void)
{
general_error(ERROR_UNDEFINED_SYMBOL,F,F,NULL);
}
/* Look up an external library symbol referenced by a compiled code block */
void *get_rel_symbol(F_ARRAY *literals, CELL index)
{
CELL symbol = array_nth(literals,index);
CELL library = array_nth(literals,index + 1);
F_DLL *dll = (library == F ? NULL : untag<F_DLL>(library));
if(dll != NULL && !dll->dll)
return (void *)undefined_symbol;
if(type_of(symbol) == BYTE_ARRAY_TYPE)
{
F_SYMBOL *name = alien_offset(symbol);
void *sym = ffi_dlsym(dll,name);
if(sym)
return sym;
}
else if(type_of(symbol) == ARRAY_TYPE)
{
CELL i;
F_ARRAY *names = untag<F_ARRAY>(symbol);
for(i = 0; i < array_capacity(names); i++)
{
F_SYMBOL *name = alien_offset(array_nth(names,i));
void *sym = ffi_dlsym(dll,name);
if(sym)
return sym;
}
}
return (void *)undefined_symbol;
}
/* Compute an address to store at a relocation */
void relocate_code_block_step(F_REL rel, CELL index, F_CODE_BLOCK *compiled)
{
#ifdef FACTOR_DEBUG
type_check(ARRAY_TYPE,compiled->literals);
type_check(BYTE_ARRAY_TYPE,compiled->relocation);
#endif
CELL offset = REL_OFFSET(rel) + (CELL)(compiled + 1);
F_ARRAY *literals = untag<F_ARRAY>(compiled->literals);
F_FIXNUM absolute_value;
switch(REL_TYPE(rel))
{
case RT_PRIMITIVE:
absolute_value = (CELL)primitives[to_fixnum(array_nth(literals,index))];
break;
case RT_DLSYM:
absolute_value = (CELL)get_rel_symbol(literals,index);
break;
case RT_IMMEDIATE:
absolute_value = array_nth(literals,index);
break;
case RT_XT:
absolute_value = object_xt(array_nth(literals,index));
break;
case RT_XT_DIRECT:
absolute_value = word_direct_xt(array_nth(literals,index));
break;
case RT_HERE:
absolute_value = offset + (short)to_fixnum(array_nth(literals,index));
break;
case RT_THIS:
absolute_value = (CELL)(compiled + 1);
break;
case RT_STACK_CHAIN:
absolute_value = (CELL)&stack_chain;
break;
case RT_UNTAGGED:
absolute_value = to_fixnum(array_nth(literals,index));
break;
default:
critical_error("Bad rel type",rel);
return; /* Can't happen */
}
store_address_in_code_block(REL_CLASS(rel),offset,absolute_value);
}
/* Perform all fixups on a code block */
void relocate_code_block(F_CODE_BLOCK *compiled)
{
compiled->block.last_scan = NURSERY;
compiled->block.needs_fixup = false;
iterate_relocations(compiled,relocate_code_block_step);
flush_icache_for(compiled);
}
/* Fixup labels. This is done at compile time, not image load time */
void fixup_labels(F_ARRAY *labels, F_CODE_BLOCK *compiled)
{
CELL i;
CELL size = array_capacity(labels);
for(i = 0; i < size; i += 3)
{
CELL klass = to_fixnum(array_nth(labels,i));
CELL offset = to_fixnum(array_nth(labels,i + 1));
CELL target = to_fixnum(array_nth(labels,i + 2));
store_address_in_code_block(klass,
offset + (CELL)(compiled + 1),
target + (CELL)(compiled + 1));
}
}
/* Might GC */
F_CODE_BLOCK *allot_code_block(CELL size)
{
F_BLOCK *block = heap_allot(&code_heap,size + sizeof(F_CODE_BLOCK));
/* If allocation failed, do a code GC */
if(block == NULL)
{
gc();
block = heap_allot(&code_heap,size + sizeof(F_CODE_BLOCK));
/* Insufficient room even after code GC, give up */
if(block == NULL)
{
CELL used, total_free, max_free;
heap_usage(&code_heap,&used,&total_free,&max_free);
print_string("Code heap stats:\n");
print_string("Used: "); print_cell(used); nl();
print_string("Total free space: "); print_cell(total_free); nl();
print_string("Largest free block: "); print_cell(max_free); nl();
fatal_error("Out of memory in add-compiled-block",0);
}
}
return (F_CODE_BLOCK *)block;
}
/* Might GC */
F_CODE_BLOCK *add_code_block(
CELL type,
CELL code_,
CELL labels_,
CELL relocation_,
CELL literals_)
{
gc_root<F_BYTE_ARRAY> code(code_);
gc_root<F_OBJECT> labels(labels_);
gc_root<F_BYTE_ARRAY> relocation(relocation_);
gc_root<F_ARRAY> literals(literals_);
CELL code_length = align8(array_capacity(code.untagged()));
F_CODE_BLOCK *compiled = allot_code_block(code_length);
/* compiled header */
compiled->block.type = type;
compiled->block.last_scan = NURSERY;
compiled->block.needs_fixup = true;
compiled->relocation = relocation.value();
/* slight space optimization */
if(literals.type() == ARRAY_TYPE && array_capacity(literals.untagged()) == 0)
compiled->literals = F;
else
compiled->literals = literals.value();
/* code */
memcpy(compiled + 1,code.untagged() + 1,code_length);
/* fixup labels */
if(labels.value() != F)
fixup_labels(labels.as<F_ARRAY>().untagged(),compiled);
/* next time we do a minor GC, we have to scan the code heap for
literals */
last_code_heap_scan = NURSERY;
return compiled;
}