]> gitweb.factorcode.org Git - factor.git/blob - vm/quotations.cpp
Store VM object in a register on x86-64
[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         return to_boolean(untag<word>(obj)->subprimitive)
88                 || obj == parent->special_objects[JIT_PRIMITIVE_WORD];
89 }
90
91 bool quotation_jit::stack_frame_p()
92 {
93         fixnum length = array_capacity(elements.untagged());
94
95         for(fixnum i = 0; i < length; i++)
96         {
97                 cell obj = array_nth(elements.untagged(),i);
98                 switch(tagged<object>(obj).type())
99                 {
100                 case WORD_TYPE:
101                         if(i != length - 1 || word_stack_frame_p(obj))
102                                 return true;
103                         break;
104                 case QUOTATION_TYPE:
105                         if(fast_dip_p(i,length) || fast_2dip_p(i,length) || fast_3dip_p(i,length))
106                                 return true;
107                         break;
108                 default:
109                         break;
110                 }
111         }
112
113         return false;
114 }
115
116 bool quotation_jit::trivial_quotation_p(array *elements)
117 {
118         return array_capacity(elements) == 1 && tagged<object>(array_nth(elements,0)).type_p(WORD_TYPE);
119 }
120
121 void quotation_jit::emit_quot(cell quot_)
122 {
123         data_root<quotation> quot(quot_,parent);
124
125         array *elements = untag<array>(quot->array);
126
127         /* If the quotation consists of a single word, compile a direct call
128         to the word. */
129         if(trivial_quotation_p(elements))
130                 literal(array_nth(elements,0));
131         else
132         {
133                 if(compiling) parent->jit_compile_quot(quot.value(),relocate);
134                 literal(quot.value());
135         }
136 }
137
138 /* Allocates memory */
139 void quotation_jit::iterate_quotation()
140 {
141         bool stack_frame = stack_frame_p();
142
143         set_position(0);
144
145         if(stack_frame)
146                 emit(parent->special_objects[JIT_PROLOG]);
147
148         cell i;
149         cell length = array_capacity(elements.untagged());
150         bool tail_call = false;
151
152         for(i = 0; i < length; i++)
153         {
154                 set_position(i);
155
156                 data_root<object> obj(array_nth(elements.untagged(),i),parent);
157
158                 switch(obj.type())
159                 {
160                 case WORD_TYPE:
161                         /* Sub-primitives */
162                         if(to_boolean(obj.as<word>()->subprimitive))
163                         {
164                                 tail_call = emit_subprimitive(obj.value(), /* word */
165                                         i == length - 1, /* tail_call_p */
166                                         stack_frame); /* stack_frame_p */
167                         }
168                         /* Everything else */
169                         else if(i == length - 1)
170                         {
171                                 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
172                                 tail_call = true;
173                                 word_jump(obj.value());
174                         }
175                         else
176                                 word_call(obj.value());
177                         break;
178                 case WRAPPER_TYPE:
179                         push(obj.as<wrapper>()->object);
180                         break;
181                 case FIXNUM_TYPE:
182                         /* Primitive calls */
183                         if(primitive_call_p(i,length))
184                         {
185                                 /* On x86-64 and PowerPC, the VM pointer is stored in
186                                 a register; on other platforms, the RT_VM relocation
187                                 is used and it needs an offset parameter */
188 #ifdef FACTOR_X86
189                                 parameter(tag_fixnum(0));
190 #endif
191                                 parameter(obj.value());
192                                 emit(parent->special_objects[JIT_PRIMITIVE]);
193
194                                 i++;
195                         }
196                         else
197                                 push(obj.value());
198                         break;
199                 case QUOTATION_TYPE:
200                         /* 'if' preceeded by two literal quotations (this is why if and ? are
201                            mutually recursive in the library, but both still work) */
202                         if(fast_if_p(i,length))
203                         {
204                                 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
205                                 tail_call = true;
206
207                                 emit_quot(array_nth(elements.untagged(),i));
208                                 emit_quot(array_nth(elements.untagged(),i + 1));
209                                 emit(parent->special_objects[JIT_IF]);
210
211                                 i += 2;
212                         }
213                         /* dip */
214                         else if(fast_dip_p(i,length))
215                         {
216                                 emit_quot(obj.value());
217                                 emit(parent->special_objects[JIT_DIP]);
218                                 i++;
219                         }
220                         /* 2dip */
221                         else if(fast_2dip_p(i,length))
222                         {
223                                 emit_quot(obj.value());
224                                 emit(parent->special_objects[JIT_2DIP]);
225                                 i++;
226                         }
227                         /* 3dip */
228                         else if(fast_3dip_p(i,length))
229                         {
230                                 emit_quot(obj.value());
231                                 emit(parent->special_objects[JIT_3DIP]);
232                                 i++;
233                         }
234                         else
235                                 push(obj.value());
236                         break;
237                 case ARRAY_TYPE:
238                         /* Method dispatch */
239                         if(mega_lookup_p(i,length))
240                         {
241                                 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
242                                 tail_call = true;
243                                 emit_mega_cache_lookup(
244                                         array_nth(elements.untagged(),i),
245                                         untag_fixnum(array_nth(elements.untagged(),i + 1)),
246                                         array_nth(elements.untagged(),i + 2));
247                                 i += 3;
248                         }
249                         /* Non-optimizing compiler ignores declarations */
250                         else if(declare_p(i,length))
251                                 i++;
252                         else
253                                 push(obj.value());
254                         break;
255                 default:
256                         push(obj.value());
257                         break;
258                 }
259         }
260
261         if(!tail_call)
262         {
263                 set_position(length);
264
265                 if(stack_frame) emit(parent->special_objects[JIT_EPILOG]);
266                 emit(parent->special_objects[JIT_RETURN]);
267         }
268 }
269
270 void factor_vm::set_quot_xt(quotation *quot, code_block *code)
271 {
272         quot->code = code;
273         quot->xt = code->xt();
274 }
275
276 /* Allocates memory */
277 code_block *factor_vm::jit_compile_quot(cell owner_, cell quot_, bool relocating)
278 {
279         data_root<object> owner(owner_,this);
280         data_root<quotation> quot(quot_,this);
281
282         quotation_jit compiler(owner.value(),true,relocating,this);
283         compiler.init_quotation(quot.value());
284         compiler.iterate_quotation();
285
286         code_block *compiled = compiler.to_code_block();
287
288         if(relocating) initialize_code_block(compiled);
289
290         return compiled;
291 }
292
293 void factor_vm::jit_compile_quot(cell quot_, bool relocating)
294 {
295         data_root<quotation> quot(quot_,this);
296         if(!quot_compiled_p(quot.untagged()))
297         {
298                 code_block *compiled = jit_compile_quot(quot.value(),quot.value(),relocating);
299                 set_quot_xt(quot.untagged(),compiled);
300         }
301 }
302
303 void factor_vm::primitive_jit_compile()
304 {
305         jit_compile_quot(ctx->pop(),true);
306 }
307
308 code_block *factor_vm::lazy_jit_compile_block()
309 {
310         return untag<word>(special_objects[LAZY_JIT_COMPILE_WORD])->code;
311 }
312
313 /* push a new quotation on the stack */
314 void factor_vm::primitive_array_to_quotation()
315 {
316         quotation *quot = allot<quotation>(sizeof(quotation));
317
318         quot->array = ctx->peek();
319         quot->cached_effect = false_object;
320         quot->cache_counter = false_object;
321         set_quot_xt(quot,lazy_jit_compile_block());
322
323         ctx->replace(tag<quotation>(quot));
324 }
325
326 void factor_vm::primitive_quotation_xt()
327 {
328         quotation *quot = untag_check<quotation>(ctx->peek());
329         ctx->replace(allot_cell((cell)quot->xt));
330 }
331
332 /* Allocates memory */
333 fixnum factor_vm::quot_code_offset_to_scan(cell quot_, cell offset)
334 {
335         data_root<quotation> quot(quot_,this);
336         data_root<array> array(quot->array,this);
337
338         quotation_jit compiler(quot.value(),false,false,this);
339         compiler.init_quotation(quot.value());
340         compiler.compute_position(offset);
341         compiler.iterate_quotation();
342
343         return compiler.get_position();
344 }
345
346 cell factor_vm::lazy_jit_compile(cell quot_)
347 {
348         data_root<quotation> quot(quot_,this);
349         jit_compile_quot(quot.value(),true);
350         return quot.value();
351 }
352
353 VM_C_API cell lazy_jit_compile(cell quot, factor_vm *parent)
354 {
355         return parent->lazy_jit_compile(quot);
356 }
357
358 bool factor_vm::quot_compiled_p(quotation *quot)
359 {
360         return quot->code != NULL && quot->code != lazy_jit_compile_block();
361 }
362
363 void factor_vm::primitive_quot_compiled_p()
364 {
365         tagged<quotation> quot(ctx->pop());
366         quot.untag_check(this);
367         ctx->push(tag_boolean(quot_compiled_p(quot.untagged())));
368 }
369
370 cell factor_vm::find_all_quotations()
371 {
372         return instances(QUOTATION_TYPE);
373 }
374
375 void factor_vm::initialize_all_quotations()
376 {
377         data_root<array> quotations(find_all_quotations(),this);
378
379         cell length = array_capacity(quotations.untagged());
380         for(cell i = 0; i < length; i++)
381         {
382                 data_root<quotation> quot(array_nth(quotations.untagged(),i),this);
383                 if(!quot->code)
384                         set_quot_xt(quot.untagged(),lazy_jit_compile_block());
385         }
386 }
387
388 }