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 bool quotation_jit::safepoint_p() {
102 cell length = array_capacity(elements.untagged());
104 for (cell i = 0; i < length; i++) {
105 cell obj = array_nth(elements.untagged(), i);
106 switch (tagged<object>(obj).type()) {
108 if (!word_safepoint_p(obj))
119 bool quotation_jit::stack_frame_p() {
120 cell length = array_capacity(elements.untagged());
122 for (cell i = 0; i < length; i++) {
123 cell obj = array_nth(elements.untagged(), i);
124 if (tagged<object>(obj).type() == WORD_TYPE && !word_safepoint_p(obj))
131 bool quotation_jit::trivial_quotation_p(array* elements) {
132 return array_capacity(elements) == 1 &&
133 tagged<object>(array_nth(elements, 0)).type_p(WORD_TYPE);
136 /* Allocates memory (emit) */
137 void quotation_jit::emit_prolog(bool safepoint, bool stack_frame) {
139 emit(parent->special_objects[JIT_SAFEPOINT]);
141 emit(parent->special_objects[JIT_PROLOG]);
144 /* Allocates memory (emit) */
145 void quotation_jit::emit_epilog(bool safepoint, bool stack_frame) {
147 emit(parent->special_objects[JIT_SAFEPOINT]);
149 emit(parent->special_objects[JIT_EPILOG]);
152 /* Allocates memory conditionally */
153 void quotation_jit::emit_quotation(cell quot_) {
154 data_root<quotation> quot(quot_, parent);
156 array* elements = untag<array>(quot->array);
158 /* If the quotation consists of a single word, compile a direct call
160 if (trivial_quotation_p(elements))
161 literal(array_nth(elements, 0));
164 parent->jit_compile_quotation(quot.value(), relocate);
165 literal(quot.value());
169 /* Allocates memory (parameter(), literal(), emit_prolog, emit_with_literal)*/
170 void quotation_jit::iterate_quotation() {
171 bool safepoint = safepoint_p();
172 bool stack_frame = stack_frame_p();
176 emit_prolog(safepoint, stack_frame);
179 cell length = array_capacity(elements.untagged());
180 bool tail_call = false;
182 for (i = 0; i < length; i++) {
185 data_root<object> obj(array_nth(elements.untagged(), i), parent);
187 switch (obj.type()) {
190 if (to_boolean(obj.as<word>()->subprimitive)) {
191 tail_call = emit_subprimitive(obj.value(), /* word */
192 i == length - 1, /* tail_call_p */
193 stack_frame); /* stack_frame_p */
194 } /* Everything else */
195 else if (i == length - 1) {
196 emit_epilog(safepoint, stack_frame);
198 word_jump(obj.value());
200 word_call(obj.value());
203 push(obj.as<wrapper>()->object);
205 case BYTE_ARRAY_TYPE:
206 /* Primitive calls */
207 if (primitive_call_p(i, length)) {
208 /* On x86-64 and PowerPC, the VM pointer is stored in
209 a register; on other platforms, the RT_VM relocation
210 is used and it needs an offset parameter */
212 parameter(tag_fixnum(0));
214 parameter(obj.value());
215 parameter(false_object);
216 #ifdef FACTOR_PPC_TOC
217 parameter(obj.value());
218 parameter(false_object);
220 emit(parent->special_objects[JIT_PRIMITIVE]);
227 /* 'if' preceded by two literal quotations (this is why if and ? are
228 mutually recursive in the library, but both still work) */
229 if (fast_if_p(i, length)) {
230 emit_epilog(safepoint, stack_frame);
233 emit_quotation(array_nth(elements.untagged(), i));
234 emit_quotation(array_nth(elements.untagged(), i + 1));
235 emit(parent->special_objects[JIT_IF]);
239 else if (fast_dip_p(i, length)) {
240 emit_quotation(obj.value());
241 emit(parent->special_objects[JIT_DIP]);
244 else if (fast_2dip_p(i, length)) {
245 emit_quotation(obj.value());
246 emit(parent->special_objects[JIT_2DIP]);
249 else if (fast_3dip_p(i, length)) {
250 emit_quotation(obj.value());
251 emit(parent->special_objects[JIT_3DIP]);
257 /* Method dispatch */
258 if (mega_lookup_p(i, length)) {
259 fixnum index = untag_fixnum(array_nth(elements.untagged(), i + 1));
260 /* Load the object from the datastack, then remove our stack frame. */
261 emit_with_literal(parent->special_objects[PIC_LOAD],
262 tag_fixnum(-index * sizeof(cell)));
263 emit_epilog(safepoint, stack_frame);
266 emit_mega_cache_lookup(array_nth(elements.untagged(), i), index,
267 array_nth(elements.untagged(), i + 2));
269 } /* Non-optimizing compiler ignores declarations */
270 else if (declare_p(i, length))
282 set_position(length);
284 emit_epilog(safepoint, stack_frame);
285 emit(parent->special_objects[JIT_RETURN]);
289 cell quotation_jit::word_stack_frame_size(cell obj) {
290 if (special_subprimitive_p(obj))
291 return SIGNAL_HANDLER_STACK_FRAME_SIZE;
293 return JIT_FRAME_SIZE;
296 /* Allocates memory */
297 code_block* factor_vm::jit_compile_quotation(cell owner_, cell quot_,
299 data_root<object> owner(owner_, this);
300 data_root<quotation> quot(quot_, this);
302 quotation_jit compiler(owner.value(), true, relocating, this);
303 compiler.init_quotation(quot.value());
304 compiler.iterate_quotation();
306 cell frame_size = compiler.word_stack_frame_size(owner_);
308 code_block* compiled = compiler.to_code_block(frame_size);
311 initialize_code_block(compiled);
316 /* Allocates memory */
317 void factor_vm::jit_compile_quotation(cell quot_, bool relocating) {
318 data_root<quotation> quot(quot_, this);
319 if (!quotation_compiled_p(quot.untagged())) {
320 code_block* compiled =
321 jit_compile_quotation(quot.value(), quot.value(), relocating);
322 quot.untagged()->entry_point = compiled->entry_point();
326 /* Allocates memory */
327 void factor_vm::primitive_jit_compile() {
328 jit_compile_quotation(ctx->pop(), true);
331 cell factor_vm::lazy_jit_compile_entry_point() {
332 return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->entry_point;
335 /* push a new quotation on the stack */
336 /* Allocates memory */
337 void factor_vm::primitive_array_to_quotation() {
338 quotation* quot = allot<quotation>(sizeof(quotation));
340 quot->array = ctx->peek();
341 quot->cached_effect = false_object;
342 quot->cache_counter = false_object;
343 quot->entry_point = lazy_jit_compile_entry_point();
345 ctx->replace(tag<quotation>(quot));
348 /* Allocates memory (from_unsigned_cell) */
349 void factor_vm::primitive_quotation_code() {
350 data_root<quotation> quot(ctx->pop(), this);
352 ctx->push(from_unsigned_cell(quot->entry_point));
353 ctx->push(from_unsigned_cell((cell)quot->code() + quot->code()->size()));
356 /* Allocates memory */
357 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset) {
358 data_root<quotation> quot(quot_, this);
359 data_root<array> array(quot->array, this);
361 quotation_jit compiler(quot.value(), false, false, this);
362 compiler.init_quotation(quot.value());
363 compiler.compute_position(offset);
364 compiler.iterate_quotation();
366 return compiler.get_position();
369 /* Allocates memory */
370 cell factor_vm::lazy_jit_compile(cell quot_) {
371 data_root<quotation> quot(quot_, this);
373 FACTOR_ASSERT(!quotation_compiled_p(quot.untagged()));
375 code_block* compiled =
376 jit_compile_quotation(quot.value(), quot.value(), true);
377 quot.untagged()->entry_point = compiled->entry_point();
382 /* Allocates memory */
383 VM_C_API cell lazy_jit_compile(cell quot, factor_vm* parent) {
384 return parent->lazy_jit_compile(quot);
387 bool factor_vm::quotation_compiled_p(quotation* quot) {
388 return quot->entry_point != 0 &&
389 quot->entry_point != lazy_jit_compile_entry_point();
392 void factor_vm::primitive_quotation_compiled_p() {
393 quotation* quot = untag_check<quotation>(ctx->pop());
394 ctx->push(tag_boolean(quotation_compiled_p(quot)));
397 /* Allocates memory */
398 void factor_vm::initialize_all_quotations() {
399 cell all_quots = instances(QUOTATION_TYPE);
400 data_root<array> quotations(all_quots, this);
402 cell length = array_capacity(quotations.untagged());
403 for (cell i = 0; i < length; i++) {
404 data_root<quotation> quot(array_nth(quotations.untagged(), i), this);
405 if (!quot->entry_point)
406 quot.untagged()->entry_point = lazy_jit_compile_entry_point();