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 void quotation_jit::init_quotation(cell quot)
41 elements = untag<quotation>(quot)->array;
44 bool quotation_jit::primitive_call_p(cell i, cell length)
46 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_PRIMITIVE_WORD];
49 bool quotation_jit::fast_if_p(cell i, cell length)
51 return (i + 3) == length
52 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(QUOTATION_TYPE)
53 && array_nth(elements.untagged(),i + 2) == parent->special_objects[JIT_IF_WORD];
56 bool quotation_jit::fast_dip_p(cell i, cell length)
58 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DIP_WORD];
61 bool quotation_jit::fast_2dip_p(cell i, cell length)
63 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_2DIP_WORD];
66 bool quotation_jit::fast_3dip_p(cell i, cell length)
68 return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_3DIP_WORD];
71 bool quotation_jit::mega_lookup_p(cell i, cell length)
73 return (i + 4) <= length
74 && tagged<object>(array_nth(elements.untagged(),i + 1)).type_p(FIXNUM_TYPE)
75 && tagged<object>(array_nth(elements.untagged(),i + 2)).type_p(ARRAY_TYPE)
76 && array_nth(elements.untagged(),i + 3) == parent->special_objects[MEGA_LOOKUP_WORD];
79 bool quotation_jit::declare_p(cell i, cell length)
81 return (i + 2) <= length
82 && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DECLARE_WORD];
85 bool quotation_jit::word_stack_frame_p(cell obj)
87 // Subprimitives should be flagged with whether they require a stack frame.
89 return (to_boolean(untag<word>(obj)->subprimitive)
90 && obj != parent->special_objects[SIGNAL_HANDLER_WORD]
91 && obj != parent->special_objects[LEAF_SIGNAL_HANDLER_WORD]
92 && obj != parent->special_objects[FFI_SIGNAL_HANDLER_WORD]
93 && obj != parent->special_objects[FFI_LEAF_SIGNAL_HANDLER_WORD])
94 || obj == parent->special_objects[JIT_PRIMITIVE_WORD];
97 bool quotation_jit::stack_frame_p()
99 fixnum length = array_capacity(elements.untagged());
101 for(fixnum i = 0; i < length; i++)
103 cell obj = array_nth(elements.untagged(),i);
104 switch(tagged<object>(obj).type())
107 if(i != length - 1 || word_stack_frame_p(obj))
111 if(fast_dip_p(i,length) || fast_2dip_p(i,length) || fast_3dip_p(i,length))
122 bool quotation_jit::trivial_quotation_p(array *elements)
124 return array_capacity(elements) == 1 && tagged<object>(array_nth(elements,0)).type_p(WORD_TYPE);
127 void quotation_jit::emit_epilog(bool stack_frame)
129 emit(parent->special_objects[JIT_SAFEPOINT]);
130 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
133 void quotation_jit::emit_quot(cell quot_)
135 data_root<quotation> quot(quot_,parent);
137 array *elements = untag<array>(quot->array);
139 /* If the quotation consists of a single word, compile a direct call
141 if(trivial_quotation_p(elements))
142 literal(array_nth(elements,0));
145 if(compiling) parent->jit_compile_quot(quot.value(),relocate);
146 literal(quot.value());
150 /* Allocates memory */
151 void quotation_jit::iterate_quotation()
153 bool stack_frame = stack_frame_p();
158 emit(parent->special_objects[JIT_PROLOG]);
161 cell length = array_capacity(elements.untagged());
162 bool tail_call = false;
164 for(i = 0; i < length; i++)
168 data_root<object> obj(array_nth(elements.untagged(),i),parent);
174 if(to_boolean(obj.as<word>()->subprimitive))
176 tail_call = emit_subprimitive(obj.value(), /* word */
177 i == length - 1, /* tail_call_p */
178 stack_frame); /* stack_frame_p */
180 /* Everything else */
181 else if(i == length - 1)
183 emit_epilog(stack_frame);
185 word_jump(obj.value());
188 word_call(obj.value());
191 push(obj.as<wrapper>()->object);
193 case BYTE_ARRAY_TYPE:
194 /* Primitive calls */
195 if(primitive_call_p(i,length))
197 /* On x86-64 and PowerPC, the VM pointer is stored in
198 a register; on other platforms, the RT_VM relocation
199 is used and it needs an offset parameter */
201 parameter(tag_fixnum(0));
203 parameter(obj.value());
204 parameter(false_object);
205 #ifdef FACTOR_PPC_TOC
206 parameter(obj.value());
207 parameter(false_object);
209 emit(parent->special_objects[JIT_PRIMITIVE]);
217 /* 'if' preceeded by two literal quotations (this is why if and ? are
218 mutually recursive in the library, but both still work) */
219 if(fast_if_p(i,length))
221 emit_epilog(stack_frame);
224 emit_quot(array_nth(elements.untagged(),i));
225 emit_quot(array_nth(elements.untagged(),i + 1));
226 emit(parent->special_objects[JIT_IF]);
231 else if(fast_dip_p(i,length))
233 emit_quot(obj.value());
234 emit(parent->special_objects[JIT_DIP]);
238 else if(fast_2dip_p(i,length))
240 emit_quot(obj.value());
241 emit(parent->special_objects[JIT_2DIP]);
245 else if(fast_3dip_p(i,length))
247 emit_quot(obj.value());
248 emit(parent->special_objects[JIT_3DIP]);
255 /* Method dispatch */
256 if(mega_lookup_p(i,length))
258 emit_epilog(stack_frame);
260 emit_mega_cache_lookup(
261 array_nth(elements.untagged(),i),
262 untag_fixnum(array_nth(elements.untagged(),i + 1)),
263 array_nth(elements.untagged(),i + 2));
266 /* Non-optimizing compiler ignores declarations */
267 else if(declare_p(i,length))
280 set_position(length);
282 emit_epilog(stack_frame);
283 emit(parent->special_objects[JIT_RETURN]);
287 void factor_vm::set_quot_entry_point(quotation *quot, code_block *code)
290 quot->entry_point = code->entry_point();
293 /* Allocates memory */
294 code_block *factor_vm::jit_compile_quot(cell owner_, cell quot_, bool relocating)
296 data_root<object> owner(owner_,this);
297 data_root<quotation> quot(quot_,this);
299 quotation_jit compiler(owner.value(),true,relocating,this);
300 compiler.init_quotation(quot.value());
301 compiler.iterate_quotation();
303 code_block *compiled = compiler.to_code_block();
305 if(relocating) initialize_code_block(compiled);
310 void factor_vm::jit_compile_quot(cell quot_, bool relocating)
312 data_root<quotation> quot(quot_,this);
313 if(!quot_compiled_p(quot.untagged()))
315 code_block *compiled = jit_compile_quot(quot.value(),quot.value(),relocating);
316 set_quot_entry_point(quot.untagged(),compiled);
320 void factor_vm::primitive_jit_compile()
322 jit_compile_quot(ctx->pop(),true);
325 code_block *factor_vm::lazy_jit_compile_block()
327 return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->code;
330 /* push a new quotation on the stack */
331 void factor_vm::primitive_array_to_quotation()
333 quotation *quot = allot<quotation>(sizeof(quotation));
335 quot->array = ctx->peek();
336 quot->cached_effect = false_object;
337 quot->cache_counter = false_object;
338 set_quot_entry_point(quot,lazy_jit_compile_block());
340 ctx->replace(tag<quotation>(quot));
343 void factor_vm::primitive_quotation_code()
345 quotation *quot = untag_check<quotation>(ctx->pop());
347 ctx->push(from_unsigned_cell((cell)quot->code->entry_point()));
348 ctx->push(from_unsigned_cell((cell)quot->code + quot->code->size()));
351 /* Allocates memory */
352 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset)
354 data_root<quotation> quot(quot_,this);
355 data_root<array> array(quot->array,this);
357 quotation_jit compiler(quot.value(),false,false,this);
358 compiler.init_quotation(quot.value());
359 compiler.compute_position(offset);
360 compiler.iterate_quotation();
362 return compiler.get_position();
365 cell factor_vm::lazy_jit_compile(cell quot_)
367 data_root<quotation> quot(quot_,this);
369 assert(!quot_compiled_p(quot.untagged()));
371 code_block *compiled = jit_compile_quot(quot.value(),quot.value(),true);
372 set_quot_entry_point(quot.untagged(),compiled);
377 VM_C_API cell lazy_jit_compile(cell quot, factor_vm *parent)
379 return parent->lazy_jit_compile(quot);
382 bool factor_vm::quot_compiled_p(quotation *quot)
384 return quot->code != NULL && quot->code != lazy_jit_compile_block();
387 void factor_vm::primitive_quot_compiled_p()
389 tagged<quotation> quot(ctx->pop());
390 quot.untag_check(this);
391 ctx->push(tag_boolean(quot_compiled_p(quot.untagged())));
394 cell factor_vm::find_all_quotations()
396 return instances(QUOTATION_TYPE);
399 void factor_vm::initialize_all_quotations()
401 data_root<array> quotations(find_all_quotations(),this);
403 cell length = array_capacity(quotations.untagged());
404 for(cell i = 0; i < length; i++)
406 data_root<quotation> quot(array_nth(quotations.untagged(),i),this);
408 set_quot_entry_point(quot.untagged(),lazy_jit_compile_block());