431 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			431 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
#include "master.h"
 | 
						|
 | 
						|
/* Simple non-optimizing compiler.
 | 
						|
 | 
						|
This is one of the two compilers implementing Factor; the second one is written
 | 
						|
in Factor and performs advanced optimizations. See core/compiler/compiler.factor.
 | 
						|
 | 
						|
The non-optimizing compiler compiles a quotation at a time by concatenating
 | 
						|
machine code chunks; prolog, epilog, call word, jump to word, etc. These machine
 | 
						|
code chunks are generated from Factor code in core/cpu/.../bootstrap.factor.
 | 
						|
 | 
						|
It actually does do a little bit of very simple optimization:
 | 
						|
 | 
						|
1) Tail call optimization.
 | 
						|
 | 
						|
2) If a quotation is determined to not call any other words (except for a few
 | 
						|
special words which are open-coded, see below), then no prolog/epilog is
 | 
						|
generated.
 | 
						|
 | 
						|
3) When in tail position and immediately preceded by literal arguments, the
 | 
						|
'if' and 'dispatch' conditionals are generated inline, instead of as a call to
 | 
						|
the 'if' word.
 | 
						|
 | 
						|
4) When preceded by an array, calls to the 'declare' word are optimized out
 | 
						|
entirely. This word is only used by the optimizing compiler, and with the
 | 
						|
non-optimizing compiler it would otherwise just decrease performance to have to
 | 
						|
push the array and immediately drop it after.
 | 
						|
 | 
						|
5) Sub-primitives are primitive words which are implemented in assembly and not
 | 
						|
in the VM. They are open-coded and no subroutine call is generated. This
 | 
						|
includes stack shufflers, some fixnum arithmetic words, and words such as tag,
 | 
						|
slot and eq?. A primitive call is relatively expensive (two subroutine calls)
 | 
						|
so this results in a big speedup for relatively little effort. */
 | 
						|
 | 
						|
bool jit_primitive_call_p(F_ARRAY *array, CELL i)
 | 
						|
{
 | 
						|
	return (i + 2) == array_capacity(array)
 | 
						|
		&& type_of(array_nth(array,i)) == FIXNUM_TYPE
 | 
						|
		&& array_nth(array,i + 1) == userenv[JIT_PRIMITIVE_WORD];
 | 
						|
}
 | 
						|
 | 
						|
bool jit_fast_if_p(F_ARRAY *array, CELL i)
 | 
						|
{
 | 
						|
	return (i + 3) == array_capacity(array)
 | 
						|
		&& type_of(array_nth(array,i)) == QUOTATION_TYPE
 | 
						|
		&& type_of(array_nth(array,i + 1)) == QUOTATION_TYPE
 | 
						|
		&& array_nth(array,i + 2) == userenv[JIT_IF_WORD];
 | 
						|
}
 | 
						|
 | 
						|
bool jit_fast_dispatch_p(F_ARRAY *array, CELL i)
 | 
						|
{
 | 
						|
	return (i + 2) == array_capacity(array)
 | 
						|
		&& type_of(array_nth(array,i)) == ARRAY_TYPE
 | 
						|
		&& array_nth(array,i + 1) == userenv[JIT_DISPATCH_WORD];
 | 
						|
}
 | 
						|
 | 
						|
bool jit_ignore_declare_p(F_ARRAY *array, CELL i)
 | 
						|
{
 | 
						|
	return (i + 1) < array_capacity(array)
 | 
						|
		&& type_of(array_nth(array,i)) == ARRAY_TYPE
 | 
						|
		&& array_nth(array,i + 1) == userenv[JIT_DECLARE_WORD];
 | 
						|
}
 | 
						|
 | 
						|
F_ARRAY *code_to_emit(CELL code)
 | 
						|
{
 | 
						|
	return untag_object(array_nth(untag_object(code),0));
 | 
						|
}
 | 
						|
 | 
						|
F_REL rel_to_emit(CELL code, CELL code_format, CELL code_length,
 | 
						|
	CELL rel_argument, bool *rel_p)
 | 
						|
{
 | 
						|
	F_ARRAY *quadruple = untag_object(code);
 | 
						|
	CELL rel_class = array_nth(quadruple,1);
 | 
						|
	CELL rel_type = array_nth(quadruple,2);
 | 
						|
	CELL offset = array_nth(quadruple,3);
 | 
						|
 | 
						|
	F_REL rel;
 | 
						|
 | 
						|
	if(rel_class == F)
 | 
						|
	{
 | 
						|
		*rel_p = false;
 | 
						|
		rel.type = 0;
 | 
						|
		rel.offset = 0;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		*rel_p = true;
 | 
						|
		rel.type = to_fixnum(rel_type)
 | 
						|
			| (to_fixnum(rel_class) << 8)
 | 
						|
			| (rel_argument << 16);
 | 
						|
		rel.offset = (code_length + to_fixnum(offset)) * code_format;
 | 
						|
	}
 | 
						|
 | 
						|
	return rel;
 | 
						|
}
 | 
						|
 | 
						|
#define EMIT(name,rel_argument) { \
 | 
						|
		bool rel_p; \
 | 
						|
		F_REL rel = rel_to_emit(name,code_format,code_count,rel_argument,&rel_p); \
 | 
						|
		if(rel_p) GROWABLE_BYTE_ARRAY_APPEND(relocation,&rel,sizeof(F_REL)); \
 | 
						|
		GROWABLE_ARRAY_APPEND(code,code_to_emit(name)); \
 | 
						|
	}
 | 
						|
 | 
						|
bool jit_stack_frame_p(F_ARRAY *array)
 | 
						|
{
 | 
						|
	F_FIXNUM length = array_capacity(array);
 | 
						|
	F_FIXNUM i;
 | 
						|
 | 
						|
	for(i = 0; i < length - 1; i++)
 | 
						|
	{
 | 
						|
		CELL obj = array_nth(array,i);
 | 
						|
		if(type_of(obj) == WORD_TYPE)
 | 
						|
		{
 | 
						|
			F_WORD *word = untag_object(obj);
 | 
						|
			if(word->subprimitive == F && obj != userenv[JIT_DECLARE_WORD])
 | 
						|
				return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
void set_quot_xt(F_QUOTATION *quot, F_COMPILED *code)
 | 
						|
{
 | 
						|
	if(code->type != QUOTATION_TYPE)
 | 
						|
		critical_error("bad param to set_quot_xt",(CELL)code);
 | 
						|
 | 
						|
	quot->code = code;
 | 
						|
	quot->xt = (XT)(code + 1);
 | 
						|
	quot->compiledp = T;
 | 
						|
}
 | 
						|
 | 
						|
/* Might GC */
 | 
						|
void jit_compile(CELL quot, bool relocate)
 | 
						|
{
 | 
						|
	if(untag_quotation(quot)->compiledp != F)
 | 
						|
		return;
 | 
						|
 | 
						|
	CELL code_format = compiled_code_format();
 | 
						|
 | 
						|
	REGISTER_ROOT(quot);
 | 
						|
 | 
						|
	CELL array = untag_quotation(quot)->array;
 | 
						|
	REGISTER_ROOT(array);
 | 
						|
 | 
						|
	GROWABLE_ARRAY(code);
 | 
						|
	REGISTER_ROOT(code);
 | 
						|
 | 
						|
	GROWABLE_BYTE_ARRAY(relocation);
 | 
						|
	REGISTER_ROOT(relocation);
 | 
						|
 | 
						|
	GROWABLE_ARRAY(literals);
 | 
						|
	REGISTER_ROOT(literals);
 | 
						|
 | 
						|
	GROWABLE_ARRAY_ADD(literals,stack_traces_p() ? quot : F);
 | 
						|
 | 
						|
	bool stack_frame = jit_stack_frame_p(untag_object(array));
 | 
						|
 | 
						|
	if(stack_frame)
 | 
						|
		EMIT(userenv[JIT_PROLOG],0);
 | 
						|
 | 
						|
	CELL i;
 | 
						|
	CELL length = array_capacity(untag_object(array));
 | 
						|
	bool tail_call = false;
 | 
						|
 | 
						|
	for(i = 0; i < length; i++)
 | 
						|
	{
 | 
						|
		CELL obj = array_nth(untag_object(array),i);
 | 
						|
		F_WORD *word;
 | 
						|
		F_WRAPPER *wrapper;
 | 
						|
 | 
						|
		switch(type_of(obj))
 | 
						|
		{
 | 
						|
		case WORD_TYPE:
 | 
						|
			word = untag_object(obj);
 | 
						|
 | 
						|
			/* Intrinsics */
 | 
						|
			if(word->subprimitive != F)
 | 
						|
			{
 | 
						|
				if(array_nth(untag_object(word->subprimitive),1) != F)
 | 
						|
				{
 | 
						|
					GROWABLE_ARRAY_ADD(literals,T);
 | 
						|
				}
 | 
						|
 | 
						|
				EMIT(word->subprimitive,literals_count - 1);
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
 | 
						|
 | 
						|
				if(i == length - 1)
 | 
						|
				{
 | 
						|
					if(stack_frame)
 | 
						|
						EMIT(userenv[JIT_EPILOG],0);
 | 
						|
 | 
						|
					EMIT(userenv[JIT_WORD_JUMP],literals_count - 1);
 | 
						|
 | 
						|
					tail_call = true;
 | 
						|
				}
 | 
						|
				else
 | 
						|
					EMIT(userenv[JIT_WORD_CALL],literals_count - 1);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		case WRAPPER_TYPE:
 | 
						|
			wrapper = untag_object(obj);
 | 
						|
			GROWABLE_ARRAY_ADD(literals,wrapper->object);
 | 
						|
			EMIT(userenv[JIT_PUSH_LITERAL],literals_count - 1);
 | 
						|
			break;
 | 
						|
		case FIXNUM_TYPE:
 | 
						|
			if(jit_primitive_call_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				EMIT(userenv[JIT_SAVE_STACK],0);
 | 
						|
				EMIT(userenv[JIT_PRIMITIVE],to_fixnum(obj));
 | 
						|
 | 
						|
				i++;
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case QUOTATION_TYPE:
 | 
						|
			if(jit_fast_if_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				if(stack_frame)
 | 
						|
					EMIT(userenv[JIT_EPILOG],0);
 | 
						|
 | 
						|
				GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
 | 
						|
				GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i + 1));
 | 
						|
				EMIT(userenv[JIT_IF_JUMP],literals_count - 2);
 | 
						|
 | 
						|
				i += 2;
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case ARRAY_TYPE:
 | 
						|
			if(jit_fast_dispatch_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				if(stack_frame)
 | 
						|
					EMIT(userenv[JIT_EPILOG],0);
 | 
						|
 | 
						|
				GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
 | 
						|
				EMIT(userenv[JIT_DISPATCH],literals_count - 1);
 | 
						|
 | 
						|
				i++;
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			else if(jit_ignore_declare_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				i++;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			GROWABLE_ARRAY_ADD(literals,obj);
 | 
						|
			EMIT(userenv[immediate_p(obj) ? JIT_PUSH_IMMEDIATE : JIT_PUSH_LITERAL],literals_count - 1);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(!tail_call)
 | 
						|
	{
 | 
						|
		if(stack_frame)
 | 
						|
			EMIT(userenv[JIT_EPILOG],0);
 | 
						|
 | 
						|
		EMIT(userenv[JIT_RETURN],0);
 | 
						|
	}
 | 
						|
 | 
						|
	GROWABLE_ARRAY_TRIM(code);
 | 
						|
	GROWABLE_ARRAY_TRIM(literals);
 | 
						|
	GROWABLE_BYTE_ARRAY_TRIM(relocation);
 | 
						|
 | 
						|
	F_COMPILED *compiled = add_compiled_block(
 | 
						|
		QUOTATION_TYPE,
 | 
						|
		untag_object(code),
 | 
						|
		NULL,
 | 
						|
		relocation,
 | 
						|
		untag_object(literals));
 | 
						|
 | 
						|
	set_quot_xt(untag_object(quot),compiled);
 | 
						|
 | 
						|
	if(relocate)
 | 
						|
		iterate_code_heap_step(compiled,relocate_code_block);
 | 
						|
 | 
						|
	UNREGISTER_ROOT(literals);
 | 
						|
	UNREGISTER_ROOT(relocation);
 | 
						|
	UNREGISTER_ROOT(code);
 | 
						|
	UNREGISTER_ROOT(array);
 | 
						|
	UNREGISTER_ROOT(quot);
 | 
						|
}
 | 
						|
 | 
						|
/* Crappy code duplication. If C had closures (not just function pointers)
 | 
						|
it would be easy to get rid of, but I can't think of a good way to deal
 | 
						|
with it right now that doesn't involve lots of boilerplate that would be
 | 
						|
worse than the duplication itself (eg, putting all state in some global
 | 
						|
struct.) */
 | 
						|
#define COUNT(name,scan) \
 | 
						|
	{ \
 | 
						|
		if(offset == 0) return scan - 1; \
 | 
						|
		offset -= array_capacity(code_to_emit(name)) * code_format; \
 | 
						|
	}
 | 
						|
 | 
						|
F_FIXNUM quot_code_offset_to_scan(CELL quot, F_FIXNUM offset)
 | 
						|
{
 | 
						|
	CELL code_format = compiled_code_format();
 | 
						|
 | 
						|
	CELL array = untag_quotation(quot)->array;
 | 
						|
 | 
						|
	bool stack_frame = jit_stack_frame_p(untag_object(array));
 | 
						|
 | 
						|
	if(stack_frame)
 | 
						|
		COUNT(userenv[JIT_PROLOG],0)
 | 
						|
 | 
						|
	CELL i;
 | 
						|
	CELL length = array_capacity(untag_object(array));
 | 
						|
	bool tail_call = false;
 | 
						|
 | 
						|
	for(i = 0; i < length; i++)
 | 
						|
	{
 | 
						|
		CELL obj = array_nth(untag_object(array),i);
 | 
						|
		F_WORD *word;
 | 
						|
 | 
						|
		switch(type_of(obj))
 | 
						|
		{
 | 
						|
		case WORD_TYPE:
 | 
						|
			/* Intrinsics */
 | 
						|
			word = untag_object(obj);
 | 
						|
			if(word->subprimitive != F)
 | 
						|
				COUNT(word->subprimitive,i)
 | 
						|
			else if(i == length - 1)
 | 
						|
			{
 | 
						|
				if(stack_frame)
 | 
						|
					COUNT(userenv[JIT_EPILOG],i);
 | 
						|
 | 
						|
				COUNT(userenv[JIT_WORD_JUMP],i)
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
			}
 | 
						|
			else
 | 
						|
				COUNT(userenv[JIT_WORD_CALL],i)
 | 
						|
			break;
 | 
						|
		case WRAPPER_TYPE:
 | 
						|
			COUNT(userenv[JIT_PUSH_LITERAL],i)
 | 
						|
			break;
 | 
						|
		case FIXNUM_TYPE:
 | 
						|
			if(jit_primitive_call_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				COUNT(userenv[JIT_SAVE_STACK],i);
 | 
						|
				COUNT(userenv[JIT_PRIMITIVE],i);
 | 
						|
 | 
						|
				i++;
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case QUOTATION_TYPE:
 | 
						|
			if(jit_fast_if_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				if(stack_frame)
 | 
						|
					COUNT(userenv[JIT_EPILOG],i)
 | 
						|
 | 
						|
				i += 2;
 | 
						|
 | 
						|
				COUNT(userenv[JIT_IF_JUMP],i)
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		case ARRAY_TYPE:
 | 
						|
			if(jit_fast_dispatch_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				if(stack_frame)
 | 
						|
					COUNT(userenv[JIT_EPILOG],i)
 | 
						|
 | 
						|
				i++;
 | 
						|
 | 
						|
				COUNT(userenv[JIT_DISPATCH],i)
 | 
						|
 | 
						|
				tail_call = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if(jit_ignore_declare_p(untag_object(array),i))
 | 
						|
			{
 | 
						|
				if(offset == 0) return i;
 | 
						|
 | 
						|
				i++;
 | 
						|
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			COUNT(userenv[immediate_p(obj) ? JIT_PUSH_IMMEDIATE : JIT_PUSH_LITERAL],i)
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if(!tail_call)
 | 
						|
	{
 | 
						|
		if(stack_frame)
 | 
						|
			COUNT(userenv[JIT_EPILOG],length)
 | 
						|
 | 
						|
		COUNT(userenv[JIT_RETURN],length)
 | 
						|
	}
 | 
						|
 | 
						|
	return -1;
 | 
						|
}
 | 
						|
 | 
						|
F_FASTCALL CELL primitive_jit_compile(CELL quot, F_STACK_FRAME *stack)
 | 
						|
{
 | 
						|
	stack_chain->callstack_top = stack;
 | 
						|
	REGISTER_ROOT(quot);
 | 
						|
	jit_compile(quot,true);
 | 
						|
	UNREGISTER_ROOT(quot);
 | 
						|
	return quot;
 | 
						|
}
 | 
						|
 | 
						|
/* push a new quotation on the stack */
 | 
						|
void primitive_array_to_quotation(void)
 | 
						|
{
 | 
						|
	F_QUOTATION *quot = allot_object(QUOTATION_TYPE,sizeof(F_QUOTATION));
 | 
						|
	quot->array = dpeek();
 | 
						|
	quot->xt = lazy_jit_compile;
 | 
						|
	quot->compiledp = F;
 | 
						|
	drepl(tag_object(quot));
 | 
						|
}
 | 
						|
 | 
						|
void primitive_quotation_xt(void)
 | 
						|
{
 | 
						|
	F_QUOTATION *quot = untag_quotation(dpeek());
 | 
						|
	drepl(allot_cell((CELL)quot->xt));
 | 
						|
}
 |