5 /* Simple non-optimizing compiler.
7 This is one of the two compilers implementing Factor; the second one is written
8 in Factor and performs advanced optimizations. See
9 basis/compiler/compiler.factor.
11 The non-optimizing compiler compiles a quotation at a time by
12 concatenating machine code chunks; prolog, epilog, call word, jump to
13 word, etc. These machine code chunks are generated from Factor code in
14 basis/bootstrap/assembler/.
16 Calls to words and constant quotations (referenced by conditionals and dips)
17 are direct jumps to machine code blocks. Literals are also referenced directly
18 without going through the literal table.
20 It actually does do a little bit of very simple optimization:
22 1) Tail call optimization.
24 2) If a quotation is determined to not call any other words (except for a few
25 special words which are open-coded, see below), then no prolog/epilog is
28 3) When in tail position and immediately preceded by literal arguments, the
29 'if' is generated inline, instead of as a call to the 'if' word.
31 4) When preceded by a quotation, calls to 'dip', '2dip' and '3dip' are
32 open-coded as retain stack manipulation surrounding a subroutine call.
34 5) Sub-primitives are primitive words which are implemented in assembly and not
35 in the VM. They are open-coded and no subroutine call is generated. This
36 includes stack shufflers, some fixnum arithmetic words, and words such as tag,
37 slot and eq?. A primitive call is relatively expensive (two subroutine calls)
38 so this results in a big speedup for relatively little effort. */
40 void quotation_jit::init_quotation(cell quot) {
41 elements = untag<quotation>(quot)->array;
44 bool quotation_jit::primitive_call_p(cell i, cell length) {
45 return (i + 2) <= length && array_nth(elements.untagged(), i + 1) ==
46 parent->special_objects[JIT_PRIMITIVE_WORD];
49 bool quotation_jit::fast_if_p(cell i, cell length) {
50 return (i + 3) == length &&
51 tagged<object>(array_nth(elements.untagged(), i + 1))
52 .type_p(QUOTATION_TYPE) &&
53 array_nth(elements.untagged(), i + 2) ==
54 parent->special_objects[JIT_IF_WORD];
57 bool quotation_jit::fast_dip_p(cell i, cell length) {
58 return (i + 2) <= length && array_nth(elements.untagged(), i + 1) ==
59 parent->special_objects[JIT_DIP_WORD];
62 bool quotation_jit::fast_2dip_p(cell i, cell length) {
63 return (i + 2) <= length && array_nth(elements.untagged(), i + 1) ==
64 parent->special_objects[JIT_2DIP_WORD];
67 bool quotation_jit::fast_3dip_p(cell i, cell length) {
68 return (i + 2) <= length && array_nth(elements.untagged(), i + 1) ==
69 parent->special_objects[JIT_3DIP_WORD];
72 bool quotation_jit::mega_lookup_p(cell i, cell length) {
73 return (i + 4) <= length &&
74 tagged<object>(array_nth(elements.untagged(), i + 1))
75 .type_p(FIXNUM_TYPE) &&
76 tagged<object>(array_nth(elements.untagged(), i + 2))
77 .type_p(ARRAY_TYPE) &&
78 array_nth(elements.untagged(), i + 3) ==
79 parent->special_objects[MEGA_LOOKUP_WORD];
82 bool quotation_jit::declare_p(cell i, cell length) {
83 return (i + 2) <= length && array_nth(elements.untagged(), i + 1) ==
84 parent->special_objects[JIT_DECLARE_WORD];
87 bool quotation_jit::special_subprimitive_p(cell obj) {
88 // Subprimitives should be flagged with whether they require a stack frame.
90 return 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[UNWIND_NATIVE_FRAMES_WORD];
97 bool quotation_jit::word_safepoint_p(cell obj) {
98 return !special_subprimitive_p(obj);
101 /* true if there are no non-safepoint words in the quoation... */
102 bool quotation_jit::no_non_safepoint_words_p() {
103 cell length = array_capacity(elements.untagged());
104 for (cell i = 0; i < length; i++) {
105 cell obj = array_nth(elements.untagged(), i);
106 if (tagged<object>(obj).type() == WORD_TYPE && !word_safepoint_p(obj))
112 bool quotation_jit::trivial_quotation_p(array* elements) {
113 return array_capacity(elements) == 1 &&
114 tagged<object>(array_nth(elements, 0)).type_p(WORD_TYPE);
117 /* Allocates memory (emit) */
118 void quotation_jit::emit_epilog(bool needed) {
120 emit(parent->special_objects[JIT_SAFEPOINT]);
121 emit(parent->special_objects[JIT_EPILOG]);
125 /* Allocates memory conditionally */
126 void quotation_jit::emit_quotation(cell quot_) {
127 data_root<quotation> quot(quot_, parent);
129 array* elements = untag<array>(quot->array);
131 /* If the quotation consists of a single word, compile a direct call
133 if (trivial_quotation_p(elements))
134 literal(array_nth(elements, 0));
137 parent->jit_compile_quotation(quot.value(), relocate);
138 literal(quot.value());
142 /* Allocates memory (parameter(), literal(), emit_epilog, emit_with_literal)*/
143 void quotation_jit::iterate_quotation() {
144 bool no_non_safepoint_words = no_non_safepoint_words_p();
148 if (no_non_safepoint_words) {
149 emit(parent->special_objects[JIT_SAFEPOINT]);
150 emit(parent->special_objects[JIT_PROLOG]);
153 cell length = array_capacity(elements.untagged());
154 bool tail_call = false;
156 for (cell i = 0; i < length; i++) {
159 data_root<object> obj(array_nth(elements.untagged(), i), parent);
161 switch (obj.type()) {
164 if (to_boolean(obj.as<word>()->subprimitive)) {
165 tail_call = emit_subprimitive(obj.value(), /* word */
166 i == length - 1, /* tail_call_p */
167 no_non_safepoint_words); /* stack_frame_p */
168 } /* Everything else */
169 else if (i == length - 1) {
170 emit_epilog(no_non_safepoint_words);
172 word_jump(obj.value());
174 word_call(obj.value());
177 push(obj.as<wrapper>()->object);
179 case BYTE_ARRAY_TYPE:
180 /* Primitive calls */
181 if (primitive_call_p(i, length)) {
182 /* On x86-64 and PowerPC, the VM pointer is stored in
183 a register; on other platforms, the RT_VM relocation
184 is used and it needs an offset parameter */
186 parameter(tag_fixnum(0));
188 parameter(obj.value());
189 parameter(false_object);
190 #ifdef FACTOR_PPC_TOC
191 parameter(obj.value());
192 parameter(false_object);
194 emit(parent->special_objects[JIT_PRIMITIVE]);
201 /* 'if' preceded by two literal quotations (this is why if and ? are
202 mutually recursive in the library, but both still work) */
203 if (fast_if_p(i, length)) {
204 emit_epilog(no_non_safepoint_words);
207 emit_quotation(array_nth(elements.untagged(), i));
208 emit_quotation(array_nth(elements.untagged(), i + 1));
209 emit(parent->special_objects[JIT_IF]);
213 else if (fast_dip_p(i, length)) {
214 emit_quotation(obj.value());
215 emit(parent->special_objects[JIT_DIP]);
218 else if (fast_2dip_p(i, length)) {
219 emit_quotation(obj.value());
220 emit(parent->special_objects[JIT_2DIP]);
223 else if (fast_3dip_p(i, length)) {
224 emit_quotation(obj.value());
225 emit(parent->special_objects[JIT_3DIP]);
231 /* Method dispatch */
232 if (mega_lookup_p(i, length)) {
233 fixnum index = untag_fixnum(array_nth(elements.untagged(), i + 1));
234 /* Load the object from the datastack, then remove our stack frame. */
235 emit_with_literal(parent->special_objects[PIC_LOAD],
236 tag_fixnum(-index * sizeof(cell)));
237 emit_epilog(no_non_safepoint_words);
240 emit_mega_cache_lookup(array_nth(elements.untagged(), i), index,
241 array_nth(elements.untagged(), i + 2));
243 } /* Non-optimizing compiler ignores declarations */
244 else if (declare_p(i, length))
256 set_position(length);
257 emit_epilog(no_non_safepoint_words);
258 emit(parent->special_objects[JIT_RETURN]);
262 cell quotation_jit::word_stack_frame_size(cell obj) {
263 if (special_subprimitive_p(obj))
264 return SIGNAL_HANDLER_STACK_FRAME_SIZE;
266 return JIT_FRAME_SIZE;
269 /* Allocates memory */
270 code_block* factor_vm::jit_compile_quotation(cell owner_, cell quot_,
272 data_root<object> owner(owner_, this);
273 data_root<quotation> quot(quot_, this);
275 quotation_jit compiler(owner.value(), true, relocating, this);
276 compiler.init_quotation(quot.value());
277 compiler.iterate_quotation();
279 cell frame_size = compiler.word_stack_frame_size(owner_);
281 code_block* compiled = compiler.to_code_block(frame_size);
284 initialize_code_block(compiled);
289 /* Allocates memory */
290 void factor_vm::jit_compile_quotation(cell quot_, bool relocating) {
291 data_root<quotation> quot(quot_, this);
292 if (!quotation_compiled_p(quot.untagged())) {
293 code_block* compiled =
294 jit_compile_quotation(quot.value(), quot.value(), relocating);
295 quot.untagged()->entry_point = compiled->entry_point();
299 /* Allocates memory */
300 void factor_vm::primitive_jit_compile() {
301 jit_compile_quotation(ctx->pop(), true);
304 cell factor_vm::lazy_jit_compile_entry_point() {
305 return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->entry_point;
308 /* push a new quotation on the stack */
309 /* Allocates memory */
310 void factor_vm::primitive_array_to_quotation() {
311 quotation* quot = allot<quotation>(sizeof(quotation));
313 quot->array = ctx->peek();
314 quot->cached_effect = false_object;
315 quot->cache_counter = false_object;
316 quot->entry_point = lazy_jit_compile_entry_point();
318 ctx->replace(tag<quotation>(quot));
321 /* Allocates memory (from_unsigned_cell) */
322 void factor_vm::primitive_quotation_code() {
323 data_root<quotation> quot(ctx->pop(), this);
325 ctx->push(from_unsigned_cell(quot->entry_point));
326 ctx->push(from_unsigned_cell((cell)quot->code() + quot->code()->size()));
329 /* Allocates memory */
330 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset) {
331 data_root<quotation> quot(quot_, this);
332 data_root<array> array(quot->array, this);
334 quotation_jit compiler(quot.value(), false, false, this);
335 compiler.init_quotation(quot.value());
336 compiler.compute_position(offset);
337 compiler.iterate_quotation();
339 return compiler.get_position();
342 /* Allocates memory */
343 cell factor_vm::lazy_jit_compile(cell quot_) {
344 data_root<quotation> quot(quot_, this);
346 FACTOR_ASSERT(!quotation_compiled_p(quot.untagged()));
348 code_block* compiled =
349 jit_compile_quotation(quot.value(), quot.value(), true);
350 quot.untagged()->entry_point = compiled->entry_point();
355 /* Allocates memory */
356 VM_C_API cell lazy_jit_compile(cell quot, factor_vm* parent) {
357 return parent->lazy_jit_compile(quot);
360 bool factor_vm::quotation_compiled_p(quotation* quot) {
361 return quot->entry_point != 0 &&
362 quot->entry_point != lazy_jit_compile_entry_point();
365 void factor_vm::primitive_quotation_compiled_p() {
366 quotation* quot = untag_check<quotation>(ctx->pop());
367 ctx->push(tag_boolean(quotation_compiled_p(quot)));
370 /* Allocates memory */
371 void factor_vm::initialize_all_quotations() {
372 cell all_quots = instances(QUOTATION_TYPE);
373 data_root<array> quotations(all_quots, this);
375 cell length = array_capacity(quotations.untagged());
376 for (cell i = 0; i < length; i++) {
377 data_root<quotation> quot(array_nth(quotations.untagged(), i), this);
378 if (!quot->entry_point)
379 quot.untagged()->entry_point = lazy_jit_compile_entry_point();