diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2006-12-31 15:02:22 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2006-12-31 15:02:22 +0000 |
commit | a3e1b1ce7ed7e7ffac23015fc2fde56511b30681 (patch) | |
tree | 7b725552a9a4ded93849ca2faab1b257f7761790 /compile.c | |
parent | 3e7566d8fb5138bb9cd647e5fdefc54fc9803509 (diff) | |
download | ruby-a3e1b1ce7ed7e7ffac23015fc2fde56511b30681.tar.gz |
* Merge YARV
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11439 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'compile.c')
-rw-r--r-- | compile.c | 4914 |
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; +} |