/compile.c
C | 5855 lines | 4882 code | 630 blank | 343 comment | 963 complexity | 237a4727ba634e6020f258fc22d8b619 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD
Large files files are truncated, but you can click here to view the full file
- /**********************************************************************
- compile.c - ruby node tree -> VM instruction sequence
- $Author$
- created at: 04/01/01 03:42:15 JST
- Copyright (C) 2004-2007 Koichi Sasada
- **********************************************************************/
- #include "ruby/ruby.h"
- #include "internal.h"
- #include <math.h>
- #define USE_INSN_STACK_INCREASE 1
- #include "vm_core.h"
- #include "iseq.h"
- #include "insns.inc"
- #include "insns_info.inc"
- #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
- #define FIXNUM_INC(n, i) ((n)+(INT2FIX(i)&~FIXNUM_FLAG))
- #define FIXNUM_OR(n, i) ((n)|INT2FIX(i))
- typedef struct iseq_link_element {
- enum {
- ISEQ_ELEMENT_NONE,
- ISEQ_ELEMENT_LABEL,
- ISEQ_ELEMENT_INSN,
- ISEQ_ELEMENT_ADJUST
- } 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;
- enum ruby_vminsn_type insn_id;
- unsigned int line_no;
- int operand_size;
- int sc_state;
- VALUE *operands;
- } INSN;
- typedef struct iseq_adjust_data {
- LINK_ELEMENT link;
- LABEL *label;
- int line_no;
- } ADJUST;
- 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;
- };
- /**
- * debug function(macro) interface depend on CPDEBUG
- * if it is less than 0, runtime option is in effect.
- *
- * debug level:
- * 0: no debug output
- * 1: show node type
- * 2: show node important parameters
- * ...
- * 5: show other parameters
- * 10: show every AST array
- */
- #ifndef CPDEBUG
- #define CPDEBUG 0
- #endif
- #if CPDEBUG >= 0
- #define compile_debug CPDEBUG
- #else
- #define compile_debug iseq->compile_data->option->debug_level
- #endif
- #if CPDEBUG
- #define compile_debug_print_indent(level) \
- ruby_debug_print_indent((level), compile_debug, gl_node_level * 2)
- #define debugp(header, value) (void) \
- (compile_debug_print_indent(1) && \
- ruby_debug_print_value(1, compile_debug, (header), (value)))
- #define debugi(header, id) (void) \
- (compile_debug_print_indent(1) && \
- ruby_debug_print_id(1, compile_debug, (header), (id)))
- #define debugp_param(header, value) (void) \
- (compile_debug_print_indent(1) && \
- ruby_debug_print_value(1, compile_debug, (header), (value)))
- #define debugp_verbose(header, value) (void) \
- (compile_debug_print_indent(2) && \
- ruby_debug_print_value(2, compile_debug, (header), (value)))
- #define debugp_verbose_node(header, value) (void) \
- (compile_debug_print_indent(10) && \
- ruby_debug_print_value(10, compile_debug, (header), (value)))
- #define debug_node_start(node) ((void) \
- (compile_debug_print_indent(1) && \
- (ruby_debug_print_node(1, CPDEBUG, "", (NODE *)(node)), gl_node_level)), \
- gl_node_level++)
- #define debug_node_end() gl_node_level --
- #else
- static inline ID
- r_id(ID id)
- {
- return id;
- }
- static inline VALUE
- r_value(VALUE value)
- {
- return value;
- }
- #define debugi(header, id) r_id(id)
- #define debugp(header, value) r_value(value)
- #define debugp_verbose(header, value) r_value(value)
- #define debugp_verbose_node(header, value) r_value(value)
- #define debugp_param(header, value) r_value(value)
- #define debug_node_start(node) ((void)0)
- #define debug_node_end() ((void)0)
- #endif
- #if CPDEBUG > 1 || CPDEBUG < 0
- #define debugs if (compile_debug_print_indent(1)) ruby_debug_printf
- #define debug_compile(msg, v) ((void)(compile_debug_print_indent(1) && fputs((msg), stderr)), (v))
- #else
- #define debugs if(0)printf
- #define debug_compile(msg, v) (v)
- #endif
- /* create new label */
- #define NEW_LABEL(l) new_label_body(iseq, (l))
- #define iseq_path(iseq) \
- (((rb_iseq_t*)DATA_PTR(iseq))->location.path)
- #define iseq_absolute_path(iseq) \
- (((rb_iseq_t*)DATA_PTR(iseq))->location.absolute_path)
- #define NEW_ISEQVAL(node, name, type, line_no) \
- new_child_iseq(iseq, (node), (name), 0, (type), (line_no))
- #define NEW_CHILD_ISEQVAL(node, name, type, line_no) \
- new_child_iseq(iseq, (node), (name), iseq->self, (type), (line_no))
- /* add instructions */
- #define ADD_SEQ(seq1, seq2) \
- APPEND_LIST((seq1), (seq2))
- /* add an instruction */
- #define ADD_INSN(seq, line, insn) \
- ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
- /* add an instruction with some operands (1, 2, 3, 5) */
- #define ADD_INSN1(seq, line, insn, op1) \
- ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
- /* add an instruction with label operand (alias of ADD_INSN1) */
- #define ADD_INSNL(seq, line, insn, label) ADD_INSN1(seq, line, insn, label)
- #define ADD_INSN2(seq, line, insn, op1, op2) \
- ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
- #define ADD_INSN3(seq, line, insn, op1, op2, op3) \
- ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_body(iseq, (line), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
- /* Specific Insn factory */
- #define ADD_SEND(seq, line, id, argc) \
- ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(0))
- #define ADD_CALL_RECEIVER(seq, line) \
- ADD_INSN((seq), (line), putself)
- #define ADD_CALL(seq, line, id, argc) \
- ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(VM_CALL_FCALL))
- #define ADD_CALL_WITH_BLOCK(seq, line, id, argc, block) \
- ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL))
- #define ADD_SEND_R(seq, line, id, argc, block, flag) \
- ADD_ELEM((seq), (LINK_ELEMENT *) \
- new_insn_send(iseq, (line), \
- (VALUE)(id), (VALUE)(argc), (VALUE)(block), (VALUE)(flag)))
- #define ADD_TRACE(seq, line, event) \
- do { \
- if ((event) == RUBY_EVENT_LINE && iseq->coverage && \
- (line) != iseq->compile_data->last_coverable_line) { \
- RARRAY_PTR(iseq->coverage)[(line) - 1] = INT2FIX(0); \
- iseq->compile_data->last_coverable_line = (line); \
- ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
- } \
- if (iseq->compile_data->option->trace_instruction) { \
- ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
- } \
- } while (0)
- /* add label */
- #define ADD_LABEL(seq, label) \
- ADD_ELEM((seq), (LINK_ELEMENT *) (label))
- #define APPEND_LABEL(seq, before, label) \
- APPEND_ELEM((seq), (before), (LINK_ELEMENT *) (label))
- #define ADD_ADJUST(seq, line, label) \
- ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (line)))
- #define ADD_ADJUST_RESTORE(seq, label) \
- ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
- #define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) \
- (rb_ary_push(iseq->compile_data->catch_table_ary, \
- rb_ary_new3(5, (type), \
- (VALUE)(ls) | 1, (VALUE)(le) | 1, \
- (VALUE)(iseqv), (VALUE)(lc) | 1)))
- /* compile node */
- #define COMPILE(anchor, desc, node) \
- (debug_compile("== " desc "\n", \
- iseq_compile_each(iseq, (anchor), (node), 0)))
- /* compile node, this node's value will be popped */
- #define COMPILE_POPED(anchor, desc, node) \
- (debug_compile("== " desc "\n", \
- iseq_compile_each(iseq, (anchor), (node), 1)))
- /* compile node, which is popped when 'poped' is true */
- #define COMPILE_(anchor, desc, node, poped) \
- (debug_compile("== " desc "\n", \
- iseq_compile_each(iseq, (anchor), (node), (poped))))
- #define OPERAND_AT(insn, idx) \
- (((INSN*)(insn))->operands[(idx)])
- #define INSN_OF(insn) \
- (((INSN*)(insn))->insn_id)
- /* error */
- #define COMPILE_ERROR(strs) \
- { \
- VALUE tmp = GET_THREAD()->errinfo; \
- if (compile_debug) rb_compile_bug strs; \
- GET_THREAD()->errinfo = iseq->compile_data->err_info; \
- rb_compile_error strs; \
- iseq->compile_data->err_info = GET_THREAD()->errinfo; \
- GET_THREAD()->errinfo = tmp; \
- ret = 0; \
- break; \
- }
- #define ERROR_ARGS ruby_sourcefile, nd_line(node),
- #define COMPILE_OK 1
- #define COMPILE_NG 0
- /* leave name uninitialized so that compiler warn if INIT_ANCHOR is
- * missing */
- #define DECL_ANCHOR(name) \
- LINK_ANCHOR *name, name##_body__ = {{0,},}
- #define INIT_ANCHOR(name) \
- (name##_body__.last = &name##_body__.anchor, name = &name##_body__)
- #define hide_obj(obj) do {OBJ_FREEZE(obj); RBASIC(obj)->klass = 0;} while (0)
- #include "optinsn.inc"
- #if OPT_INSTRUCTIONS_UNIFICATION
- #include "optunifs.inc"
- #endif
- /* for debug */
- #if CPDEBUG < 0
- #define ISEQ_ARG iseq,
- #define ISEQ_ARG_DECLARE rb_iseq_t *iseq,
- #else
- #define ISEQ_ARG
- #define ISEQ_ARG_DECLARE
- #endif
- #if CPDEBUG
- #define gl_node_level iseq->compile_data->node_level
- #if 0
- static void debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor);
- #endif
- #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 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, long line);
- static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, 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);
- /*
- * To make Array to LinkedList, use link_anchor
- */
- static void
- verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *anchor)
- {
- #if CPDEBUG
- int flag = 0;
- LINK_ELEMENT *list, *plist;
- if (!compile_debug) return;
- 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
- }
- #if CPDEBUG < 0
- #define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
- #endif
- /*
- * elem1, elem2 => elem1, elem2, elem
- */
- static void
- ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *elem)
- {
- elem->prev = anchor->last;
- anchor->last->next = elem;
- anchor->last = elem;
- verify_list("add", anchor);
- }
- /*
- * elem1, before, elem2 => elem1, before, elem, elem2
- */
- static void
- APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
- {
- elem->prev = before;
- elem->next = before->next;
- elem->next->prev = elem;
- before->next = elem;
- if (before == anchor->last) anchor->last = elem;
- verify_list("add", anchor);
- }
- #if CPDEBUG < 0
- #define ADD_ELEM(anchor, elem) ADD_ELEM(iseq, (anchor), (elem))
- #define APPEND_ELEM(anchor, before, elem) ADD_ELEM(iseq, (anchor), (before), (elem))
- #endif
- 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;
- }
- #define ruby_sourcefile RSTRING_PTR(iseq->location.path)
- 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;
- }
- static int
- validate_label(st_data_t name, st_data_t label, st_data_t arg)
- {
- rb_iseq_t *iseq = (rb_iseq_t *)arg;
- LABEL *lobj = (LABEL *)label;
- if (!lobj->link.next) {
- do {
- int ret;
- COMPILE_ERROR((ruby_sourcefile, lobj->position,
- "%s: undefined label", rb_id2name((ID)name)));
- if (ret) break;
- } while (0);
- }
- return ST_CONTINUE;
- }
- static void
- validate_labels(rb_iseq_t *iseq, st_table *labels_table)
- {
- st_foreach(labels_table, validate_label, (st_data_t)iseq);
- if (!NIL_P(iseq->compile_data->err_info)) {
- rb_exc_raise(iseq->compile_data->err_info);
- }
- }
- VALUE
- rb_iseq_compile_node(VALUE self, NODE *node)
- {
- DECL_ANCHOR(ret);
- rb_iseq_t *iseq;
- INIT_ANCHOR(ret);
- GetISeqPtr(self, iseq);
- if (node == 0) {
- COMPILE(ret, "nil", node);
- iseq_set_local_table(iseq, 0);
- }
- 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, start);
- ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_B_CALL);
- COMPILE(ret, "block body", node->nd_body);
- ADD_TRACE(ret, nd_line(node), RUBY_EVENT_B_RETURN);
- ADD_LABEL(ret, end);
- /* 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, FIX2INT(iseq->location.first_lineno), 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, FIX2INT(iseq->location.first_lineno), 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_MAIN:
- 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:
- iseq_set_local_table(iseq, 0);
- 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, getlocal, INT2FIX(2), INT2FIX(0));
- ADD_INSN1(ret, 0, throw, INT2FIX(0) /* continue throw */ );
- }
- else {
- ADD_INSN(ret, iseq->compile_data->last_line, leave);
- }
- #if SUPPORT_JOKE
- if (iseq->compile_data->labels_table) {
- validate_labels(iseq, iseq->compile_data->labels_table);
- }
- #endif
- return iseq_setup(iseq, ret);
- }
- int
- rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
- {
- #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
- const void * const *table = rb_vm_get_insns_address_table();
- unsigned long 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 = (int)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));
- }
- static ADJUST *
- compile_data_alloc_adjust(rb_iseq_t *iseq)
- {
- return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
- }
- /*
- * 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;
- }
- }
- #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(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
- {
- LINK_ELEMENT *elem = anchor->last;
- anchor->last = anchor->last->prev;
- anchor->last->next = 0;
- verify_list("pop", anchor);
- return elem;
- }
- #if CPDEBUG < 0
- #define POP_ELEMENT(anchor) POP_ELEMENT(iseq, (anchor))
- #endif
- #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(ISEQ_ARG_DECLARE 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);
- }
- #if CPDEBUG < 0
- #define APPEND_LIST(anc1, anc2) APPEND_LIST(iseq, (anc1), (anc2))
- #endif
- /*
- * anc1: e1, e2, e3
- * anc2: e4, e5
- *#=>
- * anc1: e4, e5, e1, e2, e3
- * anc2: e4, e5 (broken)
- */
- static void
- INSERT_LIST(ISEQ_ARG_DECLARE 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 CPDEBUG < 0
- #define INSERT_LIST(anc1, anc2) INSERT_LIST(iseq, (anc1), (anc2))
- #endif
- #if 0 /* unused */
- /*
- * anc1: e1, e2, e3
- * anc2: e4, e5
- *#=>
- * anc1: e4, e5
- * anc2: e1, e2, e3
- */
- static void
- SWAP_LIST(ISEQ_ARG_DECLARE 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);
- }
- #if CPDEBUG < 0
- #define SWAP_LIST(anc1, anc2) SWAP_LIST(iseq, (anc1), (anc2))
- #endif
- static LINK_ANCHOR *
- REVERSE_LIST(ISEQ_ARG_DECLARE 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;
- }
- #if CPDEBUG < 0
- #define REVERSE_LIST(anc) REVERSE_LIST(iseq, (anc))
- #endif
- #endif
- #if CPDEBUG && 0
- static void
- debug_list(ISEQ_ARG_DECLARE 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);
- }
- #if CPDEBUG < 0
- #define debug_list(anc) debug_list(iseq, (anc))
- #endif
- #endif
- static LABEL *
- new_label_body(rb_iseq_t *iseq, long line)
- {
- LABEL *labelobj = compile_data_alloc_label(iseq);
- labelobj->link.type = ISEQ_ELEMENT_LABEL;
- labelobj->link.next = 0;
- labelobj->label_no = iseq->compile_data->label_no++;
- labelobj->sc_state = 0;
- labelobj->sp = -1;
- return labelobj;
- }
- static ADJUST *
- new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
- {
- ADJUST *adjust = compile_data_alloc_adjust(iseq);
- adjust->link.type = ISEQ_ELEMENT_ADJUST;
- adjust->link.next = 0;
- adjust->label = label;
- adjust->line_no = line;
- return adjust;
- }
- 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);
- /* printf("insn_id: %d, line: %d\n", insn_id, line_no); */
- 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 rb_call_info_t *
- new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned long flag)
- {
- rb_call_info_t *ci = (rb_call_info_t *)compile_data_alloc(iseq, sizeof(rb_call_info_t));
- ci->mid = mid;
- ci->flag = flag;
- ci->orig_argc = argc;
- ci->argc = argc;
- if (block) {
- GetISeqPtr(block, ci->blockiseq);
- }
- else {
- ci->blockiseq = 0;
- if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG))) {
- ci->flag |= VM_CALL_ARGS_SKIP_SETUP;
- }
- }
- ci->vmstat = 0;
- ci->blockptr = 0;
- ci->recv = Qundef;
- ci->call = 0; /* TODO: should set default function? */
- ci->aux.index = iseq->callinfo_size++;
- return ci;
- }
- static INSN *
- new_insn_send(rb_iseq_t *iseq, int line_no, VALUE id, VALUE argc, VALUE block, VALUE flag)
- {
- VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 1);
- operands[0] = (VALUE)new_callinfo(iseq, SYM2ID(id), FIX2INT(argc), block, FIX2INT(flag));
- return new_insn_core(iseq, line_no, BIN(send), 1, operands);
- }
- static VALUE
- new_child_iseq(rb_iseq_t *iseq, NODE *node,
- VALUE name, VALUE parent, enum iseq_type type, int line_no)
- {
- VALUE ret;
- debugs("[new_child_iseq]> ---------------------------------------\n");
- ret = rb_iseq_new_with_opt(node, name,
- iseq_path(iseq->self), iseq_absolute_path(iseq->self),
- INT2FIX(line_no), 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 (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
- debugs("[compile step 3.1 (iseq_optimize)]\n");
- iseq_optimize(iseq, anchor);
- if (compile_debug > 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 (compile_debug > 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 (compile_debug > 5)
- dump_disasm_list(FIRST_ELEMENT(anchor));
- }
- debugs("[compile step 4.1 (iseq_set_sequence)]\n");
- iseq_set_sequence(iseq, anchor);
- if (compile_debug > 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");
- rb_iseq_translate_threaded_code(iseq);
- if (compile_debug > 1) {
- VALUE str = rb_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)
- {
- ID id_dollar_bang;
- CONST_ID(id_dollar_bang, "#$!");
- iseq->local_table = (ID *)ALLOC_N(ID, 1);
- iseq->local_table_size = 1;
- iseq->local_size = iseq->local_table_size + 1;
- iseq->local_table[0] = id_dollar_bang;
- return COMPILE_OK;
- }
- static int
- get_lvar_level(rb_iseq_t *iseq)
- {
- int lev = 0;
- while (iseq != iseq->local_iseq) {
- lev++;
- iseq = iseq->parent_iseq;
- }
- return lev;
- }
- 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) {
- struct rb_args_info *args = node_args->nd_ainfo;
- ID rest_id = 0;
- int last_comma = 0;
- ID block_id = 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)));
- }
- iseq->argc = (int)args->pre_args_num;
- debugs(" - argc: %d\n", iseq->argc);
- rest_id = args->rest_arg;
- if (rest_id == 1) {
- last_comma = 1;
- rest_id = 0;
- }
- block_id = args->block_arg;
- if (args->first_post_arg) {
- iseq->arg_post_start = get_dyna_var_idx_at_raw(iseq, args->first_post_arg);
- iseq->arg_post_len = args->post_args_num;
- }
- if (args->opt_args) {
- NODE *node = args->opt_args;
- LABEL *label;
- VALUE labels = rb_ary_tmp_new(1);
- 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;
- }
- rb_ary_clear(labels);
- }
- else {
- iseq->arg_opts = 0;
- }
- if (args->kw_args) {
- NODE *node = args->kw_args;
- VALUE keywords = rb_ary_tmp_new(1);
- int i = 0, j;
- iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- COMPILE(optargs, "kwarg", args->kw_rest_arg);
- while (node) {
- rb_ary_push(keywords, INT2FIX(node->nd_body->nd_vid));
- COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
- node = node->nd_next;
- i += 1;
- }
- iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
- iseq->arg_keywords = i;
- iseq->arg_keyword_table = ALLOC_N(ID, i);
- for (j = 0; j < i; j++) {
- iseq->arg_keyword_table[j] = FIX2INT(RARRAY_PTR(keywords)[j]);
- }
- ADD_INSN(optargs, nd_line(args->kw_args), pop);
- }
- else if (args->kw_rest_arg) {
- iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
- COMPILE(optargs, "kwarg", args->kw_rest_arg);
- iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
- ADD_INSN(optargs, nd_line(args->kw_rest_arg), pop);
- }
- else {
- iseq->arg_keyword = -1;
- }
- if (args->pre_init) { /* m_init */
- COMPILE_POPED(optargs, "init arguments (m)", args->pre_init);
- }
- if (args->post_init) { /* p_init */
- COMPILE_POPED(optargs, "init arguments (p)", args->post_init);
- }
- 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_keyword != -1) {
- iseq->arg_simple = 0;
- /* set arg_size: size of arguments */
- if (iseq->arg_keyword != -1) {
- iseq->arg_size = iseq->arg_keyword + 1;
- }
- else 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 = (int)*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;
- iseq->local_size += 1;
- /*
- if (lfp == dfp ) { // top, class, method
- dfp[-1]: svar
- else { // block
- dfp[-1]: cref
- }
- */
- debugs("iseq_set_local_table: %d, %d\n", iseq->local_size, iseq->local_table_size);
- return COMPILE_OK;
- }
- static int
- cdhash_cmp(VALUE val, VALUE lit)
- {
- if (val == lit) return 0;
- if (SPECIAL_CONST_P(lit)) {
- return val != lit;
- }
- if (SPECIAL_CONST_P(val) || BUILTIN_TYPE(val) != BUILTIN_TYPE(lit)) {
- return -1;
- }
- if (BUILTIN_TYPE(lit) == T_STRING) {
- return rb_str_hash_cmp(lit, val);
- }
- return !rb_eql(lit, val);
- }
- static st_index_t
- cdhash_hash(VALUE a)
- {
- if (SPECIAL_CONST_P(a)) return (st_index_t)a;
- if (RB_TYPE_P(a, T_STRING)) return rb_str_hash(a);
- {
- VALUE hval = rb_hash(a);
- return (st_index_t)FIX2LONG(hval);
- }
- }
- static const struct st_hash_type cdhash_type = {
- cdhash_cmp,
- cdhash_hash,
- };
- struct cdhash_set_label_struct {
- VALUE hash;
- int pos;
- int len;
- };
- static int
- cdhash_set_label_i(VALUE key, VALUE val, void *ptr)
- {
- struct cdhash_set_label_struct *data = (struct cdhash_set_label_struct *)ptr;
- LABEL *lobj = (LABEL *)(val & ~1);
- rb_hash_aset(data->hash, key, INT2FIX(lobj->position - (data->pos+data->len)));
- return ST_CONTINUE;
- }
- /**
- ruby insn object list -> raw instruction sequence
- */
- static int
- iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
- {
- LABEL *lobj;
- INSN *iobj;
- struct iseq_line_info_entry *line_info_table;
- unsigned int last_line = 0;
- 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;
- line = iobj->line_no;
- pos += insn_data_length(iobj);
- k++;
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- lobj = (LABEL *)list;
- lobj->position = pos;
- lobj->set = TRUE;
- break;
- }
- case ISEQ_ELEMENT_NONE:
- {
- /* ignore */
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)list;
- if (adjust->line_no != -1) {
- pos += 2 /* insn + 1 operand */;
- k++;
- }
- break;
- }
- default:
- dump_disasm_list(FIRST_ELEMENT(anchor));
- dump_disasm_list(list);
- rb_compile_error(RSTRING_PTR(iseq->location.path), line,
- "error: set_sequence");
- break;
- }
- list = list->next;
- }
- /* make instruction sequence */
- generated_iseq = ALLOC_N(VALUE, pos);
- line_info_table = ALLOC_N(struct iseq_line_info_entry, k);
- iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size);
- MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size);
- iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size);
- /* MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); */
- list = FIRST_ELEMENT(anchor);
- k = pos = sp = 0;
- while (list) {
- switch (list->type) {
- case ISEQ_ELEMENT_INSN:
- {
- int j, len, insn;
- const char *types;
- VALUE *operands;
- iobj = (INSN *)list;
- /* update sp */
- 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) {
- /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
- dump_disasm_list(list);
- rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
- "operand size miss! (%d for %d)",
- iobj->operand_size, len - 1);
- xfree(generated_iseq);
- xfree(line_info_table);
- 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) {
- rb_compile_error(RSTRING_PTR(iseq->location.path), 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:
- {
- VALUE map = operands[j];
- struct cdhash_set_label_struct data;
- data.hash = map;
- data.pos = pos;
- data.len = len;
- rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
- hide_obj(map);
- generated_iseq[pos + 1 + j] = map;
- break;
- }
- case TS_LINDEX:
- 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 */
- {
- int ic_index = FIX2INT(operands[j]);
- IC ic = &iseq->ic_entries[ic_index];
- if (UNLIKELY(ic_index >= iseq->ic_size)) {
- rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->ic_size);
- }
- generated_iseq[pos + 1 + j] = (VALUE)ic;
- break;
- }
- case TS_CALLINFO: /* call info */
- {
- rb_call_info_t *base_ci = (rb_call_info_t *)operands[j];
- rb_call_info_t *ci = &iseq->callinfo_entries[base_ci->aux.index];
- *ci = *base_ci;
- if (UNLIKELY(base_ci->aux.index >= iseq->callinfo_size)) {
- rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", base_ci->argc, iseq->callinfo_size);
- }
- generated_iseq[pos + 1 + j] = (VALUE)ci;
- break;
- }
- case TS_ID: /* ID */
- generated_iseq[pos + 1 + j] = SYM2ID(operands[j]);
- break;
- case TS_GENTRY:
- {
- struct rb_global_entry *entry =
- (struct rb_global_entry *)(operands[j] & (~1));
- generated_iseq[pos + 1 + j] = (VALUE)entry;
- }
- break;
- default:
- rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
- "unknown operand type: %c", type);
- xfree(generated_iseq);
- xfree(line_info_table);
- return 0;
- }
- }
- if (last_line != iobj->line_no) {
- line_info_table[k].line_no = last_line = iobj->line_no;
- line_info_table[k].position = pos;
- k++;
- }
- pos += len;
- break;
- }
- case ISEQ_ELEMENT_LABEL:
- {
- lobj = (LABEL *)list;
- if (lobj->sp == -1) {
- lobj->sp = sp;
- }
- else {
- sp = lobj->sp;
- }
- break;
- }
- case ISEQ_ELEMENT_ADJUST:
- {
- ADJUST *adjust = (ADJUST *)list;
- int orig_sp = sp;
- if (adjust->label) {
- sp = adjust->label->sp;
- }
- else {
- sp = 0;
- }
- if (adjust->line_no != -1) {
- if (orig_sp - sp > 0) {
- if (last_line != (unsigned int)adjust->line_no) {
- line_info_table[k].line_no = last_line = adjust->line_no;
- line_info_table[k].position = pos;
- k++;
- }
- generated_iseq[pos++] = BIN(adjuststack);
- generated_iseq[pos++] = orig_sp - sp;
- }
- else if (orig_sp - sp == 0) {
- /* jump to next insn */
- if (last_line != (unsigned int)adjust->line_no) {
- line_info_table[k].line_no = last_line = adjust->line_no;
- line_info_table[k].position = pos;
- k++;
- }
- generated_iseq[pos++] = BIN(jump);
- generated_iseq[pos++] = 0;
- }
- else {
- rb_bug("iseq_set_sequence: adjust bug");
- }
- }
- break;
- }
- default:
- /* ignore */
- break;
- }
- list = list->next;
- }
- #if 0 /* XXX */
- /* this check need dead code elimination */
- if (sp != 1) {
- 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->stack_max = stack_max;
- line_info_table = ruby_xrealloc(line_info_table, k * sizeof(struct iseq_line_info_entry));
- iseq->line_info_table = line_info_table;
- iseq->line_info_size = k;
- 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 = (int)RARRAY_LEN(iseq->compile_data->catch_table_ary);
- tptr = RARRAY_PTR(iseq->compile_data->catch_table_ary);
- iseq->catch_table = tlen ? ALLOC_N(struct iseq_catch_table_entry, tlen) : 0;
- iseq->catch_table_size = tlen;
- for (i = 0; i < tlen; i++) {
- ptr = RARRAY_PTR(tptr[i]);
- entry = &iseq->catch_table[i];
- entry->type = (enum catch_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 ||
- 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 || list->type == ISEQ_ELEMENT_ADJUST) {
- 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 || list->type == ISEQ_ELEMENT_ADJUST) {
- 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 || list->type == ISEQ_ELEMENT_ADJUST) {
- 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 to
- * 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);
- INSN *popiobj = new_insn_core(iseq, iobj->line_no,
- BIN(pop), 0, 0);
- /* replace */
- REPLACE_ELEM((LINK_ELEMENT *)iobj, (LINK_ELEMENT *)eiobj);
- INSERT_ELEM_NEXT((LINK_ELEMENT *)eiobj, (LINK_ELEMENT *)popiobj);
- iobj = popiobj;
- }
- /*
- * 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, ...
- * leave # unreachable
- */
- INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
- if (piobj->insn_id == BIN(send) || piobj->insn_id == BIN(opt_send_simple)) {
- rb_call_info_t *ci = (rb_call_info_t *)piobj->operands[0];
- if (ci->blockiseq == 0) {
- ci->flag |= VM_CALL_TAILCALL;
- }
- }
- }
- return COMPILE_OK;
- }
- static int
- insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
- {
- int old_opsize = iobj->operand_size;
- iobj->insn_id = insn_id;
- iobj->operand_size = insn_len(insn_id) - 1;
- if (iobj->operand_size > old_opsize) {
- VALUE *old_operands = iobj->operands;
- if (insn_id != BIN(opt_neq)) {
- rb_bug("insn_set_specialized_instruction: unknown insn: %d", insn_id);
- }
- iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
- iobj->operands[0] = old_operands[0];
- iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0);
- }
- return COMPILE_OK;
- }
- static int
- iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
- {
- if (iobj->insn_id == BIN(send)) {
- rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, 0);
- #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
- if (ci->blockiseq == 0 && (ci->flag & ~VM_CALL_ARGS_SKIP_SETUP) == 0) {
- switch (ci->orig_argc) {
- case 0:
- switch (ci->mid) {
- case idLength: SP_INSN(length); return COMPILE_OK;
- case idSize: SP_INSN(size); return COMPILE_OK;
- case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
- case idSucc: SP_INSN(succ); return COMPILE_OK;
- case idNot: SP_INSN(not); return COMPILE_OK;
- }
- break;
- case 1:
- switch (ci->mid) {
- case idPLUS: SP_INSN(plus); return COMPILE_OK;
- case idMINUS: SP_INSN(minus); return COMPILE_OK;
- case idMULT: SP_INSN(mult); return COMPILE_OK;
- case idDIV: SP_INSN(div); return COMPILE_OK;
- case idMOD: SP_INSN(mod); return COMPILE_OK;
- case idEq: SP_INSN(eq); return COMPILE_OK;
- case idNeq: SP_INSN(neq); return COMPILE_OK;
- case idLT: SP_INSN(lt); return COMPILE_OK;
- case idLE: SP_INSN(le); return COMPILE_OK;
- case idGT: SP_INSN(gt); return COMPILE_OK;
- case idGE: SP_INSN(ge); return COMPILE_OK;
- case idLTLT: SP_INSN(ltlt); return COMPILE_OK;
- case idAREF: SP_INSN(aref); return COMPILE_OK;
- }
- break;
- }
- }
- if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) {
- iobj->insn_id = BIN(opt_send_simple);
- }
- }
- #undef SP_INSN
- 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 c…
Large files files are truncated, but you can click here to view the full file