BlockContext's caller is now stored in sender field
[panda.git] / src / st-cpu.c
blobf127a18e105c4629f81a9de1edba310f446a1a19
1 /*
2 * st-cpu.c
4 * Copyright (C) 2008 Vincent Geddes
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 #include "st-types.h"
26 #include "st-compiler.h"
27 #include "st-universe.h"
28 #include "st-dictionary.h"
29 #include "st-symbol.h"
30 #include "st-object.h"
31 #include "st-behavior.h"
32 #include "st-context.h"
33 #include "st-primitives.h"
34 #include "st-method.h"
35 #include "st-array.h"
36 #include "st-association.h"
37 #include "st-memory.h"
39 #include <stdlib.h>
40 #include <setjmp.h>
42 #ifdef __GNUC__
43 #define HAVE_COMPUTED_GOTO
44 #endif
46 static inline st_oop
47 method_context_new (void)
49 register struct st_cpu *cpu = &__cpu;
50 st_oop context;
51 st_uint temp_count;
52 st_oop *stack;
53 bool large;
55 large = st_method_get_large_context (cpu->new_method);
56 temp_count = st_method_get_arg_count (cpu->new_method) + st_method_get_temp_count (cpu->new_method);
58 context = st_memory_allocate_context (large);
60 ST_CONTEXT_PART_SENDER (context) = cpu->context;
61 ST_CONTEXT_PART_IP (context) = st_smi_new (0);
62 ST_CONTEXT_PART_SP (context) = st_smi_new (temp_count);
63 ST_METHOD_CONTEXT_RECEIVER (context) = cpu->message_receiver;
64 ST_METHOD_CONTEXT_METHOD (context) = cpu->new_method;
66 /* clear temporaries (and nothing above) */
67 stack = ST_METHOD_CONTEXT_STACK (context);
68 for (st_uint i=0; i < temp_count; i++)
69 stack[i] = ST_NIL;
71 return context;
74 static st_oop
75 block_context_new (st_uint initial_ip, st_uint argcount)
77 register struct st_cpu *cpu = &__cpu;
78 st_oop home;
79 st_oop context;
80 st_oop method;
81 st_oop *stack;
82 st_uint stack_size;
84 stack_size = 32;
86 context = st_memory_allocate (ST_SIZE_OOPS (struct st_block_context) + stack_size);
87 if (context == 0) {
88 st_memory_perform_gc ();
89 context = st_memory_allocate (ST_SIZE_OOPS (struct st_block_context) + stack_size);
90 st_assert (context != 0);
93 st_object_initialize_header (context, ST_BLOCK_CONTEXT_CLASS);
94 st_object_set_large_context (context, true);
96 if (ST_OBJECT_CLASS (cpu->context) == ST_BLOCK_CONTEXT_CLASS)
97 home = ST_BLOCK_CONTEXT_HOME (cpu->context);
98 else
99 home = cpu->context;
101 ST_CONTEXT_PART_SENDER (context) = ST_NIL;
102 ST_CONTEXT_PART_IP (context) = st_smi_new (0);
103 ST_CONTEXT_PART_SP (context) = st_smi_new (0);
105 ST_BLOCK_CONTEXT_INITIALIP (context) = st_smi_new (initial_ip);
106 ST_BLOCK_CONTEXT_ARGCOUNT (context) = st_smi_new (argcount);
107 ST_BLOCK_CONTEXT_HOME (context) = home;
109 return context;
112 static void
113 create_actual_message (void)
115 register struct st_cpu *cpu = &__cpu;
116 st_oop *elements;
117 st_oop message;
118 st_oop array;
120 array = st_object_new_arrayed (ST_ARRAY_CLASS, cpu->message_argcount);
121 elements = st_array_elements (array);
122 for (st_uint i = 0; i < cpu->message_argcount; i++)
123 elements[i] = cpu->stack[cpu->sp - cpu->message_argcount + i];
125 cpu->sp -= cpu->message_argcount;
127 message = st_object_new (ST_MESSAGE_CLASS);
128 if (message == 0) {
129 st_memory_perform_gc ();
130 message = st_object_new (ST_MESSAGE_CLASS);
131 st_assert (message != 0);
134 ST_OBJECT_FIELDS (message)[0] = cpu->message_selector;
135 ST_OBJECT_FIELDS (message)[1] = array;
137 ST_STACK_PUSH (cpu, message);
139 cpu->message_selector = ST_SELECTOR_DOESNOTUNDERSTAND;
140 cpu->message_argcount = 1;
143 static st_oop
144 lookup_method (st_oop class)
146 register struct st_cpu *cpu = &__cpu;
147 st_oop method, dict, parent;
148 st_uint hash;
150 parent = class;
151 hash = st_byte_array_hash (cpu->message_selector);
153 while (parent != ST_NIL) {
155 st_oop el;
156 st_uint mask, i;
158 dict = ST_BEHAVIOR_METHOD_DICTIONARY (parent);
159 mask = st_smi_value (st_arrayed_object_size (ST_OBJECT_FIELDS (dict)[2])) - 1;
160 i = (hash & mask) + 1;
162 while (true) {
163 el = st_array_at (ST_OBJECT_FIELDS (dict)[2], i);
164 if (el == ST_NIL || el == (uintptr_t) ST_OBJECT_FIELDS (dict)[2])
165 break;
166 if (cpu->message_selector == ST_ASSOCIATION_KEY (el))
167 return ST_ASSOCIATION_VALUE (el);
168 i = ((i + ST_ADVANCE_SIZE) & mask) + 1;
171 parent = ST_BEHAVIOR_SUPERCLASS (parent);
174 if (cpu->message_selector == ST_SELECTOR_DOESNOTUNDERSTAND) {
175 fprintf (stderr, "panda: error: no method found for #doesNotUnderstand:\n");
176 exit(1);
179 create_actual_message ();
181 return lookup_method (class);
184 st_oop
185 st_cpu_lookup_method (st_oop class)
187 return lookup_method (class);
191 * Creates a new method context. Parameterised by
192 * @sender, @receiver, @method, and @argcount
194 * Message arguments are copied into the new context's temporary
195 * frame. Receiver and arguments are then popped off the stack.
198 static inline void
199 activate_method (void)
201 register struct st_cpu *cpu = &__cpu;
202 st_oop context;
203 st_oop *arguments;
205 context = method_context_new ();
207 arguments = ST_METHOD_CONTEXT_STACK (context);
208 for (st_uint i = 0; i < cpu->message_argcount; i++)
209 arguments[i] = cpu->stack[cpu->sp - cpu->message_argcount + i];
211 cpu->sp -= cpu->message_argcount + 1;
213 st_cpu_set_active_context (context);
216 void
217 st_cpu_execute_method (void)
219 register struct st_cpu *cpu = &__cpu;
220 st_uint primitive_index;
221 st_method_flags flags;
223 flags = st_method_get_flags (cpu->new_method);
224 if (flags == ST_METHOD_PRIMITIVE) {
225 primitive_index = st_method_get_primitive_index (cpu->new_method);
226 cpu->success = true;
227 st_primitives[primitive_index].func (cpu);
228 if (ST_LIKELY (cpu->success))
229 return;
232 activate_method ();
235 void
236 st_cpu_set_active_context (st_oop context)
238 register struct st_cpu *cpu = &__cpu;
239 st_oop home;
241 /* save executation state of active context */
242 if (ST_UNLIKELY (cpu->context != ST_NIL)) {
243 ST_CONTEXT_PART_IP (cpu->context) = st_smi_new (cpu->ip);
244 ST_CONTEXT_PART_SP (cpu->context) = st_smi_new (cpu->sp);
247 if (ST_OBJECT_CLASS (context) == ST_BLOCK_CONTEXT_CLASS) {
248 home = ST_BLOCK_CONTEXT_HOME (context);
249 cpu->method = ST_METHOD_CONTEXT_METHOD (home);
250 cpu->receiver = ST_METHOD_CONTEXT_RECEIVER (home);
251 cpu->temps = ST_METHOD_CONTEXT_STACK (home);
252 cpu->stack = ST_BLOCK_CONTEXT_STACK (context);
253 } else {
254 cpu->method = ST_METHOD_CONTEXT_METHOD (context);
255 cpu->receiver = ST_METHOD_CONTEXT_RECEIVER (context);
256 cpu->temps = ST_METHOD_CONTEXT_STACK (context);
257 cpu->stack = ST_METHOD_CONTEXT_STACK (context);
260 cpu->context = context;
261 cpu->sp = st_smi_value (ST_CONTEXT_PART_SP (context));
262 cpu->ip = st_smi_value (ST_CONTEXT_PART_IP (context));
263 cpu->bytecode = st_method_bytecode_bytes (cpu->method);
266 #define SEND_SELECTOR(selector, argcount) \
267 cpu->message_argcount = argcount; \
268 cpu->message_receiver = sp[- argcount - 1]; \
269 cpu->message_selector = selector; \
270 goto send_common;
272 #define SEND_TEMPLATE() \
273 cpu->lookup_class = st_object_class (cpu->message_receiver); \
274 ip += 1; \
275 goto common;
277 #ifdef HAVE_COMPUTED_GOTO
278 #define SWITCH(ip) \
279 static const st_pointer labels[] = \
281 && PUSH_TEMP, \
282 && PUSH_INSTVAR, \
283 && PUSH_LITERAL_CONST, \
284 && PUSH_LITERAL_VAR, \
285 && STORE_LITERAL_VAR, \
286 && STORE_TEMP, \
287 && STORE_INSTVAR, \
288 && STORE_POP_LITERAL_VAR, \
289 && STORE_POP_TEMP, \
290 && STORE_POP_INSTVAR, \
291 && PUSH_SELF, \
292 && PUSH_NIL, \
293 && PUSH_TRUE, \
294 && PUSH_FALSE, \
295 && PUSH_INTEGER, \
296 && RETURN_STACK_TOP, \
297 && BLOCK_RETURN, \
298 && POP_STACK_TOP, \
299 && DUPLICATE_STACK_TOP, \
300 && PUSH_ACTIVE_CONTEXT, \
301 && BLOCK_COPY, \
302 && JUMP_TRUE, \
303 && JUMP_FALSE, \
304 && JUMP, \
305 && SEND, \
306 && SEND_SUPER, \
307 && SEND_PLUS, \
308 && SEND_MINUS, \
309 && SEND_LT, \
310 && SEND_GT, \
311 && SEND_LE, \
312 && SEND_GE, \
313 && SEND_EQ, \
314 && SEND_NE, \
315 && SEND_MUL, \
316 && SEND_DIV, \
317 && SEND_MOD, \
318 && SEND_BITSHIFT, \
319 && SEND_BITAND, \
320 && SEND_BITOR, \
321 && SEND_BITXOR, \
322 && SEND_AT, \
323 && SEND_AT_PUT, \
324 && SEND_SIZE, \
325 && SEND_VALUE, \
326 && SEND_VALUE_ARG, \
327 && SEND_IDENTITY_EQ, \
328 && SEND_CLASS, \
329 && SEND_NEW, \
330 && SEND_NEW_ARG, \
331 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
332 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
333 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
334 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
335 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
336 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
337 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
338 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
339 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
340 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
341 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
342 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
343 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
344 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
345 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
346 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
347 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
348 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
349 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
350 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
351 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
352 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
353 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
354 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
355 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
356 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
357 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
358 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
359 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
360 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
361 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
362 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
363 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
364 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
365 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
366 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
367 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
368 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
369 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
370 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
371 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
372 && INVALID, \
373 }; \
374 goto *labels[*ip];
375 #else
376 #define SWITCH(ip) \
377 start: \
378 switch (*ip)
379 #endif
381 #ifdef HAVE_COMPUTED_GOTO
382 #define INVALID() INVALID:
383 #define CASE(OP) OP:
384 #else
385 #define INVALID() default:
386 #define CASE(OP) case OP:
387 #endif
389 #ifdef HAVE_COMPUTED_GOTO
390 #define NEXT() goto *labels[*ip]
391 #else
392 #define NEXT() goto start
393 #endif
395 static inline void
396 install_method_in_cache (void)
398 register struct st_cpu *cpu = &__cpu;
399 st_uint index;
401 index = ST_METHOD_CACHE_HASH (cpu->lookup_class, cpu->message_selector) & ST_METHOD_CACHE_MASK;
402 cpu->method_cache[index].class = cpu->lookup_class;
403 cpu->method_cache[index].selector = cpu->message_selector;
404 cpu->method_cache[index].method = cpu->new_method;
407 static inline bool
408 lookup_method_in_cache (void)
410 register struct st_cpu *cpu = &__cpu;
411 st_uint index;
413 index = ST_METHOD_CACHE_HASH (cpu->lookup_class, cpu->message_selector) & ST_METHOD_CACHE_MASK;
414 if (cpu->method_cache[index].class == cpu->lookup_class &&
415 cpu->method_cache[index].selector == cpu->message_selector) {
416 cpu->new_method = cpu->method_cache[index].method;
417 return true;
419 return false;
422 #define STACK_POP(oop) (*--sp)
423 #define STACK_PUSH(oop) (*sp++ = (oop))
424 #define STACK_PEEK(oop) (*(sp-1))
425 #define STACK_UNPOP(count) (sp += count)
426 #define STORE_REGISTERS() \
427 cpu->ip = ip - cpu->bytecode; \
428 cpu->sp = sp - cpu->stack; \
429 ST_CONTEXT_PART_IP (cpu->context) = st_smi_new (cpu->ip);
430 #define LOAD_REGISTERS() \
431 ip = cpu->bytecode + cpu->ip; \
432 sp = cpu->stack + cpu->sp; \
433 ST_CONTEXT_PART_SP (cpu->context) = st_smi_new (cpu->sp);
435 void
436 st_cpu_main (void)
438 register struct st_cpu *cpu = &__cpu;
439 register const st_uchar *ip;
440 register st_oop *sp = cpu->stack;
442 if (setjmp (cpu->main_loop))
443 goto out;
445 ip = cpu->bytecode + cpu->ip;
447 SWITCH (ip) {
449 CASE (PUSH_TEMP) {
451 STACK_PUSH (cpu->temps[ip[1]]);
453 ip += 2;
454 NEXT ();
457 CASE (PUSH_INSTVAR) {
459 STACK_PUSH (ST_OBJECT_FIELDS (cpu->receiver)[ip[1]]);
461 ip += 2;
462 NEXT ();
465 CASE (STORE_POP_INSTVAR) {
467 ST_OBJECT_FIELDS (cpu->receiver)[ip[1]] = STACK_POP ();
469 ip += 2;
470 NEXT ();
473 CASE (STORE_INSTVAR) {
475 ST_OBJECT_FIELDS (cpu->receiver)[ip[1]] = STACK_PEEK ();
477 ip += 2;
478 NEXT ();
481 CASE (STORE_POP_TEMP) {
483 cpu->temps[ip[1]] = STACK_POP ();
485 ip += 2;
486 NEXT ();
489 CASE (STORE_TEMP) {
491 cpu->temps[ip[1]] = STACK_PEEK ();
493 ip += 2;
494 NEXT ();
497 CASE (STORE_LITERAL_VAR) {
499 ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[1]]) = STACK_PEEK ();
501 ip += 2;
502 NEXT ();
505 CASE (STORE_POP_LITERAL_VAR) {
507 ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[1]]) = STACK_POP ();
509 ip += 2;
510 NEXT ();
513 CASE (PUSH_SELF) {
515 STACK_PUSH (cpu->receiver);
517 ip += 1;
518 NEXT ();
521 CASE (PUSH_TRUE) {
523 STACK_PUSH (ST_TRUE);
525 ip += 1;
526 NEXT ();
529 CASE (PUSH_FALSE) {
531 STACK_PUSH (ST_FALSE);
533 ip += 1;
534 NEXT ();
537 CASE (PUSH_NIL) {
539 STACK_PUSH (ST_NIL);
541 ip += 1;
542 NEXT ();
545 CASE (PUSH_INTEGER) {
547 STACK_PUSH (st_smi_new ((signed char) ip[1]));
549 ip += 2;
550 NEXT ();
553 CASE (PUSH_ACTIVE_CONTEXT) {
555 STACK_PUSH (cpu->context);
557 ip += 1;
558 NEXT ();
561 CASE (PUSH_LITERAL_CONST) {
563 STACK_PUSH (st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[1]]);
565 ip += 2;
566 NEXT ();
569 CASE (PUSH_LITERAL_VAR) {
571 st_oop var;
573 var = ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[1]]);
575 STACK_PUSH (var);
577 ip += 2;
578 NEXT ();
581 CASE (JUMP_TRUE) {
583 if (STACK_PEEK () == ST_TRUE) {
584 (void) STACK_POP ();
585 ip += *((unsigned short *) (ip + 1)) + 3;
586 } else if (ST_LIKELY (STACK_PEEK () == ST_FALSE)) {
587 (void) STACK_POP ();
588 ip += 3;
589 } else {
590 ip += 3;
591 SEND_SELECTOR (ST_SELECTOR_MUSTBEBOOLEAN, 0);
594 NEXT ();
598 CASE (JUMP_FALSE) {
600 if (STACK_PEEK () == ST_FALSE) {
601 (void) STACK_POP ();
602 ip += *((unsigned short *) (ip + 1)) + 3;
603 } else if (ST_LIKELY (STACK_PEEK () == ST_TRUE)) {
604 (void) STACK_POP ();
605 ip += 3;
606 } else {
607 ip += 3;
608 SEND_SELECTOR (ST_SELECTOR_MUSTBEBOOLEAN, 0);
611 NEXT ();
614 CASE (JUMP) {
616 ip += *((short *) (ip + 1)) + 3;
617 NEXT ();
620 CASE (SEND_PLUS) {
622 int a, b, result;
624 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
625 st_object_is_smi (sp[-2]))) {
626 b = st_smi_value (sp[-1]);
627 a = st_smi_value (sp[-2]);
628 result = a + b;
629 if (((result << 1) ^ (result << 2)) >= 0) {
630 sp -= 2;
631 STACK_PUSH (st_smi_new (result));
632 ip++;
633 NEXT ();
637 cpu->message_argcount = 1;
638 cpu->message_selector = ST_SELECTOR_PLUS;
639 cpu->message_receiver = sp[- cpu->message_argcount - 1];
640 cpu->lookup_class = st_object_class (cpu->message_receiver);
641 ip += 1;
642 goto send_common;
645 CASE (SEND_MINUS) {
647 int a, b, result;
649 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
650 st_object_is_smi (sp[-2]))) {
651 b = st_smi_value (sp[-1]);
652 a = st_smi_value (sp[-2]);
653 result = a - b;
654 if (((result << 1) ^ (result << 2)) >= 0) {
655 sp -= 2;
656 STACK_PUSH (st_smi_new (result));
657 ip++;
658 NEXT ();
659 } else {
660 STACK_UNPOP (2);
664 cpu->message_argcount = 1;
665 cpu->message_selector = ST_SELECTOR_MINUS;
666 cpu->message_receiver = sp[- cpu->message_argcount - 1];
667 cpu->lookup_class = st_object_class (cpu->message_receiver);
668 ip += 1;
669 goto send_common;
672 CASE (SEND_MUL) {
674 int a, b;
675 int64_t result;
677 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
678 st_object_is_smi (sp[-2]))) {
679 b = st_smi_value (sp[-1]);
680 a = st_smi_value (sp[-2]);
681 result = a * b;
682 if (result >= ST_SMALL_INTEGER_MIN && result <= ST_SMALL_INTEGER_MAX) {
683 sp -= 2;
684 STACK_PUSH (st_smi_new ((int)result));
685 ip++;
686 NEXT ();
690 cpu->message_argcount = 1;
691 cpu->message_selector = ST_SELECTOR_MUL;
692 cpu->message_receiver = sp[- cpu->message_argcount - 1];
693 cpu->lookup_class = st_object_class (cpu->message_receiver);
694 ip += 1;
695 goto send_common;
699 CASE (SEND_MOD) {
701 st_oop a, b;
703 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
704 st_object_is_smi (sp[-2]))) {
705 b = STACK_POP ();
706 a = STACK_POP ();
707 STACK_PUSH (st_smi_new (st_smi_value (a) % st_smi_value (b)));
708 ip++;
709 NEXT ();
712 cpu->message_argcount = 1;
713 cpu->message_selector = ST_SELECTOR_MOD;
714 cpu->message_receiver = sp[- cpu->message_argcount - 1];
715 cpu->lookup_class = st_object_class (cpu->message_receiver);
716 ip += 1;
717 goto send_common;
720 CASE (SEND_DIV) {
722 cpu->message_argcount = 1;
723 cpu->message_selector = ST_SELECTOR_DIV;
724 cpu->message_receiver = sp[- cpu->message_argcount - 1];
725 cpu->lookup_class = st_object_class (cpu->message_receiver);
726 ip += 1;
727 goto send_common;
730 CASE (SEND_BITSHIFT) {
732 st_oop a, b;
734 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
735 st_object_is_smi (sp[-2]))) {
736 b = STACK_POP ();
737 a = STACK_POP ();
738 if (st_smi_value (b) < 0) {
739 STACK_PUSH (st_smi_new (st_smi_value (a) >> -st_smi_value (b)));
740 } else
741 STACK_PUSH (st_smi_new (st_smi_value (a) << st_smi_value (b)));
742 ip++;
743 NEXT ();
746 cpu->message_argcount = 1;
747 cpu->message_selector = ST_SELECTOR_BITSHIFT;
748 cpu->message_receiver = sp[- cpu->message_argcount - 1];
749 cpu->lookup_class = st_object_class (cpu->message_receiver);
750 ip += 1;
751 goto send_common;
754 CASE (SEND_BITAND) {
756 st_oop a, b;
758 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
759 st_object_is_smi (sp[-2]))) {
760 b = STACK_POP ();
761 a = STACK_POP ();
762 STACK_PUSH (st_smi_new (st_smi_value (a) & st_smi_value (b)));
763 ip++;
764 NEXT ();
767 cpu->message_argcount = 1;
768 cpu->message_selector = ST_SELECTOR_BITAND;
769 cpu->message_receiver = sp[- cpu->message_argcount - 1];
770 cpu->lookup_class = st_object_class (cpu->message_receiver);
771 ip += 1;
772 goto send_common;
775 CASE (SEND_BITOR) {
777 st_oop a, b;
779 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
780 st_object_is_smi (sp[-2]))) {
781 b = STACK_POP ();
782 a = STACK_POP ();
783 STACK_PUSH (st_smi_new (st_smi_value (a) | st_smi_value (b)));
784 ip++;
785 NEXT ();
788 cpu->message_argcount = 1;
789 cpu->message_selector = ST_SELECTOR_BITOR;
790 cpu->message_receiver = sp[- cpu->message_argcount - 1];
791 cpu->lookup_class = st_object_class (cpu->message_receiver);
792 ip += 1;
793 goto send_common;
797 CASE (SEND_BITXOR) {
799 st_oop a, b;
801 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
802 st_object_is_smi (sp[-2]))) {
803 b = STACK_POP ();
804 a = STACK_POP ();
805 STACK_PUSH (st_smi_new (st_smi_value (a) ^ st_smi_value (b)));
806 ip++;
807 NEXT ();
810 cpu->message_argcount = 1;
811 cpu->message_selector = ST_SELECTOR_BITXOR;
812 cpu->message_receiver = sp[- cpu->message_argcount - 1];
813 cpu->lookup_class = st_object_class (cpu->message_receiver);
814 ip += 1;
815 goto send_common;
818 CASE (SEND_LT) {
820 st_oop a, b;
822 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
823 st_object_is_smi (sp[-2]))) {
824 b = STACK_POP ();
825 a = STACK_POP ();
826 STACK_PUSH (st_smi_value (a) < st_smi_value (b) ? ST_TRUE : ST_FALSE);
827 ip++;
828 NEXT ();
831 cpu->message_argcount = 1;
832 cpu->message_selector = ST_SELECTOR_LT;
833 cpu->message_receiver = sp[- cpu->message_argcount - 1];
834 cpu->lookup_class = st_object_class (cpu->message_receiver);
835 ip += 1;
836 goto send_common;
839 CASE (SEND_GT) {
841 st_oop a, b;
843 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
844 st_object_is_smi (sp[-2]))) {
845 b = STACK_POP ();
846 a = STACK_POP ();
847 STACK_PUSH (st_smi_value (a) > st_smi_value (b) ? ST_TRUE : ST_FALSE);
848 ip++;
849 NEXT ();
852 cpu->message_argcount = 1;
853 cpu->message_selector = ST_SELECTOR_GT;
854 cpu->message_receiver = sp[- cpu->message_argcount - 1];
855 cpu->lookup_class = st_object_class (cpu->message_receiver);
856 ip += 1;
857 goto send_common;
861 CASE (SEND_LE) {
863 st_oop a, b;
865 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
866 st_object_is_smi (sp[-2]))) {
867 b = STACK_POP ();
868 a = STACK_POP ();
869 STACK_PUSH (st_smi_value (a) <= st_smi_value (b) ? ST_TRUE : ST_FALSE);
870 ip++;
871 NEXT ();
874 cpu->message_argcount = 1;
875 cpu->message_selector = ST_SELECTOR_LE;
876 cpu->message_receiver = sp[- cpu->message_argcount - 1];
877 cpu->lookup_class = st_object_class (cpu->message_receiver);
878 ip += 1;
879 goto send_common;
882 CASE (SEND_GE) {
884 st_oop a, b;
886 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
887 st_object_is_smi (sp[-2]))) {
888 b = STACK_POP ();
889 a = STACK_POP ();
890 STACK_PUSH (st_smi_value (a) >= st_smi_value (b) ? ST_TRUE : ST_FALSE);
891 ip++;
892 NEXT ();
895 cpu->message_argcount = 1;
896 cpu->message_selector = ST_SELECTOR_GE;
897 cpu->message_receiver = sp[- cpu->message_argcount - 1];
898 cpu->lookup_class = st_object_class (cpu->message_receiver);
899 ip += 1;
900 goto send_common;
904 CASE (SEND_CLASS) {
906 cpu->message_argcount = 0;
907 cpu->message_selector = ST_SELECTOR_CLASS;
908 cpu->message_receiver = sp[- cpu->message_argcount - 1];
909 cpu->lookup_class = st_object_class (cpu->message_receiver);
910 ip += 1;
911 goto send_common;
914 CASE (SEND_SIZE) {
916 cpu->message_argcount = 0;
917 cpu->message_selector = ST_SELECTOR_SIZE;
918 cpu->message_receiver = sp[- cpu->message_argcount - 1];
919 cpu->lookup_class = st_object_class (cpu->message_receiver);
920 ip += 1;
921 goto send_common;
924 CASE (SEND_AT) {
926 cpu->message_argcount = 1;
927 cpu->message_selector = ST_SELECTOR_AT;
928 cpu->message_receiver = sp[- cpu->message_argcount - 1];
929 cpu->lookup_class = st_object_class (cpu->message_receiver);
930 ip += 1;
931 goto send_common;
934 CASE (SEND_AT_PUT) {
936 cpu->message_argcount = 2;
937 cpu->message_selector = ST_SELECTOR_ATPUT;
938 cpu->message_receiver = sp[- cpu->message_argcount - 1];
939 cpu->lookup_class = st_object_class (cpu->message_receiver);
940 ip += 1;
941 goto send_common;
944 CASE (SEND_EQ) {
946 cpu->message_argcount = 1;
947 cpu->message_selector = ST_SELECTOR_EQ;
948 cpu->message_receiver = sp[- cpu->message_argcount - 1];
949 cpu->lookup_class = st_object_class (cpu->message_receiver);
950 ip += 1;
951 goto send_common;
954 CASE (SEND_NE) {
956 cpu->message_argcount = 1;
957 cpu->message_selector = ST_SELECTOR_NE;
958 cpu->message_receiver = sp[- cpu->message_argcount - 1];
959 cpu->lookup_class = st_object_class (cpu->message_receiver);
960 ip += 1;
961 goto send_common;
964 CASE (SEND_IDENTITY_EQ) {
966 st_oop a, b;
967 a = STACK_POP ();
968 b = STACK_POP ();
970 STACK_PUSH ((a == b) ? ST_TRUE : ST_FALSE);
972 ip += 1;
973 NEXT ();
976 CASE (SEND_VALUE) {
978 cpu->message_argcount = 0;
979 cpu->message_selector = ST_SELECTOR_VALUE;
980 cpu->message_receiver = sp[- cpu->message_argcount - 1];
981 cpu->lookup_class = st_object_class (cpu->message_receiver);
982 ip += 1;
983 goto send_common;
986 CASE (SEND_VALUE_ARG) {
988 cpu->message_argcount = 1;
989 cpu->message_selector = ST_SELECTOR_VALUE_ARG;
990 cpu->message_receiver = sp[- cpu->message_argcount - 1];
991 cpu->lookup_class = st_object_class (cpu->message_receiver);
992 ip += 1;
993 goto send_common;
996 CASE (SEND_NEW) {
998 cpu->message_argcount = 0;
999 cpu->message_selector = ST_SELECTOR_NEW;
1000 cpu->message_receiver = sp[- cpu->message_argcount - 1];
1001 cpu->lookup_class = st_object_class (cpu->message_receiver);
1002 ip += 1;
1003 goto send_common;
1006 CASE (SEND_NEW_ARG) {
1008 cpu->message_argcount = 1;
1009 cpu->message_selector = ST_SELECTOR_NEW_ARG;
1010 cpu->message_receiver = sp[- cpu->message_argcount - 1];
1011 cpu->lookup_class = st_object_class (cpu->message_receiver);
1012 ip += 1;
1013 goto send_common;
1016 CASE (SEND) {
1018 st_uint primitive_index;
1019 st_method_flags flags;
1020 st_oop context;
1021 st_oop *arguments;
1023 cpu->message_argcount = ip[1];
1024 cpu->message_selector = st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[2]];
1025 cpu->message_receiver = sp[- cpu->message_argcount - 1];
1026 cpu->lookup_class = st_object_class (cpu->message_receiver);
1027 ip += 3;
1029 send_common:
1031 if (!lookup_method_in_cache ()) {
1032 STORE_REGISTERS ();
1033 cpu->new_method = lookup_method (cpu->lookup_class);
1034 LOAD_REGISTERS ();
1035 install_method_in_cache ();
1038 flags = st_method_get_flags (cpu->new_method);
1039 if (flags == ST_METHOD_PRIMITIVE) {
1040 primitive_index = st_method_get_primitive_index (cpu->new_method);
1042 cpu->success = true;
1043 STORE_REGISTERS ();
1044 st_primitives[primitive_index].func (cpu);
1045 LOAD_REGISTERS ();
1047 if (ST_LIKELY (cpu->success))
1048 NEXT ();
1051 /* store registers as a gc could occur */
1052 STORE_REGISTERS ();
1053 context = method_context_new ();
1054 LOAD_REGISTERS ();
1055 arguments = ST_METHOD_CONTEXT_STACK (context);
1056 for (int i = 0; i < cpu->message_argcount; i++)
1057 arguments[i] = sp[- cpu->message_argcount + i];
1058 sp -= cpu->message_argcount + 1;
1060 STORE_REGISTERS ();
1061 st_cpu_set_active_context (context);
1062 LOAD_REGISTERS ();
1064 /* We have to nil these fields here. Its possible that
1065 that the objects they reference may be zapped by the gc.
1066 Another GC invocation may try to remap these fields not knowing that
1067 the references are invalid.
1068 FIXME: move this nilling out of such a critical execution path */
1069 cpu->message_receiver = ST_NIL;
1070 cpu->message_selector = ST_NIL;
1072 NEXT ();
1075 CASE (SEND_SUPER) {
1077 st_oop index;
1079 cpu->message_argcount = ip[1];
1080 cpu->message_selector = st_array_elements (ST_METHOD_LITERALS (cpu->method))[ip[2]];
1081 cpu->message_receiver = sp[- cpu->message_argcount - 1];
1083 index = st_smi_value (st_arrayed_object_size (ST_METHOD_LITERALS (cpu->method))) - 1;
1084 cpu->lookup_class = ST_BEHAVIOR_SUPERCLASS (st_array_elements (ST_METHOD_LITERALS (cpu->method))[index]);
1086 ip += 3;
1088 goto send_common;
1091 CASE (POP_STACK_TOP) {
1093 (void) STACK_POP ();
1095 ip += 1;
1096 NEXT ();
1099 CASE (DUPLICATE_STACK_TOP) {
1101 STACK_PUSH (STACK_PEEK ());
1103 ip += 1;
1104 NEXT ();
1107 CASE (BLOCK_COPY) {
1109 st_oop block;
1110 st_oop home;
1111 st_uint argcount = ip[1];
1112 st_uint initial_ip;
1114 ip += 2;
1116 initial_ip = ip - cpu->bytecode + 3;
1118 STORE_REGISTERS ();
1119 block = block_context_new (initial_ip, argcount);
1120 LOAD_REGISTERS ();
1122 STACK_PUSH (block);
1124 NEXT ();
1127 CASE (RETURN_STACK_TOP) {
1129 st_oop sender;
1130 st_oop value;
1131 st_oop home;
1133 value = STACK_PEEK ();
1135 if (ST_OBJECT_CLASS (cpu->context) == ST_BLOCK_CONTEXT_CLASS)
1136 sender = ST_CONTEXT_PART_SENDER (ST_BLOCK_CONTEXT_HOME (cpu->context));
1137 else {
1138 sender = ST_CONTEXT_PART_SENDER (cpu->context);
1139 st_memory_recycle_context (cpu->context);
1142 if (ST_UNLIKELY (sender == ST_NIL)) {
1143 STACK_PUSH (cpu->context);
1144 STACK_PUSH (value);
1145 SEND_SELECTOR (ST_SELECTOR_CANNOTRETURN, 1);
1146 NEXT ();
1149 st_cpu_set_active_context (sender);
1150 LOAD_REGISTERS ();
1151 STACK_PUSH (value);
1153 NEXT ();
1156 CASE (BLOCK_RETURN) {
1158 st_oop caller;
1159 st_oop value;
1160 st_oop home;
1162 caller = ST_CONTEXT_PART_SENDER (cpu->context);
1163 value = STACK_PEEK ();
1165 st_cpu_set_active_context (caller);
1166 LOAD_REGISTERS ();
1167 STACK_PUSH (value);
1169 NEXT ();
1172 INVALID () {
1173 abort ();
1178 out:
1179 st_log ("gc", "totalPauseTime: %.6fs\n",
1180 st_timespec_to_double_seconds (&memory->total_pause_time));
1183 void
1184 st_cpu_clear_caches (void)
1186 memset (__cpu.method_cache, 0, ST_METHOD_CACHE_SIZE * 3 * sizeof (st_oop));
1189 void
1190 st_cpu_initialize (void)
1192 st_oop context;
1193 st_oop method;
1195 /* clear contents */
1196 __cpu.context = ST_NIL;
1197 __cpu.receiver = ST_NIL;
1198 __cpu.method = ST_NIL;
1200 __cpu.sp = 0;
1201 __cpu.ip = 0;
1202 __cpu.stack = NULL;
1204 st_cpu_clear_caches ();
1206 __cpu.message_argcount = 0;
1207 __cpu.message_receiver = ST_SMALLTALK;
1208 __cpu.message_selector = ST_SELECTOR_STARTUPSYSTEM;
1210 __cpu.new_method = lookup_method (st_object_class (__cpu.message_receiver));
1211 st_assert (st_method_get_flags (__cpu.new_method) == ST_METHOD_NORMAL);
1213 context = method_context_new ();
1214 st_cpu_set_active_context (context);