renaming: contain? -> any?, deep-contains? -> deep-any?, pad-left -> pad-head, pad...
[factor/jcg.git] / vm / quotations.c
blobca1a8bb3b56eefc291a13253a6734247f291432c
1 #include "master.h"
3 /* Simple non-optimizing compiler.
5 This is one of the two compilers implementing Factor; the second one is written
6 in Factor and performs advanced optimizations. See core/compiler/compiler.factor.
8 The non-optimizing compiler compiles a quotation at a time by concatenating
9 machine code chunks; prolog, epilog, call word, jump to word, etc. These machine
10 code chunks are generated from Factor code in core/cpu/.../bootstrap.factor.
12 Calls to words and constant quotations (referenced by conditionals and dips)
13 are direct jumps to machine code blocks. Literals are also referenced directly
14 without going through the literal table.
16 It actually does do a little bit of very simple optimization:
18 1) Tail call optimization.
20 2) If a quotation is determined to not call any other words (except for a few
21 special words which are open-coded, see below), then no prolog/epilog is
22 generated.
24 3) When in tail position and immediately preceded by literal arguments, the
25 'if' and 'dispatch' conditionals are generated inline, instead of as a call to
26 the 'if' word.
28 4) When preceded by a quotation, calls to 'dip', '2dip' and '3dip' are
29 open-coded as retain stack manipulation surrounding a subroutine call.
31 5) When preceded by an array, calls to the 'declare' word are optimized out
32 entirely. This word is only used by the optimizing compiler, and with the
33 non-optimizing compiler it would otherwise just decrease performance to have to
34 push the array and immediately drop it after.
36 6) Sub-primitives are primitive words which are implemented in assembly and not
37 in the VM. They are open-coded and no subroutine call is generated. This
38 includes stack shufflers, some fixnum arithmetic words, and words such as tag,
39 slot and eq?. A primitive call is relatively expensive (two subroutine calls)
40 so this results in a big speedup for relatively little effort. */
42 bool jit_primitive_call_p(F_ARRAY *array, CELL i)
44 return (i + 2) == array_capacity(array)
45 && type_of(array_nth(array,i)) == FIXNUM_TYPE
46 && array_nth(array,i + 1) == userenv[JIT_PRIMITIVE_WORD];
49 bool jit_fast_if_p(F_ARRAY *array, CELL i)
51 return (i + 3) == array_capacity(array)
52 && type_of(array_nth(array,i)) == QUOTATION_TYPE
53 && type_of(array_nth(array,i + 1)) == QUOTATION_TYPE
54 && array_nth(array,i + 2) == userenv[JIT_IF_WORD];
57 bool jit_fast_dispatch_p(F_ARRAY *array, CELL i)
59 return (i + 2) == array_capacity(array)
60 && type_of(array_nth(array,i)) == ARRAY_TYPE
61 && array_nth(array,i + 1) == userenv[JIT_DISPATCH_WORD];
64 bool jit_fast_dip_p(F_ARRAY *array, CELL i)
66 return (i + 2) <= array_capacity(array)
67 && type_of(array_nth(array,i)) == QUOTATION_TYPE
68 && array_nth(array,i + 1) == userenv[JIT_DIP_WORD];
71 bool jit_fast_2dip_p(F_ARRAY *array, CELL i)
73 return (i + 2) <= array_capacity(array)
74 && type_of(array_nth(array,i)) == QUOTATION_TYPE
75 && array_nth(array,i + 1) == userenv[JIT_2DIP_WORD];
78 bool jit_fast_3dip_p(F_ARRAY *array, CELL i)
80 return (i + 2) <= array_capacity(array)
81 && type_of(array_nth(array,i)) == QUOTATION_TYPE
82 && array_nth(array,i + 1) == userenv[JIT_3DIP_WORD];
85 bool jit_ignore_declare_p(F_ARRAY *array, CELL i)
87 return (i + 1) < array_capacity(array)
88 && type_of(array_nth(array,i)) == ARRAY_TYPE
89 && array_nth(array,i + 1) == userenv[JIT_DECLARE_WORD];
92 F_ARRAY *code_to_emit(CELL code)
94 return untag_object(array_nth(untag_object(code),0));
97 F_REL rel_to_emit(CELL code, CELL code_format, CELL code_length,
98 CELL rel_argument, bool *rel_p)
100 F_ARRAY *quadruple = untag_object(code);
101 CELL rel_class = array_nth(quadruple,1);
102 CELL rel_type = array_nth(quadruple,2);
103 CELL offset = array_nth(quadruple,3);
105 F_REL rel;
107 if(rel_class == F)
109 *rel_p = false;
110 rel.type = 0;
111 rel.offset = 0;
113 else
115 *rel_p = true;
116 rel.type = to_fixnum(rel_type)
117 | (to_fixnum(rel_class) << 8)
118 | (rel_argument << 16);
119 rel.offset = (code_length + to_fixnum(offset)) * code_format;
122 return rel;
125 #define EMIT(name,rel_argument) { \
126 bool rel_p; \
127 F_REL rel = rel_to_emit(name,code_format,code_count,rel_argument,&rel_p); \
128 if(rel_p) GROWABLE_BYTE_ARRAY_APPEND(relocation,&rel,sizeof(F_REL)); \
129 GROWABLE_ARRAY_APPEND(code,code_to_emit(name)); \
132 bool jit_stack_frame_p(F_ARRAY *array)
134 F_FIXNUM length = array_capacity(array);
135 F_FIXNUM i;
137 for(i = 0; i < length - 1; i++)
139 CELL obj = array_nth(array,i);
140 if(type_of(obj) == WORD_TYPE)
142 F_WORD *word = untag_object(obj);
143 if(word->subprimitive == F && obj != userenv[JIT_DECLARE_WORD])
144 return true;
146 else if(type_of(obj) == QUOTATION_TYPE)
148 if(jit_fast_dip_p(array,i)
149 || jit_fast_2dip_p(array,i)
150 || jit_fast_3dip_p(array,i))
151 return true;
155 return false;
158 void set_quot_xt(F_QUOTATION *quot, F_CODE_BLOCK *code)
160 if(code->type != QUOTATION_TYPE)
161 critical_error("bad param to set_quot_xt",(CELL)code);
163 quot->code = code;
164 quot->xt = (XT)(code + 1);
165 quot->compiledp = T;
168 /* Might GC */
169 void jit_compile(CELL quot, bool relocate)
171 if(untag_quotation(quot)->compiledp != F)
172 return;
174 CELL code_format = compiled_code_format();
176 REGISTER_ROOT(quot);
178 CELL array = untag_quotation(quot)->array;
179 REGISTER_ROOT(array);
181 GROWABLE_ARRAY(code);
182 REGISTER_ROOT(code);
184 GROWABLE_BYTE_ARRAY(relocation);
185 REGISTER_ROOT(relocation);
187 GROWABLE_ARRAY(literals);
188 REGISTER_ROOT(literals);
190 GROWABLE_ARRAY_ADD(literals,stack_traces_p() ? quot : F);
192 bool stack_frame = jit_stack_frame_p(untag_object(array));
194 if(stack_frame)
195 EMIT(userenv[JIT_PROLOG],0);
197 CELL i;
198 CELL length = array_capacity(untag_object(array));
199 bool tail_call = false;
201 for(i = 0; i < length; i++)
203 CELL obj = array_nth(untag_object(array),i);
204 F_WORD *word;
205 F_WRAPPER *wrapper;
207 switch(type_of(obj))
209 case WORD_TYPE:
210 word = untag_object(obj);
212 /* Intrinsics */
213 if(word->subprimitive != F)
215 if(array_nth(untag_object(word->subprimitive),1) != F)
217 GROWABLE_ARRAY_ADD(literals,T);
220 EMIT(word->subprimitive,literals_count - 1);
222 else
224 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
226 if(i == length - 1)
228 if(stack_frame)
229 EMIT(userenv[JIT_EPILOG],0);
231 EMIT(userenv[JIT_WORD_JUMP],literals_count - 1);
233 tail_call = true;
235 else
236 EMIT(userenv[JIT_WORD_CALL],literals_count - 1);
238 break;
239 case WRAPPER_TYPE:
240 wrapper = untag_object(obj);
241 GROWABLE_ARRAY_ADD(literals,wrapper->object);
242 EMIT(userenv[JIT_PUSH_IMMEDIATE],literals_count - 1);
243 break;
244 case FIXNUM_TYPE:
245 if(jit_primitive_call_p(untag_object(array),i))
247 EMIT(userenv[JIT_SAVE_STACK],0);
248 EMIT(userenv[JIT_PRIMITIVE],to_fixnum(obj));
250 i++;
252 tail_call = true;
253 break;
255 case QUOTATION_TYPE:
256 if(jit_fast_if_p(untag_object(array),i))
258 if(stack_frame)
259 EMIT(userenv[JIT_EPILOG],0);
261 jit_compile(array_nth(untag_object(array),i),relocate);
262 jit_compile(array_nth(untag_object(array),i + 1),relocate);
264 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
265 EMIT(userenv[JIT_IF_1],literals_count - 1);
266 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i + 1));
267 EMIT(userenv[JIT_IF_2],literals_count - 1);
269 i += 2;
271 tail_call = true;
272 break;
274 else if(jit_fast_dip_p(untag_object(array),i))
276 jit_compile(obj,relocate);
278 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
279 EMIT(userenv[JIT_DIP],literals_count - 1);
281 i++;
282 break;
284 else if(jit_fast_2dip_p(untag_object(array),i))
286 jit_compile(obj,relocate);
288 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
289 EMIT(userenv[JIT_2DIP],literals_count - 1);
291 i++;
292 break;
294 else if(jit_fast_3dip_p(untag_object(array),i))
296 jit_compile(obj,relocate);
298 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
299 EMIT(userenv[JIT_3DIP],literals_count - 1);
301 i++;
302 break;
304 case ARRAY_TYPE:
305 if(jit_fast_dispatch_p(untag_object(array),i))
307 if(stack_frame)
308 EMIT(userenv[JIT_EPILOG],0);
310 GROWABLE_ARRAY_ADD(literals,array_nth(untag_object(array),i));
311 EMIT(userenv[JIT_DISPATCH],literals_count - 1);
313 i++;
315 tail_call = true;
316 break;
318 else if(jit_ignore_declare_p(untag_object(array),i))
320 i++;
321 break;
323 default:
324 GROWABLE_ARRAY_ADD(literals,obj);
325 EMIT(userenv[JIT_PUSH_IMMEDIATE],literals_count - 1);
326 break;
330 if(!tail_call)
332 if(stack_frame)
333 EMIT(userenv[JIT_EPILOG],0);
335 EMIT(userenv[JIT_RETURN],0);
338 GROWABLE_ARRAY_TRIM(code);
339 GROWABLE_ARRAY_TRIM(literals);
340 GROWABLE_BYTE_ARRAY_TRIM(relocation);
342 F_CODE_BLOCK *compiled = add_compiled_block(
343 QUOTATION_TYPE,
344 untag_object(code),
345 NULL,
346 relocation,
347 literals);
349 set_quot_xt(untag_object(quot),compiled);
351 if(relocate)
352 relocate_code_block(compiled);
354 UNREGISTER_ROOT(literals);
355 UNREGISTER_ROOT(relocation);
356 UNREGISTER_ROOT(code);
357 UNREGISTER_ROOT(array);
358 UNREGISTER_ROOT(quot);
361 /* Crappy code duplication. If C had closures (not just function pointers)
362 it would be easy to get rid of, but I can't think of a good way to deal
363 with it right now that doesn't involve lots of boilerplate that would be
364 worse than the duplication itself (eg, putting all state in some global
365 struct.) */
366 #define COUNT(name,scan) \
368 CELL size = array_capacity(code_to_emit(name)) * code_format; \
369 if(offset == 0) return scan - 1; \
370 if(offset < size) return scan + 1; \
371 offset -= size; \
374 F_FIXNUM quot_code_offset_to_scan(CELL quot, F_FIXNUM offset)
376 CELL code_format = compiled_code_format();
378 CELL array = untag_quotation(quot)->array;
380 bool stack_frame = jit_stack_frame_p(untag_object(array));
382 if(stack_frame)
383 COUNT(userenv[JIT_PROLOG],0)
385 CELL i;
386 CELL length = array_capacity(untag_object(array));
387 bool tail_call = false;
389 for(i = 0; i < length; i++)
391 CELL obj = array_nth(untag_object(array),i);
392 F_WORD *word;
394 switch(type_of(obj))
396 case WORD_TYPE:
397 /* Intrinsics */
398 word = untag_object(obj);
399 if(word->subprimitive != F)
400 COUNT(word->subprimitive,i)
401 else if(i == length - 1)
403 if(stack_frame)
404 COUNT(userenv[JIT_EPILOG],i);
406 COUNT(userenv[JIT_WORD_JUMP],i)
408 tail_call = true;
410 else
411 COUNT(userenv[JIT_WORD_CALL],i)
412 break;
413 case WRAPPER_TYPE:
414 COUNT(userenv[JIT_PUSH_IMMEDIATE],i)
415 break;
416 case FIXNUM_TYPE:
417 if(jit_primitive_call_p(untag_object(array),i))
419 COUNT(userenv[JIT_SAVE_STACK],i);
420 COUNT(userenv[JIT_PRIMITIVE],i);
422 i++;
424 tail_call = true;
425 break;
427 case QUOTATION_TYPE:
428 if(jit_fast_if_p(untag_object(array),i))
430 if(stack_frame)
431 COUNT(userenv[JIT_EPILOG],i)
433 COUNT(userenv[JIT_IF_1],i)
434 COUNT(userenv[JIT_IF_2],i)
435 i += 2;
437 tail_call = true;
438 break;
440 else if(jit_fast_dip_p(untag_object(array),i))
442 COUNT(userenv[JIT_DIP],i)
443 i++;
444 break;
446 else if(jit_fast_2dip_p(untag_object(array),i))
448 COUNT(userenv[JIT_2DIP],i)
449 i++;
450 break;
452 else if(jit_fast_3dip_p(untag_object(array),i))
454 COUNT(userenv[JIT_3DIP],i)
455 i++;
456 break;
458 case ARRAY_TYPE:
459 if(jit_fast_dispatch_p(untag_object(array),i))
461 if(stack_frame)
462 COUNT(userenv[JIT_EPILOG],i)
464 i++;
466 COUNT(userenv[JIT_DISPATCH],i)
468 tail_call = true;
469 break;
471 if(jit_ignore_declare_p(untag_object(array),i))
473 if(offset == 0) return i;
475 i++;
477 break;
479 default:
480 COUNT(userenv[JIT_PUSH_IMMEDIATE],i)
481 break;
485 if(!tail_call)
487 if(stack_frame)
488 COUNT(userenv[JIT_EPILOG],length)
490 COUNT(userenv[JIT_RETURN],length)
493 return -1;
496 F_FASTCALL CELL lazy_jit_compile_impl(CELL quot, F_STACK_FRAME *stack)
498 stack_chain->callstack_top = stack;
499 REGISTER_ROOT(quot);
500 jit_compile(quot,true);
501 UNREGISTER_ROOT(quot);
502 return quot;
505 void primitive_jit_compile(void)
507 jit_compile(dpop(),true);
510 /* push a new quotation on the stack */
511 void primitive_array_to_quotation(void)
513 F_QUOTATION *quot = allot_object(QUOTATION_TYPE,sizeof(F_QUOTATION));
514 quot->array = dpeek();
515 quot->xt = lazy_jit_compile;
516 quot->compiledp = F;
517 drepl(tag_object(quot));
520 void primitive_quotation_xt(void)
522 F_QUOTATION *quot = untag_quotation(dpeek());
523 drepl(allot_cell((CELL)quot->xt));
526 void compile_all_words(void)
528 CELL words = find_all_words();
530 REGISTER_ROOT(words);
532 CELL i;
533 CELL length = array_capacity(untag_object(words));
534 for(i = 0; i < length; i++)
536 F_WORD *word = untag_word(array_nth(untag_array(words),i));
537 REGISTER_UNTAGGED(word);
538 if(word->optimizedp == F)
539 default_word_code(word,false);
540 UNREGISTER_UNTAGGED(word);
541 update_word_xt(word);
544 UNREGISTER_ROOT(words);
546 iterate_code_heap(relocate_code_block);