aboutsummaryrefslogtreecommitdiffstats
path: root/compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c4914
1 files changed, 4914 insertions, 0 deletions
diff --git a/compile.c b/compile.c
new file mode 100644
index 0000000000..025e655457
--- /dev/null
+++ b/compile.c
@@ -0,0 +1,4914 @@
+/**********************************************************************
+
+ compile.c - ruby node tree -> yarv instruction sequence
+
+ $Author$
+ $Date$
+ created at: 04/01/01 03:42:15 JST
+
+ Copyright (C) 2004-2006 Koichi Sasada
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "node.h"
+
+#include "yarvcore.h"
+#include "compile.h"
+#include "insns.inc"
+#include "insns_info.inc"
+
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+/* types */
+
+#define ISEQ_ELEMENT_NONE INT2FIX(0x00)
+#define ISEQ_ELEMENT_LABEL INT2FIX(0x01)
+#define ISEQ_ELEMENT_INSN INT2FIX(0x02)
+#define ISEQ_ELEMENT_SEQ INT2FIX(0x03)
+
+typedef struct iseq_link_element {
+ int type;
+ struct iseq_link_element *next;
+ struct iseq_link_element *prev;
+} LINK_ELEMENT;
+
+typedef struct iseq_link_anchor {
+ LINK_ELEMENT anchor;
+ LINK_ELEMENT *last;
+} LINK_ANCHOR;
+
+typedef struct iseq_label_data {
+ LINK_ELEMENT link;
+ int label_no;
+ int position;
+ int sc_state;
+ int set;
+ int sp;
+} LABEL;
+
+typedef struct iseq_insn_data {
+ LINK_ELEMENT link;
+ int insn_id;
+ int line_no;
+ int operand_size;
+ int sc_state;
+ VALUE *operands;
+} INSN;
+
+struct ensure_range {
+ LABEL *begin;
+ LABEL *end;
+ struct ensure_range *next;
+};
+
+struct iseq_compile_data_ensure_node_stack {
+ NODE *ensure_node;
+ struct iseq_compile_data_ensure_node_stack *prev;
+ struct ensure_range *erange;
+};
+
+
+/* for debug */
+#if CPDEBUG > 0
+static long gl_node_level = 0;
+static long gl_tmp = 0;
+static void debug_list(LINK_ANCHOR *anchor);
+#endif
+
+static void dump_disasm_anchor(LINK_ANCHOR *anc);
+static void dump_disasm_list(LINK_ELEMENT *elem);
+
+static int insn_data_length(INSN *iobj);
+static int insn_data_line_no(INSN *iobj);
+static int calc_sp_depth(int depth, INSN *iobj);
+static int insn_ret_num(int insn);
+
+static void ADD_ELEM(LINK_ANCHOR *anchor, LINK_ELEMENT *elem);
+
+static INSN *new_insn_body(yarv_iseq_t *iseq, int line_no,
+ int insn_id, int argc, ...);
+static LABEL *new_label_body(yarv_iseq_t *iseq, int line);
+
+static int iseq_compile_each(yarv_iseq_t *iseq, LINK_ANCHOR *anchor,
+ NODE * n, int);
+static int iseq_setup(yarv_iseq_t *iseq, LINK_ANCHOR *anchor);
+
+static int iseq_optimize(yarv_iseq_t *iseq, LINK_ANCHOR *anchor);
+static int iseq_insns_unification(yarv_iseq_t *iseq, LINK_ANCHOR *anchor);
+static int set_sequence_stackcaching(yarv_iseq_t *iseq, LINK_ANCHOR *anchor);
+static int set_sequence(yarv_iseq_t *iseq, LINK_ANCHOR *anchor);
+
+static int set_exception_table(yarv_iseq_t *iseq);
+static int set_localtbl(yarv_iseq_t *iseq, ID *tbl);
+static int set_localtbl_eval(yarv_iseq_t *iseq, ID *tbl);
+static int set_arguments(yarv_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * node);
+static NODE *set_block_local_tbl(yarv_iseq_t *iseq, NODE * node,
+ LINK_ANCHOR *anchor);
+static int set_exception_tbl(yarv_iseq_t *iseq);
+static int set_optargs_table(yarv_iseq_t *iseq);
+
+static int
+iseq_add_mark_object(yarv_iseq_t *iseq, VALUE v)
+{
+ rb_ary_push(iseq->iseq_mark_ary, v);
+ return COMPILE_OK;
+}
+
+static int
+iseq_add_mark_object_compile_time(yarv_iseq_t *iseq, VALUE v)
+{
+ rb_ary_push(iseq->compile_data->mark_ary, v);
+ return COMPILE_OK;
+}
+
+
+#include "optinsn.inc"
+
+#if OPT_INSTRUCTIONS_UNIFICATION
+#include "optunifs.inc"
+#endif
+
+VALUE
+iseq_compile(VALUE self, NODE *narg)
+{
+ DECL_ANCHOR(list_anchor);
+ yarv_iseq_t *iseq;
+ NODE *node = (NODE *) narg;
+ GetISeqPtr(self, iseq);
+
+ debugs("[compile step 1 (traverse each node)]\n");
+
+
+ iseq->node = node;
+
+ if (iseq->type == ISEQ_TYPE_BLOCK) {
+ node = set_block_local_tbl(iseq, node, list_anchor);
+ }
+
+ if (node && nd_type(node) == NODE_SCOPE) {
+ /* with node scope */
+ NODE *sn_body = node->nd_next; /* sn: scope node */
+ NODE *ndargs = 0;
+
+ if (iseq->type != ISEQ_TYPE_BLOCK) {
+ set_localtbl(iseq, ((NODE *) node)->nd_tbl);
+ }
+
+ if (sn_body) {
+ switch (nd_type(sn_body)) {
+ case NODE_BLOCK:
+ if (nd_type(sn_body->nd_head) == NODE_ARGS) {
+ /* some method attribute process */
+ ndargs = sn_body->nd_head;
+ set_arguments(iseq, list_anchor, ndargs);
+
+ /* with sn_body->nd_head */
+ if (iseq->type == ISEQ_TYPE_METHOD) {
+ COMPILE(list_anchor, "normal method",
+ sn_body->nd_next);
+ }
+ else if (iseq->type == ISEQ_TYPE_CLASS) {
+ COMPILE(list_anchor, "class/module",
+ sn_body->nd_next);
+ }
+ else {
+ rb_bug("must be class or method");
+ }
+ }
+ else {
+ /* normal block */
+ if (iseq->type == ISEQ_TYPE_CLASS) {
+ COMPILE(list_anchor, "class/module", sn_body);
+ }
+ else if (iseq->type == ISEQ_TYPE_BLOCK) {
+ COMPILE(list_anchor, "normal block", sn_body);
+ }
+ else {
+ rb_bug("must be class or block");
+ }
+ }
+ break;
+ case NODE_ARGS:
+ /* empty method */
+ /* some method attribute process */
+ debugs("empty method\n");
+
+ set_arguments(iseq, list_anchor, sn_body);
+ ADD_INSN(list_anchor, nd_line(sn_body), putnil);
+ break;
+
+ default:
+ COMPILE(list_anchor, "other scope", sn_body);
+ break;
+ }
+ }
+ else {
+ /* sn_body == 0 */
+ ADD_INSN(list_anchor, 0, putnil);
+ }
+ }
+ else {
+ if (iseq->type == ISEQ_TYPE_BLOCK) {
+ VALUE tmp;
+ LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0);
+ LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0);
+
+ ADD_LABEL(list_anchor, iseq->compile_data->start_label);
+ COMPILE(list_anchor, "block body", node);
+ ADD_LABEL(list_anchor, iseq->compile_data->end_label);
+
+ /* wide range catch handler must put at last */
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, start);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
+ }
+ else if (iseq->type == ISEQ_TYPE_TOP) {
+ set_localtbl(iseq, GET_THREAD()->top_local_tbl);
+ COMPILE(list_anchor, "top level node", node);
+ }
+ else if (iseq->type == ISEQ_TYPE_EVAL) {
+ set_localtbl_eval(iseq, GET_THREAD()->top_local_tbl);
+ COMPILE(list_anchor, "eval node", node);
+ }
+ else if (iseq->type == ISEQ_TYPE_RESCUE) {
+ set_exception_tbl(iseq);
+ COMPILE(list_anchor, "rescue", node);
+ }
+ else if (iseq->type == ISEQ_TYPE_ENSURE) {
+ set_exception_tbl(iseq);
+ COMPILE_POPED(list_anchor, "ensure", node);
+ }
+ else if (iseq->type == ISEQ_TYPE_DEFINED_GUARD) {
+ COMPILE(list_anchor, "defined guard", node);
+ }
+ else if (node == 0) {
+ COMPILE(list_anchor, "nil", node);
+ }
+ else {
+ rb_bug("unknown scope");
+ }
+ }
+
+ GC_CHECK();
+
+ if (iseq->type == ISEQ_TYPE_RESCUE || iseq->type == ISEQ_TYPE_ENSURE) {
+ ADD_INSN2(list_anchor, 0, getdynamic, INT2FIX(1), INT2FIX(0));
+ ADD_INSN1(list_anchor, 0, throw, INT2FIX(0) /* continue throw */ );
+ }
+ else {
+ ADD_INSN(list_anchor, iseq->compile_data->last_line, leave);
+ }
+
+ return iseq_setup(iseq, list_anchor);
+}
+
+VALUE th_eval(void *);
+
+static int
+iseq_translate_direct_threaded_code(yarv_iseq_t *iseq)
+{
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+
+#if OPT_DIRECT_THREADED_CODE
+ void **table = (void **)th_eval(0);
+#else
+ extern void **insns_address_table();
+ void **table = get_insns_address_table();
+#endif
+ int i;
+
+ iseq->iseq_encoded = ALLOC_N(VALUE, iseq->size);
+ MEMCPY(iseq->iseq_encoded, iseq->iseq, VALUE, iseq->size);
+
+ for (i = 0; i < iseq->size; /* */ ) {
+ int insn = iseq->iseq_encoded[i];
+ int len = insn_len(insn);
+ iseq->iseq_encoded[i] = (VALUE)table[insn];
+ i += len;
+ }
+#else
+ iseq->iseq_encoded = iseq->iseq;
+#endif
+ return COMPILE_OK;
+}
+
+/*********************************************/
+/* definition of data structure for compiler */
+/*********************************************/
+
+
+static void *
+compile_data_alloc(yarv_iseq_t *iseq, size_t size)
+{
+ void *ptr = 0;
+ struct iseq_compile_data_storage *storage =
+ iseq->compile_data->storage_current;
+
+ if (storage->pos + size > storage->size) {
+ unsigned long alloc_size = storage->size * 2;
+
+ retry:
+ if (alloc_size < size) {
+ alloc_size *= 2;
+ goto retry;
+ }
+ storage->next = (void *)ALLOC_N(char, alloc_size +
+ sizeof(struct
+ iseq_compile_data_storage));
+ storage = iseq->compile_data->storage_current = storage->next;
+
+ storage->next = 0;
+ storage->pos = 0;
+ storage->size = alloc_size;
+ storage->buff = (char *)(&storage->buff + 1);
+ }
+
+ ptr = (void *)&storage->buff[storage->pos];
+ storage->pos += size;
+ return ptr;
+}
+
+static INSN *
+compile_data_alloc_insn(yarv_iseq_t *iseq)
+{
+ return (INSN *)compile_data_alloc(iseq, sizeof(INSN));
+}
+
+static LABEL *
+compile_data_alloc_label(yarv_iseq_t *iseq)
+{
+ return (LABEL *)compile_data_alloc(iseq, sizeof(LABEL));
+}
+
+/*
+ * To make Array to LinkedList, use link_anchor
+ */
+
+static void
+verify_list(char *info, LINK_ANCHOR *anchor)
+{
+#if CPDEBUG > 0
+ int flag = 0;
+ LINK_ELEMENT *list = anchor->anchor.next, *plist = &anchor->anchor;
+
+ while (list) {
+ if (plist != list->prev) {
+ flag += 1;
+ }
+ plist = list;
+ list = list->next;
+ }
+
+ if (anchor->last != plist && anchor->last != 0) {
+ flag |= 0x70000;
+ }
+
+ if (flag != 0) {
+ rb_bug("list verify error: %08x (%s)", flag, info);
+ }
+#endif
+}
+
+/*
+ * elem1, elem2 => elem1, elem2, elem
+ */
+static void
+ADD_ELEM(LINK_ANCHOR *anchor, LINK_ELEMENT *elem)
+{
+ elem->prev = anchor->last;
+ anchor->last->next = elem;
+ anchor->last = elem;
+ verify_list("add", anchor);
+}
+
+/*******************************************/
+#if 0
+/*
+ * elem1, elemX => elem1, elem2, elemX
+ */
+static void
+INSERT_ELEM_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+{
+ elem2->next = elem1->next;
+ elem2->prev = elem1;
+ elem1->next = elem2;
+ if (elem2->next) {
+ elem2->next->prev = elem2;
+ }
+}
+#endif
+
+/*
+ * elemX, elem1 => elemX, elem2, elem1
+ */
+static void
+INSERT_ELEM_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+{
+ elem2->prev = elem1->prev;
+ elem2->next = elem1;
+ elem1->prev = elem2;
+ if (elem2->prev) {
+ elem2->prev->next = elem2;
+ }
+}
+/*******************************************/
+
+/*
+ * elemX, elem1, elemY => elemX, elem2, elemY
+ */
+static void
+REPLACE_ELEM(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
+{
+ elem2->prev = elem1->prev;
+ elem2->next = elem1->next;
+ if (elem1->prev) {
+ elem1->prev->next = elem2;
+ }
+ if (elem1->next) {
+ elem1->next->prev = elem2;
+ }
+}
+
+static void
+REMOVE_ELEM(LINK_ELEMENT *elem)
+{
+ elem->prev->next = elem->next;
+ if (elem->next) {
+ elem->next->prev = elem->prev;
+ }
+}
+
+static LINK_ELEMENT *
+FIRST_ELEMENT(LINK_ANCHOR *anchor)
+{
+ return anchor->anchor.next;
+}
+
+/*
+static LINK_ELEMENT *
+LAST_ELEMENT(LINK_ANCHOR *anchor)
+{
+ return anchor->last;
+}
+ */
+
+static LINK_ELEMENT *
+POP_ELEMENT(LINK_ANCHOR *anchor)
+{
+ LINK_ELEMENT *elem = anchor->last;
+ anchor->last = anchor->last->prev;
+ anchor->last->next = 0;
+ verify_list("pop", anchor);
+ return elem;
+}
+
+static LINK_ELEMENT *
+SHIFT_ELEMENT(LINK_ANCHOR *anchor)
+{
+ LINK_ELEMENT *elem = anchor->anchor.next;
+ if (elem) {
+ anchor->anchor.next = elem->next;
+ }
+ return elem;
+}
+
+static int
+LIST_SIZE(LINK_ANCHOR *anchor)
+{
+ LINK_ELEMENT *elem = anchor->anchor.next;
+ int size = 0;
+ while (elem) {
+ size += 1;
+ elem = elem->next;
+ }
+ return size;
+}
+
+static int
+LIST_SIZE_ZERO(LINK_ANCHOR *anchor)
+{
+ if (anchor->anchor.next == 0) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+ * anc1: e1, e2, e3
+ * anc2: e4, e5
+ *#=>
+ * anc1: e1, e2, e3, e4, e5
+ * anc2: e4, e5 (broken)
+ */
+static void
+APPEND_LIST(LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
+{
+ if (anc2->anchor.next) {
+ anc1->last->next = anc2->anchor.next;
+ anc2->anchor.next->prev = anc1->last;
+ anc1->last = anc2->last;
+ }
+ verify_list("append", anc1);
+}
+
+/*
+ * anc1: e1, e2, e3
+ * anc2: e4, e5
+ *#=>
+ * anc1: e4, e5, e1, e2, e3
+ * anc2: e4, e5 (broken)
+ */
+static void
+INSERT_LIST(LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
+{
+ if (anc2->anchor.next) {
+ LINK_ELEMENT *first = anc1->anchor.next;
+ anc1->anchor.next = anc2->anchor.next;
+ anc1->anchor.next->prev = &anc1->anchor;
+ anc2->last->next = first;
+ if (first) {
+ first->prev = anc2->last;
+ }
+ else {
+ anc1->last = anc2->last;
+ }
+ }
+
+ verify_list("append", anc1);
+}
+
+#if 0
+/*
+ * anc1: e1, e2, e3
+ * anc2: e4, e5
+ *#=>
+ * anc1: e4, e5
+ * anc2: e1, e2, e3
+ */
+static void
+SWAP_LIST(LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
+{
+ LINK_ANCHOR tmp = *anc2;
+
+ /* it has bug */
+ *anc2 = *anc1;
+ *anc1 = tmp;
+
+ verify_list("swap1", anc1);
+ verify_list("swap2", anc2);
+}
+
+static LINK_ANCHOR *
+REVERSE_LIST(LINK_ANCHOR *anc)
+{
+ LINK_ELEMENT *first, *last, *elem, *e;
+ first = &anc->anchor;
+ elem = first->next;
+ last = anc->last;
+
+ if (elem != 0) {
+ anc->anchor.next = last;
+ anc->last = elem;
+ }
+ else {
+ /* null list */
+ return anc;
+ }
+ while (elem) {
+ e = elem->next;
+ elem->next = elem->prev;
+ elem->prev = e;
+ elem = e;
+ }
+
+ first->next = last;
+ last->prev = first;
+ anc->last->next = 0;
+
+ verify_list("reverse", anc);
+ return anc;
+}
+#endif
+
+#if CPDEBUG > 0
+static void
+debug_list(LINK_ANCHOR *anchor)
+{
+ LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
+ printf("----\n");
+ printf("anch: %p, frst: %p, last: %p\n", &anchor->anchor,
+ anchor->anchor.next, anchor->last);
+ while (list) {
+ printf("curr: %p, next: %p, prev: %p, type: %d\n", list, list->next,
+ list->prev, FIX2INT(list->type));
+ list = list->next;
+ }
+ printf("----\n");
+
+ dump_disasm_list(anchor->anchor.next);
+ verify_list("debug list", anchor);
+}
+#endif
+
+static LABEL *
+new_label_body(yarv_iseq_t *iseq, int line)
+{
+ LABEL *labelobj = compile_data_alloc_label(iseq);
+ static int label_no = 0;
+
+ labelobj->link.type = ISEQ_ELEMENT_LABEL;
+ labelobj->link.next = 0;
+
+ labelobj->label_no = label_no++;
+ labelobj->sc_state = 0;
+ labelobj->sp = -1;
+ return labelobj;
+}
+
+static INSN *
+new_insn_core(yarv_iseq_t *iseq, int line_no,
+ int insn_id, int argc, VALUE *argv)
+{
+ INSN *iobj = compile_data_alloc_insn(iseq);
+
+ iobj->link.type = ISEQ_ELEMENT_INSN;
+ iobj->link.next = 0;
+ iobj->insn_id = insn_id;
+ iobj->line_no = line_no;
+ iobj->operands = argv;
+ iobj->operand_size = argc;
+ iobj->sc_state = 0;
+ return iobj;
+}
+
+static INSN *
+new_insn_body(yarv_iseq_t *iseq, int line_no, int insn_id, int argc, ...)
+{
+ VALUE *operands = 0;
+ va_list argv;
+ if (argc > 0) {
+ int i;
+ va_init_list(argv, argc);
+ operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc);
+ for (i = 0; i < argc; i++) {
+ VALUE v = va_arg(argv, VALUE);
+ operands[i] = v;
+ }
+ va_end(argv);
+ }
+ return new_insn_core(iseq, line_no, insn_id, argc, operands);
+}
+
+static INSN *
+new_insn_send(yarv_iseq_t *iseq, int line_no,
+ VALUE id, VALUE argc, VALUE block, VALUE flag)
+{
+ INSN *iobj = 0;
+ VALUE *operands =
+ (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 5);
+ operands[0] = id;
+ operands[1] = argc;
+ operands[2] = block;
+ operands[3] = flag;
+ operands[4] = 0;
+ iobj = new_insn_core(iseq, line_no, BIN(send), 5, operands);
+ return iobj;
+}
+
+static VALUE
+new_child_iseq(yarv_iseq_t *iseq, NODE *node,
+ VALUE name, VALUE parent, VALUE type)
+{
+ VALUE args[6];
+ VALUE ret;
+
+ debugs("[new_child_iseq]> ---------------------------------------\n");
+ ret = yarv_iseq_new_with_opt(node, name, iseq_filename(iseq->self),
+ parent, type, iseq->compile_data->option);
+ debugs("[new_child_iseq]< ---------------------------------------\n");
+ iseq_add_mark_object(iseq, ret);
+ return ret;
+}
+
+static int
+iseq_setup(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
+{
+ // debugs("[compile step 2] (iseq_array_to_linkedlist)\n");
+
+ GC_CHECK();
+ if (CPDEBUG > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ GC_CHECK();
+
+ debugs("[compile step 3.1 (iseq_optimize)]\n");
+ iseq_optimize(iseq, anchor);
+
+ if (CPDEBUG > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ GC_CHECK();
+
+ if (iseq->compile_data->option->instructions_unification) {
+ debugs("[compile step 3.2 (iseq_insns_unification)]\n");
+ iseq_insns_unification(iseq, anchor);
+ if (CPDEBUG > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ GC_CHECK();
+ }
+
+ if (iseq->compile_data->option->stack_caching) {
+ debugs("[compile step 3.3 (set_sequence_stackcaching)]\n");
+ set_sequence_stackcaching(iseq, anchor);
+ if (CPDEBUG > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ GC_CHECK();
+ }
+
+ debugs("[compile step 4.1 (set_sequence)]\n");
+ set_sequence(iseq, anchor);
+ if (CPDEBUG > 5)
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ GC_CHECK();
+
+ GC_CHECK();
+ debugs("[compile step 4.2 (set_exception_table)]\n");
+ set_exception_table(iseq);
+
+ debugs("[compile step 4.3 (set_optargs_table)] \n");
+ set_optargs_table(iseq);
+
+ debugs("[compile step 5 (iseq_translate_direct_threaded_code)] \n");
+ iseq_translate_direct_threaded_code(iseq);
+ GC_CHECK();
+
+ if (CPDEBUG > 1) {
+ VALUE str = iseq_disasm(iseq->self);
+ printf("%s\n", StringValueCStr(str));
+ fflush(stdout);
+ }
+ debugs("[compile step: finish]\n");
+
+ return 0;
+}
+
+VALUE
+iseq_assemble_setup(VALUE self, VALUE args, VALUE locals, VALUE insn_ary)
+{
+ /* unsupported */
+ return Qnil;
+}
+
+int
+set_exception_tbl(yarv_iseq_t *iseq)
+{
+ static ID id_dollar_bang;
+
+ if (!id_dollar_bang) {
+ id_dollar_bang = rb_intern("#$!");
+ }
+ iseq->local_tbl = (ID *)ALLOC_N(ID *, 1);
+ iseq->local_size = 1;
+ iseq->local_tbl[0] = id_dollar_bang;
+ return COMPILE_OK;
+}
+
+static int
+search_block_local_variables(NODE * node, VALUE local_vars)
+{
+ switch (nd_type(node)) {
+ case NODE_DASGN_CURR:{
+ rb_ary_push(local_vars, ID2SYM(node->nd_vid));
+ break;
+ }
+ case NODE_MASGN:{
+ NODE *narg = node->nd_head;
+ while (narg) {
+ search_block_local_variables(narg->nd_head, local_vars);
+ narg = narg->nd_next;
+ }
+ if (node->nd_args != 0 && (long)node->nd_args != -1) {
+ search_block_local_variables(node->nd_args, local_vars);
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ return COMPILE_OK;
+}
+
+static NODE *
+search_block_local_parameters(yarv_iseq_t *iseq, NODE * lnode)
+{
+ NODE *node = lnode;
+ NODE *nelem;
+ VALUE local_vars = rb_ary_new();
+ VALUE param_vars = rb_ary_new();
+
+ /* search args */
+ if (node->nd_var && (VALUE)node->nd_var != 1) {
+ switch (nd_type(node->nd_var)) {
+ case NODE_DASGN_CURR:
+ iseq->argc = 1;
+ rb_ary_push(param_vars, ID2SYM(node->nd_var->nd_vid));
+ debugi("block 1arg", node->nd_var->nd_vid);
+ break;
+ case NODE_MASGN:{
+ int i;
+ nelem = node->nd_var->nd_head;
+ if (nelem != 0) {
+ iseq->argc = node->nd_var->nd_head->nd_alen;
+ for (i = 0; i < iseq->argc; i++, nelem = nelem->nd_next) {
+ if (nd_type(nelem->nd_head) == NODE_DASGN_CURR) {
+ rb_ary_push(param_vars,
+ ID2SYM(nelem->nd_head->nd_vid));
+ debugi("block arg", nelem->nd_head->nd_vid);
+ }
+ else {
+ char buff[0x20];
+ ID id;
+ int idx = iseq->argc - RARRAY_LEN(param_vars);
+ snprintf(buff, 0x20, "#blp%d", idx);
+ id = rb_intern(buff);
+ rb_ary_push(param_vars, ID2SYM(id));
+ debugi("block arg (auto)", id);
+ search_block_local_variables(nelem->nd_head,
+ local_vars);
+ }
+ }
+ }
+ if (node->nd_var->nd_args) {
+ NODE *sn = node->nd_var->nd_args;
+ if ((long)sn != -1) {
+ if (nd_type(sn) == NODE_DASGN_CURR) {
+ rb_ary_push(param_vars, ID2SYM(sn->nd_vid));
+ }
+ else {
+ rb_ary_push(param_vars,
+ ID2SYM(rb_intern("#blp_splat")));
+ debugi("block/splat (auto)",
+ rb_intern("#blp_splat"));
+ }
+ }
+ }
+ break;
+ }
+ default:
+ rb_ary_push(param_vars, ID2SYM(rb_intern("#blp")));
+ debugi("block 1arg (auto)", rb_intern("#blp"));
+ iseq->argc = 1;
+ break;
+ }
+ }
+ else {
+ iseq->argc = 0;
+ }
+
+ node = node->nd_body;
+
+ /* other block local variables 2 */
+ if (node && nd_type(node) == NODE_BLOCK) {
+ nelem = node->nd_head;
+ if (nelem && nd_type(nelem) == NODE_DASGN_CURR) {
+ while (nelem && nd_type(nelem) == NODE_DASGN_CURR) {
+ if (!rb_ary_includes(local_vars, ID2SYM(nelem->nd_vid))) {
+ debugi("block initialized variable", nelem->nd_vid);
+ rb_ary_push(local_vars, ID2SYM(nelem->nd_vid));
+ }
+ nelem = nelem->nd_value;
+ }
+ if (nelem == 0) {
+ node = node->nd_next;
+ }
+ }
+ }
+
+ /* translate to block inlining code */
+ if (iseq->special_block_builder != 0) {
+ node = ((NODE * (*)(yarv_iseq_t *, NODE *, NODE *, VALUE, VALUE))
+ iseq->special_block_builder) (iseq, node, lnode->nd_var,
+ param_vars, local_vars);
+ }
+
+ rb_ary_concat(param_vars, local_vars);
+ local_vars = param_vars;
+
+ {
+ int i, size = RARRAY_LEN(local_vars);
+
+ if (size > 0) {
+ iseq->local_tbl = ALLOC_N(ID, size);
+ for (i = 0; i < size; i++) {
+ iseq->local_tbl[i] = SYM2ID(RARRAY_PTR(local_vars)[i]);
+ debugi("block local variable", iseq->local_tbl[i]);
+ }
+ }
+ iseq->local_size = size;
+ }
+ return node;
+}
+
+static int
+set_block_initializer(yarv_iseq_t *iseq, NODE * node, LINK_ANCHOR *anchor, int didx)
+{
+ DECL_ANCHOR(anc);
+ LINK_ELEMENT *elem;
+
+ COMPILE_POPED(anc, "set_block_local_tbl#masgn/other", node);
+
+ if (nd_type(node) == NODE_ATTRASGN) {
+ INSN *iobj = (INSN *)anc->last->prev;
+ iobj->operands[1] = INT2FIX(FIX2INT(iobj->operands[1]) + 1);
+ INSERT_ELEM_PREV((void *)iobj,
+ (void *)new_insn_body(iseq, nd_line(node),
+ BIN(getdynamic), 2,
+ INT2FIX(didx), INT2FIX(0)));
+ }
+ else {
+ ADD_INSN2(anchor, nd_line(node), getdynamic,
+ INT2FIX(didx), INT2FIX(0));
+ elem = FIRST_ELEMENT(anc);
+ if (elem->type == ISEQ_ELEMENT_INSN &&
+ ((INSN *)elem)->insn_id == BIN(putnil)) {
+ SHIFT_ELEMENT(anc);
+ }
+ }
+ APPEND_LIST(anchor, anc);
+
+ return COMPILE_OK;
+}
+
+static NODE *
+set_block_local_tbl(yarv_iseq_t *iseq, NODE * node, LINK_ANCHOR *anchor)
+{
+ NODE *rnode;
+
+ /* argument check */
+ if (iseq->type != ISEQ_TYPE_BLOCK) {
+ rb_bug("set_block_local_tbl: unexpected iseq type");
+ }
+
+ rnode = search_block_local_parameters(iseq, node);
+
+ if ((VALUE)node->nd_var == 1) {
+ /* TODO */
+ }
+ else if (node->nd_var) {
+ NODE *nargs = node->nd_var;
+ switch (nd_type(nargs)) {
+ case NODE_MASGN:{
+ NODE *massign = nargs;
+ int i = 0;
+ if (nargs->nd_head != 0) {
+ NODE *lhsn = massign->nd_head;
+
+ while (lhsn) {
+ if (nd_type(lhsn->nd_head) != NODE_DASGN_CURR) {
+ /* idx-th param, current level */
+ set_block_initializer(iseq, lhsn->nd_head,
+ anchor, iseq->local_size - i);
+ }
+ i++;
+ lhsn = lhsn->nd_next;
+ }
+ }
+
+ /* check rest */
+ if (massign->nd_args != 0 && (long)massign->nd_args != -1) {
+ iseq->argc++;
+ iseq->arg_rest = i + 1;
+
+ if (nd_type(massign->nd_args) != NODE_DASGN_CURR) {
+ set_block_initializer(iseq, massign->nd_args,
+ anchor, iseq->local_size - i);
+ }
+ }
+ else if (i == 1) {
+ iseq->arg_rest = -1;
+ }
+ break;
+ }
+
+ case NODE_DASGN_CURR:
+ break;
+
+ /* for 1.x compatibility */
+ default:{
+ /* first param, current level */
+ set_block_initializer(iseq, nargs, anchor, iseq->local_size);
+ break;
+ }
+ }
+ }
+
+ if (iseq->arg_opts || iseq->arg_rest) {
+ iseq->arg_simple = 0;
+ }
+ else {
+ iseq->arg_simple = 1;
+ }
+
+ if (nd_type(node) == NODE_FOR) {
+ iseq->compile_data->for_iseq = 1;
+ }
+ return rnode;
+}
+
+static int
+get_dyna_var_idx_at_raw(yarv_iseq_t *iseq, ID id)
+{
+ int i;
+ for (i = 0; i < iseq->local_size; i++) {
+ if (iseq->local_tbl[i] == id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+get_dyna_var_idx(yarv_iseq_t *iseq, ID id, int *level, int *ls)
+{
+ int lv = 0, idx;
+
+ while (iseq) {
+ if ((idx = get_dyna_var_idx_at_raw(iseq, id)) >= 0) {
+ *level = lv;
+ *ls = iseq->local_size;
+ return idx;
+ }
+ iseq = iseq->parent_iseq;
+ lv++;
+ }
+ return -1;
+}
+
+/**
+
+ */
+static int
+set_arguments(yarv_iseq_t *iseq, LINK_ANCHOR *optargs, NODE * node)
+{
+ int i, j;
+
+ if (node) {
+ /* normal method */
+ if (node->nd_frml) {
+ iseq->argc = RARRAY_LEN(node->nd_frml);
+ }
+ else {
+ iseq->argc = 0;
+ }
+
+ if (node->nd_rest) {
+ iseq->arg_rest = node->nd_rest->nd_cnt - 2 + 1;
+ }
+
+ /* optional initializer */
+ if (node->nd_opt) {
+ NODE *optarg = node->nd_opt;
+ LABEL *label;
+ VALUE labels = rb_ary_new();
+ i = 0;
+ while (optarg) {
+ label = NEW_LABEL(nd_line(node));
+ rb_ary_push(labels, (VALUE)label | 1);
+ ADD_LABEL(optargs, label);
+ COMPILE_POPED(optargs, "optarg", optarg->nd_head);
+
+ optarg = optarg->nd_next;
+ i += 1;
+ }
+ /* last label */
+ label = NEW_LABEL(nd_line(node));
+ rb_ary_push(labels, (VALUE)label | 1);
+ ADD_LABEL(optargs, label);
+ i += 1;
+
+ iseq->arg_opts = i;
+ iseq->arg_opt_tbl = ALLOC_N(VALUE, i);
+ MEMCPY(iseq->arg_opt_tbl, RARRAY_PTR(labels), VALUE, i);
+ for (j = 0; j < i; j++) {
+ iseq->arg_opt_tbl[j] &= ~1;
+ }
+ }
+ else {
+ iseq->arg_opts = 0;
+ }
+ }
+ else {
+ iseq->argc = 0;
+ iseq->arg_rest = 0;
+ iseq->arg_opts = 0;
+ }
+
+ if (iseq->arg_rest != 0 || iseq->arg_opts != 0) {
+ iseq->arg_simple = 0;
+ }
+ else {
+ iseq->arg_simple = 1;
+ }
+ return COMPILE_OK;
+}
+
+static int
+set_localtbl(yarv_iseq_t *iseq, ID *tbl)
+{
+ int size;
+ if (tbl) {
+ size = *tbl - 2 /* $~, $_ */ + 1 /* svar location */ ;
+ }
+ else {
+ size = 1;
+ }
+ iseq->local_tbl = (ID *)ALLOC_N(ID *, size);
+ if (tbl && size > 1) {
+ MEMCPY(iseq->local_tbl + 1, tbl + 3, ID *, size - 1);
+ }
+ iseq->local_size = size;
+ return COMPILE_OK;
+}
+
+static int
+set_localtbl_eval(yarv_iseq_t *iseq, ID *tbl)
+{
+ int size;
+ if (tbl) {
+ size = *tbl;
+ }
+ else {
+ size = 0;
+ }
+ if (tbl) {
+ iseq->local_tbl = (ID *)ALLOC_N(ID *, size);
+ MEMCPY(iseq->local_tbl, tbl + 1, ID *, size);
+ }
+ iseq->local_size = size;
+ return COMPILE_OK;
+}
+
+/**
+ ruby insn object array -> raw instruction sequence
+ */
+static int
+set_sequence(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
+{
+ LABEL *lobj;
+ INSN *iobj;
+ struct insn_info_struct *insn_info_tbl;
+ LINK_ELEMENT *list;
+ VALUE *generated_iseq;
+
+ int k, pos, sp, stack_max = 0;
+
+ GC_CHECK();
+
+ /* set label position */
+ list = FIRST_ELEMENT(anchor);
+ k = pos = 0;
+ while (list) {
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:{
+ iobj = (INSN *)list;
+ pos += insn_data_length(iobj);
+
+ k += 1;
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:{
+ lobj = (LABEL *)list;
+ lobj->position = pos;
+ lobj->set = Qtrue;
+ break;
+ }
+ case ISEQ_ELEMENT_NONE:{
+ /* ignore */
+ break;
+ }
+ default:
+ dump_disasm_list(FIRST_ELEMENT(anchor));
+ dump_disasm_list(list);
+ rb_bug("error: set_sequence");
+ break;
+ }
+ list = list->next;
+ }
+
+ /* make instruction sequence */
+ generated_iseq = ALLOC_N(VALUE, pos);
+ insn_info_tbl = ALLOC_N(struct insn_info_struct, k);
+
+ GC_CHECK();
+
+ list = FIRST_ELEMENT(anchor);
+ k = pos = sp = 0;
+
+ while (list) {
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:{
+ int j, len, insn;
+ char *types;
+ VALUE *operands;
+
+ iobj = (INSN *)list;
+
+ if (iobj->insn_id == BIN(emptstack) && sp == 0) {
+ iobj->insn_id = BIN(nop);
+ }
+ else {
+ sp = calc_sp_depth(sp, iobj);
+ if (sp > stack_max) {
+ stack_max = sp;
+ }
+ }
+
+ // fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp);
+
+ operands = iobj->operands;
+ insn = iobj->insn_id;
+ generated_iseq[pos] = insn;
+ types = insn_op_types(insn);
+ len = insn_len(insn);
+
+ /* operand check */
+ if (iobj->operand_size != len - 1) {
+ dump_disasm_list(list);
+ rb_bug("operand size miss! (%d for %d)",
+ iobj->operand_size, len - 1);
+ return 0;
+ }
+
+ for (j = 0; types[j]; j++) {
+ char type = types[j];
+ // printf("--> [%c - (%d-%d)]\n", type, k, j);
+ switch (type) {
+ case TS_OFFSET:{
+ /* label(destination position) */
+ lobj = (LABEL *)operands[j];
+ if (lobj->set != Qtrue) {
+ rb_bug("unknown label");
+ }
+ if (lobj->sp == -1) {
+ lobj->sp = sp;
+ }
+ generated_iseq[pos + 1 + j] =
+ lobj->position - (pos + len);
+ break;
+ }
+ case TS_CDHASH:{
+ /*
+ * [obj, label, ...]
+ */
+ int i;
+ VALUE lits = operands[j];
+ VALUE map = rb_hash_new();
+
+ for (i=0; i < RARRAY_LEN(lits); i+=2) {
+ VALUE obj = rb_ary_entry(lits, i);
+ VALUE lv = rb_ary_entry(lits, i+1);
+ lobj = (LABEL *)(lv & ~1);
+
+ if (lobj->set != Qtrue) {
+ rb_bug("unknown label");
+ }
+ rb_hash_aset(map, obj,
+ INT2FIX(lobj->position - (pos+len)));
+ }
+ generated_iseq[pos + 1 + j] = map;
+ iseq_add_mark_object(iseq, map);
+ break;
+ }
+ case TS_LINDEX:
+ case TS_DINDEX:
+ case TS_NUM: /* ulong */
+ generated_iseq[pos + 1 + j] = FIX2INT(operands[j]);
+ break;
+ case TS_ISEQ: /* iseq */
+ {
+ VALUE v = operands[j];
+ yarv_iseq_t *block = 0;
+ if (v) {
+ GetISeqPtr(v, block);
+ }
+ generated_iseq[pos + 1 + j] = (VALUE)block;
+ break;
+ }
+ case TS_VALUE: /* VALUE */
+ {
+ VALUE v = operands[j];
+ generated_iseq[pos + 1 + j] = v;
+ /* to mark ruby object */
+ if (!SPECIAL_CONST_P(v)) {
+ iseq_add_mark_object(iseq, v);
+ }
+ break;
+ }
+ case TS_IC: /* inline cache */
+ {
+ VALUE v = (VALUE)NEW_INLINE_CACHE_ENTRY();
+ generated_iseq[pos + 1 + j] = v;
+ iseq_add_mark_object(iseq, v);
+ break;
+ }
+ case TS_ID: /* ID */
+ generated_iseq[pos + 1 + j] = SYM2ID(operands[j]);
+ break;
+ case TS_GENTRY:
+ {
+ struct global_entry *entry =
+ (struct global_entry *)(operands[j] & (~1));
+ generated_iseq[pos + 1 + j] = (VALUE)entry;
+ }
+ break;
+ default:
+ rb_bug("unknown operand type: %c", type);
+ return 0;
+ }
+ }
+ insn_info_tbl[k].line_no = iobj->line_no;
+ insn_info_tbl[k].position = pos;
+ pos += len;
+ k++;
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:{
+ lobj = (LABEL *)list;
+ if (lobj->sp == -1) {
+ lobj->sp = sp;
+ }
+ else {
+ sp = lobj->sp;
+ }
+ break;
+ }
+ default:
+ /* ignore */
+ break;
+ }
+ list = list->next;
+ }
+
+ {
+ iseq->iseq = (void *)generated_iseq;
+ iseq->size = pos;
+ iseq->insn_info_tbl = insn_info_tbl;
+ iseq->insn_info_size = k;
+ iseq->stack_max = stack_max;
+ }
+ return COMPILE_OK;
+}
+
+static int
+label_get_position(LABEL *lobj)
+{
+ return lobj->position;
+}
+
+static int
+label_get_sp(LABEL *lobj)
+{
+ return lobj->sp;
+}
+
+static int
+set_exception_table(yarv_iseq_t *iseq)
+{
+ VALUE *tptr, *ptr;
+ int tlen, i;
+ struct catch_table_entry *entry;
+
+ tlen = RARRAY_LEN(iseq->compile_data->catch_table_ary);
+ tptr = RARRAY_PTR(iseq->compile_data->catch_table_ary);
+
+ iseq->catch_table = ALLOC_N(struct catch_table_entry, tlen);
+ iseq->catch_table_size = tlen;
+
+ for (i = 0; i < tlen; i++) {
+ ptr = RARRAY_PTR(tptr[i]);
+ entry = &iseq->catch_table[i];
+ entry->type = ptr[0] & 0xffff;
+ entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
+ entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
+ entry->iseq = ptr[3];
+
+ /* register iseq as mark object */
+ if (entry->iseq != 0) {
+ iseq_add_mark_object(iseq, entry->iseq);
+ }
+
+ /* stack depth */
+ if (ptr[4]) {
+ LABEL *lobj = (LABEL *)(ptr[4] & ~1);
+ entry->cont = label_get_position(lobj);
+ entry->sp = label_get_sp(lobj);
+
+ /* TODO: Dirty Hack! Fix me */
+ if (entry->type == CATCH_TYPE_RESCUE ||
+ entry->type == CATCH_TYPE_BREAK ||
+ (((ptr[0] & 0x10000) == 0)
+ && entry->type == CATCH_TYPE_NEXT)) {
+ entry->sp--;
+ }
+ }
+ else {
+ entry->cont = 0;
+ }
+ }
+ //
+ iseq->compile_data->catch_table_ary = 0; /* free */
+ return COMPILE_OK;
+}
+
+/*
+ * set optional argument table
+ * def foo(a, b=expr1, c=expr2)
+ * =>
+ * b:
+ * expr1
+ * c:
+ * expr2
+ */
+static int
+set_optargs_table(yarv_iseq_t *iseq)
+{
+ int i;
+
+ if (iseq->arg_opts != 0) {
+ for (i = 0; i < iseq->arg_opts; i++) {
+ iseq->arg_opt_tbl[i] =
+ label_get_position((LABEL *)iseq->arg_opt_tbl[i]);
+ }
+ }
+ return COMPILE_OK;
+}
+
+static LINK_ELEMENT *
+get_destination_insn(INSN *iobj)
+{
+ LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
+ LINK_ELEMENT *list;
+
+ list = lobj->link.next;
+ while (list) {
+ if (list->type == ISEQ_ELEMENT_INSN) {
+ break;
+ }
+ list = list->next;
+ }
+ return list;
+}
+
+static LINK_ELEMENT *
+get_next_insn(INSN *iobj)
+{
+ LINK_ELEMENT *list = iobj->link.next;
+
+ while (list) {
+ if (list->type == ISEQ_ELEMENT_INSN) {
+ return list;
+ }
+ list = list->next;
+ }
+ return 0;
+}
+
+static LINK_ELEMENT *
+get_prev_insn(INSN *iobj)
+{
+ LINK_ELEMENT *list = iobj->link.prev;
+
+ while (list) {
+ if (list->type == ISEQ_ELEMENT_INSN) {
+ return list;
+ }
+ list = list->prev;
+ }
+ return 0;
+}
+
+static int
+iseq_peephole_optimize(yarv_iseq_t *iseq, LINK_ELEMENT *list)
+{
+ INSN *iobj = (INSN *)list;
+ again:
+ if (iobj->insn_id == BIN(jump)) {
+ INSN *niobj, *diobj, *piobj;
+ /*
+ * useless jump elimination:
+ * jump LABEL1
+ * ...
+ * LABEL1:
+ * jump LABEL2
+ *
+ * => in this case, first jump instruction should jump tp
+ * LABEL2 directly
+ */
+ diobj = (INSN *)get_destination_insn(iobj);
+ niobj = (INSN *)get_next_insn(iobj);
+
+ if (diobj == niobj) {
+ REMOVE_ELEM(&iobj->link);
+ }
+ else if (iobj != diobj && diobj->insn_id == BIN(jump)) {
+ OPERAND_AT(iobj, 0) = OPERAND_AT(diobj, 0);
+ goto again;
+ }
+ else if (diobj->insn_id == BIN(leave)) {
+ INSN *eiobj = new_insn_core(iseq, iobj->line_no, BIN(leave),
+ diobj->operand_size,
+ diobj->operands);
+ /* replace */
+ REPLACE_ELEM((LINK_ELEMENT *)iobj, (LINK_ELEMENT *)eiobj);
+ }
+ /*
+ * useless jump elimination (if/unless destination):
+ * if L1
+ * jump L2
+ * L1:
+ * ...
+ * L2:
+ *
+ * ==>
+ * unless L2
+ * L1:
+ * ...
+ * L2:
+ */
+ else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&
+ (piobj->insn_id == BIN(branchif) ||
+ piobj->insn_id == BIN(branchunless))) {
+ if (niobj == (INSN *)get_destination_insn(piobj)) {
+ piobj->insn_id = (piobj->insn_id == BIN(branchif))
+ ? BIN(branchunless) : BIN(branchif) ;
+ OPERAND_AT(piobj, 0) = OPERAND_AT(iobj, 0);
+ REMOVE_ELEM(&iobj->link);
+ }
+ }
+ }
+ if (iobj->insn_id == BIN(branchif) ||
+ iobj->insn_id == BIN(branchunless)) {
+ /*
+ * if L1
+ * ...
+ * L1:
+ * jump L2
+ * =>
+ * if L2
+ */
+ INSN *nobj = (INSN *)get_destination_insn(iobj);
+ if (nobj->insn_id == BIN(jump)) {
+ OPERAND_AT(iobj, 0) = OPERAND_AT(nobj, 0);
+ }
+ }
+
+ if (iobj->insn_id == BIN(leave)) {
+ INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
+ if (piobj->insn_id == BIN(send)) {
+ /* TODO: tail call optimization */
+ if (piobj->operands[2] == 0) {
+ //piobj->operands[3] = INT2FIX(FIX2INT(piobj->operands[3]) | VM_CALL_TAILCALL_BIT);
+ //piobj->operands[3] = INT2FIX(FIX2INT(piobj->operands[3]) | VM_CALL_TAILRECURSION_BIT);
+ }
+ }
+ }
+ return COMPILE_OK;
+}
+
+static int
+insn_set_specialized_instruction(INSN *iobj, int insn_id)
+{
+ iobj->insn_id = insn_id;
+ iobj->operand_size = 0;
+ return COMPILE_OK;
+}
+
+
+static int
+iseq_specialized_instruction(yarv_iseq_t *iseq, INSN *iobj)
+{
+ if (iobj->insn_id == BIN(send)) {
+ ID mid = SYM2ID(OPERAND_AT(iobj, 0));
+ int argc = FIX2INT(OPERAND_AT(iobj, 1));
+ VALUE block = OPERAND_AT(iobj, 2);
+ VALUE flag = OPERAND_AT(iobj, 3);
+
+ /* TODO: should be more sophisticated search */
+ if (block == 0 && flag == INT2FIX(0)) {
+ if (argc == 0) {
+ if (mid == idLength) {
+ insn_set_specialized_instruction(iobj, BIN(opt_length));
+ }
+ else if (mid == idSucc) {
+ insn_set_specialized_instruction(iobj, BIN(opt_succ));
+ }
+ }
+ else if (argc == 1) {
+ if (0) {
+ }
+ else if (mid == idPLUS) {
+ insn_set_specialized_instruction(iobj, BIN(opt_plus));
+ }
+ else if (mid == idMINUS) {
+ insn_set_specialized_instruction(iobj, BIN(opt_minus));
+ }
+ else if (mid == idMULT) {
+ insn_set_specialized_instruction(iobj, BIN(opt_mult));
+ }
+ else if (mid == idDIV) {
+ insn_set_specialized_instruction(iobj, BIN(opt_div));
+ }
+ else if (mid == idMOD) {
+ insn_set_specialized_instruction(iobj, BIN(opt_mod));
+ }
+ else if (mid == idEq) {
+ insn_set_specialized_instruction(iobj, BIN(opt_eq));
+ }
+ else if (mid == idLT) {
+ insn_set_specialized_instruction(iobj, BIN(opt_lt));
+ }
+ else if (mid == idLE) {
+ insn_set_specialized_instruction(iobj, BIN(opt_le));
+ }
+ else if (mid == idLTLT) {
+ insn_set_specialized_instruction(iobj, BIN(opt_ltlt));
+ }
+ else if (mid == idAREF) {
+ insn_set_specialized_instruction(iobj, BIN(opt_aref));
+ }
+ }
+ }
+ }
+ return COMPILE_OK;
+}
+
+static int
+iseq_optimize(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
+{
+ LINK_ELEMENT *list;
+ const int do_peephole = iseq->compile_data->option->peephole_optimization;
+ const int do_si = iseq->compile_data->option->specialized_instruction;
+ const int do_ou = iseq->compile_data->option->operands_unification;
+ list = FIRST_ELEMENT(anchor);
+
+ while (list) {
+ if (list->type == ISEQ_ELEMENT_INSN) {
+ if (do_peephole) {
+ iseq_peephole_optimize(iseq, list);
+ }
+ if (do_si) {
+ iseq_specialized_instruction(iseq, (INSN *)list);
+ }
+ if (do_ou) {
+ insn_operands_unification((INSN *)list);
+ }
+ }
+ list = list->next;
+ }
+ return COMPILE_OK;
+}
+
+#if OPT_INSTRUCTIONS_UNIFICATION
+static INSN *
+new_unified_insn(yarv_iseq_t *iseq,
+ int insn_id, int size, LINK_ELEMENT *seq_list)
+{
+ INSN *iobj = 0;
+ LINK_ELEMENT *list = seq_list;
+ int i, argc = 0;
+ VALUE *operands = 0, *ptr = 0;
+
+
+ /* count argc */
+ for (i = 0; i < size; i++) {
+ iobj = (INSN *)list;
+ argc += iobj->operand_size;
+ list = list->next;
+ }
+
+ if (argc > 0) {
+ ptr = operands =
+ (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc);
+ }
+
+ /* copy operands */
+ list = seq_list;
+ for (i = 0; i < size; i++) {
+ iobj = (INSN *)list;
+ MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
+ ptr += iobj->operand_size;
+ list = list->next;
+ }
+
+ return new_insn_core(iseq, iobj->line_no, insn_id, argc, operands);
+}
+#endif
+
+/*
+ * This scheme can get more performance if do this optimize with
+ * label address resolving.
+ * It's future work (if compile time was bottle neck).
+ */
+static int
+iseq_insns_unification(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
+{
+#if OPT_INSTRUCTIONS_UNIFICATION
+ LINK_ELEMENT *list;
+ INSN *iobj, *niobj;
+ int id, j, k;
+
+ list = FIRST_ELEMENT(anchor);
+ while (list) {
+ if (list->type == ISEQ_ELEMENT_INSN) {
+ iobj = (INSN *)list;
+ id = iobj->insn_id;
+ if (unified_insns_data[id] != 0) {
+ int **entry = unified_insns_data[id];
+ for (j = 1; j < (int)entry[0]; j++) {
+ int *unified = entry[j];
+ LINK_ELEMENT *li = list->next;
+ for (k = 2; k < unified[1]; k++) {
+ if (li->type != ISEQ_ELEMENT_INSN ||
+ ((INSN *)li)->insn_id != unified[k]) {
+ goto miss;
+ }
+ li = li->next;
+ }
+ /* matched */
+ niobj =
+ new_unified_insn(iseq, unified[0], unified[1] - 1,
+ list);
+
+ /* insert to list */
+ niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
+ niobj->link.next = li;
+ if (li) {
+ li->prev = (LINK_ELEMENT *)niobj;
+ }
+
+ list->prev->next = (LINK_ELEMENT *)niobj;
+ list = (LINK_ELEMENT *)niobj;
+ break;
+ miss:;
+ }
+ }
+ }
+ list = list->next;
+ }
+#endif
+ return COMPILE_OK;
+}
+
+#if OPT_STACK_CACHING
+
+#define SC_INSN(insn, stat) sc_insn_info[(insn)][(stat)]
+#define SC_NEXT(insn) sc_insn_next[insn]
+
+#include "opt_sc.inc"
+
+static int
+insn_set_sc_state(INSN *iobj, int state)
+{
+ int nstate;
+ int insn_id;
+
+ insn_id = iobj->insn_id;
+ iobj->insn_id = SC_INSN(insn_id, state);
+ nstate = SC_NEXT(iobj->insn_id);
+
+ if (insn_id == BIN(jump) ||
+ insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
+ LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
+
+ if (lobj->sc_state != 0) {
+ if (lobj->sc_state != nstate) {
+ dump_disasm_list((LINK_ELEMENT *)iobj);
+ dump_disasm_list((LINK_ELEMENT *)lobj);
+ printf("\n-- %d, %d\n", lobj->sc_state, nstate);
+ rb_bug("insn_set_sc_state error\n");
+ return 0;
+ }
+ }
+ else {
+ lobj->sc_state = nstate;
+ }
+ if (insn_id == BIN(jump)) {
+ nstate = SCS_XX;
+ }
+ }
+ else if (insn_id == BIN(leave)) {
+ nstate = SCS_XX;
+ }
+
+ return nstate;
+}
+
+static int
+label_set_sc_state(LABEL *lobj, int state)
+{
+ if (lobj->sc_state != 0) {
+ if (lobj->sc_state != state) {
+ state = lobj->sc_state;
+ }
+ }
+ else {
+ lobj->sc_state = state;
+ }
+
+ return state;
+}
+
+
+#endif
+
+static int
+set_sequence_stackcaching(yarv_iseq_t *iseq, LINK_ANCHOR *anchor)
+{
+#if OPT_STACK_CACHING
+ LINK_ELEMENT *list;
+ int state, insn_id;
+
+ /* initialize */
+ state = SCS_XX;
+ list = FIRST_ELEMENT(anchor);
+ // dump_disasm_list(list);
+
+ /* for each list element */
+ while (list) {
+ redo_point:
+ switch (list->type) {
+ case ISEQ_ELEMENT_INSN:{
+ INSN *iobj = (INSN *)list;
+ insn_id = iobj->insn_id;
+
+ // dump_disasm_list(list);
+
+ switch (insn_id) {
+ case BIN(nop):{
+ /* exception merge point */
+ if (state != SCS_AX) {
+ INSN *rpobj =
+ new_insn_body(iseq, 0, BIN(reput), 0);
+
+ /* replace this insn */
+ REPLACE_ELEM(list, (LINK_ELEMENT *)rpobj);
+ list = (LINK_ELEMENT *)rpobj;
+ goto redo_point;
+ }
+ break;
+ }
+ case BIN(swap):{
+ if (state == SCS_AB || state == SCS_BA) {
+ state = (state == SCS_AB ? SCS_BA : SCS_AB);
+
+ REMOVE_ELEM(list);
+ list = list->next;
+ goto redo_point;
+ }
+ break;
+ }
+ case BIN(pop):{
+ switch (state) {
+ case SCS_AX:
+ case SCS_BX:
+ state = SCS_XX;
+ break;
+ case SCS_AB:
+ state = SCS_AX;
+ break;
+ case SCS_BA:
+ state = SCS_BX;
+ break;
+ case SCS_XX:
+ goto normal_insn;
+ default:
+ rb_bug("unreachable");
+ }
+ /* remove useless pop */
+ REMOVE_ELEM(list);
+ list = list->next;
+ goto redo_point;
+ }
+ default:;
+ /* none */
+ } /* end of switch */
+ normal_insn:
+ state = insn_set_sc_state(iobj, state);
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:{
+ LABEL *lobj;
+ lobj = (LABEL *)list;
+
+ state = label_set_sc_state(lobj, state);
+ }
+ default:
+ break;
+ }
+ list = list->next;
+ }
+#endif
+ return COMPILE_OK;
+}
+
+
+
+static int
+compile_dstr(yarv_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
+{
+ NODE *list = node->nd_next;
+ VALUE lit = node->nd_lit;
+ int cnt = 1;
+
+ debugp_param("nd_lit", lit);
+ ADD_INSN1(ret, nd_line(node), putobject, node->nd_lit);
+
+ while (list) {
+ COMPILE(ret, "each string", list->nd_head);
+ cnt++;
+ list = list->nd_next;
+ }
+
+ ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt));
+ return COMPILE_OK;
+}
+
+static int
+compile_branch_condition(yarv_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
+ LABEL *then_label, LABEL *else_label)
+{
+ switch (nd_type(cond)) {
+ case NODE_NOT:
+ compile_branch_condition(iseq, ret, cond->nd_body, else_label,
+ then_label);
+ break;
+
+ case NODE_AND:{
+ LABEL *label = NEW_LABEL(nd_line(cond));
+ compile_branch_condition(iseq, ret, cond->nd_1st, label,
+ else_label);
+ ADD_LABEL(ret, label);
+ compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
+ else_label);
+ break;
+ }
+ case NODE_OR:{
+ LABEL *label = NEW_LABEL(nd_line(cond));
+ compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
+ label);
+ ADD_LABEL(ret, label);
+ compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
+ else_label);
+ break;
+ }
+ case NODE_LIT: /* NODE_LIT is always not true */
+ case NODE_TRUE:
+ case NODE_STR:
+ /* printf("useless conditon eliminate (%s)\n", node_name(nd_type(cond))); */
+ ADD_INSNL(ret, nd_line(cond), jump, then_label);
+ break;
+ case NODE_FALSE:
+ case NODE_NIL:
+ /* printf("useless conditon eliminate (%s)\n", node_name(nd_type(cond))); */
+ ADD_INSNL(ret, nd_line(cond), jump, else_label);
+ break;
+ default:
+ COMPILE(ret, "branch condition", cond);
+ ADD_INSNL(ret, nd_line(cond), branchunless, else_label);
+ ADD_INSNL(ret, nd_line(cond), jump, then_label);
+ break;
+ }
+ return COMPILE_OK;
+}
+
+static int
+compile_array(yarv_iseq_t *iseq,
+ LINK_ANCHOR *ret, NODE * node_root, VALUE opt_p)
+{
+ NODE *node = node_root;
+ int len = node->nd_alen, line = nd_line(node), i=0;
+ DECL_ANCHOR(anchor);
+
+ while (node) {
+ i++;
+ if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
+ opt_p = Qfalse;
+ }
+ COMPILE(anchor, "array element", node->nd_head);
+ node = node->nd_next;
+ }
+
+ if (len != i) {
+ if (0) rb_bug("node error: compile_array (%d: %d-%d)",
+ nd_line(node_root), len, i);
+ len = i;
+ }
+
+ if (opt_p == Qtrue) {
+ VALUE ary = rb_ary_new();
+ node = node_root;
+ while (node) {
+ rb_ary_push(ary, node->nd_head->nd_lit);
+ node = node->nd_next;
+ }
+
+ iseq_add_mark_object_compile_time(iseq, ary);
+ ADD_INSN1(ret, nd_line(node_root), duparray, ary);
+ }
+ else {
+ ADD_INSN1(anchor, line, newarray, INT2FIX(len));
+ APPEND_LIST(ret, anchor);
+ }
+ return len;
+}
+
+static VALUE
+case_when_optimizable_literal(NODE * node)
+{
+ if (nd_type(node) == NODE_LIT) {
+ VALUE v = node->nd_lit;
+ VALUE klass = CLASS_OF(v);
+ if (klass == rb_cSymbol || rb_obj_is_kind_of(v, rb_cNumeric)) {
+ return v;
+ }
+ }
+ else if (nd_type(node) == NODE_STR) {
+ return node->nd_lit;
+ }
+ return Qfalse;
+}
+
+static VALUE
+when_vals(yarv_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, VALUE special_literals)
+{
+ while (vals) {
+ VALUE lit;
+ NODE* val;
+
+ val = vals->nd_head;
+
+ if (special_literals &&
+ (lit = case_when_optimizable_literal(val)) != Qfalse) {
+ rb_ary_push(special_literals, lit);
+ rb_ary_push(special_literals, (VALUE)(l1) | 1);
+ }
+ else {
+ special_literals = Qfalse;
+ }
+
+ COMPILE(cond_seq, "when cond", val);
+ ADD_INSN1(cond_seq, nd_line(val), topn, INT2FIX(1));
+ ADD_SEND(cond_seq, nd_line(val), ID2SYM(idEqq), INT2FIX(1));
+ ADD_INSNL(cond_seq, nd_line(val), branchif, l1);
+ vals = vals->nd_next;
+ }
+ return special_literals;
+}
+
+static int
+make_masgn_lhs(yarv_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
+{
+
+ switch (nd_type(node)) {
+ case NODE_ATTRASGN:{
+ INSN *iobj;
+ VALUE dupidx;
+
+ COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node);
+ POP_ELEMENT(ret); /* pop pop insn */
+ iobj = (INSN *)POP_ELEMENT(ret); /* pop send insn */
+
+ dupidx = iobj->operands[1];
+ dupidx = INT2FIX(FIX2INT(dupidx) + 1);
+ iobj->operands[1] = dupidx;
+
+ ADD_INSN1(ret, nd_line(node), topn, dupidx);
+ ADD_ELEM(ret, (LINK_ELEMENT *)iobj);
+ ADD_INSN(ret, nd_line(node), pop); /* result */
+ ADD_INSN(ret, nd_line(node), pop); /* rhs */
+ break;
+ }
+
+ case NODE_MASGN:
+ COMPILE_POPED(ret, "nest masgn lhs", node);
+ break;
+
+ default:{
+ DECL_ANCHOR(anchor);
+ COMPILE_POPED(anchor, "masgn lhs", node);
+ // dump_disasm_list(FIRST_ELEMENT(anchor));
+ REMOVE_ELEM(FIRST_ELEMENT(anchor));
+ // dump_disasm_list(FIRST_ELEMENT(anchor));
+ ADD_SEQ(ret, anchor);
+ // ADD_ELEM(ret, LAST_ELEMENT(anchor));
+ }
+ }
+
+ return COMPILE_OK;
+}
+
+static int
+compile_massign(yarv_iseq_t *iseq, LINK_ANCHOR *ret,
+ NODE * rhsn, NODE * splatn, NODE * lhsn, int llen)
+{
+ if (lhsn != 0) {
+ compile_massign(iseq, ret, rhsn, splatn, lhsn->nd_next, llen + 1);
+ make_masgn_lhs(iseq, ret, lhsn->nd_head);
+ }
+ else {
+ int lhs_splat = 0;
+
+ if (splatn && (VALUE)splatn != -1) {
+ lhs_splat = 1;
+ }
+
+ if (rhsn) {
+ switch (nd_type(rhsn)) {
+ case NODE_ARRAY:{
+ int rlen = rhsn->nd_alen;
+ int max = rlen > llen ? rlen : llen;
+ int i, si = 0;
+
+ for (i = 0; i < max; i++) {
+ if (i < rlen && i < llen) {
+ /* a, b = c, d */
+ COMPILE(ret, "masgn val1", rhsn->nd_head);
+ rhsn = rhsn->nd_next;
+ }
+ else if (i < rlen) {
+ if (lhs_splat) {
+ while (rhsn) {
+ /* a, *b = x, y, z */
+ si++;
+ COMPILE(ret, "masgn rhs for lhs splat",
+ rhsn->nd_head);
+ rhsn = rhsn->nd_next;
+ }
+ break;
+ }
+ else {
+ /* a, b = c, d, e */
+ COMPILE_POPED(ret, "masgn rhs (popped)",
+ rhsn->nd_head);
+ rhsn = rhsn->nd_next;
+ }
+ }
+ else if (i < llen) {
+ /* a, b, c = c, d */
+ ADD_INSN(ret, 0, putnil);
+ }
+ }
+
+ if (lhs_splat) {
+ ADD_INSN1(ret, 0, newarray, INT2FIX(si));
+ }
+ break;
+ }
+ case NODE_TO_ARY:
+ COMPILE(ret, "rhs to ary", rhsn->nd_head);
+ ADD_INSN2(ret, nd_line(rhsn), expandarray, INT2FIX(llen),
+ INT2FIX(lhs_splat));
+ break;
+
+ case NODE_SPLAT:
+ COMPILE(ret, "rhs to ary (splat)", rhsn->nd_head);
+ ADD_INSN2(ret, nd_line(rhsn), expandarray, INT2FIX(llen),
+ INT2FIX(lhs_splat));
+ break;
+
+ case NODE_ARGSCAT:{
+ NODE *ary = rhsn->nd_head;
+ int idx = 0;
+
+ while (ary) {
+ if (idx < llen || lhs_splat) {
+ COMPILE(ret, "rhs aggscat each head",
+ ary->nd_head);
+ }
+ else {
+ COMPILE_POPED(ret,
+ "rhs aggscat each head (popped)",
+ ary->nd_head);
+ }
+ ary = ary->nd_next;
+ idx++;
+ }
+
+ if (llen > idx) {
+ COMPILE(ret, "rhs to ary (argscat/splat)",
+ rhsn->nd_body);
+ ADD_INSN2(ret, nd_line(rhsn), expandarray,
+ INT2FIX(llen - idx), INT2FIX(lhs_splat));
+ }
+ else if (lhs_splat) {
+ COMPILE(ret, "rhs to ary (argscat/splat)",
+ rhsn->nd_body);
+ ADD_INSN2(ret, nd_line(rhsn), expandarray,
+ INT2FIX(llen - idx), INT2FIX(lhs_splat));
+ }
+ break;
+ }
+ default:
+ COMPILE(ret, "rhs to ary (splat/default)", rhsn);
+ ADD_INSN2(ret, nd_line(rhsn), expandarray, INT2FIX(llen),
+ INT2FIX(lhs_splat));
+ // rb_bug("unknown rhs: %s", node_name(nd_type(rhsn)));
+ }
+ }
+ else {
+ /* nested massign */
+ ADD_INSN2(ret, 0, expandarray, INT2FIX(llen), INT2FIX(lhs_splat));
+ }
+
+ if (lhs_splat) {
+ make_masgn_lhs(iseq, ret, splatn);
+ }
+ }
+ return COMPILE_OK;
+}
+
+static int
+compile_colon2(yarv_iseq_t *iseq, NODE * node,
+ LINK_ANCHOR *pref, LINK_ANCHOR *body)
+{
+ switch (nd_type(node)) {
+ case NODE_CONST:
+ debugi("compile_colon2 - colon", node->nd_vid);
+ ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_vid));
+ break;
+ case NODE_COLON3:
+ debugi("compile_colon2 - colon3", node->nd_mid);
+ ADD_INSN(body, nd_line(node), pop);
+ ADD_INSN1(body, nd_line(node), putobject, rb_cObject);
+ ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
+ break;
+ case NODE_COLON2:
+ compile_colon2(iseq, node->nd_head, pref, body);
+ debugi("compile_colon2 - colon2", node->nd_mid);
+ ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
+ break;
+ default:
+ COMPILE(pref, "const colon2 prefix", node);
+ break;
+ }
+ return COMPILE_OK;
+}
+
+static int
+compile_cpath(LINK_ANCHOR *ret, yarv_iseq_t *iseq, NODE *cpath)
+{
+ if(cpath->nd_head) {
+ COMPILE(ret, "nd_else->nd_head", cpath->nd_head);
+ }
+ else if (nd_type(cpath) == NODE_COLON2) {
+ COMPILE(ret, "cpath (NODE_COLON2)", cpath->nd_head);
+ }
+ else {
+ ADD_INSN1(ret, nd_line(cpath), putobject, rb_cObject);
+ }
+ return COMPILE_OK;
+}
+
+static int
+defined_expr(yarv_iseq_t *iseq, LINK_ANCHOR *ret,
+ NODE * node, LABEL *lfinish, VALUE needstr)
+{
+ char *estr = 0;
+
+ switch (nd_type(node)) {
+
+ /* easy literals */
+ case NODE_NIL:
+ estr = "nil";
+ break;
+ case NODE_SELF:
+ estr = "self";
+ break;
+ case NODE_TRUE:
+ estr = "true";
+ break;
+ case NODE_FALSE:
+ estr = "false";
+ break;
+ case NODE_STR:
+ case NODE_LIT:
+ estr = "expression";
+ break;
+
+ /* variables */
+ case NODE_LVAR:
+ estr = "local-variable";
+ break;
+ case NODE_DVAR:
+ estr = "local-variable(in-block)";
+ break;
+
+ case NODE_IVAR:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_IVAR),
+ ID2SYM(node->nd_vid), needstr);
+ return 1;
+
+ case NODE_GVAR:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_GVAR),
+ ((VALUE)node->nd_entry) | 1, needstr);
+ return 1;
+
+ case NODE_CVAR:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CVAR),
+ ID2SYM(node->nd_vid), needstr);
+ return 1;
+
+ case NODE_CONST:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CONST),
+ ID2SYM(node->nd_vid), needstr);
+ return 1;
+ case NODE_COLON2:
+ if (rb_is_const_id(node->nd_mid)) {
+ LABEL *lcont = NEW_LABEL(nd_line(node));
+ defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
+
+ ADD_INSNL(ret, nd_line(node), branchif, lcont) ;
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSNL(ret, nd_line(node), jump, lfinish);
+
+ ADD_LABEL(ret, lcont);
+ COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CONST),
+ ID2SYM(node->nd_mid), needstr);
+ }
+ else {
+ LABEL *lcont = NEW_LABEL(nd_line(node));
+ defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
+
+ ADD_INSNL(ret, nd_line(node), branchif, lcont) ;
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSNL(ret, nd_line(node), jump, lfinish);
+
+ ADD_LABEL(ret, lcont);
+ COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
+ ID2SYM(node->nd_mid), needstr);
+ }
+ return 1;
+ case NODE_COLON3:
+ ADD_INSN1(ret, nd_line(node), putobject, rb_cObject);
+ ADD_INSN3(ret, nd_line(node), defined,
+ INT2FIX(DEFINED_CONST), ID2SYM(node->nd_mid), needstr);
+ return 1;
+
+ /* method dispatch */
+ case NODE_CALL:
+ case NODE_VCALL:
+ case NODE_FCALL:
+ if (nd_type(node) == NODE_CALL) {
+ LABEL *lcont = NEW_LABEL(nd_line(node));
+
+ defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
+ ADD_INSNL(ret, nd_line(node), branchif, lcont) ;
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSNL(ret, nd_line(node), jump, lfinish);
+
+ ADD_LABEL(ret, lcont);
+ COMPILE(ret, "defined/recv", node->nd_recv);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
+ ID2SYM(node->nd_mid), needstr);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putself);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_FUNC),
+ ID2SYM(node->nd_mid), needstr);
+ }
+ return 1;
+
+ case NODE_YIELD:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_YIELD), 0,
+ needstr);
+ return 1;
+
+ case NODE_NTH_REF:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_REF),
+ INT2FIX(node->nd_nth), needstr);
+ return 1;
+
+ case NODE_ZSUPER:
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_ZSUPER), 0,
+ needstr);
+ return 1;
+
+ default:{
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ LABEL *ldefed = NEW_LABEL(nd_line(node));
+ VALUE str = rb_str_new2("expression");
+ VALUE tmp;
+ VALUE ensure = NEW_CHILD_ISEQVAL(NEW_NIL(),
+ rb_str_concat(rb_str_new2
+ ("defined guard in "),
+ iseq->name),
+ ISEQ_TYPE_DEFINED_GUARD);
+
+ iseq_add_mark_object_compile_time(iseq, str);
+
+ ADD_LABEL(ret, lstart);
+ COMPILE(ret, "defined expr (others)", node);
+ ADD_INSNL(ret, nd_line(node), branchif, ldefed) ;
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSNL(ret, nd_line(node), jump, lend);
+ ADD_LABEL(ret, ldefed);
+ ADD_INSN1(ret, nd_line(node), putobject, str);
+ ADD_LABEL(ret, lend);
+
+ ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, lstart, lend, ensure, lfinish);
+ return 1;
+ // rb_bug("unimplemented defined: %s", node_name(nd_type(node)));
+ } /* end of default */
+ }
+
+ if (estr != 0) {
+ if (needstr != Qfalse) {
+ VALUE str = rb_str_new2(estr);
+ ADD_INSN1(ret, nd_line(node), putstring, str);
+ iseq_add_mark_object_compile_time(iseq, str);
+ }
+ else {
+ ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+#define BUFSIZE 0x100
+
+static VALUE
+make_name_for_block(yarv_iseq_t *iseq)
+{
+ char buf[BUFSIZE];
+ if (iseq->parent_iseq == 0) {
+ snprintf(buf, BUFSIZE, "block in %s", RSTRING_PTR(iseq->name));
+ }
+ else {
+ int level = 1;
+ yarv_iseq_t *ip = iseq;
+ while (1) {
+ if (ip->local_iseq != ip) {
+ ip = ip->parent_iseq;
+ }
+ else {
+ break;
+ }
+ level++;
+ }
+ snprintf(buf, BUFSIZE, "block (%d levels) in %s", level,
+ RSTRING_PTR(ip->name));
+ }
+ return rb_str_new2(buf);
+}
+
+static VALUE
+make_name_with_str(const char *fmt, const char *str)
+{
+ char buf[BUFSIZE];
+ snprintf(buf, BUFSIZE, fmt, str);
+ return rb_str_new2(buf);
+}
+
+static void
+add_ensure_range(yarv_iseq_t *iseq, struct ensure_range *erange,
+ LABEL *lstart, LABEL *lend)
+{
+ struct ensure_range *ne =
+ compile_data_alloc(iseq, sizeof(struct ensure_range));
+
+ while (erange->next != 0) {
+ erange = erange->next;
+ }
+ ne->next = 0;
+ ne->begin = lend;
+ ne->end = erange->end;
+ erange->end = lstart;
+
+ erange->next = ne;
+}
+
+static void
+add_ensure_iseq(LINK_ANCHOR *ret, yarv_iseq_t *iseq)
+{
+ struct iseq_compile_data_ensure_node_stack *enlp =
+ iseq->compile_data->ensure_node_stack;
+ struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
+ DECL_ANCHOR(ensure);
+
+ while (enlp) {
+ DECL_ANCHOR(ensure_part);
+ LABEL *lstart = NEW_LABEL(0);
+ LABEL *lend = NEW_LABEL(0);
+ add_ensure_range(iseq, enlp->erange, lstart, lend);
+
+ iseq->compile_data->ensure_node_stack = enlp->prev;
+ ADD_LABEL(ensure_part, lstart);
+ COMPILE_POPED(ensure_part, "ensure part", enlp->ensure_node);
+ ADD_LABEL(ensure_part, lend);
+
+ ADD_SEQ(ensure, ensure_part);
+ enlp = enlp->prev;
+ }
+ iseq->compile_data->ensure_node_stack = prev_enlp;
+ ADD_SEQ(ret, ensure);
+}
+
+static VALUE
+setup_arg(yarv_iseq_t *iseq, LINK_ANCHOR *args, NODE *node, VALUE *flag)
+{
+ VALUE argc = INT2FIX(0);
+ NODE *argn = node->nd_args;
+ NODE *argp = 0;
+ DECL_ANCHOR(arg_block);
+ DECL_ANCHOR(args_push);
+
+ if (argn && nd_type(argn) == NODE_BLOCK_PASS) {
+ COMPILE(arg_block, "block", argn->nd_body);
+ *flag |= VM_CALL_ARGS_BLOCKARG_BIT;
+ argn = argn->nd_head;
+ }
+
+ setup_argn:
+ if (argn) {
+ switch (nd_type(argn)) {
+ case NODE_SPLAT: {
+ COMPILE(args, "args (splat)", argn->nd_head);
+ argc = INT2FIX(1);
+ *flag |= VM_CALL_ARGS_SPLAT_BIT;
+ break;
+ }
+ case NODE_ARGSCAT: {
+ argc = INT2FIX(compile_array(iseq, args, argn->nd_head, Qfalse) + 1);
+ POP_ELEMENT(args);
+ COMPILE(args, "args (cat: splat)", argn->nd_body);
+ *flag |= VM_CALL_ARGS_SPLAT_BIT;
+ break;
+ }
+ case NODE_ARGSPUSH: {
+ DECL_ANCHOR(args_push_e);
+ COMPILE(args_push_e, "argspush (cdr)", argn->nd_body);
+ ADD_INSN(args_push_e, nd_line(node), concatarray);
+ INSERT_LIST(args_push, args_push_e);
+ argn = argn->nd_head;
+ goto setup_argn;
+ }
+ default: {
+ argc = INT2FIX(compile_array(iseq, args, argn, Qfalse));
+ POP_ELEMENT(args);
+ break;
+ }
+ }
+ }
+
+ if (!LIST_SIZE_ZERO(args_push)) {
+ ADD_SEQ(args, args_push);
+ }
+
+ if (*flag & VM_CALL_ARGS_BLOCKARG_BIT) {
+ ADD_SEQ(args, arg_block);
+ }
+ return argc;
+}
+
+
+/**
+ compile each node
+
+ self: InstructionSequence
+ node: Ruby compiled node
+ poped: This node will be poped
+ */
+static int
+iseq_compile_each(yarv_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
+{
+ VALUE tmp; /* reserved for macro */
+ int type;
+
+ GC_CHECK();
+
+ if (node == 0) {
+ if (!poped) {
+ debug_nodeprint("NODE_NIL(implicit)");
+ debug_nodeprint_close();
+ ADD_INSN(ret, 0, putnil);
+ return COMPILE_OK;
+ }
+ return COMPILE_OK;
+ }
+
+ iseq->compile_data->last_line = nd_line(node);
+ debug_nodeprint(node);
+
+ type = nd_type(node);
+
+ switch (type) {
+
+ case NODE_METHOD:{
+ /* OK */
+ bp();
+ COMPILE_ERROR(("BUG: unknown node: NODE_METHOD"));
+ break;
+ }
+ case NODE_FBODY:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_FBODY"));
+ break;
+ }
+ case NODE_CFUNC:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_CFUNC"));
+ break;
+ }
+ case NODE_SCOPE:{
+ /* OK */
+ COMPILE_ERROR(("BUG: shouldn't reach: NODE_SCOPE"));
+ break;
+ }
+ case NODE_BLOCK:{
+ while (node && nd_type(node) == NODE_BLOCK) {
+ COMPILE_(ret, "BLOCK body", node->nd_head,
+ (node->nd_next == 0 && poped == 0) ? 0 : 1);
+ node = node->nd_next;
+ }
+ if (node) {
+ COMPILE_(ret, "BLOCK next", node->nd_next, poped);
+ }
+ break;
+ }
+ case NODE_IF:{
+ DECL_ANCHOR(cond_seq);
+ DECL_ANCHOR(then_seq);
+ DECL_ANCHOR(else_seq);
+ LABEL *then_label, *else_label, *end_label;
+
+ then_label = NEW_LABEL(nd_line(node));
+ else_label = NEW_LABEL(nd_line(node));
+ end_label = NEW_LABEL(nd_line(node));
+
+ compile_branch_condition(iseq, cond_seq, node->nd_cond,
+ then_label, else_label);
+ COMPILE_(then_seq, "then", node->nd_body, poped);
+ COMPILE_(else_seq, "else", node->nd_else, poped);
+
+ ADD_SEQ(ret, cond_seq);
+
+ ADD_LABEL(ret, then_label);
+ ADD_SEQ(ret, then_seq);
+ ADD_INSNL(ret, nd_line(node), jump, end_label);
+
+ ADD_LABEL(ret, else_label);
+ ADD_SEQ(ret, else_seq);
+
+ ADD_LABEL(ret, end_label);
+
+ break;
+ }
+ case NODE_CASE:{
+ NODE *vals;
+ NODE *val;
+ NODE *tempnode = node;
+ LABEL *endlabel, *elselabel;
+ DECL_ANCHOR(head);
+ DECL_ANCHOR(body_seq);
+ DECL_ANCHOR(cond_seq);
+ VALUE special_literals = rb_ary_new();
+
+ if (node->nd_head == 0) {
+ COMPILE_(ret, "when", node->nd_body, poped);
+ break;
+ }
+ COMPILE(head, "case base", node->nd_head);
+
+ node = node->nd_body;
+ type = nd_type(node);
+
+ if (type != NODE_WHEN) {
+ COMPILE_ERROR(("NODE_CASE: unexpected node. must be NODE_WHEN, but %s", node_name(type)));
+ }
+
+ endlabel = NEW_LABEL(nd_line(node));
+ elselabel = NEW_LABEL(nd_line(node));
+
+ ADD_SEQ(ret, head); /* case VAL */
+
+ while (type == NODE_WHEN) {
+ LABEL *l1;
+
+ l1 = NEW_LABEL(nd_line(node));
+ ADD_LABEL(body_seq, l1);
+ ADD_INSN(body_seq, nd_line(node), pop);
+ COMPILE_(body_seq, "when body", node->nd_body, poped);
+ ADD_INSNL(body_seq, nd_line(node), jump, endlabel);
+
+ vals = node->nd_head;
+ if (vals) {
+ if (nd_type(vals) == NODE_ARRAY) {
+ special_literals = when_vals(iseq, cond_seq, vals, l1, special_literals);
+ }
+ else if (nd_type(vals) == NODE_SPLAT || nd_type(vals) == NODE_ARGSCAT) {
+ NODE *val = vals->nd_head;
+ special_literals = 0;
+
+ if (nd_type(vals) == NODE_ARGSCAT) {
+ when_vals(iseq, cond_seq, vals->nd_head, l1, 0);
+ val = vals->nd_body;
+ }
+
+ COMPILE(cond_seq, "when/cond splat", val);
+ ADD_INSN1(cond_seq, nd_line(val), checkincludearray, Qtrue);
+ ADD_INSNL(cond_seq, nd_line(val), branchif, l1);
+ }
+ else {
+ rb_bug("NODE_CASAE: unknown node (%s)", node_name(nd_type(vals)));
+ }
+ }
+ else {
+ rb_bug("NODE_CASAE: must be NODE_ARRAY, but 0\n");
+ }
+
+ node = node->nd_next;
+ if (!node) {
+ break;
+ }
+ type = nd_type(node);
+ }
+ /* else */
+ if (node) {
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, nd_line(node), pop);
+ COMPILE_(cond_seq, "else", node, poped);
+ ADD_INSNL(cond_seq, nd_line(node), jump, endlabel);
+ }
+ else {
+ debugs("== else (implicit)\n");
+ ADD_LABEL(cond_seq, elselabel);
+ ADD_INSN(cond_seq, nd_line(tempnode), pop);
+ if (!poped) {
+ ADD_INSN(cond_seq, nd_line(tempnode), putnil);
+ }
+ ADD_INSNL(cond_seq, nd_line(tempnode), jump, endlabel);
+ }
+
+ if (special_literals) {
+ ADD_INSN(ret, nd_line(tempnode), dup);
+ ADD_INSN2(ret, nd_line(tempnode), opt_case_dispatch,
+ special_literals, elselabel);
+ iseq_add_mark_object_compile_time(iseq, special_literals);
+ }
+
+ ADD_SEQ(ret, cond_seq);
+ ADD_SEQ(ret, body_seq);
+ ADD_LABEL(ret, endlabel);
+ break;
+ }
+ case NODE_WHEN:{
+ NODE *vals;
+ NODE *val;
+ NODE *orig_node = node;
+ LABEL *endlabel;
+ DECL_ANCHOR(body_seq);
+
+ endlabel = NEW_LABEL(nd_line(node));
+
+ while (node && nd_type(node) == NODE_WHEN) {
+ LABEL *l1 = NEW_LABEL(nd_line(node));
+ ADD_LABEL(body_seq, l1);
+ COMPILE_(body_seq, "when", node->nd_body, poped);
+ ADD_INSNL(body_seq, nd_line(node), jump, endlabel);
+
+ vals = node->nd_head;
+ if (vals && nd_type(vals) == NODE_ARRAY) {
+ while (vals) {
+ val = vals->nd_head;
+ COMPILE(ret, "when2", val);
+ ADD_INSNL(ret, nd_line(val), branchif, l1) ;
+ vals = vals->nd_next;
+ }
+ }
+ else if (nd_type(vals) == NODE_SPLAT || nd_type(vals) == NODE_ARGSCAT) {
+ NODE *val = vals->nd_head;
+
+ if (nd_type(vals) == NODE_ARGSCAT) {
+ NODE *vs = vals->nd_head;
+ val = vals->nd_body;
+
+ while (vs) {
+ NODE* val = vs->nd_head;
+ COMPILE(ret, "when/argscat", val);
+ ADD_INSNL(ret, nd_line(val), branchif, l1);
+ vs = vs->nd_next;
+ }
+ }
+
+ ADD_INSN(ret, nd_line(val), putnil);
+ COMPILE(ret, "when2/splat", val);
+ ADD_INSN1(ret, nd_line(val), checkincludearray, Qfalse);
+ ADD_INSN(ret, nd_line(val), pop);
+ ADD_INSNL(ret, nd_line(val), branchif, l1);
+ }
+ else {
+ rb_bug("err");
+ }
+ node = node->nd_next;
+ }
+ /* else */
+ COMPILE_(ret, "else", node, poped);
+ ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
+
+ ADD_SEQ(ret, body_seq);
+ ADD_LABEL(ret, endlabel);
+
+ break;
+ }
+ case NODE_OPT_N:
+ case NODE_WHILE:
+ case NODE_UNTIL:{
+ LABEL *prev_start_label = iseq->compile_data->start_label;
+ LABEL *prev_end_label = iseq->compile_data->end_label;
+ LABEL *prev_redo_label = iseq->compile_data->redo_label;
+ VALUE prev_loopval_popped = iseq->compile_data->loopval_popped;
+
+ struct iseq_compile_data_ensure_node_stack *enlp =
+ iseq->compile_data->ensure_node_stack;
+
+ LABEL *next_label = iseq->compile_data->start_label = NEW_LABEL(nd_line(node)); /* next */
+ LABEL *redo_label = iseq->compile_data->redo_label = NEW_LABEL(nd_line(node)); /* redo */
+ LABEL *break_label = iseq->compile_data->end_label = NEW_LABEL(nd_line(node)); /* break */
+ LABEL *end_label = NEW_LABEL(nd_line(node));
+
+ iseq->compile_data->loopval_popped = 0;
+ iseq->compile_data->ensure_node_stack = 0;
+
+ if (type == NODE_OPT_N || node->nd_state) {
+ ADD_INSNL(ret, nd_line(node), jump, next_label);
+ }
+
+ ADD_LABEL(ret, redo_label);
+ COMPILE_POPED(ret, "while body", node->nd_body);
+ ADD_LABEL(ret, next_label); /* next */
+
+ if (type == NODE_WHILE) {
+ compile_branch_condition(iseq, ret, node->nd_cond,
+ redo_label, end_label);
+ }
+ else if (type == NODE_UNTIL) {
+ /* untile */
+ compile_branch_condition(iseq, ret, node->nd_cond,
+ end_label, redo_label);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putself);
+ ADD_CALL(ret, nd_line(node), ID2SYM(idGets), INT2FIX(0));
+ ADD_INSNL(ret, nd_line(node), branchif, redo_label) ;
+ /* opt_n */
+ }
+
+ ADD_LABEL(ret, end_label);
+
+ if (node->nd_state == Qundef) {
+ ADD_INSN(ret, nd_line(node), putundef);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+
+ ADD_LABEL(ret, break_label); /* braek */
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+
+ ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label,
+ 0, break_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_NEXT | 0x10000, redo_label,
+ break_label, 0, iseq->compile_data->start_label);
+ ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, 0,
+ iseq->compile_data->redo_label);
+
+ iseq->compile_data->start_label = prev_start_label;
+ iseq->compile_data->end_label = prev_end_label;
+ iseq->compile_data->redo_label = prev_redo_label;
+ iseq->compile_data->loopval_popped = prev_loopval_popped;
+ iseq->compile_data->ensure_node_stack = enlp;
+ break;
+ }
+ case NODE_ITER:
+ case NODE_FOR:{
+ VALUE prevblock = iseq->compile_data->current_block;
+ LABEL *retry_label = NEW_LABEL(nd_line(node));
+ LABEL *retry_end_l = NEW_LABEL(nd_line(node));
+ ID mid = 0;
+
+ ADD_LABEL(ret, retry_label);
+ if (nd_type(node) == NODE_FOR) {
+ COMPILE(ret, "iter caller (for)", node->nd_iter);
+
+ iseq->compile_data->current_block =
+ NEW_CHILD_ISEQVAL(node, make_name_for_block(iseq),
+ ISEQ_TYPE_BLOCK);
+
+ mid = idEach;
+ ADD_SEND_R(ret, nd_line(node), ID2SYM(idEach), INT2FIX(0),
+ iseq->compile_data->current_block, INT2FIX(0));
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ }
+ else {
+ iseq->compile_data->current_block =
+ NEW_CHILD_ISEQVAL(node, make_name_for_block(iseq),
+ ISEQ_TYPE_BLOCK);
+ COMPILE_(ret, "iter caller", node->nd_iter, poped);
+ }
+ ADD_LABEL(ret, retry_end_l);
+ iseq->compile_data->current_block = prevblock;
+
+ ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, retry_label, retry_end_l, 0,
+ retry_label);
+ break;
+ }
+ case NODE_BREAK:{
+ unsigned long level = 0;
+
+ if (iseq->compile_data->redo_label != 0) {
+ /* while/until */
+ add_ensure_iseq(ret, iseq);
+ COMPILE_(ret, "break val (while/until)", node->nd_stts,
+ iseq->compile_data->loopval_popped);
+ ADD_INSNL(ret, nd_line(node), jump,
+ iseq->compile_data->end_label);
+ }
+ else if (iseq->type == ISEQ_TYPE_BLOCK) {
+ break_by_insn:
+ /* escape from block */
+ COMPILE(ret, "break val (block)", node->nd_stts);
+ ADD_INSN1(ret, nd_line(node), throw,
+ INT2FIX(level | 0x02) /* TAG_BREAK */ );
+ }
+ else if (iseq->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(("Can't escape from eval with break"));
+ }
+ else {
+ yarv_iseq_t *ip = iseq->parent_iseq;
+ while (ip) {
+ level++;
+ if (ip->compile_data->redo_label != 0) {
+ level = 0x8000;
+ if (ip->compile_data->loopval_popped == 0) {
+ /* need value */
+ level |= 0x4000;
+ }
+ goto break_by_insn;
+ }
+ else if (ip->type == ISEQ_TYPE_BLOCK) {
+ level <<= 16;
+ goto break_by_insn;
+ }
+ ip = ip->parent_iseq;
+ }
+ COMPILE_ERROR(("Illegal break"));
+ }
+ break;
+ }
+ case NODE_NEXT:{
+ unsigned long level = 0;
+
+ if (iseq->compile_data->redo_label != 0) {
+ add_ensure_iseq(ret, iseq);
+ ADD_INSNL(ret, nd_line(node), jump,
+ iseq->compile_data->start_label);
+ }
+ else if (iseq->compile_data->end_label) {
+ COMPILE(ret, "next val", node->nd_stts);
+ add_ensure_iseq(ret, iseq);
+ ADD_INSNL(ret, nd_line(node), jump,
+ iseq->compile_data->end_label);
+ }
+ else if (iseq->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(("Can't escape from eval with next"));
+ }
+ else {
+ yarv_iseq_t *ip = iseq->parent_iseq;
+ while (ip) {
+ level = 0x8000;
+ if (ip->type == ISEQ_TYPE_BLOCK) {
+ level |= 0x4000;
+ break;
+ }
+ else if (ip->compile_data->redo_label != 0) {
+ break;
+ }
+ ip = ip->parent_iseq;
+ }
+ if (ip != 0) {
+ COMPILE(ret, "next val", node->nd_stts);
+ add_ensure_iseq(ret, iseq);
+ ADD_INSN1(ret, nd_line(node), throw,
+ INT2FIX(level | 0x03) /* TAG_NEXT */ );
+ }
+ else {
+ COMPILE_ERROR(("Illegal next"));
+ }
+ }
+ break;
+ }
+ case NODE_REDO:{
+ if (iseq->compile_data->redo_label) {
+ add_ensure_iseq(ret, iseq);
+ ADD_INSNL(ret, nd_line(node), jump,
+ iseq->compile_data->redo_label);
+ if (!poped) { /* for stack consistency */
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ }
+ else if (iseq->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(("Can't escape from eval with redo"));
+ }
+ else if (iseq->compile_data->start_label) {
+ ADD_INSNL(ret, nd_line(node), jump,
+ iseq->compile_data->start_label);
+ if (!poped) { /* for stack consistency */
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ }
+ else {
+ yarv_iseq_t *ip = iseq->parent_iseq;
+ unsigned long level = 0x8000 | 0x4000;
+ while (ip) {
+ if (ip->type == ISEQ_TYPE_BLOCK) {
+ break;
+ }
+ else if (ip->type == ISEQ_TYPE_EVAL) {
+ COMPILE_ERROR(("Can't escape from eval with redo"));
+ }
+ else if (ip->compile_data->redo_label != 0) {
+ break;
+ }
+ ip = ip->parent_iseq;
+ }
+ if (ip != 0) {
+ add_ensure_iseq(ret, iseq);
+ ADD_INSN1(ret, nd_line(node), throw,
+ INT2FIX(level | 0x05) /* TAG_REDO */ );
+ }
+ else {
+ COMPILE_ERROR(("Illegal redo"));
+ }
+ }
+ break;
+ }
+ case NODE_RETRY:{
+ if (iseq->type == ISEQ_TYPE_BLOCK ||
+ iseq->type == ISEQ_TYPE_RESCUE) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN1(ret, nd_line(node), throw,
+ INT2FIX(0x04) /* TAG_RETRY */ );
+ }
+ else {
+ COMPILE_ERROR(("Illegal retry"));
+ }
+ break;
+ }
+ case NODE_BEGIN:{
+ COMPILE_(ret, "NODE_BEGIN", node->nd_body, poped);
+ break;
+ }
+ case NODE_RESCUE:{
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ LABEL *lcont = NEW_LABEL(nd_line(node));
+ VALUE rescue = NEW_CHILD_ISEQVAL(node->nd_resq,
+ rb_str_concat(rb_str_new2
+ ("rescue in "),
+ iseq->name),
+ ISEQ_TYPE_RESCUE);
+
+ ADD_LABEL(ret, lstart);
+ COMPILE(ret, "rescue head", node->nd_head);
+ ADD_LABEL(ret, lend);
+ if (node->nd_else) {
+ ADD_INSN(ret, nd_line(node), pop);
+ COMPILE(ret, "rescue else", node->nd_else);
+ }
+ ADD_INSN(ret, nd_line(node), nop);
+ ADD_LABEL(ret, lcont);
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+
+ /* resgister catch entry */
+ ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
+ ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, 0, lstart);
+ break;
+ }
+ case NODE_RESBODY:{
+ NODE *resq = node;
+ NODE *narg;
+ LABEL *label_miss, *label_hit;
+
+ while (resq) {
+ label_miss = NEW_LABEL(nd_line(node));
+ label_hit = NEW_LABEL(nd_line(node));
+
+ narg = resq->nd_args;
+ while (narg) {
+ COMPILE(ret, "rescue arg", narg->nd_head);
+ ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(1),
+ INT2FIX(0));
+ ADD_SEND(ret, nd_line(node), ID2SYM(idEqq), INT2FIX(1));
+ ADD_INSNL(ret, nd_line(node), branchif, label_hit) ;
+ narg = narg->nd_next;
+ }
+ if (resq->nd_args == 0) {
+ ADD_INSN1(ret, nd_line(node), putobject,
+ rb_eStandardError);
+ ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(1),
+ INT2FIX(0));
+ ADD_SEND(ret, nd_line(node), ID2SYM(idEqq), INT2FIX(1));
+ ADD_INSNL(ret, nd_line(node), branchif, label_hit) ;
+ }
+ ADD_INSNL(ret, nd_line(node), jump, label_miss);
+ ADD_LABEL(ret, label_hit);
+ COMPILE(ret, "resbody body", resq->nd_body);
+ ADD_INSN(ret, nd_line(node), leave);
+ ADD_LABEL(ret, label_miss);
+ resq = resq->nd_head;
+ }
+ break;
+ }
+ case NODE_ENSURE:{
+ DECL_ANCHOR(ensr);
+ VALUE ensure = NEW_CHILD_ISEQVAL(node->nd_ensr,
+ rb_str_concat(rb_str_new2
+ ("ensure in "),
+ iseq->name),
+ ISEQ_TYPE_ENSURE);
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ LABEL *lcont = NEW_LABEL(nd_line(node));
+ struct ensure_range er = { lstart, lend, 0 };
+ struct iseq_compile_data_ensure_node_stack enl = {
+ node->nd_ensr,
+ iseq->compile_data->ensure_node_stack, /* prev */
+ &er,
+ };
+ struct ensure_range *erange;
+
+ COMPILE_POPED(ensr, "ensure ensr", node->nd_ensr);
+
+ iseq->compile_data->ensure_node_stack = &enl;
+
+ ADD_LABEL(ret, lstart);
+ COMPILE_(ret, "ensure head", node->nd_head, poped);
+ ADD_LABEL(ret, lend);
+ if (ensr->anchor.next == 0) {
+ ADD_INSN(ret, nd_line(node), nop);
+ }
+ else {
+ ADD_SEQ(ret, ensr);
+ }
+ ADD_LABEL(ret, lcont);
+
+ erange = iseq->compile_data->ensure_node_stack->erange;
+ while (erange) {
+ ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
+ ensure, lcont);
+ erange = erange->next;
+ }
+ iseq->compile_data->ensure_node_stack = enl.prev;
+ break;
+ }
+
+ case NODE_AND:
+ case NODE_OR:{
+ LABEL *end_label = NEW_LABEL(nd_line(node));
+ COMPILE(ret, "nd_1st", node->nd_1st);
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ if (type == NODE_AND) {
+ ADD_INSNL(ret, nd_line(node), branchunless, end_label);
+ }
+ else {
+ ADD_INSNL(ret, nd_line(node), branchif, end_label) ;
+ }
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ COMPILE_(ret, "nd_2nd", node->nd_2nd, poped);
+ ADD_LABEL(ret, end_label);
+ break;
+ }
+ case NODE_NOT:{
+ COMPILE(ret, "value", node->nd_body);
+ ADD_INSN(ret, nd_line(node), putnot);
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+
+ case NODE_MASGN:{
+ compile_massign(iseq, ret, node->nd_value, /* rhsn */
+ node->nd_args, /* splat */
+ node->nd_head, /* lhsn */
+ 0);
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
+ }
+ break;
+ }
+
+ case NODE_LASGN:{
+ int idx = iseq->local_iseq->local_size + 2 - node->nd_cnt;
+ debugs("lvar: %d\n", idx);
+ COMPILE(ret, "lvalue", node->nd_value);
+
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ ADD_INSN1(ret, nd_line(node), setlocal, INT2FIX(idx));
+
+ break;
+ }
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:{
+ int idx, lv, ls;
+ COMPILE(ret, "dvalue", node->nd_value);
+ debugp_param("dassn id", rb_str_new2(rb_id2name(node->nd_vid)));
+
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
+ if (nd_type(node) == NODE_DASGN_CURR &&
+ lv > 0 &&
+ iseq->type == ISEQ_TYPE_BLOCK &&
+ iseq->compile_data->for_iseq != 1) {
+
+ dpi(node->nd_vid);
+ rb_bug("NODE_DASGN_CURR, but lv == %d (line: %d)", lv,
+ nd_line(node));
+ }
+
+ if (idx < 0) {
+ debugi("unknown id", node->nd_vid);
+ COMPILE_ERROR(("NODE_DASGN error"));
+ }
+ ADD_INSN2(ret, nd_line(node), setdynamic,
+ INT2FIX(ls - idx), INT2FIX(lv));
+ break;
+ }
+ case NODE_GASGN:{
+ COMPILE(ret, "lvalue", node->nd_value);
+
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ ADD_INSN1(ret, nd_line(node), setglobal,
+ (((long)node->nd_entry) | 1));
+ break;
+ }
+ case NODE_IASGN:{
+ COMPILE(ret, "lvalue", node->nd_value);
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ ADD_INSN1(ret, nd_line(node), setinstancevariable,
+ ID2SYM(node->nd_vid));
+ break;
+ }
+ case NODE_CDECL:{
+ COMPILE(ret, "lvalue", node->nd_value);
+
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+
+ if (node->nd_vid) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN1(ret, nd_line(node), setconstant,
+ ID2SYM(node->nd_vid));
+ }
+ else {
+ compile_cpath(ret, iseq, node->nd_else);
+ ADD_INSN1(ret, nd_line(node), setconstant,
+ ID2SYM(node->nd_else->nd_mid));
+ }
+ break;
+ }
+ case NODE_CVASGN:
+ case NODE_CVDECL:{
+ COMPILE(ret, "cvasgn val", node->nd_value);
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), dup);
+ }
+ ADD_INSN2(ret, nd_line(node), setclassvariable,
+ ID2SYM(node->nd_vid),
+ nd_type(node) == NODE_CVDECL ? Qtrue : Qfalse);
+ break;
+ }
+ case NODE_OP_ASGN1:{
+ DECL_ANCHOR(args);
+ int argc;
+ ID id = node->nd_mid;
+
+ /*
+ * a[x] (op)= y
+ *
+ * eval a # a
+ * eval x # a x
+ * dupn 2 # a x a x
+ * send :[] # a x a[x]
+ * eval y # a x a[x] y
+ * send op # a x a[x]+y
+ * send []= # ret
+ */
+
+ /*
+ * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
+ * NODE_OP_ASGN nd_recv
+ * nd_args->nd_head
+ * nd_args->nd_body
+ * nd_mid
+ */
+
+ COMPILE(ret, "NODE_OP_ASGN1 recv", node->nd_recv);
+ argc = compile_array(iseq, args, node->nd_args->nd_body, Qfalse);
+ POP_ELEMENT(args);
+ ADD_SEQ(ret, args);
+ ADD_INSN1(ret, nd_line(node), dupn, INT2FIX(argc+1));
+ ADD_SEND(ret, nd_line(node), ID2SYM(idAREF), INT2FIX(argc));
+
+ if (id == 0 || id == 1) {
+ /* 0: or, 1: and
+ a[x] ||= y
+
+ unless/if a[x]
+ a[x]= y
+ else
+ nil
+ end
+ */
+ LABEL *label = NEW_LABEL(nd_line(node));
+ LABEL *lfin = NEW_LABEL(nd_line(node));
+
+ if (id == 0) {
+ /* or */
+ ADD_INSN(ret, nd_line(node), dup);
+ ADD_INSNL(ret, nd_line(node), branchif, label) ;
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ else {
+ /* and */
+ ADD_INSNL(ret, nd_line(node), branchunless, label);
+ }
+
+ COMPILE(ret, "NODE_OP_ASGN1 args->head: ",
+ node->nd_args->nd_head);
+ ADD_SEND(ret, nd_line(node), ID2SYM(idASET),
+ INT2FIX(argc + 1));
+ ADD_INSNL(ret, nd_line(node), jump, lfin);
+ ADD_LABEL(ret, label);
+ if (id == 0) { /* or */
+ ADD_INSN(ret, nd_line(node), swap);
+ ADD_INSN(ret, nd_line(node), pop);
+ ADD_INSN(ret, nd_line(node), swap);
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ else if (id == 1) { /* and */
+ ADD_INSN(ret, nd_line(node), pop);
+ ADD_INSN(ret, nd_line(node), pop);
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ ADD_LABEL(ret, lfin);
+ }
+ else {
+ COMPILE(ret, "NODE_OP_ASGN1 args->head: ",
+ node->nd_args->nd_head);
+ ADD_SEND(ret, nd_line(node), ID2SYM(id), INT2FIX(1));
+ ADD_SEND(ret, nd_line(node), ID2SYM(idASET),
+ INT2FIX(argc + 1));
+ }
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+
+ break;
+ }
+ case NODE_OP_ASGN2:{
+ ID atype = node->nd_next->nd_mid;
+ LABEL *lfin = NEW_LABEL(nd_line(node));
+ LABEL *lcfin = NEW_LABEL(nd_line(node));
+ /*
+ class C; attr_accessor :c; end
+ r = C.new
+ r.a &&= v # asgn2
+
+ eval r # r
+ dup # r r
+ eval r.a # r o
+
+ # or
+ dup # r o o
+ if lcfin # r o
+ pop # r
+ eval v # r v
+ send a= # v
+ jump lfin # v
+
+ lcfin: # r o
+ swap # o r
+ pop # o
+
+ lfin: # v
+
+ # and
+ dup # r o o
+ unless lcfin
+ pop # r
+ eval v # r v
+ send a= # v
+ jump lfin # v
+
+ # others
+ eval v # r o v
+ send ?? # r w
+ send a= # w
+
+ */
+
+ COMPILE(ret, "NODE_OP_ASGN2#recv", node->nd_recv);
+ ADD_INSN(ret, nd_line(node), dup);
+ ADD_SEND(ret, nd_line(node), ID2SYM(node->nd_next->nd_vid),
+ INT2FIX(0));
+
+ if (atype == 0 || atype == 1) { /* 0: OR or 1: AND */
+ ADD_INSN(ret, nd_line(node), dup);
+ if (atype == 0) {
+ ADD_INSNL(ret, nd_line(node), branchif, lcfin) ;
+ }
+ else {
+ ADD_INSNL(ret, nd_line(node), branchunless, lcfin);
+ }
+ ADD_INSN(ret, nd_line(node), pop);
+ COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
+ ADD_SEND(ret, nd_line(node), ID2SYM(node->nd_next->nd_aid),
+ INT2FIX(1));
+ ADD_INSNL(ret, nd_line(node), jump, lfin);
+
+ ADD_LABEL(ret, lcfin);
+ ADD_INSN(ret, nd_line(node), swap);
+ ADD_INSN(ret, nd_line(node), pop);
+
+ ADD_LABEL(ret, lfin);
+ }
+ else {
+ COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
+ ADD_SEND(ret, nd_line(node), ID2SYM(node->nd_next->nd_mid),
+ INT2FIX(1));
+ ADD_SEND(ret, nd_line(node), ID2SYM(node->nd_next->nd_aid),
+ INT2FIX(1));
+ }
+
+ if (poped) {
+ /* we can apply more optimize */
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_OP_ASGN_AND:
+ case NODE_OP_ASGN_OR:{
+ LABEL *lfin = NEW_LABEL(nd_line(node));
+ LABEL *lassign = NEW_LABEL(nd_line(node));
+
+ if (nd_type(node) == NODE_OP_ASGN_OR) {
+ defined_expr(iseq, ret, node->nd_head, lassign, Qfalse);
+ ADD_INSNL(ret, nd_line(node), branchunless, lassign);
+ }
+
+ COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head);
+ ADD_INSN(ret, nd_line(node), dup);
+
+ if (nd_type(node) == NODE_OP_ASGN_AND) {
+ ADD_INSNL(ret, nd_line(node), branchunless, lfin);
+ }
+ else {
+ ADD_INSNL(ret, nd_line(node), branchif, lfin) ;
+ }
+
+ ADD_INSN(ret, nd_line(node), pop);
+ ADD_LABEL(ret, lassign);
+ COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value);
+ ADD_LABEL(ret, lfin);
+
+ if (poped) {
+ /* we can apply more optimize */
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_CALL:
+ case NODE_FCALL:
+ case NODE_VCALL:{ /* VCALL: variable or call */
+ /*
+ call: obj.method(...)
+ fcall: func(...)
+ vcall: func
+ */
+ DECL_ANCHOR(recv);
+ DECL_ANCHOR(args);
+ ID mid = node->nd_mid;
+ VALUE argc;
+ VALUE flag = 0;
+ VALUE parent_block = iseq->compile_data->current_block;
+ iseq->compile_data->current_block = Qfalse;
+
+#if SUPPORT_JOKE
+ if (nd_type(node) == NODE_VCALL) {
+ if (mid == idBitblt) {
+ ADD_INSN(ret, nd_line(node), bitblt);
+ break;
+ }
+ else if (mid == idAnswer) {
+ ADD_INSN(ret, nd_line(node), answer);
+ break;
+ }
+ }
+ /* only joke */
+ {
+ static ID goto_id;
+ static ID label_id;
+ VALUE label;
+ VALUE label_sym;
+
+ if (goto_id == 0) {
+ goto_id = rb_intern("__goto__");
+ label_id = rb_intern("__label__");
+ }
+
+ if (nd_type(node) == NODE_FCALL &&
+ (mid == goto_id || mid == label_id)) {
+ if (nd_type(node->nd_args->nd_head) == NODE_LIT &&
+ SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
+
+ label_sym = label = node->nd_args->nd_head->nd_lit;
+ if ((label =
+ rb_hash_aref(iseq->compile_data,
+ label_sym)) == Qnil) {
+ rb_hash_aset(iseq->compile_data, label_sym,
+ label = NEW_LABEL(nd_line(node)));
+ }
+ }
+ else {
+ rb_bug("illegal goto/label format");
+ }
+
+
+ if (mid == goto_id) {
+ ADD_INSNL(ret, nd_line(node), jump, label);
+ }
+ else {
+ ADD_LABEL(ret, label);
+ }
+ break;
+ }
+ }
+#endif
+ /* reciever */
+ if (type == NODE_CALL) {
+ COMPILE(recv, "recv", node->nd_recv);
+ }
+ else if (type == NODE_FCALL || type == NODE_VCALL) {
+ ADD_INSN(recv, nd_line(node), putself);
+ }
+
+ /* args */
+ if (nd_type(node) != NODE_VCALL) {
+ argc = setup_arg(iseq, args, node, &flag);
+ }
+ else {
+ argc = INT2FIX(0);
+ }
+
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, args);
+
+ debugp_param("call args argc", argc);
+ debugp_param("call method", ID2SYM(mid));
+
+ switch (nd_type(node)) {
+ case NODE_VCALL:
+ flag |= VM_CALL_VCALL_BIT;
+ /* VCALL is funcall, so fall through */
+ case NODE_FCALL:
+ flag |= VM_CALL_FCALL_BIT;
+ }
+
+ ADD_SEND_R(ret, nd_line(node), ID2SYM(mid),
+ argc, parent_block, INT2FIX(flag));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_SUPER:
+ case NODE_ZSUPER:{
+ DECL_ANCHOR(args);
+ VALUE argc;
+ VALUE flag = 0;
+ VALUE parent_block = iseq->compile_data->current_block;
+ iseq->compile_data->current_block = Qfalse;
+
+ if (nd_type(node) == NODE_SUPER) {
+ argc = setup_arg(iseq, args, node, &flag);
+ }
+ else {
+ /* NODE_ZSUPER */
+ int i;
+ yarv_iseq_t *liseq = iseq->local_iseq;
+
+ argc = INT2FIX(liseq->argc);
+
+ /* normal arguments */
+ for (i = 0; i < liseq->argc; i++) {
+ int idx = liseq->local_size - i;
+ ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
+ }
+ if (!liseq->arg_simple) {
+ if (liseq->arg_opts) {
+ /* optional arguments */
+ int j;
+ for (j = 0; j < liseq->arg_opts - 1; j++) {
+ int idx = liseq->local_size - (i + j);
+ ADD_INSN1(args, nd_line(node), getlocal,
+ INT2FIX(idx));
+ }
+ i += j;
+ argc = INT2FIX(i);
+ }
+ if (liseq->arg_rest) {
+ /* rest arguments */
+ int idx = liseq->local_size - liseq->arg_rest + 1;
+ ADD_INSN1(args, nd_line(node), getlocal,
+ INT2FIX(idx));
+ argc = INT2FIX(liseq->arg_rest);
+ flag |= VM_CALL_ARGS_SPLAT_BIT;
+ }
+ }
+ }
+
+ /* dummy reciever */
+ ADD_INSN1(ret, nd_line(node), putobject,
+ nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
+ ADD_SEQ(ret, args);
+ ADD_INSN3(ret, nd_line(node), invokesuper,
+ argc, parent_block, INT2FIX(flag));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_ARRAY:{
+ compile_array(iseq, ret, node, Qtrue);
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_ZARRAY:{
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), newarray, INT2FIX(0));
+ }
+ break;
+ }
+ case NODE_VALUES:{
+ NODE *n = node;
+ while (n) {
+ COMPILE(ret, "values item", n->nd_head);
+ n = n->nd_next;
+ }
+ ADD_INSN1(ret, nd_line(node), newarray, INT2FIX(node->nd_alen));
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_HASH:{
+ DECL_ANCHOR(list);
+ VALUE size = 0;
+ int type = node->nd_head ? nd_type(node->nd_head) : NODE_ZARRAY;
+
+ switch (type) {
+ case NODE_ARRAY:{
+ compile_array(iseq, list, node->nd_head, Qfalse);
+ size = OPERAND_AT(POP_ELEMENT(list), 0);
+ ADD_SEQ(ret, list);
+ break;
+ }
+ case NODE_ZARRAY:
+ size = INT2FIX(0);
+ break;
+
+ default:
+ rb_bug("can't make hash with this node: %s", node_name(type));
+ }
+
+ ADD_INSN1(ret, nd_line(node), newhash, size);
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_RETURN:{
+ yarv_iseq_t *is = iseq;
+
+ while (is) {
+ if (is->type == ISEQ_TYPE_TOP || is->type == ISEQ_TYPE_CLASS) {
+ COMPILE_ERROR(("Illegal return"));
+ break;
+ }
+ else {
+ if (is->type == ISEQ_TYPE_METHOD) {
+ ADD_INSN(ret, nd_line(node), emptstack);
+ }
+
+ COMPILE(ret, "return nd_stts (return val)",
+ node->nd_stts);
+
+ if (is->type == ISEQ_TYPE_METHOD) {
+ add_ensure_iseq(ret, iseq);
+ ADD_INSN(ret, nd_line(node), leave);
+ }
+ else {
+ ADD_INSN1(ret, nd_line(node), throw,
+ INT2FIX(0x01) /* TAG_RETURN */ );
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+ case NODE_YIELD:{
+ DECL_ANCHOR(args);
+ int argc;
+ unsigned long flag = 0;
+
+ if (iseq->type == ISEQ_TYPE_TOP || iseq->type == ISEQ_TYPE_CLASS) {
+ COMPILE_ERROR(("Illegal yield"));
+ }
+
+ if (node->nd_head) {
+ if (nd_type(node->nd_head) == NODE_ARRAY) {
+ NODE *p;
+ for (argc = 0, p = node->nd_head; p;
+ p = p->nd_next, argc++) {
+ /* count argc */
+ }
+ if (argc == 1) {
+ COMPILE(args, "yield with an arg", node->nd_head);
+ }
+ else {
+ compile_array(iseq, args, node->nd_head, Qfalse);
+ POP_ELEMENT(args);
+ }
+ debugs("argc: %d\n", argc);
+ }
+ else {
+ if (nd_type(node->nd_head) == NODE_ARGSCAT) {
+ if (node->nd_state == 2) {
+ flag |= VM_CALL_ARGS_SPLAT_BIT;
+ }
+
+ compile_array(iseq, args, node->nd_head->nd_head,
+ Qfalse);
+ POP_ELEMENT(args);
+ argc = LIST_SIZE(args) + 1;
+
+ COMPILE(args, "args(cat: splat)",
+ node->nd_head->nd_body);
+ }
+ else if (nd_type(node->nd_head) == NODE_SPLAT) {
+ if (node->nd_state == 2) {
+ flag |= VM_CALL_ARGS_SPLAT_BIT;
+ }
+
+ argc = 1;
+ COMPILE(args, "splat", node->nd_head->nd_head);
+ }
+ else {
+ COMPILE(args, "nd_head(1)", node->nd_head);
+ argc = 1;
+ }
+ }
+ }
+ else {
+ argc = 0;
+ }
+ ADD_SEQ(ret, args);
+ ADD_INSN2(ret, nd_line(node), invokeblock, INT2FIX(argc),
+ INT2FIX(flag));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_LVAR:{
+ if (!poped) {
+ int idx = iseq->local_iseq->local_size + 2 - node->nd_cnt;
+ debugs("idx: %d\n", idx);
+ ADD_INSN1(ret, nd_line(node), getlocal, INT2FIX(idx));
+ }
+ break;
+ }
+ case NODE_DVAR:{
+ int lv, idx, ls;
+ debugi("nd_vid", node->nd_vid);
+ if (!poped) {
+ idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
+ if (idx < 0) {
+ rb_bug("unknown dvar (%s)", rb_id2name(node->nd_vid));
+ }
+ ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(ls - idx),
+ INT2FIX(lv));
+ }
+ break;
+ }
+ case NODE_GVAR:{
+ ADD_INSN1(ret, nd_line(node), getglobal,
+ (((long)node->nd_entry) | 1));
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_IVAR:{
+ debugi("nd_vid", node->nd_vid);
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), getinstancevariable,
+ ID2SYM(node->nd_vid));
+ }
+ break;
+ }
+ case NODE_CONST:{
+ debugi("nd_vid", node->nd_vid);
+
+ if (iseq->compile_data->option->inline_const_cache) {
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+
+ ADD_LABEL(ret, lstart);
+ ADD_INSN2(ret, nd_line(node), getinlinecache,
+ NEW_INLINE_CACHE_ENTRY(), lend);
+ ADD_INSN1(ret, nd_line(node), getconstant, ID2SYM(node->nd_vid));
+ ADD_INSN1(ret, nd_line(node), setinlinecache, lstart);
+ ADD_LABEL(ret, lend);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_INSN1(ret, nd_line(node), getconstant, ID2SYM(node->nd_vid));
+ }
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_CVAR:{
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), getclassvariable,
+ ID2SYM(node->nd_vid));
+ }
+ break;
+ }
+ case NODE_NTH_REF:{
+ ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(node->nd_cnt),
+ INT2FIX(node->nd_nth << 1));
+ break;
+ }
+ case NODE_BACK_REF:{
+ ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(node->nd_cnt),
+ INT2FIX(0x01 | (node->nd_nth << 1)));
+ break;
+ }
+ case NODE_MATCH:
+ case NODE_MATCH2:
+ case NODE_MATCH3:{
+ DECL_ANCHOR(recv);
+ DECL_ANCHOR(val);
+
+ switch(nd_type(node)) {
+ case NODE_MATCH:
+ ADD_INSN1(recv, nd_line(node), putobject, node->nd_lit);
+ ADD_INSN2(val, nd_line(node), getspecial, INT2FIX(0),
+ INT2FIX(0));
+ break;
+ case NODE_MATCH2:
+ COMPILE(recv, "reciever", node->nd_recv);
+ COMPILE(val, "value", node->nd_value);
+ break;
+ case NODE_MATCH3:
+ COMPILE(recv, "reciever", node->nd_value);
+ COMPILE(val, "value", node->nd_recv);
+ break;
+ }
+
+ if (iseq->compile_data->option->specialized_instruction) {
+ /* TODO: detect by node */
+ if (recv->last == recv->anchor.next &&
+ INSN_OF(recv->last) == BIN(putobject) &&
+ nd_type(node) == NODE_MATCH2) {
+ ADD_SEQ(ret, val);
+ ADD_INSN1(ret, nd_line(node), opt_regexpmatch1,
+ OPERAND_AT(recv->last, 0));
+ }
+ else {
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, val);
+ ADD_INSN(ret, nd_line(node), opt_regexpmatch2);
+ }
+ }
+ else {
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, val);
+ ADD_SEND(ret, nd_line(node), ID2SYM(idEqTilde), INT2FIX(1));
+ }
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_LIT:{
+ debugp_param("lit", node->nd_lit);
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), putobject, node->nd_lit);
+ }
+ break;
+ }
+ case NODE_STR:{
+ debugp_param("nd_lit", node->nd_lit);
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), putstring, node->nd_lit);
+ }
+ break;
+ }
+ case NODE_DSTR:{
+ compile_dstr(iseq, ret, node);
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_XSTR:{
+ ADD_INSN(ret, nd_line(node), putself);
+ ADD_INSN1(ret, nd_line(node), putobject, node->nd_lit);
+ ADD_CALL(ret, nd_line(node), ID2SYM(idBackquote), INT2FIX(1));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_DXSTR:{
+ ADD_INSN(ret, nd_line(node), putself);
+ compile_dstr(iseq, ret, node);
+ ADD_CALL(ret, nd_line(node), ID2SYM(idBackquote), INT2FIX(1));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_EVSTR:{
+ COMPILE(ret, "nd_body", node->nd_body);
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), tostring);
+ }
+ break;
+ }
+ case NODE_DREGX:{
+ compile_dstr(iseq, ret, node);
+ ADD_INSN1(ret, nd_line(node), toregexp, INT2FIX(node->nd_cflag));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_DREGX_ONCE:{
+ /* fix me: once? */
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+
+ ADD_LABEL(ret, lstart);
+ ADD_INSN2(ret, nd_line(node), onceinlinecache,
+ NEW_INLINE_CACHE_ENTRY(), lend);
+ ADD_INSN(ret, nd_line(node), pop);
+
+ compile_dstr(iseq, ret, node);
+ ADD_INSN1(ret, nd_line(node), toregexp, INT2FIX(node->nd_cflag));
+
+ ADD_INSN1(ret, nd_line(node), setinlinecache, lstart);
+ ADD_LABEL(ret, lend);
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_ARGS:{
+ /* OK */
+ COMPILE_ERROR(("BUG: should not reach here: compile_each#NODE_ARGS"));
+ break;
+ }
+ case NODE_ARGSCAT:{
+ COMPILE(ret, "argscat head", node->nd_head);
+ COMPILE(ret, "argscat body", node->nd_body);
+ ADD_INSN(ret, nd_line(node), concatarray);
+ break;
+ }
+ case NODE_ARGSPUSH:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_ARGSPUSH"));
+ break;
+ }
+ case NODE_SPLAT:{
+ COMPILE(ret, "splat", node->nd_head);
+ ADD_INSN1(ret, nd_line(node), splatarray, Qfalse);
+ break;
+ }
+ case NODE_TO_ARY:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_TO_ARY"));
+ break;
+ }
+ case NODE_BLOCK_ARG:{
+ iseq->arg_block = node->nd_cnt - 2 + 1;
+ iseq->arg_simple = 0;
+ break;
+ }
+ case NODE_BLOCK_PASS:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_BLOCK_PASS"));
+ break;
+ }
+ case NODE_DEFN:{
+ VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
+ rb_str_new2(rb_id2name(node->nd_mid)),
+ ISEQ_TYPE_METHOD);
+
+ debugp_param("defn/iseq", iseqval);
+
+ ADD_INSN (ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), definemethod,
+ ID2SYM(node->nd_mid), iseqval, INT2FIX(0));
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ debugp_param("defn", iseqval);
+ break;
+ }
+ case NODE_DEFS:{
+ VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
+ rb_str_new2(rb_id2name(node->nd_mid)),
+ ISEQ_TYPE_METHOD);
+
+ debugp_param("defs/iseq", iseqval);
+
+ COMPILE(ret, "defs: recv", node->nd_recv);
+ ADD_INSN3(ret, nd_line(node), definemethod,
+ ID2SYM(node->nd_mid), iseqval, INT2FIX(1));
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+ case NODE_ALIAS:{
+ VALUE s1, s2;
+
+ if (nd_type(node->u1.node) != NODE_LIT ||
+ nd_type(node->u2.node) != NODE_LIT) {
+ rb_bug("alias args must be NODE_LIT");
+ }
+ s1 = node->u1.node->nd_lit;
+ s2 = node->u2.node->nd_lit;
+
+ ADD_INSN3(ret, nd_line(node), alias, Qfalse, ID2SYM(rb_to_id(s1)),
+ ID2SYM(rb_to_id(s2)));
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+ case NODE_VALIAS:{
+ ADD_INSN3(ret, nd_line(node), alias, Qtrue, ID2SYM(node->u1.id),
+ ID2SYM(node->u2.id));
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+ case NODE_UNDEF:{
+ if (nd_type(node->u2.node) != NODE_LIT) {
+ rb_bug("undef args must be NODE_LIT");
+ }
+ ADD_INSN1(ret, nd_line(node), undef,
+ ID2SYM(rb_to_id(node->u2.node->nd_lit)));
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+ case NODE_CLASS:{
+ VALUE iseqval =
+ NEW_CHILD_ISEQVAL(
+ node->nd_body,
+ make_name_with_str("<class:%s>",
+ rb_id2name(node->nd_cpath->nd_mid)),
+ ISEQ_TYPE_CLASS);
+ compile_cpath(ret, iseq, node->nd_cpath);
+ COMPILE(ret, "super", node->nd_super);
+ ADD_INSN3(ret, nd_line(node), defineclass,
+ ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(0));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_MODULE:{
+ VALUE iseqval = NEW_CHILD_ISEQVAL(node->nd_body,
+ make_name_with_str
+ ("<module:%s>",
+ rb_id2name(node->nd_cpath->
+ nd_mid)),
+ ISEQ_TYPE_CLASS);
+
+ COMPILE(ret, "mbase", node->nd_cpath->nd_head);
+ ADD_INSN (ret, nd_line(node), putnil); /* dummy */
+ ADD_INSN3(ret, nd_line(node), defineclass,
+ ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(2));
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_SCLASS:{
+ VALUE iseqval =
+ NEW_ISEQVAL(node->nd_body, rb_str_new2("singletonclass"),
+ ISEQ_TYPE_CLASS);
+
+ COMPILE(ret, "sclass#recv", node->nd_recv);
+ ADD_INSN (ret, nd_line(node), putnil);
+ ADD_INSN3(ret, nd_line(node), defineclass,
+ ID2SYM(rb_intern("singletonclass")), iseqval, INT2FIX(1));
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_COLON2:{
+ if (rb_is_const_id(node->nd_mid)) {
+ /* constant */
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ DECL_ANCHOR(pref);
+ DECL_ANCHOR(body);
+
+ compile_colon2(iseq, node, pref, body);
+ if (LIST_SIZE_ZERO(pref)) {
+ if (iseq->compile_data->option->inline_const_cache) {
+ ADD_LABEL(ret, lstart);
+ ADD_INSN2(ret, nd_line(node), getinlinecache,
+ NEW_INLINE_CACHE_ENTRY(), lend);
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+
+ ADD_SEQ(ret, body);
+
+ if (iseq->compile_data->option->inline_const_cache) {
+ ADD_INSN1(ret, nd_line(node), setinlinecache, lstart);
+ ADD_LABEL(ret, lend);
+ }
+ }
+ else {
+ ADD_SEQ(ret, pref);
+ ADD_SEQ(ret, body);
+ }
+ }
+ else {
+ /* function call */
+ ADD_INSN(ret, nd_line(node), putself);
+ COMPILE(ret, "colon2#nd_head", node->nd_head);
+ ADD_CALL(ret, nd_line(node), ID2SYM(node->nd_mid),
+ INT2FIX(1));
+ }
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_COLON3:{
+ LABEL *lstart = NEW_LABEL(nd_line(node));
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ debugi("colon3#nd_mid", node->nd_mid);
+
+ /* add cache insn */
+ if (iseq->compile_data->option->inline_const_cache) {
+ ADD_LABEL(ret, lstart);
+ ADD_INSN2(ret, nd_line(node), getinlinecache,
+ NEW_INLINE_CACHE_ENTRY(), lend);
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+
+ ADD_INSN1(ret, nd_line(node), putobject, rb_cObject);
+ ADD_INSN1(ret, nd_line(node), getconstant, ID2SYM(node->nd_mid));
+
+ if (iseq->compile_data->option->inline_const_cache) {
+ ADD_INSN1(ret, nd_line(node), setinlinecache, lstart);
+ ADD_LABEL(ret, lend);
+ }
+
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_CREF:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_CREF"));
+ break;
+ }
+ case NODE_DOT2:
+ case NODE_DOT3:{
+ int flag = type == NODE_DOT2 ? INT2FIX(0) : INT2FIX(1);
+ COMPILE(ret, "min", (NODE *) node->nd_beg);
+ COMPILE(ret, "max", (NODE *) node->nd_end);
+ if (poped) {
+ ADD_INSN(ret, nd_line(node), pop);
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ else {
+ ADD_INSN1(ret, nd_line(node), newrange, flag);
+ }
+ break;
+ }
+ case NODE_FLIP2:
+ case NODE_FLIP3:{
+ LABEL *lend = NEW_LABEL(nd_line(node));
+ LABEL *lfin = NEW_LABEL(nd_line(node));
+ LABEL *ltrue = NEW_LABEL(nd_line(node));
+
+ ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(node->nd_cnt),
+ INT2FIX(0));
+ ADD_INSNL(ret, nd_line(node), branchif, lend) ;
+
+ /* *flip == 0 */
+ COMPILE(ret, "flip2 beg", node->nd_beg);
+ ADD_INSN(ret, nd_line(node), dup);
+ ADD_INSNL(ret, nd_line(node), branchunless, lfin);
+ if (nd_type(node) == NODE_FLIP3) {
+ ADD_INSN(ret, nd_line(node), dup);
+ ADD_INSN1(ret, nd_line(node), setspecial, INT2FIX(node->nd_cnt));
+ ADD_INSNL(ret, nd_line(node), jump, lfin);
+ }
+ else {
+ ADD_INSN1(ret, nd_line(node), setspecial, INT2FIX(node->nd_cnt));
+ }
+
+ /* *flip == 1 */
+ ADD_LABEL(ret, lend);
+ COMPILE(ret, "flip2 end", node->nd_end);
+ ADD_INSNL(ret, nd_line(node), branchunless, ltrue);
+ ADD_INSN1(ret, nd_line(node), putobject, Qfalse);
+ ADD_INSN1(ret, nd_line(node), setspecial, INT2FIX(node->nd_cnt));
+
+ ADD_LABEL(ret, ltrue);
+ ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
+
+ ADD_LABEL(ret, lfin);
+ break;
+ }
+ case NODE_ATTRSET:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_ATTRSET"));
+ break;
+ }
+ case NODE_SELF:{
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putself);
+ }
+
+ break;
+ }
+ case NODE_NIL:{
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+ case NODE_TRUE:{
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
+ }
+ break;
+ }
+ case NODE_FALSE:{
+ if (!poped) {
+ ADD_INSN1(ret, nd_line(node), putobject, Qfalse);
+ }
+ break;
+ }
+ case NODE_ERRINFO:{
+ if (!poped) {
+ if (iseq->type == ISEQ_TYPE_RESCUE) {
+ ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(1),
+ INT2FIX(0));
+ }
+ else {
+ yarv_iseq_t *ip = iseq;
+ int level = 0;
+ while (ip) {
+ if (ip->type == ISEQ_TYPE_RESCUE) {
+ break;
+ }
+ ip = ip->parent_iseq;
+ level++;
+ }
+ if (ip) {
+ ADD_INSN2(ret, nd_line(node), getdynamic, INT2FIX(1),
+ INT2FIX(level));
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ }
+ }
+ break;
+ }
+ case NODE_DEFINED:{
+ if (!poped) {
+ LABEL *lfinish = NEW_LABEL(nd_line(node));
+ defined_expr(iseq, ret, node->nd_head, lfinish, Qtrue);
+ ADD_LABEL(ret, lfinish);
+ }
+ break;
+ }
+ case NODE_POSTEXE:{
+ VALUE block = NEW_CHILD_ISEQVAL(node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK);
+ ADD_INSN1(ret, nd_line(node), postexe, block);
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ }
+ break;
+ }
+#ifdef C_ALLOCA
+ case NODE_ALLOCA:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_ALLOCA"));
+ break;
+ }
+#endif
+ case NODE_BMETHOD:{
+ /* block method, OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_BMETHOD"));
+ break;
+ }
+ case NODE_MEMO:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_MEMO"));
+ break;
+ }
+ case NODE_IFUNC:{
+ /* OK */
+ COMPILE_ERROR(("BUG: unknown node: NODE_IFUNC"));
+ break;
+ }
+ case NODE_DSYM:{
+ compile_dstr(iseq, ret, node);
+ if (!poped) {
+ ADD_SEND(ret, nd_line(node), ID2SYM(idIntern), INT2FIX(0));
+ }
+ else {
+ ADD_INSN(ret, nd_line(node), pop);
+ }
+ break;
+ }
+ case NODE_ATTRASGN:{
+ DECL_ANCHOR(recv);
+ DECL_ANCHOR(args);
+ VALUE flag = 0;
+ VALUE argc;
+
+ argc = setup_arg(iseq, args, node, &flag);
+
+ if (node->nd_recv == (NODE *) 1) {
+ ADD_INSN(recv, nd_line(node), putself);
+ }
+ else {
+ COMPILE(recv, "recv", node->nd_recv);
+ }
+
+ debugp_param("argc", argc);
+ debugp_param("nd_mid", ID2SYM(node->nd_mid));
+
+ if (!poped) {
+ ADD_INSN(ret, nd_line(node), putnil);
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, args);
+ ADD_INSN1(ret, nd_line(node), setn, INT2FIX(FIX2INT(argc) + 1));
+ }
+ else {
+ ADD_SEQ(ret, recv);
+ ADD_SEQ(ret, args);
+ }
+ ADD_SEND_R(ret, nd_line(node), ID2SYM(node->nd_mid), argc, 0, INT2FIX(flag));
+ ADD_INSN(ret, nd_line(node), pop);
+
+ break;
+ }
+ case NODE_OPTBLOCK:{
+ /* for optimize */
+ LABEL *redo_label = NEW_LABEL(0);
+ LABEL *next_label = NEW_LABEL(0);
+
+ iseq->compile_data->start_label = next_label;
+ iseq->compile_data->redo_label = redo_label;
+
+ ADD_LABEL(ret, redo_label);
+ COMPILE_(ret, "optblock body", node->nd_head, 1 /* pop */ );
+ ADD_LABEL(ret, next_label);
+ ADD_INSN(ret, 0, opt_checkenv);
+ break;
+ }
+ case NODE_PRELUDE:{
+ COMPILE_POPED(ret, "prelude", node->nd_head);
+ COMPILE_(ret, "body", node->nd_body, poped);
+ break;
+ }
+ default:
+ COMPILE_ERROR(("BUG: unknown node (default): %s", node_name(type)));
+ return Qnil;
+ }
+
+ debug_nodeprint_close();
+ return COMPILE_OK;
+}
+
+/***************************/
+/* instruction information */
+/***************************/
+
+static int
+insn_data_length(INSN *iobj)
+{
+ return insn_len(iobj->insn_id);
+}
+
+static int
+calc_sp_depth(int depth, INSN *insn)
+{
+ return insn_stack_increase(depth, insn->insn_id, insn->operands);
+}
+
+static int
+insn_data_line_no(INSN *iobj)
+{
+ return insn_len(iobj->line_no);
+}
+
+static VALUE
+insn_data_to_s_detail(INSN *iobj)
+{
+ VALUE str = rb_str_new(0, 0);
+ char buff[0x100];
+
+ snprintf(buff, sizeof(buff), "%-16s", insn_name(iobj->insn_id));
+ rb_str_cat2(str, buff);
+ if (iobj->operands) {
+ char *types = insn_op_types(iobj->insn_id);
+ int j;
+
+ for (j = 0; types[j]; j++) {
+ char type = types[j];
+
+ switch (type) {
+ case TS_OFFSET: /* label(destination position) */
+ {
+ char buff[0x100];
+ LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
+ snprintf(buff, sizeof(buff), "<L%03d>", lobj->label_no);
+ rb_str_concat(str, rb_str_new2(buff));
+ break;
+ }
+ break;
+ case TS_ISEQ: /* iseq */
+ {
+ yarv_iseq_t *iseq = (yarv_iseq_t *)OPERAND_AT(iobj, j);
+ VALUE val = Qnil;
+ if (iseq) {
+ val = iseq->self;
+ }
+ rb_str_concat(str, rb_inspect(val));
+ }
+ break;
+ case TS_LINDEX:
+ case TS_DINDEX:
+ case TS_NUM: /* ulong */
+ case TS_VALUE: /* VALUE */
+ rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
+ break;
+ case TS_ID: /* ID */
+ rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
+ break;
+ case TS_GENTRY:
+ {
+ struct global_entry *entry = (struct global_entry *)
+ (OPERAND_AT(iobj, j) & (~1));
+ rb_str_cat2(str, rb_id2name(entry->id));
+ }
+ case TS_IC: /* method cache */
+ rb_str_cat2(str, "<ic>");
+ break;
+ case TS_CDHASH: /* case/when condition cache */
+ rb_str_cat2(str, "<ch>");
+ break;
+ default:{
+ rb_bug("unknown operand type: %c", type);
+ }
+ }
+ if (types[j + 1]) {
+ rb_str_cat2(str, ", ");
+ }
+ }
+ }
+ return str;
+}
+
+static void
+dump_disasm_anchor(LINK_ANCHOR *anc)
+{
+ dump_disasm_list(FIRST_ELEMENT(anc));
+}
+
+static void
+dump_disasm_list(struct iseq_link_element *link)
+{
+ int pos = 0;
+ INSN *iobj;
+ LABEL *lobj;
+ VALUE str;
+
+ printf("-- raw disasm--------\n");
+
+ while (link) {
+ switch (link->type) {
+ case ISEQ_ELEMENT_INSN:{
+ iobj = (INSN *)link;
+ str = insn_data_to_s_detail(iobj);
+ printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str),
+ insn_data_line_no(iobj));
+ pos += insn_data_length(iobj);
+ break;
+ }
+ case ISEQ_ELEMENT_LABEL:{
+ lobj = (LABEL *)link;
+ printf("<L%03d>\n", lobj->label_no);
+ break;
+ }
+ case ISEQ_ELEMENT_NONE:{
+ printf("[none]\n");
+ break;
+ }
+ default:
+ /* ignore */
+ printf("%ld\n", FIX2LONG(link->type));
+ rb_bug("dump_disasm_list error");
+ }
+ link = link->next;
+ }
+ printf("---------------------\n");
+}
+
+int
+nd_line_debug(NODE * n)
+{
+ return nd_line(n);
+}
+
+VALUE
+insns_name_array(void)
+{
+ VALUE ary = rb_ary_new();
+ int i;
+ for (i = 0; i < sizeof(insn_name_info) / sizeof(insn_name_info[0]); i++) {
+ rb_ary_push(ary, rb_str_new2(insn_name_info[i]));
+ }
+ return ary;
+}
+
+static LABEL *
+register_label(yarv_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
+{
+ LABEL *label = 0;
+ obj = rb_convert_type(obj, T_SYMBOL, "Symbol", "to_sym");
+
+ if (st_lookup(labels_table, obj, (st_data_t *)&label) == 0) {
+ label = NEW_LABEL(0);
+ st_insert(labels_table, obj, (st_data_t)label);
+ }
+ return label;
+}
+
+static VALUE
+get_exception_sym2type(VALUE sym)
+{
+ static VALUE symRescue, symEnsure, symRetry;
+ static VALUE symBreak, symRedo, symNext;
+
+ if (symRescue == 0) {
+ symRescue = ID2SYM(rb_intern("rescue"));
+ symEnsure = ID2SYM(rb_intern("ensure"));
+ symRetry = ID2SYM(rb_intern("retry"));
+ symBreak = ID2SYM(rb_intern("break"));
+ symRedo = ID2SYM(rb_intern("redo"));
+ symNext = ID2SYM(rb_intern("next"));
+ }
+
+ if (sym == symRescue) return CATCH_TYPE_RESCUE;
+ if (sym == symEnsure) return CATCH_TYPE_ENSURE;
+ if (sym == symRetry) return CATCH_TYPE_RETRY;
+ if (sym == symBreak) return CATCH_TYPE_BREAK;
+ if (sym == symRedo) return CATCH_TYPE_REDO;
+ if (sym == symNext) return CATCH_TYPE_NEXT;
+ rb_bug("get_exception_sym2type");
+ return 0;
+}
+
+VALUE iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt);
+
+static int
+iseq_build_exception(yarv_iseq_t *iseq, struct st_table *labels_table,
+ VALUE exception)
+{
+ int i;
+ VALUE tmp;
+
+ for (i=0; i<RARRAY_LEN(exception); i++) {
+ VALUE v = rb_ary_entry(exception, i);
+ VALUE *ptr = RARRAY_PTR(v);
+ VALUE type = get_exception_sym2type(ptr[0]);
+ VALUE eiseqval;
+ LABEL *lstart, *lend, *lcont;
+ int sp;
+
+ if (ptr[1] == Qnil) {
+ eiseqval = 0;
+ }
+ else {
+ eiseqval = iseq_load(0, ptr[1], iseq->self, Qnil);
+ }
+
+ lstart = register_label(iseq, labels_table, ptr[2]);
+ lend = register_label(iseq, labels_table, ptr[3]);
+ lcont = register_label(iseq, labels_table, ptr[4]);
+ sp = NUM2INT(ptr[5]);
+
+ ADD_CATCH_ENTRY(type, lstart, lend, eiseqval, lcont);
+ }
+ return COMPILE_OK;
+}
+
+
+struct st_table *insn_make_insn_table(void);
+
+static int
+iseq_build_body(yarv_iseq_t *iseq, LINK_ANCHOR *anchor,
+ VALUE body, VALUE line, struct st_table *labels_table)
+{
+ /* TODO: body should be freezed */
+ VALUE *ptr = RARRAY_PTR(body);
+ int len = RARRAY_LEN(body);
+ int i, j;
+ int line_no = 0;
+ /*
+ * index -> LABEL *label
+ */
+ static struct st_table *insn_table;
+
+ if (insn_table == 0) {
+ insn_table = insn_make_insn_table();
+ }
+
+ for (i=0; i<len; i++) {
+ VALUE obj = ptr[i];
+
+ if (SYMBOL_P(obj)) {
+ LABEL *label = register_label(iseq, labels_table, obj);
+ ADD_LABEL(anchor, label);
+ }
+ else if (FIXNUM_P(obj)) {
+ line_no = NUM2INT(obj);
+ }
+ else if (TYPE(obj) == T_ARRAY) {
+ VALUE *argv = 0;
+ int argc = RARRAY_LEN(obj) - 1;
+ VALUE insn_id;
+
+ if (st_lookup(insn_table, rb_ary_entry(obj, 0), &insn_id) == 0) {
+ // TODO: exception
+ rb_bug("unknown instruction: ");
+ }
+
+ if (argc != insn_len(insn_id)-1) {
+ rb_bug("operand size mismatch");
+ }
+
+ if (argc > 0) {
+ argv = compile_data_alloc(iseq, sizeof(VALUE) * argc);
+ for (j=0; j<argc; j++) {
+ VALUE op = rb_ary_entry(obj, j+1);
+ switch (insn_op_type(insn_id, j)) {
+ case TS_OFFSET: {
+ LABEL *label = register_label(iseq, labels_table, op);
+ argv[j] = (VALUE)label;
+ break;
+ }
+ case TS_LINDEX:
+ case TS_DINDEX:
+ case TS_NUM:
+ argv[j] = (NUM2INT(op), op);
+ break;
+ case TS_VALUE:
+ argv[j] = op;
+ if (!SPECIAL_CONST_P(op)) {
+ iseq_add_mark_object(iseq, op);
+ }
+ break;
+ case TS_ISEQ:
+ {
+ if (op != Qnil) {
+ if (TYPE(op) == T_ARRAY) {
+ argv[j] =
+ iseq_load(0, op, iseq->self, Qnil);
+ }
+ else if (CLASS_OF(op) == cYarvISeq) {
+ argv[j] = op;
+ }
+ else {
+ /* TODO: exception */
+ rb_bug("not an iseq");
+ }
+ iseq_add_mark_object(iseq, argv[j]);
+ }
+ else {
+ argv[j] = 0;
+ }
+ }
+ break;
+ case TS_GENTRY:
+ op = rb_convert_type(op, T_SYMBOL, "Symbol", "to_sym");
+ argv[j] = (VALUE)rb_global_entry(SYM2ID(op));
+ break;
+ case TS_IC:
+ argv[j] = (VALUE)NEW_INLINE_CACHE_ENTRY();
+ iseq_add_mark_object(iseq, argv[j]);
+ break;
+ case TS_ID:
+ argv[j] = rb_convert_type(op, T_SYMBOL,
+ "Symbol", "to_sym");
+ break;
+ case TS_CDHASH:
+ {
+ int i;
+ op = rb_convert_type(op, T_ARRAY, "Array", "to_ary");
+ for (i=0; i<RARRAY_LEN(op); i+=2) {
+ VALUE sym = rb_ary_entry(op, i+1);
+ LABEL *label =
+ register_label(iseq, labels_table, sym);
+ rb_ary_store(op, i+1, (VALUE)label | 1);
+ }
+ argv[j] = op;
+ }
+ break;
+ default:
+ rb_bug("unknown operand: %c", insn_op_type(insn_id, j));
+ }
+ }
+ }
+ ADD_ELEM(anchor,
+ (LINK_ELEMENT*)new_insn_core(iseq, line_no,
+ insn_id, argc, argv));
+ }
+ else {
+ rb_raise(rb_eTypeError, "unexpected object for instruction");
+ }
+ }
+ st_free_table(labels_table);
+ iseq_setup(iseq, anchor);
+ return COMPILE_OK;
+}
+
+VALUE
+iseq_build_from_ary(yarv_iseq_t *iseq, VALUE line,
+ VALUE locals, VALUE args, VALUE exception, VALUE body)
+{
+ int i;
+ int opt = 0;
+ ID *tbl;
+ struct st_table *labels_table = st_init_numtable();
+
+ DECL_ANCHOR(anchor);
+
+ if (iseq->type == ISEQ_TYPE_METHOD ||
+ iseq->type == ISEQ_TYPE_TOP ||
+ iseq->type == ISEQ_TYPE_CLASS) {
+ opt = 1;
+ }
+
+ iseq->local_size = opt + RARRAY_LEN(locals);
+ iseq->local_tbl = (ID *)ALLOC_N(ID *, iseq->local_size);
+ tbl = iseq->local_tbl + opt;
+
+ for (i=0; i<RARRAY_LEN(locals); i++) {
+ tbl[i] = SYM2ID(RARRAY_PTR(locals)[i]);
+ }
+
+ /* args */
+ if (FIXNUM_P(args)) {
+ iseq->argc = FIX2INT(args);
+ iseq->arg_simple = 1;
+ }
+ else {
+ /*
+ * [argc, # argc
+ * opts, # opts
+ * [label1, label2, ...] # opt labels
+ * rest_iex,
+ * block_idx,
+ * ]
+ * or
+ * argc (Fixnum) # arg_simple
+ */
+ int i = 0;
+ VALUE argc = rb_ary_entry(args, i++);
+ VALUE arg_opts = rb_ary_entry(args, i++);
+ VALUE arg_opt_labels = rb_ary_entry(args, i++);
+ VALUE arg_rest = rb_ary_entry(args, i++);
+ VALUE arg_block = rb_ary_entry(args, i++);
+
+ iseq->argc = FIX2INT(argc);
+ iseq->arg_opts = FIX2INT(arg_opts);
+ iseq->arg_rest = FIX2INT(arg_rest);
+ iseq->arg_block = FIX2INT(arg_block);
+
+ iseq->arg_opt_tbl = (VALUE *)ALLOC_N(VALUE, iseq->arg_opts);
+
+ for (i=0; i<RARRAY_LEN(arg_opt_labels); i++) {
+ iseq->arg_opt_tbl[i] =
+ (VALUE)register_label(iseq, labels_table,
+ rb_ary_entry(arg_opt_labels, i));
+ }
+ }
+
+ /* exception */
+ iseq_build_exception(iseq, labels_table, exception);
+
+ /* body */
+ iseq_build_body(iseq, anchor, body, line, labels_table);
+ return iseq->self;
+}