Add methods for converting between Collections (asBag, asSet, etc)
[panda.git] / src / st-machine.c
blobbc9eb7553746cf70db79e034af77f5b2f7aaf71c
1 /*
2 * st-machine.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 (st_machine *machine)
49 st_oop context;
50 st_uint temp_count;
51 st_oop *stack;
52 bool large;
54 large = st_method_get_large_context (machine->new_method);
55 temp_count = st_method_get_arg_count (machine->new_method) + st_method_get_temp_count (machine->new_method);
57 context = st_memory_allocate_context (large);
59 ST_CONTEXT_PART_SENDER (context) = machine->context;
60 ST_CONTEXT_PART_IP (context) = st_smi_new (0);
61 ST_CONTEXT_PART_SP (context) = st_smi_new (temp_count);
62 ST_METHOD_CONTEXT_RECEIVER (context) = machine->message_receiver;
63 ST_METHOD_CONTEXT_METHOD (context) = machine->new_method;
65 /* clear temporaries (and nothing above) */
66 stack = ST_METHOD_CONTEXT_STACK (context);
67 for (st_uint i=0; i < temp_count; i++)
68 stack[i] = ST_NIL;
70 return context;
73 static st_oop
74 block_context_new (st_machine *machine, st_uint initial_ip, st_uint argcount)
76 st_oop home;
77 st_oop context;
78 st_oop method;
79 st_oop *stack;
80 st_uint stack_size;
82 stack_size = 32;
84 context = st_memory_allocate (ST_SIZE_OOPS (struct st_block_context) + stack_size);
85 if (context == 0) {
86 st_memory_perform_gc ();
87 context = st_memory_allocate (ST_SIZE_OOPS (struct st_block_context) + stack_size);
88 st_assert (context != 0);
91 st_object_initialize_header (context, ST_BLOCK_CONTEXT_CLASS);
92 st_object_set_large_context (context, true);
94 if (ST_OBJECT_CLASS (machine->context) == ST_BLOCK_CONTEXT_CLASS)
95 home = ST_BLOCK_CONTEXT_HOME (machine->context);
96 else
97 home = machine->context;
99 ST_CONTEXT_PART_SENDER (context) = ST_NIL;
100 ST_CONTEXT_PART_IP (context) = st_smi_new (0);
101 ST_CONTEXT_PART_SP (context) = st_smi_new (0);
103 ST_BLOCK_CONTEXT_INITIALIP (context) = st_smi_new (initial_ip);
104 ST_BLOCK_CONTEXT_ARGCOUNT (context) = st_smi_new (argcount);
105 ST_BLOCK_CONTEXT_HOME (context) = home;
107 return context;
110 static void
111 create_actual_message (st_machine *machine)
113 st_oop *elements;
114 st_oop message;
115 st_oop array;
117 array = st_object_new_arrayed (ST_ARRAY_CLASS, machine->message_argcount);
118 elements = st_array_elements (array);
119 for (st_uint i = 0; i < machine->message_argcount; i++)
120 elements[i] = machine->stack[machine->sp - machine->message_argcount + i];
122 machine->sp -= machine->message_argcount;
124 message = st_object_new (ST_MESSAGE_CLASS);
125 if (message == 0) {
126 st_memory_perform_gc ();
127 message = st_object_new (ST_MESSAGE_CLASS);
128 st_assert (message != 0);
131 ST_OBJECT_FIELDS (message)[0] = machine->message_selector;
132 ST_OBJECT_FIELDS (message)[1] = array;
134 ST_STACK_PUSH (machine, message);
136 machine->message_selector = ST_SELECTOR_DOESNOTUNDERSTAND;
137 machine->message_argcount = 1;
140 static st_oop
141 lookup_method (st_machine *machine, st_oop class)
143 st_oop method, dict, parent;
144 st_uint hash;
146 parent = class;
147 hash = st_byte_array_hash (machine->message_selector);
149 while (parent != ST_NIL) {
151 st_oop el;
152 st_uint mask, i;
154 dict = ST_BEHAVIOR_METHOD_DICTIONARY (parent);
155 mask = st_smi_value (st_arrayed_object_size (ST_OBJECT_FIELDS (dict)[2])) - 1;
156 i = (hash & mask) + 1;
158 while (true) {
159 el = st_array_at (ST_OBJECT_FIELDS (dict)[2], i);
160 if (el == ST_NIL || el == (uintptr_t) ST_OBJECT_FIELDS (dict)[2])
161 break;
162 if (machine->message_selector == ST_ASSOCIATION_KEY (el))
163 return ST_ASSOCIATION_VALUE (el);
164 i = ((i + ST_ADVANCE_SIZE) & mask) + 1;
167 parent = ST_BEHAVIOR_SUPERCLASS (parent);
170 if (machine->message_selector == ST_SELECTOR_DOESNOTUNDERSTAND) {
171 fprintf (stderr, "panda: error: no method found for #doesNotUnderstand:\n");
172 exit(1);
175 create_actual_message (machine);
177 return lookup_method (machine, class);
180 st_oop
181 st_machine_lookup_method (st_machine *machine, st_oop class)
183 return lookup_method (machine, class);
187 * Creates a new method context. Parameterised by
188 * @sender, @receiver, @method, and @argcount
190 * Message arguments are copied into the new context's temporary
191 * frame. Receiver and arguments are then popped off the stack.
194 static inline void
195 activate_method (st_machine *machine)
197 st_oop context;
198 st_oop *arguments;
200 context = method_context_new (machine);
202 arguments = ST_METHOD_CONTEXT_STACK (context);
203 for (st_uint i = 0; i < machine->message_argcount; i++)
204 arguments[i] = machine->stack[machine->sp - machine->message_argcount + i];
206 machine->sp -= machine->message_argcount + 1;
208 st_machine_set_active_context (machine, context);
211 void
212 st_machine_execute_method (st_machine *machine)
214 st_uint primitive_index;
215 st_method_flags flags;
217 flags = st_method_get_flags (machine->new_method);
218 if (flags == ST_METHOD_PRIMITIVE) {
219 primitive_index = st_method_get_primitive_index (machine->new_method);
220 machine->success = true;
221 st_primitives[primitive_index].func (machine);
222 if (ST_LIKELY (machine->success))
223 return;
226 activate_method (machine);
229 void
230 st_machine_set_active_context (st_machine *machine, st_oop context)
232 st_oop home;
234 /* save executation state of active context */
235 if (ST_UNLIKELY (machine->context != ST_NIL)) {
236 ST_CONTEXT_PART_IP (machine->context) = st_smi_new (machine->ip);
237 ST_CONTEXT_PART_SP (machine->context) = st_smi_new (machine->sp);
240 if (ST_OBJECT_CLASS (context) == ST_BLOCK_CONTEXT_CLASS) {
241 home = ST_BLOCK_CONTEXT_HOME (context);
242 machine->method = ST_METHOD_CONTEXT_METHOD (home);
243 machine->receiver = ST_METHOD_CONTEXT_RECEIVER (home);
244 machine->temps = ST_METHOD_CONTEXT_STACK (home);
245 machine->stack = ST_BLOCK_CONTEXT_STACK (context);
246 } else {
247 machine->method = ST_METHOD_CONTEXT_METHOD (context);
248 machine->receiver = ST_METHOD_CONTEXT_RECEIVER (context);
249 machine->temps = ST_METHOD_CONTEXT_STACK (context);
250 machine->stack = ST_METHOD_CONTEXT_STACK (context);
253 machine->context = context;
254 machine->sp = st_smi_value (ST_CONTEXT_PART_SP (context));
255 machine->ip = st_smi_value (ST_CONTEXT_PART_IP (context));
256 machine->bytecode = st_method_bytecode_bytes (machine->method);
259 #define SEND_SELECTOR(selector, argcount) \
260 machine->message_argcount = argcount; \
261 machine->message_receiver = sp[- argcount - 1]; \
262 machine->message_selector = selector; \
263 goto send_common;
265 #define SEND_TEMPLATE() \
266 machine->lookup_class = st_object_class (machine->message_receiver); \
267 ip += 1; \
268 goto common;
270 #ifdef HAVE_COMPUTED_GOTO
271 #define SWITCH(ip) \
272 static const st_pointer labels[] = \
274 && PUSH_TEMP, \
275 && PUSH_INSTVAR, \
276 && PUSH_LITERAL_CONST, \
277 && PUSH_LITERAL_VAR, \
278 && STORE_LITERAL_VAR, \
279 && STORE_TEMP, \
280 && STORE_INSTVAR, \
281 && STORE_POP_LITERAL_VAR, \
282 && STORE_POP_TEMP, \
283 && STORE_POP_INSTVAR, \
284 && PUSH_SELF, \
285 && PUSH_NIL, \
286 && PUSH_TRUE, \
287 && PUSH_FALSE, \
288 && PUSH_INTEGER, \
289 && RETURN_STACK_TOP, \
290 && BLOCK_RETURN, \
291 && POP_STACK_TOP, \
292 && DUPLICATE_STACK_TOP, \
293 && PUSH_ACTIVE_CONTEXT, \
294 && BLOCK_COPY, \
295 && JUMP_TRUE, \
296 && JUMP_FALSE, \
297 && JUMP, \
298 && SEND, \
299 && SEND_SUPER, \
300 && SEND_PLUS, \
301 && SEND_MINUS, \
302 && SEND_LT, \
303 && SEND_GT, \
304 && SEND_LE, \
305 && SEND_GE, \
306 && SEND_EQ, \
307 && SEND_NE, \
308 && SEND_MUL, \
309 && SEND_DIV, \
310 && SEND_MOD, \
311 && SEND_BITSHIFT, \
312 && SEND_BITAND, \
313 && SEND_BITOR, \
314 && SEND_BITXOR, \
315 && SEND_AT, \
316 && SEND_AT_PUT, \
317 && SEND_SIZE, \
318 && SEND_VALUE, \
319 && SEND_VALUE_ARG, \
320 && SEND_IDENTITY_EQ, \
321 && SEND_CLASS, \
322 && SEND_NEW, \
323 && SEND_NEW_ARG, \
324 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
325 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
326 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
327 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
328 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
329 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
330 && INVALID, && INVALID, && INVALID, && INVALID, && INVALID, \
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, \
366 }; \
367 goto *labels[*ip];
368 #else
369 #define SWITCH(ip) \
370 start: \
371 switch (*ip)
372 #endif
374 #ifdef HAVE_COMPUTED_GOTO
375 #define INVALID() INVALID:
376 #define CASE(OP) OP:
377 #else
378 #define INVALID() default:
379 #define CASE(OP) case OP:
380 #endif
382 #ifdef HAVE_COMPUTED_GOTO
383 #define NEXT() goto *labels[*ip]
384 #else
385 #define NEXT() goto start
386 #endif
388 static inline void
389 install_method_in_cache (st_machine *machine)
391 st_uint index;
393 index = ST_METHOD_CACHE_HASH (machine->lookup_class, machine->message_selector) & ST_METHOD_CACHE_MASK;
394 machine->method_cache[index].class = machine->lookup_class;
395 machine->method_cache[index].selector = machine->message_selector;
396 machine->method_cache[index].method = machine->new_method;
399 static inline bool
400 lookup_method_in_cache (st_machine *machine)
402 st_uint index;
404 index = ST_METHOD_CACHE_HASH (machine->lookup_class, machine->message_selector) & ST_METHOD_CACHE_MASK;
405 if (machine->method_cache[index].class == machine->lookup_class &&
406 machine->method_cache[index].selector == machine->message_selector) {
407 machine->new_method = machine->method_cache[index].method;
408 return true;
410 return false;
413 #define STACK_POP(oop) (*--sp)
414 #define STACK_PUSH(oop) (*sp++ = (oop))
415 #define STACK_PEEK(oop) (*(sp-1))
416 #define STACK_UNPOP(count) (sp += count)
417 #define STORE_REGISTERS() \
418 machine->ip = ip - machine->bytecode; \
419 machine->sp = sp - machine->stack; \
420 ST_CONTEXT_PART_IP (machine->context) = st_smi_new (machine->ip);
421 #define LOAD_REGISTERS() \
422 ip = machine->bytecode + machine->ip; \
423 sp = machine->stack + machine->sp; \
424 ST_CONTEXT_PART_SP (machine->context) = st_smi_new (machine->sp);
426 void
427 st_machine_main (st_machine *machine)
429 register const st_uchar *ip;
430 register st_oop *sp = machine->stack;
432 if (setjmp (machine->main_loop))
433 goto out;
435 ip = machine->bytecode + machine->ip;
437 SWITCH (ip) {
439 CASE (PUSH_TEMP) {
441 STACK_PUSH (machine->temps[ip[1]]);
443 ip += 2;
444 NEXT ();
447 CASE (PUSH_INSTVAR) {
449 STACK_PUSH (ST_OBJECT_FIELDS (machine->receiver)[ip[1]]);
451 ip += 2;
452 NEXT ();
455 CASE (STORE_POP_INSTVAR) {
457 ST_OBJECT_FIELDS (machine->receiver)[ip[1]] = STACK_POP ();
459 ip += 2;
460 NEXT ();
463 CASE (STORE_INSTVAR) {
465 ST_OBJECT_FIELDS (machine->receiver)[ip[1]] = STACK_PEEK ();
467 ip += 2;
468 NEXT ();
471 CASE (STORE_POP_TEMP) {
473 machine->temps[ip[1]] = STACK_POP ();
475 ip += 2;
476 NEXT ();
479 CASE (STORE_TEMP) {
481 machine->temps[ip[1]] = STACK_PEEK ();
483 ip += 2;
484 NEXT ();
487 CASE (STORE_LITERAL_VAR) {
490 ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[1]]) = STACK_PEEK ();
492 ip += 2;
493 NEXT ();
496 CASE (STORE_POP_LITERAL_VAR) {
498 ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[1]]) = STACK_POP ();
500 ip += 2;
501 NEXT ();
504 CASE (PUSH_SELF) {
506 STACK_PUSH (machine->receiver);
508 ip += 1;
509 NEXT ();
512 CASE (PUSH_TRUE) {
514 STACK_PUSH (ST_TRUE);
516 ip += 1;
517 NEXT ();
520 CASE (PUSH_FALSE) {
522 STACK_PUSH (ST_FALSE);
524 ip += 1;
525 NEXT ();
528 CASE (PUSH_NIL) {
530 STACK_PUSH (ST_NIL);
532 ip += 1;
533 NEXT ();
536 CASE (PUSH_INTEGER) {
538 STACK_PUSH (st_smi_new ((signed char) ip[1]));
540 ip += 2;
541 NEXT ();
544 CASE (PUSH_ACTIVE_CONTEXT) {
546 STACK_PUSH (machine->context);
548 ip += 1;
549 NEXT ();
552 CASE (PUSH_LITERAL_CONST) {
554 STACK_PUSH (st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[1]]);
556 ip += 2;
557 NEXT ();
560 CASE (PUSH_LITERAL_VAR) {
562 st_oop var;
564 var = ST_ASSOCIATION_VALUE (st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[1]]);
566 STACK_PUSH (var);
568 ip += 2;
569 NEXT ();
572 CASE (JUMP_TRUE) {
574 if (STACK_PEEK () == ST_TRUE) {
575 (void) STACK_POP ();
576 ip += *((unsigned short *) (ip + 1)) + 3;
577 } else if (ST_LIKELY (STACK_PEEK () == ST_FALSE)) {
578 (void) STACK_POP ();
579 ip += 3;
580 } else {
581 ip += 3;
582 SEND_SELECTOR (ST_SELECTOR_MUSTBEBOOLEAN, 0);
585 NEXT ();
589 CASE (JUMP_FALSE) {
591 if (STACK_PEEK () == ST_FALSE) {
592 (void) STACK_POP ();
593 ip += *((unsigned short *) (ip + 1)) + 3;
594 } else if (ST_LIKELY (STACK_PEEK () == ST_TRUE)) {
595 (void) STACK_POP ();
596 ip += 3;
597 } else {
598 ip += 3;
599 SEND_SELECTOR (ST_SELECTOR_MUSTBEBOOLEAN, 0);
602 NEXT ();
605 CASE (JUMP) {
607 ip += *((short *) (ip + 1)) + 3;
608 NEXT ();
611 CASE (SEND_PLUS) {
613 int a, b, result;
615 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
616 st_object_is_smi (sp[-2]))) {
617 b = st_smi_value (sp[-1]);
618 a = st_smi_value (sp[-2]);
619 result = a + b;
620 if (((result << 1) ^ (result << 2)) >= 0) {
621 sp -= 2;
622 STACK_PUSH (st_smi_new (result));
623 ip++;
624 NEXT ();
628 machine->message_argcount = 1;
629 machine->message_selector = ST_SELECTOR_PLUS;
630 machine->message_receiver = sp[- machine->message_argcount - 1];
631 machine->lookup_class = st_object_class (machine->message_receiver);
632 ip += 1;
633 goto send_common;
636 CASE (SEND_MINUS) {
638 int a, b, result;
640 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
641 st_object_is_smi (sp[-2]))) {
642 b = st_smi_value (sp[-1]);
643 a = st_smi_value (sp[-2]);
644 result = a - b;
645 if (((result << 1) ^ (result << 2)) >= 0) {
646 sp -= 2;
647 STACK_PUSH (st_smi_new (result));
648 ip++;
649 NEXT ();
650 } else {
651 STACK_UNPOP (2);
655 machine->message_argcount = 1;
656 machine->message_selector = ST_SELECTOR_MINUS;
657 machine->message_receiver = sp[- machine->message_argcount - 1];
658 machine->lookup_class = st_object_class (machine->message_receiver);
659 ip += 1;
660 goto send_common;
663 CASE (SEND_MUL) {
665 int a, b;
666 int64_t result;
668 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
669 st_object_is_smi (sp[-2]))) {
670 b = st_smi_value (sp[-1]);
671 a = st_smi_value (sp[-2]);
672 result = a * b;
673 if (result >= ST_SMALL_INTEGER_MIN && result <= ST_SMALL_INTEGER_MAX) {
674 sp -= 2;
675 STACK_PUSH (st_smi_new ((int)result));
676 ip++;
677 NEXT ();
681 machine->message_argcount = 1;
682 machine->message_selector = ST_SELECTOR_MUL;
683 machine->message_receiver = sp[- machine->message_argcount - 1];
684 machine->lookup_class = st_object_class (machine->message_receiver);
685 ip += 1;
686 goto send_common;
690 CASE (SEND_MOD) {
692 st_oop a, b;
694 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
695 st_object_is_smi (sp[-2]))) {
696 b = STACK_POP ();
697 a = STACK_POP ();
698 STACK_PUSH (st_smi_new (st_smi_value (a) % st_smi_value (b)));
699 ip++;
700 NEXT ();
703 machine->message_argcount = 1;
704 machine->message_selector = ST_SELECTOR_MOD;
705 machine->message_receiver = sp[- machine->message_argcount - 1];
706 machine->lookup_class = st_object_class (machine->message_receiver);
707 ip += 1;
708 goto send_common;
711 CASE (SEND_DIV) {
713 machine->message_argcount = 1;
714 machine->message_selector = ST_SELECTOR_DIV;
715 machine->message_receiver = sp[- machine->message_argcount - 1];
716 machine->lookup_class = st_object_class (machine->message_receiver);
717 ip += 1;
718 goto send_common;
721 CASE (SEND_BITSHIFT) {
723 st_oop a, b;
725 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
726 st_object_is_smi (sp[-2]))) {
727 b = STACK_POP ();
728 a = STACK_POP ();
729 if (st_smi_value (b) < 0) {
730 STACK_PUSH (st_smi_new (st_smi_value (a) >> -st_smi_value (b)));
731 } else
732 STACK_PUSH (st_smi_new (st_smi_value (a) << st_smi_value (b)));
733 ip++;
734 NEXT ();
737 machine->message_argcount = 1;
738 machine->message_selector = ST_SELECTOR_BITSHIFT;
739 machine->message_receiver = sp[- machine->message_argcount - 1];
740 machine->lookup_class = st_object_class (machine->message_receiver);
741 ip += 1;
742 goto send_common;
745 CASE (SEND_BITAND) {
747 st_oop a, b;
749 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
750 st_object_is_smi (sp[-2]))) {
751 b = STACK_POP ();
752 a = STACK_POP ();
753 STACK_PUSH (st_smi_new (st_smi_value (a) & st_smi_value (b)));
754 ip++;
755 NEXT ();
758 machine->message_argcount = 1;
759 machine->message_selector = ST_SELECTOR_BITAND;
760 machine->message_receiver = sp[- machine->message_argcount - 1];
761 machine->lookup_class = st_object_class (machine->message_receiver);
762 ip += 1;
763 goto send_common;
766 CASE (SEND_BITOR) {
768 st_oop a, b;
770 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
771 st_object_is_smi (sp[-2]))) {
772 b = STACK_POP ();
773 a = STACK_POP ();
774 STACK_PUSH (st_smi_new (st_smi_value (a) | st_smi_value (b)));
775 ip++;
776 NEXT ();
779 machine->message_argcount = 1;
780 machine->message_selector = ST_SELECTOR_BITOR;
781 machine->message_receiver = sp[- machine->message_argcount - 1];
782 machine->lookup_class = st_object_class (machine->message_receiver);
783 ip += 1;
784 goto send_common;
788 CASE (SEND_BITXOR) {
790 st_oop a, b;
792 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
793 st_object_is_smi (sp[-2]))) {
794 b = STACK_POP ();
795 a = STACK_POP ();
796 STACK_PUSH (st_smi_new (st_smi_value (a) ^ st_smi_value (b)));
797 ip++;
798 NEXT ();
801 machine->message_argcount = 1;
802 machine->message_selector = ST_SELECTOR_BITXOR;
803 machine->message_receiver = sp[- machine->message_argcount - 1];
804 machine->lookup_class = st_object_class (machine->message_receiver);
805 ip += 1;
806 goto send_common;
809 CASE (SEND_LT) {
811 st_oop a, b;
813 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
814 st_object_is_smi (sp[-2]))) {
815 b = STACK_POP ();
816 a = STACK_POP ();
817 STACK_PUSH (st_smi_value (a) < st_smi_value (b) ? ST_TRUE : ST_FALSE);
818 ip++;
819 NEXT ();
822 machine->message_argcount = 1;
823 machine->message_selector = ST_SELECTOR_LT;
824 machine->message_receiver = sp[- machine->message_argcount - 1];
825 machine->lookup_class = st_object_class (machine->message_receiver);
826 ip += 1;
827 goto send_common;
830 CASE (SEND_GT) {
832 st_oop a, b;
834 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
835 st_object_is_smi (sp[-2]))) {
836 b = STACK_POP ();
837 a = STACK_POP ();
838 STACK_PUSH (st_smi_value (a) > st_smi_value (b) ? ST_TRUE : ST_FALSE);
839 ip++;
840 NEXT ();
843 machine->message_argcount = 1;
844 machine->message_selector = ST_SELECTOR_GT;
845 machine->message_receiver = sp[- machine->message_argcount - 1];
846 machine->lookup_class = st_object_class (machine->message_receiver);
847 ip += 1;
848 goto send_common;
852 CASE (SEND_LE) {
854 st_oop a, b;
856 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
857 st_object_is_smi (sp[-2]))) {
858 b = STACK_POP ();
859 a = STACK_POP ();
860 STACK_PUSH (st_smi_value (a) <= st_smi_value (b) ? ST_TRUE : ST_FALSE);
861 ip++;
862 NEXT ();
865 machine->message_argcount = 1;
866 machine->message_selector = ST_SELECTOR_LE;
867 machine->message_receiver = sp[- machine->message_argcount - 1];
868 machine->lookup_class = st_object_class (machine->message_receiver);
869 ip += 1;
870 goto send_common;
873 CASE (SEND_GE) {
875 st_oop a, b;
877 if (ST_LIKELY (st_object_is_smi (sp[-1]) &&
878 st_object_is_smi (sp[-2]))) {
879 b = STACK_POP ();
880 a = STACK_POP ();
881 STACK_PUSH (st_smi_value (a) >= st_smi_value (b) ? ST_TRUE : ST_FALSE);
882 ip++;
883 NEXT ();
886 machine->message_argcount = 1;
887 machine->message_selector = ST_SELECTOR_GE;
888 machine->message_receiver = sp[- machine->message_argcount - 1];
889 machine->lookup_class = st_object_class (machine->message_receiver);
890 ip += 1;
891 goto send_common;
895 CASE (SEND_CLASS) {
897 machine->message_argcount = 0;
898 machine->message_selector = ST_SELECTOR_CLASS;
899 machine->message_receiver = sp[- machine->message_argcount - 1];
900 machine->lookup_class = st_object_class (machine->message_receiver);
901 ip += 1;
902 goto send_common;
905 CASE (SEND_SIZE) {
907 machine->message_argcount = 0;
908 machine->message_selector = ST_SELECTOR_SIZE;
909 machine->message_receiver = sp[- machine->message_argcount - 1];
910 machine->lookup_class = st_object_class (machine->message_receiver);
911 ip += 1;
912 goto send_common;
915 CASE (SEND_AT) {
917 machine->message_argcount = 1;
918 machine->message_selector = ST_SELECTOR_AT;
919 machine->message_receiver = sp[- machine->message_argcount - 1];
920 machine->lookup_class = st_object_class (machine->message_receiver);
921 ip += 1;
922 goto send_common;
925 CASE (SEND_AT_PUT) {
927 machine->message_argcount = 2;
928 machine->message_selector = ST_SELECTOR_ATPUT;
929 machine->message_receiver = sp[- machine->message_argcount - 1];
930 machine->lookup_class = st_object_class (machine->message_receiver);
931 ip += 1;
932 goto send_common;
935 CASE (SEND_EQ) {
937 machine->message_argcount = 1;
938 machine->message_selector = ST_SELECTOR_EQ;
939 machine->message_receiver = sp[- machine->message_argcount - 1];
940 machine->lookup_class = st_object_class (machine->message_receiver);
941 ip += 1;
942 goto send_common;
945 CASE (SEND_NE) {
947 machine->message_argcount = 1;
948 machine->message_selector = ST_SELECTOR_NE;
949 machine->message_receiver = sp[- machine->message_argcount - 1];
950 machine->lookup_class = st_object_class (machine->message_receiver);
951 ip += 1;
952 goto send_common;
955 CASE (SEND_IDENTITY_EQ) {
957 st_oop a, b;
958 a = STACK_POP ();
959 b = STACK_POP ();
961 STACK_PUSH ((a == b) ? ST_TRUE : ST_FALSE);
963 ip += 1;
964 NEXT ();
967 CASE (SEND_VALUE) {
969 machine->message_argcount = 0;
970 machine->message_selector = ST_SELECTOR_VALUE;
971 machine->message_receiver = sp[- machine->message_argcount - 1];
972 machine->lookup_class = st_object_class (machine->message_receiver);
973 ip += 1;
974 goto send_common;
977 CASE (SEND_VALUE_ARG) {
979 machine->message_argcount = 1;
980 machine->message_selector = ST_SELECTOR_VALUE_ARG;
981 machine->message_receiver = sp[- machine->message_argcount - 1];
982 machine->lookup_class = st_object_class (machine->message_receiver);
983 ip += 1;
984 goto send_common;
987 CASE (SEND_NEW) {
989 machine->message_argcount = 0;
990 machine->message_selector = ST_SELECTOR_NEW;
991 machine->message_receiver = sp[- machine->message_argcount - 1];
992 machine->lookup_class = st_object_class (machine->message_receiver);
993 ip += 1;
994 goto send_common;
997 CASE (SEND_NEW_ARG) {
999 machine->message_argcount = 1;
1000 machine->message_selector = ST_SELECTOR_NEW_ARG;
1001 machine->message_receiver = sp[- machine->message_argcount - 1];
1002 machine->lookup_class = st_object_class (machine->message_receiver);
1003 ip += 1;
1004 goto send_common;
1007 CASE (SEND) {
1009 st_uint primitive_index;
1010 st_method_flags flags;
1011 st_oop context;
1012 st_oop *arguments;
1014 machine->message_argcount = ip[1];
1015 machine->message_selector = st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[2]];
1016 machine->message_receiver = sp[- machine->message_argcount - 1];
1017 machine->lookup_class = st_object_class (machine->message_receiver);
1018 ip += 3;
1020 send_common:
1022 if (!lookup_method_in_cache (machine)) {
1023 STORE_REGISTERS ();
1024 machine->new_method = lookup_method (machine, machine->lookup_class);
1025 LOAD_REGISTERS ();
1026 install_method_in_cache (machine);
1029 flags = st_method_get_flags (machine->new_method);
1030 if (flags == ST_METHOD_PRIMITIVE) {
1031 primitive_index = st_method_get_primitive_index (machine->new_method);
1033 machine->success = true;
1034 STORE_REGISTERS ();
1035 st_primitives[primitive_index].func (machine);
1036 LOAD_REGISTERS ();
1038 if (ST_LIKELY (machine->success))
1039 NEXT ();
1042 /* store registers as a gc could occur */
1043 STORE_REGISTERS ();
1044 context = method_context_new (machine);
1045 LOAD_REGISTERS ();
1046 arguments = ST_METHOD_CONTEXT_STACK (context);
1047 for (int i = 0; i < machine->message_argcount; i++)
1048 arguments[i] = sp[- machine->message_argcount + i];
1049 sp -= machine->message_argcount + 1;
1051 STORE_REGISTERS ();
1052 st_machine_set_active_context (machine, context);
1053 LOAD_REGISTERS ();
1055 /* We have to nil these fields here. Its possible that
1056 that the objects they reference may be zapped by the gc.
1057 Another GC invocation may try to remap these fields not knowing that
1058 the references are invalid.
1059 FIXME: move this nilling out of such a critical execution path */
1060 machine->message_receiver = ST_NIL;
1061 machine->message_selector = ST_NIL;
1063 NEXT ();
1066 CASE (SEND_SUPER) {
1068 st_oop index;
1070 machine->message_argcount = ip[1];
1071 machine->message_selector = st_array_elements (ST_METHOD_LITERALS (machine->method))[ip[2]];
1072 machine->message_receiver = sp[- machine->message_argcount - 1];
1074 index = st_smi_value (st_arrayed_object_size (ST_METHOD_LITERALS (machine->method))) - 1;
1075 machine->lookup_class = ST_BEHAVIOR_SUPERCLASS (st_array_elements (ST_METHOD_LITERALS (machine->method))[index]);
1077 ip += 3;
1079 goto send_common;
1082 CASE (POP_STACK_TOP) {
1084 (void) STACK_POP ();
1086 ip += 1;
1087 NEXT ();
1090 CASE (DUPLICATE_STACK_TOP) {
1092 STACK_PUSH (STACK_PEEK ());
1094 ip += 1;
1095 NEXT ();
1098 CASE (BLOCK_COPY) {
1100 st_oop block;
1101 st_oop home;
1102 st_uint argcount = ip[1];
1103 st_uint initial_ip;
1105 ip += 2;
1107 initial_ip = ip - machine->bytecode + 3;
1109 STORE_REGISTERS ();
1110 block = block_context_new (machine, initial_ip, argcount);
1111 LOAD_REGISTERS ();
1113 STACK_PUSH (block);
1115 NEXT ();
1118 CASE (RETURN_STACK_TOP) {
1120 st_oop sender;
1121 st_oop value;
1122 st_oop home;
1124 value = STACK_PEEK ();
1126 if (ST_OBJECT_CLASS (machine->context) == ST_BLOCK_CONTEXT_CLASS)
1127 sender = ST_CONTEXT_PART_SENDER (ST_BLOCK_CONTEXT_HOME (machine->context));
1128 else {
1129 sender = ST_CONTEXT_PART_SENDER (machine->context);
1130 st_memory_recycle_context (machine->context);
1133 if (ST_UNLIKELY (sender == ST_NIL)) {
1134 STACK_PUSH (machine->context);
1135 STACK_PUSH (value);
1136 SEND_SELECTOR (ST_SELECTOR_CANNOTRETURN, 1);
1137 NEXT ();
1140 st_machine_set_active_context (machine, sender);
1141 LOAD_REGISTERS ();
1142 STACK_PUSH (value);
1144 NEXT ();
1147 CASE (BLOCK_RETURN) {
1149 st_oop caller;
1150 st_oop value;
1151 st_oop home;
1153 caller = ST_CONTEXT_PART_SENDER (machine->context);
1154 value = STACK_PEEK ();
1156 st_machine_set_active_context (machine, caller);
1157 LOAD_REGISTERS ();
1158 STACK_PUSH (value);
1160 NEXT ();
1163 INVALID () {
1164 abort ();
1169 out:
1170 st_log ("gc", "totalPauseTime: %.6fs\n",
1171 st_timespec_to_double_seconds (&memory->total_pause_time));
1174 void
1175 st_machine_clear_caches (st_machine *machine)
1177 memset (machine->method_cache, 0, ST_METHOD_CACHE_SIZE * 3 * sizeof (st_oop));
1180 void
1181 st_machine_initialize (st_machine *machine)
1183 st_oop context;
1184 st_oop method;
1186 /* clear contents */
1187 machine->context = ST_NIL;
1188 machine->receiver = ST_NIL;
1189 machine->method = ST_NIL;
1191 machine->sp = 0;
1192 machine->ip = 0;
1193 machine->stack = NULL;
1195 st_machine_clear_caches (machine);
1197 machine->message_argcount = 0;
1198 machine->message_receiver = ST_SMALLTALK;
1199 machine->message_selector = ST_SELECTOR_STARTUPSYSTEM;
1201 machine->new_method = lookup_method (machine, st_object_class (machine->message_receiver));
1202 st_assert (st_method_get_flags (machine->new_method) == ST_METHOD_NORMAL);
1204 context = method_context_new (machine);
1205 st_machine_set_active_context (machine, context);