]> gitweb.factorcode.org Git - factor.git/blob - vm/quotations.cpp
vm: strip out call-counting profiler
[factor.git] / vm / quotations.cpp
1 #include "master.hpp"
2
3 namespace factor
4 {
5
6 /* Simple non-optimizing compiler.
7
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.
10
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.
14
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.
18
19 It actually does do a little bit of very simple optimization:
20
21 1) Tail call optimization.
22
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
25 generated.
26
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.
29
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.
32
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. */
38
39 void quotation_jit::init_quotation(cell quot)
40 {
41         elements = untag<quotation>(quot)->array;
42 }
43
44 bool quotation_jit::primitive_call_p(cell i, cell length)
45 {
46         return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_PRIMITIVE_WORD];
47 }
48
49 bool quotation_jit::fast_if_p(cell i, cell length)
50 {
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];
54 }
55
56 bool quotation_jit::fast_dip_p(cell i, cell length)
57 {
58         return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DIP_WORD];
59 }
60
61 bool quotation_jit::fast_2dip_p(cell i, cell length)
62 {
63         return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_2DIP_WORD];
64 }
65
66 bool quotation_jit::fast_3dip_p(cell i, cell length)
67 {
68         return (i + 2) <= length && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_3DIP_WORD];
69 }
70
71 bool quotation_jit::mega_lookup_p(cell i, cell length)
72 {
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];
77 }
78
79 bool quotation_jit::declare_p(cell i, cell length)
80 {
81         return (i + 2) <= length
82                 && array_nth(elements.untagged(),i + 1) == parent->special_objects[JIT_DECLARE_WORD];
83 }
84
85 bool quotation_jit::word_stack_frame_p(cell obj)
86 {
87         // Subprimitives should be flagged with whether they require a stack frame.
88         // See #295.
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];
95 }
96
97 bool quotation_jit::stack_frame_p()
98 {
99         fixnum length = array_capacity(elements.untagged());
100
101         for(fixnum i = 0; i < length; i++)
102         {
103                 cell obj = array_nth(elements.untagged(),i);
104                 switch(tagged<object>(obj).type())
105                 {
106                 case WORD_TYPE:
107                         if(i != length - 1 || word_stack_frame_p(obj))
108                                 return true;
109                         break;
110                 case QUOTATION_TYPE:
111                         if(fast_dip_p(i,length) || fast_2dip_p(i,length) || fast_3dip_p(i,length))
112                                 return true;
113                         break;
114                 default:
115                         break;
116                 }
117         }
118
119         return false;
120 }
121
122 bool quotation_jit::trivial_quotation_p(array *elements)
123 {
124         return array_capacity(elements) == 1 && tagged<object>(array_nth(elements,0)).type_p(WORD_TYPE);
125 }
126
127 void quotation_jit::emit_epilog(bool stack_frame)
128 {
129         emit(parent->special_objects[JIT_SAFEPOINT]);
130         if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
131 }
132
133 void quotation_jit::emit_quot(cell quot_)
134 {
135         data_root<quotation> quot(quot_,parent);
136
137         array *elements = untag<array>(quot->array);
138
139         /* If the quotation consists of a single word, compile a direct call
140         to the word. */
141         if(trivial_quotation_p(elements))
142                 literal(array_nth(elements,0));
143         else
144         {
145                 if(compiling) parent->jit_compile_quot(quot.value(),relocate);
146                 literal(quot.value());
147         }
148 }
149
150 /* Allocates memory */
151 void quotation_jit::iterate_quotation()
152 {
153         bool stack_frame = stack_frame_p();
154
155         set_position(0);
156
157         if(stack_frame)
158                 emit(parent->special_objects[JIT_PROLOG]);
159
160         cell i;
161         cell length = array_capacity(elements.untagged());
162         bool tail_call = false;
163
164         for(i = 0; i < length; i++)
165         {
166                 set_position(i);
167
168                 data_root<object> obj(array_nth(elements.untagged(),i),parent);
169
170                 switch(obj.type())
171                 {
172                 case WORD_TYPE:
173                         /* Sub-primitives */
174                         if(to_boolean(obj.as<word>()->subprimitive))
175                         {
176                                 tail_call = emit_subprimitive(obj.value(), /* word */
177                                         i == length - 1, /* tail_call_p */
178                                         stack_frame); /* stack_frame_p */
179                         }
180                         /* Everything else */
181                         else if(i == length - 1)
182                         {
183                                 emit_epilog(stack_frame);
184                                 tail_call = true;
185                                 word_jump(obj.value());
186                         }
187                         else
188                                 word_call(obj.value());
189                         break;
190                 case WRAPPER_TYPE:
191                         push(obj.as<wrapper>()->object);
192                         break;
193                 case BYTE_ARRAY_TYPE:
194                         /* Primitive calls */
195                         if(primitive_call_p(i,length))
196                         {
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 */
200 #ifdef FACTOR_X86
201                                 parameter(tag_fixnum(0));
202 #endif
203                                 parameter(obj.value());
204                                 parameter(false_object);
205 #ifdef FACTOR_PPC_TOC
206                                 parameter(obj.value());
207                                 parameter(false_object);
208 #endif
209                                 emit(parent->special_objects[JIT_PRIMITIVE]);
210
211                                 i++;
212                         }
213                         else
214                                 push(obj.value());
215                         break;
216                 case QUOTATION_TYPE:
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))
220                         {
221                                 emit_epilog(stack_frame);
222                                 tail_call = true;
223
224                                 emit_quot(array_nth(elements.untagged(),i));
225                                 emit_quot(array_nth(elements.untagged(),i + 1));
226                                 emit(parent->special_objects[JIT_IF]);
227
228                                 i += 2;
229                         }
230                         /* dip */
231                         else if(fast_dip_p(i,length))
232                         {
233                                 emit_quot(obj.value());
234                                 emit(parent->special_objects[JIT_DIP]);
235                                 i++;
236                         }
237                         /* 2dip */
238                         else if(fast_2dip_p(i,length))
239                         {
240                                 emit_quot(obj.value());
241                                 emit(parent->special_objects[JIT_2DIP]);
242                                 i++;
243                         }
244                         /* 3dip */
245                         else if(fast_3dip_p(i,length))
246                         {
247                                 emit_quot(obj.value());
248                                 emit(parent->special_objects[JIT_3DIP]);
249                                 i++;
250                         }
251                         else
252                                 push(obj.value());
253                         break;
254                 case ARRAY_TYPE:
255                         /* Method dispatch */
256                         if(mega_lookup_p(i,length))
257                         {
258                                 emit_epilog(stack_frame);
259                                 tail_call = true;
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));
264                                 i += 3;
265                         }
266                         /* Non-optimizing compiler ignores declarations */
267                         else if(declare_p(i,length))
268                                 i++;
269                         else
270                                 push(obj.value());
271                         break;
272                 default:
273                         push(obj.value());
274                         break;
275                 }
276         }
277
278         if(!tail_call)
279         {
280                 set_position(length);
281
282                 emit_epilog(stack_frame);
283                 emit(parent->special_objects[JIT_RETURN]);
284         }
285 }
286
287 /* Allocates memory */
288 code_block *factor_vm::jit_compile_quot(cell owner_, cell quot_, bool relocating)
289 {
290         data_root<object> owner(owner_,this);
291         data_root<quotation> quot(quot_,this);
292
293         quotation_jit compiler(owner.value(),true,relocating,this);
294         compiler.init_quotation(quot.value());
295         compiler.iterate_quotation();
296
297         code_block *compiled = compiler.to_code_block();
298
299         if(relocating) initialize_code_block(compiled);
300
301         return compiled;
302 }
303
304 void factor_vm::jit_compile_quot(cell quot_, bool relocating)
305 {
306         data_root<quotation> quot(quot_,this);
307         if(!quot_compiled_p(quot.untagged()))
308         {
309                 code_block *compiled = jit_compile_quot(quot.value(),quot.value(),relocating);
310                 quot.untagged()->entry_point = compiled->entry_point();
311         }
312 }
313
314 void factor_vm::primitive_jit_compile()
315 {
316         jit_compile_quot(ctx->pop(),true);
317 }
318
319 void *factor_vm::lazy_jit_compile_entry_point()
320 {
321         return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->entry_point;
322 }
323
324 /* push a new quotation on the stack */
325 void factor_vm::primitive_array_to_quotation()
326 {
327         quotation *quot = allot<quotation>(sizeof(quotation));
328
329         quot->array = ctx->peek();
330         quot->cached_effect = false_object;
331         quot->cache_counter = false_object;
332         quot->entry_point = lazy_jit_compile_entry_point();
333
334         ctx->replace(tag<quotation>(quot));
335 }
336
337 void factor_vm::primitive_quotation_code()
338 {
339         quotation *quot = untag_check<quotation>(ctx->pop());
340
341         ctx->push(from_unsigned_cell((cell)quot->entry_point));
342         ctx->push(from_unsigned_cell((cell)quot->code() + quot->code()->size()));
343 }
344
345 /* Allocates memory */
346 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset)
347 {
348         data_root<quotation> quot(quot_,this);
349         data_root<array> array(quot->array,this);
350
351         quotation_jit compiler(quot.value(),false,false,this);
352         compiler.init_quotation(quot.value());
353         compiler.compute_position(offset);
354         compiler.iterate_quotation();
355
356         return compiler.get_position();
357 }
358
359 cell factor_vm::lazy_jit_compile(cell quot_)
360 {
361         data_root<quotation> quot(quot_,this);
362
363         assert(!quot_compiled_p(quot.untagged()));
364
365         code_block *compiled = jit_compile_quot(quot.value(),quot.value(),true);
366         quot.untagged()->entry_point = compiled->entry_point();
367
368         return quot.value();
369 }
370
371 VM_C_API cell lazy_jit_compile(cell quot, factor_vm *parent)
372 {
373         return parent->lazy_jit_compile(quot);
374 }
375
376 bool factor_vm::quot_compiled_p(quotation *quot)
377 {
378         return quot->entry_point != NULL && quot->entry_point != lazy_jit_compile_entry_point();
379 }
380
381 void factor_vm::primitive_quot_compiled_p()
382 {
383         tagged<quotation> quot(ctx->pop());
384         quot.untag_check(this);
385         ctx->push(tag_boolean(quot_compiled_p(quot.untagged())));
386 }
387
388 cell factor_vm::find_all_quotations()
389 {
390         return instances(QUOTATION_TYPE);
391 }
392
393 void factor_vm::initialize_all_quotations()
394 {
395         data_root<array> quotations(find_all_quotations(),this);
396
397         cell length = array_capacity(quotations.untagged());
398         for(cell i = 0; i < length; i++)
399         {
400                 data_root<quotation> quot(array_nth(quotations.untagged(),i),this);
401                 if(!quot->entry_point)
402                         quot.untagged()->entry_point = lazy_jit_compile_entry_point();
403         }
404 }
405
406 }