factor/vm/quotations.c

545 lines
12 KiB
C
Raw Normal View History

2007-09-20 18:09:08 -04:00
#include "master.h"
2008-07-11 18:38:53 -04:00
/* 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.
2008-11-24 10:27:15 -05:00
Calls to words and constant quotations (referenced by conditionals and dips)
are direct jumps to machine code blocks. Literals are also referenced directly
without going through the literal table.
2008-07-11 18:38:53 -04:00
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.
2008-11-24 10:27:15 -05:00
4) When preceded by a quotation, calls to 'dip', '2dip' and '3dip' are
open-coded as retain stack manipulation surrounding a subroutine call.
5) When preceded by an array, calls to the 'declare' word are optimized out
2008-07-11 18:38:53 -04:00
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.
2008-11-24 10:27:15 -05:00
6) Sub-primitives are primitive words which are implemented in assembly and not
2008-07-11 18:38:53 -04:00
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)
2008-07-12 23:27:28 -04:00
so this results in a big speedup for relatively little effort. */
2008-07-11 18:38:53 -04:00
2008-01-02 19:36:36 -05:00
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];
}
2007-09-20 18:09:08 -04:00
bool jit_fast_if_p(F_ARRAY *array, CELL i)
{
return (i + 3) == array_capacity(array)
2007-09-20 18:09:08 -04:00
&& 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
2007-09-20 18:09:08 -04:00
&& array_nth(array,i + 1) == userenv[JIT_DISPATCH_WORD];
}
bool jit_fast_dip_p(F_ARRAY *array, CELL i)
{
return (i + 2) <= array_capacity(array)
&& type_of(array_nth(array,i)) == QUOTATION_TYPE
&& array_nth(array,i + 1) == userenv[JIT_DIP_WORD];
}
bool jit_fast_2dip_p(F_ARRAY *array, CELL i)
{
return (i + 2) <= array_capacity(array)
&& type_of(array_nth(array,i)) == QUOTATION_TYPE
&& array_nth(array,i + 1) == userenv[JIT_2DIP_WORD];
}
bool jit_fast_3dip_p(F_ARRAY *array, CELL i)
{
return (i + 2) <= array_capacity(array)
&& type_of(array_nth(array,i)) == QUOTATION_TYPE
&& array_nth(array,i + 1) == userenv[JIT_3DIP_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, 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);
if(rel_class == F)
{
*rel_p = false;
2009-03-19 21:03:07 -04:00
return 0;
}
else
{
*rel_p = true;
2009-03-19 21:03:07 -04:00
return (to_fixnum(rel_type) << 28)
| (to_fixnum(rel_class) << 24)
| ((code_length + to_fixnum(offset)) * code_format);
}
}
2009-03-19 21:03:07 -04:00
#define EMIT(name) { \
bool rel_p; \
F_REL rel = rel_to_emit(name,code_format,code_count,&rel_p); \
if(rel_p) GROWABLE_BYTE_ARRAY_APPEND(relocation,&rel,sizeof(F_REL)); \
GROWABLE_ARRAY_APPEND(code,code_to_emit(name)); \
2007-09-20 18:09:08 -04:00
}
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;
}
else if(type_of(obj) == QUOTATION_TYPE)
{
if(jit_fast_dip_p(array,i)
|| jit_fast_2dip_p(array,i)
|| jit_fast_3dip_p(array,i))
return true;
}
2007-09-20 18:09:08 -04:00
}
return false;
}
void set_quot_xt(F_QUOTATION *quot, F_CODE_BLOCK *code)
{
if(code->block.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 */
2008-01-09 01:33:40 -05:00
void jit_compile(CELL quot, bool relocate)
2007-09-20 18:09:08 -04:00
{
2007-12-26 02:33:49 -05:00
if(untag_quotation(quot)->compiledp != F)
return;
CELL code_format = compiled_code_format();
REGISTER_ROOT(quot);
2007-09-20 18:09:08 -04:00
CELL array = untag_quotation(quot)->array;
REGISTER_ROOT(array);
2007-09-20 18:09:08 -04:00
GROWABLE_ARRAY(code);
REGISTER_ROOT(code);
GROWABLE_BYTE_ARRAY(relocation);
REGISTER_ROOT(relocation);
2007-09-20 18:09:08 -04:00
GROWABLE_ARRAY(literals);
REGISTER_ROOT(literals);
2007-09-20 18:09:08 -04:00
if(stack_traces_p())
GROWABLE_ARRAY_ADD(literals,quot);
bool stack_frame = jit_stack_frame_p(untag_object(array));
2007-09-20 18:09:08 -04:00
if(stack_frame)
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_PROLOG]);
2007-09-20 18:09:08 -04:00
CELL i;
CELL length = array_capacity(untag_object(array));
2007-09-20 18:09:08 -04:00
bool tail_call = false;
for(i = 0; i < length; i++)
{
CELL obj = array_nth(untag_object(array),i);
2007-09-20 18:09:08 -04:00
F_WORD *word;
F_WRAPPER *wrapper;
2007-09-20 18:09:08 -04:00
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);
}
2009-03-19 21:03:07 -04:00
EMIT(word->subprimitive);
}
else
{
2009-03-19 21:03:07 -04:00
GROWABLE_ARRAY_ADD(literals,obj);
2007-09-20 18:09:08 -04:00
if(i == length - 1)
{
if(stack_frame)
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_EPILOG]);
2007-09-20 18:09:08 -04:00
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_WORD_JUMP]);
2007-12-26 02:33:49 -05:00
tail_call = true;
}
else
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_WORD_CALL]);
2007-09-20 18:09:08 -04:00
}
break;
case WRAPPER_TYPE:
wrapper = untag_object(obj);
GROWABLE_ARRAY_ADD(literals,wrapper->object);
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_PUSH_IMMEDIATE]);
2007-09-20 18:09:08 -04:00
break;
2008-01-02 19:36:36 -05:00
case FIXNUM_TYPE:
if(jit_primitive_call_p(untag_object(array),i))
{
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_SAVE_STACK]);
GROWABLE_ARRAY_ADD(literals,obj);
EMIT(userenv[JIT_PRIMITIVE]);
2008-01-02 19:36:36 -05:00
i++;
tail_call = true;
break;
}
2007-09-20 18:09:08 -04:00
case QUOTATION_TYPE:
if(jit_fast_if_p(untag_object(array),i))
2007-09-20 18:09:08 -04:00
{
if(stack_frame)
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_EPILOG]);
2007-09-20 18:09:08 -04:00
2008-11-24 03:03:01 -05:00
jit_compile(array_nth(untag_object(array),i),relocate);
jit_compile(array_nth(untag_object(array),i + 1),relocate);
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_IF_1]);
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i + 1));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_IF_2]);
i += 2;
2007-09-20 18:09:08 -04:00
tail_call = true;
2007-09-20 18:09:08 -04:00
break;
}
else if(jit_fast_dip_p(untag_object(array),i))
{
2008-11-24 03:03:01 -05:00
jit_compile(obj,relocate);
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_DIP]);
i++;
break;
}
else if(jit_fast_2dip_p(untag_object(array),i))
{
2008-11-24 03:03:01 -05:00
jit_compile(obj,relocate);
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_2DIP]);
i++;
break;
}
else if(jit_fast_3dip_p(untag_object(array),i))
{
2008-11-24 03:03:01 -05:00
jit_compile(obj,relocate);
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_3DIP]);
i++;
break;
}
2007-09-20 18:09:08 -04:00
case ARRAY_TYPE:
if(jit_fast_dispatch_p(untag_object(array),i))
2007-09-20 18:09:08 -04:00
{
if(stack_frame)
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_EPILOG]);
2007-09-20 18:09:08 -04:00
GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_DISPATCH]);
i++;
2007-09-20 18:09:08 -04:00
tail_call = true;
break;
}
else if(jit_ignore_declare_p(untag_object(array),i))
{
i++;
break;
}
2007-09-20 18:09:08 -04:00
default:
GROWABLE_ARRAY_ADD(literals,obj);
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_PUSH_IMMEDIATE]);
2007-09-20 18:09:08 -04:00
break;
}
}
if(!tail_call)
{
if(stack_frame)
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_EPILOG]);
2007-09-20 18:09:08 -04:00
2009-03-19 21:03:07 -04:00
EMIT(userenv[JIT_RETURN]);
2007-09-20 18:09:08 -04:00
}
GROWABLE_ARRAY_TRIM(code);
GROWABLE_ARRAY_TRIM(literals);
GROWABLE_BYTE_ARRAY_TRIM(relocation);
F_CODE_BLOCK *compiled = add_code_block(
QUOTATION_TYPE,
untag_object(code),
NULL,
relocation,
literals);
2007-09-20 18:09:08 -04:00
set_quot_xt(untag_object(quot),compiled);
2008-01-09 01:33:40 -05:00
if(relocate)
relocate_code_block(compiled);
2008-01-02 19:36:36 -05:00
UNREGISTER_ROOT(literals);
UNREGISTER_ROOT(relocation);
UNREGISTER_ROOT(code);
UNREGISTER_ROOT(array);
UNREGISTER_ROOT(quot);
2007-09-20 18:09:08 -04:00
}
2007-12-30 16:54:01 -05:00
/* 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) \
{ \
2008-11-23 22:40:54 -05:00
CELL size = array_capacity(code_to_emit(name)) * code_format; \
2007-12-30 16:54:01 -05:00
if(offset == 0) return scan - 1; \
2008-11-23 22:40:54 -05:00
if(offset < size) return scan + 1; \
offset -= size; \
2007-12-30 16:54:01 -05:00
}
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)
2007-12-30 16:54:01 -05:00
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;
2007-12-30 16:54:01 -05:00
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)
2007-12-30 16:54:01 -05:00
{
if(stack_frame)
COUNT(userenv[JIT_EPILOG],i);
COUNT(userenv[JIT_WORD_JUMP],i)
tail_call = true;
2007-12-30 16:54:01 -05:00
}
else
COUNT(userenv[JIT_WORD_CALL],i)
2007-12-30 16:54:01 -05:00
break;
case WRAPPER_TYPE:
COUNT(userenv[JIT_PUSH_IMMEDIATE],i)
2007-12-30 16:54:01 -05:00
break;
2008-01-02 19:36:36 -05:00
case FIXNUM_TYPE:
if(jit_primitive_call_p(untag_object(array),i))
{
COUNT(userenv[JIT_SAVE_STACK],i);
COUNT(userenv[JIT_PRIMITIVE],i);
2008-01-02 19:36:36 -05:00
i++;
tail_call = true;
break;
}
2007-12-30 16:54:01 -05:00
case QUOTATION_TYPE:
if(jit_fast_if_p(untag_object(array),i))
{
if(stack_frame)
COUNT(userenv[JIT_EPILOG],i)
2007-12-30 16:54:01 -05:00
COUNT(userenv[JIT_IF_1],i)
COUNT(userenv[JIT_IF_2],i)
2008-11-23 22:40:54 -05:00
i += 2;
2007-12-30 16:54:01 -05:00
tail_call = true;
break;
}
else if(jit_fast_dip_p(untag_object(array),i))
{
COUNT(userenv[JIT_DIP],i)
2008-11-23 22:40:54 -05:00
i++;
break;
}
else if(jit_fast_2dip_p(untag_object(array),i))
{
COUNT(userenv[JIT_2DIP],i)
2008-11-23 22:40:54 -05:00
i++;
break;
}
else if(jit_fast_3dip_p(untag_object(array),i))
{
COUNT(userenv[JIT_3DIP],i)
2008-11-23 22:40:54 -05:00
i++;
break;
}
2007-12-30 16:54:01 -05:00
case ARRAY_TYPE:
if(jit_fast_dispatch_p(untag_object(array),i))
{
if(stack_frame)
COUNT(userenv[JIT_EPILOG],i)
2007-12-30 16:54:01 -05:00
i++;
COUNT(userenv[JIT_DISPATCH],i)
2007-12-30 16:54:01 -05:00
tail_call = true;
break;
}
if(jit_ignore_declare_p(untag_object(array),i))
{
2008-07-07 20:26:58 -04:00
if(offset == 0) return i;
i++;
2008-07-07 20:26:58 -04:00
break;
}
2007-12-30 16:54:01 -05:00
default:
COUNT(userenv[JIT_PUSH_IMMEDIATE],i)
2007-12-30 16:54:01 -05:00
break;
}
}
if(!tail_call)
{
if(stack_frame)
COUNT(userenv[JIT_EPILOG],length)
2007-12-30 16:54:01 -05:00
COUNT(userenv[JIT_RETURN],length)
2007-12-30 16:54:01 -05:00
}
return -1;
}
F_FASTCALL CELL lazy_jit_compile_impl(CELL quot, F_STACK_FRAME *stack)
2007-09-20 18:09:08 -04:00
{
stack_chain->callstack_top = stack;
REGISTER_ROOT(quot);
2008-01-09 01:33:40 -05:00
jit_compile(quot,true);
UNREGISTER_ROOT(quot);
return quot;
2007-09-20 18:09:08 -04:00
}
2007-10-02 17:53:05 -04:00
void primitive_jit_compile(void)
{
jit_compile(dpop(),true);
}
2007-10-02 17:53:05 -04:00
/* push a new quotation on the stack */
void primitive_array_to_quotation(void)
2007-10-02 17:53:05 -04:00
{
F_QUOTATION *quot = allot_object(QUOTATION_TYPE,sizeof(F_QUOTATION));
quot->array = dpeek();
quot->xt = lazy_jit_compile;
quot->compiledp = F;
2009-03-16 21:11:36 -04:00
quot->cached_effect = F;
quot->cache_counter = F;
2007-10-02 17:53:05 -04:00
drepl(tag_object(quot));
}
void primitive_quotation_xt(void)
2007-10-02 17:53:05 -04:00
{
F_QUOTATION *quot = untag_quotation(dpeek());
drepl(allot_cell((CELL)quot->xt));
}
2008-12-08 22:24:45 -05:00
void compile_all_words(void)
{
CELL words = find_all_words();
REGISTER_ROOT(words);
CELL i;
CELL length = array_capacity(untag_object(words));
for(i = 0; i < length; i++)
{
F_WORD *word = untag_word(array_nth(untag_array(words),i));
REGISTER_UNTAGGED(word);
2009-04-21 01:05:39 -04:00
if(word->optimizedp == F)
jit_compile_word(word,word->def,false);
2008-12-08 22:24:45 -05:00
UNREGISTER_UNTAGGED(word);
update_word_xt(word);
}
UNREGISTER_ROOT(words);
iterate_code_heap(relocate_code_block);
}