379 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			379 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
| #include "master.h"
 | |
| 
 | |
| /* 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);
 | |
| }
 | |
| 
 | |
| #define CREF(array,i) ((CELL)(array) + CELLS * (i))
 | |
| 
 | |
| INLINE CELL get_literal(CELL literals_start, CELL num)
 | |
| {
 | |
| 	return get(CREF(literals_start,num));
 | |
| }
 | |
| 
 | |
| /* Look up an external library symbol referenced by a compiled code block */
 | |
| void *get_rel_symbol(F_REL *rel, CELL literals_start)
 | |
| {
 | |
| 	CELL arg = REL_ARGUMENT(rel);
 | |
| 	F_SYMBOL *symbol = alien_offset(get_literal(literals_start,arg));
 | |
| 	CELL library = get_literal(literals_start,arg + 1);
 | |
| 	F_DLL *dll = (library == F ? NULL : untag_dll(library));
 | |
| 
 | |
| 	if(dll != NULL && !dll->dll)
 | |
| 		return undefined_symbol;
 | |
| 
 | |
| 	if(!symbol)
 | |
| 		return undefined_symbol;
 | |
| 
 | |
| 	void *sym = ffi_dlsym(dll,symbol);
 | |
| 
 | |
| 	if(sym)
 | |
| 		return sym;
 | |
| 	else
 | |
| 		return undefined_symbol;
 | |
| }
 | |
| 
 | |
| /* Compute an address to store at a relocation */
 | |
| INLINE CELL compute_code_rel(F_REL *rel,
 | |
| 	CELL code_start, CELL literals_start)
 | |
| {
 | |
| 	switch(REL_TYPE(rel))
 | |
| 	{
 | |
| 	case RT_PRIMITIVE:
 | |
| 		return (CELL)primitives[REL_ARGUMENT(rel)];
 | |
| 	case RT_DLSYM:
 | |
| 		return (CELL)get_rel_symbol(rel,literals_start);
 | |
| 	case RT_LITERAL:
 | |
| 		return CREF(literals_start,REL_ARGUMENT(rel));
 | |
| 	case RT_XT:
 | |
| 		return (CELL)untag_word(get(CREF(literals_start,REL_ARGUMENT(rel))))->xt;
 | |
| 	case RT_HERE:
 | |
| 		return rel->offset + code_start;
 | |
| 	case RT_LABEL:
 | |
| 		return code_start + REL_ARGUMENT(rel);
 | |
| 	default:
 | |
| 		critical_error("Bad rel type",rel->type);
 | |
| 		return -1; /* Can't happen */
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Store a 32-bit value into a PowerPC LIS/ORI sequence */
 | |
| INLINE void reloc_set_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 reloc_set_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 apply_relocation(CELL class, CELL offset, F_FIXNUM absolute_value)
 | |
| {
 | |
| 	F_FIXNUM relative_value = absolute_value - offset;
 | |
| 
 | |
| 	switch(class)
 | |
| 	{
 | |
| 	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:
 | |
| 		reloc_set_2_2(offset,absolute_value);
 | |
| 		break;
 | |
| 	case RC_RELATIVE_PPC_2:
 | |
| 		reloc_set_masked(offset,relative_value,REL_RELATIVE_PPC_2_MASK,0);
 | |
| 		break;
 | |
| 	case RC_RELATIVE_PPC_3:
 | |
| 		reloc_set_masked(offset,relative_value,REL_RELATIVE_PPC_3_MASK,0);
 | |
| 		break;
 | |
| 	case RC_RELATIVE_ARM_3:
 | |
| 		reloc_set_masked(offset,relative_value - CELLS * 2,
 | |
| 			REL_RELATIVE_ARM_3_MASK,2);
 | |
| 		break;
 | |
| 	case RC_INDIRECT_ARM:
 | |
| 		reloc_set_masked(offset,relative_value - CELLS,
 | |
| 			REL_INDIRECT_ARM_MASK,0);
 | |
| 		break;
 | |
| 	case RC_INDIRECT_ARM_PC:
 | |
| 		reloc_set_masked(offset,relative_value - CELLS * 2,
 | |
| 			REL_INDIRECT_ARM_MASK,0);
 | |
| 		break;
 | |
| 	default:
 | |
| 		critical_error("Bad rel class",class);
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Perform all fixups on a code block */
 | |
| void relocate_code_block(F_COMPILED *relocating, CELL code_start,
 | |
| 	CELL reloc_start, CELL literals_start)
 | |
| {
 | |
| 	if(reloc_start != literals_start)
 | |
| 	{
 | |
| 		F_REL *rel = (F_REL *)reloc_start;
 | |
| 		F_REL *rel_end = (F_REL *)literals_start;
 | |
| 
 | |
| 		while(rel < rel_end)
 | |
| 		{
 | |
| 			CELL offset = rel->offset + code_start;
 | |
| 
 | |
| 			F_FIXNUM absolute_value = compute_code_rel(
 | |
| 				rel,code_start,literals_start);
 | |
| 
 | |
| 			apply_relocation(REL_CLASS(rel),offset,absolute_value);
 | |
| 
 | |
| 			rel++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	flush_icache(code_start,reloc_start - code_start);
 | |
| }
 | |
| 
 | |
| /* Fixup labels. This is done at compile time, not image load time */
 | |
| void fixup_labels(F_ARRAY *labels, CELL code_format, CELL code_start)
 | |
| {
 | |
| 	CELL i;
 | |
| 	CELL size = array_capacity(labels);
 | |
| 
 | |
| 	for(i = 0; i < size; i += 3)
 | |
| 	{
 | |
| 		CELL class = to_fixnum(array_nth(labels,i));
 | |
| 		CELL offset = to_fixnum(array_nth(labels,i + 1));
 | |
| 		CELL target = to_fixnum(array_nth(labels,i + 2));
 | |
| 
 | |
| 		apply_relocation(class,
 | |
| 			offset + code_start,
 | |
| 			target + code_start);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Write a sequence of integers to memory, with 'format' bytes per integer */
 | |
| void deposit_integers(CELL here, F_ARRAY *array, CELL format)
 | |
| {
 | |
| 	CELL count = array_capacity(array);
 | |
| 	CELL i;
 | |
| 
 | |
| 	for(i = 0; i < count; i++)
 | |
| 	{
 | |
| 		F_FIXNUM value = to_fixnum(array_nth(array,i));
 | |
| 		if(format == 1)
 | |
| 			cput(here + i,value);
 | |
| 		else if(format == sizeof(unsigned int))
 | |
| 			*(unsigned int *)(here + format * i) = value;
 | |
| 		else if(format == CELLS)
 | |
| 			put(CREF(here,i),value);
 | |
| 		else
 | |
| 			critical_error("Bad format in deposit_integers()",format);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Write a sequence of tagged pointers to memory */
 | |
| void deposit_objects(CELL here, F_ARRAY *array)
 | |
| {
 | |
| 	memcpy((void*)here,array + 1,array_capacity(array) * CELLS);
 | |
| }
 | |
| 
 | |
| bool stack_traces_p(void)
 | |
| {
 | |
| 	return to_boolean(userenv[STACK_TRACES_ENV]);
 | |
| }
 | |
| 
 | |
| CELL compiled_code_format(void)
 | |
| {
 | |
| 	return untag_fixnum_fast(userenv[JIT_CODE_FORMAT]);
 | |
| }
 | |
| 
 | |
| CELL allot_code_block(CELL size)
 | |
| {
 | |
| 	CELL start = heap_allot(&code_heap,size);
 | |
| 
 | |
| 	/* If allocation failed, do a code GC */
 | |
| 	if(start == 0)
 | |
| 	{
 | |
| 		code_gc();
 | |
| 		start = heap_allot(&code_heap,size);
 | |
| 
 | |
| 		/* Insufficient room even after code GC, give up */
 | |
| 		if(start == 0)
 | |
| 			critical_error("Out of memory in add-compiled-block",0);
 | |
| 	}
 | |
| 
 | |
| 	return start;
 | |
| }
 | |
| 
 | |
| /* Might GC */
 | |
| F_COMPILED *add_compiled_block(
 | |
| 	CELL type,
 | |
| 	F_ARRAY *code,
 | |
| 	F_ARRAY *labels,
 | |
| 	F_ARRAY *relocation,
 | |
| 	F_ARRAY *literals)
 | |
| {
 | |
| 	CELL code_format = compiled_code_format();
 | |
| 
 | |
| 	CELL code_length = align8(array_capacity(code) * code_format);
 | |
| 	CELL rel_length = array_capacity(relocation) * sizeof(unsigned int);
 | |
| 	CELL literals_length = array_capacity(literals) * CELLS;
 | |
| 
 | |
| 	REGISTER_UNTAGGED(code);
 | |
| 	REGISTER_UNTAGGED(labels);
 | |
| 	REGISTER_UNTAGGED(relocation);
 | |
| 	REGISTER_UNTAGGED(literals);
 | |
| 
 | |
| 	CELL here = allot_code_block(sizeof(F_COMPILED) + code_length
 | |
| 		+ rel_length + literals_length);
 | |
| 
 | |
| 	UNREGISTER_UNTAGGED(literals);
 | |
| 	UNREGISTER_UNTAGGED(relocation);
 | |
| 	UNREGISTER_UNTAGGED(labels);
 | |
| 	UNREGISTER_UNTAGGED(code);
 | |
| 
 | |
| 	/* compiled header */
 | |
| 	F_COMPILED *header = (void *)here;
 | |
| 	header->type = type;
 | |
| 	header->code_length = code_length;
 | |
| 	header->reloc_length = rel_length;
 | |
| 	header->literals_length = literals_length;
 | |
| 
 | |
| 	here += sizeof(F_COMPILED);
 | |
| 
 | |
| 	CELL code_start = here;
 | |
| 
 | |
| 	/* code */
 | |
| 	deposit_integers(here,code,code_format);
 | |
| 	here += code_length;
 | |
| 
 | |
| 	/* relation info */
 | |
| 	deposit_integers(here,relocation,sizeof(unsigned int));
 | |
| 	here += rel_length;
 | |
| 
 | |
| 	/* literals */
 | |
| 	deposit_objects(here,literals);
 | |
| 	here += literals_length;
 | |
| 
 | |
| 	/* fixup labels */
 | |
| 	if(labels)
 | |
| 		fixup_labels(labels,code_format,code_start);
 | |
| 
 | |
| 	/* next time we do a minor GC, we have to scan the code heap for
 | |
| 	literals */
 | |
| 	last_code_heap_scan = NURSERY;
 | |
| 
 | |
| 	return header;
 | |
| }
 | |
| 
 | |
| void set_word_code(F_WORD *word, F_COMPILED *compiled)
 | |
| {
 | |
| 	if(compiled->type != WORD_TYPE)
 | |
| 		critical_error("bad param to set_word_xt",(CELL)compiled);
 | |
| 
 | |
| 	word->code = compiled;
 | |
| 	word->compiledp = T;
 | |
| }
 | |
| 
 | |
| /* Allocates memory */
 | |
| void default_word_code(F_WORD *word, bool relocate)
 | |
| {
 | |
| 	REGISTER_UNTAGGED(word);
 | |
| 	jit_compile(word->def,relocate);
 | |
| 	UNREGISTER_UNTAGGED(word);
 | |
| 
 | |
| 	word->code = untag_quotation(word->def)->code;
 | |
| 	word->compiledp = F;
 | |
| }
 | |
| 
 | |
| DEFINE_PRIMITIVE(modify_code_heap)
 | |
| {
 | |
| 	F_ARRAY *alist = untag_array(dpop());
 | |
| 
 | |
| 	bool rescan_code_heap = false;
 | |
| 
 | |
| 	CELL count = untag_fixnum_fast(alist->capacity);
 | |
| 	CELL i;
 | |
| 	for(i = 0; i < count; i++)
 | |
| 	{
 | |
| 		F_ARRAY *pair = untag_array(array_nth(alist,i));
 | |
| 
 | |
| 		F_WORD *word = untag_word(array_nth(pair,0));
 | |
| 
 | |
| 		if(word->vocabulary != F)
 | |
| 			rescan_code_heap = true;
 | |
| 
 | |
| 		CELL data = array_nth(pair,1);
 | |
| 
 | |
| 		if(data == F)
 | |
| 		{
 | |
| 			REGISTER_UNTAGGED(alist);
 | |
| 			REGISTER_UNTAGGED(word);
 | |
| 			default_word_code(word,false);
 | |
| 			UNREGISTER_UNTAGGED(word);
 | |
| 			UNREGISTER_UNTAGGED(alist);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			F_ARRAY *compiled_code = untag_array(data);
 | |
| 
 | |
| 			F_ARRAY *literals = untag_array(array_nth(compiled_code,0));
 | |
| 			F_ARRAY *relocation = untag_array(array_nth(compiled_code,1));
 | |
| 			F_ARRAY *labels = untag_array(array_nth(compiled_code,2));
 | |
| 			F_ARRAY *code = untag_array(array_nth(compiled_code,3));
 | |
| 
 | |
| 			REGISTER_UNTAGGED(alist);
 | |
| 			REGISTER_UNTAGGED(word);
 | |
| 
 | |
| 			F_COMPILED *compiled = add_compiled_block(
 | |
| 				WORD_TYPE,
 | |
| 				code,
 | |
| 				labels,
 | |
| 				relocation,
 | |
| 				literals);
 | |
| 
 | |
| 			UNREGISTER_UNTAGGED(word);
 | |
| 			UNREGISTER_UNTAGGED(alist);
 | |
| 
 | |
| 			set_word_code(word,compiled);
 | |
| 		}
 | |
| 
 | |
| 		REGISTER_UNTAGGED(alist);
 | |
| 		update_word_xt(word);
 | |
| 		UNREGISTER_UNTAGGED(alist);
 | |
| 	}
 | |
| 
 | |
| 	/* If there were any interned words in the set, we relocate all XT
 | |
| 	references in the entire code heap. But if all the words are
 | |
| 	uninterned, it is impossible that other words reference them, so we
 | |
| 	only have to relocate the new words. This makes compile-call much
 | |
| 	more efficient */
 | |
| 	if(rescan_code_heap)
 | |
| 		iterate_code_heap(relocate_code_block);
 | |
| 	else
 | |
| 	{
 | |
| 		for(i = 0; i < count; i++)
 | |
| 		{
 | |
| 			F_ARRAY *pair = untag_array(array_nth(alist,i));
 | |
| 			F_WORD *word = untag_word(array_nth(pair,0));
 | |
| 
 | |
| 			iterate_code_heap_step(word->code,relocate_code_block);
 | |
| 		}
 | |
| 	}
 | |
| }
 |