6 /* Simple non-optimizing compiler.
8 This is one of the two compilers implementing Factor; the second one is written
9 in Factor and performs advanced optimizations. See core/compiler/compiler.factor.
11 The non-optimizing compiler compiles a quotation at a time by concatenating
12 machine code chunks; prolog, epilog, call word, jump to word, etc. These machine
13 code chunks are generated from Factor code in core/cpu/.../bootstrap.factor.
15 Calls to words and constant quotations (referenced by conditionals and dips)
16 are direct jumps to machine code blocks. Literals are also referenced directly
17 without going through the literal table.
19 It actually does do a little bit of very simple optimization:
21 1) Tail call optimization.
23 2) If a quotation is determined to not call any other words (except for a few
24 special words which are open-coded, see below), then no prolog/epilog is
27 3) When in tail position and immediately preceded by literal arguments, the
28 'if' is generated inline, instead of as a call to the 'if' word.
30 4) When preceded by a quotation, calls to 'dip', '2dip' and '3dip' are
31 open-coded as retain stack manipulation surrounding a subroutine call.
33 5) Sub-primitives are primitive words which are implemented in assembly and not
34 in the VM. They are open-coded and no subroutine call is generated. This
35 includes stack shufflers, some fixnum arithmetic words, and words such as tag,
36 slot and eq?. A primitive call is relatively expensive (two subroutine calls)
37 so this results in a big speedup for relatively little effort. */
39 bool quotation_jit::primitive_call_p(cell i)
41 return (i + 2) == array_capacity(elements.untagged())
42 && tagged<object>(array_nth(elements.untagged(),i)).type_p(FIXNUM_TYPE)
43 && array_nth(elements.untagged(),i + 1) == userenv[JIT_PRIMITIVE_WORD];
46 bool quotation_jit::fast_if_p(cell i)
48 return (i + 3) == array_capacity(elements.untagged())
49 && tagged<object>(array_nth(elements.untagged(),i)).type_p(QUOTATION_TYPE)
50 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(QUOTATION_TYPE)
51 && array_nth(elements.untagged(),i + 2) == userenv[JIT_IF_WORD];
54 bool quotation_jit::fast_dip_p(cell i)
56 return (i + 2) <= array_capacity(elements.untagged())
57 && tagged<object>(array_nth(elements.untagged(),i)).type_p(QUOTATION_TYPE)
58 && array_nth(elements.untagged(),i + 1) == userenv[JIT_DIP_WORD];
61 bool quotation_jit::fast_2dip_p(cell i)
63 return (i + 2) <= array_capacity(elements.untagged())
64 && tagged<object>(array_nth(elements.untagged(),i)).type_p(QUOTATION_TYPE)
65 && array_nth(elements.untagged(),i + 1) == userenv[JIT_2DIP_WORD];
68 bool quotation_jit::fast_3dip_p(cell i)
70 return (i + 2) <= array_capacity(elements.untagged())
71 && tagged<object>(array_nth(elements.untagged(),i)).type_p(QUOTATION_TYPE)
72 && array_nth(elements.untagged(),i + 1) == userenv[JIT_3DIP_WORD];
75 bool quotation_jit::mega_lookup_p(cell i)
77 return (i + 3) < array_capacity(elements.untagged())
78 && tagged<object>(array_nth(elements.untagged(),i)).type_p(ARRAY_TYPE)
79 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(FIXNUM_TYPE)
80 && tagged<object>(array_nth(elements.untagged(),i + 2)).type_p(ARRAY_TYPE)
81 && array_nth(elements.untagged(),i + 3) == userenv[MEGA_LOOKUP_WORD];
84 bool quotation_jit::stack_frame_p()
86 fixnum length = array_capacity(elements.untagged());
89 for(i = 0; i < length - 1; i++)
91 cell obj = array_nth(elements.untagged(),i);
92 switch(tagged<object>(obj).type())
95 if(untag<word>(obj)->subprimitive == F)
99 if(fast_dip_p(i) || fast_2dip_p(i) || fast_3dip_p(i))
110 /* Allocates memory */
111 void quotation_jit::iterate_quotation()
113 bool stack_frame = stack_frame_p();
118 emit(userenv[JIT_PROLOG]);
121 cell length = array_capacity(elements.untagged());
122 bool tail_call = false;
124 for(i = 0; i < length; i++)
128 gc_root<object> obj(array_nth(elements.untagged(),i));
134 if(obj.as<word>()->subprimitive != F)
135 emit_subprimitive(obj.value());
136 /* The (execute) primitive is special-cased */
137 else if(obj.value() == userenv[JIT_EXECUTE_WORD])
141 if(stack_frame) emit(userenv[JIT_EPILOG]);
143 emit(userenv[JIT_EXECUTE_JUMP]);
146 emit(userenv[JIT_EXECUTE_CALL]);
148 /* Everything else */
153 if(stack_frame) emit(userenv[JIT_EPILOG]);
155 /* Inline cache misses are special-cased.
156 The calling convention for tail
157 calls stores the address of the next
158 instruction in a register. However,
159 PIC miss stubs themselves tail-call
160 the inline cache miss primitive, and
161 we don't want to clobber the saved
163 if(obj.value() == userenv[PIC_MISS_WORD]
164 || obj.value() == userenv[PIC_MISS_TAIL_WORD])
166 word_special(obj.value());
170 word_jump(obj.value());
174 word_call(obj.value());
178 push(obj.as<wrapper>()->object);
181 /* Primitive calls */
182 if(primitive_call_p(i))
184 emit_with(userenv[JIT_PRIMITIVE],obj.value());
192 /* 'if' preceeded by two literal quotations (this is why if and ? are
193 mutually recursive in the library, but both still work) */
196 if(stack_frame) emit(userenv[JIT_EPILOG]);
201 jit_compile(array_nth(elements.untagged(),i),relocate);
202 jit_compile(array_nth(elements.untagged(),i + 1),relocate);
205 literal(array_nth(elements.untagged(),i));
206 literal(array_nth(elements.untagged(),i + 1));
207 emit(userenv[JIT_IF]);
214 else if(fast_dip_p(i))
217 jit_compile(obj.value(),relocate);
218 emit_with(userenv[JIT_DIP],obj.value());
223 else if(fast_2dip_p(i))
226 jit_compile(obj.value(),relocate);
227 emit_with(userenv[JIT_2DIP],obj.value());
232 else if(fast_3dip_p(i))
235 jit_compile(obj.value(),relocate);
236 emit_with(userenv[JIT_3DIP],obj.value());
241 /* Method dispatch */
244 emit_mega_cache_lookup(
245 array_nth(elements.untagged(),i),
246 untag_fixnum(array_nth(elements.untagged(),i + 1)),
247 array_nth(elements.untagged(),i + 2));
260 set_position(length);
263 emit(userenv[JIT_EPILOG]);
264 emit(userenv[JIT_RETURN]);
268 void set_quot_xt(quotation *quot, code_block *code)
270 if(code->type != QUOTATION_TYPE)
271 critical_error("Bad param to set_quot_xt",(cell)code);
274 quot->xt = code->xt();
277 /* Allocates memory */
278 void jit_compile(cell quot_, bool relocating)
280 gc_root<quotation> quot(quot_);
281 if(quot->code) return;
283 quotation_jit compiler(quot.value(),true,relocating);
284 compiler.iterate_quotation();
286 code_block *compiled = compiler.to_code_block();
287 set_quot_xt(quot.untagged(),compiled);
289 if(relocating) relocate_code_block(compiled);
292 PRIMITIVE(jit_compile)
294 jit_compile(dpop(),true);
297 /* push a new quotation on the stack */
298 PRIMITIVE(array_to_quotation)
300 quotation *quot = allot<quotation>(sizeof(quotation));
301 quot->array = dpeek();
302 quot->cached_effect = F;
303 quot->cache_counter = F;
304 quot->xt = (void *)lazy_jit_compile;
306 drepl(tag<quotation>(quot));
309 PRIMITIVE(quotation_xt)
311 quotation *quot = untag_check<quotation>(dpeek());
312 drepl(allot_cell((cell)quot->xt));
315 void compile_all_words()
317 gc_root<array> words(find_all_words());
320 cell length = array_capacity(words.untagged());
321 for(i = 0; i < length; i++)
323 gc_root<word> word(array_nth(words.untagged(),i));
325 if(!word->code || !word_optimized_p(word.untagged()))
326 jit_compile_word(word.value(),word->def,false);
328 update_word_xt(word.value());
332 iterate_code_heap(relocate_code_block);
335 /* Allocates memory */
336 fixnum quot_code_offset_to_scan(cell quot_, cell offset)
338 gc_root<quotation> quot(quot_);
339 gc_root<array> array(quot->array);
341 quotation_jit compiler(quot.value(),false,false);
342 compiler.compute_position(offset);
343 compiler.iterate_quotation();
345 return compiler.get_position();
348 VM_ASM_API cell lazy_jit_compile_impl(cell quot_, stack_frame *stack)
350 gc_root<quotation> quot(quot_);
351 stack_chain->callstack_top = stack;
352 jit_compile(quot.value(),true);
356 PRIMITIVE(quot_compiled_p)
358 tagged<quotation> quot(dpop());
360 dpush(tag_boolean(quot->code != NULL));