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]);
154 cell length = array_capacity(elements.untagged());
155 bool tail_call = false;
157 for (i = 0; i < length; i++) {
160 data_root<object> obj(array_nth(elements.untagged(), i), parent);
162 switch (obj.type()) {
165 if (to_boolean(obj.as<word>()->subprimitive)) {
166 tail_call = emit_subprimitive(obj.value(), /* word */
167 i == length - 1, /* tail_call_p */
168 no_non_safepoint_words); /* stack_frame_p */
169 } /* Everything else */
170 else if (i == length - 1) {
171 emit_epilog(no_non_safepoint_words);
173 word_jump(obj.value());
175 word_call(obj.value());
178 push(obj.as<wrapper>()->object);
180 case BYTE_ARRAY_TYPE:
181 /* Primitive calls */
182 if (primitive_call_p(i, length)) {
183 /* On x86-64 and PowerPC, the VM pointer is stored in
184 a register; on other platforms, the RT_VM relocation
185 is used and it needs an offset parameter */
187 parameter(tag_fixnum(0));
189 parameter(obj.value());
190 parameter(false_object);
191 #ifdef FACTOR_PPC_TOC
192 parameter(obj.value());
193 parameter(false_object);
195 emit(parent->special_objects[JIT_PRIMITIVE]);
202 /* 'if' preceded by two literal quotations (this is why if and ? are
203 mutually recursive in the library, but both still work) */
204 if (fast_if_p(i, length)) {
205 emit_epilog(no_non_safepoint_words);
208 emit_quotation(array_nth(elements.untagged(), i));
209 emit_quotation(array_nth(elements.untagged(), i + 1));
210 emit(parent->special_objects[JIT_IF]);
214 else if (fast_dip_p(i, length)) {
215 emit_quotation(obj.value());
216 emit(parent->special_objects[JIT_DIP]);
219 else if (fast_2dip_p(i, length)) {
220 emit_quotation(obj.value());
221 emit(parent->special_objects[JIT_2DIP]);
224 else if (fast_3dip_p(i, length)) {
225 emit_quotation(obj.value());
226 emit(parent->special_objects[JIT_3DIP]);
232 /* Method dispatch */
233 if (mega_lookup_p(i, length)) {
234 fixnum index = untag_fixnum(array_nth(elements.untagged(), i + 1));
235 /* Load the object from the datastack, then remove our stack frame. */
236 emit_with_literal(parent->special_objects[PIC_LOAD],
237 tag_fixnum(-index * sizeof(cell)));
238 emit_epilog(no_non_safepoint_words);
241 emit_mega_cache_lookup(array_nth(elements.untagged(), i), index,
242 array_nth(elements.untagged(), i + 2));
244 } /* Non-optimizing compiler ignores declarations */
245 else if (declare_p(i, length))
257 set_position(length);
258 emit_epilog(no_non_safepoint_words);
259 emit(parent->special_objects[JIT_RETURN]);
263 cell quotation_jit::word_stack_frame_size(cell obj) {
264 if (special_subprimitive_p(obj))
265 return SIGNAL_HANDLER_STACK_FRAME_SIZE;
267 return JIT_FRAME_SIZE;
270 /* Allocates memory */
271 code_block* factor_vm::jit_compile_quotation(cell owner_, cell quot_,
273 data_root<object> owner(owner_, this);
274 data_root<quotation> quot(quot_, this);
276 quotation_jit compiler(owner.value(), true, relocating, this);
277 compiler.init_quotation(quot.value());
278 compiler.iterate_quotation();
280 cell frame_size = compiler.word_stack_frame_size(owner_);
282 code_block* compiled = compiler.to_code_block(frame_size);
285 initialize_code_block(compiled);
290 /* Allocates memory */
291 void factor_vm::jit_compile_quotation(cell quot_, bool relocating) {
292 data_root<quotation> quot(quot_, this);
293 if (!quotation_compiled_p(quot.untagged())) {
294 code_block* compiled =
295 jit_compile_quotation(quot.value(), quot.value(), relocating);
296 quot.untagged()->entry_point = compiled->entry_point();
300 /* Allocates memory */
301 void factor_vm::primitive_jit_compile() {
302 jit_compile_quotation(ctx->pop(), true);
305 cell factor_vm::lazy_jit_compile_entry_point() {
306 return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->entry_point;
309 /* push a new quotation on the stack */
310 /* Allocates memory */
311 void factor_vm::primitive_array_to_quotation() {
312 quotation* quot = allot<quotation>(sizeof(quotation));
314 quot->array = ctx->peek();
315 quot->cached_effect = false_object;
316 quot->cache_counter = false_object;
317 quot->entry_point = lazy_jit_compile_entry_point();
319 ctx->replace(tag<quotation>(quot));
322 /* Allocates memory (from_unsigned_cell) */
323 void factor_vm::primitive_quotation_code() {
324 data_root<quotation> quot(ctx->pop(), this);
326 ctx->push(from_unsigned_cell(quot->entry_point));
327 ctx->push(from_unsigned_cell((cell)quot->code() + quot->code()->size()));
330 /* Allocates memory */
331 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset) {
332 data_root<quotation> quot(quot_, this);
333 data_root<array> array(quot->array, this);
335 quotation_jit compiler(quot.value(), false, false, this);
336 compiler.init_quotation(quot.value());
337 compiler.compute_position(offset);
338 compiler.iterate_quotation();
340 return compiler.get_position();
343 /* Allocates memory */
344 cell factor_vm::lazy_jit_compile(cell quot_) {
345 data_root<quotation> quot(quot_, this);
347 FACTOR_ASSERT(!quotation_compiled_p(quot.untagged()));
349 code_block* compiled =
350 jit_compile_quotation(quot.value(), quot.value(), true);
351 quot.untagged()->entry_point = compiled->entry_point();
356 /* Allocates memory */
357 VM_C_API cell lazy_jit_compile(cell quot, factor_vm* parent) {
358 return parent->lazy_jit_compile(quot);
361 bool factor_vm::quotation_compiled_p(quotation* quot) {
362 return quot->entry_point != 0 &&
363 quot->entry_point != lazy_jit_compile_entry_point();
366 void factor_vm::primitive_quotation_compiled_p() {
367 quotation* quot = untag_check<quotation>(ctx->pop());
368 ctx->push(tag_boolean(quotation_compiled_p(quot)));
371 /* Allocates memory */
372 void factor_vm::initialize_all_quotations() {
373 cell all_quots = instances(QUOTATION_TYPE);
374 data_root<array> quotations(all_quots, this);
376 cell length = array_capacity(quotations.untagged());
377 for (cell i = 0; i < length; i++) {
378 data_root<quotation> quot(array_nth(quotations.untagged(), i), this);
379 if (!quot->entry_point)
380 quot.untagged()->entry_point = lazy_jit_compile_entry_point();