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 basis/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 basis/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, cell length)
41 return (i + 2) == length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_PRIMITIVE_WORD];
44 bool quotation_jit::fast_if_p(cell i, cell length)
46 return (i + 3) == length
47 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(QUOTATION_TYPE)
48 && array_nth(elements.untagged(),i + 2) == parent->special_objects[JIT_IF_WORD];
51 bool quotation_jit::fast_dip_p(cell i, cell length)
53 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DIP_WORD];
56 bool quotation_jit::fast_2dip_p(cell i, cell length)
58 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_2DIP_WORD];
61 bool quotation_jit::fast_3dip_p(cell i, cell length)
63 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_3DIP_WORD];
66 bool quotation_jit::mega_lookup_p(cell i, cell length)
68 return (i + 4) <= length
69 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(FIXNUM_TYPE)
70 && tagged<object>(array_nth(elements.untagged(),i + 2)).type_p(ARRAY_TYPE)
71 && array_nth(elements.untagged(),i + 3) == parent->special_objects[MEGA_LOOKUP_WORD];
74 bool quotation_jit::declare_p(cell i, cell length)
76 return (i + 2) <= length
77 && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DECLARE_WORD];
80 bool quotation_jit::stack_frame_p()
82 fixnum length = array_capacity(elements.untagged());
85 for(i = 0; i < length - 1; i++)
87 cell obj = array_nth(elements.untagged(),i);
88 switch(tagged<object>(obj).type())
91 if(!parent->to_boolean(untag<word>(obj)->subprimitive))
95 if(fast_dip_p(i,length) || fast_2dip_p(i,length) || fast_3dip_p(i,length))
106 bool quotation_jit::trivial_quotation_p(array *elements)
108 return array_capacity(elements) == 1 && tagged<object>(array_nth(elements,0)).type_p(WORD_TYPE);
111 void quotation_jit::emit_quot(cell quot_)
113 data_root<quotation> quot(quot_,parent);
115 array *elements = untag<array>(quot->array);
117 /* If the quotation consists of a single word, compile a direct call
119 if(trivial_quotation_p(elements))
120 literal(array_nth(elements,0));
123 if(compiling) parent->jit_compile(quot.value(),relocate);
124 literal(quot.value());
128 /* Allocates memory */
129 void quotation_jit::iterate_quotation()
131 bool stack_frame = stack_frame_p();
136 emit(parent->special_objects[JIT_PROLOG]);
139 cell length = array_capacity(elements.untagged());
140 bool tail_call = false;
142 for(i = 0; i < length; i++)
146 data_root<object> obj(array_nth(elements.untagged(),i),parent);
152 if(parent->to_boolean(obj.as<word>()->subprimitive))
153 emit_subprimitive(obj.value());
154 /* The (execute) primitive is special-cased */
155 else if(obj.value() == parent->special_objects[JIT_EXECUTE_WORD])
159 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
161 emit(parent->special_objects[JIT_EXECUTE_JUMP]);
164 emit(parent->special_objects[JIT_EXECUTE_CALL]);
166 /* Everything else */
171 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
173 /* Inline cache misses are special-cased.
174 The calling convention for tail
175 calls stores the address of the next
176 instruction in a register. However,
177 PIC miss stubs themselves tail-call
178 the inline cache miss primitive, and
179 we don't want to clobber the saved
181 if(obj.value() == parent->special_objects[PIC_MISS_WORD]
182 || obj.value() == parent->special_objects[PIC_MISS_TAIL_WORD])
184 word_special(obj.value());
188 word_jump(obj.value());
192 word_call(obj.value());
196 push(obj.as<wrapper>()->object);
199 /* Primitive calls */
200 if(primitive_call_p(i,length))
202 literal(tag_fixnum(0));
203 literal(obj.value());
204 emit(parent->special_objects[JIT_PRIMITIVE]);
214 /* 'if' preceeded by two literal quotations (this is why if and ? are
215 mutually recursive in the library, but both still work) */
216 if(fast_if_p(i,length))
218 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
221 emit_quot(array_nth(elements.untagged(),i));
222 emit_quot(array_nth(elements.untagged(),i + 1));
223 emit(parent->special_objects[JIT_IF]);
228 else if(fast_dip_p(i,length))
230 emit_quot(obj.value());
231 emit(parent->special_objects[JIT_DIP]);
235 else if(fast_2dip_p(i,length))
237 emit_quot(obj.value());
238 emit(parent->special_objects[JIT_2DIP]);
242 else if(fast_3dip_p(i,length))
244 emit_quot(obj.value());
245 emit(parent->special_objects[JIT_3DIP]);
252 /* Method dispatch */
253 if(mega_lookup_p(i,length))
255 emit_mega_cache_lookup(
256 array_nth(elements.untagged(),i),
257 untag_fixnum(array_nth(elements.untagged(),i + 1)),
258 array_nth(elements.untagged(),i + 2));
262 /* Non-optimizing compiler ignores declarations */
263 else if(declare_p(i,length))
276 set_position(length);
279 emit(parent->special_objects[JIT_EPILOG]);
280 emit(parent->special_objects[JIT_RETURN]);
284 void factor_vm::set_quot_xt(quotation *quot, code_block *code)
287 quot->xt = code->xt();
290 /* Allocates memory */
291 void factor_vm::jit_compile(cell quot_, bool relocating)
293 data_root<quotation> quot(quot_,this);
294 if(quot->code) return;
296 quotation_jit compiler(quot.value(),true,relocating,this);
297 compiler.iterate_quotation();
299 code_block *compiled = compiler.to_code_block();
300 set_quot_xt(quot.untagged(),compiled);
302 if(relocating) relocate_code_block(compiled);
305 void factor_vm::primitive_jit_compile()
307 jit_compile(dpop(),true);
310 /* push a new quotation on the stack */
311 void factor_vm::primitive_array_to_quotation()
313 quotation *quot = allot<quotation>(sizeof(quotation));
314 quot->array = dpeek();
315 quot->cached_effect = false_object;
316 quot->cache_counter = false_object;
317 quot->xt = (void *)lazy_jit_compile;
319 drepl(tag<quotation>(quot));
322 void factor_vm::primitive_quotation_xt()
324 quotation *quot = untag_check<quotation>(dpeek());
325 drepl(allot_cell((cell)quot->xt));
328 void factor_vm::compile_all_words()
330 data_root<array> words(find_all_words(),this);
333 cell length = array_capacity(words.untagged());
334 for(i = 0; i < length; i++)
336 data_root<word> word(array_nth(words.untagged(),i),this);
338 if(!word->code || !word->code->optimized_p())
339 jit_compile_word(word.value(),word->def,false);
341 update_word_xt(word.untagged());
346 /* Allocates memory */
347 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset)
349 data_root<quotation> quot(quot_,this);
350 data_root<array> array(quot->array,this);
352 quotation_jit compiler(quot.value(),false,false,this);
353 compiler.compute_position(offset);
354 compiler.iterate_quotation();
356 return compiler.get_position();
359 cell factor_vm::lazy_jit_compile_impl(cell quot_, stack_frame *stack)
361 data_root<quotation> quot(quot_,this);
362 ctx->callstack_top = stack;
363 jit_compile(quot.value(),true);
367 VM_ASM_API cell lazy_jit_compile_impl(cell quot_, stack_frame *stack, factor_vm *parent)
369 return parent->lazy_jit_compile_impl(quot_,stack);
372 void factor_vm::primitive_quot_compiled_p()
374 tagged<quotation> quot(dpop());
375 quot.untag_check(this);
376 dpush(tag_boolean(quot->code != NULL));