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
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
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);
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
;
125 #define EMIT(name,rel_argument) { \
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
);
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
])
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
))
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
);
164 quot
->xt
= (XT
)(code
+ 1);
169 void jit_compile(CELL quot
, bool relocate
)
171 if(untag_quotation(quot
)->compiledp
!= F
)
174 CELL code_format
= compiled_code_format();
178 CELL array
= untag_quotation(quot
)->array
;
179 REGISTER_ROOT(array
);
181 GROWABLE_ARRAY(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
));
195 EMIT(userenv
[JIT_PROLOG
],0);
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
);
210 word
= untag_object(obj
);
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);
224 GROWABLE_ARRAY_ADD(literals
,array_nth(untag_object(array
),i
));
229 EMIT(userenv
[JIT_EPILOG
],0);
231 EMIT(userenv
[JIT_WORD_JUMP
],literals_count
- 1);
236 EMIT(userenv
[JIT_WORD_CALL
],literals_count
- 1);
240 wrapper
= untag_object(obj
);
241 GROWABLE_ARRAY_ADD(literals
,wrapper
->object
);
242 EMIT(userenv
[JIT_PUSH_IMMEDIATE
],literals_count
- 1);
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
));
256 if(jit_fast_if_p(untag_object(array
),i
))
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);
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);
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);
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);
305 if(jit_fast_dispatch_p(untag_object(array
),i
))
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);
318 else if(jit_ignore_declare_p(untag_object(array
),i
))
324 GROWABLE_ARRAY_ADD(literals
,obj
);
325 EMIT(userenv
[JIT_PUSH_IMMEDIATE
],literals_count
- 1);
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(
349 set_quot_xt(untag_object(quot
),compiled
);
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
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; \
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
));
383 COUNT(userenv
[JIT_PROLOG
],0)
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
);
398 word
= untag_object(obj
);
399 if(word
->subprimitive
!= F
)
400 COUNT(word
->subprimitive
,i
)
401 else if(i
== length
- 1)
404 COUNT(userenv
[JIT_EPILOG
],i
);
406 COUNT(userenv
[JIT_WORD_JUMP
],i
)
411 COUNT(userenv
[JIT_WORD_CALL
],i
)
414 COUNT(userenv
[JIT_PUSH_IMMEDIATE
],i
)
417 if(jit_primitive_call_p(untag_object(array
),i
))
419 COUNT(userenv
[JIT_SAVE_STACK
],i
);
420 COUNT(userenv
[JIT_PRIMITIVE
],i
);
428 if(jit_fast_if_p(untag_object(array
),i
))
431 COUNT(userenv
[JIT_EPILOG
],i
)
433 COUNT(userenv
[JIT_IF_1
],i
)
434 COUNT(userenv
[JIT_IF_2
],i
)
440 else if(jit_fast_dip_p(untag_object(array
),i
))
442 COUNT(userenv
[JIT_DIP
],i
)
446 else if(jit_fast_2dip_p(untag_object(array
),i
))
448 COUNT(userenv
[JIT_2DIP
],i
)
452 else if(jit_fast_3dip_p(untag_object(array
),i
))
454 COUNT(userenv
[JIT_3DIP
],i
)
459 if(jit_fast_dispatch_p(untag_object(array
),i
))
462 COUNT(userenv
[JIT_EPILOG
],i
)
466 COUNT(userenv
[JIT_DISPATCH
],i
)
471 if(jit_ignore_declare_p(untag_object(array
),i
))
473 if(offset
== 0) return i
;
480 COUNT(userenv
[JIT_PUSH_IMMEDIATE
],i
)
488 COUNT(userenv
[JIT_EPILOG
],length
)
490 COUNT(userenv
[JIT_RETURN
],length
)
496 F_FASTCALL CELL
lazy_jit_compile_impl(CELL quot
, F_STACK_FRAME
*stack
)
498 stack_chain
->callstack_top
= stack
;
500 jit_compile(quot
,true);
501 UNREGISTER_ROOT(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
;
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
);
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
);