ruby/compile.c

4884 строки
114 KiB
C

/**********************************************************************
compile.c - ruby node tree -> VM instruction sequence
$Author$
$Date$
created at: 04/01/01 03:42:15 JST
Copyright (C) 2004-2007 Koichi Sasada
**********************************************************************/
#include "ruby/ruby.h"
#include "ruby/node.h"
#define USE_INSN_STACK_INCREASE 1
#include "vm_core.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
/* iseq.c */
VALUE iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt);
/* vm.c */
VALUE vm_eval(void *);
/* 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;
};
#include "optinsn.inc"
#if OPT_INSTRUCTIONS_UNIFICATION
#include "optunifs.inc"
#endif
/* for debug */
#if CPDEBUG > 0
static long gl_node_level = 0;
static void debug_list(LINK_ANCHOR *anchor);
#endif
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 void ADD_ELEM(LINK_ANCHOR *anchor, LINK_ELEMENT *elem);
static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int insn_id, int argc, ...);
static LABEL *new_label_body(rb_iseq_t *iseq, int line);
static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * n, int);
static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_set_local_table(rb_iseq_t *iseq, ID *tbl);
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * node);
static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
static int iseq_set_exception_table(rb_iseq_t *iseq);
static int iseq_set_optargs_table(rb_iseq_t *iseq);
static int
iseq_add_mark_object(rb_iseq_t *iseq, VALUE v)
{
if (!SPECIAL_CONST_P(v)) {
rb_ary_push(iseq->mark_ary, v);
}
return COMPILE_OK;
}
static int
iseq_add_mark_object_compile_time(rb_iseq_t *iseq, VALUE v)
{
if (!SPECIAL_CONST_P(v)) {
rb_ary_push(iseq->compile_data->mark_ary, v);
}
return COMPILE_OK;
}
VALUE
iseq_compile(VALUE self, NODE *node)
{
DECL_ANCHOR(ret);
rb_iseq_t *iseq;
INIT_ANCHOR(ret);
GetISeqPtr(self, iseq);
if (node == 0) {
COMPILE(ret, "nil", node);
}
else if (nd_type(node) == NODE_SCOPE) {
/* iseq type of top, method, class, block */
iseq_set_local_table(iseq, node->nd_tbl);
iseq_set_arguments(iseq, ret, node->nd_args);
switch (iseq->type) {
case ISEQ_TYPE_BLOCK: {
LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0);
LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0);
ADD_LABEL(ret, iseq->compile_data->start_label);
COMPILE(ret, "block body", node->nd_body);
ADD_LABEL(ret, 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);
break;
}
case ISEQ_TYPE_CLASS: {
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CLASS);
COMPILE(ret, "scoped node", node->nd_body);
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
break;
}
case ISEQ_TYPE_METHOD: {
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CALL);
COMPILE(ret, "scoped node", node->nd_body);
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
break;
}
default: {
COMPILE(ret, "scoped node", node->nd_body);
break;
}
}
}
else {
switch (iseq->type) {
case ISEQ_TYPE_METHOD:
case ISEQ_TYPE_CLASS:
case ISEQ_TYPE_BLOCK:
case ISEQ_TYPE_EVAL:
case ISEQ_TYPE_TOP:
rb_compile_error(ERROR_ARGS "compile/should not be reached: %s:%d",
__FILE__, __LINE__);
break;
case ISEQ_TYPE_RESCUE:
iseq_set_exception_local_table(iseq);
COMPILE(ret, "rescue", node);
break;
case ISEQ_TYPE_ENSURE:
iseq_set_exception_local_table(iseq);
COMPILE_POPED(ret, "ensure", node);
break;
case ISEQ_TYPE_DEFINED_GUARD:
COMPILE(ret, "defined guard", node);
break;
default:
rb_bug("unknown scope");
}
}
if (iseq->type == ISEQ_TYPE_RESCUE || iseq->type == ISEQ_TYPE_ENSURE) {
ADD_INSN2(ret, 0, getdynamic, INT2FIX(1), INT2FIX(0));
ADD_INSN1(ret, 0, throw, INT2FIX(0) /* continue throw */ );
}
else {
ADD_INSN(ret, iseq->compile_data->last_line, leave);
}
return iseq_setup(iseq, ret);
}
int
iseq_translate_threaded_code(rb_iseq_t *iseq)
{
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
#if OPT_DIRECT_THREADED_CODE
const void *const *table = (const void **)vm_eval(0);
#else
extern const void *const *get_insns_address_table();
const void *const *table = get_insns_address_table();
#endif
int i;
iseq->iseq_encoded = ALLOC_N(VALUE, iseq->iseq_size);
MEMCPY(iseq->iseq_encoded, iseq->iseq, VALUE, iseq->iseq_size);
for (i = 0; i < iseq->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(rb_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(rb_iseq_t *iseq)
{
return (INSN *)compile_data_alloc(iseq, sizeof(INSN));
}
static LABEL *
compile_data_alloc_label(rb_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 /* unused */
/*
* 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
#if 0 /* unused */
/*
* 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;
}
}
#endif
/*
* 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;
}
#if 0 /* unused */
static LINK_ELEMENT *
LAST_ELEMENT(LINK_ANCHOR *anchor)
{
return anchor->last;
}
#endif
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;
}
#if 0 /* unused */
static LINK_ELEMENT *
SHIFT_ELEMENT(LINK_ANCHOR *anchor)
{
LINK_ELEMENT *elem = anchor->anchor.next;
if (elem) {
anchor->anchor.next = elem->next;
}
return elem;
}
#endif
#if 0 /* unused */
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;
}
#endif
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 /* unused */
/*
* 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(rb_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(rb_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(rb_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(rb_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(rb_iseq_t *iseq, NODE *node,
VALUE name, VALUE parent, VALUE type)
{
VALUE ret;
debugs("[new_child_iseq]> ---------------------------------------\n");
ret = rb_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(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
{
/* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
if (CPDEBUG > 5)
dump_disasm_list(FIRST_ELEMENT(anchor));
debugs("[compile step 3.1 (iseq_optimize)]\n");
iseq_optimize(iseq, anchor);
if (CPDEBUG > 5)
dump_disasm_list(FIRST_ELEMENT(anchor));
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));
}
if (iseq->compile_data->option->stack_caching) {
debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
iseq_set_sequence_stackcaching(iseq, anchor);
if (CPDEBUG > 5)
dump_disasm_list(FIRST_ELEMENT(anchor));
}
debugs("[compile step 4.1 (iseq_set_sequence)]\n");
iseq_set_sequence(iseq, anchor);
if (CPDEBUG > 5)
dump_disasm_list(FIRST_ELEMENT(anchor));
debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
iseq_set_exception_table(iseq);
debugs("[compile step 4.3 (set_optargs_table)] \n");
iseq_set_optargs_table(iseq);
debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
iseq_translate_threaded_code(iseq);
if (CPDEBUG > 1) {
VALUE str = ruby_iseq_disasm(iseq->self);
printf("%s\n", StringValueCStr(str));
fflush(stdout);
}
debugs("[compile step: finish]\n");
return 0;
}
static int
iseq_set_exception_local_table(rb_iseq_t *iseq)
{
static ID id_dollar_bang;
if (!id_dollar_bang) {
id_dollar_bang = rb_intern("#$!");
}
iseq->local_table = (ID *)ALLOC_N(ID *, 1);
iseq->local_table_size = iseq->local_size = 1;
iseq->local_table[0] = id_dollar_bang;
return COMPILE_OK;
}
static int
get_dyna_var_idx_at_raw(rb_iseq_t *iseq, ID id)
{
int i;
for (i = 0; i < iseq->local_table_size; i++) {
if (iseq->local_table[i] == id) {
return i;
}
}
return -1;
}
static int
get_local_var_idx(rb_iseq_t *iseq, ID id)
{
int idx = get_dyna_var_idx_at_raw(iseq->local_iseq, id);
if (idx < 0) {
rb_bug("get_local_var_idx: %d", idx);
}
return idx;
}
static int
get_dyna_var_idx(rb_iseq_t *iseq, ID id, int *level, int *ls)
{
int lv = 0, idx = -1;
while (iseq) {
idx = get_dyna_var_idx_at_raw(iseq, id);
if (idx >= 0) {
break;
}
iseq = iseq->parent_iseq;
lv++;
}
if (idx < 0) {
rb_bug("get_dyna_var_idx: -1");
}
*level = lv;
*ls = iseq->local_size;
return idx;
}
static int
iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
{
debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
if (node_args) {
NODE *node_aux = node_args->nd_next;
NODE *node_opt = node_args->nd_opt;
ID rest_id = 0;
int last_comma = 0;
ID block_id = 0;
NODE *node_init = 0;
if (nd_type(node_args) != NODE_ARGS) {
rb_bug("iseq_set_arguments: NODE_ARGS is expected, but %s",
ruby_node_name(nd_type(node_args)));
}
/*
* new argument infromation:
* NODE_ARGS [m: int, o: NODE_OPT_ARG, ->]
* NODE_ARGS_AUX [r: ID, b: ID, ->]
* NODE_ARGS_AUX [Pst: id, Plen: int, init: NODE*]
* optarg information:
* NODE_OPT_ARGS [idx, expr, next ->]
* init arg:
* NODE_AND(m_init, p_init)
* if "r" is 1, it's means "{|x,|}" type block parameter.
*/
iseq->argc = node_args->nd_frml;
debugs(" - argc: %d\n", iseq->argc);
if (node_aux) {
rest_id = node_aux->nd_rest;
if (rest_id == 1) {
last_comma = 1;
rest_id = 0;
}
block_id = (ID)node_aux->nd_body;
node_aux = node_aux->nd_next;
if (node_aux) {
ID post_start_id = node_aux->nd_pid;
iseq->arg_post_start = get_dyna_var_idx_at_raw(iseq, post_start_id);
iseq->arg_post_len = node_aux->nd_plen;
node_init = node_aux->nd_next;
}
}
if (node_opt) {
NODE *node = node_opt;
LABEL *label;
VALUE labels = rb_ary_new();
int i = 0, j;
while (node) {
label = NEW_LABEL(nd_line(node));
rb_ary_push(labels, (VALUE)label | 1);
ADD_LABEL(optargs, label);
COMPILE_POPED(optargs, "optarg", node->nd_body);
node = node->nd_next;
i += 1;
}
/* last label */
label = NEW_LABEL(nd_line(node_args));
rb_ary_push(labels, (VALUE)label | 1);
ADD_LABEL(optargs, label);
i += 1;
iseq->arg_opts = i;
iseq->arg_opt_table = ALLOC_N(VALUE, i);
MEMCPY(iseq->arg_opt_table, RARRAY_PTR(labels), VALUE, i);
for (j = 0; j < i; j++) {
iseq->arg_opt_table[j] &= ~1;
}
}
else {
iseq->arg_opts = 0;
}
if (node_init) {
if (node_init->nd_1st) { /* m_init */
COMPILE_POPED(optargs, "init arguments (m)", node_init->nd_1st);
}
if (node_init->nd_2nd) { /* p_init */
COMPILE_POPED(optargs, "init arguments (p)", node_init->nd_2nd);
}
}
if (rest_id) {
iseq->arg_rest = get_dyna_var_idx_at_raw(iseq, rest_id);
if (iseq->arg_rest == -1) {
rb_bug("arg_rest: -1");
}
if (iseq->arg_post_start == 0) {
iseq->arg_post_start = iseq->arg_rest + 1;
}
}
if (block_id) {
iseq->arg_block = get_dyna_var_idx_at_raw(iseq, block_id);
}
if (iseq->arg_opts != 0 || iseq->arg_post_len != 0 ||
iseq->arg_rest != -1 || iseq->arg_block != -1) {
iseq->arg_simple = 0;
/* set arg_size: size of arguments */
if (iseq->arg_block != -1) {
iseq->arg_size = iseq->arg_block + 1;
}
else if (iseq->arg_post_len) {
iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
}
else if (iseq->arg_rest != -1) {
iseq->arg_size = iseq->arg_rest + 1;
}
else if (iseq->arg_opts) {
iseq->arg_size = iseq->argc + iseq->arg_opts - 1;
}
else {
iseq->arg_size = iseq->argc;
}
}
else {
iseq->arg_simple = 1;
iseq->arg_size = iseq->argc;
}
if (iseq->type == ISEQ_TYPE_BLOCK) {
if (iseq->arg_opts == 0 && iseq->arg_post_len == 0 && iseq->arg_rest == -1) {
if (iseq->argc == 1 && last_comma == 0) {
/* {|a|} */
iseq->arg_simple |= 0x02;
}
}
}
}
else {
iseq->arg_simple = 1;
}
return COMPILE_OK;
}
static int
iseq_set_local_table(rb_iseq_t *iseq, ID *tbl)
{
int size;
if (tbl) {
size = *tbl;
tbl++;
}
else {
size = 0;
}
if (size > 0) {
iseq->local_table = (ID *)ALLOC_N(ID *, size);
MEMCPY(iseq->local_table, tbl, ID *, size);
}
iseq->local_size = iseq->local_table_size = size;
if (iseq->type == ISEQ_TYPE_METHOD ||
iseq->type == ISEQ_TYPE_CLASS ||
iseq->type == ISEQ_TYPE_TOP) {
iseq->local_size += 1 /* svar */;
}
debugs("iseq_set_local_table: %d, %d\n", iseq->local_size, iseq->local_table_size);
return COMPILE_OK;
}
/**
ruby insn object array -> raw instruction sequence
*/
static int
iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
{
LABEL *lobj;
INSN *iobj;
struct iseq_insn_info_entry *insn_info_table;
LINK_ELEMENT *list;
VALUE *generated_iseq;
int k, pos, sp, stack_max = 0, line = 0;
/* 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);
line = iobj->line_no;
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_compile_error(RSTRING_PTR(iseq->filename), line,
"error: set_sequence");
break;
}
list = list->next;
}
/* make instruction sequence */
generated_iseq = ALLOC_N(VALUE, pos);
insn_info_table = ALLOC_N(struct iseq_insn_info_entry, k);
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_compile_error(RSTRING_PTR(iseq->filename), iobj->line_no,
"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_compile_error(RSTRING_PTR(iseq->filename), iobj->line_no,
"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_compile_error(RSTRING_PTR(iseq->filename), iobj->line_no,
"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];
rb_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 */
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_compile_error(RSTRING_PTR(iseq->filename), iobj->line_no,
"unknown operand type: %c", type);
return 0;
}
}
insn_info_table[k].line_no = iobj->line_no;
insn_info_table[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;
}
#if 0
/* this check need dead code elimination */
if (sp != 0) {
rb_bug("SP is not 0 on %s (%d)\n", RSTRING_PTR(iseq->name), sp);
}
#endif
iseq->iseq = (void *)generated_iseq;
iseq->iseq_size = pos;
iseq->insn_info_table = insn_info_table;
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
iseq_set_exception_table(rb_iseq_t *iseq)
{
VALUE *tptr, *ptr;
int tlen, i;
struct iseq_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 iseq_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
iseq_set_optargs_table(rb_iseq_t *iseq)
{
int i;
if (iseq->arg_opts != 0) {
for (i = 0; i < iseq->arg_opts; i++) {
iseq->arg_opt_table[i] =
label_get_position((LABEL *)iseq->arg_opt_table[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(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
{
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) {
/*
* jump LABEL
* LABEL:
* =>
* LABEL:
*/
REMOVE_ELEM(&iobj->link);
}
else if (iobj != diobj && diobj->insn_id == BIN(jump)) {
if (OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {
OPERAND_AT(iobj, 0) = OPERAND_AT(diobj, 0);
goto again;
}
}
else if (diobj->insn_id == BIN(leave)) {
/*
* jump LABEL
* ...
* LABEL:
* leave
* =>
* leave
* ...
* LABEL:
* 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);
iobj = 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 (do_tailcallopt && iobj->insn_id == BIN(leave)) {
/*
* send ...
* leave
* =>
* send ..., ... | VM_CALL_TAILCALL_BIT, ...
* leave # unreachable
*/
INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
if (piobj->insn_id == BIN(send) &&
piobj->operands[2] == 0 /* block */
) {
piobj->operands[3] = INT2FIX(FIX2INT(piobj->operands[3]) | VM_CALL_TAILCALL_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(rb_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 == idGT) {
insn_set_specialized_instruction(iobj, BIN(opt_gt));
}
else if (mid == idGE) {
insn_set_specialized_instruction(iobj, BIN(opt_ge));
}
else if (mid == idLTLT) {
insn_set_specialized_instruction(iobj, BIN(opt_ltlt));
}
else if (mid == idAREF) {
insn_set_specialized_instruction(iobj, BIN(opt_aref));
}
}
}
if (argc > 0) {
if (mid == idSend || mid == id__send ||
mid == idSendBang ||
mid == id__send__ ) {
OPERAND_AT(iobj, 3) |= INT2FIX(VM_CALL_SEND_BIT);
}
if (mid == idSendBang) {
OPERAND_AT(iobj, 3) |= INT2FIX(VM_CALL_SEND_BANG_BIT);
}
}
}
return COMPILE_OK;
}
static int
iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
{
LINK_ELEMENT *list;
const int do_peepholeopt = iseq->compile_data->option->peephole_optimization;
const int do_tailcallopt = iseq->compile_data->option->tailcall_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_peepholeopt) {
iseq_peephole_optimize(iseq, list, do_tailcallopt);
}
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(rb_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(rb_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(rb_iseq_t *iseq, 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_compile_error(RSTRING_PTR(iseq->filename), iobj->lineno,
"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
iseq_set_sequence_stackcaching(rb_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_compile_error(RSTRING_PTR(iseq->filename), iobj->line_no,
"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(iseq, 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(rb_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(rb_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", ruby_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", ruby_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_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
VALUE opt_p, int poped)
{
NODE *node = node_root;
int len = node->nd_alen, line = nd_line(node), i=0;
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
if (nd_type(node) != NODE_ZARRAY) {
while (node) {
if (nd_type(node) != NODE_ARRAY) {
rb_bug("compile_array: This node is not NODE_ARRAY, but %s",
ruby_node_name(nd_type(node)));
}
i++;
if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
opt_p = Qfalse;
}
COMPILE_(anchor, "array element", node->nd_head, poped);
node = node->nd_next;
}
}
if (len != i) {
if (0) {
rb_bug("node error: compile_array (%d: %d-%d)",
(int)nd_line(node_root), len, i);
}
len = i;
}
if (opt_p == Qtrue) {
if (!poped) {
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 {
if (!poped) {
ADD_INSN1(anchor, line, newarray, INT2FIX(len));
}
APPEND_LIST(ret, anchor);
}
return len;
}
static VALUE
compile_array(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, VALUE opt_p)
{
return compile_array_(iseq, ret, node_root, opt_p, 0);
}
static VALUE
case_when_optimizable_literal(NODE * node)
{
switch (nd_type(node)) {
case NODE_LIT: {
VALUE v = node->nd_lit;
if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) {
return v;
}
break;
}
case NODE_STR:
return node->nd_lit;
}
return Qfalse;
}
static VALUE
when_vals(rb_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
compile_massign_lhs(rb_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: {
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
COMPILE_POPED(anchor, "nest masgn lhs", node);
REMOVE_ELEM(FIRST_ELEMENT(anchor));
ADD_SEQ(ret, anchor);
break;
}
default: {
DECL_ANCHOR(anchor);
INIT_ANCHOR(anchor);
COMPILE_POPED(anchor, "masgn lhs", node);
REMOVE_ELEM(FIRST_ELEMENT(anchor));
ADD_SEQ(ret, anchor);
}
}
return COMPILE_OK;
}
static void
compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *lhsn)
{
if (lhsn) {
compile_massign_opt_lhs(iseq, ret, lhsn->nd_next);
compile_massign_lhs(iseq, ret, lhsn->nd_head);
}
}
static int
compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
NODE *rhsn, NODE *orig_lhsn)
{
int memsize = 64;
int memindex = 0;
VALUE *mem = ALLOCA_N(VALUE, memsize);
int llen = 0, rlen = 0;
int i;
NODE *lhsn = orig_lhsn;
#define MEMORY(v) { \
int i; \
if (memindex == memsize) return 0; \
for (i=0; i<memindex; i++) { \
if (mem[i] == (v)) return 0; \
} \
mem[memindex++] = (v); \
}
if (rhsn == 0 || nd_type(rhsn) != NODE_ARRAY) {
return 0;
}
while (lhsn) {
NODE *ln = lhsn->nd_head;
switch (nd_type(ln)) {
case NODE_LASGN:
MEMORY(ln->nd_vid);
break;
case NODE_DASGN:
case NODE_DASGN_CURR:
case NODE_IASGN:
case NODE_IASGN2:
case NODE_CVASGN:
MEMORY(ln->nd_vid);
break;
default:
return 0;
}
lhsn = lhsn->nd_next;
llen++;
}
while (rhsn) {
if (llen <= rlen) {
COMPILE_POPED(ret, "masgn val (poped)", rhsn->nd_head);
}
else {
COMPILE(ret, "masgn val", rhsn->nd_head);
}
rhsn = rhsn->nd_next;
rlen++;
}
if (llen > rlen) {
for (i=0; i<llen-rlen; i++) {
ADD_INSN(ret, nd_line(orig_lhsn), putnil);
}
}
compile_massign_opt_lhs(iseq, ret, orig_lhsn);
return 1;
}
static int
compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
{
NODE *rhsn = node->nd_value;
NODE *splatn = node->nd_args;
NODE *lhsn = node->nd_head;
int lhs_splat = (splatn && (VALUE)splatn != (VALUE)-1) ? 1 : 0;
if (!poped || splatn || !compile_massign_opt(iseq, ret, rhsn, lhsn)) {
int llen = 0;
DECL_ANCHOR(lhsseq);
INIT_ANCHOR(lhsseq);
while (lhsn) {
compile_massign_lhs(iseq, lhsseq, lhsn->nd_head);
llen += 1;
lhsn = lhsn->nd_next;
}
COMPILE(ret, "normal masgn rhs", rhsn);
if (!poped) {
ADD_INSN(ret, nd_line(node), dup);
}
ADD_INSN2(ret, nd_line(node), expandarray,
INT2FIX(llen), INT2FIX(lhs_splat));
ADD_SEQ(ret, lhsseq);
if (lhs_splat) {
if (nd_type(splatn) == NODE_POSTARG) {
/*a, b, *r, p1, p2 */
NODE *postn = splatn->nd_2nd;
NODE *restn = splatn->nd_1st;
int num = postn->nd_alen;
int flag = 0x02 | (((VALUE)restn == (VALUE)-1) ? 0x00 : 0x01);
ADD_INSN2(ret, nd_line(splatn), expandarray,
INT2FIX(num), INT2FIX(flag));
if ((VALUE)restn != (VALUE)-1) {
compile_massign_lhs(iseq, ret, restn);
}
while (postn) {
compile_massign_lhs(iseq, ret, postn->nd_head);
postn = postn->nd_next;
}
}
else {
/* a, b, *r */
compile_massign_lhs(iseq, ret, splatn);
}
}
}
return COMPILE_OK;
}
static int
compile_colon2(rb_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, rb_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(rb_iseq_t *iseq, LINK_ANCHOR *ret,
NODE * node, LABEL *lfinish, VALUE needstr)
{
char *estr = 0;
enum node_type type;
switch (type = 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_ARRAY:{
LABEL *lfalse = NULL;
NODE *vals = node;
do {
defined_expr(iseq, ret, vals->nd_head, lfinish, Qfalse);
if (lfalse) {
ADD_INSNL(ret, nd_line(node), branchunless, lfalse);
}
else {
LABEL *lcont = NEW_LABEL(nd_line(node));
lfalse = NEW_LABEL(nd_line(node));
ADD_INSNL(ret, nd_line(node), branchif, lcont);
ADD_LABEL(ret, lfalse);
ADD_INSN(ret, nd_line(node), putnil);
ADD_INSNL(ret, nd_line(node), jump, lfinish);
ADD_LABEL(ret, lcont);
}
} while ((vals = vals->nd_next) != NULL);
}
case NODE_STR:
case NODE_LIT:
case NODE_ZARRAY:
estr = "expression";
break;
/* variables */
case NODE_LVAR:
case NODE_DVAR:
estr = "local-variable";
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:
case NODE_ATTRASGN:{
LABEL *lfalse = NULL;
int self = Qtrue;
switch (type) {
case NODE_ATTRASGN:
if (node->nd_recv == (NODE *)1) break;
case NODE_CALL:
self = Qfalse;
break;
default:
/* through */;
}
if (node->nd_args) {
lfalse = NEW_LABEL(nd_line(node));
defined_expr(iseq, ret, node->nd_args, lfinish, Qfalse);
ADD_INSNL(ret, nd_line(node), branchunless, lfalse);
}
if (!self) {
LABEL *lcont = NEW_LABEL(nd_line(node));
defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
ADD_INSNL(ret, nd_line(node), branchif, lcont);
if (lfalse) {
ADD_LABEL(ret, lfalse);
}
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);
ADD_INSNL(ret, nd_line(node), jump, lfinish);
if (lfalse) {
ADD_LABEL(ret, lfalse);
ADD_INSN(ret, nd_line(node), putnil);
ADD_INSNL(ret, nd_line(node), jump, lfinish);
}
}
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_BACK_REF:
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 << 1 | type == NODE_BACK_REF),
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;
case NODE_OP_ASGN1:
case NODE_OP_ASGN2:
case NODE_MASGN:
case NODE_LASGN:
case NODE_DASGN:
case NODE_DASGN_CURR:
case NODE_GASGN:
case NODE_IASGN:
case NODE_CDECL:
case NODE_CVDECL:
case NODE_CVASGN:
estr = "assignment";
break;
default:{
LABEL *lstart = NEW_LABEL(nd_line(node));
LABEL *lend = NEW_LABEL(nd_line(node));
LABEL *ldefed = NEW_LABEL(nd_line(node));
VALUE ensure = NEW_CHILD_ISEQVAL(NEW_NIL(),
rb_str_concat(rb_str_new2
("defined guard in "),
iseq->name),
ISEQ_TYPE_DEFINED_GUARD);
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);
if (needstr) {
ADD_INSN1(ret, nd_line(node), putstring, rb_str_new2("expression"));
}
else {
ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
}
ADD_LABEL(ret, lend);
ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, lstart, lend, ensure, lfinish);
return 1;
} /* 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(rb_iseq_t *iseq)
{
if (iseq->parent_iseq == 0) {
return rb_sprintf("block in %s", RSTRING_PTR(iseq->name));
}
else {
int level = 1;
rb_iseq_t *ip = iseq;
while (ip->local_iseq != ip) {
ip = ip->parent_iseq;
level++;
}
return rb_sprintf("block (%d levels) in %s", level, RSTRING_PTR(ip->name));
}
}
static void
add_ensure_range(rb_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, rb_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);
INIT_ANCHOR(ensure);
while (enlp) {
DECL_ANCHOR(ensure_part);
LABEL *lstart = NEW_LABEL(0);
LABEL *lend = NEW_LABEL(0);
INIT_ANCHOR(ensure_part);
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_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, unsigned long *flag)
{
VALUE argc = INT2FIX(0);
int nsplat = 0;
DECL_ANCHOR(arg_block);
DECL_ANCHOR(args_splat);
INIT_ANCHOR(arg_block);
INIT_ANCHOR(args_splat);
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);
nsplat++;
*flag |= VM_CALL_ARGS_SPLAT_BIT;
break;
}
case NODE_ARGSCAT:
case NODE_ARGSPUSH: {
int next_is_array = (nd_type(argn->nd_head) == NODE_ARRAY);
DECL_ANCHOR(tmp);
INIT_ANCHOR(tmp);
COMPILE(tmp, "args (cat: splat)", argn->nd_body);
if (next_is_array && nsplat == 0) {
/* none */
}
else {
if (nd_type(argn) == NODE_ARGSCAT) {
ADD_INSN1(tmp, nd_line(argn), splatarray, Qfalse);
}
else {
ADD_INSN1(tmp, nd_line(argn), newarray, INT2FIX(1));
}
}
INSERT_LIST(args_splat, tmp);
nsplat++;
*flag |= VM_CALL_ARGS_SPLAT_BIT;
if (next_is_array) {
argc = INT2FIX(compile_array(iseq, args, argn->nd_head, Qfalse) + 1);
POP_ELEMENT(args);
}
else {
argn = argn->nd_head;
goto setup_argn;
}
break;
}
case NODE_ARRAY: {
argc = INT2FIX(compile_array(iseq, args, argn, Qfalse));
POP_ELEMENT(args);
break;
}
default: {
rb_bug("setup_arg: unknown node: %s\n", ruby_node_name(nd_type(argn)));
}
}
}
if (nsplat > 1) {
int i;
for (i=1; i<nsplat; i++) {
ADD_INSN(args_splat, nd_line(args), concatarray);
}
}
if (!LIST_SIZE_ZERO(args_splat)) {
ADD_SEQ(args, args_splat);
}
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(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
{
enum node_type type;
if (node == 0) {
if (!poped) {
debugs("node: NODE_NIL(implicit)\n");
ADD_INSN(ret, iseq->compile_data->last_line, putnil);
}
return COMPILE_OK;
}
iseq->compile_data->last_line = nd_line(node);
debug_node_start(node);
type = nd_type(node);
if (node->flags & NODE_NEWLINE) {
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_LINE);
}
switch (type) {
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;
INIT_ANCHOR(cond_seq);
INIT_ANCHOR(then_seq);
INIT_ANCHOR(else_seq);
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 *tempnode = node;
LABEL *endlabel, *elselabel;
DECL_ANCHOR(head);
DECL_ANCHOR(body_seq);
DECL_ANCHOR(cond_seq);
VALUE special_literals = rb_ary_new();
INIT_ANCHOR(head);
INIT_ANCHOR(body_seq);
INIT_ANCHOR(cond_seq);
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((ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_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 ||
nd_type(vals) == NODE_ARGSPUSH) {
NODE *val = vals->nd_head;
special_literals = 0;
if (nd_type(vals) == NODE_ARGSCAT ||
nd_type(vals) == NODE_ARGSPUSH) {
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_CASE: unknown node (%s)",
ruby_node_name(nd_type(vals)));
}
}
else {
rb_bug("NODE_CASE: must be NODE_ARRAY, but 0");
}
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);
INIT_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 ||
nd_type(vals) == NODE_ARGSPUSH) {
NODE *val = vals->nd_head;
if (nd_type(vals) == NODE_ARGSCAT || nd_type(vals) == NODE_ARGSPUSH) {
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 == 1) {
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_CALL_RECEIVER(ret, nd_line(node));
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); */
rb_bug("unsupported: 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->nd_body, 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));
}
else {
iseq->compile_data->current_block =
NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK);
COMPILE(ret, "iter caller", node->nd_iter);
}
ADD_LABEL(ret, retry_end_l);
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
iseq->compile_data->current_block = prevblock;
ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, retry_label, retry_end_l, 0, retry_label);
ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, 0, retry_end_l);
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((ERROR_ARGS "Can't escape from eval with break"));
}
else {
rb_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((ERROR_ARGS "Illegal break"));
}
break;
}
case NODE_NEXT:{
unsigned long level = 0;
int pop_after_throw = 0;
if (iseq->compile_data->redo_label != 0) {
/* next in while loop */
debugs("next in while\n");
pop_after_throw = 1;
goto next_by_throw;
}
else if (iseq->compile_data->end_label) {
debugs("next in block\n");
ADD_INSN (ret, nd_line(node), emptstack);
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((ERROR_ARGS "Can't escape from eval with next"));
}
else {
rb_iseq_t *ip;
next_by_throw:
ip = iseq;
while (ip) {
level = 0x8000;
if (ip->compile_data->redo_label != 0) {
/* while loop */
break;
}
else if (ip->type == ISEQ_TYPE_BLOCK) {
level |= 0x4000;
break;
}
ip = ip->parent_iseq;
}
if (ip != 0) {
COMPILE(ret, "next val", node->nd_stts);
ADD_INSN1(ret, nd_line(node), throw,
INT2FIX(level | 0x03) /* TAG_NEXT */ );
if (pop_after_throw) {
ADD_INSN(ret, nd_line(node), pop);
}
}
else {
COMPILE_ERROR((ERROR_ARGS "Illegal next"));
}
}
break;
}
case NODE_REDO:{
int pop_after_throw = 0;
if (iseq->compile_data->redo_label) {
debugs("redo in while");
#if 1
pop_after_throw = 1;
goto redo_by_throw;
#else
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);
}
#endif
}
else if (iseq->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo"));
}
else if (iseq->compile_data->start_label) {
ADD_INSN (ret, nd_line(node), emptstack);
add_ensure_iseq(ret, iseq);
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 {
rb_iseq_t *ip;
unsigned long level;
redo_by_throw:
level = 0x8000 | 0x4000;
ip = iseq;
while (ip) {
if (ip->compile_data->redo_label != 0) {
break;
}
else if (ip->type == ISEQ_TYPE_BLOCK) {
break;
}
else if (ip->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo"));
}
ip = ip->parent_iseq;
}
if (ip != 0) {
ADD_INSN1(ret, nd_line(node), throw,
INT2FIX(level | 0x05) /* TAG_REDO */ );
if (pop_after_throw) {
ADD_INSN(ret, nd_line(node), pop);
}
}
else {
COMPILE_ERROR((ERROR_ARGS "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((ERROR_ARGS "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);
if (iseq->compile_data->option->tailcall_optimization) {
ADD_INSN(ret, nd_line(node), nop);
}
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 = { 0 };
struct iseq_compile_data_ensure_node_stack enl;
struct ensure_range *erange;
INIT_ANCHOR(ensr);
er.begin = lstart;
er.end = lend;
enl.ensure_node = node->nd_ensr;
enl.prev = iseq->compile_data->ensure_node_stack; /* prev */
enl.erange = &er;
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, poped);
break;
}
case NODE_LASGN:{
ID id = node->nd_vid;
int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
COMPILE(ret, "rvalue", 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) ? 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 (idx < 0) {
rb_bug("NODE_DASGN(_CURR): unknown id (%s)", rb_id2name(node->nd_vid));
}
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:
case NODE_IASGN2:{
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:{
COMPILE(ret, "cvasgn val", node->nd_value);
if (!poped) {
ADD_INSN(ret, nd_line(node), dup);
}
ADD_INSN1(ret, nd_line(node), setclassvariable,
ID2SYM(node->nd_vid));
break;
}
case NODE_OP_ASGN1: {
DECL_ANCHOR(args);
VALUE argc;
unsigned long flag = 0;
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);
if (nd_type(node->nd_args->nd_body) != NODE_ZARRAY) {
INIT_ANCHOR(args);
argc = setup_args(iseq, args, node->nd_args->nd_body, &flag);
ADD_SEQ(ret, args);
}
else {
argc = FIX2INT(0);
}
ADD_INSN1(ret, nd_line(node), dupn, INT2FIX(FIX2INT(argc)+1));
ADD_SEND_R(ret, nd_line(node), ID2SYM(idAREF), argc, Qfalse, LONG2FIX(flag));
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);
if (flag & VM_CALL_ARGS_SPLAT_BIT) {
ADD_INSN1(ret, nd_line(node), newarray, INT2FIX(1));
ADD_INSN(ret, nd_line(node), concatarray);
ADD_SEND_R(ret, nd_line(node), ID2SYM(idASET),
argc, Qfalse, LONG2FIX(flag));
}
else {
ADD_SEND_R(ret, nd_line(node), ID2SYM(idASET),
INT2FIX(FIX2INT(argc) + 1), Qfalse, LONG2FIX(flag));
}
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));
if (flag & VM_CALL_ARGS_SPLAT_BIT) {
ADD_INSN1(ret, nd_line(node), newarray, INT2FIX(1));
ADD_INSN(ret, nd_line(node), concatarray);
ADD_SEND_R(ret, nd_line(node), ID2SYM(idASET),
argc, Qfalse, LONG2FIX(flag));
}
else {
ADD_SEND_R(ret, nd_line(node), ID2SYM(idASET),
INT2FIX(FIX2INT(argc) + 1), Qfalse, LONG2FIX(flag));
}
}
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;
unsigned long flag = 0;
VALUE parent_block = iseq->compile_data->current_block;
iseq->compile_data->current_block = Qfalse;
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
#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_CALL_RECEIVER(recv, nd_line(node));
}
/* args */
if (nd_type(node) != NODE_VCALL) {
argc = setup_args(iseq, args, node->nd_args, &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, LONG2FIX(flag));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
break;
}
case NODE_SUPER:
case NODE_ZSUPER:{
DECL_ANCHOR(args);
VALUE argc;
unsigned long flag = 0;
VALUE parent_block = iseq->compile_data->current_block;
INIT_ANCHOR(args);
iseq->compile_data->current_block = Qfalse;
if (nd_type(node) == NODE_SUPER) {
argc = setup_args(iseq, args, node->nd_args, &flag);
}
else {
/* NODE_ZSUPER */
int i;
rb_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 != -1) {
/* rest argument */
int idx = liseq->local_size - liseq->arg_rest;
ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
argc = INT2FIX(liseq->arg_rest + 1);
flag |= VM_CALL_ARGS_SPLAT_BIT;
}
if (liseq->arg_post_len) {
/* post arguments */
int post_len = liseq->arg_post_len;
int post_start = liseq->arg_post_start;
if (liseq->arg_rest != -1) {
int j;
for (j=0; j<post_len; j++) {
int idx = liseq->local_size - (post_start + j);
ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
}
ADD_INSN1(args, nd_line(node), newarray, INT2FIX(j));
ADD_INSN (args, nd_line(node), concatarray);
/* argc is setteled at above */
}
else {
int j;
for (j=0; j<post_len; j++) {
int idx = liseq->local_size - (post_start + j);
ADD_INSN1(args, nd_line(node), getlocal, INT2FIX(idx));
}
argc = INT2FIX(post_len + post_start);
}
}
}
}
/* 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, LONG2FIX(flag));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
break;
}
case NODE_ARRAY:{
compile_array_(iseq, ret, node, Qtrue, poped);
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;
INIT_ANCHOR(list);
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", ruby_node_name(type));
}
ADD_INSN1(ret, nd_line(node), newhash, size);
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
break;
}
case NODE_RETURN:{
rb_iseq_t *is = iseq;
while (is) {
if (is->type == ISEQ_TYPE_TOP || is->type == ISEQ_TYPE_CLASS) {
COMPILE_ERROR((ERROR_ARGS "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);
VALUE argc;
unsigned long flag = 0;
INIT_ANCHOR(args);
if (iseq->type == ISEQ_TYPE_TOP || iseq->type == ISEQ_TYPE_CLASS) {
COMPILE_ERROR((ERROR_ARGS "Illegal yield"));
}
if (node->nd_head) {
argc = setup_args(iseq, args, node->nd_head, &flag);
}
else {
argc = INT2FIX(0);
}
ADD_SEQ(ret, args);
ADD_INSN2(ret, nd_line(node), invokeblock, argc, LONG2FIX(flag));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
break;
}
case NODE_LVAR:{
if (!poped) {
ID id = node->nd_vid;
int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
debugs("id: %s idx: %d\n", rb_id2name(id), 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, 0, 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:{
if (!poped) {
ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(1) /* '~' */,
INT2FIX(node->nd_nth << 1));
}
break;
}
case NODE_BACK_REF:{
if (!poped) {
ADD_INSN2(ret, nd_line(node), getspecial, INT2FIX(1) /* '~' */,
INT2FIX(0x01 | (node->nd_nth << 1)));
}
break;
}
case NODE_MATCH:
case NODE_MATCH2:
case NODE_MATCH3:{
DECL_ANCHOR(recv);
DECL_ANCHOR(val);
INIT_ANCHOR(recv);
INIT_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_CALL_RECEIVER(ret, nd_line(node));
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_CALL_RECEIVER(ret, nd_line(node));
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:{
/* TODO: 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, 0, 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_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:{
COMPILE(ret, "arsgpush head", node->nd_head);
COMPILE(ret, "argspush body", node->nd_body);
ADD_INSN1(ret, nd_line(node), newarray, INT2FIX(1));
ADD_INSN(ret, nd_line(node), concatarray);
break;
}
case NODE_SPLAT:{
COMPILE(ret, "splat", node->nd_head);
ADD_INSN1(ret, nd_line(node), splatarray, Qfalse);
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
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;
enum node_type t;
COMPILE(ret, "alias arg1", node->u1.node);
COMPILE(ret, "alias arg2", node->u2.node);
ADD_INSN1(ret, nd_line(node), alias, Qfalse);
if (!poped) {
ADD_INSN(ret, nd_line(node), putnil);
}
break;
}
case NODE_VALIAS:{
ADD_INSN1(ret, nd_line(node), putobject, ID2SYM(node->u1.id));
ADD_INSN1(ret, nd_line(node), putobject, ID2SYM(node->u2.id));
ADD_INSN1(ret, nd_line(node), alias, Qtrue);
if (!poped) {
ADD_INSN(ret, nd_line(node), putnil);
}
break;
}
case NODE_UNDEF:{
COMPILE(ret, "undef arg", node->u2.node);
ADD_INSN(ret, nd_line(node), undef);
if (!poped) {
ADD_INSN(ret, nd_line(node), putnil);
}
break;
}
case NODE_CLASS:{
VALUE iseqval =
NEW_CHILD_ISEQVAL(
node->nd_body,
rb_sprintf("<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,
rb_sprintf("<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);
INIT_ANCHOR(pref);
INIT_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, 0, 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_CALL_RECEIVER(ret, nd_line(node));
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, 0, 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_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));
VALUE key = rb_sprintf("flipflag/%s-%p-%d",
RSTRING_PTR(iseq->name), iseq,
iseq->compile_data->flip_cnt++);
iseq_add_mark_object_compile_time(iseq, key);
ADD_INSN2(ret, nd_line(node), getspecial, key, 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, key);
ADD_INSNL(ret, nd_line(node), jump, lfin);
}
else {
ADD_INSN1(ret, nd_line(node), setspecial, key);
}
/* *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, key);
ADD_LABEL(ret, ltrue);
ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
ADD_LABEL(ret, lfin);
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 {
rb_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->nd_body, 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;
}
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);
unsigned long flag = 0;
VALUE argc;
INIT_ANCHOR(recv);
INIT_ANCHOR(args);
argc = setup_args(iseq, args, node->nd_args, &flag);
if (node->nd_recv == (NODE *) 1) {
flag |= VM_CALL_FCALL_BIT;
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);
if (flag & VM_CALL_ARGS_BLOCKARG_BIT) {
ADD_INSN1(ret, nd_line(node), topn, INT2FIX(1));
ADD_INSN1(ret, nd_line(node), setn, INT2FIX(FIX2INT(argc) + 3));
ADD_INSN (ret, nd_line(node), pop);
}
else {
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, LONG2FIX(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;
}
case NODE_LAMBDA:{
/* compile same as lambda{...} */
VALUE block = NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK);
VALUE argc = INT2FIX(0);
ADD_CALL_RECEIVER(ret, nd_line(node));
ADD_CALL_WITH_BLOCK(ret, nd_line(node), ID2SYM(idLambda), argc, block);
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
}
break;
}
default:
rb_bug("iseq_compile_each: unknown node: %s", ruby_node_name(type));
return Qnil;
}
debug_node_end();
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);
str = rb_sprintf("%-16s", insn_name(iobj->insn_id));
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 */
{
rb_iseq_t *iseq = (rb_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_raise(rb_eSyntaxError, "unknown operand type: %c", type);
}
}
if (types[j + 1]) {
rb_str_cat2(str, ", ");
}
}
}
return str;
}
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 */
rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
}
link = link->next;
}
printf("---------------------\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(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
{
LABEL *label = 0;
st_data_t tmp;
obj = rb_convert_type(obj, T_SYMBOL, "Symbol", "to_sym");
if (st_lookup(labels_table, obj, &tmp) == 0) {
label = NEW_LABEL(0);
st_insert(labels_table, obj, (st_data_t)label);
}
else {
label = (LABEL *)tmp;
}
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_raise(rb_eSyntaxError, "invalid exception symbol: %s",
RSTRING_PTR(rb_inspect(sym)));
return 0;
}
static int
iseq_build_exception(rb_iseq_t *iseq, struct st_table *labels_table,
VALUE exception)
{
int i;
for (i=0; i<RARRAY_LEN(exception); i++) {
VALUE v, type, *ptr, eiseqval;
LABEL *lstart, *lend, *lcont;
int sp;
RB_GC_GUARD(v) = rb_convert_type(RARRAY_PTR(exception)[i], T_ARRAY,
"Array", "to_ary");
if (RARRAY_LEN(v) != 6) {
rb_raise(rb_eSyntaxError, "wrong exception entry");
}
ptr = RARRAY_PTR(v);
type = get_exception_sym2type(ptr[0]);
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(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
VALUE body, 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;
VALUE insn;
insn = (argc < 0) ? Qnil : RARRAY_PTR(obj)[0];
if (st_lookup(insn_table, insn, &insn_id) == 0) {
/* TODO: exception */
RB_GC_GUARD(insn) = rb_inspect(insn);
rb_compile_error(RSTRING_PTR(iseq->filename), line_no,
"unknown instruction: %s", RSTRING_PTR(insn));
}
if (argc != insn_len(insn_id)-1) {
rb_compile_error(RSTRING_PTR(iseq->filename), line_no,
"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;
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) == rb_cISeq) {
argv[j] = op;
}
else {
rb_raise(rb_eSyntaxError, "ISEQ is required");
}
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_raise(rb_eSyntaxError, "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;
}
#define CHECK_ARRAY(v) rb_convert_type(v, T_ARRAY, "Array", "to_ary")
#define CHECK_STRING(v) rb_convert_type(v, T_STRING, "String", "to_str")
#define CHECK_SYMBOL(v) rb_convert_type(v, T_SYMBOL, "Symbol", "to_sym")
#define CHECK_INTEGER(v) (NUM2LONG(v), v)
VALUE
iseq_build_from_ary(rb_iseq_t *iseq, 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);
INIT_ANCHOR(anchor);
if (iseq->type == ISEQ_TYPE_METHOD ||
iseq->type == ISEQ_TYPE_TOP ||
iseq->type == ISEQ_TYPE_CLASS) {
opt = 1;
}
iseq->local_table_size = opt + RARRAY_LEN(locals);
iseq->local_table = tbl = (ID *)ALLOC_N(ID *, iseq->local_table_size);
iseq->local_size = opt + iseq->local_table_size;
for (i=0; i<RARRAY_LEN(locals); i++) {
VALUE lv = RARRAY_PTR(locals)[i];
tbl[i] = FIXNUM_P(lv) ? FIX2INT(lv) : SYM2ID(CHECK_SYMBOL(lv));
}
/* args */
if (FIXNUM_P(args)) {
iseq->arg_size = iseq->argc = FIX2INT(args);
iseq->arg_simple = 1;
}
else {
int i = 0;
VALUE argc = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_opts = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_opt_labels = CHECK_ARRAY(rb_ary_entry(args, i++));
VALUE arg_post_len = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_post_start = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_rest = CHECK_INTEGER(rb_ary_entry(args, i++));
VALUE arg_block = CHECK_INTEGER(rb_ary_entry(args, i++));
iseq->argc = FIX2INT(argc);
iseq->arg_opts = FIX2INT(arg_opts);
iseq->arg_rest = FIX2INT(arg_rest);
iseq->arg_post_len = FIX2INT(arg_post_len);
iseq->arg_post_start = FIX2INT(arg_post_start);
iseq->arg_block = FIX2INT(arg_block);
iseq->arg_opt_table = (VALUE *)ALLOC_N(VALUE, iseq->arg_opts);
if (iseq->arg_block != -1) {
iseq->arg_size = iseq->arg_block + 1;
}
else if (iseq->arg_post_len) {
iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
}
else if (iseq->arg_rest != -1) {
iseq->arg_size = iseq->arg_rest + 1;
}
else {
iseq->arg_size = iseq->argc + iseq->arg_opts;
}
for (i=0; i<RARRAY_LEN(arg_opt_labels); i++) {
iseq->arg_opt_table[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, labels_table);
return iseq->self;
}