360 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
		
		
			
		
	
	
			360 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
|  | #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, CELL words_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_DISPATCH: | ||
|  | 		return CREF(words_start,REL_ARGUMENT(rel)); | ||
|  | 	case RT_XT: | ||
|  | 		return get(CREF(words_start,REL_ARGUMENT(rel))); | ||
|  | 	case RT_LABEL: | ||
|  | 		return code_start + REL_ARGUMENT(rel); | ||
|  | 	default: | ||
|  | 		critical_error("Bad rel type",rel->type); | ||
|  | 		return -1; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | /* 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) | ||
|  | { | ||
|  | 	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, CELL words_start, CELL words_end) | ||
|  | { | ||
|  | 	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,words_start); | ||
|  | 
 | ||
|  | 		apply_relocation(REL_CLASS(rel),offset,absolute_value); | ||
|  | 
 | ||
|  | 		rel++; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | /* 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); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | /* After compiling a batch of words, we replace all mutual word references with
 | ||
|  | direct XT references, and perform fixups */ | ||
|  | void finalize_code_block(F_COMPILED *relocating, CELL code_start, | ||
|  | 	CELL reloc_start, CELL literals_start, CELL words_start, CELL words_end) | ||
|  | { | ||
|  | 	CELL scan; | ||
|  | 
 | ||
|  | 	if(relocating->finalized != false) | ||
|  | 		critical_error("Finalizing a finalized block",(CELL)relocating); | ||
|  | 
 | ||
|  | 	for(scan = words_start; scan < words_end; scan += CELLS) | ||
|  | 		put(scan,(CELL)(untag_word(get(scan))->xt)); | ||
|  | 
 | ||
|  | 	relocating->finalized = true; | ||
|  | 
 | ||
|  | 	relocate_code_block(relocating,code_start,reloc_start, | ||
|  | 		literals_start,words_start,words_end); | ||
|  | 
 | ||
|  | 	flush_icache(code_start,reloc_start - 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); | ||
|  | } | ||
|  | 
 | ||
|  | 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; | ||
|  | } | ||
|  | 
 | ||
|  | XT add_compiled_block( | ||
|  | 	CELL type, | ||
|  | 	F_ARRAY *code, | ||
|  | 	F_ARRAY *labels, | ||
|  | 	F_ARRAY *rel, | ||
|  | 	F_ARRAY *words, | ||
|  | 	F_ARRAY *literals) | ||
|  | { | ||
|  | 	CELL code_format = compiled_code_format(); | ||
|  | 
 | ||
|  | 	CELL code_length = align8(array_capacity(code) * code_format); | ||
|  | 	CELL rel_length = (rel ? array_capacity(rel) * sizeof(unsigned int) : 0); | ||
|  | 	CELL words_length = (words ? array_capacity(words) * CELLS : 0); | ||
|  | 	CELL literals_length = (literals ? array_capacity(literals) * CELLS : 0); | ||
|  | 
 | ||
|  | 	REGISTER_UNTAGGED(code); | ||
|  | 	REGISTER_UNTAGGED(labels); | ||
|  | 	REGISTER_UNTAGGED(rel); | ||
|  | 	REGISTER_UNTAGGED(words); | ||
|  | 	REGISTER_UNTAGGED(literals); | ||
|  | 
 | ||
|  | 	CELL start = allot_code_block(sizeof(F_COMPILED) + code_length | ||
|  | 		+ rel_length + literals_length + words_length); | ||
|  | 
 | ||
|  | 	UNREGISTER_UNTAGGED(literals); | ||
|  | 	UNREGISTER_UNTAGGED(words); | ||
|  | 	UNREGISTER_UNTAGGED(rel); | ||
|  | 	UNREGISTER_UNTAGGED(labels); | ||
|  | 	UNREGISTER_UNTAGGED(code); | ||
|  | 
 | ||
|  | 	/* begin depositing the code block's contents */ | ||
|  | 	CELL here = start; | ||
|  | 
 | ||
|  | 	/* 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; | ||
|  | 	header->words_length = words_length; | ||
|  | 	header->finalized = false; | ||
|  | 
 | ||
|  | 	here += sizeof(F_COMPILED); | ||
|  | 
 | ||
|  | 	/* code */ | ||
|  | 	deposit_integers(here,code,code_format); | ||
|  | 	here += code_length; | ||
|  | 
 | ||
|  | 	/* relation info */ | ||
|  | 	if(rel) | ||
|  | 	{ | ||
|  | 		deposit_integers(here,rel,sizeof(unsigned int)); | ||
|  | 		here += rel_length; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* literals */ | ||
|  | 	if(literals) | ||
|  | 	{ | ||
|  | 		deposit_objects(here,literals); | ||
|  | 		here += literals_length; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* words */ | ||
|  | 	if(words) | ||
|  | 	{ | ||
|  | 		deposit_objects(here,words); | ||
|  | 		here += words_length; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* compute the XT */ | ||
|  | 	XT xt = (XT)(start + sizeof(F_COMPILED)); | ||
|  | 
 | ||
|  | 	/* fixup labels */ | ||
|  | 	if(labels) | ||
|  | 		fixup_labels(labels,code_format,(CELL)xt); | ||
|  | 
 | ||
|  | 	/* next time we do a minor GC, we have to scan the code heap for
 | ||
|  | 	literals */ | ||
|  | 	last_code_heap_scan = NURSERY; | ||
|  | 
 | ||
|  | 	return xt; | ||
|  | } | ||
|  | 
 | ||
|  | DEFINE_PRIMITIVE(add_compiled_block) | ||
|  | { | ||
|  | 	F_ARRAY *code = untag_array(dpop()); | ||
|  | 	F_ARRAY *labels = untag_array(dpop()); | ||
|  | 	F_ARRAY *rel = untag_array(dpop()); | ||
|  | 	F_ARRAY *words = untag_array(dpop()); | ||
|  | 	F_ARRAY *literals = untag_array(dpop()); | ||
|  | 
 | ||
|  | 	XT xt = add_compiled_block(WORD_TYPE,code,labels,rel,words,literals); | ||
|  | 
 | ||
|  | 	/* push the XT of the new word on the stack */ | ||
|  | 	F_WORD *word = allot_word(F,F); | ||
|  | 	word->xt = xt; | ||
|  | 	word->compiledp = T; | ||
|  | 	dpush(tag_object(word)); | ||
|  | } | ||
|  | 
 | ||
|  | /* After batch compiling a bunch of words, perform various fixups to make them
 | ||
|  | executable */ | ||
|  | DEFINE_PRIMITIVE(finalize_compile) | ||
|  | { | ||
|  | 	F_ARRAY *array = untag_array(dpop()); | ||
|  | 
 | ||
|  | 	/* set word XT's */ | ||
|  | 	CELL count = untag_fixnum_fast(array->capacity); | ||
|  | 	CELL i; | ||
|  | 	for(i = 0; i < count; i++) | ||
|  | 	{ | ||
|  | 		F_ARRAY *pair = untag_array(array_nth(array,i)); | ||
|  | 		F_WORD *word = untag_word(array_nth(pair,0)); | ||
|  | 		XT xt = untag_word(array_nth(pair,1))->xt; | ||
|  | 		F_BLOCK *block = xt_to_block(xt); | ||
|  | 		if(block->status != B_ALLOCATED) | ||
|  | 			critical_error("bad XT",(CELL)xt); | ||
|  | 
 | ||
|  | 		word->xt = xt; | ||
|  | 		word->compiledp = T; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* perform relocation */ | ||
|  | 	for(i = 0; i < count; i++) | ||
|  | 	{ | ||
|  | 		F_ARRAY *pair = untag_array(array_nth(array,i)); | ||
|  | 		F_WORD *word = untag_word(array_nth(pair,0)); | ||
|  | 		XT xt = word->xt; | ||
|  | 		iterate_code_heap_step(xt_to_compiled(xt),finalize_code_block); | ||
|  | 	} | ||
|  | } |