diff options
Diffstat (limited to 'compile.c')
| -rw-r--r-- | compile.c | 5426 |
1 files changed, 3720 insertions, 1706 deletions
@@ -17,7 +17,6 @@ #endif #include "encindex.h" -#include "gc.h" #include "id_table.h" #include "internal.h" #include "internal/array.h" @@ -25,30 +24,31 @@ #include "internal/complex.h" #include "internal/encoding.h" #include "internal/error.h" +#include "internal/gc.h" #include "internal/hash.h" +#include "internal/io.h" #include "internal/numeric.h" #include "internal/object.h" #include "internal/rational.h" #include "internal/re.h" +#include "internal/ruby_parser.h" #include "internal/symbol.h" #include "internal/thread.h" #include "internal/variable.h" #include "iseq.h" +#include "ruby/ractor.h" #include "ruby/re.h" #include "ruby/util.h" #include "vm_core.h" #include "vm_callinfo.h" #include "vm_debug.h" +#include "yjit.h" #include "builtin.h" #include "insns.inc" #include "insns_info.inc" -#undef RUBY_UNTYPED_DATA_WARNING -#define RUBY_UNTYPED_DATA_WARNING 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 { @@ -118,7 +118,7 @@ struct ensure_range { }; struct iseq_compile_data_ensure_node_stack { - const NODE *ensure_node; + const void *ensure_node; struct iseq_compile_data_ensure_node_stack *prev; struct ensure_range *erange; }; @@ -213,36 +213,43 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO}; #define NEW_CHILD_ISEQ(node, name, type, line_no) \ new_child_iseq(iseq, (node), rb_fstring(name), iseq, (type), (line_no)) +#define NEW_CHILD_ISEQ_WITH_CALLBACK(callback_func, name, type, line_no) \ + new_child_iseq_with_callback(iseq, (callback_func), (name), iseq, (type), (line_no)) + /* add instructions */ #define ADD_SEQ(seq1, seq2) \ APPEND_LIST((seq1), (seq2)) /* add an instruction */ #define ADD_INSN(seq, line_node, insn) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0)) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 0)) + +/* add an instruction with the given line number and node id */ +#define ADD_SYNTHETIC_INSN(seq, line_no, node_id, insn) \ + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line_no), (node_id), BIN(insn), 0)) /* insert an instruction before next */ -#define INSERT_BEFORE_INSN(next, line_node, insn) \ - ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0)) +#define INSERT_BEFORE_INSN(next, line_no, node_id, insn) \ + ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0)) /* insert an instruction after prev */ -#define INSERT_AFTER_INSN(prev, line_node, insn) \ - ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, (line_node), BIN(insn), 0)) +#define INSERT_AFTER_INSN(prev, line_no, node_id, insn) \ + ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) new_insn_body(iseq, line_no, node_id, BIN(insn), 0)) /* add an instruction with some operands (1, 2, 3, 5) */ #define ADD_INSN1(seq, line_node, insn, op1) \ ADD_ELEM((seq), (LINK_ELEMENT *) \ - new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1))) + new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 1, (VALUE)(op1))) /* insert an instruction with some operands (1, 2, 3, 5) before next */ -#define INSERT_BEFORE_INSN1(next, line_node, insn, op1) \ +#define INSERT_BEFORE_INSN1(next, line_no, node_id, insn, op1) \ ELEM_INSERT_PREV(&(next)->link, (LINK_ELEMENT *) \ - new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1))) + new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1))) /* insert an instruction with some operands (1, 2, 3, 5) after prev */ -#define INSERT_AFTER_INSN1(prev, line_node, insn, op1) \ +#define INSERT_AFTER_INSN1(prev, line_no, node_id, insn, op1) \ ELEM_INSERT_NEXT(&(prev)->link, (LINK_ELEMENT *) \ - new_insn_body(iseq, (line_node), BIN(insn), 1, (VALUE)(op1))) + new_insn_body(iseq, line_no, node_id, BIN(insn), 1, (VALUE)(op1))) #define LABEL_REF(label) ((label)->refcnt++) @@ -251,11 +258,11 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO}; #define ADD_INSN2(seq, line_node, insn, op1, op2) \ ADD_ELEM((seq), (LINK_ELEMENT *) \ - new_insn_body(iseq, (line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2))) + new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2))) #define ADD_INSN3(seq, line_node, insn, op1, op2, op3) \ ADD_ELEM((seq), (LINK_ELEMENT *) \ - new_insn_body(iseq, (line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3))) + new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3))) /* Specific Insn factory */ #define ADD_SEND(seq, line_node, id, argc) \ @@ -277,7 +284,7 @@ const ID rb_iseq_shared_exc_local_tbl[] = {idERROR_INFO}; ADD_SEND_R((seq), (line_node), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL) #define ADD_SEND_R(seq, line_node, id, argc, block, flag, keywords) \ - ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords))) + ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords))) #define ADD_TRACE(seq, event) \ ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event), 0)) @@ -332,10 +339,10 @@ static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NOD (debug_compile("== " desc "\n", \ iseq_compile_each(iseq, (anchor), (node), (popped)))) -#define COMPILE_RECV(anchor, desc, node) \ +#define COMPILE_RECV(anchor, desc, node, recv) \ (private_recv_p(node) ? \ (ADD_INSN(anchor, node, putself), VM_CALL_FCALL) : \ - COMPILE(anchor, desc, node->nd_recv) ? 0 : -1) + COMPILE(anchor, desc, recv) ? 0 : -1) #define OPERAND_AT(insn, idx) \ (((INSN*)(insn))->operands[(idx)]) @@ -431,12 +438,10 @@ do { \ #define NO_CHECK(sub) (void)(sub) #define BEFORE_RETURN -/* leave name uninitialized so that compiler warn if INIT_ANCHOR is - * missing */ #define DECL_ANCHOR(name) \ - LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},}} + LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},&name[0].anchor}} #define INIT_ANCHOR(name) \ - (name->last = &name->anchor) + ((name->last = &name->anchor)->next = NULL) /* re-initialize */ static inline VALUE freeze_hide_obj(VALUE obj) @@ -470,7 +475,7 @@ static void dump_disasm_list(const LINK_ELEMENT *elem); static int insn_data_length(INSN *iobj); static int calc_sp_depth(int depth, INSN *iobj); -static INSN *new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...); +static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type 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 TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event, long data); @@ -482,16 +487,16 @@ static int iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor); static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor); static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor); -static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl); +static int iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE *const node_args); static int iseq_set_exception_local_table(rb_iseq_t *iseq); static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node); -static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor); static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor); static int iseq_set_exception_table(rb_iseq_t *iseq); static int iseq_set_optargs_table(rb_iseq_t *iseq); +static int iseq_set_parameters_lvar_state(const rb_iseq_t *iseq); -static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr); +static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore); static int compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped); /* @@ -606,12 +611,25 @@ branch_coverage_valid_p(rb_iseq_t *iseq, int first_line) } static VALUE -decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type) +setup_branch(const rb_code_location_t *loc, const char *type, VALUE structure, VALUE key) { - const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node); - const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node); + const int first_lineno = loc->beg_pos.lineno, first_column = loc->beg_pos.column; + const int last_lineno = loc->end_pos.lineno, last_column = loc->end_pos.column; + VALUE branch = rb_ary_hidden_new(6); - if (!branch_coverage_valid_p(iseq, first_lineno)) return Qundef; + rb_hash_aset(structure, key, branch); + rb_ary_push(branch, ID2SYM(rb_intern(type))); + rb_ary_push(branch, INT2FIX(first_lineno)); + rb_ary_push(branch, INT2FIX(first_column)); + rb_ary_push(branch, INT2FIX(last_lineno)); + rb_ary_push(branch, INT2FIX(last_column)); + return branch; +} + +static VALUE +decl_branch_base(rb_iseq_t *iseq, VALUE key, const rb_code_location_t *loc, const char *type) +{ + if (!branch_coverage_valid_p(iseq, loc->beg_pos.lineno)) return Qundef; /* * if !structure[node] @@ -622,18 +640,11 @@ decl_branch_base(rb_iseq_t *iseq, const NODE *node, const char *type) */ VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); - VALUE key = (VALUE)node | 1; // FIXNUM for hash key VALUE branch_base = rb_hash_aref(structure, key); VALUE branches; if (NIL_P(branch_base)) { - branch_base = rb_ary_hidden_new(6); - rb_hash_aset(structure, key, branch_base); - rb_ary_push(branch_base, ID2SYM(rb_intern(type))); - rb_ary_push(branch_base, INT2FIX(first_lineno)); - rb_ary_push(branch_base, INT2FIX(first_column)); - rb_ary_push(branch_base, INT2FIX(last_lineno)); - rb_ary_push(branch_base, INT2FIX(last_column)); + branch_base = setup_branch(loc, type, structure, key); branches = rb_hash_new(); rb_obj_hide(branches); rb_ary_push(branch_base, branches); @@ -655,12 +666,9 @@ generate_dummy_line_node(int lineno, int node_id) } static void -add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *node, int branch_id, const char *type, VALUE branches) +add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const rb_code_location_t *loc, int node_id, int branch_id, const char *type, VALUE branches) { - const int first_lineno = nd_first_lineno(node), first_column = nd_first_column(node); - const int last_lineno = nd_last_lineno(node), last_column = nd_last_column(node); - - if (!branch_coverage_valid_p(iseq, first_lineno)) return; + if (!branch_coverage_valid_p(iseq, loc->beg_pos.lineno)) return; /* * if !branches[branch_id] @@ -675,13 +683,7 @@ add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *n long counter_idx; if (NIL_P(branch)) { - branch = rb_ary_hidden_new(6); - rb_hash_aset(branches, key, branch); - rb_ary_push(branch, ID2SYM(rb_intern(type))); - rb_ary_push(branch, INT2FIX(first_lineno)); - rb_ary_push(branch, INT2FIX(first_column)); - rb_ary_push(branch, INT2FIX(last_lineno)); - rb_ary_push(branch, INT2FIX(last_column)); + branch = setup_branch(loc, type, branches, key); VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1); counter_idx = RARRAY_LEN(counters); rb_ary_push(branch, LONG2FIX(counter_idx)); @@ -692,9 +694,7 @@ add_trace_branch_coverage(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *n } ADD_TRACE_WITH_DATA(seq, RUBY_EVENT_COVERAGE_BRANCH, counter_idx); - - NODE dummy_line_node = generate_dummy_line_node(last_lineno, nd_node_id(node)); - ADD_INSN(seq, &dummy_line_node, nop); + ADD_SYNTHETIC_INSN(seq, loc->end_pos.lineno, node_id, nop); } #define ISEQ_LAST_LINE(iseq) (ISEQ_COMPILE_DATA(iseq)->last_line) @@ -721,6 +721,129 @@ validate_labels(rb_iseq_t *iseq, st_table *labels_table) st_free_table(labels_table); } +static NODE * +get_nd_recv(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_CALL: + return RNODE_CALL(node)->nd_recv; + case NODE_OPCALL: + return RNODE_OPCALL(node)->nd_recv; + case NODE_FCALL: + return 0; + case NODE_QCALL: + return RNODE_QCALL(node)->nd_recv; + case NODE_VCALL: + return 0; + case NODE_ATTRASGN: + return RNODE_ATTRASGN(node)->nd_recv; + case NODE_OP_ASGN1: + return RNODE_OP_ASGN1(node)->nd_recv; + case NODE_OP_ASGN2: + return RNODE_OP_ASGN2(node)->nd_recv; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static ID +get_node_call_nd_mid(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_CALL: + return RNODE_CALL(node)->nd_mid; + case NODE_OPCALL: + return RNODE_OPCALL(node)->nd_mid; + case NODE_FCALL: + return RNODE_FCALL(node)->nd_mid; + case NODE_QCALL: + return RNODE_QCALL(node)->nd_mid; + case NODE_VCALL: + return RNODE_VCALL(node)->nd_mid; + case NODE_ATTRASGN: + return RNODE_ATTRASGN(node)->nd_mid; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static NODE * +get_nd_args(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_CALL: + return RNODE_CALL(node)->nd_args; + case NODE_OPCALL: + return RNODE_OPCALL(node)->nd_args; + case NODE_FCALL: + return RNODE_FCALL(node)->nd_args; + case NODE_QCALL: + return RNODE_QCALL(node)->nd_args; + case NODE_VCALL: + return 0; + case NODE_ATTRASGN: + return RNODE_ATTRASGN(node)->nd_args; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static ID +get_node_colon_nd_mid(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_COLON2: + return RNODE_COLON2(node)->nd_mid; + case NODE_COLON3: + return RNODE_COLON3(node)->nd_mid; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static ID +get_nd_vid(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_LASGN: + return RNODE_LASGN(node)->nd_vid; + case NODE_DASGN: + return RNODE_DASGN(node)->nd_vid; + case NODE_IASGN: + return RNODE_IASGN(node)->nd_vid; + case NODE_CVASGN: + return RNODE_CVASGN(node)->nd_vid; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static NODE * +get_nd_value(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_LASGN: + return RNODE_LASGN(node)->nd_value; + case NODE_DASGN: + return RNODE_DASGN(node)->nd_value; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + +static VALUE +get_string_value(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_STR: + return RB_OBJ_SET_SHAREABLE(rb_node_str_string_val(node)); + case NODE_FILE: + return RB_OBJ_SET_SHAREABLE(rb_node_file_path_val(node)); + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + } +} + VALUE rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback_callback_func * ifunc) { @@ -729,32 +852,30 @@ rb_iseq_compile_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_callback (*ifunc->func)(iseq, ret, ifunc->data); - NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1); - ADD_INSN(ret, &dummy_line_node, leave); + ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave); CHECK(iseq_setup_insn(iseq, ret)); return iseq_setup(iseq, ret); } +static bool drop_unreachable_return(LINK_ANCHOR *ret); + VALUE rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) { DECL_ANCHOR(ret); INIT_ANCHOR(ret); - if (IMEMO_TYPE_P(node, imemo_ifunc)) { - rb_raise(rb_eArgError, "unexpected imemo_ifunc"); - } - if (node == 0) { NO_CHECK(COMPILE(ret, "nil", node)); - iseq_set_local_table(iseq, 0); + iseq_set_local_table(iseq, 0, 0); } /* assume node is T_NODE */ else if (nd_type_p(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); + iseq_set_local_table(iseq, RNODE_SCOPE(node)->nd_tbl, (NODE *)RNODE_SCOPE(node)->nd_args); + iseq_set_arguments(iseq, ret, (NODE *)RNODE_SCOPE(node)->nd_args); + iseq_set_parameters_lvar_state(iseq); switch (ISEQ_BODY(iseq)->type) { case ISEQ_TYPE_BLOCK: @@ -766,10 +887,9 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) end->rescued = LABEL_RESCUE_END; ADD_TRACE(ret, RUBY_EVENT_B_CALL); - NODE dummy_line_node = generate_dummy_line_node(FIX2INT(ISEQ_BODY(iseq)->location.first_lineno), -1); - ADD_INSN (ret, &dummy_line_node, nop); + ADD_SYNTHETIC_INSN(ret, ISEQ_BODY(iseq)->location.first_lineno, -1, nop); ADD_LABEL(ret, start); - CHECK(COMPILE(ret, "block body", node->nd_body)); + CHECK(COMPILE(ret, "block body", RNODE_SCOPE(node)->nd_body)); ADD_LABEL(ret, end); ADD_TRACE(ret, RUBY_EVENT_B_RETURN); ISEQ_COMPILE_DATA(iseq)->last_line = ISEQ_BODY(iseq)->location.code_location.end_pos.lineno; @@ -782,23 +902,23 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) case ISEQ_TYPE_CLASS: { ADD_TRACE(ret, RUBY_EVENT_CLASS); - CHECK(COMPILE(ret, "scoped node", node->nd_body)); + CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body)); ADD_TRACE(ret, RUBY_EVENT_END); ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node); break; } case ISEQ_TYPE_METHOD: { - ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body; + ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body; ADD_TRACE(ret, RUBY_EVENT_CALL); - CHECK(COMPILE(ret, "scoped node", node->nd_body)); - ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body; + CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body)); + ISEQ_COMPILE_DATA(iseq)->root_node = RNODE_SCOPE(node)->nd_body; ADD_TRACE(ret, RUBY_EVENT_RETURN); ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node); break; } default: { - CHECK(COMPILE(ret, "scoped node", node->nd_body)); + CHECK(COMPILE(ret, "scoped node", RNODE_SCOPE(node)->nd_body)); break; } } @@ -840,9 +960,8 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0); ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0) /* continue throw */ ); } - else { - NODE dummy_line_node = generate_dummy_line_node(ISEQ_COMPILE_DATA(iseq)->last_line, -1); - ADD_INSN(ret, &dummy_line_node, leave); + else if (!drop_unreachable_return(ret)) { + ADD_SYNTHETIC_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, -1, leave); } #if OPT_SUPPORT_JOKE @@ -872,6 +991,12 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq) } FL_SET((VALUE)iseq, ISEQ_TRANSLATED); #endif + +#if USE_YJIT + rb_yjit_live_iseq_count++; + rb_yjit_iseq_alloc_count++; +#endif + return COMPILE_OK; } @@ -904,75 +1029,44 @@ rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */ /* definition of data structure for compiler */ /*********************************************/ -/* - * On 32-bit SPARC, GCC by default generates SPARC V7 code that may require - * 8-byte word alignment. On the other hand, Oracle Solaris Studio seems to - * generate SPARCV8PLUS code with unaligned memory access instructions. - * That is why the STRICT_ALIGNMENT is defined only with GCC. - */ -#if defined(__sparc) && SIZEOF_VOIDP == 4 && defined(__GNUC__) - #define STRICT_ALIGNMENT +#if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE +# define ALIGNMENT_SIZE SIZEOF_LONG_LONG +#else +# define ALIGNMENT_SIZE SIZEOF_VALUE #endif +#define PADDING_SIZE_MAX ((size_t)((ALIGNMENT_SIZE) - 1)) -/* - * Some OpenBSD platforms (including sparc64) require strict alignment. - */ -#if defined(__OpenBSD__) - #include <sys/endian.h> - #ifdef __STRICT_ALIGNMENT - #define STRICT_ALIGNMENT - #endif -#endif +#define ALIGNMENT_SIZE_OF(type) alignment_size_assert(RUBY_ALIGNOF(type), #type) -#ifdef STRICT_ALIGNMENT - #if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE - #define ALIGNMENT_SIZE SIZEOF_LONG_LONG - #else - #define ALIGNMENT_SIZE SIZEOF_VALUE - #endif - #define PADDING_SIZE_MAX ((size_t)((ALIGNMENT_SIZE) - 1)) - #define ALIGNMENT_SIZE_MASK PADDING_SIZE_MAX - /* Note: ALIGNMENT_SIZE == (2 ** N) is expected. */ -#else - #define PADDING_SIZE_MAX 0 -#endif /* STRICT_ALIGNMENT */ +static inline size_t +alignment_size_assert(size_t align, const char *type) +{ + RUBY_ASSERT((align & (align - 1)) == 0, + "ALIGNMENT_SIZE_OF(%s):%zd == (2 ** N) is expected", type, align); + return align; +} -#ifdef STRICT_ALIGNMENT /* calculate padding size for aligned memory access */ -static size_t -calc_padding(void *ptr, size_t size) +static inline size_t +calc_padding(void *ptr, size_t align) { size_t mis; size_t padding = 0; - mis = (size_t)ptr & ALIGNMENT_SIZE_MASK; + mis = (size_t)ptr & (align - 1); if (mis > 0) { - padding = ALIGNMENT_SIZE - mis; - } -/* - * On 32-bit sparc or equivalents, when a single VALUE is requested - * and padding == sizeof(VALUE), it is clear that no padding is needed. - */ -#if ALIGNMENT_SIZE > SIZEOF_VALUE - if (size == sizeof(VALUE) && padding == sizeof(VALUE)) { - padding = 0; + padding = align - mis; } -#endif return padding; } -#endif /* STRICT_ALIGNMENT */ static void * -compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t size) +compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t size, size_t align) { void *ptr = 0; struct iseq_compile_data_storage *storage = *arena; -#ifdef STRICT_ALIGNMENT - size_t padding = calc_padding((void *)&storage->buff[storage->pos], size); -#else - const size_t padding = 0; /* expected to be optimized by compiler */ -#endif /* STRICT_ALIGNMENT */ + size_t padding = calc_padding((void *)&storage->buff[storage->pos], align); if (size >= INT_MAX - padding) rb_memerror(); if (storage->pos + size + padding > storage->size) { @@ -988,14 +1082,10 @@ compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t s storage->next = 0; storage->pos = 0; storage->size = alloc_size; -#ifdef STRICT_ALIGNMENT - padding = calc_padding((void *)&storage->buff[storage->pos], size); -#endif /* STRICT_ALIGNMENT */ + padding = calc_padding((void *)&storage->buff[storage->pos], align); } -#ifdef STRICT_ALIGNMENT storage->pos += (int)padding; -#endif /* STRICT_ALIGNMENT */ ptr = (void *)&storage->buff[storage->pos]; storage->pos += (int)size; @@ -1003,51 +1093,60 @@ compile_data_alloc_with_arena(struct iseq_compile_data_storage **arena, size_t s } static void * -compile_data_alloc(rb_iseq_t *iseq, size_t size) +compile_data_alloc(rb_iseq_t *iseq, size_t size, size_t align) { struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->node.storage_current; - return compile_data_alloc_with_arena(arena, size); + return compile_data_alloc_with_arena(arena, size, align); } +#define compile_data_alloc_type(iseq, type) \ + (type *)compile_data_alloc(iseq, sizeof(type), ALIGNMENT_SIZE_OF(type)) + static inline void * -compile_data_alloc2(rb_iseq_t *iseq, size_t x, size_t y) +compile_data_alloc2(rb_iseq_t *iseq, size_t elsize, size_t num, size_t align) { - size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError); - return compile_data_alloc(iseq, size); + size_t size = rb_size_mul_or_raise(elsize, num, rb_eRuntimeError); + return compile_data_alloc(iseq, size, align); } +#define compile_data_alloc2_type(iseq, type, num) \ + (type *)compile_data_alloc2(iseq, sizeof(type), num, ALIGNMENT_SIZE_OF(type)) + static inline void * -compile_data_calloc2(rb_iseq_t *iseq, size_t x, size_t y) +compile_data_calloc2(rb_iseq_t *iseq, size_t elsize, size_t num, size_t align) { - size_t size = rb_size_mul_or_raise(x, y, rb_eRuntimeError); - void *p = compile_data_alloc(iseq, size); + size_t size = rb_size_mul_or_raise(elsize, num, rb_eRuntimeError); + void *p = compile_data_alloc(iseq, size, align); memset(p, 0, size); return p; } +#define compile_data_calloc2_type(iseq, type, num) \ + (type *)compile_data_calloc2(iseq, sizeof(type), num, ALIGNMENT_SIZE_OF(type)) + static INSN * compile_data_alloc_insn(rb_iseq_t *iseq) { struct iseq_compile_data_storage ** arena = &ISEQ_COMPILE_DATA(iseq)->insn.storage_current; - return (INSN *)compile_data_alloc_with_arena(arena, sizeof(INSN)); + return (INSN *)compile_data_alloc_with_arena(arena, sizeof(INSN), ALIGNMENT_SIZE_OF(INSN)); } static LABEL * compile_data_alloc_label(rb_iseq_t *iseq) { - return (LABEL *)compile_data_alloc(iseq, sizeof(LABEL)); + return compile_data_alloc_type(iseq, LABEL); } static ADJUST * compile_data_alloc_adjust(rb_iseq_t *iseq) { - return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST)); + return compile_data_alloc_type(iseq, ADJUST); } static TRACE * compile_data_alloc_trace(rb_iseq_t *iseq) { - return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE)); + return compile_data_alloc_type(iseq, TRACE); } /* @@ -1165,10 +1264,15 @@ static void APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *const anc1, LINK_ANCHOR *const anc2) { if (anc2->anchor.next) { + /* LINK_ANCHOR must not loop */ + RUBY_ASSERT(anc2->last != &anc2->anchor); anc1->last->next = anc2->anchor.next; anc2->anchor.next->prev = anc1->last; anc1->last = anc2->last; } + else { + RUBY_ASSERT(anc2->last == &anc2->anchor); + } verify_list("append", anc1); } #if CPDEBUG < 0 @@ -1228,6 +1332,7 @@ new_label_body(rb_iseq_t *iseq, long line) labelobj->set = 0; labelobj->rescued = LABEL_RESCUE_NONE; labelobj->unremovable = 0; + labelobj->position = -1; return labelobj; } @@ -1243,9 +1348,37 @@ new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line) return adjust; } +static void +iseq_insn_each_markable_object(INSN *insn, void (*func)(VALUE *, VALUE), VALUE data) +{ + const char *types = insn_op_types(insn->insn_id); + for (int j = 0; types[j]; j++) { + char type = types[j]; + switch (type) { + case TS_CDHASH: + case TS_ISEQ: + case TS_VALUE: + case TS_IC: // constant path array + case TS_CALLDATA: // ci is stored. + func(&OPERAND_AT(insn, j), data); + break; + default: + break; + } + } +} + +static void +iseq_insn_each_object_write_barrier(VALUE * obj, VALUE iseq) +{ + RB_OBJ_WRITTEN(iseq, Qundef, *obj); + RUBY_ASSERT(SPECIAL_CONST_P(*obj) || + RBASIC_CLASS(*obj) == 0 || // hidden + RB_OBJ_SHAREABLE_P(*obj)); +} + static INSN * -new_insn_core(rb_iseq_t *iseq, const NODE *line_node, - int insn_id, int argc, VALUE *argv) +new_insn_core(rb_iseq_t *iseq, int line_no, int node_id, int insn_id, int argc, VALUE *argv) { INSN *iobj = compile_data_alloc_insn(iseq); @@ -1254,31 +1387,58 @@ new_insn_core(rb_iseq_t *iseq, const NODE *line_node, iobj->link.type = ISEQ_ELEMENT_INSN; iobj->link.next = 0; iobj->insn_id = insn_id; - iobj->insn_info.line_no = nd_line(line_node); - iobj->insn_info.node_id = nd_node_id(line_node); + iobj->insn_info.line_no = line_no; + iobj->insn_info.node_id = node_id; iobj->insn_info.events = 0; iobj->operands = argv; iobj->operand_size = argc; iobj->sc_state = 0; + + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + return iobj; } static INSN * -new_insn_body(rb_iseq_t *iseq, const NODE *const line_node, enum ruby_vminsn_type insn_id, int argc, ...) +new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type insn_id, int argc, ...) { VALUE *operands = 0; va_list argv; if (argc > 0) { int i; va_start(argv, argc); - operands = compile_data_alloc2(iseq, sizeof(VALUE), argc); + operands = compile_data_alloc2_type(iseq, 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_node, insn_id, argc, operands); + return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands); +} + +static INSN * +insn_replace_with_operands(rb_iseq_t *iseq, INSN *iobj, enum ruby_vminsn_type insn_id, int argc, ...) +{ + VALUE *operands = 0; + va_list argv; + if (argc > 0) { + int i; + va_start(argv, argc); + operands = compile_data_alloc2_type(iseq, VALUE, argc); + for (i = 0; i < argc; i++) { + VALUE v = va_arg(argv, VALUE); + operands[i] = v; + } + va_end(argv); + } + + iobj->insn_id = insn_id; + iobj->operand_size = argc; + iobj->operands = operands; + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + + return iobj; } static const struct rb_callinfo * @@ -1286,16 +1446,16 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal { VM_ASSERT(argc >= 0); - if (!(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) && - kw_arg == NULL && !has_blockiseq) { - flag |= VM_CALL_ARGS_SIMPLE; - } - if (kw_arg) { flag |= VM_CALL_KWARG; argc += kw_arg->keyword_len; } + if (!(flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KWARG | VM_CALL_KW_SPLAT | VM_CALL_FORWARDING)) + && !has_blockiseq) { + flag |= VM_CALL_ARGS_SIMPLE; + } + ISEQ_BODY(iseq)->ci_size++; const struct rb_callinfo *ci = vm_ci_new(mid, flag, argc, kw_arg); RB_OBJ_WRITTEN(iseq, Qundef, ci); @@ -1303,16 +1463,25 @@ new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_cal } static INSN * -new_insn_send(rb_iseq_t *iseq, const NODE *const line_node, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords) +new_insn_send(rb_iseq_t *iseq, int line_no, int node_id, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_callinfo_kwarg *keywords) { - VALUE *operands = compile_data_calloc2(iseq, sizeof(VALUE), 2); + VALUE *operands = compile_data_calloc2_type(iseq, VALUE, 2); VALUE ci = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL); operands[0] = ci; operands[1] = (VALUE)blockiseq; if (blockiseq) { RB_OBJ_WRITTEN(iseq, Qundef, blockiseq); } - INSN *insn = new_insn_core(iseq, line_node, BIN(send), 2, operands); + + INSN *insn; + + if (vm_ci_flag((struct rb_callinfo *)ci) & VM_CALL_FORWARDING) { + insn = new_insn_core(iseq, line_no, node_id, BIN(sendforward), 2, operands); + } + else { + insn = new_insn_core(iseq, line_no, node_id, BIN(send), 2, operands); + } + RB_OBJ_WRITTEN(iseq, Qundef, ci); RB_GC_GUARD(ci); return insn; @@ -1323,19 +1492,16 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, VALUE name, const rb_iseq_t *parent, enum rb_iseq_type type, int line_no) { rb_iseq_t *ret_iseq; - rb_ast_body_t ast; - - ast.root = node; - ast.compile_option = 0; - ast.script_lines = ISEQ_BODY(iseq)->variable.script_lines; + VALUE ast_value = rb_ruby_ast_new(node); debugs("[new_child_iseq]> ---------------------------------------\n"); int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; - ret_iseq = rb_iseq_new_with_opt(&ast, name, + ret_iseq = rb_iseq_new_with_opt(ast_value, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, + line_no, parent, isolated_depth ? isolated_depth + 1 : 0, - type, ISEQ_COMPILE_DATA(iseq)->option); + type, ISEQ_COMPILE_DATA(iseq)->option, + ISEQ_BODY(iseq)->variable.script_lines); debugs("[new_child_iseq]< ---------------------------------------\n"); return ret_iseq; } @@ -1349,29 +1515,32 @@ new_child_iseq_with_callback(rb_iseq_t *iseq, const struct rb_iseq_new_with_call debugs("[new_child_iseq_with_callback]> ---------------------------------------\n"); ret_iseq = rb_iseq_new_with_callback(ifunc, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option); + line_no, parent, type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq_with_callback]< ---------------------------------------\n"); return ret_iseq; } static void -set_catch_except_p(struct rb_iseq_constant_body *body) +set_catch_except_p(rb_iseq_t *iseq) { - body->catch_except_p = TRUE; - if (body->parent_iseq != NULL) { - set_catch_except_p(ISEQ_BODY(body->parent_iseq)); + RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)); + ISEQ_COMPILE_DATA(iseq)->catch_except_p = true; + if (ISEQ_BODY(iseq)->parent_iseq != NULL) { + if (ISEQ_COMPILE_DATA(ISEQ_BODY(iseq)->parent_iseq)) { + set_catch_except_p((rb_iseq_t *) ISEQ_BODY(iseq)->parent_iseq); + } } } -/* Set body->catch_except_p to TRUE if the ISeq may catch an exception. If it is FALSE, - JIT-ed code may be optimized. If we are extremely conservative, we should set TRUE +/* Set body->catch_except_p to true if the ISeq may catch an exception. If it is false, + JIT-ed code may be optimized. If we are extremely conservative, we should set true if catch table exists. But we want to optimize while loop, which always has catch table entries for break/next/redo. - So this function sets TRUE for limited ISeqs with break/next/redo catch table entries + So this function sets true for limited ISeqs with break/next/redo catch table entries whose child ISeq would really raise an exception. */ static void -update_catch_except_flags(struct rb_iseq_constant_body *body) +update_catch_except_flags(rb_iseq_t *iseq, struct rb_iseq_constant_body *body) { unsigned int pos; size_t i; @@ -1384,7 +1553,7 @@ update_catch_except_flags(struct rb_iseq_constant_body *body) while (pos < body->iseq_size) { insn = rb_vm_insn_decode(body->iseq_encoded[pos]); if (insn == BIN(throw)) { - set_catch_except_p(body); + set_catch_except_p(iseq); break; } pos += insn_len(insn); @@ -1399,7 +1568,8 @@ update_catch_except_flags(struct rb_iseq_constant_body *body) if (entry->type != CATCH_TYPE_BREAK && entry->type != CATCH_TYPE_NEXT && entry->type != CATCH_TYPE_REDO) { - body->catch_except_p = TRUE; + RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)); + ISEQ_COMPILE_DATA(iseq)->catch_except_p = true; break; } } @@ -1411,9 +1581,9 @@ iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq) VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; if (NIL_P(catch_table_ary)) return; unsigned int i, tlen = (unsigned int)RARRAY_LEN(catch_table_ary); - const VALUE *tptr = RARRAY_CONST_PTR_TRANSIENT(catch_table_ary); + const VALUE *tptr = RARRAY_CONST_PTR(catch_table_ary); for (i = 0; i < tlen; i++) { - const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]); + const VALUE *ptr = RARRAY_CONST_PTR(tptr[i]); LINK_ELEMENT *end = (LINK_ELEMENT *)(ptr[2] & ~1); LINK_ELEMENT *cont = (LINK_ELEMENT *)(ptr[4] & ~1); LINK_ELEMENT *e; @@ -1426,14 +1596,15 @@ iseq_insert_nop_between_end_and_cont(rb_iseq_t *iseq) for (e = end; e && (IS_LABEL(e) || IS_TRACE(e)); e = e->next) { if (e == cont) { - NODE dummy_line_node = generate_dummy_line_node(0, -1); - INSN *nop = new_insn_core(iseq, &dummy_line_node, BIN(nop), 0, 0); + INSN *nop = new_insn_core(iseq, 0, -1, BIN(nop), 0, 0); ELEM_INSERT_NEXT(end, &nop->link); break; } } } } + + RB_GC_GUARD(catch_table_ary); } static int @@ -1460,13 +1631,6 @@ iseq_setup_insn(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) dump_disasm_list(FIRST_ELEMENT(anchor)); } - if (ISEQ_COMPILE_DATA(iseq)->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 3.4 (iseq_insert_nop_between_end_and_cont)]\n"); iseq_insert_nop_between_end_and_cont(iseq); if (compile_debug > 5) @@ -1496,11 +1660,13 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG; debugs("[compile step 6 (update_catch_except_flags)] \n"); - update_catch_except_flags(ISEQ_BODY(iseq)); + RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)); + update_catch_except_flags(iseq, ISEQ_BODY(iseq)); debugs("[compile step 6.1 (remove unused catch tables)] \n"); - if (!ISEQ_BODY(iseq)->catch_except_p && ISEQ_BODY(iseq)->catch_table) { - xfree(ISEQ_BODY(iseq)->catch_table); + RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)); + if (!ISEQ_COMPILE_DATA(iseq)->catch_except_p && ISEQ_BODY(iseq)->catch_table) { + ruby_xfree_sized(ISEQ_BODY(iseq)->catch_table, iseq_catch_table_bytes(ISEQ_BODY(iseq)->catch_table->size)); ISEQ_BODY(iseq)->catch_table = NULL; } @@ -1526,6 +1692,7 @@ iseq_set_exception_local_table(rb_iseq_t *iseq) { ISEQ_BODY(iseq)->local_table_size = numberof(rb_iseq_shared_exc_local_tbl); ISEQ_BODY(iseq)->local_table = rb_iseq_shared_exc_local_tbl; + ISEQ_BODY(iseq)->lvar_states = NULL; // $! is read-only, so don't need lvar_states return COMPILE_OK; } @@ -1635,7 +1802,7 @@ access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write) COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc"); } else { - COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable `%s' from isolated Proc", rb_id2name(id)); + COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable '%s' from isolated Proc", rb_id2name(id)); } } @@ -1673,6 +1840,46 @@ iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level) } static void +update_lvar_state(const rb_iseq_t *iseq, int level, int idx) +{ + for (int i=0; i<level; i++) { + iseq = ISEQ_BODY(iseq)->parent_iseq; + } + + enum lvar_state *states = ISEQ_BODY(iseq)->lvar_states; + int table_idx = ISEQ_BODY(iseq)->local_table_size - idx; + switch (states[table_idx]) { + case lvar_uninitialized: + states[table_idx] = lvar_initialized; + break; + case lvar_initialized: + states[table_idx] = lvar_reassigned; + break; + case lvar_reassigned: + /* nothing */ + break; + default: + rb_bug("unreachable"); + } +} + +static int +iseq_set_parameters_lvar_state(const rb_iseq_t *iseq) +{ + for (unsigned int i=0; i<ISEQ_BODY(iseq)->param.size; i++) { + ISEQ_BODY(iseq)->lvar_states[i] = lvar_initialized; + } + + int lead_num = ISEQ_BODY(iseq)->param.lead_num; + int opt_num = ISEQ_BODY(iseq)->param.opt_num; + for (int i=0; i<opt_num; i++) { + ISEQ_BODY(iseq)->lvar_states[lead_num + i] = lvar_uninitialized; + } + + return COMPILE_OK; +} + +static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const line_node, int idx, int level) { if (iseq_local_block_param_p(iseq, idx, level)) { @@ -1693,6 +1900,7 @@ iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, const NODE *const lin else { ADD_INSN2(seq, line_node, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } + update_lvar_state(iseq, level, idx); if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue); } @@ -1740,7 +1948,7 @@ static int iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const struct rb_args_info *args, int arg_size) { - const NODE *node = args->kw_args; + const rb_node_kw_arg_t *node = args->kw_args; struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); struct rb_iseq_param_keyword *keyword; const VALUE default_values = rb_ary_hidden_new(1); @@ -1759,7 +1967,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, node = args->kw_args; while (node) { - const NODE *val_node = node->nd_body->nd_value; + const NODE *val_node = get_nd_value(node->nd_body); VALUE dv; if (val_node == NODE_SPECIAL_REQUIRED_KEYWORD) { @@ -1767,8 +1975,29 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, } else { switch (nd_type(val_node)) { - case NODE_LIT: - dv = val_node->nd_lit; + case NODE_SYM: + dv = rb_node_sym_string_val(val_node); + break; + case NODE_REGX: + dv = rb_node_regx_string_val(val_node); + break; + case NODE_LINE: + dv = rb_node_line_lineno_val(val_node); + break; + case NODE_INTEGER: + dv = rb_node_integer_literal_val(val_node); + break; + case NODE_FLOAT: + dv = rb_node_float_literal_val(val_node); + break; + case NODE_RATIONAL: + dv = rb_node_rational_literal_val(val_node); + break; + case NODE_IMAGINARY: + dv = rb_node_imaginary_literal_val(val_node); + break; + case NODE_ENCODING: + dv = rb_node_encoding_val(val_node); break; case NODE_NIL: dv = Qnil; @@ -1780,7 +2009,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, dv = Qfalse; break; default: - NO_CHECK(COMPILE_POPPED(optargs, "kwarg", node)); /* nd_type_p(node, NODE_KW_ARG) */ + NO_CHECK(COMPILE_POPPED(optargs, "kwarg", RNODE(node))); /* nd_type_p(node, NODE_KW_ARG) */ dv = complex_mark; } @@ -1793,23 +2022,24 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, keyword->num = kw; - if (args->kw_rest_arg->nd_vid != 0) { + if (RNODE_DVAR(args->kw_rest_arg)->nd_vid != 0) { + ID kw_id = ISEQ_BODY(iseq)->local_table[arg_size]; keyword->rest_start = arg_size++; body->param.flags.has_kwrest = TRUE; + + if (kw_id == idPow) body->param.flags.anon_kwrest = TRUE; } keyword->required_num = rkw; keyword->table = &body->local_table[keyword->bits_start - keyword->num]; - { + if (RARRAY_LEN(default_values)) { VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values)); for (i = 0; i < RARRAY_LEN(default_values); i++) { VALUE dv = RARRAY_AREF(default_values, i); if (dv == complex_mark) dv = Qundef; - if (!SPECIAL_CONST_P(dv)) { - RB_OBJ_WRITTEN(iseq, Qundef, dv); - } - dvs[i] = dv; + if (!SPECIAL_CONST_P(dv)) rb_ractor_make_shareable(dv); + RB_OBJ_WRITE(iseq, &dvs[i], dv); } keyword->default_values = dvs; @@ -1817,6 +2047,22 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, return arg_size; } +static void +iseq_set_use_block(rb_iseq_t *iseq) +{ + struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); + if (!body->param.flags.use_block) { + body->param.flags.use_block = 1; + + rb_vm_t *vm = GET_VM(); + + if (!rb_warning_category_enabled_p(RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK)) { + st_data_t key = (st_data_t)rb_intern_str(body->location.label); // String -> ID + set_insert(&vm->unused_block_warning_table, key); + } + } +} + static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const node_args) { @@ -1824,7 +2070,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons if (node_args) { struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); - struct rb_args_info *args = node_args->nd_ainfo; + const struct rb_args_info *const args = &RNODE_ARGS(node_args)->nd_ainfo; ID rest_id = 0; int last_comma = 0; ID block_id = 0; @@ -1832,7 +2078,6 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG); - body->param.flags.ruby2_keywords = args->ruby2_keywords; body->param.lead_num = arg_size = (int)args->pre_args_num; if (body->param.lead_num > 0) body->param.flags.has_lead = TRUE; debugs(" - argc: %d\n", body->param.lead_num); @@ -1844,15 +2089,22 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons } block_id = args->block_arg; + bool optimized_forward = (args->forwarding && args->pre_args_num == 0 && !args->opt_args); + + if (optimized_forward) { + rest_id = 0; + block_id = 0; + } + if (args->opt_args) { - const NODE *node = args->opt_args; + const rb_node_opt_arg_t *node = args->opt_args; LABEL *label; VALUE labels = rb_ary_hidden_new(1); VALUE *opt_table; int i = 0, j; while (node) { - label = NEW_LABEL(nd_line(node)); + label = NEW_LABEL(nd_line(RNODE(node))); rb_ary_push(labels, (VALUE)label | 1); ADD_LABEL(optargs, label); NO_CHECK(COMPILE_POPPED(optargs, "optarg", node->nd_body)); @@ -1867,7 +2119,7 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons opt_table = ALLOC_N(VALUE, i+1); - MEMCPY(opt_table, RARRAY_CONST_PTR_TRANSIENT(labels), VALUE, i+1); + MEMCPY(opt_table, RARRAY_CONST_PTR(labels), VALUE, i+1); for (j = 0; j < i+1; j++) { opt_table[j] &= ~1; } @@ -1882,7 +2134,8 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons if (rest_id) { body->param.rest_start = arg_size++; body->param.flags.has_rest = TRUE; - assert(body->param.rest_start != -1); + if (rest_id == '*') body->param.flags.anon_rest = TRUE; + RUBY_ASSERT(body->param.rest_start != -1); } if (args->first_post_arg) { @@ -1899,19 +2152,35 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons if (args->kw_args) { arg_size = iseq_set_arguments_keywords(iseq, optargs, args, arg_size); } - else if (args->kw_rest_arg) { + else if (args->kw_rest_arg && !optimized_forward) { + ID kw_id = ISEQ_BODY(iseq)->local_table[arg_size]; struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1); keyword->rest_start = arg_size++; body->param.keyword = keyword; body->param.flags.has_kwrest = TRUE; + + static ID anon_kwrest = 0; + if (!anon_kwrest) anon_kwrest = rb_intern("**"); + if (kw_id == anon_kwrest) body->param.flags.anon_kwrest = TRUE; } else if (args->no_kwarg) { body->param.flags.accepts_no_kwarg = TRUE; } - if (block_id) { + if (args->no_blockarg) { + body->param.flags.accepts_no_block = TRUE; + } + else if (block_id) { body->param.block_start = arg_size++; body->param.flags.has_block = TRUE; + iseq_set_use_block(iseq); + } + + // Only optimize specifically methods like this: `foo(...)` + if (optimized_forward) { + body->param.flags.use_block = 1; + body->param.flags.forwardable = TRUE; + arg_size = 1; } iseq_calc_param_size(iseq); @@ -1943,14 +2212,36 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons } static int -iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl) +iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE *const node_args) { unsigned int size = tbl ? tbl->size : 0; + unsigned int offset = 0; + + if (node_args) { + struct rb_args_info *args = &RNODE_ARGS(node_args)->nd_ainfo; + + // If we have a function that only has `...` as the parameter, + // then its local table should only be `...` + // FIXME: I think this should be fixed in the AST rather than special case here. + if (args->forwarding && args->pre_args_num == 0 && !args->opt_args) { + CHECK(size >= 3); + size -= 3; + offset += 3; + } + } if (size > 0) { - ID *ids = (ID *)ALLOC_N(ID, size); - MEMCPY(ids, tbl->ids, ID, size); + ID *ids = ALLOC_N(ID, size); + MEMCPY(ids, tbl->ids + offset, ID, size); ISEQ_BODY(iseq)->local_table = ids; + + enum lvar_state *states = ALLOC_N(enum lvar_state, size); + // fprintf(stderr, "iseq:%p states:%p size:%d\n", iseq, states, (int)size); + for (unsigned int i=0; i<size; i++) { + states[i] = lvar_uninitialized; + // fprintf(stderr, "id:%s\n", rb_id2name(ISEQ_BODY(iseq)->local_table[i])); + } + ISEQ_BODY(iseq)->lvar_states = states; } ISEQ_BODY(iseq)->local_table_size = size; @@ -2039,6 +2330,33 @@ static const struct st_hash_type cdhash_type = { rb_iseq_cdhash_hash, }; +static VALUE +cdhash_new(size_t size) +{ + VALUE cdhash = rb_imemo_cdhash_new(size, &cdhash_type); + RB_OBJ_SET_SHAREABLE(cdhash); + return cdhash; +} + +static void +cdhash_aset(VALUE cdhash, VALUE key, VALUE val) +{ + st_table *tbl = rb_imemo_cdhash_tbl(cdhash); + st_insert(tbl, key, val); + RB_OBJ_WRITTEN(cdhash, Qundef, key); +} + +static void +cdhash_aset_if_missing(VALUE cdhash, VALUE key, VALUE val) +{ + st_table *tbl = rb_imemo_cdhash_tbl(cdhash); + VALUE dontcare; + if (!st_lookup(tbl, key, &dontcare)) { + st_insert(tbl, key, val); + RB_OBJ_WRITTEN(cdhash, Qundef, key); + } +} + struct cdhash_set_label_struct { VALUE hash; int pos; @@ -2046,32 +2364,24 @@ struct cdhash_set_label_struct { }; static int -cdhash_set_label_i(VALUE key, VALUE val, VALUE ptr) +cdhash_set_label_check_i(st_data_t key, st_data_t value, st_data_t argp, int error) +{ + return ST_REPLACE; +} + +static int +cdhash_set_label_replace_i(st_data_t *key, st_data_t *value, st_data_t ptr, int existing) { 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))); + LABEL *lobj = (LABEL *)(*value & ~1); + *value = lobj->position - (data->pos+data->len); return ST_CONTINUE; } - static inline VALUE get_ivar_ic_value(rb_iseq_t *iseq,ID id) { - VALUE val; - struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table; - if (tbl) { - if (rb_id_table_lookup(tbl,id,&val)) { - return val; - } - } - else { - tbl = rb_id_table_create(1); - ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl; - } - val = INT2FIX(ISEQ_BODY(iseq)->ivc_size++); - rb_id_table_insert(tbl,id,val); - return val; + return INT2FIX(ISEQ_BODY(iseq)->ivc_size++); } static inline VALUE @@ -2097,8 +2407,8 @@ get_cvar_ic_value(rb_iseq_t *iseq,ID id) dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest) #define BADINSN_ERROR \ - (xfree(generated_iseq), \ - xfree(insns_info), \ + (SIZED_FREE_N(generated_iseq, generated_iseq_size), \ + SIZED_FREE_N(insns_info, insns_info_size), \ BADINSN_DUMP(anchor, list, NULL), \ COMPILE_ERROR) @@ -2246,11 +2556,36 @@ add_adjust_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions int insns_info_index, int code_index, const ADJUST *adjust) { insns_info[insns_info_index].line_no = adjust->line_no; + insns_info[insns_info_index].node_id = -1; insns_info[insns_info_index].events = 0; positions[insns_info_index] = code_index; return TRUE; } +static ID * +array_to_idlist(VALUE arr) +{ + RUBY_ASSERT(RB_TYPE_P(arr, T_ARRAY)); + long size = RARRAY_LEN(arr); + ID *ids = (ID *)ALLOC_N(ID, size + 1); + for (long i = 0; i < size; i++) { + VALUE sym = RARRAY_AREF(arr, i); + ids[i] = SYM2ID(sym); + } + ids[size] = 0; + return ids; +} + +static VALUE +idlist_to_array(const ID *ids) +{ + VALUE arr = rb_ary_new(); + while (*ids) { + rb_ary_push(arr, ID2SYM(*ids++)); + } + return arr; +} + /** ruby insn object list -> raw instruction sequence */ @@ -2284,9 +2619,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) if (ISEQ_COVERAGE(iseq)) { if (ISEQ_LINE_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_LINE) && !(rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES)) { - int line = iobj->insn_info.line_no; - if (line >= 1) { - RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, INT2FIX(0)); + int line = iobj->insn_info.line_no - 1; + if (line >= 0 && line < RARRAY_LEN(ISEQ_LINE_COVERAGE(iseq))) { + RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line, INT2FIX(0)); } } if (ISEQ_BRANCH_COVERAGE(iseq) && (events & RUBY_EVENT_COVERAGE_BRANCH)) { @@ -2339,11 +2674,27 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) } /* make instruction sequence */ + const int generated_iseq_size = code_index; generated_iseq = ALLOC_N(VALUE, code_index); + + const int insns_info_size = insn_num; insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num); + + const int positions_size = insn_num; positions = ALLOC_N(unsigned int, insn_num); - body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body)); - body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size); + if (ISEQ_IS_SIZE(body)) { + body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body)); + } + else { + body->is_entries = NULL; + } + + if (body->ci_size) { + body->call_data = ZALLOC_N(struct rb_call_data, body->ci_size); + } + else { + body->call_data = NULL; + } ISEQ_COMPILE_DATA(iseq)->ci_index = 0; // Calculate the bitmask buffer size. @@ -2354,16 +2705,22 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) iseq_bits_t * mark_offset_bits; int code_size = code_index; - iseq_bits_t tmp[1] = {0}; bool needs_bitmap = false; - if (ISEQ_MBITS_BUFLEN(code_index) == 1) { - mark_offset_bits = tmp; + const size_t mark_offset_bits_size = ISEQ_MBITS_BUFLEN(code_index); + if (mark_offset_bits_size == 1) { + mark_offset_bits = &ISEQ_COMPILE_DATA(iseq)->mark_bits.single; + ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit = true; } else { - mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(code_index)); + mark_offset_bits = ZALLOC_N(iseq_bits_t, mark_offset_bits_size); + ISEQ_COMPILE_DATA(iseq)->mark_bits.list = mark_offset_bits; + ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit = false; } + ISEQ_COMPILE_DATA(iseq)->iseq_encoded = (void *)generated_iseq; + ISEQ_COMPILE_DATA(iseq)->iseq_size = code_index; + list = FIRST_ELEMENT(anchor); insns_info_index = code_index = sp = 0; @@ -2404,10 +2761,8 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) data.hash = map; data.pos = code_index; data.len = len; - rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data); + st_foreach_with_replace(rb_imemo_cdhash_tbl(map), cdhash_set_label_check_i, cdhash_set_label_replace_i, (VALUE)&data); - rb_hash_rehash(map); - freeze_hide_obj(map); generated_iseq[code_index + 1 + j] = map; ISEQ_MBITS_SET(mark_offset_bits, code_index + 1 + j); RB_OBJ_WRITTEN(iseq, Qundef, map); @@ -2433,9 +2788,38 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) } /* [ TS_IVC | TS_ICVARC | TS_ISE | TS_IC ] */ case TS_IC: /* inline cache: constants */ + { + unsigned int ic_index = ISEQ_COMPILE_DATA(iseq)->ic_index++; + IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache; + if (UNLIKELY(ic_index >= body->ic_size)) { + BADINSN_DUMP(anchor, &iobj->link, 0); + COMPILE_ERROR(iseq, iobj->insn_info.line_no, + "iseq_set_sequence: ic_index overflow: index: %d, size: %d", + ic_index, ISEQ_IS_SIZE(body)); + } + + ic->segments = array_to_idlist(operands[j]); + + generated_iseq[code_index + 1 + j] = (VALUE)ic; + } + break; + case TS_IVC: /* inline ivar cache */ + { + unsigned int ic_index = FIX2UINT(operands[j]); + + IVC cache = ((IVC)&body->is_entries[ic_index]); + + if (insn == BIN(setinstancevariable)) { + cache->iv_set_name = SYM2ID(operands[j - 1]); + cache->value = IVAR_CACHE_INIT; + } + else { + cache->iv_set_name = 0; + cache->value = rb_getivar_cache_pack(ROOT_SHAPE_ID, ATTR_INDEX_NOT_SET); + } + } case TS_ISE: /* inline storage entry: `once` insn */ case TS_ICVARC: /* inline cvar cache */ - case TS_IVC: /* inline ivar cache */ { unsigned int ic_index = FIX2UINT(operands[j]); IC ic = &ISEQ_IS_ENTRY_START(body, type)[ic_index].ic_cache; @@ -2447,18 +2831,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) } generated_iseq[code_index + 1 + j] = (VALUE)ic; - if (insn == BIN(opt_getinlinecache) && type == TS_IC) { - // Store the instruction index for opt_getinlinecache on the IC for - // YJIT to invalidate code when opt_setinlinecache runs. - ic->get_insn_idx = (unsigned int)code_index; - } break; } - case TS_CALLDATA: + case TS_CALLDATA: { const struct rb_callinfo *source_ci = (const struct rb_callinfo *)operands[j]; + RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size); struct rb_call_data *cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++]; - assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size); cd->ci = source_ci; cd->cc = vm_cc_empty(); generated_iseq[code_index + 1 + j] = (VALUE)cd; @@ -2524,11 +2903,11 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) } else if (diff < 0) { int label_no = adjust->label ? adjust->label->label_no : -1; - xfree(generated_iseq); - xfree(insns_info); - xfree(positions); + SIZED_FREE_N(generated_iseq, generated_iseq_size); + SIZED_FREE_N(insns_info, insns_info_size); + SIZED_FREE_N(positions, positions_size); if (ISEQ_MBITS_BUFLEN(code_size) > 1) { - xfree(mark_offset_bits); + SIZED_FREE_N(mark_offset_bits, ISEQ_MBITS_BUFLEN(code_index)); } debug_list(anchor, list); COMPILE_ERROR(iseq, adjust->line_no, @@ -2550,16 +2929,17 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) body->iseq_size = code_index; body->stack_max = stack_max; - if (ISEQ_MBITS_BUFLEN(body->iseq_size) == 1) { - body->mark_bits.single = mark_offset_bits[0]; + if (ISEQ_COMPILE_DATA(iseq)->is_single_mark_bit) { + body->mark_bits.single = ISEQ_COMPILE_DATA(iseq)->mark_bits.single; } else { if (needs_bitmap) { body->mark_bits.list = mark_offset_bits; } else { - body->mark_bits.list = 0; - ruby_xfree(mark_offset_bits); + body->mark_bits.list = NULL; + ISEQ_COMPILE_DATA(iseq)->mark_bits.list = NULL; + SIZED_FREE_N(mark_offset_bits, mark_offset_bits_size); } } @@ -2567,9 +2947,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) body->insns_info.body = insns_info; body->insns_info.positions = positions; - REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index); + SIZED_REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index, insns_info_size); body->insns_info.body = insns_info; - REALLOC_N(positions, unsigned int, insns_info_index); + SIZED_REALLOC_N(positions, unsigned int, insns_info_index, positions_size); body->insns_info.positions = positions; body->insns_info.size = insns_info_index; @@ -2596,20 +2976,27 @@ iseq_set_exception_table(rb_iseq_t *iseq) struct iseq_catch_table_entry *entry; ISEQ_BODY(iseq)->catch_table = NULL; - if (NIL_P(ISEQ_COMPILE_DATA(iseq)->catch_table_ary)) return COMPILE_OK; - tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary); - tptr = RARRAY_CONST_PTR_TRANSIENT(ISEQ_COMPILE_DATA(iseq)->catch_table_ary); + + VALUE catch_table_ary = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; + if (NIL_P(catch_table_ary)) return COMPILE_OK; + tlen = (int)RARRAY_LEN(catch_table_ary); + tptr = RARRAY_CONST_PTR(catch_table_ary); if (tlen > 0) { struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen)); table->size = tlen; for (i = 0; i < table->size; i++) { - ptr = RARRAY_CONST_PTR_TRANSIENT(tptr[i]); + int pos; + ptr = RARRAY_CONST_PTR(tptr[i]); entry = UNALIGNED_MEMBER_PTR(table, entries[i]); entry->type = (enum rb_catch_type)(ptr[0] & 0xffff); - entry->start = label_get_position((LABEL *)(ptr[1] & ~1)); - entry->end = label_get_position((LABEL *)(ptr[2] & ~1)); + pos = label_get_position((LABEL *)(ptr[1] & ~1)); + RUBY_ASSERT(pos >= 0); + entry->start = (unsigned int)pos; + pos = label_get_position((LABEL *)(ptr[2] & ~1)); + RUBY_ASSERT(pos >= 0); + entry->end = (unsigned int)pos; entry->iseq = (rb_iseq_t *)ptr[3]; RB_OBJ_WRITTEN(iseq, Qundef, entry->iseq); @@ -2623,6 +3010,7 @@ iseq_set_exception_table(rb_iseq_t *iseq) if (entry->type == CATCH_TYPE_RESCUE || entry->type == CATCH_TYPE_BREAK || entry->type == CATCH_TYPE_NEXT) { + RUBY_ASSERT(entry->sp > 0); entry->sp--; } } @@ -2634,6 +3022,8 @@ iseq_set_exception_table(rb_iseq_t *iseq) RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */ } + RB_GC_GUARD(catch_table_ary); + return COMPILE_OK; } @@ -2730,16 +3120,18 @@ unref_destination(INSN *iobj, int pos) if (!lobj->refcnt) ELEM_REMOVE(&lobj->link); } -static void +static bool replace_destination(INSN *dobj, INSN *nobj) { VALUE n = OPERAND_AT(nobj, 0); LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0); LABEL *nl = (LABEL *)n; + if (dl == nl) return false; --dl->refcnt; ++nl->refcnt; OPERAND_AT(dobj, 0) = n; if (!dl->refcnt) ELEM_REMOVE(&dl->link); + return true; } static LABEL* @@ -2772,7 +3164,6 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i) break; } else if ((lab = find_destination((INSN *)i)) != 0) { - if (lab->unremovable) break; unref_counts[lab->label_no]++; } } @@ -2789,8 +3180,7 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i) /* do nothing */ } else if (IS_ADJUST(i)) { - LABEL *dest = ((ADJUST *)i)->label; - if (dest && dest->unremovable) return 0; + return 0; } end = i; } while ((i = i->next) != 0); @@ -2835,7 +3225,7 @@ iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj) static int is_frozen_putstring(INSN *insn, VALUE *op) { - if (IS_INSN_ID(insn, putstring)) { + if (IS_INSN_ID(insn, dupstring) || IS_INSN_ID(insn, dupchilledstring)) { *op = OPERAND_AT(insn, 0); return 1; } @@ -2847,6 +3237,25 @@ is_frozen_putstring(INSN *insn, VALUE *op) } static int +insn_has_label_before(LINK_ELEMENT *elem) +{ + LINK_ELEMENT *prev = elem->prev; + while (prev) { + if (prev->type == ISEQ_ELEMENT_LABEL) { + LABEL *label = (LABEL *)prev; + if (label->refcnt > 0) { + return 1; + } + } + else if (prev->type == ISEQ_ELEMENT_INSN) { + break; + } + prev = prev->prev; + } + return 0; +} + +static int optimize_checktype(rb_iseq_t *iseq, INSN *iobj) { /* @@ -2876,7 +3285,8 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj) VALUE type; switch (INSN_OF(iobj)) { - case BIN(putstring): + case BIN(dupstring): + case BIN(dupchilledstring): type = INT2FIX(T_STRING); break; case BIN(putnil): @@ -2917,7 +3327,6 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj) } line = ciobj->insn_info.line_no; node_id = ciobj->insn_info.node_id; - NODE dummy_line_node = generate_dummy_line_node(line, node_id); if (!dest) { if (niobj->link.next && IS_LABEL(niobj->link.next)) { dest = (LABEL *)niobj->link.next; /* reuse label */ @@ -2927,9 +3336,9 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj) ELEM_INSERT_NEXT(&niobj->link, &dest->link); } } - INSERT_AFTER_INSN1(iobj, &dummy_line_node, jump, dest); + INSERT_AFTER_INSN1(iobj, line, node_id, jump, dest); LABEL_REF(dest); - if (!dup) INSERT_AFTER_INSN(iobj, &dummy_line_node, pop); + if (!dup) INSERT_AFTER_INSN(iobj, line, node_id, pop); return TRUE; } @@ -2955,6 +3364,8 @@ ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc) return nci; } +#define vm_ci_simple(ci) (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) + static int iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt) { @@ -2993,9 +3404,10 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * => in this case, first jump instruction should jump to * LABEL2 directly */ - replace_destination(iobj, diobj); - remove_unreachable_chunk(iseq, iobj->link.next); - goto again; + if (replace_destination(iobj, diobj)) { + remove_unreachable_chunk(iseq, iobj->link.next); + goto again; + } } else if (IS_INSN_ID(diobj, leave)) { /* @@ -3040,8 +3452,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal */ piobj->insn_id = (IS_INSN_ID(piobj, branchif)) ? BIN(branchunless) : BIN(branchif); - replace_destination(piobj, iobj); - if (refcnt <= 1) { + if (replace_destination(piobj, iobj) && refcnt <= 1) { ELEM_REMOVE(&iobj->link); } else { @@ -3063,8 +3474,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * pop * jump L1 */ - NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id); - INSN *popiobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, 0); + INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, BIN(pop), 0, 0); ELEM_REPLACE(&piobj->link, &popiobj->link); } } @@ -3074,8 +3484,8 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } /* - * putstring "beg" - * putstring "end" + * dupstring "beg" + * dupstring "end" * newrange excl * * ==> @@ -3090,9 +3500,10 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if ((end = (INSN *)get_prev_insn(range)) != 0 && is_frozen_putstring(end, &str_end) && (beg = (INSN *)get_prev_insn(end)) != 0 && - is_frozen_putstring(beg, &str_beg)) { + is_frozen_putstring(beg, &str_beg) && + !(insn_has_label_before(&beg->link) || insn_has_label_before(&end->link))) { int excl = FIX2INT(OPERAND_AT(range, 0)); - VALUE lit_range = rb_range_new(str_beg, str_end, excl); + VALUE lit_range = RB_OBJ_SET_SHAREABLE(rb_range_new(str_beg, str_end, excl)); ELEM_REMOVE(&beg->link); ELEM_REMOVE(&end->link); @@ -3109,19 +3520,102 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal /* * ... * duparray [...] - * concatarray + * concatarray | concattoarray * => * ... * putobject [...] - * concatarray + * concatarray | concattoarray */ if (IS_INSN_ID(iobj, duparray)) { LINK_ELEMENT *next = iobj->link.next; - if (IS_INSN(next) && IS_INSN_ID(next, concatarray)) { + if (IS_INSN(next) && (IS_INSN_ID(next, concatarray) || IS_INSN_ID(next, concattoarray))) { iobj->insn_id = BIN(putobject); } } + /* + * duparray [...] + * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil + * => + * opt_ary_freeze [...], <calldata!mid:freeze, argc:0, ARGS_SIMPLE> + */ + if (IS_INSN_ID(iobj, duparray)) { + LINK_ELEMENT *next = iobj->link.next; + if (IS_INSN(next) && (IS_INSN_ID(next, send))) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0); + const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); + + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { + VALUE ary = iobj->operands[0]; + rb_obj_reveal(ary, rb_cArray); + + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci); + ELEM_REMOVE(next); + } + } + } + + /* + * duphash {...} + * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil + * => + * opt_hash_freeze {...}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE> + */ + if (IS_INSN_ID(iobj, duphash)) { + LINK_ELEMENT *next = iobj->link.next; + if (IS_INSN(next) && (IS_INSN_ID(next, send))) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0); + const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); + + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { + VALUE hash = iobj->operands[0]; + rb_obj_reveal(hash, rb_cHash); + RB_OBJ_SET_SHAREABLE(hash); + + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci); + ELEM_REMOVE(next); + } + } + } + + /* + * newarray 0 + * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil + * => + * opt_ary_freeze [], <calldata!mid:freeze, argc:0, ARGS_SIMPLE> + */ + if (IS_INSN_ID(iobj, newarray) && iobj->operands[0] == INT2FIX(0)) { + LINK_ELEMENT *next = iobj->link.next; + if (IS_INSN(next) && (IS_INSN_ID(next, send))) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0); + const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); + + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci); + ELEM_REMOVE(next); + } + } + } + + /* + * newhash 0 + * send <calldata!mid:freeze, argc:0, ARGS_SIMPLE>, nil + * => + * opt_hash_freeze {}, <calldata!mid:freeze, argc:0, ARGS_SIMPLE> + */ + if (IS_INSN_ID(iobj, newhash) && iobj->operands[0] == INT2FIX(0)) { + LINK_ELEMENT *next = iobj->link.next; + if (IS_INSN(next) && (IS_INSN_ID(next, send))) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(next, 0); + const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); + + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci); + ELEM_REMOVE(next); + } + } + } + if (IS_INSN_ID(iobj, branchif) || IS_INSN_ID(iobj, branchnil) || IS_INSN_ID(iobj, branchunless)) { @@ -3171,7 +3665,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal for (;;) { if (IS_INSN(&nobj->link) && IS_INSN_ID(nobj, jump)) { - replace_destination(iobj, nobj); + if (!replace_destination(iobj, nobj)) break; } else if (prev_dup && IS_INSN_ID(nobj, dup) && !!(nobj = (INSN *)nobj->link.next) && @@ -3192,7 +3686,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * dup * if L2 */ - replace_destination(iobj, nobj); + if (!replace_destination(iobj, nobj)) break; } else if (pobj) { /* @@ -3206,16 +3700,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * => * jump L1 * - * putstring ".." + * dupstring ".." * if L1 * => * jump L1 * - * putstring ".." + * dupstring ".." * dup * if L1 * => - * putstring ".." + * dupstring ".." * jump L1 * */ @@ -3230,7 +3724,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal OPERAND_AT(pobj, 0) == Qfalse : FALSE); } - else if (IS_INSN_ID(pobj, putstring) || + else if (IS_INSN_ID(pobj, dupstring) || IS_INSN_ID(pobj, duparray) || IS_INSN_ID(pobj, newarray)) { cond = IS_INSN_ID(iobj, branchif); @@ -3243,14 +3737,12 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal ELEM_REMOVE(iobj->link.prev); } else if (!iseq_pop_newarray(iseq, pobj)) { - NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id); - pobj = new_insn_core(iseq, &dummy_line_node, BIN(pop), 0, NULL); + pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(pop), 0, NULL); ELEM_INSERT_PREV(&iobj->link, &pobj->link); } if (cond) { if (prev_dup) { - NODE dummy_line_node = generate_dummy_line_node(pobj->insn_info.line_no, pobj->insn_info.node_id); - pobj = new_insn_core(iseq, &dummy_line_node, BIN(putnil), 0, NULL); + pobj = new_insn_core(iseq, pobj->insn_info.line_no, pobj->insn_info.node_id, BIN(putnil), 0, NULL); ELEM_INSERT_NEXT(&iobj->link, &pobj->link); } iobj->insn_id = BIN(jump); @@ -3270,7 +3762,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, pop)) { /* - * putself / putnil / putobject obj / putstring "..." + * putself / putnil / putobject obj / dupstring "..." * pop * => * # do nothing @@ -3279,12 +3771,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN(prev)) { enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id; if (previ == BIN(putobject) || previ == BIN(putnil) || - previ == BIN(putself) || previ == BIN(putstring) || + previ == BIN(putself) || previ == BIN(dupstring) || + previ == BIN(dupchilledstring) || previ == BIN(dup) || previ == BIN(getlocal) || previ == BIN(getblockparam) || previ == BIN(getblockparamproxy) || - /* getinstancevariable may issue a warning */ + previ == BIN(getinstancevariable) || previ == BIN(duparray)) { /* just push operand or static value and pop soon, no * side effects */ @@ -3296,8 +3789,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } else if (previ == BIN(concatarray)) { INSN *piobj = (INSN *)prev; - NODE dummy_line_node = generate_dummy_line_node(piobj->insn_info.line_no, piobj->insn_info.node_id); - INSERT_BEFORE_INSN1(piobj, &dummy_line_node, splatarray, Qfalse); + INSERT_BEFORE_INSN1(piobj, piobj->insn_info.line_no, piobj->insn_info.node_id, splatarray, Qfalse); INSN_OF(piobj) = BIN(pop); } else if (previ == BIN(concatstrings)) { @@ -3314,7 +3806,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, newarray) || IS_INSN_ID(iobj, duparray) || - IS_INSN_ID(iobj, expandarray) || IS_INSN_ID(iobj, concatarray) || IS_INSN_ID(iobj, splatarray) || 0) { @@ -3332,6 +3823,83 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + if (IS_INSN_ID(iobj, newarray)) { + LINK_ELEMENT *next = iobj->link.next; + if (IS_INSN(next) && IS_INSN_ID(next, expandarray) && + OPERAND_AT(next, 1) == INT2FIX(0)) { + VALUE op1, op2; + op1 = OPERAND_AT(iobj, 0); + op2 = OPERAND_AT(next, 0); + ELEM_REMOVE(next); + + if (op1 == op2) { + /* + * newarray 2 + * expandarray 2, 0 + * => + * swap + */ + if (op1 == INT2FIX(2)) { + INSN_OF(iobj) = BIN(swap); + iobj->operand_size = 0; + } + /* + * newarray X + * expandarray X, 0 + * => + * opt_reverse X + */ + else { + INSN_OF(iobj) = BIN(opt_reverse); + } + } + else { + long diff = FIX2LONG(op1) - FIX2LONG(op2); + INSN_OF(iobj) = BIN(opt_reverse); + OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0); + + if (op1 > op2) { + /* X > Y + * newarray X + * expandarray Y, 0 + * => + * pop * (Y-X) + * opt_reverse Y + */ + for (; diff > 0; diff--) { + INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, pop); + } + } + else { /* (op1 < op2) */ + /* X < Y + * newarray X + * expandarray Y, 0 + * => + * putnil * (Y-X) + * opt_reverse Y + */ + for (; diff < 0; diff++) { + INSERT_BEFORE_INSN(iobj, iobj->insn_info.line_no, iobj->insn_info.node_id, putnil); + } + } + } + } + } + + if (IS_INSN_ID(iobj, duparray)) { + LINK_ELEMENT *next = iobj->link.next; + /* + * duparray obj + * expandarray X, 0 + * => + * putobject obj + * expandarray X, 0 + */ + if (IS_INSN(next) && IS_INSN_ID(next, expandarray)) { + INSN_OF(iobj) = BIN(putobject); + } + } + if (IS_INSN_ID(iobj, anytostring)) { LINK_ELEMENT *next = iobj->link.next; /* @@ -3346,10 +3914,10 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } - if (IS_INSN_ID(iobj, putstring) || + if (IS_INSN_ID(iobj, dupstring) || IS_INSN_ID(iobj, dupchilledstring) || (IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) { /* - * putstring "" + * dupstring "" * concatstrings N * => * concatstrings N-1 @@ -3362,6 +3930,27 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } ELEM_REMOVE(&iobj->link); } + if (IS_NEXT_INSN_ID(&iobj->link, toregexp)) { + INSN *next = (INSN *)iobj->link.next; + if (OPERAND_AT(next, 1) == INT2FIX(1)) { + VALUE src = OPERAND_AT(iobj, 0); + int opt = (int)FIX2LONG(OPERAND_AT(next, 0)); + VALUE path = rb_iseq_path(iseq); + int line = iobj->insn_info.line_no; + VALUE errinfo = rb_errinfo(); + VALUE re = rb_reg_compile(src, opt, RSTRING_PTR(path), line); + if (NIL_P(re)) { + VALUE message = rb_attr_get(rb_errinfo(), idMesg); + rb_set_errinfo(errinfo); + COMPILE_ERROR(iseq, line, "%" PRIsVALUE, message); + } + else { + RB_OBJ_SET_SHAREABLE(re); + } + RB_OBJ_WRITE(iseq, &OPERAND_AT(iobj, 0), re); + ELEM_REMOVE(iobj->link.next); + } + } } if (IS_INSN_ID(iobj, concatstrings)) { @@ -3399,8 +3988,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (do_tailcallopt && (IS_INSN_ID(iobj, send) || - IS_INSN_ID(iobj, opt_aref_with) || - IS_INSN_ID(iobj, opt_aset_with) || IS_INSN_ID(iobj, invokesuper))) { /* * send ... @@ -3459,6 +4046,15 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, dup)) { if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) { LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL; + + /* + * dup + * setlocal x, y + * setlocal x, y + * => + * dup + * setlocal x, y + */ if (IS_NEXT_INSN_ID(set1, setlocal)) { set2 = set1->next; if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) && @@ -3467,6 +4063,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal ELEM_REMOVE(&iobj->link); } } + + /* + * dup + * setlocal x, y + * dup + * setlocal x, y + * => + * dup + * setlocal x, y + */ else if (IS_NEXT_INSN_ID(set1, dup) && IS_NEXT_INSN_ID(set1->next, setlocal)) { set2 = set1->next->next; @@ -3479,6 +4085,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * getlocal x, y + * dup + * setlocal x, y + * => + * dup + */ if (IS_INSN_ID(iobj, getlocal)) { LINK_ELEMENT *niobj = &iobj->link; if (IS_NEXT_INSN_ID(niobj, dup)) { @@ -3494,10 +4107,94 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * opt_invokebuiltin_delegate + * trace + * leave + * => + * opt_invokebuiltin_delegate_leave + * trace + * leave + */ if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) { if (IS_TRACE(iobj->link.next)) { if (IS_NEXT_INSN_ID(iobj->link.next, leave)) { iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave); + const struct rb_builtin_function *bf = (const struct rb_builtin_function *)iobj->operands[0]; + if (iobj == (INSN *)list && bf->argc == 0 && (ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_LEAF)) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_SINGLE_NOARG_LEAF; + } + } + } + } + + /* + * getblockparam + * branchif / branchunless + * => + * getblockparamproxy + * branchif / branchunless + */ + if (IS_INSN_ID(iobj, getblockparam)) { + if (IS_NEXT_INSN_ID(&iobj->link, branchif) || IS_NEXT_INSN_ID(&iobj->link, branchunless)) { + iobj->insn_id = BIN(getblockparamproxy); + } + } + + if (IS_INSN_ID(iobj, splatarray) && OPERAND_AT(iobj, 0) == false) { + LINK_ELEMENT *niobj = &iobj->link; + if (IS_NEXT_INSN_ID(niobj, duphash)) { + niobj = niobj->next; + LINK_ELEMENT *siobj; + unsigned int set_flags = 0, unset_flags = 0; + + /* + * Eliminate hash allocation for f(*a, kw: 1) + * + * splatarray false + * duphash + * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT and not ARGS_BLOCKARG + * => + * splatarray false + * putobject + * send ARGS_SPLAT|KW_SPLAT + */ + if (IS_NEXT_INSN_ID(niobj, send)) { + siobj = niobj->next; + set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT; + unset_flags = VM_CALL_ARGS_BLOCKARG; + } + /* + * Eliminate hash allocation for f(*a, kw: 1, &{arg,lvar,@iv}) + * + * splatarray false + * duphash + * getlocal / getinstancevariable / getblockparamproxy + * send ARGS_SPLAT|KW_SPLAT|KW_SPLAT_MUT|ARGS_BLOCKARG + * => + * splatarray false + * putobject + * getlocal / getinstancevariable / getblockparamproxy + * send ARGS_SPLAT|KW_SPLAT|ARGS_BLOCKARG + */ + else if ((IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || + IS_NEXT_INSN_ID(niobj, getblockparamproxy)) && (IS_NEXT_INSN_ID(niobj->next, send))) { + siobj = niobj->next->next; + set_flags = VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG; + } + + if (set_flags) { + const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(siobj, 0); + unsigned int flags = vm_ci_flag(ci); + if ((flags & set_flags) == set_flags && !(flags & unset_flags)) { + ((INSN*)niobj)->insn_id = BIN(putobject); + RB_OBJ_WRITE(iseq, &OPERAND_AT(niobj, 0), RB_OBJ_SET_SHAREABLE(rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0))))); + + const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci), + flags & ~VM_CALL_KW_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci)); + RB_OBJ_WRITTEN(iseq, ci, nci); + OPERAND_AT(siobj, 0) = (VALUE)nci; + } } } } @@ -3508,17 +4205,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal static int insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) { - iobj->insn_id = insn_id; - iobj->operand_size = insn_len(insn_id) - 1; - iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; - if (insn_id == BIN(opt_neq)) { VALUE original_ci = iobj->operands[0]; - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); - iobj->operands[1] = original_ci; + VALUE new_ci = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); + insn_replace_with_operands(iseq, iobj, insn_id, 2, new_ci, original_ci); } + else { + iobj->insn_id = insn_id; + iobj->operand_size = insn_len(insn_id) - 1; + } + iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; return COMPILE_OK; } @@ -3529,32 +4225,145 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (IS_INSN_ID(iobj, newarray) && iobj->link.next && IS_INSN(iobj->link.next)) { /* - * [a, b, ...].max/min -> a, b, c, opt_newarray_max/min + * [a, b, ...].max/min -> a, b, c, opt_newarray_send max/min */ INSN *niobj = (INSN *)iobj->link.next; if (IS_INSN_ID(niobj, send)) { const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0); - if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) { + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0) { + VALUE method = INT2FIX(0); switch (vm_ci_mid(ci)) { case idMax: - iobj->insn_id = BIN(opt_newarray_max); - ELEM_REMOVE(&niobj->link); - return COMPILE_OK; + method = INT2FIX(VM_OPT_NEWARRAY_SEND_MAX); + break; case idMin: - iobj->insn_id = BIN(opt_newarray_min); + method = INT2FIX(VM_OPT_NEWARRAY_SEND_MIN); + break; + case idHash: + method = INT2FIX(VM_OPT_NEWARRAY_SEND_HASH); + break; + } + + if (method != INT2FIX(0)) { + VALUE num = iobj->operands[0]; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, num, method); ELEM_REMOVE(&niobj->link); return COMPILE_OK; } } } + else if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) || + (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) && + IS_NEXT_INSN_ID(&niobj->link, send)) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0); + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) { + VALUE num = iobj->operands[0]; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK)); + ELEM_REMOVE(&iobj->link); + ELEM_REMOVE(niobj->link.next); + ELEM_INSERT_NEXT(&niobj->link, &iobj->link); + return COMPILE_OK; + } + } + // newarray n, dupchilledstring "E", getlocal b, send :pack with {buffer: b} + // -> dupchilledstring "E", getlocal b, opt_newarray_send n+2, :pack, :buffer + else if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) || + (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) && + IS_NEXT_INSN_ID(&niobj->link, getlocal) && + (niobj->link.next && IS_NEXT_INSN_ID(niobj->link.next, send))) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)(niobj->link.next)->next, 0); + const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci); + if (vm_ci_mid(ci) == idPack && vm_ci_argc(ci) == 2 && + (kwarg && kwarg->keyword_len == 1 && kwarg->keywords[0] == rb_id2sym(idBuffer))) { + VALUE num = iobj->operands[0]; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 2), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER)); + // Remove the "send" insn. + ELEM_REMOVE((niobj->link.next)->next); + // Remove the modified insn from its original "newarray" position... + ELEM_REMOVE(&iobj->link); + // and insert it after the buffer insn. + ELEM_INSERT_NEXT(niobj->link.next, &iobj->link); + return COMPILE_OK; + } + } + + // Break the "else if" chain since some prior checks abort after sub-ifs. + // We already found "newarray". To match `[...].include?(arg)` we look for + // the instruction(s) representing the argument followed by a "send". + if ((IS_INSN_ID(niobj, dupstring) || IS_INSN_ID(niobj, dupchilledstring) || + IS_INSN_ID(niobj, putobject) || + IS_INSN_ID(niobj, putself) || + IS_INSN_ID(niobj, getlocal) || + IS_INSN_ID(niobj, getinstancevariable)) && + IS_NEXT_INSN_ID(&niobj->link, send)) { + + LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next; + const struct rb_callinfo *ci; + // Allow any number (0 or more) of simple method calls on the argument + // (as in `[...].include?(arg.method1.method2)`. + do { + sendobj = sendobj->next; + ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0); + } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send)); + + // If this send is for .include? with one arg we can do our opt. + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) { + VALUE num = iobj->operands[0]; + INSN *sendins = (INSN *)sendobj; + insn_replace_with_operands(iseq, sendins, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P)); + // Remove the original "newarray" insn. + ELEM_REMOVE(&iobj->link); + return COMPILE_OK; + } + } } + /* + * duparray [...] + * some insn for the arg... + * send <calldata!mid:include?, argc:1, ARGS_SIMPLE>, nil + * => + * arg insn... + * opt_duparray_send [...], :include?, 1 + */ + if (IS_INSN_ID(iobj, duparray) && iobj->link.next && IS_INSN(iobj->link.next)) { + INSN *niobj = (INSN *)iobj->link.next; + if ((IS_INSN_ID(niobj, getlocal) || + IS_INSN_ID(niobj, getinstancevariable) || + IS_INSN_ID(niobj, putself)) && + IS_NEXT_INSN_ID(&niobj->link, send)) { + + LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next; + const struct rb_callinfo *ci; + // Allow any number (0 or more) of simple method calls on the argument + // (as in `[...].include?(arg.method1.method2)`. + do { + sendobj = sendobj->next; + ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0); + } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send)); + + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) { + // Move the array arg from duparray to opt_duparray_send. + VALUE ary = iobj->operands[0]; + rb_obj_reveal(ary, rb_cArray); + + INSN *sendins = (INSN *)sendobj; + insn_replace_with_operands(iseq, sendins, BIN(opt_duparray_send), 3, ary, rb_id2sym(idIncludeP), INT2FIX(1)); + + // Remove the duparray insn. + ELEM_REMOVE(&iobj->link); + return COMPILE_OK; + } + } + } + + if (IS_INSN_ID(iobj, send)) { const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0); const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1); #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt)) - if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) { + if (vm_ci_simple(ci)) { switch (vm_ci_argc(ci)) { case 0: switch (vm_ci_mid(ci)) { @@ -3594,7 +4403,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) } } - if ((vm_ci_flag(ci) & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) { + if ((vm_ci_flag(ci) & (VM_CALL_ARGS_BLOCKARG | VM_CALL_FORWARDING)) == 0 && blockiseq == NULL) { iobj->insn_id = BIN(opt_send_without_block); iobj->operand_size = insn_len(iobj->insn_id) - 1; } @@ -3630,15 +4439,25 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization; const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction; const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification; + const int do_without_ints = ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_WITHOUT_INTERRUPTS; int rescue_level = 0; int tailcallopt = do_tailcallopt; list = FIRST_ELEMENT(anchor); int do_block_optimization = 0; + LABEL * block_loop_label = NULL; - if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK && !ISEQ_BODY(iseq)->catch_except_p) { + // If we're optimizing a block + if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) { do_block_optimization = 1; + + // If the block starts with a nop and a label, + // record the label so we can detect if it's a jump target + LINK_ELEMENT * le = FIRST_ELEMENT(anchor)->next; + if (IS_INSN(le) && IS_INSN_ID((INSN *)le, nop) && IS_LABEL(le->next)) { + block_loop_label = (LABEL *)le->next; + } } while (list) { @@ -3653,11 +4472,45 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) insn_operands_unification((INSN *)list); } + if (do_without_ints) { + INSN *item = (INSN *)list; + if (IS_INSN_ID(item, jump)) { + item->insn_id = BIN(jump_without_ints); + } + else if (IS_INSN_ID(item, branchif)) { + item->insn_id = BIN(branchif_without_ints); + } + else if (IS_INSN_ID(item, branchunless)) { + item->insn_id = BIN(branchunless_without_ints); + } + else if (IS_INSN_ID(item, branchnil)) { + item->insn_id = BIN(branchnil_without_ints); + } + } + if (do_block_optimization) { INSN * item = (INSN *)list; - if (IS_INSN_ID(item, jump)) { + // Give up if there is a throw + if (IS_INSN_ID(item, throw)) { do_block_optimization = 0; } + else { + // If the instruction has a jump target, check if the + // jump target is the block loop label + const char *types = insn_op_types(item->insn_id); + for (int j = 0; types[j]; j++) { + if (types[j] == TS_OFFSET) { + // If the jump target is equal to the block loop + // label, then we can't do the optimization because + // the leading `nop` instruction fires the block + // entry tracepoint + LABEL * target = (LABEL *)OPERAND_AT(item, j); + if (target == block_loop_label) { + do_block_optimization = 0; + } + } + } + } } } if (IS_LABEL(list)) { @@ -3702,7 +4555,7 @@ new_unified_insn(rb_iseq_t *iseq, } if (argc > 0) { - ptr = operands = compile_data_alloc2(iseq, sizeof(VALUE), argc); + ptr = operands = compile_data_alloc2_type(iseq, VALUE, argc); } /* copy operands */ @@ -3714,8 +4567,7 @@ new_unified_insn(rb_iseq_t *iseq, list = list->next; } - NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id); - return new_insn_core(iseq, &dummy_line_node, insn_id, argc, operands); + return new_insn_core(iseq, iobj->insn_info.line_no, iobj->insn_info.node_id, insn_id, argc, operands); } #endif @@ -3775,231 +4627,114 @@ iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) 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, const LINK_ELEMENT *anchor, INSN *iobj, int state) +all_string_result_p(const NODE *node) { - 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) { - BADINSN_DUMP(anchor, iobj, lobj); - COMPILE_ERROR(iseq, iobj->insn_info.line_no, - "insn_set_sc_state error: %d at "LABEL_FORMAT - ", %d expected\n", - lobj->sc_state, lobj->label_no, nstate); - return COMPILE_NG; - } - } - else { - lobj->sc_state = nstate; - } - if (insn_id == BIN(jump)) { - nstate = SCS_XX; - } - } - else if (insn_id == BIN(leave)) { - nstate = SCS_XX; + if (!node) return FALSE; + switch (nd_type(node)) { + case NODE_STR: case NODE_DSTR: case NODE_FILE: + return TRUE; + case NODE_IF: case NODE_UNLESS: + if (!RNODE_IF(node)->nd_body || !RNODE_IF(node)->nd_else) return FALSE; + if (all_string_result_p(RNODE_IF(node)->nd_body)) + return all_string_result_p(RNODE_IF(node)->nd_else); + return FALSE; + case NODE_AND: case NODE_OR: + if (!RNODE_AND(node)->nd_2nd) + return all_string_result_p(RNODE_AND(node)->nd_1st); + if (!all_string_result_p(RNODE_AND(node)->nd_1st)) + return FALSE; + return all_string_result_p(RNODE_AND(node)->nd_2nd); + default: + return FALSE; } - - return nstate; } +struct dstr_ctxt { + rb_iseq_t *const iseq; + LINK_ANCHOR *const ret; + VALUE lit; + const NODE *lit_node; + int cnt; + int dregx; +}; + static int -label_set_sc_state(LABEL *lobj, int state) +append_dstr_fragment(struct dstr_ctxt *args, const NODE *const node, rb_parser_string_t *str) { - if (lobj->sc_state != 0) { - if (lobj->sc_state != state) { - state = lobj->sc_state; + VALUE s = rb_str_new_mutable_parser_string(str); + if (args->dregx) { + VALUE error = rb_reg_check_preprocess(s); + if (!NIL_P(error)) { + COMPILE_ERROR(args->iseq, nd_line(node), "%" PRIsVALUE, error); + return COMPILE_NG; } } - else { - lobj->sc_state = state; + if (NIL_P(args->lit)) { + args->lit = s; + args->lit_node = node; } - - return state; -} - - -#endif - -static int -iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const 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) { - NODE dummy_line_node = generate_dummy_line_node(0, -1); - INSN *rpobj = - new_insn_body(iseq, &dummy_line_node, BIN(reput), 0); - - /* replace this insn */ - ELEM_REPLACE(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); - - ELEM_REMOVE(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: - COMPILE_ERROR(iseq, iobj->insn_info.line_no, - "unreachable"); - return COMPILE_NG; - } - /* remove useless pop */ - ELEM_REMOVE(list); - list = list->next; - goto redo_point; - } - default:; - /* none */ - } /* end of switch */ - normal_insn: - state = insn_set_sc_state(iseq, anchor, iobj, state); - break; - } - case ISEQ_ELEMENT_LABEL: - { - LABEL *lobj; - lobj = (LABEL *)list; - - state = label_set_sc_state(lobj, state); - } - default: - break; - } - list = list->next; + else { + rb_str_buf_append(args->lit, s); } -#endif return COMPILE_OK; } -static int -all_string_result_p(const NODE *node) +static void +flush_dstr_fragment(struct dstr_ctxt *args) { - if (!node) return FALSE; - switch (nd_type(node)) { - case NODE_STR: case NODE_DSTR: - return TRUE; - case NODE_IF: case NODE_UNLESS: - if (!node->nd_body || !node->nd_else) return FALSE; - if (all_string_result_p(node->nd_body)) - return all_string_result_p(node->nd_else); - return FALSE; - case NODE_AND: case NODE_OR: - if (!node->nd_2nd) - return all_string_result_p(node->nd_1st); - if (!all_string_result_p(node->nd_1st)) - return FALSE; - return all_string_result_p(node->nd_2nd); - default: - return FALSE; + if (!NIL_P(args->lit)) { + rb_iseq_t *iseq = args->iseq; + VALUE lit = args->lit; + args->lit = Qnil; + lit = rb_fstring(lit); + ADD_INSN1(args->ret, args->lit_node, putobject, lit); + RB_OBJ_WRITTEN(args->iseq, Qundef, lit); + args->cnt++; } } static int -compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp) +compile_dstr_fragments_0(struct dstr_ctxt *args, const NODE *const node) { - const NODE *list = node->nd_next; - VALUE lit = node->nd_lit; - LINK_ELEMENT *first_lit = 0; - int cnt = 0; + const struct RNode_LIST *list = RNODE_DSTR(node)->nd_next; + rb_parser_string_t *str = RNODE_DSTR(node)->string; - debugp_param("nd_lit", lit); - if (!NIL_P(lit)) { - cnt++; - if (!RB_TYPE_P(lit, T_STRING)) { - COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s", - rb_builtin_type_name(TYPE(lit))); - return COMPILE_NG; - } - lit = rb_fstring(lit); - ADD_INSN1(ret, node, putobject, lit); - RB_OBJ_WRITTEN(iseq, Qundef, lit); - if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret); + if (str) { + CHECK(append_dstr_fragment(args, node, str)); } while (list) { const NODE *const head = list->nd_head; if (nd_type_p(head, NODE_STR)) { - lit = rb_fstring(head->nd_lit); - ADD_INSN1(ret, head, putobject, lit); - RB_OBJ_WRITTEN(iseq, Qundef, lit); - lit = Qnil; + CHECK(append_dstr_fragment(args, node, RNODE_STR(head)->string)); + } + else if (nd_type_p(head, NODE_DSTR)) { + CHECK(compile_dstr_fragments_0(args, head)); } else { - CHECK(COMPILE(ret, "each string", head)); + flush_dstr_fragment(args); + rb_iseq_t *iseq = args->iseq; + CHECK(COMPILE(args->ret, "each string", head)); + args->cnt++; } - cnt++; - list = list->nd_next; - } - if (NIL_P(lit) && first_lit) { - ELEM_REMOVE(first_lit); - --cnt; + list = (struct RNode_LIST *)list->nd_next; } - *cntp = cnt; + return COMPILE_OK; +} + +static int +compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp, int dregx) +{ + struct dstr_ctxt args = { + .iseq = iseq, .ret = ret, + .lit = Qnil, .lit_node = NULL, + .cnt = 0, .dregx = dregx, + }; + CHECK(compile_dstr_fragments_0(&args, node)); + flush_dstr_fragment(&args); + + *cntp = args.cnt; return COMPILE_OK; } @@ -4008,12 +4743,12 @@ static int compile_block(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped) { while (node && nd_type_p(node, NODE_BLOCK)) { - CHECK(COMPILE_(ret, "BLOCK body", node->nd_head, - (node->nd_next ? 1 : popped))); - node = node->nd_next; + CHECK(COMPILE_(ret, "BLOCK body", RNODE_BLOCK(node)->nd_head, + (RNODE_BLOCK(node)->nd_next ? 1 : popped))); + node = RNODE_BLOCK(node)->nd_next; } if (node) { - CHECK(COMPILE_(ret, "BLOCK next", node->nd_next, popped)); + CHECK(COMPILE_(ret, "BLOCK next", RNODE_BLOCK(node)->nd_next, popped)); } return COMPILE_OK; } @@ -4022,24 +4757,43 @@ static int compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node) { int cnt; - if (!node->nd_next) { - VALUE lit = rb_fstring(node->nd_lit); - ADD_INSN1(ret, node, putstring, lit); + if (!RNODE_DSTR(node)->nd_next) { + VALUE lit = rb_node_dstr_string_val(node); + ADD_INSN1(ret, node, dupstring, lit); + RB_OBJ_SET_SHAREABLE(lit); RB_OBJ_WRITTEN(iseq, Qundef, lit); } else { - CHECK(compile_dstr_fragments(iseq, ret, node, &cnt)); + CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, FALSE)); ADD_INSN1(ret, node, concatstrings, INT2FIX(cnt)); } return COMPILE_OK; } static int -compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node) +compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { int cnt; - CHECK(compile_dstr_fragments(iseq, ret, node, &cnt)); - ADD_INSN2(ret, node, toregexp, INT2FIX(node->nd_cflag), INT2FIX(cnt)); + int cflag = (int)RNODE_DREGX(node)->as.nd_cflag; + + if (!RNODE_DREGX(node)->nd_next) { + if (!popped) { + VALUE src = rb_node_dregx_string_val(node); + VALUE match = rb_reg_compile(src, cflag, NULL, 0); + RB_OBJ_SET_SHAREABLE(match); + ADD_INSN1(ret, node, putobject, match); + RB_OBJ_WRITTEN(iseq, Qundef, match); + } + return COMPILE_OK; + } + + CHECK(compile_dstr_fragments(iseq, ret, node, &cnt, TRUE)); + ADD_INSN2(ret, node, toregexp, INT2FIX(cflag), INT2FIX(cnt)); + + if (popped) { + ADD_INSN(ret, node, pop); + } + return COMPILE_OK; } @@ -4057,7 +4811,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod ADD_INSNL(ret, node, branchif, lend); /* *flip == 0 */ - CHECK(COMPILE(ret, "flip2 beg", node->nd_beg)); + CHECK(COMPILE(ret, "flip2 beg", RNODE_FLIP2(node)->nd_beg)); ADD_INSNL(ret, node, branchunless, else_label); ADD_INSN1(ret, node, putobject, Qtrue); ADD_INSN1(ret, node, setspecial, key); @@ -4067,7 +4821,7 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod /* *flip == 1 */ ADD_LABEL(ret, lend); - CHECK(COMPILE(ret, "flip2 end", node->nd_end)); + CHECK(COMPILE(ret, "flip2 end", RNODE_FLIP2(node)->nd_end)); ADD_INSNL(ret, node, branchunless, then_label); ADD_INSN1(ret, node, putobject, Qfalse); ADD_INSN1(ret, node, setspecial, key); @@ -4077,40 +4831,75 @@ compile_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod } static int -compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond, +compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond, + LABEL *then_label, LABEL *else_label); + +#define COMPILE_SINGLE 2 +static int +compile_logical(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond, + LABEL *then_label, LABEL *else_label) +{ + DECL_ANCHOR(seq); + INIT_ANCHOR(seq); + LABEL *label = NEW_LABEL(nd_line(cond)); + if (!then_label) then_label = label; + else if (!else_label) else_label = label; + + CHECK(compile_branch_condition(iseq, seq, cond, then_label, else_label)); + + if (LIST_INSN_SIZE_ONE(seq)) { + INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(seq)); + if (insn->insn_id == BIN(jump) && (LABEL *)(insn->operands[0]) == label) + return COMPILE_OK; + } + if (!label->refcnt) { + return COMPILE_SINGLE; + } + ADD_LABEL(seq, label); + ADD_SEQ(ret, seq); + return COMPILE_OK; +} + +static int +compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond, LABEL *then_label, LABEL *else_label) { + int ok; + DECL_ANCHOR(ignore); + again: switch (nd_type(cond)) { case NODE_AND: - { - LABEL *label = NEW_LABEL(nd_line(cond)); - CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label, - else_label)); - if (!label->refcnt) { - ADD_INSN(ret, cond, putnil); - break; - } - ADD_LABEL(ret, label); - cond = cond->nd_2nd; - goto again; - } + CHECK(ok = compile_logical(iseq, ret, RNODE_AND(cond)->nd_1st, NULL, else_label)); + cond = RNODE_AND(cond)->nd_2nd; + if (ok == COMPILE_SINGLE) { + ADD_INSNL(ret, cond, jump, else_label); + INIT_ANCHOR(ignore); + ret = ignore; + then_label = NEW_LABEL(nd_line(cond)); + } + goto again; case NODE_OR: - { - LABEL *label = NEW_LABEL(nd_line(cond)); - CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label, - label)); - if (!label->refcnt) { - ADD_INSN(ret, cond, putnil); - break; - } - ADD_LABEL(ret, label); - cond = cond->nd_2nd; - goto again; - } - case NODE_LIT: /* NODE_LIT is always true */ + CHECK(ok = compile_logical(iseq, ret, RNODE_OR(cond)->nd_1st, then_label, NULL)); + cond = RNODE_OR(cond)->nd_2nd; + if (ok == COMPILE_SINGLE) { + ADD_INSNL(ret, cond, jump, then_label); + INIT_ANCHOR(ignore); + ret = ignore; + else_label = NEW_LABEL(nd_line(cond)); + } + goto again; + case NODE_SYM: + case NODE_LINE: + case NODE_FILE: + case NODE_ENCODING: + case NODE_INTEGER: /* NODE_INTEGER is always true */ + case NODE_FLOAT: /* NODE_FLOAT is always true */ + case NODE_RATIONAL: /* NODE_RATIONAL is always true */ + case NODE_IMAGINARY: /* NODE_IMAGINARY is always true */ case NODE_TRUE: case NODE_STR: + case NODE_REGX: case NODE_ZLIST: case NODE_LAMBDA: /* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */ @@ -4135,10 +4924,31 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *co CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label)); return COMPILE_OK; case NODE_DEFINED: - CHECK(compile_defined_expr(iseq, ret, cond, Qfalse)); + CHECK(compile_defined_expr(iseq, ret, cond, Qfalse, ret == ignore)); break; default: - CHECK(COMPILE(ret, "branch condition", cond)); + { + DECL_ANCHOR(cond_seq); + INIT_ANCHOR(cond_seq); + + CHECK(COMPILE(cond_seq, "branch condition", cond)); + + if (LIST_INSN_SIZE_ONE(cond_seq)) { + INSN *insn = (INSN *)ELEM_FIRST_INSN(FIRST_ELEMENT(cond_seq)); + if (insn->insn_id == BIN(putobject)) { + if (RTEST(insn->operands[0])) { + ADD_INSNL(ret, cond, jump, then_label); + // maybe unreachable + return COMPILE_OK; + } + else { + ADD_INSNL(ret, cond, jump, else_label); + return COMPILE_OK; + } + } + } + ADD_SEQ(ret, cond_seq); + } break; } @@ -4152,33 +4962,68 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *co static int keyword_node_p(const NODE *const node) { - return nd_type_p(node, NODE_HASH) && (node->nd_brace & HASH_BRACE) != HASH_BRACE; + return nd_type_p(node, NODE_HASH) && (RNODE_HASH(node)->nd_brace & HASH_BRACE) != HASH_BRACE; +} + +static VALUE +get_symbol_value(rb_iseq_t *iseq, const NODE *node) +{ + switch (nd_type(node)) { + case NODE_SYM: + return rb_node_sym_string_val(node); + default: + UNKNOWN_NODE("get_symbol_value", node, Qnil); + } +} + +static VALUE +node_hash_unique_key_index(rb_iseq_t *iseq, rb_node_hash_t *node_hash, int *count_ptr) +{ + NODE *node = node_hash->nd_head; + VALUE hash = rb_hash_new(); + VALUE ary = rb_ary_new(); + + for (int i = 0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { + VALUE key = get_symbol_value(iseq, RNODE_LIST(node)->nd_head); + VALUE idx = rb_hash_aref(hash, key); + if (!NIL_P(idx)) { + rb_ary_store(ary, FIX2INT(idx), Qfalse); + (*count_ptr)--; + } + rb_hash_aset(hash, key, INT2FIX(i)); + rb_ary_store(ary, i, Qtrue); + (*count_ptr)++; + } + + return ary; } static int compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, - const NODE *const root_node, - struct rb_callinfo_kwarg **const kw_arg_ptr, - unsigned int *flag) + const NODE *const root_node, + struct rb_callinfo_kwarg **const kw_arg_ptr, + unsigned int *flag) { - if (kw_arg_ptr == NULL) return FALSE; + RUBY_ASSERT(nd_type_p(root_node, NODE_HASH)); + RUBY_ASSERT(kw_arg_ptr != NULL); + RUBY_ASSERT(flag != NULL); - if (root_node->nd_head && nd_type_p(root_node->nd_head, NODE_LIST)) { - const NODE *node = root_node->nd_head; + if (RNODE_HASH(root_node)->nd_head && nd_type_p(RNODE_HASH(root_node)->nd_head, NODE_LIST)) { + const NODE *node = RNODE_HASH(root_node)->nd_head; int seen_nodes = 0; while (node) { - const NODE *key_node = node->nd_head; + const NODE *key_node = RNODE_LIST(node)->nd_head; seen_nodes++; - assert(nd_type_p(node, NODE_LIST)); - if (key_node && nd_type_p(key_node, NODE_LIT) && SYMBOL_P(key_node->nd_lit)) { + RUBY_ASSERT(nd_type_p(node, NODE_LIST)); + if (key_node && nd_type_p(key_node, NODE_SYM)) { /* can be keywords */ } else { if (flag) { *flag |= VM_CALL_KW_SPLAT; - if (seen_nodes > 1 || node->nd_next->nd_next) { + if (seen_nodes > 1 || RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { /* A new hash will be created for the keyword arguments * in this case, so mark the method as passing mutable * keyword splat. @@ -4188,29 +5033,37 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, } return FALSE; } - node = node->nd_next; /* skip value node */ - node = node->nd_next; + node = RNODE_LIST(node)->nd_next; /* skip value node */ + node = RNODE_LIST(node)->nd_next; } /* may be keywords */ - node = root_node->nd_head; + node = RNODE_HASH(root_node)->nd_head; { - int len = (int)node->nd_alen / 2; + int len = 0; + VALUE key_index = node_hash_unique_key_index(iseq, RNODE_HASH(root_node), &len); struct rb_callinfo_kwarg *kw_arg = rb_xmalloc_mul_add(len, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg)); VALUE *keywords = kw_arg->keywords; int i = 0; + int j = 0; + kw_arg->references = 0; kw_arg->keyword_len = len; *kw_arg_ptr = kw_arg; - for (i=0; node != NULL; i++, node = node->nd_next->nd_next) { - const NODE *key_node = node->nd_head; - const NODE *val_node = node->nd_next->nd_head; - keywords[i] = key_node->nd_lit; - NO_CHECK(COMPILE(ret, "keyword values", val_node)); + for (i=0; node != NULL; i++, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { + const NODE *key_node = RNODE_LIST(node)->nd_head; + const NODE *val_node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head; + int popped = TRUE; + if (rb_ary_entry(key_index, i)) { + keywords[j] = get_symbol_value(iseq, key_node); + j++; + popped = FALSE; + } + NO_CHECK(COMPILE_(ret, "keyword values", val_node, popped)); } - assert(i == len); + RUBY_ASSERT(j == len); return TRUE; } } @@ -4218,43 +5071,52 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, } static int -compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, - struct rb_callinfo_kwarg **keywords_ptr, unsigned int *flag) +compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **kwnode_ptr) { int len = 0; - for (; node; len++, node = node->nd_next) { + for (; node; len++, node = RNODE_LIST(node)->nd_next) { if (CPDEBUG > 0) { EXPECT_NODE("compile_args", node, NODE_LIST, -1); } - if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node */ - if (compile_keyword_arg(iseq, ret, node->nd_head, keywords_ptr, flag)) { - len--; - } - else { - compile_hash(iseq, ret, node->nd_head, TRUE, FALSE); - } + if (RNODE_LIST(node)->nd_next == NULL && keyword_node_p(RNODE_LIST(node)->nd_head)) { /* last node is kwnode */ + *kwnode_ptr = RNODE_LIST(node)->nd_head; } else { - NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE)); + RUBY_ASSERT(!keyword_node_p(RNODE_LIST(node)->nd_head)); + NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, FALSE)); } } return len; } -static inline int -static_literal_node_p(const NODE *node, const rb_iseq_t *iseq) +static inline bool +frozen_string_literal_p(const rb_iseq_t *iseq) +{ + return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal > 0; +} + +static inline bool +static_literal_node_p(const NODE *node, const rb_iseq_t *iseq, bool hash_key) { switch (nd_type(node)) { - case NODE_LIT: + case NODE_SYM: + case NODE_REGX: + case NODE_LINE: + case NODE_ENCODING: + case NODE_INTEGER: + case NODE_FLOAT: + case NODE_RATIONAL: + case NODE_IMAGINARY: case NODE_NIL: case NODE_TRUE: case NODE_FALSE: return TRUE; case NODE_STR: - return ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal; + case NODE_FILE: + return hash_key || frozen_string_literal_p(iseq); default: return FALSE; } @@ -4264,30 +5126,54 @@ static inline VALUE static_literal_value(const NODE *node, rb_iseq_t *iseq) { switch (nd_type(node)) { + case NODE_INTEGER: + { + VALUE lit = rb_node_integer_literal_val(node); + if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit); + return lit; + } + case NODE_FLOAT: + { + VALUE lit = rb_node_float_literal_val(node); + if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit); + return lit; + } + case NODE_RATIONAL: + return rb_ractor_make_shareable(rb_node_rational_literal_val(node)); + case NODE_IMAGINARY: + return rb_ractor_make_shareable(rb_node_imaginary_literal_val(node)); case NODE_NIL: return Qnil; case NODE_TRUE: return Qtrue; case NODE_FALSE: return Qfalse; + case NODE_SYM: + return rb_node_sym_string_val(node); + case NODE_REGX: + return RB_OBJ_SET_SHAREABLE(rb_node_regx_string_val(node)); + case NODE_LINE: + return rb_node_line_lineno_val(node); + case NODE_ENCODING: + return rb_node_encoding_val(node); + case NODE_FILE: case NODE_STR: if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) { - VALUE lit; - VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX((int)nd_line(node))); - lit = rb_str_dup(node->nd_lit); - rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info)); - return rb_str_freeze(lit); + VALUE lit = get_string_value(node); + VALUE str = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), (int)nd_line(node)); + RB_OBJ_SET_SHAREABLE(str); + return str; } else { - return rb_fstring(node->nd_lit); + return get_string_value(node); } default: - return node->nd_lit; + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); } } static int -compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped) +compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped, bool first_chunk) { const NODE *line_node = node; @@ -4301,8 +5187,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop EXPECT_NODE("compile_array", node, NODE_LIST, -1); if (popped) { - for (; node; node = node->nd_next) { - NO_CHECK(COMPILE_(ret, "array element", node->nd_head, popped)); + for (; node; node = RNODE_LIST(node)->nd_next) { + NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, popped)); } return 1; } @@ -4322,8 +5208,8 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop * * [x1,x2,...,x10000] => * push x1 ; push x2 ; ...; push x256; newarray 256; - * push x257; push x258; ...; push x512; newarray 256; concatarray; - * push x513; push x514; ...; push x768; newarray 256; concatarray; + * push x257; push x258; ...; push x512; pushtoarray 256; + * push x513; push x514; ...; push x768; pushtoarray 256; * ... * * - Long subarray can be optimized by pre-allocating a hidden array. @@ -4333,38 +5219,38 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop * * [x, 1,2,3,...,100, z] => * push x; newarray 1; - * putobject [1,2,3,...,100] (<- hidden array); concatarray; - * push z; newarray 1; concatarray + * putobject [1,2,3,...,100] (<- hidden array); concattoarray; + * push z; pushtoarray 1; * - * - If the last element is a keyword, newarraykwsplat should be emitted - * to check and remove empty keyword arguments hash from array. + * - If the last element is a keyword, pushtoarraykwsplat should be emitted + * to only push it onto the array if it is not empty * (Note: a keyword is NODE_HASH which is not static_literal_node_p.) * * [1,2,3,**kw] => - * putobject 1; putobject 2; putobject 3; push kw; newarraykwsplat + * putobject 1; putobject 2; putobject 3; newarray 3; ...; pushtoarraykwsplat kw */ const int max_stack_len = 0x100; const int min_tmp_ary_len = 0x40; int stack_len = 0; - int first_chunk = 1; - /* Convert pushed elements to an array, and concatarray if needed */ -#define FLUSH_CHUNK(newarrayinsn) \ + /* Either create a new array, or push to the existing array */ +#define FLUSH_CHUNK \ if (stack_len) { \ - ADD_INSN1(ret, line_node, newarrayinsn, INT2FIX(stack_len)); \ - if (!first_chunk) ADD_INSN(ret, line_node, concatarray); \ - first_chunk = stack_len = 0; \ + if (first_chunk) ADD_INSN1(ret, line_node, newarray, INT2FIX(stack_len)); \ + else ADD_INSN1(ret, line_node, pushtoarray, INT2FIX(stack_len)); \ + first_chunk = FALSE; \ + stack_len = 0; \ } while (node) { int count = 1; /* pre-allocation check (this branch can be omittable) */ - if (static_literal_node_p(node->nd_head, iseq)) { + if (static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, false)) { /* count the elements that are optimizable */ - const NODE *node_tmp = node->nd_next; - for (; node_tmp && static_literal_node_p(node_tmp->nd_head, iseq); node_tmp = node_tmp->nd_next) + const NODE *node_tmp = RNODE_LIST(node)->nd_next; + for (; node_tmp && static_literal_node_p(RNODE_LIST(node_tmp)->nd_head, iseq, false); node_tmp = RNODE_LIST(node_tmp)->nd_next) count++; if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) { @@ -4372,72 +5258,62 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop VALUE ary = rb_ary_hidden_new(count); /* Create a hidden array */ - for (; count; count--, node = node->nd_next) - rb_ary_push(ary, static_literal_value(node->nd_head, iseq)); - OBJ_FREEZE(ary); + for (; count; count--, node = RNODE_LIST(node)->nd_next) + rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq)); + RB_OBJ_SET_FROZEN_SHAREABLE(ary); /* Emit optimized code */ - FLUSH_CHUNK(newarray); + FLUSH_CHUNK; if (first_chunk) { ADD_INSN1(ret, line_node, duparray, ary); - first_chunk = 0; + first_chunk = FALSE; } else { ADD_INSN1(ret, line_node, putobject, ary); - ADD_INSN(ret, line_node, concatarray); + ADD_INSN(ret, line_node, concattoarray); } + RB_OBJ_SET_SHAREABLE(ary); RB_OBJ_WRITTEN(iseq, Qundef, ary); } } /* Base case: Compile "count" elements */ - for (; count; count--, node = node->nd_next) { + for (; count; count--, node = RNODE_LIST(node)->nd_next) { if (CPDEBUG > 0) { EXPECT_NODE("compile_array", node, NODE_LIST, -1); } - NO_CHECK(COMPILE_(ret, "array element", node->nd_head, 0)); - stack_len++; - - if (!node->nd_next && keyword_node_p(node->nd_head)) { - /* Reached the end, and the last element is a keyword */ - FLUSH_CHUNK(newarraykwsplat); + if (!RNODE_LIST(node)->nd_next && keyword_node_p(RNODE_LIST(node)->nd_head)) { + /* Create array or push existing non-keyword elements onto array */ + if (stack_len == 0 && first_chunk) { + ADD_INSN1(ret, line_node, newarray, INT2FIX(0)); + } + else { + FLUSH_CHUNK; + } + NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0)); + ADD_INSN(ret, line_node, pushtoarraykwsplat); return 1; } + else { + NO_CHECK(COMPILE_(ret, "array element", RNODE_LIST(node)->nd_head, 0)); + stack_len++; + } /* If there are many pushed elements, flush them to avoid stack overflow */ - if (stack_len >= max_stack_len) FLUSH_CHUNK(newarray); + if (stack_len >= max_stack_len) FLUSH_CHUNK; } } - FLUSH_CHUNK(newarray); + FLUSH_CHUNK; #undef FLUSH_CHUNK return 1; } -/* Compile an array containing the single element represented by node */ -static int -compile_array_1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node) -{ - if (static_literal_node_p(node, iseq)) { - VALUE ary = rb_ary_hidden_new(1); - rb_ary_push(ary, static_literal_value(node, iseq)); - OBJ_FREEZE(ary); - - ADD_INSN1(ret, node, duparray, ary); - } - else { - CHECK(COMPILE_(ret, "array element", node, FALSE)); - ADD_INSN1(ret, node, newarray, INT2FIX(1)); - } - - return 1; -} - static inline int static_literal_node_pair_p(const NODE *node, const rb_iseq_t *iseq) { - return node->nd_head && static_literal_node_p(node->nd_head, iseq) && static_literal_node_p(node->nd_next->nd_head, iseq); + return RNODE_LIST(node)->nd_head && static_literal_node_p(RNODE_LIST(node)->nd_head, iseq, true) && static_literal_node_p(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq, false); } static int @@ -4445,7 +5321,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth { const NODE *line_node = node; - node = node->nd_head; + node = RNODE_HASH(node)->nd_head; if (!node || nd_type_p(node, NODE_ZLIST)) { if (!popped) { @@ -4457,8 +5333,8 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth EXPECT_NODE("compile_hash", node, NODE_LIST, -1); if (popped) { - for (; node; node = node->nd_next) { - NO_CHECK(COMPILE_(ret, "hash element", node->nd_head, popped)); + for (; node; node = RNODE_LIST(node)->nd_next) { + NO_CHECK(COMPILE_(ret, "hash element", RNODE_LIST(node)->nd_head, popped)); } return 1; } @@ -4511,8 +5387,8 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth /* pre-allocation check (this branch can be omittable) */ if (static_literal_node_pair_p(node, iseq)) { /* count the elements that are optimizable */ - const NODE *node_tmp = node->nd_next->nd_next; - for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = node_tmp->nd_next->nd_next) + const NODE *node_tmp = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; + for (; node_tmp && static_literal_node_pair_p(node_tmp, iseq); node_tmp = RNODE_LIST(RNODE_LIST(node_tmp)->nd_next)->nd_next) count++; if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_hash_len) { @@ -4520,16 +5396,18 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth VALUE ary = rb_ary_hidden_new(count); /* Create a hidden hash */ - for (; count; count--, node = node->nd_next->nd_next) { + for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { VALUE elem[2]; - elem[0] = static_literal_value(node->nd_head, iseq); - elem[1] = static_literal_value(node->nd_next->nd_head, iseq); + elem[0] = static_literal_value(RNODE_LIST(node)->nd_head, iseq); + if (!RB_SPECIAL_CONST_P(elem[0])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[0]); + elem[1] = static_literal_value(RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, iseq); + if (!RB_SPECIAL_CONST_P(elem[1])) RB_OBJ_SET_FROZEN_SHAREABLE(elem[1]); rb_ary_cat(ary, elem, 2); } - VALUE hash = rb_hash_new_with_size(RARRAY_LEN(ary) / 2); - rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR_TRANSIENT(ary), hash); - hash = rb_obj_hide(hash); - OBJ_FREEZE(hash); + VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2); + rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash); + RB_GC_GUARD(ary); + hash = RB_OBJ_SET_FROZEN_SHAREABLE(hash); /* Emit optimized code */ FLUSH_CHUNK(); @@ -4550,16 +5428,16 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth } /* Base case: Compile "count" elements */ - for (; count; count--, node = node->nd_next->nd_next) { + for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { if (CPDEBUG > 0) { EXPECT_NODE("compile_hash", node, NODE_LIST, -1); } - if (node->nd_head) { + if (RNODE_LIST(node)->nd_head) { /* Normal key-value pair */ - NO_CHECK(COMPILE_(anchor, "hash key element", node->nd_head, 0)); - NO_CHECK(COMPILE_(anchor, "hash value element", node->nd_next->nd_head, 0)); + NO_CHECK(COMPILE_(anchor, "hash key element", RNODE_LIST(node)->nd_head, 0)); + NO_CHECK(COMPILE_(anchor, "hash value element", RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head, 0)); stack_len += 2; /* If there are many pushed elements, flush them to avoid stack overflow */ @@ -4569,12 +5447,13 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth /* kwsplat case: foo(..., **kw, ...) */ FLUSH_CHUNK(); - const NODE *kw = node->nd_next->nd_head; - int empty_kw = nd_type_p(kw, NODE_LIT) && RB_TYPE_P(kw->nd_lit, T_HASH); /* foo( ..., **{}, ...) */ + const NODE *kw = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_head; + int empty_kw = nd_type_p(kw, NODE_HASH) && (!RNODE_HASH(kw)->nd_head); /* foo( ..., **{}, ...) */ int first_kw = first_chunk && stack_len == 0; /* foo(1,2,3, **kw, ...) */ - int last_kw = !node->nd_next->nd_next; /* foo( ..., **kw) */ + int last_kw = !RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next; /* foo( ..., **kw) */ int only_kw = last_kw && first_kw; /* foo(1,2,3, **kw) */ + empty_kw = empty_kw || nd_type_p(kw, NODE_NIL); /* foo( ..., **nil, ...) */ if (empty_kw) { if (only_kw && method_call_keywords) { /* **{} appears at the only keyword argument in method call, @@ -4634,29 +5513,34 @@ VALUE rb_node_case_when_optimizable_literal(const NODE *const node) { switch (nd_type(node)) { - case NODE_LIT: { - VALUE v = node->nd_lit; + case NODE_INTEGER: + return rb_node_integer_literal_val(node); + case NODE_FLOAT: { + VALUE v = rb_node_float_literal_val(node); double ival; - if (RB_FLOAT_TYPE_P(v) && - modf(RFLOAT_VALUE(v), &ival) == 0.0) { + + if (modf(RFLOAT_VALUE(v), &ival) == 0.0) { return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival); } - if (RB_TYPE_P(v, T_RATIONAL) || RB_TYPE_P(v, T_COMPLEX)) { - return Qundef; - } - if (SYMBOL_P(v) || rb_obj_is_kind_of(v, rb_cNumeric)) { - return v; - } - break; + return v; } + case NODE_RATIONAL: + case NODE_IMAGINARY: + return Qundef; case NODE_NIL: return Qnil; case NODE_TRUE: return Qtrue; case NODE_FALSE: return Qfalse; + case NODE_SYM: + return rb_node_sym_string_val(node); + case NODE_LINE: + return rb_node_line_lineno_val(node); case NODE_STR: - return rb_fstring(node->nd_lit); + return rb_node_str_string_val(node); + case NODE_FILE: + return rb_node_file_path_val(node); } return Qundef; } @@ -4666,19 +5550,19 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals, LABEL *l1, int only_special_literals, VALUE literals) { while (vals) { - const NODE *val = vals->nd_head; + const NODE *val = RNODE_LIST(vals)->nd_head; VALUE lit = rb_node_case_when_optimizable_literal(val); - if (lit == Qundef) { + if (UNDEF_P(lit)) { only_special_literals = 0; } - else if (NIL_P(rb_hash_lookup(literals, lit))) { - rb_hash_aset(literals, lit, (VALUE)(l1) | 1); + else { + cdhash_aset_if_missing(literals, lit, (VALUE)(l1)); } - if (nd_type_p(val, NODE_STR)) { - debugp_param("nd_lit", val->nd_lit); - lit = rb_fstring(val->nd_lit); + if (nd_type_p(val, NODE_STR) || nd_type_p(val, NODE_FILE)) { + debugp_param("nd_lit", get_string_value(val)); + lit = get_string_value(val); ADD_INSN1(cond_seq, val, putobject, lit); RB_OBJ_WRITTEN(iseq, Qundef, lit); } @@ -4686,11 +5570,11 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals, if (!COMPILE(cond_seq, "when cond", val)) return -1; } - // Emit patern === target + // Emit pattern === target ADD_INSN1(cond_seq, vals, topn, INT2FIX(1)); ADD_CALL(cond_seq, vals, idEqq, INT2FIX(1)); ADD_INSNL(cond_seq, val, branchif, l1); - vals = vals->nd_next; + vals = RNODE_LIST(vals)->nd_next; } return only_special_literals; } @@ -4708,19 +5592,19 @@ when_splat_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals, break; case NODE_SPLAT: ADD_INSN (cond_seq, line_node, dup); - CHECK(COMPILE(cond_seq, "when splat", vals->nd_head)); + CHECK(COMPILE(cond_seq, "when splat", RNODE_SPLAT(vals)->nd_head)); ADD_INSN1(cond_seq, line_node, splatarray, Qfalse); ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY)); ADD_INSNL(cond_seq, line_node, branchif, l1); break; case NODE_ARGSCAT: - CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals)); - CHECK(when_splat_vals(iseq, cond_seq, vals->nd_body, l1, only_special_literals, literals)); + CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_head, l1, only_special_literals, literals)); + CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSCAT(vals)->nd_body, l1, only_special_literals, literals)); break; case NODE_ARGSPUSH: - CHECK(when_splat_vals(iseq, cond_seq, vals->nd_head, l1, only_special_literals, literals)); + CHECK(when_splat_vals(iseq, cond_seq, RNODE_ARGSPUSH(vals)->nd_head, l1, only_special_literals, literals)); ADD_INSN (cond_seq, line_node, dup); - CHECK(COMPILE(cond_seq, "when argspush body", vals->nd_body)); + CHECK(COMPILE(cond_seq, "when argspush body", RNODE_ARGSPUSH(vals)->nd_body)); ADD_INSN1(cond_seq, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE)); ADD_INSNL(cond_seq, line_node, branchif, l1); break; @@ -4879,12 +5763,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node)); + bool safenav_call = false; LINK_ELEMENT *insn_element = LAST_ELEMENT(pre); iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */ ASSUME(iobj); - ELEM_REMOVE(LAST_ELEMENT(pre)); - ELEM_REMOVE((LINK_ELEMENT *)iobj); - pre->last = iobj->link.prev; + ELEM_REMOVE(insn_element); + if (!IS_INSN_ID(iobj, send)) { + safenav_call = true; + iobj = (INSN *)get_prev_insn(iobj); + ELEM_INSERT_NEXT(&iobj->link, insn_element); + } + (pre->last = iobj->link.prev)->next = 0; const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0); int argc = vm_ci_argc(ci) + 1; @@ -4903,18 +5792,46 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const return COMPILE_NG; } - ADD_ELEM(lhs, (LINK_ELEMENT *)iobj); + iobj->link.prev = lhs->last; + lhs->last->next = &iobj->link; + for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next); if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { int argc = vm_ci_argc(ci); + bool dupsplat = false; ci = ci_argc_set(iseq, ci, argc - 1); + if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) { + /* Given h[*a], _ = ary + * setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT + * `a` must be dupped, because it will be appended with ary[0] + * Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT + */ + dupsplat = true; + ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT); + } OPERAND_AT(iobj, 0) = (VALUE)ci; RB_OBJ_WRITTEN(iseq, Qundef, iobj); - INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1)); - INSERT_BEFORE_INSN(iobj, line_node, concatarray); + + /* Given: h[*a], h[*b, 1] = ary + * h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT, + * so this uses splatarray true on a to dup it before using pushtoarray + * h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT, + * so you can use pushtoarray directly + */ + int line_no = nd_line(line_node); + int node_id = nd_node_id(line_node); + + if (dupsplat) { + INSERT_BEFORE_INSN(iobj, line_no, node_id, swap); + INSERT_BEFORE_INSN1(iobj, line_no, node_id, splatarray, Qtrue); + INSERT_BEFORE_INSN(iobj, line_no, node_id, swap); + } + INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1)); } - ADD_INSN(lhs, line_node, pop); - if (argc != 1) { + if (!safenav_call) { ADD_INSN(lhs, line_node, pop); + if (argc != 1) { + ADD_INSN(lhs, line_node, pop); + } } for (int i=0; i < argc; i++) { ADD_INSN(post, line_node, pop); @@ -4940,7 +5857,7 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const break; } case NODE_CDECL: - if (!node->nd_vid) { + if (!RNODE_CDECL(node)->nd_vid) { /* Special handling only needed for expr::C, not for C */ INSN *iobj; @@ -4978,8 +5895,8 @@ static int compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn) { if (lhsn) { - CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next)); - CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, lhsn->nd_head, NULL, 0)); + CHECK(compile_massign_opt_lhs(iseq, ret, RNODE_LIST(lhsn)->nd_next)); + CHECK(compile_massign_lhs(iseq, ret, ret, ret, ret, RNODE_LIST(lhsn)->nd_head, NULL, 0)); } return COMPILE_OK; } @@ -5009,31 +5926,29 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret, } while (lhsn) { - const NODE *ln = lhsn->nd_head; + const NODE *ln = RNODE_LIST(lhsn)->nd_head; switch (nd_type(ln)) { case NODE_LASGN: - MEMORY(ln->nd_vid); - break; case NODE_DASGN: case NODE_IASGN: case NODE_CVASGN: - MEMORY(ln->nd_vid); + MEMORY(get_nd_vid(ln)); break; default: return 0; } - lhsn = lhsn->nd_next; + lhsn = RNODE_LIST(lhsn)->nd_next; llen++; } while (rhsn) { if (llen <= rlen) { - NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", rhsn->nd_head)); + NO_CHECK(COMPILE_POPPED(ret, "masgn val (popped)", RNODE_LIST(rhsn)->nd_head)); } else { - NO_CHECK(COMPILE(ret, "masgn val", rhsn->nd_head)); + NO_CHECK(COMPILE(ret, "masgn val", RNODE_LIST(rhsn)->nd_head)); } - rhsn = rhsn->nd_next; + rhsn = RNODE_LIST(rhsn)->nd_next; rlen++; } @@ -5050,32 +5965,31 @@ compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret, static int compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs, LINK_ANCHOR *const lhs, LINK_ANCHOR *const post, const NODE *const node, struct masgn_state *state, int popped) { - const NODE *rhsn = node->nd_value; - const NODE *splatn = node->nd_args; - const NODE *lhsn = node->nd_head; + const NODE *rhsn = RNODE_MASGN(node)->nd_value; + const NODE *splatn = RNODE_MASGN(node)->nd_args; + const NODE *lhsn = RNODE_MASGN(node)->nd_head; const NODE *lhsn_count = lhsn; int lhs_splat = (splatn && NODE_NAMED_REST_P(splatn)) ? 1 : 0; int llen = 0; int lpos = 0; - int expand = 1; while (lhsn_count) { llen++; - lhsn_count = lhsn_count->nd_next; + lhsn_count = RNODE_LIST(lhsn_count)->nd_next; } while (lhsn) { - CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, lhsn->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level)); + CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(lhsn)->nd_head, state, (llen - lpos) + lhs_splat + state->lhs_level)); lpos++; - lhsn = lhsn->nd_next; + lhsn = RNODE_LIST(lhsn)->nd_next; } if (lhs_splat) { if (nd_type_p(splatn, NODE_POSTARG)) { /*a, b, *r, p1, p2 */ - const NODE *postn = splatn->nd_2nd; - const NODE *restn = splatn->nd_1st; - int plen = (int)postn->nd_alen; + const NODE *postn = RNODE_POSTARG(splatn)->nd_2nd; + const NODE *restn = RNODE_POSTARG(splatn)->nd_1st; + int plen = (int)RNODE_LIST(postn)->as.nd_alen; int ppos = 0; int flag = 0x02 | (NODE_NAMED_REST_P(restn) ? 0x01 : 0x00); @@ -5085,9 +5999,9 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, restn, state, 1 + plen + state->lhs_level)); } while (postn) { - CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, postn->nd_head, state, (plen - ppos) + state->lhs_level)); + CHECK(compile_massign_lhs(iseq, pre, rhs, lhs, post, RNODE_LIST(postn)->nd_head, state, (plen - ppos) + state->lhs_level)); ppos++; - postn = postn->nd_next; + postn = RNODE_LIST(postn)->nd_next; } } else { @@ -5096,7 +6010,6 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs } } - if (!state->nested) { NO_CHECK(COMPILE(rhs, "normal masgn rhs", rhsn)); } @@ -5104,16 +6017,14 @@ compile_massign0(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const rhs if (!popped) { ADD_INSN(rhs, node, dup); } - if (expand) { - ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat)); - } + ADD_INSN2(rhs, node, expandarray, INT2FIX(llen), INT2FIX(lhs_splat)); return COMPILE_OK; } static int compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { - if (!popped || node->nd_args || !compile_massign_opt(iseq, ret, node->nd_value, node->nd_head)) { + if (!popped || RNODE_MASGN(node)->nd_args || !compile_massign_opt(iseq, ret, RNODE_MASGN(node)->nd_value, RNODE_MASGN(node)->nd_head)) { struct masgn_state state; state.lhs_level = popped ? 0 : 1; state.nested = 0; @@ -5135,7 +6046,7 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, while (memo) { VALUE topn_arg = INT2FIX((state.num_args - memo->argn) + memo->lhs_pos); for (int i = 0; i < memo->num_args; i++) { - INSERT_BEFORE_INSN1(memo->before_insn, memo->line_node, topn, topn_arg); + INSERT_BEFORE_INSN1(memo->before_insn, nd_line(memo->line_node), nd_node_id(memo->line_node), topn, topn_arg); } tmp_memo = memo->next; free(memo); @@ -5155,28 +6066,53 @@ compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, return COMPILE_OK; } +static VALUE +collect_const_segments(rb_iseq_t *iseq, const NODE *node) +{ + VALUE arr = rb_ary_new(); + for (;;) { + switch (nd_type(node)) { + case NODE_CONST: + rb_ary_unshift(arr, ID2SYM(RNODE_CONST(node)->nd_vid)); + RB_OBJ_SET_SHAREABLE(arr); + return arr; + case NODE_COLON3: + rb_ary_unshift(arr, ID2SYM(RNODE_COLON3(node)->nd_mid)); + rb_ary_unshift(arr, ID2SYM(idNULL)); + RB_OBJ_SET_SHAREABLE(arr); + return arr; + case NODE_COLON2: + rb_ary_unshift(arr, ID2SYM(RNODE_COLON2(node)->nd_mid)); + node = RNODE_COLON2(node)->nd_head; + break; + default: + return Qfalse; + } + } +} + static int compile_const_prefix(rb_iseq_t *iseq, const NODE *const node, LINK_ANCHOR *const pref, LINK_ANCHOR *const body) { switch (nd_type(node)) { case NODE_CONST: - debugi("compile_const_prefix - colon", node->nd_vid); + debugi("compile_const_prefix - colon", RNODE_CONST(node)->nd_vid); ADD_INSN1(body, node, putobject, Qtrue); - ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_vid)); + ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid)); break; case NODE_COLON3: - debugi("compile_const_prefix - colon3", node->nd_mid); + debugi("compile_const_prefix - colon3", RNODE_COLON3(node)->nd_mid); ADD_INSN(body, node, pop); ADD_INSN1(body, node, putobject, rb_cObject); ADD_INSN1(body, node, putobject, Qtrue); - ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid)); + ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid)); break; case NODE_COLON2: - CHECK(compile_const_prefix(iseq, node->nd_head, pref, body)); - debugi("compile_const_prefix - colon2", node->nd_mid); + CHECK(compile_const_prefix(iseq, RNODE_COLON2(node)->nd_head, pref, body)); + debugi("compile_const_prefix - colon2", RNODE_COLON2(node)->nd_mid); ADD_INSN1(body, node, putobject, Qfalse); - ADD_INSN1(body, node, getconstant, ID2SYM(node->nd_mid)); + ADD_INSN1(body, node, getconstant, ID2SYM(RNODE_COLON2(node)->nd_mid)); break; default: CHECK(COMPILE(pref, "const colon2 prefix", node)); @@ -5186,6 +6122,23 @@ compile_const_prefix(rb_iseq_t *iseq, const NODE *const node, } static int +cpath_const_p(const NODE *node) +{ + switch (nd_type(node)) { + case NODE_CONST: + case NODE_COLON3: + return TRUE; + case NODE_COLON2: + if (RNODE_COLON2(node)->nd_head) { + return cpath_const_p(RNODE_COLON2(node)->nd_head); + } + return TRUE; + default: + return FALSE; + } +} + +static int compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath) { if (nd_type_p(cpath, NODE_COLON3)) { @@ -5193,10 +6146,14 @@ compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath) ADD_INSN1(ret, cpath, putobject, rb_cObject); return VM_DEFINECLASS_FLAG_SCOPED; } - else if (cpath->nd_head) { - /* Bar::Foo */ - NO_CHECK(COMPILE(ret, "nd_else->nd_head", cpath->nd_head)); - return VM_DEFINECLASS_FLAG_SCOPED; + else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) { + /* Bar::Foo or expr::Foo */ + NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head)); + int flags = VM_DEFINECLASS_FLAG_SCOPED; + if (!cpath_const_p(RNODE_COLON2(cpath)->nd_head)) { + flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF; + } + return flags; } else { /* class at cbase Foo */ @@ -5209,16 +6166,16 @@ compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath) static inline int private_recv_p(const NODE *node) { - if (nd_type_p(node->nd_recv, NODE_SELF)) { - NODE *self = node->nd_recv; - return self->nd_state != 0; + NODE *recv = get_nd_recv(node); + if (recv && nd_type_p(recv, NODE_SELF)) { + return RNODE_SELF(recv)->nd_state != 0; } return 0; } static void defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, - const NODE *const node, LABEL **lfinish, VALUE needstr); + const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore); static int compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const enum node_type type, const NODE *const line_node, int popped, bool assume_receiver); @@ -5249,21 +6206,34 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, expr_type = DEFINED_FALSE; break; + case NODE_HASH: case NODE_LIST:{ - const NODE *vals = node; + const NODE *vals = (nd_type(node) == NODE_HASH) ? RNODE_HASH(node)->nd_head : node; - do { - defined_expr0(iseq, ret, vals->nd_head, lfinish, Qfalse, false); + if (vals) { + do { + if (RNODE_LIST(vals)->nd_head) { + defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false); - if (!lfinish[1]) { - lfinish[1] = NEW_LABEL(line); - } - ADD_INSNL(ret, line_node, branchunless, lfinish[1]); - } while ((vals = vals->nd_next) != NULL); + if (!lfinish[1]) { + lfinish[1] = NEW_LABEL(line); + } + ADD_INSNL(ret, line_node, branchunless, lfinish[1]); + } + } while ((vals = RNODE_LIST(vals)->nd_next) != NULL); + } } /* fall through */ case NODE_STR: - case NODE_LIT: + case NODE_SYM: + case NODE_REGX: + case NODE_LINE: + case NODE_FILE: + case NODE_ENCODING: + case NODE_INTEGER: + case NODE_FLOAT: + case NODE_RATIONAL: + case NODE_IMAGINARY: case NODE_ZLIST: case NODE_AND: case NODE_OR: @@ -5271,6 +6241,15 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, expr_type = DEFINED_EXPR; break; + case NODE_SPLAT: + defined_expr0(iseq, ret, RNODE_LIST(node)->nd_head, lfinish, Qfalse, false); + if (!lfinish[1]) { + lfinish[1] = NEW_LABEL(line); + } + ADD_INSNL(ret, line_node, branchunless, lfinish[1]); + expr_type = DEFINED_EXPR; + break; + /* variables */ case NODE_LVAR: case NODE_DVAR: @@ -5279,49 +6258,48 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, #define PUSH_VAL(type) (needstr == Qfalse ? Qtrue : rb_iseq_defined_string(type)) case NODE_IVAR: - ADD_INSN(ret, line_node, putnil); - ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_IVAR), - ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_IVAR)); + ADD_INSN3(ret, line_node, definedivar, + ID2SYM(RNODE_IVAR(node)->nd_vid), get_ivar_ic_value(iseq,RNODE_IVAR(node)->nd_vid), PUSH_VAL(DEFINED_IVAR)); return; case NODE_GVAR: ADD_INSN(ret, line_node, putnil); ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_GVAR), - ID2SYM(node->nd_entry), PUSH_VAL(DEFINED_GVAR)); + ID2SYM(RNODE_GVAR(node)->nd_vid), PUSH_VAL(DEFINED_GVAR)); return; case NODE_CVAR: ADD_INSN(ret, line_node, putnil); ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CVAR), - ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CVAR)); + ID2SYM(RNODE_CVAR(node)->nd_vid), PUSH_VAL(DEFINED_CVAR)); return; case NODE_CONST: ADD_INSN(ret, line_node, putnil); ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST), - ID2SYM(node->nd_vid), PUSH_VAL(DEFINED_CONST)); + ID2SYM(RNODE_CONST(node)->nd_vid), PUSH_VAL(DEFINED_CONST)); return; case NODE_COLON2: if (!lfinish[1]) { lfinish[1] = NEW_LABEL(line); } - defined_expr0(iseq, ret, node->nd_head, lfinish, Qfalse, false); + defined_expr0(iseq, ret, RNODE_COLON2(node)->nd_head, lfinish, Qfalse, false); ADD_INSNL(ret, line_node, branchunless, lfinish[1]); - NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", node->nd_head)); + NO_CHECK(COMPILE(ret, "defined/colon2#nd_head", RNODE_COLON2(node)->nd_head)); - if (rb_is_const_id(node->nd_mid)) { + if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) { ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_CONST_FROM), - ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST)); + ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_CONST)); } else { ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD), - ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD)); + ID2SYM(RNODE_COLON2(node)->nd_mid), PUSH_VAL(DEFINED_METHOD)); } return; case NODE_COLON3: ADD_INSN1(ret, line_node, putobject, rb_cObject); ADD_INSN3(ret, line_node, defined, - INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_CONST)); + INT2FIX(DEFINED_CONST_FROM), ID2SYM(RNODE_COLON3(node)->nd_mid), PUSH_VAL(DEFINED_CONST)); return; /* method dispatch */ @@ -5334,7 +6312,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, (type == NODE_CALL || type == NODE_OPCALL || (type == NODE_ATTRASGN && !private_recv_p(node))); - if (node->nd_args || explicit_receiver) { + if (get_nd_args(node) || explicit_receiver) { if (!lfinish[1]) { lfinish[1] = NEW_LABEL(line); } @@ -5342,31 +6320,31 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, lfinish[2] = NEW_LABEL(line); } } - if (node->nd_args) { - defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse, false); + if (get_nd_args(node)) { + defined_expr0(iseq, ret, get_nd_args(node), lfinish, Qfalse, false); ADD_INSNL(ret, line_node, branchunless, lfinish[1]); } if (explicit_receiver) { - defined_expr0(iseq, ret, node->nd_recv, lfinish, Qfalse, true); - switch (nd_type(node->nd_recv)) { + defined_expr0(iseq, ret, get_nd_recv(node), lfinish, Qfalse, true); + switch (nd_type(get_nd_recv(node))) { case NODE_CALL: case NODE_OPCALL: case NODE_VCALL: case NODE_FCALL: case NODE_ATTRASGN: ADD_INSNL(ret, line_node, branchunless, lfinish[2]); - compile_call(iseq, ret, node->nd_recv, nd_type(node->nd_recv), line_node, 0, true); + compile_call(iseq, ret, get_nd_recv(node), nd_type(get_nd_recv(node)), line_node, 0, true); break; default: ADD_INSNL(ret, line_node, branchunless, lfinish[1]); - NO_CHECK(COMPILE(ret, "defined/recv", node->nd_recv)); + NO_CHECK(COMPILE(ret, "defined/recv", get_nd_recv(node))); break; } if (keep_result) { ADD_INSN(ret, line_node, dup); } ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_METHOD), - ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD)); + ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD)); } else { ADD_INSN(ret, line_node, putself); @@ -5374,7 +6352,7 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, ADD_INSN(ret, line_node, dup); } ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_FUNC), - ID2SYM(node->nd_mid), PUSH_VAL(DEFINED_METHOD)); + ID2SYM(get_node_call_nd_mid(node)), PUSH_VAL(DEFINED_METHOD)); } return; } @@ -5383,13 +6361,14 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, ADD_INSN(ret, line_node, putnil); ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_YIELD), 0, PUSH_VAL(DEFINED_YIELD)); + iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq); return; case NODE_BACK_REF: case NODE_NTH_REF: ADD_INSN(ret, line_node, putnil); ADD_INSN3(ret, line_node, defined, INT2FIX(DEFINED_REF), - INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)), + INT2FIX((RNODE_BACK_REF(node)->nd_nth << 1) | (type == NODE_BACK_REF)), PUSH_VAL(DEFINED_GVAR)); return; @@ -5412,11 +6391,12 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, case NODE_IASGN: case NODE_CDECL: case NODE_CVASGN: + case NODE_OP_CDECL: expr_type = DEFINED_ASGN; break; } - assert(expr_type != DEFINED_NOT_DEFINED); + RUBY_ASSERT(expr_type != DEFINED_NOT_DEFINED); if (needstr != Qfalse) { VALUE str = rb_iseq_defined_string(expr_type); @@ -5430,14 +6410,13 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, static void build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const void *unused) { - NODE dummy_line_node = generate_dummy_line_node(0, -1); - ADD_INSN(ret, &dummy_line_node, putnil); + ADD_SYNTHETIC_INSN(ret, 0, -1, putnil); iseq_set_exception_local_table(iseq); } static void defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, - const NODE *const node, LABEL **lfinish, VALUE needstr) + const NODE *const node, LABEL **lfinish, VALUE needstr, bool ignore) { LINK_ELEMENT *lcur = ret->last; defined_expr0(iseq, ret, node, lfinish, needstr, false); @@ -5448,24 +6427,26 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const rb_iseq_t *rescue; struct rb_iseq_new_with_callback_callback_func *ifunc = rb_iseq_new_with_callback_new_callback(build_defined_rescue_iseq, NULL); - rescue = new_child_iseq_with_callback(iseq, ifunc, + rescue = NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc, rb_str_concat(rb_str_new2("defined guard in "), ISEQ_BODY(iseq)->location.label), - iseq, ISEQ_TYPE_RESCUE, 0); + ISEQ_TYPE_RESCUE, 0); lstart->rescued = LABEL_RESCUE_BEG; lend->rescued = LABEL_RESCUE_END; APPEND_LABEL(ret, lcur, lstart); ADD_LABEL(ret, lend); - ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]); + if (!ignore) { + ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]); + } } } static int -compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr) +compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr, bool ignore) { const int line = nd_line(node); const NODE *line_node = node; - if (!node->nd_head) { + if (!RNODE_DEFINED(node)->nd_head) { VALUE str = rb_iseq_defined_string(DEFINED_NIL); ADD_INSN1(ret, line_node, putobject, str); } @@ -5475,9 +6456,9 @@ compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const lfinish[0] = NEW_LABEL(line); lfinish[1] = 0; lfinish[2] = 0; - defined_expr(iseq, ret, node->nd_head, lfinish, needstr); + defined_expr(iseq, ret, RNODE_DEFINED(node)->nd_head, lfinish, needstr, ignore); if (lfinish[1]) { - ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line_node, BIN(putnil), 0)->link); + ELEM_INSERT_NEXT(last, &new_insn_body(iseq, nd_line(line_node), nd_node_id(line_node), BIN(putnil), 0)->link); ADD_INSN(ret, line_node, swap); if (lfinish[2]) { ADD_LABEL(ret, lfinish[2]); @@ -5516,7 +6497,7 @@ make_name_for_block(const rb_iseq_t *orig_iseq) static void push_ensure_entry(rb_iseq_t *iseq, struct iseq_compile_data_ensure_node_stack *enl, - struct ensure_range *er, const NODE *const node) + struct ensure_range *er, const void *const node) { enl->ensure_node = node; enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; /* prev */ @@ -5529,7 +6510,7 @@ 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)); + compile_data_alloc_type(iseq, struct ensure_range); while (erange->next != 0) { erange = erange->next; @@ -5558,7 +6539,7 @@ can_add_ensure_iseq(const rb_iseq_t *iseq) static void add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return) { - assert(can_add_ensure_iseq(iseq)); + RUBY_ASSERT(can_add_ensure_iseq(iseq)); struct iseq_compile_data_ensure_node_stack *enlp = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack; @@ -5592,79 +6573,213 @@ add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return) ADD_SEQ(ret, ensure); } +#if RUBY_DEBUG static int check_keyword(const NODE *node) { /* This check is essentially a code clone of compile_keyword_arg. */ if (nd_type_p(node, NODE_LIST)) { - while (node->nd_next) { - node = node->nd_next; + while (RNODE_LIST(node)->nd_next) { + node = RNODE_LIST(node)->nd_next; } - node = node->nd_head; + node = RNODE_LIST(node)->nd_head; } return keyword_node_p(node); } +#endif -static VALUE +static bool +keyword_node_single_splat_p(NODE *kwnode) +{ + RUBY_ASSERT(keyword_node_p(kwnode)); + + NODE *node = RNODE_HASH(kwnode)->nd_head; + return RNODE_LIST(node)->nd_head == NULL && + RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next == NULL; +} + +static void +compile_single_keyword_splat_mutable(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, + NODE *kwnode, unsigned int *flag_ptr) +{ + *flag_ptr |= VM_CALL_KW_SPLAT_MUT; + ADD_INSN1(args, argn, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + ADD_INSN1(args, argn, newhash, INT2FIX(0)); + compile_hash(iseq, args, kwnode, TRUE, FALSE); + ADD_SEND(args, argn, id_core_hash_merge_kwd, INT2FIX(2)); +} + +#define SPLATARRAY_FALSE 0 +#define SPLATARRAY_TRUE 1 +#define DUP_SINGLE_KW_SPLAT 2 + +static int setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, - int dup_rest, unsigned int *flag, struct rb_callinfo_kwarg **keywords) + unsigned int *dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr) { - if (argn) { - switch (nd_type(argn)) { - case NODE_SPLAT: { - NO_CHECK(COMPILE(args, "args (splat)", argn->nd_head)); - ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest)); - if (flag) *flag |= VM_CALL_ARGS_SPLAT; - return INT2FIX(1); - } - case NODE_ARGSCAT: - case NODE_ARGSPUSH: { - int next_is_list = (nd_type_p(argn->nd_head, NODE_LIST)); - VALUE argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL); - if (nd_type_p(argn->nd_body, NODE_LIST)) { - /* This branch is needed to avoid "newarraykwsplat" [Bug #16442] */ - int rest_len = compile_args(iseq, args, argn->nd_body, NULL, NULL); - ADD_INSN1(args, argn, newarray, INT2FIX(rest_len)); + if (!argn) return 0; + + NODE *kwnode = NULL; + + switch (nd_type(argn)) { + case NODE_LIST: { + // f(x, y, z) + int len = compile_args(iseq, args, argn, &kwnode); + RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0); + + if (kwnode) { + if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) { + len -= 1; } else { - NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body)); - } - if (flag) { - *flag |= VM_CALL_ARGS_SPLAT; - /* This is a dirty hack. It traverses the AST twice. - * In a long term, it should be fixed by a redesign of keyword arguments */ - if (check_keyword(argn->nd_body)) - *flag |= VM_CALL_KW_SPLAT; - } - if (nd_type_p(argn, NODE_ARGSCAT)) { - if (next_is_list) { - ADD_INSN1(args, argn, splatarray, Qtrue); - return INT2FIX(FIX2INT(argc) + 1); + if (keyword_node_single_splat_p(kwnode) && (*dup_rest & DUP_SINGLE_KW_SPLAT)) { + compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr); } else { - ADD_INSN1(args, argn, splatarray, Qfalse); - ADD_INSN(args, argn, concatarray); - return argc; + compile_hash(iseq, args, kwnode, TRUE, FALSE); } } + } + + return len; + } + case NODE_SPLAT: { + // f(*a) + NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head)); + ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE)); + if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE; + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0); + return 1; + } + case NODE_ARGSCAT: { + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, dup_rest, NULL, NULL); + bool args_pushed = false; + + if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) { + int rest_len = compile_args(iseq, args, RNODE_ARGSCAT(argn)->nd_body, &kwnode); + if (kwnode) rest_len--; + ADD_INSN1(args, argn, pushtoarray, INT2FIX(rest_len)); + args_pushed = true; + } + else { + RUBY_ASSERT(!check_keyword(RNODE_ARGSCAT(argn)->nd_body)); + NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSCAT(argn)->nd_body)); + } + + if (nd_type_p(RNODE_ARGSCAT(argn)->nd_head, NODE_LIST)) { + ADD_INSN1(args, argn, splatarray, RBOOL(*dup_rest & SPLATARRAY_TRUE)); + if (*dup_rest & SPLATARRAY_TRUE) *dup_rest &= ~SPLATARRAY_TRUE; + argc += 1; + } + else if (!args_pushed) { + ADD_INSN(args, argn, concattoarray); + } + + // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...}) + if (kwnode) { + // kwsplat + *flag_ptr |= VM_CALL_KW_SPLAT; + compile_hash(iseq, args, kwnode, TRUE, FALSE); + argc += 1; + } + + return argc; + } + case NODE_ARGSPUSH: { + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, dup_rest, NULL, NULL); + + if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) { + int rest_len = compile_args(iseq, args, RNODE_ARGSPUSH(argn)->nd_body, &kwnode); + if (kwnode) rest_len--; + ADD_INSN1(args, argn, newarray, INT2FIX(rest_len)); + ADD_INSN1(args, argn, pushtoarray, INT2FIX(1)); + } + else { + if (keyword_node_p(RNODE_ARGSPUSH(argn)->nd_body)) { + kwnode = RNODE_ARGSPUSH(argn)->nd_body; + } else { - ADD_INSN1(args, argn, newarray, INT2FIX(1)); - ADD_INSN(args, argn, concatarray); - return argc; + NO_CHECK(COMPILE(args, "args (cat: splat)", RNODE_ARGSPUSH(argn)->nd_body)); + ADD_INSN1(args, argn, pushtoarray, INT2FIX(1)); } - } - case NODE_LIST: { - int len = compile_args(iseq, args, argn, keywords, flag); - return INT2FIX(len); - } - default: { - UNKNOWN_NODE("setup_arg", argn, Qnil); - } } + + if (kwnode) { + // f(*a, k:1) + *flag_ptr |= VM_CALL_KW_SPLAT; + if (!keyword_node_single_splat_p(kwnode)) { + *flag_ptr |= VM_CALL_KW_SPLAT_MUT; + compile_hash(iseq, args, kwnode, TRUE, FALSE); + } + else if (*dup_rest & DUP_SINGLE_KW_SPLAT) { + compile_single_keyword_splat_mutable(iseq, args, argn, kwnode, flag_ptr); + } + else { + compile_hash(iseq, args, kwnode, TRUE, FALSE); + } + argc += 1; + } + + return argc; + } + default: { + UNKNOWN_NODE("setup_arg", argn, Qnil); + } + } +} + +static void +setup_args_splat_mut(unsigned int *flag, int dup_rest, int initial_dup_rest) +{ + if ((*flag & VM_CALL_ARGS_SPLAT) && dup_rest != initial_dup_rest) { + *flag |= VM_CALL_ARGS_SPLAT_MUT; + } +} + +static bool +setup_args_dup_rest_p(const NODE *argn) +{ + switch(nd_type(argn)) { + case NODE_LVAR: + case NODE_DVAR: + case NODE_GVAR: + case NODE_IVAR: + case NODE_CVAR: + case NODE_CONST: + case NODE_COLON3: + case NODE_INTEGER: + case NODE_FLOAT: + case NODE_RATIONAL: + case NODE_IMAGINARY: + case NODE_STR: + case NODE_SYM: + case NODE_REGX: + case NODE_SELF: + case NODE_NIL: + case NODE_TRUE: + case NODE_FALSE: + case NODE_LAMBDA: + case NODE_NTH_REF: + case NODE_BACK_REF: + return false; + case NODE_COLON2: + return setup_args_dup_rest_p(RNODE_COLON2(argn)->nd_head); + case NODE_LIST: + while (argn) { + if (setup_args_dup_rest_p(RNODE_LIST(argn)->nd_head)) { + return true; + } + argn = RNODE_LIST(argn)->nd_next; + } + return false; + default: + return true; } - return INT2FIX(0); } static VALUE @@ -5672,13 +6787,94 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, unsigned int *flag, struct rb_callinfo_kwarg **keywords) { VALUE ret; + unsigned int dup_rest = SPLATARRAY_TRUE, initial_dup_rest; + + if (argn) { + const NODE *check_arg = nd_type_p(argn, NODE_BLOCK_PASS) ? + RNODE_BLOCK_PASS(argn)->nd_head : argn; + + if (check_arg) { + switch(nd_type(check_arg)) { + case(NODE_SPLAT): + // avoid caller side array allocation for f(*arg) + dup_rest = SPLATARRAY_FALSE; + break; + case(NODE_ARGSCAT): + // avoid caller side array allocation for f(1, *arg) + dup_rest = !nd_type_p(RNODE_ARGSCAT(check_arg)->nd_head, NODE_LIST); + break; + case(NODE_ARGSPUSH): + // avoid caller side array allocation for f(*arg, **hash) and f(1, *arg, **hash) + dup_rest = !((nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_SPLAT) || + (nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_head, NODE_ARGSCAT) && + nd_type_p(RNODE_ARGSCAT(RNODE_ARGSPUSH(check_arg)->nd_head)->nd_head, NODE_LIST))) && + nd_type_p(RNODE_ARGSPUSH(check_arg)->nd_body, NODE_HASH) && + !RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_brace); + + if (dup_rest == SPLATARRAY_FALSE) { + // require allocation for keyword key/value/splat that may modify splatted argument + NODE *node = RNODE_HASH(RNODE_ARGSPUSH(check_arg)->nd_body)->nd_head; + while (node) { + NODE *key_node = RNODE_LIST(node)->nd_head; + if (key_node && setup_args_dup_rest_p(key_node)) { + dup_rest = SPLATARRAY_TRUE; + break; + } + + node = RNODE_LIST(node)->nd_next; + NODE *value_node = RNODE_LIST(node)->nd_head; + if (setup_args_dup_rest_p(value_node)) { + dup_rest = SPLATARRAY_TRUE; + break; + } + + node = RNODE_LIST(node)->nd_next; + } + } + break; + default: + break; + } + } + + if (check_arg != argn && setup_args_dup_rest_p(RNODE_BLOCK_PASS(argn)->nd_body)) { + // for block pass that may modify splatted argument, dup rest and kwrest if given + dup_rest = SPLATARRAY_TRUE | DUP_SINGLE_KW_SPLAT; + } + } + initial_dup_rest = dup_rest; + if (argn && nd_type_p(argn, NODE_BLOCK_PASS)) { - unsigned int dup_rest = 1; DECL_ANCHOR(arg_block); INIT_ANCHOR(arg_block); - NO_CHECK(COMPILE(arg_block, "block", argn->nd_body)); - *flag |= VM_CALL_ARGS_BLOCKARG; + if (RNODE_BLOCK_PASS(argn)->forwarding && ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->param.flags.forwardable) { + int idx = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size;// - get_local_var_idx(iseq, idDot3); + + RUBY_ASSERT(nd_type_p(RNODE_BLOCK_PASS(argn)->nd_head, NODE_ARGSPUSH)); + const NODE * arg_node = + RNODE_ARGSPUSH(RNODE_BLOCK_PASS(argn)->nd_head)->nd_head; + + int argc = 0; + + // Only compile leading args: + // foo(x, y, ...) + // ^^^^ + if (nd_type_p(arg_node, NODE_ARGSCAT)) { + argc += setup_args_core(iseq, args, RNODE_ARGSCAT(arg_node)->nd_head, &dup_rest, flag, keywords); + } + + *flag |= VM_CALL_FORWARDING; + + ADD_GETLOCAL(args, argn, idx, get_lvar_level(iseq)); + setup_args_splat_mut(flag, dup_rest, initial_dup_rest); + return INT2FIX(argc); + } + else { + *flag |= VM_CALL_ARGS_BLOCKARG; + + NO_CHECK(COMPILE(arg_block, "block", RNODE_BLOCK_PASS(argn)->nd_body)); + } if (LIST_INSN_SIZE_ONE(arg_block)) { LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block); @@ -5687,15 +6883,15 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, if (iobj->insn_id == BIN(getblockparam)) { iobj->insn_id = BIN(getblockparamproxy); } - dup_rest = 0; } } - ret = setup_args_core(iseq, args, argn->nd_head, dup_rest, flag, keywords); + ret = INT2FIX(setup_args_core(iseq, args, RNODE_BLOCK_PASS(argn)->nd_head, &dup_rest, flag, keywords)); ADD_SEQ(args, arg_block); } else { - ret = setup_args_core(iseq, args, argn, 0, flag, keywords); + ret = INT2FIX(setup_args_core(iseq, args, argn, &dup_rest, flag, keywords)); } + setup_args_splat_mut(flag, dup_rest, initial_dup_rest); return ret; } @@ -5710,7 +6906,7 @@ build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, const void *ptr) ADD_INSN1(ret, body, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); ADD_CALL_WITH_BLOCK(ret, body, id_core_set_postexe, argc, block); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block); - iseq_set_local_table(iseq, 0); + iseq_set_local_table(iseq, 0, 0); } static void @@ -5730,19 +6926,19 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE ADD_INSN(ret, line_node, dup); ADD_INSNL(ret, line_node, branchunless, fail_label); - for (vars = node; vars; vars = vars->nd_next) { + for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) { INSN *cap; - if (vars->nd_next) { + if (RNODE_BLOCK(vars)->nd_next) { ADD_INSN(ret, line_node, dup); } last = ret->last; - NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head)); + NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head)); last = last->next; /* putobject :var */ - cap = new_insn_send(iseq, line_node, idAREF, INT2FIX(1), + cap = new_insn_send(iseq, nd_line(line_node), nd_node_id(line_node), idAREF, INT2FIX(1), NULL, INT2FIX(0), NULL); ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap); #if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0 - if (!vars->nd_next && vars == node) { + if (!RNODE_BLOCK(vars)->nd_next && vars == node) { /* only one name */ DECL_ANCHOR(nom); @@ -5763,9 +6959,9 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE ADD_INSNL(ret, line_node, jump, end_label); ADD_LABEL(ret, fail_label); ADD_INSN(ret, line_node, pop); - for (vars = node; vars; vars = vars->nd_next) { + for (vars = node; vars; vars = RNODE_BLOCK(vars)->nd_next) { last = ret->last; - NO_CHECK(COMPILE_POPPED(ret, "capture", vars->nd_head)); + NO_CHECK(COMPILE_POPPED(ret, "capture", RNODE_BLOCK(vars)->nd_head)); last = last->next; /* putobject :var */ ((INSN*)last)->insn_id = BIN(putnil); ((INSN*)last)->operand_size = 0; @@ -5778,8 +6974,10 @@ optimizable_range_item_p(const NODE *n) { if (!n) return FALSE; switch (nd_type(n)) { - case NODE_LIT: - return RB_INTEGER_TYPE_P(n->nd_lit); + case NODE_LINE: + return TRUE; + case NODE_INTEGER: + return TRUE; case NODE_NIL: return TRUE; default: @@ -5787,69 +6985,70 @@ optimizable_range_item_p(const NODE *n) } } +static VALUE +optimized_range_item(const NODE *n) +{ + switch (nd_type(n)) { + case NODE_LINE: + return rb_node_line_lineno_val(n); + case NODE_INTEGER: + return rb_node_integer_literal_val(n); + case NODE_FLOAT: + return rb_node_float_literal_val(n); + case NODE_RATIONAL: + return rb_node_rational_literal_val(n); + case NODE_IMAGINARY: + return rb_node_imaginary_literal_val(n); + case NODE_NIL: + return Qnil; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(n))); + } +} + static int compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type) { - struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); - const NODE *const node_body = type == NODE_IF ? node->nd_body : node->nd_else; - const NODE *const node_else = type == NODE_IF ? node->nd_else : node->nd_body; + const NODE *const node_body = type == NODE_IF ? RNODE_IF(node)->nd_body : RNODE_UNLESS(node)->nd_else; + const NODE *const node_else = type == NODE_IF ? RNODE_IF(node)->nd_else : RNODE_UNLESS(node)->nd_body; const int line = nd_line(node); const NODE *line_node = node; DECL_ANCHOR(cond_seq); - DECL_ANCHOR(then_seq); - DECL_ANCHOR(else_seq); LABEL *then_label, *else_label, *end_label; VALUE branches = Qfalse; - int ci_size; - VALUE catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; - long catch_table_size = NIL_P(catch_table) ? 0 : RARRAY_LEN(catch_table); INIT_ANCHOR(cond_seq); - INIT_ANCHOR(then_seq); - INIT_ANCHOR(else_seq); then_label = NEW_LABEL(line); else_label = NEW_LABEL(line); end_label = 0; - compile_branch_condition(iseq, cond_seq, node->nd_cond, - then_label, else_label); - - ci_size = body->ci_size; - CHECK(COMPILE_(then_seq, "then", node_body, popped)); - catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; - if (!then_label->refcnt) { - body->ci_size = ci_size; - if (!NIL_P(catch_table)) rb_ary_set_len(catch_table, catch_table_size); - } - else { - if (!NIL_P(catch_table)) catch_table_size = RARRAY_LEN(catch_table); - } - - ci_size = body->ci_size; - CHECK(COMPILE_(else_seq, "else", node_else, popped)); - catch_table = ISEQ_COMPILE_DATA(iseq)->catch_table_ary; - if (!else_label->refcnt) { - body->ci_size = ci_size; - if (!NIL_P(catch_table)) rb_ary_set_len(catch_table, catch_table_size); - } - else { - if (!NIL_P(catch_table)) catch_table_size = RARRAY_LEN(catch_table); + NODE *cond = RNODE_IF(node)->nd_cond; + if (nd_type(cond) == NODE_BLOCK) { + cond = RNODE_BLOCK(cond)->nd_head; } + CHECK(compile_branch_condition(iseq, cond_seq, cond, then_label, else_label)); ADD_SEQ(ret, cond_seq); if (then_label->refcnt && else_label->refcnt) { - branches = decl_branch_base(iseq, node, type == NODE_IF ? "if" : "unless"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_IF ? "if" : "unless"); } if (then_label->refcnt) { ADD_LABEL(ret, then_label); + + DECL_ANCHOR(then_seq); + INIT_ANCHOR(then_seq); + CHECK(COMPILE_(then_seq, "then", node_body, popped)); + if (else_label->refcnt) { + const NODE *const coverage_node = node_body ? node_body : node; add_trace_branch_coverage( iseq, ret, - node_body ? node_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 0, type == NODE_IF ? "then" : "else", branches); @@ -5864,11 +7063,18 @@ compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int if (else_label->refcnt) { ADD_LABEL(ret, else_label); + + DECL_ANCHOR(else_seq); + INIT_ANCHOR(else_seq); + CHECK(COMPILE_(else_seq, "else", node_else, popped)); + if (then_label->refcnt) { + const NODE *const coverage_node = node_else ? node_else : node; add_trace_branch_coverage( iseq, ret, - node_else ? node_else : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 1, type == NODE_IF ? "else" : "then", branches); @@ -5893,7 +7099,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod DECL_ANCHOR(body_seq); DECL_ANCHOR(cond_seq); int only_special_literals = 1; - VALUE literals = rb_hash_new(); + VALUE literals = cdhash_new(0); int line; enum node_type type; const NODE *line_node; @@ -5904,13 +7110,11 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod INIT_ANCHOR(body_seq); INIT_ANCHOR(cond_seq); - RHASH_TBL_RAW(literals)->type = &cdhash_type; - - CHECK(COMPILE(head, "case base", node->nd_head)); + CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head)); - branches = decl_branch_base(iseq, node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); - node = node->nd_body; + node = RNODE_CASE(node)->nd_body; EXPECT_NODE("NODE_CASE", node, NODE_WHEN, COMPILE_NG); type = nd_type(node); line = nd_line(node); @@ -5927,17 +7131,21 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); ADD_INSN(body_seq, line_node, pop); + + const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - node->nd_body ? node->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "when", branches); - CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped)); + + CHECK(COMPILE_(body_seq, "when body", RNODE_WHEN(node)->nd_body, popped)); ADD_INSNL(body_seq, line_node, jump, endlabel); - vals = node->nd_head; + vals = RNODE_WHEN(node)->nd_head; if (vals) { switch (nd_type(vals)) { case NODE_LIST: @@ -5958,7 +7166,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod EXPECT_NODE_NONULL("NODE_CASE", node, NODE_LIST, COMPILE_NG); } - node = node->nd_next; + node = RNODE_WHEN(node)->nd_next; if (!node) { break; } @@ -5970,7 +7178,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod if (node) { ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, line_node, pop); - add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches); CHECK(COMPILE_(cond_seq, "else", node, popped)); ADD_INSNL(cond_seq, line_node, jump, endlabel); } @@ -5978,7 +7186,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod debugs("== else (implicit)\n"); ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, orig_node, pop); - add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches); if (!popped) { ADD_INSN(cond_seq, orig_node, putnil); } @@ -6003,13 +7211,13 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no { const NODE *vals; const NODE *val; - const NODE *node = orig_node->nd_body; + const NODE *node = RNODE_CASE2(orig_node)->nd_body; LABEL *endlabel; DECL_ANCHOR(body_seq); VALUE branches = Qfalse; int branch_id = 0; - branches = decl_branch_base(iseq, orig_node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(orig_node), nd_code_loc(orig_node), "case"); INIT_ANCHOR(body_seq); endlabel = NEW_LABEL(nd_line(node)); @@ -6018,17 +7226,21 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no const int line = nd_line(node); LABEL *l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); + + const NODE *const coverage_node = RNODE_WHEN(node)->nd_body ? RNODE_WHEN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - node->nd_body ? node->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "when", branches); - CHECK(COMPILE_(body_seq, "when", node->nd_body, popped)); + + CHECK(COMPILE_(body_seq, "when", RNODE_WHEN(node)->nd_body, popped)); ADD_INSNL(body_seq, node, jump, endlabel); - vals = node->nd_head; + vals = RNODE_WHEN(node)->nd_head; if (!vals) { EXPECT_NODE_NONULL("NODE_WHEN", node, NODE_LIST, COMPILE_NG); } @@ -6036,12 +7248,12 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no case NODE_LIST: while (vals) { LABEL *lnext; - val = vals->nd_head; + val = RNODE_LIST(vals)->nd_head; lnext = NEW_LABEL(nd_line(val)); debug_compile("== when2\n", (void)0); CHECK(compile_branch_condition(iseq, ret, val, l1, lnext)); ADD_LABEL(ret, lnext); - vals = vals->nd_next; + vals = RNODE_LIST(vals)->nd_next; } break; case NODE_SPLAT: @@ -6055,13 +7267,15 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no default: UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG); } - node = node->nd_next; + node = RNODE_WHEN(node)->nd_next; } /* else */ + const NODE *const coverage_node = node ? node : orig_node; add_trace_branch_coverage( iseq, ret, - node ? node : orig_node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id, "else", branches); @@ -6147,14 +7361,13 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c * match_failed: * goto unmatched */ - struct rb_ary_pattern_info *apinfo = node->nd_apinfo; - const NODE *args = apinfo->pre_args; - const int pre_args_num = apinfo->pre_args ? rb_long2int(apinfo->pre_args->nd_alen) : 0; - const int post_args_num = apinfo->post_args ? rb_long2int(apinfo->post_args->nd_alen) : 0; + const NODE *args = RNODE_ARYPTN(node)->pre_args; + const int pre_args_num = RNODE_ARYPTN(node)->pre_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->pre_args)->as.nd_alen) : 0; + const int post_args_num = RNODE_ARYPTN(node)->post_args ? rb_long2int(RNODE_LIST(RNODE_ARYPTN(node)->post_args)->as.nd_alen) : 0; const int min_argc = pre_args_num + post_args_num; - const int use_rest_num = apinfo->rest_arg && (NODE_NAMED_REST_P(apinfo->rest_arg) || - (!NODE_NAMED_REST_P(apinfo->rest_arg) && post_args_num > 0)); + const int use_rest_num = RNODE_ARYPTN(node)->rest_arg && (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) || + (!NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg) && post_args_num > 0)); LABEL *match_failed, *type_error, *deconstruct, *deconstructed; int i; @@ -6178,10 +7391,10 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN(ret, line_node, dup); ADD_SEND(ret, line_node, idLength, INT2FIX(0)); ADD_INSN1(ret, line_node, putobject, INT2FIX(min_argc)); - ADD_SEND(ret, line_node, apinfo->rest_arg ? idGE : idEq, INT2FIX(1)); // (1) + ADD_SEND(ret, line_node, RNODE_ARYPTN(node)->rest_arg ? idGE : idEq, INT2FIX(1)); // (1) if (in_single_pattern) { CHECK(iseq_compile_pattern_set_length_errmsg(iseq, ret, node, - apinfo->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") : + RNODE_ARYPTN(node)->rest_arg ? rb_fstring_lit("%p length mismatch (given %p, expected %p+)") : rb_fstring_lit("%p length mismatch (given %p, expected %p)"), INT2FIX(min_argc), base_index + 1 /* (1) */)); } @@ -6191,12 +7404,12 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN(ret, line_node, dup); ADD_INSN1(ret, line_node, putobject, INT2FIX(i)); ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (2) - CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false)); - args = args->nd_next; + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (2) */, false)); + args = RNODE_LIST(args)->nd_next; } - if (apinfo->rest_arg) { - if (NODE_NAMED_REST_P(apinfo->rest_arg)) { + if (RNODE_ARYPTN(node)->rest_arg) { + if (NODE_NAMED_REST_P(RNODE_ARYPTN(node)->rest_arg)) { ADD_INSN(ret, line_node, dup); ADD_INSN1(ret, line_node, putobject, INT2FIX(pre_args_num)); ADD_INSN1(ret, line_node, topn, INT2FIX(1)); @@ -6206,7 +7419,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN1(ret, line_node, setn, INT2FIX(4)); ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (3) - CHECK(iseq_compile_pattern_match(iseq, ret, apinfo->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_ARYPTN(node)->rest_arg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (3) */, false)); } else { if (post_args_num > 0) { @@ -6220,7 +7433,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c } } - args = apinfo->post_args; + args = RNODE_ARYPTN(node)->post_args; for (i = 0; i < post_args_num; i++) { ADD_INSN(ret, line_node, dup); @@ -6229,8 +7442,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_SEND(ret, line_node, idPLUS, INT2FIX(1)); ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (4) - CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false)); - args = args->nd_next; + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (4) */, false)); + args = RNODE_LIST(args)->nd_next; } ADD_INSN(ret, line_node, pop); @@ -6308,9 +7521,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c * match_failed: * goto unmatched */ - struct rb_fnd_pattern_info *fpinfo = node->nd_fpinfo; - const NODE *args = fpinfo->args; - const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0; + const NODE *args = RNODE_FNDPTN(node)->args; + const int args_num = RNODE_FNDPTN(node)->args ? rb_long2int(RNODE_LIST(RNODE_FNDPTN(node)->args)->as.nd_alen) : 0; LABEL *match_failed, *type_error, *deconstruct, *deconstructed; match_failed = NEW_LABEL(line); @@ -6363,25 +7575,25 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c } ADD_SEND(ret, line_node, idAREF, INT2FIX(1)); // (5) - CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false)); - args = args->nd_next; + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(args)->nd_head, next_loop, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (5) */, false)); + args = RNODE_LIST(args)->nd_next; } - if (NODE_NAMED_REST_P(fpinfo->pre_rest_arg)) { + if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->pre_rest_arg)) { ADD_INSN1(ret, line_node, topn, INT2FIX(3)); ADD_INSN1(ret, line_node, putobject, INT2FIX(0)); ADD_INSN1(ret, line_node, topn, INT2FIX(2)); ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (6) - CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->pre_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3), (4), (6) */, false)); } - if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) { + if (NODE_NAMED_REST_P(RNODE_FNDPTN(node)->post_rest_arg)) { ADD_INSN1(ret, line_node, topn, INT2FIX(3)); ADD_INSN1(ret, line_node, topn, INT2FIX(1)); ADD_INSN1(ret, line_node, putobject, INT2FIX(args_num)); ADD_SEND(ret, line_node, idPLUS, INT2FIX(1)); ADD_INSN1(ret, line_node, topn, INT2FIX(3)); ADD_SEND(ret, line_node, idAREF, INT2FIX(2)); // (7) - CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_FNDPTN(node)->post_rest_arg, find_failed, in_single_pattern, in_alt_pattern, base_index + 4 /* (2), (3),(4), (7) */, false)); } ADD_INSNL(ret, line_node, jump, find_succeeded); @@ -6495,12 +7707,12 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c match_failed = NEW_LABEL(line); type_error = NEW_LABEL(line); - if (node->nd_pkwargs && !node->nd_pkwrestarg) { - const NODE *kw_args = node->nd_pkwargs->nd_head; - keys = rb_ary_new_capa(kw_args ? kw_args->nd_alen/2 : 0); + if (RNODE_HSHPTN(node)->nd_pkwargs && !RNODE_HSHPTN(node)->nd_pkwrestarg) { + const NODE *kw_args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head; + keys = rb_ary_new_capa(kw_args ? RNODE_LIST(kw_args)->as.nd_alen/2 : 0); while (kw_args) { - rb_ary_push(keys, kw_args->nd_head->nd_lit); - kw_args = kw_args->nd_next->nd_next; + rb_ary_push(keys, get_symbol_value(iseq, RNODE_LIST(kw_args)->nd_head)); + kw_args = RNODE_LIST(RNODE_LIST(kw_args)->nd_next)->nd_next; } } @@ -6518,6 +7730,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN(ret, line_node, putnil); } else { + RB_OBJ_SET_FROZEN_SHAREABLE(keys); ADD_INSN1(ret, line_node, duparray, keys); RB_OBJ_WRITTEN(iseq, Qundef, rb_obj_hide(keys)); } @@ -6527,28 +7740,23 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN1(ret, line_node, checktype, INT2FIX(T_HASH)); ADD_INSNL(ret, line_node, branchunless, type_error); - if (node->nd_pkwrestarg) { + if (RNODE_HSHPTN(node)->nd_pkwrestarg) { ADD_SEND(ret, line_node, rb_intern("dup"), INT2FIX(0)); } - if (node->nd_pkwargs) { + if (RNODE_HSHPTN(node)->nd_pkwargs) { int i; int keys_num; const NODE *args; - args = node->nd_pkwargs->nd_head; + args = RNODE_HASH(RNODE_HSHPTN(node)->nd_pkwargs)->nd_head; if (args) { DECL_ANCHOR(match_values); INIT_ANCHOR(match_values); - keys_num = rb_long2int(args->nd_alen) / 2; + keys_num = rb_long2int(RNODE_LIST(args)->as.nd_alen) / 2; for (i = 0; i < keys_num; i++) { - NODE *key_node = args->nd_head; - NODE *value_node = args->nd_next->nd_head; - VALUE key; - - if (!nd_type_p(key_node, NODE_LIT)) { - UNKNOWN_NODE("NODE_IN", key_node, COMPILE_NG); - } - key = key_node->nd_lit; + NODE *key_node = RNODE_LIST(args)->nd_head; + NODE *value_node = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_head; + VALUE key = get_symbol_value(iseq, key_node); ADD_INSN(ret, line_node, dup); ADD_INSN1(ret, line_node, putobject, key); @@ -6560,7 +7768,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN(ret, line_node, dup); ADD_INSNL(ret, line_node, branchif, match_succeeded); - ADD_INSN1(ret, line_node, putobject, rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key))); // (4) + VALUE str = rb_str_freeze(rb_sprintf("key not found: %+"PRIsVALUE, key)); + ADD_INSN1(ret, line_node, putobject, RB_OBJ_SET_SHAREABLE(str)); // (4) ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_ERROR_STRING + 2 /* (3), (4) */)); ADD_INSN1(ret, line_node, putobject, Qtrue); // (5) ADD_INSN1(ret, line_node, setn, INT2FIX(base_index + CASE3_BI_OFFSET_KEY_ERROR_P + 3 /* (3), (4), (5) */)); @@ -6577,9 +7786,9 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSN(match_values, line_node, dup); ADD_INSN1(match_values, line_node, putobject, key); - ADD_SEND(match_values, line_node, node->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8) + ADD_SEND(match_values, line_node, RNODE_HSHPTN(node)->nd_pkwrestarg ? rb_intern("delete") : idAREF, INT2FIX(1)); // (8) CHECK(iseq_compile_pattern_match(iseq, match_values, value_node, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (8) */, false)); - args = args->nd_next->nd_next; + args = RNODE_LIST(RNODE_LIST(args)->nd_next)->nd_next; } ADD_SEQ(ret, match_values); } @@ -6593,8 +7802,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSNL(ret, line_node, branchunless, match_failed); } - if (node->nd_pkwrestarg) { - if (node->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) { + if (RNODE_HSHPTN(node)->nd_pkwrestarg) { + if (RNODE_HSHPTN(node)->nd_pkwrestarg == NODE_SPECIAL_NO_REST_KEYWORD) { ADD_INSN(ret, line_node, dup); ADD_SEND(ret, line_node, idEmptyP, INT2FIX(0)); // (10) if (in_single_pattern) { @@ -6604,7 +7813,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c } else { ADD_INSN(ret, line_node, dup); // (11) - CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_HSHPTN(node)->nd_pkwrestarg, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (11) */, false)); } } @@ -6624,7 +7833,15 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c ADD_INSNL(ret, line_node, jump, unmatched); break; } - case NODE_LIT: + case NODE_SYM: + case NODE_REGX: + case NODE_LINE: + case NODE_INTEGER: + case NODE_FLOAT: + case NODE_RATIONAL: + case NODE_IMAGINARY: + case NODE_FILE: + case NODE_ENCODING: case NODE_STR: case NODE_XSTR: case NODE_DSTR: @@ -6648,6 +7865,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c case NODE_COLON2: case NODE_COLON3: case NODE_BEGIN: + case NODE_BLOCK: + case NODE_ONCE: CHECK(COMPILE(ret, "case in literal", node)); // (1) if (in_single_pattern) { ADD_INSN1(ret, line_node, dupn, INT2FIX(2)); @@ -6661,7 +7880,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c break; case NODE_LASGN: { struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); - ID id = node->nd_vid; + ID id = RNODE_LASGN(node)->nd_vid; int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id); if (in_alt_pattern) { @@ -6679,7 +7898,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c } case NODE_DASGN: { int idx, lv, ls; - ID id = node->nd_vid; + ID id = RNODE_DASGN(node)->nd_vid; idx = get_dyna_var_idx(iseq, id, &lv, &ls); @@ -6705,8 +7924,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c case NODE_UNLESS: { LABEL *match_failed; match_failed = unmatched; - CHECK(iseq_compile_pattern_match(iseq, ret, node->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache)); - CHECK(COMPILE(ret, "case in if", node->nd_cond)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_IF(node)->nd_body, unmatched, in_single_pattern, in_alt_pattern, base_index, use_deconstructed_cache)); + CHECK(COMPILE(ret, "case in if", RNODE_IF(node)->nd_cond)); if (in_single_pattern) { LABEL *match_succeeded; match_succeeded = NEW_LABEL(line); @@ -6743,15 +7962,15 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c LABEL *match_failed; match_failed = NEW_LABEL(line); - n = node->nd_head; - if (! (nd_type_p(n, NODE_LIST) && n->nd_alen == 2)) { + n = RNODE_HASH(node)->nd_head; + if (! (nd_type_p(n, NODE_LIST) && RNODE_LIST(n)->as.nd_alen == 2)) { COMPILE_ERROR(ERROR_ARGS "unexpected node"); return COMPILE_NG; } ADD_INSN(ret, line_node, dup); // (1) - CHECK(iseq_compile_pattern_match(iseq, ret, n->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache)); - CHECK(iseq_compile_pattern_each(iseq, ret, n->nd_next->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false)); + CHECK(iseq_compile_pattern_match(iseq, ret, RNODE_LIST(n)->nd_head, match_failed, in_single_pattern, in_alt_pattern, base_index + 1 /* (1) */, use_deconstructed_cache)); + CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head, matched, match_failed, in_single_pattern, in_alt_pattern, base_index, false)); ADD_INSN(ret, line_node, putnil); ADD_LABEL(ret, match_failed); @@ -6765,13 +7984,13 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c fin = NEW_LABEL(line); ADD_INSN(ret, line_node, dup); // (1) - CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache)); + CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_1st, match_succeeded, fin, in_single_pattern, true, base_index + 1 /* (1) */, use_deconstructed_cache)); ADD_LABEL(ret, match_succeeded); ADD_INSN(ret, line_node, pop); ADD_INSNL(ret, line_node, jump, matched); ADD_INSN(ret, line_node, putnil); ADD_LABEL(ret, fin); - CHECK(iseq_compile_pattern_each(iseq, ret, node->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache)); + CHECK(iseq_compile_pattern_each(iseq, ret, RNODE_OR(node)->nd_2nd, matched, unmatched, in_single_pattern, true, base_index, use_deconstructed_cache)); break; } default: @@ -6794,9 +8013,9 @@ iseq_compile_pattern_constant(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD { const NODE *line_node = node; - if (node->nd_pconst) { + if (RNODE_ARYPTN(node)->nd_pconst) { ADD_INSN(ret, line_node, dup); // (1) - CHECK(COMPILE(ret, "constant", node->nd_pconst)); // (2) + CHECK(COMPILE(ret, "constant", RNODE_ARYPTN(node)->nd_pconst)); // (2) if (in_single_pattern) { ADD_INSN1(ret, line_node, dupn, INT2FIX(2)); } @@ -6997,14 +8216,14 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no INIT_ANCHOR(body_seq); INIT_ANCHOR(cond_seq); - branches = decl_branch_base(iseq, node, "case"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); - node = node->nd_body; + node = RNODE_CASE3(node)->nd_body; EXPECT_NODE("NODE_CASE3", node, NODE_IN, COMPILE_NG); type = nd_type(node); line = nd_line(node); line_node = node; - single_pattern = !node->nd_next; + single_pattern = !RNODE_IN(node)->nd_next; endlabel = NEW_LABEL(line); elselabel = NEW_LABEL(line); @@ -7018,7 +8237,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no } ADD_INSN(head, line_node, putnil); /* allocate stack for cached #deconstruct value */ - CHECK(COMPILE(head, "case base", orig_node->nd_head)); + CHECK(COMPILE(head, "case base", RNODE_CASE3(orig_node)->nd_head)); ADD_SEQ(ret, head); /* case VAL */ @@ -7031,17 +8250,21 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no l1 = NEW_LABEL(line); ADD_LABEL(body_seq, l1); ADD_INSN1(body_seq, line_node, adjuststack, INT2FIX(single_pattern ? 6 : 2)); + + const NODE *const coverage_node = RNODE_IN(node)->nd_body ? RNODE_IN(node)->nd_body : node; add_trace_branch_coverage( iseq, body_seq, - node->nd_body ? node->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), branch_id++, "in", branches); - CHECK(COMPILE_(body_seq, "in body", node->nd_body, popped)); + + CHECK(COMPILE_(body_seq, "in body", RNODE_IN(node)->nd_body, popped)); ADD_INSNL(body_seq, line_node, jump, endlabel); - pattern = node->nd_head; + pattern = RNODE_IN(node)->nd_head; if (pattern) { int pat_line = nd_line(pattern); LABEL *next_pat = NEW_LABEL(pat_line); @@ -7056,7 +8279,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no return COMPILE_NG; } - node = node->nd_next; + node = RNODE_IN(node)->nd_next; if (!node) { break; } @@ -7069,7 +8292,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no ADD_LABEL(cond_seq, elselabel); ADD_INSN(cond_seq, line_node, pop); ADD_INSN(cond_seq, line_node, pop); /* discard cached #deconstruct value */ - add_trace_branch_coverage(iseq, cond_seq, node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(node), nd_node_id(node), branch_id, "else", branches); CHECK(COMPILE_(cond_seq, "else", node, popped)); ADD_INSNL(cond_seq, line_node, jump, endlabel); ADD_INSN(cond_seq, line_node, putnil); @@ -7080,7 +8303,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no else { debugs("== else (implicit)\n"); ADD_LABEL(cond_seq, elselabel); - add_trace_branch_coverage(iseq, cond_seq, orig_node, branch_id, "else", branches); + add_trace_branch_coverage(iseq, cond_seq, nd_code_loc(orig_node), nd_node_id(orig_node), branch_id, "else", branches); ADD_INSN1(cond_seq, orig_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); if (single_pattern) { @@ -7098,6 +8321,7 @@ compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no fin = NEW_LABEL(line); kw_arg = rb_xmalloc_mul_add(2, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg)); + kw_arg->references = 0; kw_arg->keyword_len = 2; kw_arg->keywords[0] = ID2SYM(rb_intern("matchee")); kw_arg->keywords[1] = ID2SYM(rb_intern("key")); @@ -7181,7 +8405,7 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0; push_ensure_entry(iseq, &enl, NULL, NULL); - if (node->nd_state == 1) { + if (RNODE_WHILE(node)->nd_state == 1) { ADD_INSNL(ret, line_node, jump, next_label); } else { @@ -7196,31 +8420,35 @@ compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in if (tmp_label) ADD_LABEL(ret, tmp_label); ADD_LABEL(ret, redo_label); - branches = decl_branch_base(iseq, node, type == NODE_WHILE ? "while" : "until"); + branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), type == NODE_WHILE ? "while" : "until"); + + const NODE *const coverage_node = RNODE_WHILE(node)->nd_body ? RNODE_WHILE(node)->nd_body : node; add_trace_branch_coverage( iseq, ret, - node->nd_body ? node->nd_body : node, + nd_code_loc(coverage_node), + nd_node_id(coverage_node), 0, "body", branches); - CHECK(COMPILE_POPPED(ret, "while body", node->nd_body)); + + CHECK(COMPILE_POPPED(ret, "while body", RNODE_WHILE(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); + CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond, + redo_label, end_label)); } else { /* until */ - compile_branch_condition(iseq, ret, node->nd_cond, - end_label, redo_label); + CHECK(compile_branch_condition(iseq, ret, RNODE_WHILE(node)->nd_cond, + end_label, redo_label)); } ADD_LABEL(ret, end_label); ADD_ADJUST_RESTORE(ret, adjust_label); - if (node->nd_state == Qundef) { + if (UNDEF_P(RNODE_WHILE(node)->nd_state)) { /* ADD_INSN(ret, line_node, putundef); */ COMPILE_ERROR(ERROR_ARGS "unsupported: putundef"); return COMPILE_NG; @@ -7262,20 +8490,43 @@ compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in ADD_LABEL(ret, retry_label); if (nd_type_p(node, NODE_FOR)) { - CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter)); + CHECK(COMPILE(ret, "iter caller (for)", RNODE_FOR(node)->nd_iter)); ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq = - NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), + NEW_CHILD_ISEQ(RNODE_FOR(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); ADD_SEND_WITH_BLOCK(ret, line_node, idEach, INT2FIX(0), child_iseq); } else { ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq = - NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), + NEW_CHILD_ISEQ(RNODE_ITER(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); - CHECK(COMPILE(ret, "iter caller", node->nd_iter)); + CHECK(COMPILE(ret, "iter caller", RNODE_ITER(node)->nd_iter)); + } + + { + // We need to put the label "retry_end_l" immediately after the last "send" instruction. + // This because vm_throw checks if the break cont is equal to the index of next insn of the "send". + // (Otherwise, it is considered "break from proc-closure". See "TAG_BREAK" handling in "vm_throw_start".) + // + // Normally, "send" instruction is at the last. + // However, qcall under branch coverage measurement adds some instructions after the "send". + // + // Note that "invokesuper", "invokesuperforward" appears instead of "send". + INSN *iobj; + LINK_ELEMENT *last_elem = LAST_ELEMENT(ret); + iobj = IS_INSN(last_elem) ? (INSN*) last_elem : (INSN*) get_prev_insn((INSN*) last_elem); + while (!IS_INSN_ID(iobj, send) && !IS_INSN_ID(iobj, invokesuper) && !IS_INSN_ID(iobj, sendforward) && !IS_INSN_ID(iobj, invokesuperforward)) { + iobj = (INSN*) get_prev_insn(iobj); + } + ELEM_INSERT_NEXT(&iobj->link, (LINK_ELEMENT*) retry_end_l); + + // LINK_ANCHOR has a pointer to the last element, but ELEM_INSERT_NEXT does not update it + // even if we add an insn to the last of LINK_ANCHOR. So this updates it manually. + if (&iobj->link == LAST_ELEMENT(ret)) { + ret->last = (LINK_ELEMENT*) retry_end_l; + } } - ADD_LABEL(ret, retry_end_l); if (popped) { ADD_INSN(ret, line_node, pop); @@ -7294,7 +8545,7 @@ compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const nod * (args.length == 1 && Array.try_convert(args[0])) || args */ const NODE *line_node = node; - const NODE *var = node->nd_var; + const NODE *var = RNODE_FOR_MASGN(node)->nd_var; LABEL *not_single = NEW_LABEL(nd_line(var)); LABEL *not_ary = NEW_LABEL(nd_line(var)); CHECK(COMPILE(ret, "for var", var)); @@ -7329,7 +8580,7 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i LABEL *splabel = NEW_LABEL(0); ADD_LABEL(ret, splabel); ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label); - CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts, + CHECK(COMPILE_(ret, "break val (while/until)", RNODE_BREAK(node)->nd_stts, ISEQ_COMPILE_DATA(iseq)->loopval_popped)); add_ensure_iseq(ret, iseq, 0); ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label); @@ -7364,7 +8615,7 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i } /* escape from block */ - CHECK(COMPILE(ret, "break val (block)", node->nd_stts)); + CHECK(COMPILE(ret, "break val (block)", RNODE_BREAK(node)->nd_stts)); ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_BREAK)); if (popped) { ADD_INSN(ret, line_node, pop); @@ -7387,7 +8638,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in LABEL *splabel = NEW_LABEL(0); debugs("next in while loop\n"); ADD_LABEL(ret, splabel); - CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts)); + CHECK(COMPILE(ret, "next val/valid syntax?", RNODE_NEXT(node)->nd_stts)); add_ensure_iseq(ret, iseq, 0); ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->redo_label); ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->start_label); @@ -7401,11 +8652,10 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in debugs("next in block\n"); ADD_LABEL(ret, splabel); ADD_ADJUST(ret, line_node, ISEQ_COMPILE_DATA(iseq)->start_label); - CHECK(COMPILE(ret, "next val", node->nd_stts)); + CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts)); add_ensure_iseq(ret, iseq, 0); ADD_INSNL(ret, line_node, jump, ISEQ_COMPILE_DATA(iseq)->end_label); ADD_ADJUST_RESTORE(ret, splabel); - splabel->unremovable = FALSE; if (!popped) { ADD_INSN(ret, line_node, putnil); @@ -7436,7 +8686,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in ip = ISEQ_BODY(ip)->parent_iseq; } if (ip != 0) { - CHECK(COMPILE(ret, "next val", node->nd_stts)); + CHECK(COMPILE(ret, "next val", RNODE_NEXT(node)->nd_stts)); ADD_INSN1(ret, line_node, throw, INT2FIX(throw_flag | TAG_NEXT)); if (popped) { @@ -7548,7 +8798,7 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *lstart = NEW_LABEL(line); LABEL *lend = NEW_LABEL(line); LABEL *lcont = NEW_LABEL(line); - const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq, + const rb_iseq_t *rescue = NEW_CHILD_ISEQ(RNODE_RESCUE(node)->nd_resq, rb_str_concat(rb_str_new2("rescue in "), ISEQ_BODY(iseq)->location.label), ISEQ_TYPE_RESCUE, line); @@ -7560,14 +8810,14 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue; ISEQ_COMPILE_DATA(iseq)->in_rescue = true; { - CHECK(COMPILE(ret, "rescue head", node->nd_head)); + CHECK(COMPILE(ret, "rescue head", RNODE_RESCUE(node)->nd_head)); } ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue; ADD_LABEL(ret, lend); - if (node->nd_else) { + if (RNODE_RESCUE(node)->nd_else) { ADD_INSN(ret, line_node, pop); - CHECK(COMPILE(ret, "rescue else", node->nd_else)); + CHECK(COMPILE(ret, "rescue else", RNODE_RESCUE(node)->nd_else)); } ADD_INSN(ret, line_node, nop); ADD_LABEL(ret, lcont); @@ -7595,16 +8845,16 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, label_miss = NEW_LABEL(line); label_hit = NEW_LABEL(line); - narg = resq->nd_args; + narg = RNODE_RESBODY(resq)->nd_args; if (narg) { switch (nd_type(narg)) { case NODE_LIST: while (narg) { ADD_GETLOCAL(ret, line_node, LVAR_ERRINFO, 0); - CHECK(COMPILE(ret, "rescue arg", narg->nd_head)); + CHECK(COMPILE(ret, "rescue arg", RNODE_LIST(narg)->nd_head)); ADD_INSN1(ret, line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE)); ADD_INSNL(ret, line_node, branchif, label_hit); - narg = narg->nd_next; + narg = RNODE_LIST(narg)->nd_next; } break; case NODE_SPLAT: @@ -7627,13 +8877,26 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, } ADD_INSNL(ret, line_node, jump, label_miss); ADD_LABEL(ret, label_hit); - CHECK(COMPILE(ret, "resbody body", resq->nd_body)); + ADD_TRACE(ret, RUBY_EVENT_RESCUE); + + if (RNODE_RESBODY(resq)->nd_exc_var) { + CHECK(COMPILE_POPPED(ret, "resbody exc_var", RNODE_RESBODY(resq)->nd_exc_var)); + } + + if (nd_type(RNODE_RESBODY(resq)->nd_body) == NODE_BEGIN && RNODE_BEGIN(RNODE_RESBODY(resq)->nd_body)->nd_body == NULL && !RNODE_RESBODY(resq)->nd_exc_var) { + // empty body + ADD_SYNTHETIC_INSN(ret, nd_line(RNODE_RESBODY(resq)->nd_body), -1, putnil); + } + else { + CHECK(COMPILE(ret, "resbody body", RNODE_RESBODY(resq)->nd_body)); + } + if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) { ADD_INSN(ret, line_node, nop); } ADD_INSN(ret, line_node, leave); ADD_LABEL(ret, label_miss); - resq = resq->nd_head; + resq = RNODE_RESBODY(resq)->nd_next; } return COMPILE_OK; } @@ -7641,10 +8904,10 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, static int compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { - const int line = nd_line(node); + const int line = nd_line(RNODE_ENSURE(node)->nd_ensr); const NODE *line_node = node; DECL_ANCHOR(ensr); - const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr, + const rb_iseq_t *ensure = NEW_CHILD_ISEQ(RNODE_ENSURE(node)->nd_ensr, rb_str_concat(rb_str_new2 ("ensure in "), ISEQ_BODY(iseq)->location.label), ISEQ_TYPE_ENSURE, line); LABEL *lstart = NEW_LABEL(line); @@ -7657,17 +8920,17 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, struct ensure_range *erange; INIT_ANCHOR(ensr); - CHECK(COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr)); + CHECK(COMPILE_POPPED(ensr, "ensure ensr", RNODE_ENSURE(node)->nd_ensr)); last = ensr->last; last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave); er.begin = lstart; er.end = lend; er.next = 0; - push_ensure_entry(iseq, &enl, &er, node->nd_ensr); + push_ensure_entry(iseq, &enl, &er, RNODE_ENSURE(node)->nd_ensr); ADD_LABEL(ret, lstart); - CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave))); + CHECK(COMPILE_(ret, "ensure head", RNODE_ENSURE(node)->nd_head, (popped | last_leave))); ADD_LABEL(ret, lend); ADD_SEQ(ret, ensr); if (!popped && last_leave) ADD_INSN(ret, line_node, putnil); @@ -7696,7 +8959,7 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, enum rb_iseq_type type = ISEQ_BODY(iseq)->type; const rb_iseq_t *is = iseq; enum rb_iseq_type t = type; - const NODE *retval = node->nd_stts; + const NODE *retval = RNODE_RETURN(node)->nd_stts; LABEL *splabel = 0; while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) { @@ -7746,6 +9009,27 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, return COMPILE_OK; } +static bool +drop_unreachable_return(LINK_ANCHOR *ret) +{ + LINK_ELEMENT *i = ret->last, *last; + if (!i) return false; + if (IS_TRACE(i)) i = i->prev; + if (!IS_INSN(i) || !IS_INSN_ID(i, putnil)) return false; + last = i = i->prev; + if (IS_ADJUST(i)) i = i->prev; + if (!IS_INSN(i)) return false; + switch (INSN_OF(i)) { + case BIN(leave): + case BIN(jump): + break; + default: + return false; + } + (ret->last = last->prev)->next = NULL; + return true; +} + static int compile_evstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { @@ -7779,11 +9063,11 @@ qcall_branch_start(rb_iseq_t *iseq, LINK_ANCHOR *const recv, VALUE *branches, co LABEL *else_label = NEW_LABEL(nd_line(line_node)); VALUE br = 0; - br = decl_branch_base(iseq, node, "&."); + br = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "&."); *branches = br; ADD_INSN(recv, line_node, dup); ADD_INSNL(recv, line_node, branchnil, else_label); - add_trace_branch_coverage(iseq, recv, node, 0, "then", br); + add_trace_branch_coverage(iseq, recv, nd_code_loc(node), nd_node_id(node), 0, "then", br); return else_label; } @@ -7795,7 +9079,7 @@ qcall_branch_end(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *else_label, VAL end_label = NEW_LABEL(nd_line(line_node)); ADD_INSNL(ret, line_node, jump, end_label); ADD_LABEL(ret, else_label); - add_trace_branch_coverage(iseq, ret, node, 1, "else", branches); + add_trace_branch_coverage(iseq, ret, nd_code_loc(node), nd_node_id(node), 1, "else", branches); ADD_LABEL(ret, end_label); } @@ -7805,13 +9089,14 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE /* optimization shortcut * "literal".freeze -> opt_str_freeze("literal") */ - if (node->nd_recv && nd_type_p(node->nd_recv, NODE_STR) && - (node->nd_mid == idFreeze || node->nd_mid == idUMinus) && - node->nd_args == NULL && + if (get_nd_recv(node) && + (nd_type_p(get_nd_recv(node), NODE_STR) || nd_type_p(get_nd_recv(node), NODE_FILE)) && + (get_node_call_nd_mid(node) == idFreeze || get_node_call_nd_mid(node) == idUMinus) && + get_nd_args(node) == NULL && ISEQ_COMPILE_DATA(iseq)->current_block == NULL && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) { - VALUE str = rb_fstring(node->nd_recv->nd_lit); - if (node->nd_mid == idUMinus) { + VALUE str = get_string_value(get_nd_recv(node)); + if (get_node_call_nd_mid(node) == idUMinus) { ADD_INSN2(ret, line_node, opt_str_uminus, str, new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE)); } @@ -7825,25 +9110,6 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE } return TRUE; } - /* optimization shortcut - * obj["literal"] -> opt_aref_with(obj, "literal") - */ - if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args && - nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 1 && - nd_type_p(node->nd_args->nd_head, NODE_STR) && - ISEQ_COMPILE_DATA(iseq)->current_block == NULL && - !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal && - ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) { - VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); - CHECK(COMPILE(ret, "recv", node->nd_recv)); - ADD_INSN2(ret, line_node, opt_aref_with, str, - new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE)); - RB_OBJ_WRITTEN(iseq, Qundef, str); - if (popped) { - ADD_INSN(ret, line_node, pop); - } - return TRUE; - } return FALSE; } @@ -7878,12 +9144,12 @@ iseq_builtin_function_name(const enum node_type type, const NODE *recv, ID mid) if (recv) { switch (nd_type(recv)) { case NODE_VCALL: - if (recv->nd_mid == rb_intern("__builtin")) { + if (RNODE_VCALL(recv)->nd_mid == rb_intern("__builtin")) { return name; } break; case NODE_CONST: - if (recv->nd_vid == rb_intern("Primitive")) { + if (RNODE_CONST(recv)->nd_vid == rb_intern("Primitive")) { return name; } break; @@ -7964,16 +9230,82 @@ delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *arg } } +// Compile Primitive.attr! :leaf, ... +static int +compile_builtin_attr(rb_iseq_t *iseq, const NODE *node) +{ + VALUE symbol; + VALUE string; + if (!node) goto no_arg; + while (node) { + if (!nd_type_p(node, NODE_LIST)) goto bad_arg; + const NODE *next = RNODE_LIST(node)->nd_next; + + node = RNODE_LIST(node)->nd_head; + if (!node) goto no_arg; + switch (nd_type(node)) { + case NODE_SYM: + symbol = rb_node_sym_string_val(node); + break; + default: + goto bad_arg; + } + + if (!SYMBOL_P(symbol)) goto non_symbol_arg; + + string = rb_sym2str(symbol); + if (strcmp(RSTRING_PTR(string), "leaf") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF; + } + else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK; + } + else if (strcmp(RSTRING_PTR(string), "use_block") == 0) { + iseq_set_use_block(iseq); + } + else if (strcmp(RSTRING_PTR(string), "c_trace") == 0) { + // Let the iseq act like a C method in backtraces + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE; + } + else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS; + } + else { + goto unknown_arg; + } + node = next; + } + return COMPILE_OK; + no_arg: + COMPILE_ERROR(ERROR_ARGS "attr!: no argument"); + return COMPILE_NG; + non_symbol_arg: + COMPILE_ERROR(ERROR_ARGS "non symbol argument to attr!: %s", rb_builtin_class_name(symbol)); + return COMPILE_NG; + unknown_arg: + COMPILE_ERROR(ERROR_ARGS "unknown argument to attr!: %s", RSTRING_PTR(string)); + return COMPILE_NG; + bad_arg: + UNKNOWN_NODE("attr!", node, COMPILE_NG); +} + static int compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, const NODE *line_node, int popped) { + VALUE name; + if (!node) goto no_arg; if (!nd_type_p(node, NODE_LIST)) goto bad_arg; - if (node->nd_next) goto too_many_arg; - node = node->nd_head; + if (RNODE_LIST(node)->nd_next) goto too_many_arg; + node = RNODE_LIST(node)->nd_head; if (!node) goto no_arg; - if (!nd_type_p(node, NODE_LIT)) goto bad_arg; - VALUE name = node->nd_lit; + switch (nd_type(node)) { + case NODE_SYM: + name = rb_node_sym_string_val(node); + break; + default: + goto bad_arg; + } if (!SYMBOL_P(name)) goto non_symbol_arg; if (!popped) { compile_lvar(iseq, ret, line_node, SYM2ID(name)); @@ -7997,8 +9329,8 @@ static NODE * mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node) { const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node; - if (nd_type(node) == NODE_IF && node->nd_cond == cond_node) { - return node->nd_body; + if (nd_type(node) == NODE_IF && RNODE_IF(node)->nd_cond == cond_node) { + return RNODE_IF(node)->nd_body; } else { rb_bug("mandatory_node: can't find mandatory node"); @@ -8012,8 +9344,9 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N struct rb_args_info args = { .pre_args_num = ISEQ_BODY(iseq)->param.lead_num, }; - NODE args_node; - rb_node_init(&args_node, NODE_ARGS, 0, 0, (VALUE)&args); + rb_node_args_t args_node; + rb_node_init(RNODE(&args_node), NODE_ARGS); + args_node.nd_ainfo = args; // local table without non-mandatory parameters const int skip_local_size = ISEQ_BODY(iseq)->param.size - ISEQ_BODY(iseq)->param.lead_num; @@ -8034,24 +9367,23 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N tbl->ids[i] = ISEQ_BODY(iseq)->local_table[i + skip_local_size]; } - NODE scope_node; - rb_node_init(&scope_node, NODE_SCOPE, (VALUE)tbl, (VALUE)mandatory_node(iseq, node), (VALUE)&args_node); + rb_node_scope_t scope_node; + rb_node_init(RNODE(&scope_node), NODE_SCOPE); + scope_node.nd_tbl = tbl; + scope_node.nd_body = mandatory_node(iseq, node); + scope_node.nd_parent = NULL; + scope_node.nd_args = &args_node; - rb_ast_body_t ast = { - .root = &scope_node, - .compile_option = 0, - .script_lines = ISEQ_BODY(iseq)->variable.script_lines, - }; + VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node)); - int prev_inline_index = GET_VM()->builtin_inline_index; - - ISEQ_BODY(iseq)->mandatory_only_iseq = - rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq), + const rb_iseq_t *mandatory_only_iseq = + rb_iseq_new_with_opt(ast_value, rb_iseq_base_label(iseq), rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(nd_line(line_node)), NULL, 0, - ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option); + nd_line(line_node), NULL, 0, + ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option, + ISEQ_BODY(iseq)->variable.script_lines); + RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->mandatory_only_iseq, (VALUE)mandatory_only_iseq); - GET_VM()->builtin_inline_index = prev_inline_index; ALLOCV_END(idtmp); return COMPILE_OK; } @@ -8060,15 +9392,15 @@ static int compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped, const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func) { - NODE *args_node = node->nd_args; + NODE *args_node = get_nd_args(node); if (parent_block != NULL) { - COMPILE_ERROR(iseq, nd_line(line_node), "should not call builtins here."); + COMPILE_ERROR(ERROR_ARGS_AT(line_node) "should not call builtins here."); return COMPILE_NG; } else { # define BUILTIN_INLINE_PREFIX "_bi" - char inline_func[DECIMAL_SIZE_OF_BITS(sizeof(int) * CHAR_BIT) + sizeof(BUILTIN_INLINE_PREFIX)]; + char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)]; bool cconst = false; retry:; const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func); @@ -8083,13 +9415,10 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD } else if (strcmp("cinit!", builtin_func) == 0) { // ignore - GET_VM()->builtin_inline_index++; return COMPILE_OK; } else if (strcmp("attr!", builtin_func) == 0) { - // There's only "inline" attribute for now - ISEQ_BODY(iseq)->builtin_inline_p = true; - return COMPILE_OK; + return compile_builtin_attr(iseq, args_node); } else if (strcmp("arg!", builtin_func) == 0) { return compile_builtin_arg(iseq, ret, args_node, line_node, popped); @@ -8113,10 +9442,7 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD return COMPILE_NG; } - if (GET_VM()->builtin_inline_index == INT_MAX) { - rb_bug("builtin inline function index overflow:%s", builtin_func); - } - int inline_index = GET_VM()->builtin_inline_index++; + int inline_index = nd_line(node); snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index); builtin_func = inline_func; args_node = NULL; @@ -8125,7 +9451,7 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD if (cconst) { typedef VALUE(*builtin_func0)(void *, VALUE); - VALUE const_val = (*(builtin_func0)bf->func_ptr)(NULL, Qnil); + VALUE const_val = (*(builtin_func0)(uintptr_t)bf->func_ptr)(NULL, Qnil); ADD_INSN1(ret, line_node, putobject, const_val); return COMPILE_OK; } @@ -8165,7 +9491,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co */ DECL_ANCHOR(recv); DECL_ANCHOR(args); - ID mid = node->nd_mid; + ID mid = get_node_call_nd_mid(node); VALUE argc; unsigned int flag = 0; struct rb_callinfo_kwarg *keywords = NULL; @@ -8177,6 +9503,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co INIT_ANCHOR(recv); INIT_ANCHOR(args); + #if OPT_SUPPORT_JOKE if (nd_type_p(node, NODE_VCALL)) { ID id_bitblt; @@ -8213,20 +9540,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co labels_table = st_init_numtable(); ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table; } - if (nd_type_p(node->nd_args->nd_head, NODE_LIT) && - SYMBOL_P(node->nd_args->nd_head->nd_lit)) { - - label_name = node->nd_args->nd_head->nd_lit; - if (!st_lookup(labels_table, (st_data_t)label_name, &data)) { - label = NEW_LABEL(nd_line(line_node)); - label->position = nd_line(line_node); - st_insert(labels_table, (st_data_t)label_name, (st_data_t)label); - } - else { - label = (LABEL *)data; - } - } - else { + { COMPILE_ERROR(ERROR_ARGS "invalid goto/label format"); return COMPILE_NG; } @@ -8244,7 +9558,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co const char *builtin_func; if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && - (builtin_func = iseq_builtin_function_name(type, node->nd_recv, mid)) != NULL) { + (builtin_func = iseq_builtin_function_name(type, get_nd_recv(node), mid)) != NULL) { return compile_builtin_function_call(iseq, ret, node, line_node, popped, parent_block, args, builtin_func); } @@ -8254,16 +9568,16 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co int idx, level; if (mid == idCall && - nd_type_p(node->nd_recv, NODE_LVAR) && - iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) { - ADD_INSN2(recv, node->nd_recv, getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); + nd_type_p(get_nd_recv(node), NODE_LVAR) && + iseq_block_param_id_p(iseq, RNODE_LVAR(get_nd_recv(node))->nd_vid, &idx, &level)) { + ADD_INSN2(recv, get_nd_recv(node), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } else if (private_recv_p(node)) { ADD_INSN(recv, node, putself); flag |= VM_CALL_FCALL; } else { - CHECK(COMPILE(recv, "recv", node->nd_recv)); + CHECK(COMPILE(recv, "recv", get_nd_recv(node))); } if (type == NODE_QCALL) { @@ -8277,7 +9591,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co /* args */ if (type != NODE_VCALL) { - argc = setup_args(iseq, args, node->nd_args, &flag, &keywords); + argc = setup_args(iseq, args, get_nd_args(node), &flag, &keywords); CHECK(!NIL_P(argc)); } else { @@ -8285,6 +9599,17 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co } ADD_SEQ(ret, recv); + + bool inline_new = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction && + mid == rb_intern("new") && + parent_block == NULL && + !(flag & VM_CALL_ARGS_BLOCKARG); + + if (inline_new) { + ADD_INSN(ret, node, putnil); + ADD_INSN(ret, node, swap); + } + ADD_SEQ(ret, args); debugp_param("call args argc", argc); @@ -8298,7 +9623,40 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co flag |= VM_CALL_FCALL; } - ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords); + if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) { + ADD_INSN(ret, line_node, splatkw); + } + + LABEL *not_basic_new = NEW_LABEL(nd_line(node)); + LABEL *not_basic_new_finish = NEW_LABEL(nd_line(node)); + + if (inline_new) { + // Jump unless the receiver uses the "basic" implementation of "new" + VALUE ci; + if (flag & VM_CALL_FORWARDING) { + ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc) + 1, flag, keywords, 0); + } + else { + ci = (VALUE)new_callinfo(iseq, mid, NUM2INT(argc), flag, keywords, 0); + } + ADD_INSN2(ret, node, opt_new, ci, not_basic_new); + LABEL_REF(not_basic_new); + + // optimized path + ADD_SEND_R(ret, line_node, rb_intern("initialize"), argc, parent_block, INT2FIX(flag | VM_CALL_FCALL), keywords); + ADD_INSNL(ret, line_node, jump, not_basic_new_finish); + + ADD_LABEL(ret, not_basic_new); + // Fall back to normal send + ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords); + ADD_INSN(ret, line_node, swap); + + ADD_LABEL(ret, not_basic_new_finish); + ADD_INSN(ret, line_node, pop); + } + else { + ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords); + } qcall_branch_end(iseq, ret, else_label, branches, node, line_node); if (popped) { @@ -8314,8 +9672,7 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node VALUE argc; unsigned int flag = 0; int asgnflag = 0; - ID id = node->nd_mid; - int boff = 0; + ID id = RNODE_OP_ASGN1(node)->nd_mid; /* * a[x] (op)= y @@ -8343,22 +9700,20 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node if (!popped) { ADD_INSN(ret, node, putnil); } - asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node); + asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node, RNODE_OP_ASGN1(node)->nd_recv); CHECK(asgnflag != -1); - switch (nd_type(node->nd_args->nd_head)) { + switch (nd_type(RNODE_OP_ASGN1(node)->nd_index)) { case NODE_ZLIST: argc = INT2FIX(0); break; - case NODE_BLOCK_PASS: - boff = 1; - /* fall through */ default: - argc = setup_args(iseq, ret, node->nd_args->nd_head, &flag, NULL); + argc = setup_args(iseq, ret, RNODE_OP_ASGN1(node)->nd_index, &flag, NULL); CHECK(!NIL_P(argc)); } - ADD_INSN1(ret, node, dupn, FIXNUM_INC(argc, 1 + boff)); + int dup_argn = FIX2INT(argc) + 1; + ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn)); flag |= asgnflag; - ADD_SEND_WITH_FLAG(ret, node, idAREF, argc, INT2FIX(flag)); + ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_ARGS_SPLAT_MUT), NULL); if (id == idOROP || id == idANDOP) { /* a[x] ||= y or a[x] &&= y @@ -8381,64 +9736,63 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node } ADD_INSN(ret, node, pop); - CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body)); + CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue)); if (!popped) { - ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff)); + ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1)); } if (flag & VM_CALL_ARGS_SPLAT) { - ADD_INSN1(ret, node, newarray, INT2FIX(1)); - if (boff > 0) { - ADD_INSN1(ret, node, dupn, INT2FIX(3)); + if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) { ADD_INSN(ret, node, swap); - ADD_INSN(ret, node, pop); - } - ADD_INSN(ret, node, concatarray); - if (boff > 0) { - ADD_INSN1(ret, node, setn, INT2FIX(3)); - ADD_INSN(ret, node, pop); - ADD_INSN(ret, node, pop); + ADD_INSN1(ret, node, splatarray, Qtrue); + ADD_INSN(ret, node, swap); + flag |= VM_CALL_ARGS_SPLAT_MUT; } - ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag)); + ADD_INSN1(ret, node, pushtoarray, INT2FIX(1)); + ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL); } else { - if (boff > 0) - ADD_INSN(ret, node, swap); - ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag)); + ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL); } ADD_INSN(ret, node, pop); ADD_INSNL(ret, node, jump, lfin); ADD_LABEL(ret, label); if (!popped) { - ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff)); + ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1)); } - ADD_INSN1(ret, node, adjuststack, FIXNUM_INC(argc, 2+boff)); + ADD_INSN1(ret, node, adjuststack, INT2FIX(dup_argn+1)); ADD_LABEL(ret, lfin); } else { - CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body)); + CHECK(COMPILE(ret, "NODE_OP_ASGN1 nd_rvalue: ", RNODE_OP_ASGN1(node)->nd_rvalue)); ADD_SEND(ret, node, id, INT2FIX(1)); if (!popped) { - ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 2+boff)); + ADD_INSN1(ret, node, setn, INT2FIX(dup_argn+1)); } if (flag & VM_CALL_ARGS_SPLAT) { - ADD_INSN1(ret, node, newarray, INT2FIX(1)); - if (boff > 0) { - ADD_INSN1(ret, node, dupn, INT2FIX(3)); + if (flag & VM_CALL_KW_SPLAT) { + ADD_INSN1(ret, node, topn, INT2FIX(2)); + if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) { + ADD_INSN1(ret, node, splatarray, Qtrue); + flag |= VM_CALL_ARGS_SPLAT_MUT; + } ADD_INSN(ret, node, swap); + ADD_INSN1(ret, node, pushtoarray, INT2FIX(1)); + ADD_INSN1(ret, node, setn, INT2FIX(2)); ADD_INSN(ret, node, pop); } - ADD_INSN(ret, node, concatarray); - if (boff > 0) { - ADD_INSN1(ret, node, setn, INT2FIX(3)); - ADD_INSN(ret, node, pop); - ADD_INSN(ret, node, pop); + else { + if (!(flag & VM_CALL_ARGS_SPLAT_MUT)) { + ADD_INSN(ret, node, swap); + ADD_INSN1(ret, node, splatarray, Qtrue); + ADD_INSN(ret, node, swap); + flag |= VM_CALL_ARGS_SPLAT_MUT; + } + ADD_INSN1(ret, node, pushtoarray, INT2FIX(1)); } - ADD_SEND_WITH_FLAG(ret, node, idASET, argc, INT2FIX(flag)); + ADD_SEND_R(ret, node, idASET, argc, NULL, INT2FIX(flag), NULL); } else { - if (boff > 0) - ADD_INSN(ret, node, swap); - ADD_SEND_WITH_FLAG(ret, node, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag)); + ADD_SEND_R(ret, node, idASET, FIXNUM_INC(argc, 1), NULL, INT2FIX(flag), NULL); } ADD_INSN(ret, node, pop); } @@ -8449,8 +9803,8 @@ static int compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { const int line = nd_line(node); - ID atype = node->nd_next->nd_mid; - ID vid = node->nd_next->nd_vid, aid = rb_id_attrset(vid); + ID atype = RNODE_OP_ASGN2(node)->nd_mid; + ID vid = RNODE_OP_ASGN2(node)->nd_vid, aid = rb_id_attrset(vid); int asgnflag; LABEL *lfin = NEW_LABEL(line); LABEL *lcfin = NEW_LABEL(line); @@ -8480,6 +9834,17 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node lfin: # o ? pop # o + # or (popped) + if lcfin # r + eval v # r v + send a= # ? + jump lfin # ? + + lcfin: # r + + lfin: # ? + pop # + # and dup # r o o unless lcfin @@ -8497,9 +9862,9 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node */ - asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node); + asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node, RNODE_OP_ASGN2(node)->nd_recv); CHECK(asgnflag != -1); - if (node->nd_next->nd_aid) { + if (RNODE_OP_ASGN2(node)->nd_aid) { lskip = NEW_LABEL(line); ADD_INSN(ret, node, dup); ADD_INSNL(ret, node, branchnil, lskip); @@ -8508,52 +9873,54 @@ compile_op_asgn2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_SEND_WITH_FLAG(ret, node, vid, INT2FIX(0), INT2FIX(asgnflag)); if (atype == idOROP || atype == idANDOP) { - ADD_INSN(ret, node, dup); + if (!popped) { + ADD_INSN(ret, node, dup); + } if (atype == idOROP) { ADD_INSNL(ret, node, branchif, lcfin); } else { /* idANDOP */ ADD_INSNL(ret, node, branchunless, lcfin); } - ADD_INSN(ret, node, pop); - CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); - ADD_INSN(ret, node, swap); - ADD_INSN1(ret, node, topn, INT2FIX(1)); + if (!popped) { + ADD_INSN(ret, node, pop); + } + CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value)); + if (!popped) { + ADD_INSN(ret, node, swap); + ADD_INSN1(ret, node, topn, INT2FIX(1)); + } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); ADD_INSNL(ret, node, jump, lfin); ADD_LABEL(ret, lcfin); - ADD_INSN(ret, node, swap); + if (!popped) { + ADD_INSN(ret, node, swap); + } ADD_LABEL(ret, lfin); - ADD_INSN(ret, node, pop); - if (lskip) { - ADD_LABEL(ret, lskip); - } - if (popped) { - /* we can apply more optimize */ - ADD_INSN(ret, node, pop); - } } else { - CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value)); + CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", RNODE_OP_ASGN2(node)->nd_value)); ADD_SEND(ret, node, atype, INT2FIX(1)); if (!popped) { ADD_INSN(ret, node, swap); ADD_INSN1(ret, node, topn, INT2FIX(1)); } ADD_SEND_WITH_FLAG(ret, node, aid, INT2FIX(1), INT2FIX(asgnflag)); - if (lskip && popped) { - ADD_LABEL(ret, lskip); - } - ADD_INSN(ret, node, pop); - if (lskip && !popped) { - ADD_LABEL(ret, lskip); - } + } + if (lskip && popped) { + ADD_LABEL(ret, lskip); + } + ADD_INSN(ret, node, pop); + if (lskip && !popped) { + ADD_LABEL(ret, lskip); } return COMPILE_OK; } +static int compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value); + static int compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { @@ -8562,21 +9929,21 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node LABEL *lassign = 0; ID mid; - switch (nd_type(node->nd_head)) { + switch (nd_type(RNODE_OP_CDECL(node)->nd_head)) { case NODE_COLON3: ADD_INSN1(ret, node, putobject, rb_cObject); break; case NODE_COLON2: - CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head)); + CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", RNODE_COLON2(RNODE_OP_CDECL(node)->nd_head)->nd_head)); break; default: COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL", - ruby_node_name(nd_type(node->nd_head))); + ruby_node_name(nd_type(RNODE_OP_CDECL(node)->nd_head))); return COMPILE_NG; } - mid = node->nd_head->nd_mid; + mid = get_node_colon_nd_mid(RNODE_OP_CDECL(node)->nd_head); /* cref */ - if (node->nd_aid == idOROP) { + if (RNODE_OP_CDECL(node)->nd_aid == idOROP) { lassign = NEW_LABEL(line); ADD_INSN(ret, node, dup); /* cref cref */ ADD_INSN3(ret, node, defined, INT2FIX(DEFINED_CONST_FROM), @@ -8587,17 +9954,17 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_INSN1(ret, node, putobject, Qtrue); ADD_INSN1(ret, node, getconstant, ID2SYM(mid)); /* cref obj */ - if (node->nd_aid == idOROP || node->nd_aid == idANDOP) { + if (RNODE_OP_CDECL(node)->nd_aid == idOROP || RNODE_OP_CDECL(node)->nd_aid == idANDOP) { lfin = NEW_LABEL(line); if (!popped) ADD_INSN(ret, node, dup); /* cref [obj] obj */ - if (node->nd_aid == idOROP) + if (RNODE_OP_CDECL(node)->nd_aid == idOROP) ADD_INSNL(ret, node, branchif, lfin); else /* idANDOP */ ADD_INSNL(ret, node, branchunless, lfin); /* cref [obj] */ if (!popped) ADD_INSN(ret, node, pop); /* cref */ if (lassign) ADD_LABEL(ret, lassign); - CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value)); /* cref value */ if (popped) ADD_INSN1(ret, node, topn, INT2FIX(1)); /* cref value cref */ @@ -8611,9 +9978,9 @@ compile_op_cdecl(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_INSN(ret, node, pop); /* [value] */ } else { - CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value)); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_OP_CDECL(node)->shareability, RNODE_OP_CDECL(node)->nd_head, RNODE_OP_CDECL(node)->nd_value)); /* cref obj value */ - ADD_CALL(ret, node, node->nd_aid, INT2FIX(1)); + ADD_CALL(ret, node, RNODE_OP_CDECL(node)->nd_aid, INT2FIX(1)); /* cref value */ ADD_INSN(ret, node, swap); /* value cref */ if (!popped) { @@ -8632,11 +9999,11 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *lfin = NEW_LABEL(line); LABEL *lassign; - if (type == NODE_OP_ASGN_OR && !nd_type_p(node->nd_head, NODE_IVAR)) { + if (type == NODE_OP_ASGN_OR && !nd_type_p(RNODE_OP_ASGN_OR(node)->nd_head, NODE_IVAR)) { LABEL *lfinish[2]; lfinish[0] = lfin; lfinish[1] = 0; - defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse); + defined_expr(iseq, ret, RNODE_OP_ASGN_OR(node)->nd_head, lfinish, Qfalse, false); lassign = lfinish[1]; if (!lassign) { lassign = NEW_LABEL(line); @@ -8647,8 +10014,11 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, lassign = NEW_LABEL(line); } - CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head)); - ADD_INSN(ret, node, dup); + CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", RNODE_OP_ASGN_OR(node)->nd_head)); + + if (!popped) { + ADD_INSN(ret, node, dup); + } if (type == NODE_OP_ASGN_AND) { ADD_INSNL(ret, node, branchunless, lfin); @@ -8657,15 +10027,13 @@ compile_op_log(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, ADD_INSNL(ret, node, branchif, lfin); } - ADD_INSN(ret, node, pop); - ADD_LABEL(ret, lassign); - CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value)); - ADD_LABEL(ret, lfin); - - if (popped) { - /* we can apply more optimize */ + if (!popped) { ADD_INSN(ret, node, pop); } + + ADD_LABEL(ret, lassign); + CHECK(COMPILE_(ret, "NODE_OP_ASGN_AND/OR#nd_value", RNODE_OP_ASGN_OR(node)->nd_value, popped)); + ADD_LABEL(ret, lfin); return COMPILE_OK; } @@ -8678,13 +10046,22 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i unsigned int flag = 0; struct rb_callinfo_kwarg *keywords = NULL; const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block; + int use_block = 1; INIT_ANCHOR(args); ISEQ_COMPILE_DATA(iseq)->current_block = NULL; + if (type == NODE_SUPER) { - VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords); + VALUE vargc = setup_args(iseq, args, RNODE_SUPER(node)->nd_args, &flag, &keywords); CHECK(!NIL_P(vargc)); argc = FIX2INT(vargc); + if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) { + ADD_INSN(args, node, splatkw); + } + + if (flag & VM_CALL_ARGS_BLOCKARG) { + use_block = 0; + } } else { /* NODE_ZSUPER */ @@ -8702,6 +10079,13 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i ADD_GETLOCAL(args, node, idx, lvar_level); } + /* forward ... */ + if (local_body->param.flags.forwardable) { + flag |= VM_CALL_FORWARDING; + int idx = local_body->local_table_size - get_local_var_idx(liseq, idDot3); + ADD_GETLOCAL(args, node, idx, lvar_level); + } + if (local_body->param.flags.has_opt) { /* optional arguments */ int j; @@ -8716,7 +10100,7 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i /* rest argument */ int idx = local_body->local_table_size - local_body->param.rest_start; ADD_GETLOCAL(args, node, idx, lvar_level); - ADD_INSN1(args, node, splatarray, Qfalse); + ADD_INSN1(args, node, splatarray, RBOOL(local_body->param.flags.has_post)); argc = local_body->param.rest_start + 1; flag |= VM_CALL_ARGS_SPLAT; @@ -8732,8 +10116,8 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i int idx = local_body->local_table_size - (post_start + j); ADD_GETLOCAL(args, node, idx, lvar_level); } - ADD_INSN1(args, node, newarray, INT2FIX(j)); - ADD_INSN (args, node, concatarray); + ADD_INSN1(args, node, pushtoarray, INT2FIX(j)); + flag |= VM_CALL_ARGS_SPLAT_MUT; /* argc is settled at above */ } else { @@ -8755,14 +10139,11 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i if (local_body->param.flags.has_kwrest) { int idx = local_body->local_table_size - local_kwd->rest_start; ADD_GETLOCAL(args, node, idx, lvar_level); - if (local_kwd->num > 0) { - ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0)); - flag |= VM_CALL_KW_SPLAT_MUT; - } + RUBY_ASSERT(local_kwd->num > 0); + ADD_SEND (args, node, rb_intern("dup"), INT2FIX(0)); } else { ADD_INSN1(args, node, newhash, INT2FIX(0)); - flag |= VM_CALL_KW_SPLAT_MUT; } for (i = 0; i < local_kwd->num; ++i) { ID id = local_kwd->table[i]; @@ -8771,35 +10152,33 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i ADD_GETLOCAL(args, node, idx, lvar_level); } ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1)); - if (local_body->param.flags.has_rest) { - ADD_INSN1(args, node, newarray, INT2FIX(1)); - ADD_INSN (args, node, concatarray); - --argc; - } - flag |= VM_CALL_KW_SPLAT; + flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT; } else if (local_body->param.flags.has_kwrest) { int idx = local_body->local_table_size - local_kwd->rest_start; ADD_GETLOCAL(args, node, idx, lvar_level); - - if (local_body->param.flags.has_rest) { - ADD_INSN1(args, node, newarray, INT2FIX(1)); - ADD_INSN (args, node, concatarray); - } - else { - argc++; - } + argc++; flag |= VM_CALL_KW_SPLAT; } } + if (use_block && parent_block == NULL) { + iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq); + } + flag |= VM_CALL_SUPER | VM_CALL_FCALL; if (type == NODE_ZSUPER) flag |= VM_CALL_ZSUPER; ADD_INSN(ret, node, putself); ADD_SEQ(ret, args); - ADD_INSN2(ret, node, invokesuper, - new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL), - parent_block); + + const struct rb_callinfo * ci = new_callinfo(iseq, 0, argc, flag, keywords, parent_block != NULL); + + if (vm_ci_flag(ci) & VM_CALL_FORWARDING) { + ADD_INSN2(ret, node, invokesuperforward, ci, parent_block); + } + else { + ADD_INSN2(ret, node, invokesuper, ci, parent_block); + } if (popped) { ADD_INSN(ret, node, pop); @@ -8826,8 +10205,8 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i default: /* valid */; } - if (node->nd_head) { - argc = setup_args(iseq, args, node->nd_head, &flag, &keywords); + if (RNODE_YIELD(node)->nd_head) { + argc = setup_args(iseq, args, RNODE_YIELD(node)->nd_head, &flag, &keywords); CHECK(!NIL_P(argc)); } else { @@ -8836,6 +10215,7 @@ compile_yield(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i ADD_SEQ(ret, args); ADD_INSN1(ret, node, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE)); + iseq_set_use_block(ISEQ_BODY(iseq)->local_iseq); if (popped) { ADD_INSN(ret, node, pop); @@ -8861,17 +10241,21 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i INIT_ANCHOR(val); switch ((int)type) { case NODE_MATCH: - ADD_INSN1(recv, node, putobject, node->nd_lit); - ADD_INSN2(val, node, getspecial, INT2FIX(0), - INT2FIX(0)); + { + VALUE re = rb_node_regx_string_val(node); + RB_OBJ_SET_FROZEN_SHAREABLE(re); + ADD_INSN1(recv, node, putobject, re); + ADD_INSN2(val, node, getspecial, INT2FIX(0), + INT2FIX(0)); + } break; case NODE_MATCH2: - CHECK(COMPILE(recv, "receiver", node->nd_recv)); - CHECK(COMPILE(val, "value", node->nd_value)); + CHECK(COMPILE(recv, "receiver", RNODE_MATCH2(node)->nd_recv)); + CHECK(COMPILE(val, "value", RNODE_MATCH2(node)->nd_value)); break; case NODE_MATCH3: - CHECK(COMPILE(recv, "receiver", node->nd_value)); - CHECK(COMPILE(val, "value", node->nd_recv)); + CHECK(COMPILE(recv, "receiver", RNODE_MATCH3(node)->nd_value)); + CHECK(COMPILE(val, "value", RNODE_MATCH3(node)->nd_recv)); break; } @@ -8879,8 +10263,8 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i ADD_SEQ(ret, val); ADD_SEND(ret, node, idEqTilde, INT2FIX(1)); - if (node->nd_args) { - compile_named_capture_assign(iseq, ret, node->nd_args); + if (nd_type_p(node, NODE_MATCH2) && RNODE_MATCH2(node)->nd_args) { + compile_named_capture_assign(iseq, ret, RNODE_MATCH2(node)->nd_args); } if (popped) { @@ -8892,43 +10276,38 @@ compile_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i static int compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { - const int line = nd_line(node); - if (rb_is_const_id(node->nd_mid)) { + if (rb_is_const_id(RNODE_COLON2(node)->nd_mid)) { /* constant */ - LABEL *lend = NEW_LABEL(line); - int ic_index = ISEQ_BODY(iseq)->ic_size++; - - DECL_ANCHOR(pref); - DECL_ANCHOR(body); - - INIT_ANCHOR(pref); - INIT_ANCHOR(body); - CHECK(compile_const_prefix(iseq, node, pref, body)); - if (LIST_INSN_SIZE_ZERO(pref)) { - if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { - ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index)); - } - else { + VALUE segments; + if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache && + (segments = collect_const_segments(iseq, node))) { + ISEQ_BODY(iseq)->ic_size++; + ADD_INSN1(ret, node, opt_getconstant_path, segments); + RB_OBJ_WRITTEN(iseq, Qundef, segments); + } + else { + /* constant */ + DECL_ANCHOR(pref); + DECL_ANCHOR(body); + + INIT_ANCHOR(pref); + INIT_ANCHOR(body); + CHECK(compile_const_prefix(iseq, node, pref, body)); + if (LIST_INSN_SIZE_ZERO(pref)) { ADD_INSN(ret, node, putnil); + ADD_SEQ(ret, body); } - - ADD_SEQ(ret, body); - - if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { - ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index)); - ADD_LABEL(ret, lend); + else { + ADD_SEQ(ret, pref); + ADD_SEQ(ret, body); } } - else { - ADD_SEQ(ret, pref); - ADD_SEQ(ret, body); - } } else { /* function call */ ADD_CALL_RECEIVER(ret, node); - CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head)); - ADD_CALL(ret, node, node->nd_mid, INT2FIX(1)); + CHECK(COMPILE(ret, "colon2#nd_head", RNODE_COLON2(node)->nd_head)); + ADD_CALL(ret, node, RNODE_COLON2(node)->nd_mid, INT2FIX(1)); } if (popped) { ADD_INSN(ret, node, pop); @@ -8939,25 +10318,20 @@ compile_colon2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, static int compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped) { - const int line = nd_line(node); - LABEL *lend = NEW_LABEL(line); - int ic_index = ISEQ_BODY(iseq)->ic_size++; - - debugi("colon3#nd_mid", node->nd_mid); + debugi("colon3#nd_mid", RNODE_COLON3(node)->nd_mid); /* add cache insn */ if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { - ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index)); - ADD_INSN(ret, node, pop); + ISEQ_BODY(iseq)->ic_size++; + VALUE segments = rb_ary_new_from_args(2, ID2SYM(idNULL), ID2SYM(RNODE_COLON3(node)->nd_mid)); + RB_OBJ_SET_FROZEN_SHAREABLE(segments); + ADD_INSN1(ret, node, opt_getconstant_path, segments); + RB_OBJ_WRITTEN(iseq, Qundef, segments); } - - ADD_INSN1(ret, node, putobject, rb_cObject); - ADD_INSN1(ret, node, putobject, Qtrue); - ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_mid)); - - if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { - ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index)); - ADD_LABEL(ret, lend); + else { + ADD_INSN1(ret, node, putobject, rb_cObject); + ADD_INSN1(ret, node, putobject, Qtrue); + ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_COLON3(node)->nd_mid)); } if (popped) { @@ -8970,14 +10344,15 @@ static int compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const int excl) { VALUE flag = INT2FIX(excl); - const NODE *b = node->nd_beg; - const NODE *e = node->nd_end; + const NODE *b = RNODE_DOT2(node)->nd_beg; + const NODE *e = RNODE_DOT2(node)->nd_end; if (optimizable_range_item_p(b) && optimizable_range_item_p(e)) { if (!popped) { - VALUE bv = nd_type_p(b, NODE_LIT) ? b->nd_lit : Qnil; - VALUE ev = nd_type_p(e, NODE_LIT) ? e->nd_lit : Qnil; + VALUE bv = optimized_range_item(b); + VALUE ev = optimized_range_item(e); VALUE val = rb_range_new(bv, ev, excl); + rb_ractor_make_shareable(rb_obj_freeze(val)); ADD_INSN1(ret, node, putobject, val); RB_OBJ_WRITTEN(iseq, Qundef, val); } @@ -9025,14 +10400,20 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, { struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); LABEL *end_label = NEW_LABEL(nd_line(node)); - const NODE *default_value = node->nd_body->nd_value; + const NODE *default_value = get_nd_value(RNODE_KW_ARG(node)->nd_body); if (default_value == NODE_SPECIAL_REQUIRED_KEYWORD) { /* required argument. do nothing */ COMPILE_ERROR(ERROR_ARGS "unreachable"); return COMPILE_NG; } - else if (nd_type_p(default_value, NODE_LIT) || + else if (nd_type_p(default_value, NODE_SYM) || + nd_type_p(default_value, NODE_REGX) || + nd_type_p(default_value, NODE_LINE) || + nd_type_p(default_value, NODE_INTEGER) || + nd_type_p(default_value, NODE_FLOAT) || + nd_type_p(default_value, NODE_RATIONAL) || + nd_type_p(default_value, NODE_IMAGINARY) || nd_type_p(default_value, NODE_NIL) || nd_type_p(default_value, NODE_TRUE) || nd_type_p(default_value, NODE_FALSE)) { @@ -9049,7 +10430,7 @@ compile_kw_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, ADD_INSN2(ret, node, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx)); ADD_INSNL(ret, node, branchif, end_label); - CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body)); + CHECK(COMPILE_POPPED(ret, "keyword default argument", RNODE_KW_ARG(node)->nd_body)); ADD_LABEL(ret, end_label); } return COMPILE_OK; @@ -9061,41 +10442,17 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node DECL_ANCHOR(recv); DECL_ANCHOR(args); unsigned int flag = 0; - ID mid = node->nd_mid; + ID mid = RNODE_ATTRASGN(node)->nd_mid; VALUE argc; LABEL *else_label = NULL; VALUE branches = Qfalse; - /* optimization shortcut - * obj["literal"] = value -> opt_aset_with(obj, "literal", value) - */ - if (mid == idASET && !private_recv_p(node) && node->nd_args && - nd_type_p(node->nd_args, NODE_LIST) && node->nd_args->nd_alen == 2 && - nd_type_p(node->nd_args->nd_head, NODE_STR) && - ISEQ_COMPILE_DATA(iseq)->current_block == NULL && - !ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal && - ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) - { - VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); - CHECK(COMPILE(ret, "recv", node->nd_recv)); - CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head)); - if (!popped) { - ADD_INSN(ret, node, swap); - ADD_INSN1(ret, node, topn, INT2FIX(1)); - } - ADD_INSN2(ret, node, opt_aset_with, str, - new_callinfo(iseq, idASET, 2, 0, NULL, FALSE)); - RB_OBJ_WRITTEN(iseq, Qundef, str); - ADD_INSN(ret, node, pop); - return COMPILE_OK; - } - INIT_ANCHOR(recv); INIT_ANCHOR(args); - argc = setup_args(iseq, args, node->nd_args, &flag, NULL); + argc = setup_args(iseq, args, RNODE_ATTRASGN(node)->nd_args, &flag, NULL); CHECK(!NIL_P(argc)); - int asgnflag = COMPILE_RECV(recv, "recv", node); + int asgnflag = COMPILE_RECV(recv, "recv", node, RNODE_ATTRASGN(node)->nd_recv); CHECK(asgnflag != -1); flag |= (unsigned int)asgnflag; @@ -9112,16 +10469,7 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node ADD_SEQ(ret, recv); ADD_SEQ(ret, args); - if (flag & VM_CALL_ARGS_BLOCKARG) { - ADD_INSN1(ret, node, topn, INT2FIX(1)); - if (flag & VM_CALL_ARGS_SPLAT) { - ADD_INSN1(ret, node, putobject, INT2FIX(-1)); - ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag)); - } - ADD_INSN1(ret, node, setn, FIXNUM_INC(argc, 3)); - ADD_INSN (ret, node, pop); - } - else if (flag & VM_CALL_ARGS_SPLAT) { + if (flag & VM_CALL_ARGS_SPLAT) { ADD_INSN(ret, node, dup); ADD_INSN1(ret, node, putobject, INT2FIX(-1)); ADD_SEND_WITH_FLAG(ret, node, idAREF, INT2FIX(1), INT2FIX(asgnflag)); @@ -9142,6 +10490,375 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node return COMPILE_OK; } +static int +compile_make_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, LINK_ANCHOR *sub, const NODE *value, bool copy) +{ + ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + ADD_SEQ(ret, sub); + + if (copy) { + /* + * NEW_CALL(fcore, rb_intern("make_shareable_copy"), + * NEW_LIST(value, loc), loc); + */ + ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable_copy"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + else { + /* + * NEW_CALL(fcore, rb_intern("make_shareable"), + * NEW_LIST(value, loc), loc); + */ + ADD_SEND_WITH_FLAG(ret, value, rb_intern("make_shareable"), INT2FIX(1), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + + return COMPILE_OK; +} + +static VALUE +node_const_decl_val(const NODE *node) +{ + VALUE path; + switch (nd_type(node)) { + case NODE_CDECL: + if (RNODE_CDECL(node)->nd_vid) { + path = rb_id2str(RNODE_CDECL(node)->nd_vid); + goto end; + } + else { + node = RNODE_CDECL(node)->nd_else; + } + break; + case NODE_COLON2: + break; + case NODE_COLON3: + // ::Const + path = rb_str_new_cstr("::"); + rb_str_append(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); + goto end; + default: + rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); + UNREACHABLE_RETURN(0); + } + + path = rb_ary_new(); + if (node) { + for (; node && nd_type_p(node, NODE_COLON2); node = RNODE_COLON2(node)->nd_head) { + rb_ary_push(path, rb_id2str(RNODE_COLON2(node)->nd_mid)); + } + if (node && nd_type_p(node, NODE_CONST)) { + // Const::Name + rb_ary_push(path, rb_id2str(RNODE_CONST(node)->nd_vid)); + } + else if (node && nd_type_p(node, NODE_COLON3)) { + // ::Const::Name + rb_ary_push(path, rb_id2str(RNODE_COLON3(node)->nd_mid)); + rb_ary_push(path, rb_str_new(0, 0)); + } + else { + // expression::Name + rb_ary_push(path, rb_str_new_cstr("...")); + } + path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::")); + } + end: + path = rb_fstring(path); + return path; +} + +static VALUE +const_decl_path(NODE *dest) +{ + VALUE path = Qnil; + if (!nd_type_p(dest, NODE_CALL)) { + path = node_const_decl_val(dest); + } + return path; +} + +static int +compile_ensure_shareable_node(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *dest, const NODE *value) +{ + /* + *. RubyVM::FrozenCore.ensure_shareable(value, const_decl_path(dest)) + */ + VALUE path = const_decl_path(dest); + ADD_INSN1(ret, value, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + CHECK(COMPILE(ret, "compile_ensure_shareable_node", value)); + ADD_INSN1(ret, value, putobject, path); + RB_OBJ_WRITTEN(iseq, Qundef, path); + ADD_SEND_WITH_FLAG(ret, value, rb_intern("ensure_shareable"), INT2FIX(2), INT2FIX(VM_CALL_ARGS_SIMPLE)); + + return COMPILE_OK; +} + +#ifndef SHAREABLE_BARE_EXPRESSION +#define SHAREABLE_BARE_EXPRESSION 1 +#endif + +static int +compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, NODE *dest, const NODE *node, size_t level, VALUE *value_p, int *shareable_literal_p) +{ +# define compile_shareable_literal_constant_next(node, anchor, value_p, shareable_literal_p) \ + compile_shareable_literal_constant(iseq, anchor, shareable, dest, node, level+1, value_p, shareable_literal_p) + VALUE lit = Qnil; + DECL_ANCHOR(anchor); + + enum node_type type = node ? nd_type(node) : NODE_NIL; + switch (type) { + case NODE_TRUE: + *value_p = Qtrue; + goto compile; + case NODE_FALSE: + *value_p = Qfalse; + goto compile; + case NODE_NIL: + *value_p = Qnil; + goto compile; + case NODE_SYM: + *value_p = rb_node_sym_string_val(node); + goto compile; + case NODE_REGX: + *value_p = rb_node_regx_string_val(node); + goto compile; + case NODE_LINE: + *value_p = rb_node_line_lineno_val(node); + goto compile; + case NODE_INTEGER: + *value_p = rb_node_integer_literal_val(node); + goto compile; + case NODE_FLOAT: + *value_p = rb_node_float_literal_val(node); + goto compile; + case NODE_RATIONAL: + *value_p = rb_node_rational_literal_val(node); + goto compile; + case NODE_IMAGINARY: + *value_p = rb_node_imaginary_literal_val(node); + goto compile; + case NODE_ENCODING: + *value_p = rb_node_encoding_val(node); + + compile: + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + *shareable_literal_p = 1; + return COMPILE_OK; + + case NODE_DSTR: + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + if (shareable == rb_parser_shareable_literal) { + /* + * NEW_CALL(node, idUMinus, 0, loc); + * + * -"#{var}" + */ + ADD_SEND_WITH_FLAG(ret, node, idUMinus, INT2FIX(0), INT2FIX(VM_CALL_ARGS_SIMPLE)); + } + *value_p = Qundef; + *shareable_literal_p = 1; + return COMPILE_OK; + + case NODE_STR:{ + VALUE lit = rb_node_str_string_val(node); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_FILE:{ + VALUE lit = rb_node_file_path_val(node); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_ZLIST:{ + VALUE lit = rb_ary_new(); + OBJ_FREEZE(lit); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + *value_p = lit; + *shareable_literal_p = 1; + + return COMPILE_OK; + } + + case NODE_LIST:{ + INIT_ANCHOR(anchor); + lit = rb_ary_new(); + for (NODE *n = (NODE *)node; n; n = RNODE_LIST(n)->nd_next) { + VALUE val; + int shareable_literal_p2; + NODE *elt = RNODE_LIST(n)->nd_head; + if (elt) { + CHECK(compile_shareable_literal_constant_next(elt, anchor, &val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_ary_clear(lit); + lit = Qfalse; + } + } + if (RTEST(lit)) { + if (!UNDEF_P(val)) { + rb_ary_push(lit, val); + } + else { + rb_ary_clear(lit); + lit = Qnil; /* make shareable at runtime */ + } + } + } + break; + } + case NODE_HASH:{ + if (!RNODE_HASH(node)->nd_brace) { + *value_p = Qundef; + *shareable_literal_p = 0; + return COMPILE_OK; + } + for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) { + if (!RNODE_LIST(n)->nd_head) { + // If the hash node have a keyword splat, fall back to the default case. + goto compile_shareable; + } + } + + INIT_ANCHOR(anchor); + lit = rb_hash_new(); + for (NODE *n = RNODE_HASH(node)->nd_head; n; n = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_next) { + VALUE key_val = 0; + VALUE value_val = 0; + int shareable_literal_p2; + NODE *key = RNODE_LIST(n)->nd_head; + NODE *val = RNODE_LIST(RNODE_LIST(n)->nd_next)->nd_head; + CHECK(compile_shareable_literal_constant_next(key, anchor, &key_val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_hash_clear(lit); + lit = Qfalse; + } + CHECK(compile_shareable_literal_constant_next(val, anchor, &value_val, &shareable_literal_p2)); + if (shareable_literal_p2) { + /* noop */ + } + else if (RTEST(lit)) { + rb_hash_clear(lit); + lit = Qfalse; + } + if (RTEST(lit)) { + if (!UNDEF_P(key_val) && !UNDEF_P(value_val)) { + rb_hash_aset(lit, key_val, value_val); + } + else { + rb_hash_clear(lit); + lit = Qnil; /* make shareable at runtime */ + } + } + } + break; + } + + default: + + compile_shareable: + if (shareable == rb_parser_shareable_literal && + (SHAREABLE_BARE_EXPRESSION || level > 0)) { + CHECK(compile_ensure_shareable_node(iseq, ret, dest, node)); + *value_p = Qundef; + *shareable_literal_p = 1; + return COMPILE_OK; + } + CHECK(COMPILE(ret, "shareable_literal_constant", node)); + *value_p = Qundef; + *shareable_literal_p = 0; + return COMPILE_OK; + } + + /* Array or Hash that does not have keyword splat */ + if (!lit) { + if (nd_type(node) == NODE_LIST) { + ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); + } + else if (nd_type(node) == NODE_HASH) { + long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; + RBIMPL_ASSERT_OR_ASSUME(len >= 0); + RBIMPL_ASSERT_OR_ASSUME(RB_POSFIXABLE(len)); + ADD_INSN1(anchor, node, newhash, LONG2FIX(len)); + } + *value_p = Qundef; + *shareable_literal_p = 0; + ADD_SEQ(ret, anchor); + return COMPILE_OK; + } + if (NIL_P(lit)) { + // if shareable_literal, all elements should have been ensured + // as shareable + if (nd_type(node) == NODE_LIST) { + ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); + } + else if (nd_type(node) == NODE_HASH) { + long len = RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; + RBIMPL_ASSERT_OR_ASSUME(len >= 0); + RBIMPL_ASSERT_OR_ASSUME(RB_POSFIXABLE(len)); + ADD_INSN1(anchor, node, newhash, LONG2FIX(len)); + } + CHECK(compile_make_shareable_node(iseq, ret, anchor, node, false)); + *value_p = Qundef; + *shareable_literal_p = 1; + } + else { + VALUE val = rb_ractor_make_shareable(lit); + ADD_INSN1(ret, node, putobject, val); + RB_OBJ_WRITTEN(iseq, Qundef, val); + *value_p = val; + *shareable_literal_p = 1; + } + + return COMPILE_OK; +} + +static int +compile_shareable_constant_value(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_parser_shareability shareable, const NODE *lhs, const NODE *value) +{ + int literal_p = 0; + VALUE val; + DECL_ANCHOR(anchor); + INIT_ANCHOR(anchor); + + switch (shareable) { + case rb_parser_shareable_none: + CHECK(COMPILE(ret, "compile_shareable_constant_value", value)); + return COMPILE_OK; + + case rb_parser_shareable_literal: + CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p)); + ADD_SEQ(ret, anchor); + return COMPILE_OK; + + case rb_parser_shareable_copy: + case rb_parser_shareable_everything: + CHECK(compile_shareable_literal_constant(iseq, anchor, shareable, (NODE *)lhs, value, 0, &val, &literal_p)); + if (!literal_p) { + CHECK(compile_make_shareable_node(iseq, ret, anchor, value, shareable == rb_parser_shareable_copy)); + } + else { + ADD_SEQ(ret, anchor); + } + return COMPILE_OK; + default: + rb_bug("unexpected rb_parser_shareability: %d", shareable); + } +} + static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped); /** compile each node @@ -9158,8 +10875,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int poppe int lineno = ISEQ_COMPILE_DATA(iseq)->last_line; if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq)); debugs("node: NODE_NIL(implicit)\n"); - NODE dummy_line_node = generate_dummy_line_node(lineno, -1); - ADD_INSN(ret, &dummy_line_node, putnil); + ADD_SYNTHETIC_INSN(ret, lineno, -1, putnil); } return COMPILE_OK; } @@ -9177,10 +10893,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no /* ignore */ } else { - if (node->flags & NODE_FL_NEWLINE) { + if (nd_fl_newline(node)) { int event = RUBY_EVENT_LINE; ISEQ_COMPILE_DATA(iseq)->last_line = line; - if (ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) { + if (line > 0 && ISEQ_COVERAGE(iseq) && ISEQ_LINE_COVERAGE(iseq)) { event |= RUBY_EVENT_COVERAGE_LINE; } ADD_TRACE(ret, event); @@ -9232,7 +10948,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no CHECK(compile_retry(iseq, ret, node, popped)); break; case NODE_BEGIN:{ - CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped)); + CHECK(COMPILE_(ret, "NODE_BEGIN", RNODE_BEGIN(node)->nd_body, popped)); break; } case NODE_RESCUE: @@ -9248,7 +10964,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no case NODE_AND: case NODE_OR:{ LABEL *end_label = NEW_LABEL(line); - CHECK(COMPILE(ret, "nd_1st", node->nd_1st)); + CHECK(COMPILE(ret, "nd_1st", RNODE_OR(node)->nd_1st)); if (!popped) { ADD_INSN(ret, node, dup); } @@ -9261,7 +10977,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no if (!popped) { ADD_INSN(ret, node, pop); } - CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped)); + CHECK(COMPILE_(ret, "nd_2nd", RNODE_OR(node)->nd_2nd, popped)); ADD_LABEL(ret, end_label); break; } @@ -9272,11 +10988,11 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_LASGN:{ - ID id = node->nd_vid; + ID id = RNODE_LASGN(node)->nd_vid; int idx = ISEQ_BODY(body->local_iseq)->local_table_size - get_local_var_idx(iseq, id); debugs("lvar: %s idx: %d\n", rb_id2name(id), idx); - CHECK(COMPILE(ret, "rvalue", node->nd_value)); + CHECK(COMPILE(ret, "rvalue", RNODE_LASGN(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); @@ -9286,8 +11002,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_DASGN: { int idx, lv, ls; - ID id = node->nd_vid; - CHECK(COMPILE(ret, "dvalue", node->nd_value)); + ID id = RNODE_DASGN(node)->nd_vid; + CHECK(COMPILE(ret, "dvalue", RNODE_DASGN(node)->nd_value)); debugi("dassn id", rb_id2str(id) ? id : '*'); if (!popped) { @@ -9305,27 +11021,27 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_GASGN:{ - CHECK(COMPILE(ret, "lvalue", node->nd_value)); + CHECK(COMPILE(ret, "lvalue", RNODE_GASGN(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); } - ADD_INSN1(ret, node, setglobal, ID2SYM(node->nd_entry)); + ADD_INSN1(ret, node, setglobal, ID2SYM(RNODE_GASGN(node)->nd_vid)); break; } case NODE_IASGN:{ - CHECK(COMPILE(ret, "lvalue", node->nd_value)); + CHECK(COMPILE(ret, "lvalue", RNODE_IASGN(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); } ADD_INSN2(ret, node, setinstancevariable, - ID2SYM(node->nd_vid), - get_ivar_ic_value(iseq,node->nd_vid)); + ID2SYM(RNODE_IASGN(node)->nd_vid), + get_ivar_ic_value(iseq,RNODE_IASGN(node)->nd_vid)); break; } case NODE_CDECL:{ - if (node->nd_vid) { - CHECK(COMPILE(ret, "lvalue", node->nd_value)); + if (RNODE_CDECL(node)->nd_vid) { + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); @@ -9333,11 +11049,11 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE)); - ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_vid)); + ADD_INSN1(ret, node, setconstant, ID2SYM(RNODE_CDECL(node)->nd_vid)); } else { - compile_cpath(ret, iseq, node->nd_else); - CHECK(COMPILE(ret, "lvalue", node->nd_value)); + compile_cpath(ret, iseq, RNODE_CDECL(node)->nd_else); + CHECK(compile_shareable_constant_value(iseq, ret, RNODE_CDECL(node)->shareability, node, RNODE_CDECL(node)->nd_value)); ADD_INSN(ret, node, swap); if (!popped) { @@ -9345,18 +11061,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no ADD_INSN(ret, node, swap); } - ADD_INSN1(ret, node, setconstant, ID2SYM(node->nd_else->nd_mid)); + ADD_INSN1(ret, node, setconstant, ID2SYM(get_node_colon_nd_mid(RNODE_CDECL(node)->nd_else))); } break; } case NODE_CVASGN:{ - CHECK(COMPILE(ret, "cvasgn val", node->nd_value)); + CHECK(COMPILE(ret, "cvasgn val", RNODE_CVASGN(node)->nd_value)); if (!popped) { ADD_INSN(ret, node, dup); } ADD_INSN2(ret, node, setclassvariable, - ID2SYM(node->nd_vid), - get_cvar_ic_value(iseq,node->nd_vid)); + ID2SYM(RNODE_CVASGN(node)->nd_vid), + get_cvar_ic_value(iseq, RNODE_CVASGN(node)->nd_vid)); break; } case NODE_OP_ASGN1: @@ -9389,7 +11105,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no CHECK(compile_super(iseq, ret, node, popped, type)); break; case NODE_LIST:{ - CHECK(compile_array(iseq, ret, node, popped) >= 0); + CHECK(compile_array(iseq, ret, node, popped, TRUE) >= 0); break; } case NODE_ZLIST:{ @@ -9398,18 +11114,6 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } break; } - case NODE_VALUES:{ - const NODE *n = node; - if (popped) { - COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped"); - } - while (n) { - CHECK(COMPILE(ret, "values item", n->nd_head)); - n = n->nd_next; - } - ADD_INSN1(ret, node, newarray, INT2FIX(node->nd_alen)); - break; - } case NODE_HASH: CHECK(compile_hash(iseq, ret, node, FALSE, popped) >= 0); break; @@ -9421,18 +11125,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; case NODE_LVAR:{ if (!popped) { - compile_lvar(iseq, ret, node, node->nd_vid); + compile_lvar(iseq, ret, node, RNODE_LVAR(node)->nd_vid); } break; } case NODE_DVAR:{ int lv, idx, ls; - debugi("nd_vid", node->nd_vid); + debugi("nd_vid", RNODE_DVAR(node)->nd_vid); if (!popped) { - idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls); + idx = get_dyna_var_idx(iseq, RNODE_DVAR(node)->nd_vid, &lv, &ls); if (idx < 0) { COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")", - rb_id2str(node->nd_vid)); + rb_id2str(RNODE_DVAR(node)->nd_vid)); goto ng; } ADD_GETLOCAL(ret, node, ls - idx, lv); @@ -9440,38 +11144,35 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_GVAR:{ - ADD_INSN1(ret, node, getglobal, ID2SYM(node->nd_entry)); + ADD_INSN1(ret, node, getglobal, ID2SYM(RNODE_GVAR(node)->nd_vid)); if (popped) { ADD_INSN(ret, node, pop); } break; } case NODE_IVAR:{ - debugi("nd_vid", node->nd_vid); + debugi("nd_vid", RNODE_IVAR(node)->nd_vid); if (!popped) { ADD_INSN2(ret, node, getinstancevariable, - ID2SYM(node->nd_vid), - get_ivar_ic_value(iseq,node->nd_vid)); + ID2SYM(RNODE_IVAR(node)->nd_vid), + get_ivar_ic_value(iseq, RNODE_IVAR(node)->nd_vid)); } break; } case NODE_CONST:{ - debugi("nd_vid", node->nd_vid); + debugi("nd_vid", RNODE_CONST(node)->nd_vid); if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { - LABEL *lend = NEW_LABEL(line); - int ic_index = body->ic_size++; - - ADD_INSN2(ret, node, opt_getinlinecache, lend, INT2FIX(ic_index)); - ADD_INSN1(ret, node, putobject, Qtrue); - ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid)); - ADD_INSN1(ret, node, opt_setinlinecache, INT2FIX(ic_index)); - ADD_LABEL(ret, lend); + body->ic_size++; + VALUE segments = rb_ary_new_from_args(1, ID2SYM(RNODE_CONST(node)->nd_vid)); + RB_OBJ_SET_FROZEN_SHAREABLE(segments); + ADD_INSN1(ret, node, opt_getconstant_path, segments); + RB_OBJ_WRITTEN(iseq, Qundef, segments); } else { ADD_INSN(ret, node, putnil); ADD_INSN1(ret, node, putobject, Qtrue); - ADD_INSN1(ret, node, getconstant, ID2SYM(node->nd_vid)); + ADD_INSN1(ret, node, getconstant, ID2SYM(RNODE_CONST(node)->nd_vid)); } if (popped) { @@ -9482,26 +11183,26 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no case NODE_CVAR:{ if (!popped) { ADD_INSN2(ret, node, getclassvariable, - ID2SYM(node->nd_vid), - get_cvar_ic_value(iseq,node->nd_vid)); + ID2SYM(RNODE_CVAR(node)->nd_vid), + get_cvar_ic_value(iseq, RNODE_CVAR(node)->nd_vid)); } break; } case NODE_NTH_REF:{ if (!popped) { - if (!node->nd_nth) { + if (!RNODE_NTH_REF(node)->nd_nth) { ADD_INSN(ret, node, putnil); break; } ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */, - INT2FIX(node->nd_nth << 1)); + INT2FIX(RNODE_NTH_REF(node)->nd_nth << 1)); } break; } case NODE_BACK_REF:{ if (!popped) { ADD_INSN2(ret, node, getspecial, INT2FIX(1) /* '~' */, - INT2FIX(0x01 | (node->nd_nth << 1))); + INT2FIX(0x01 | (RNODE_BACK_REF(node)->nd_nth << 1))); } break; } @@ -9510,36 +11211,89 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no case NODE_MATCH3: CHECK(compile_match(iseq, ret, node, popped, type)); break; - case NODE_LIT:{ - debugp_param("lit", node->nd_lit); + case NODE_SYM:{ if (!popped) { - ADD_INSN1(ret, node, putobject, node->nd_lit); - RB_OBJ_WRITTEN(iseq, Qundef, node->nd_lit); + ADD_INSN1(ret, node, putobject, rb_node_sym_string_val(node)); + } + break; + } + case NODE_LINE:{ + if (!popped) { + ADD_INSN1(ret, node, putobject, rb_node_line_lineno_val(node)); + } + break; + } + case NODE_ENCODING:{ + if (!popped) { + ADD_INSN1(ret, node, putobject, rb_node_encoding_val(node)); + } + break; + } + case NODE_INTEGER:{ + VALUE lit = rb_node_integer_literal_val(node); + if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit); + debugp_param("integer", lit); + if (!popped) { + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + } + break; + } + case NODE_FLOAT:{ + VALUE lit = rb_node_float_literal_val(node); + if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit); + debugp_param("float", lit); + if (!popped) { + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); } break; } + case NODE_RATIONAL:{ + VALUE lit = rb_node_rational_literal_val(node); + rb_ractor_make_shareable(lit); + debugp_param("rational", lit); + if (!popped) { + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + } + break; + } + case NODE_IMAGINARY:{ + VALUE lit = rb_node_imaginary_literal_val(node); + rb_ractor_make_shareable(lit); + debugp_param("imaginary", lit); + if (!popped) { + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); + } + break; + } + case NODE_FILE: case NODE_STR:{ - debugp_param("nd_lit", node->nd_lit); + debugp_param("nd_lit", get_string_value(node)); if (!popped) { - VALUE lit = node->nd_lit; - if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) { - lit = rb_fstring(lit); - ADD_INSN1(ret, node, putstring, lit); - RB_OBJ_WRITTEN(iseq, Qundef, lit); - } - else { - if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) { - VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line)); - lit = rb_str_dup(lit); - rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info)); - lit = rb_str_freeze(lit); - } - else { - lit = rb_fstring(lit); - } + VALUE lit = get_string_value(node); + const rb_compile_option_t *option = ISEQ_COMPILE_DATA(iseq)->option; + if ((option->debug_frozen_string_literal || RTEST(ruby_debug)) && + option->frozen_string_literal != ISEQ_FROZEN_STRING_LITERAL_DISABLED) { + lit = rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), line); + RB_OBJ_SET_SHAREABLE(lit); + } + switch (option->frozen_string_literal) { + case ISEQ_FROZEN_STRING_LITERAL_UNSET: + ADD_INSN1(ret, node, dupchilledstring, lit); + break; + case ISEQ_FROZEN_STRING_LITERAL_DISABLED: + ADD_INSN1(ret, node, dupstring, lit); + break; + case ISEQ_FROZEN_STRING_LITERAL_ENABLED: ADD_INSN1(ret, node, putobject, lit); - RB_OBJ_WRITTEN(iseq, Qundef, lit); + break; + default: + rb_bug("invalid frozen_string_literal"); } + RB_OBJ_WRITTEN(iseq, Qundef, lit); } break; } @@ -9553,7 +11307,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_XSTR:{ ADD_CALL_RECEIVER(ret, node); - VALUE str = rb_fstring(node->nd_lit); + VALUE str = rb_node_str_string_val(node); ADD_INSN1(ret, node, putobject, str); RB_OBJ_WRITTEN(iseq, Qundef, str); ADD_CALL(ret, node, idBackquote, INT2FIX(1)); @@ -9574,20 +11328,24 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_EVSTR: - CHECK(compile_evstr(iseq, ret, node->nd_body, popped)); + CHECK(compile_evstr(iseq, ret, RNODE_EVSTR(node)->nd_body, popped)); break; - case NODE_DREGX:{ - compile_dregx(iseq, ret, node); - - if (popped) { - ADD_INSN(ret, node, pop); + case NODE_REGX:{ + if (!popped) { + VALUE lit = rb_node_regx_string_val(node); + RB_OBJ_SET_SHAREABLE(lit); + ADD_INSN1(ret, node, putobject, lit); + RB_OBJ_WRITTEN(iseq, Qundef, lit); } break; } + case NODE_DREGX: + compile_dregx(iseq, ret, node, popped); + break; case NODE_ONCE:{ int ic_index = body->ise_size++; const rb_iseq_t *block_iseq; - block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line); + block_iseq = NEW_CHILD_ISEQ(RNODE_ONCE(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line); ADD_INSN2(ret, node, once, block_iseq, INT2FIX(ic_index)); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)block_iseq); @@ -9599,36 +11357,53 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_ARGSCAT:{ if (popped) { - CHECK(COMPILE(ret, "argscat head", node->nd_head)); + CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head)); ADD_INSN1(ret, node, splatarray, Qfalse); ADD_INSN(ret, node, pop); - CHECK(COMPILE(ret, "argscat body", node->nd_body)); + CHECK(COMPILE(ret, "argscat body", RNODE_ARGSCAT(node)->nd_body)); ADD_INSN1(ret, node, splatarray, Qfalse); ADD_INSN(ret, node, pop); } else { - CHECK(COMPILE(ret, "argscat head", node->nd_head)); - CHECK(COMPILE(ret, "argscat body", node->nd_body)); - ADD_INSN(ret, node, concatarray); + CHECK(COMPILE(ret, "argscat head", RNODE_ARGSCAT(node)->nd_head)); + const NODE *body_node = RNODE_ARGSCAT(node)->nd_body; + if (nd_type_p(body_node, NODE_LIST)) { + CHECK(compile_array(iseq, ret, body_node, popped, FALSE) >= 0); + } + else { + CHECK(COMPILE(ret, "argscat body", body_node)); + ADD_INSN(ret, node, concattoarray); + } } break; } case NODE_ARGSPUSH:{ if (popped) { - CHECK(COMPILE(ret, "argspush head", node->nd_head)); + CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head)); ADD_INSN1(ret, node, splatarray, Qfalse); ADD_INSN(ret, node, pop); - CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped)); + CHECK(COMPILE_(ret, "argspush body", RNODE_ARGSPUSH(node)->nd_body, popped)); } else { - CHECK(COMPILE(ret, "argspush head", node->nd_head)); - CHECK(compile_array_1(iseq, ret, node->nd_body)); - ADD_INSN(ret, node, concatarray); + CHECK(COMPILE(ret, "argspush head", RNODE_ARGSPUSH(node)->nd_head)); + const NODE *body_node = RNODE_ARGSPUSH(node)->nd_body; + if (keyword_node_p(body_node)) { + CHECK(COMPILE_(ret, "array element", body_node, FALSE)); + ADD_INSN(ret, node, pushtoarraykwsplat); + } + else if (static_literal_node_p(body_node, iseq, false)) { + ADD_INSN1(ret, body_node, putobject, static_literal_value(body_node, iseq)); + ADD_INSN1(ret, node, pushtoarray, INT2FIX(1)); + } + else { + CHECK(COMPILE_(ret, "array element", body_node, FALSE)); + ADD_INSN1(ret, node, pushtoarray, INT2FIX(1)); + } } break; } case NODE_SPLAT:{ - CHECK(COMPILE(ret, "splat", node->nd_head)); + CHECK(COMPILE(ret, "splat", RNODE_SPLAT(node)->nd_head)); ADD_INSN1(ret, node, splatarray, Qtrue); if (popped) { @@ -9637,8 +11412,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_DEFN:{ - ID mid = node->nd_mid; - const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn, + ID mid = RNODE_DEFN(node)->nd_mid; + const rb_iseq_t *method_iseq = NEW_ISEQ(RNODE_DEFN(node)->nd_defn, rb_id2str(mid), ISEQ_TYPE_METHOD, line); @@ -9653,13 +11428,13 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_DEFS:{ - ID mid = node->nd_mid; - const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(node->nd_defn, + ID mid = RNODE_DEFS(node)->nd_mid; + const rb_iseq_t * singleton_method_iseq = NEW_ISEQ(RNODE_DEFS(node)->nd_defn, rb_id2str(mid), ISEQ_TYPE_METHOD, line); debugp_param("defs/iseq", rb_iseqw_new(singleton_method_iseq)); - CHECK(COMPILE(ret, "defs: recv", node->nd_recv)); + CHECK(COMPILE(ret, "defs: recv", RNODE_DEFS(node)->nd_recv)); ADD_INSN2(ret, node, definesmethod, ID2SYM(mid), singleton_method_iseq); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_method_iseq); @@ -9671,8 +11446,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no case NODE_ALIAS:{ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE)); - CHECK(COMPILE(ret, "alias arg1", node->nd_1st)); - CHECK(COMPILE(ret, "alias arg2", node->nd_2nd)); + CHECK(COMPILE(ret, "alias arg1", RNODE_ALIAS(node)->nd_1st)); + CHECK(COMPILE(ret, "alias arg2", RNODE_ALIAS(node)->nd_2nd)); ADD_SEND(ret, node, id_core_set_method_alias, INT2FIX(3)); if (popped) { @@ -9682,8 +11457,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_VALIAS:{ ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_alias)); - ADD_INSN1(ret, node, putobject, ID2SYM(node->nd_orig)); + ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_alias)); + ADD_INSN1(ret, node, putobject, ID2SYM(RNODE_VALIAS(node)->nd_orig)); ADD_SEND(ret, node, id_core_set_variable_alias, INT2FIX(2)); if (popped) { @@ -9692,10 +11467,18 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_UNDEF:{ - ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); - ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE)); - CHECK(COMPILE(ret, "undef arg", node->nd_undef)); - ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2)); + const rb_parser_ary_t *ary = RNODE_UNDEF(node)->nd_undefs; + + for (long i = 0; i < ary->len; i++) { + ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE)); + CHECK(COMPILE(ret, "undef arg", ary->data[i])); + ADD_SEND(ret, node, id_core_undef_method, INT2FIX(2)); + + if (i < ary->len - 1) { + ADD_INSN(ret, node, pop); + } + } if (popped) { ADD_INSN(ret, node, pop); @@ -9703,15 +11486,15 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_CLASS:{ - const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body, - rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))), + const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(RNODE_CLASS(node)->nd_body, + rb_str_freeze(rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)))), ISEQ_TYPE_CLASS, line); const int flags = VM_DEFINECLASS_TYPE_CLASS | - (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) | - compile_cpath(ret, iseq, node->nd_cpath); + (RNODE_CLASS(node)->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) | + compile_cpath(ret, iseq, RNODE_CLASS(node)->nd_cpath); - CHECK(COMPILE(ret, "super", node->nd_super)); - ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags)); + CHECK(COMPILE(ret, "super", RNODE_CLASS(node)->nd_super)); + ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_CLASS(node)->nd_cpath)), class_iseq, INT2FIX(flags)); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)class_iseq); if (popped) { @@ -9720,14 +11503,14 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; } case NODE_MODULE:{ - const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body, - rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid))), + const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(RNODE_MODULE(node)->nd_body, + rb_str_freeze(rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)))), ISEQ_TYPE_CLASS, line); const int flags = VM_DEFINECLASS_TYPE_MODULE | - compile_cpath(ret, iseq, node->nd_cpath); + compile_cpath(ret, iseq, RNODE_MODULE(node)->nd_cpath); ADD_INSN (ret, node, putnil); /* dummy */ - ADD_INSN3(ret, node, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags)); + ADD_INSN3(ret, node, defineclass, ID2SYM(get_node_colon_nd_mid(RNODE_MODULE(node)->nd_cpath)), module_iseq, INT2FIX(flags)); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)module_iseq); if (popped) { @@ -9737,15 +11520,26 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_SCLASS:{ ID singletonclass; - const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_lit("singleton class"), + const rb_iseq_t *singleton_class = NEW_ISEQ(RNODE_SCLASS(node)->nd_body, rb_fstring_lit("singleton class"), ISEQ_TYPE_CLASS, line); - CHECK(COMPILE(ret, "sclass#recv", node->nd_recv)); + CHECK(COMPILE(ret, "sclass#recv", RNODE_SCLASS(node)->nd_recv)); ADD_INSN (ret, node, putnil); CONST_ID(singletonclass, "singletonclass"); + + /* `class << self` in a class body and `class << Foo` (constant + receiver) are stable. All other forms are potentially dynamic. */ + int sclass_flags = VM_DEFINECLASS_TYPE_SINGLETON_CLASS; + const NODE *recv = RNODE_SCLASS(node)->nd_recv; + if (!(nd_type_p(recv, NODE_SELF) && + ISEQ_BODY(iseq)->type == ISEQ_TYPE_CLASS) && + !cpath_const_p(recv)) { + sclass_flags |= VM_DEFINECLASS_FLAG_DYNAMIC_CREF; + } + ADD_INSN3(ret, node, defineclass, ID2SYM(singletonclass), singleton_class, - INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS)); + INT2FIX(sclass_flags)); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)singleton_class); if (popped) { @@ -9809,7 +11603,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; case NODE_DEFINED: if (!popped) { - CHECK(compile_defined_expr(iseq, ret, node, Qtrue)); + CHECK(compile_defined_expr(iseq, ret, node, Qtrue, false)); } break; case NODE_POSTEXE:{ @@ -9818,10 +11612,9 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no */ int is_index = body->ise_size++; struct rb_iseq_new_with_callback_callback_func *ifunc = - rb_iseq_new_with_callback_new_callback(build_postexe_iseq, node->nd_body); + rb_iseq_new_with_callback_new_callback(build_postexe_iseq, RNODE_POSTEXE(node)->nd_body); const rb_iseq_t *once_iseq = - new_child_iseq_with_callback(iseq, ifunc, - rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line); + NEW_CHILD_ISEQ_WITH_CALLBACK(ifunc, rb_fstring(make_name_for_block(iseq)), ISEQ_TYPE_BLOCK, line); ADD_INSN2(ret, node, once, once_iseq, INT2FIX(is_index)); RB_OBJ_WRITTEN(iseq, Qundef, (VALUE)once_iseq); @@ -9849,7 +11642,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no break; case NODE_LAMBDA:{ /* compile same as lambda{...} */ - const rb_iseq_t *block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); + const rb_iseq_t *block = NEW_CHILD_ISEQ(RNODE_LAMBDA(node)->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line); VALUE argc = INT2FIX(0); ADD_INSN1(ret, node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); @@ -9954,16 +11747,22 @@ insn_data_to_s_detail(INSN *iobj) rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j))); break; case TS_IC: /* inline cache */ + rb_str_concat(str, opobj_inspect(OPERAND_AT(iobj, j))); + break; case TS_IVC: /* inline ivar cache */ + rb_str_catf(str, "<ivc:%d>", FIX2INT(OPERAND_AT(iobj, j))); + break; case TS_ICVARC: /* inline cvar cache */ + rb_str_catf(str, "<icvarc:%d>", FIX2INT(OPERAND_AT(iobj, j))); + break; case TS_ISE: /* inline storage entry */ - rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j))); + rb_str_catf(str, "<ise:%d>", FIX2INT(OPERAND_AT(iobj, j))); break; case TS_CALLDATA: /* we store these as call infos at compile time */ { const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, j); rb_str_cat2(str, "<calldata:"); - if (vm_ci_mid(ci)) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(vm_ci_mid(ci))); + if (vm_ci_mid(ci)) rb_str_append(str, rb_id2str(vm_ci_mid(ci))); rb_str_catf(str, ", %d>", vm_ci_argc(ci)); break; } @@ -10028,7 +11827,7 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, case ISEQ_ELEMENT_LABEL: { lobj = (LABEL *)link; - printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp, + printf(LABEL_FORMAT" [sp: %d, unremovable: %d, refcnt: %d]%s\n", lobj->label_no, lobj->sp, lobj->unremovable, lobj->refcnt, dest == lobj ? " <---" : ""); break; } @@ -10046,7 +11845,7 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, } default: /* ignore */ - rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type)); + rb_raise(rb_eSyntaxError, "dump_disasm_list error: %d\n", (int)link->type); } link = link->next; } @@ -10054,6 +11853,12 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, fflush(stdout); } +int +rb_insn_len(VALUE insn) +{ + return insn_len(insn); +} + const char * rb_insns_name(int i) { @@ -10068,7 +11873,7 @@ rb_insns_name_array(void) for (i = 0; i < VM_INSTRUCTION_SIZE; i++) { rb_ary_push(ary, rb_fstring_cstr(insn_name(i))); } - return rb_obj_freeze(ary); + return rb_ary_freeze(ary); } static LABEL * @@ -10217,6 +12022,7 @@ iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op) size_t n = rb_callinfo_kwarg_bytes(len); kw_arg = xmalloc(n); + kw_arg->references = 0; kw_arg->keyword_len = len; for (i = 0; i < len; i++) { VALUE kw = RARRAY_AREF(vkw_arg, i); @@ -10242,6 +12048,7 @@ event_name_to_flag(VALUE sym) CHECK_EVENT(RUBY_EVENT_RETURN); CHECK_EVENT(RUBY_EVENT_B_CALL); CHECK_EVENT(RUBY_EVENT_B_RETURN); + CHECK_EVENT(RUBY_EVENT_RESCUE); #undef CHECK_EVENT return RUBY_EVENT_NONE; } @@ -10252,7 +12059,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, { /* TODO: body should be frozen */ long i, len = RARRAY_LEN(body); - struct st_table *labels_table = DATA_PTR(labels_wrapper); + struct st_table *labels_table = RTYPEDDATA_DATA(labels_wrapper); int j; int line_no = 0, node_id = -1, insn_idx = 0; int ret = COMPILE_OK; @@ -10309,12 +12116,11 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, } if (argc > 0) { - argv = compile_data_calloc2(iseq, sizeof(VALUE), argc); + argv = compile_data_calloc2_type(iseq, VALUE, argc); // add element before operand setup to make GC root - NODE dummy_line_node = generate_dummy_line_node(line_no, node_id); ADD_ELEM(anchor, - (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node, + (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id, (enum ruby_vminsn_type)insn_id, argc, argv)); for (j=0; j<argc; j++) { @@ -10353,9 +12159,20 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, } break; case TS_IC: - argv[j] = op; - if (NUM2UINT(op) >= ISEQ_BODY(iseq)->ic_size) { - ISEQ_BODY(iseq)->ic_size = NUM2INT(op) + 1; + { + VALUE segments = rb_ary_new(); + op = rb_to_array_type(op); + + for (int i = 0; i < RARRAY_LEN(op); i++) { + VALUE sym = RARRAY_AREF(op, i); + sym = rb_to_symbol_type(sym); + rb_ary_push(segments, sym); + } + + RB_GC_GUARD(op); + argv[j] = segments; + RB_OBJ_WRITTEN(iseq, Qundef, segments); + ISEQ_BODY(iseq)->ic_size++; } break; case TS_IVC: /* inline ivar cache */ @@ -10379,18 +12196,18 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, case TS_CDHASH: { int i; - VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2); + VALUE map = cdhash_new(RARRAY_LEN(op) / 2); - RHASH_TBL_RAW(map)->type = &cdhash_type; op = rb_to_array_type(op); for (i=0; i<RARRAY_LEN(op); i+=2) { VALUE key = RARRAY_AREF(op, i); VALUE sym = RARRAY_AREF(op, i+1); LABEL *label = register_label(iseq, labels_table, sym); - rb_hash_aset(map, key, (VALUE)label | 1); + cdhash_aset(map, key, (VALUE)label); } RB_GC_GUARD(op); + RB_OBJ_SET_SHAREABLE(map); // allow mutation while compiling argv[j] = map; RB_OBJ_WRITTEN(iseq, Qundef, map); } @@ -10411,9 +12228,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, } } else { - NODE dummy_line_node = generate_dummy_line_node(line_no, node_id); ADD_ELEM(anchor, - (LINK_ELEMENT*)new_insn_core(iseq, &dummy_line_node, + (LINK_ELEMENT*)new_insn_core(iseq, line_no, node_id, (enum ruby_vminsn_type)insn_id, argc, NULL)); } } @@ -10421,7 +12237,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, rb_raise(rb_eTypeError, "unexpected object for instruction"); } } - DATA_PTR(labels_wrapper) = 0; + RTYPEDDATA_DATA(labels_wrapper) = 0; + RB_GC_GUARD(labels_wrapper); validate_labels(iseq, labels_table); if (!ret) return ret; return iseq_setup(iseq, anchor); @@ -10505,7 +12322,7 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords) rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key); } ids[i] = SYM2ID(sym); - dvs[j] = default_val; + RB_OBJ_WRITE(iseq, &dvs[j], default_val); } keyword->table = ids; @@ -10514,60 +12331,49 @@ iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords) return keyword; } +static void +iseq_insn_each_object_mark_and_move(VALUE * obj, VALUE _) +{ + rb_gc_mark_and_move(obj); +} + void -rb_iseq_mark_insn_storage(struct iseq_compile_data_storage *storage) +rb_iseq_mark_and_move_insn_storage(struct iseq_compile_data_storage *storage) { INSN *iobj = 0; size_t size = sizeof(INSN); + size_t align = ALIGNMENT_SIZE_OF(INSN); unsigned int pos = 0; while (storage) { -#ifdef STRICT_ALIGNMENT - size_t padding = calc_padding((void *)&storage->buff[pos], size); -#else - const size_t padding = 0; /* expected to be optimized by compiler */ -#endif /* STRICT_ALIGNMENT */ + size_t padding = calc_padding((void *)&storage->buff[pos], align); size_t offset = pos + size + padding; if (offset > storage->size || offset > storage->pos) { pos = 0; storage = storage->next; } else { -#ifdef STRICT_ALIGNMENT pos += (int)padding; -#endif /* STRICT_ALIGNMENT */ iobj = (INSN *)&storage->buff[pos]; if (iobj->operands) { - int j; - const char *types = insn_op_types(iobj->insn_id); - - for (j = 0; types[j]; j++) { - char type = types[j]; - switch (type) { - case TS_CDHASH: - case TS_ISEQ: - case TS_VALUE: - case TS_CALLDATA: // ci is stored. - { - VALUE op = OPERAND_AT(iobj, j); - - if (!SPECIAL_CONST_P(op)) { - rb_gc_mark(op); - } - } - break; - default: - break; - } - } + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_mark_and_move, (VALUE)0); } pos += (int)size; } } } +static const rb_data_type_t labels_wrapper_type = { + .wrap_struct_name = "compiler/labels_wrapper", + .function = { + .dmark = (RUBY_DATA_FUNC)rb_mark_set, + .dfree = (RUBY_DATA_FUNC)st_free_table, + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, +}; + void rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params, VALUE exception, VALUE body) @@ -10577,7 +12383,7 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params, unsigned int arg_size, local_size, stack_max; ID *tbl; struct st_table *labels_table = st_init_numtable(); - VALUE labels_wrapper = Data_Wrap_Struct(0, rb_mark_set, st_free_table, labels_table); + VALUE labels_wrapper = TypedData_Wrap_Struct(0, &labels_wrapper_type, labels_table); VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt)); VALUE keywords = rb_hash_aref(params, SYM(keyword)); VALUE sym_arg_rest = ID2SYM(rb_intern_const("#arg_rest")); @@ -10659,6 +12465,10 @@ rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params, ISEQ_BODY(iseq)->param.flags.ambiguous_param0 = TRUE; } + if (Qtrue == rb_hash_aref(params, SYM(use_block))) { + ISEQ_BODY(iseq)->param.flags.use_block = TRUE; + } + if (int_param(&i, params, SYM(kwrest))) { struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)ISEQ_BODY(iseq)->param.keyword; if (keyword == NULL) { @@ -10734,28 +12544,38 @@ rb_local_defined(ID id, const rb_iseq_t *iseq) #define IBF_ISEQ_ENABLE_LOCAL_BUFFER 0 #endif -typedef unsigned int ibf_offset_t; +typedef uint32_t ibf_offset_t; #define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr)) #define IBF_MAJOR_VERSION ISEQ_MAJOR_VERSION #ifdef RUBY_DEVEL -#define IBF_DEVEL_VERSION 4 +#define IBF_DEVEL_VERSION 5 #define IBF_MINOR_VERSION (ISEQ_MINOR_VERSION * 10000 + IBF_DEVEL_VERSION) #else #define IBF_MINOR_VERSION ISEQ_MINOR_VERSION #endif +static const char IBF_ENDIAN_MARK = +#ifdef WORDS_BIGENDIAN + 'b' +#else + 'l' +#endif + ; + struct ibf_header { char magic[4]; /* YARB */ - unsigned int major_version; - unsigned int minor_version; - unsigned int size; - unsigned int extra_size; + uint32_t major_version; + uint32_t minor_version; + uint32_t size; + uint32_t extra_size; - unsigned int iseq_list_size; - unsigned int global_object_list_size; + uint32_t iseq_list_size; + uint32_t global_object_list_size; ibf_offset_t iseq_list_offset; ibf_offset_t global_object_list_offset; + uint8_t endian; + uint8_t wordsize; /* assume no 2048-bit CPU */ }; struct ibf_dump_buffer { @@ -10790,7 +12610,7 @@ struct ibf_load { struct pinned_list { long size; - VALUE * buffer; + VALUE buffer[1]; }; static void @@ -10805,25 +12625,14 @@ pinned_list_mark(void *ptr) } } -static void -pinned_list_free(void *ptr) -{ - struct pinned_list *list = (struct pinned_list *)ptr; - xfree(list->buffer); - xfree(ptr); -} - -static size_t -pinned_list_memsize(const void *ptr) -{ - struct pinned_list *list = (struct pinned_list *)ptr; - return sizeof(struct pinned_list) + (list->size * sizeof(VALUE *)); -} - static const rb_data_type_t pinned_list_type = { "pinned_list", - {pinned_list_mark, pinned_list_free, pinned_list_memsize,}, - 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY + { + pinned_list_mark, + RUBY_DEFAULT_FREE, + NULL, // No external memory to report, + }, + 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE }; static VALUE @@ -10857,13 +12666,10 @@ pinned_list_store(VALUE list, long offset, VALUE object) static VALUE pinned_list_new(long size) { - struct pinned_list * ptr; - VALUE obj_list = - TypedData_Make_Struct(0, struct pinned_list, &pinned_list_type, ptr); - - ptr->buffer = xcalloc(size, sizeof(VALUE)); + size_t memsize = offsetof(struct pinned_list, buffer) + size * sizeof(VALUE); + VALUE obj_list = rb_data_typed_object_zalloc(0, memsize, &pinned_list_type); + struct pinned_list * ptr = RTYPEDDATA_GET_DATA(obj_list); ptr->size = size; - return obj_list; } @@ -10902,8 +12708,13 @@ static ibf_offset_t ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size) { ibf_offset_t pos = ibf_dump_pos(dump); +#if SIZEOF_LONG > SIZEOF_INT + /* ensure the resulting dump does not exceed UINT_MAX */ + if (size >= UINT_MAX || pos + size >= UINT_MAX) { + rb_raise(rb_eRuntimeError, "dump size exceeds"); + } +#endif rb_str_cat(dump->current_buffer->str, (const char *)buff, size); - /* TODO: overflow check */ return pos; } @@ -11012,6 +12823,10 @@ ibf_load_id(const struct ibf_load *load, const ID id_index) return 0; } VALUE sym = ibf_load_object(load, id_index); + if (rb_integer_type_p(sym)) { + /* Load hidden local variables as indexes */ + return NUM2ULONG(sym); + } return rb_sym2id(sym); } @@ -11137,7 +12952,8 @@ ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset) const struct rb_builtin_function *table = GET_VM()->builtin_function_table; if (table == NULL) rb_raise(rb_eArgError, "builtin function table is not provided"); if (strncmp(table[i].name, name, len) != 0) { - rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %s but %s)", i, name, table[i].name); + rb_raise(rb_eArgError, "builtin function index (%d) mismatch (expect %.*s but %s)", + i, len, name, table[i].name); } // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc); @@ -11177,6 +12993,12 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq) wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op); break; case TS_IC: + { + IC ic = (IC)op; + VALUE arr = idlist_to_array(ic->segments); + wv = ibf_dump_object(dump, arr); + } + break; case TS_ISE: case TS_IVC: case TS_ICVARC: @@ -11205,7 +13027,7 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_dump_write_small_value(dump, wv); skip_wv:; } - assert(insn_len(insn) == op_index+1); + RUBY_ASSERT(insn_len(insn) == op_index+1); } return offset; @@ -11217,27 +13039,29 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod VALUE iseqv = (VALUE)iseq; unsigned int code_index; ibf_offset_t reading_pos = bytecode_offset; - VALUE *code = ALLOC_N(VALUE, iseq_size); + VALUE *code = ZALLOC_N(VALUE, iseq_size); struct rb_iseq_constant_body *load_body = ISEQ_BODY(iseq); struct rb_call_data *cd_entries = load_body->call_data; + int ic_index = 0; - iseq_bits_t * mark_offset_bits; - - iseq_bits_t tmp[1] = {0}; + load_body->iseq_encoded = code; + load_body->iseq_size = iseq_size; + iseq_bits_t * mark_offset_bits; if (ISEQ_MBITS_BUFLEN(iseq_size) == 1) { - mark_offset_bits = tmp; + load_body->mark_bits.single = 0; + mark_offset_bits = &load_body->mark_bits.single; } else { - mark_offset_bits = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size)); + load_body->mark_bits.list = ZALLOC_N(iseq_bits_t, ISEQ_MBITS_BUFLEN(iseq_size)); + mark_offset_bits = load_body->mark_bits.list; } bool needs_bitmap = false; for (code_index=0; code_index<iseq_size;) { /* opcode */ const VALUE insn = code[code_index] = ibf_load_small_value(load, &reading_pos); - const unsigned int insn_index = code_index; const char *types = insn_op_types(insn); int op_index; @@ -11263,10 +13087,6 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod { VALUE op = ibf_load_small_value(load, &reading_pos); VALUE v = ibf_load_object(load, op); - v = rb_hash_dup(v); // hash dumped as frozen - RHASH_TBL_RAW(v)->type = &cdhash_type; - rb_hash_rehash(v); // hash function changed - freeze_hide_obj(v); // Overwrite the existing hash in the object list. This // is to keep the object alive during load time. @@ -11292,6 +13112,16 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod break; } case TS_IC: + { + VALUE op = ibf_load_small_value(load, &reading_pos); + VALUE arr = ibf_load_object(load, op); + + IC ic = &ISEQ_IS_IC_ENTRY(load_body, ic_index++); + ic->segments = array_to_idlist(arr); + + code[code_index] = (VALUE)ic; + } + break; case TS_ISE: case TS_ICVARC: case TS_IVC: @@ -11301,11 +13131,20 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod ISE ic = ISEQ_IS_ENTRY_START(load_body, operand_type) + op; code[code_index] = (VALUE)ic; - if (insn == BIN(opt_getinlinecache) && operand_type == TS_IC) { - // Store the instruction index for opt_getinlinecache on the IC for - // YJIT to invalidate code when opt_setinlinecache runs. - ic->ic_cache.get_insn_idx = insn_index; + if (operand_type == TS_IVC) { + IVC cache = (IVC)ic; + + if (insn == BIN(setinstancevariable)) { + ID iv_name = (ID)code[code_index - 1]; + cache->iv_set_name = iv_name; + cache->value = IVAR_CACHE_INIT; + } + else { + cache->iv_set_name = 0; + cache->value = rb_getivar_cache_pack(ROOT_SHAPE_ID, ATTR_INDEX_NOT_SET); + } } + } break; case TS_CALLDATA: @@ -11335,24 +13174,13 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod } } - load_body->iseq_encoded = code; - load_body->iseq_size = code_index; - - if (ISEQ_MBITS_BUFLEN(load_body->iseq_size) == 1) { - load_body->mark_bits.single = mark_offset_bits[0]; - } - else { - if (needs_bitmap) { - load_body->mark_bits.list = mark_offset_bits; - } - else { - load_body->mark_bits.list = 0; - ruby_xfree(mark_offset_bits); - } + if (!needs_bitmap) { + SIZED_FREE_N(load_body->mark_bits.list, ISEQ_MBITS_BUFLEN(iseq_size)); + load_body->mark_bits.list = NULL; } - assert(code_index == iseq_size); - assert(reading_pos == bytecode_offset + bytecode_size); + RUBY_ASSERT(code_index == iseq_size); + RUBY_ASSERT(reading_pos == bytecode_offset + bytecode_size); return code; } @@ -11413,19 +13241,17 @@ ibf_load_param_keyword(const struct ibf_load *load, ibf_offset_t param_keyword_o { if (param_keyword_offset) { struct rb_iseq_param_keyword *kw = IBF_R(param_keyword_offset, struct rb_iseq_param_keyword, 1); - ID *ids = IBF_R(kw->table, ID, kw->num); int dv_num = kw->num - kw->required_num; - VALUE *dvs = IBF_R(kw->default_values, VALUE, dv_num); - int i; + VALUE *dvs = dv_num ? IBF_R(kw->default_values, VALUE, dv_num) : NULL; - for (i=0; i<kw->num; i++) { - ids[i] = ibf_load_id(load, ids[i]); - } + int i; for (i=0; i<dv_num; i++) { dvs[i] = ibf_load_object(load, dvs[i]); } - kw->table = ids; + // Will be set once the local table is loaded. + kw->table = NULL; + kw->default_values = dvs; return kw; } @@ -11510,14 +13336,19 @@ ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq) int i; for (i=0; i<size; i++) { - table[i] = ibf_dump_id(dump, body->local_table[i]); + VALUE v = ibf_dump_id(dump, body->local_table[i]); + if (v == 0) { + /* Dump hidden local variables as indexes, so load_from_binary will work with them */ + v = ibf_dump_object(dump, ULONG2NUM(body->local_table[i])); + } + table[i] = v; } IBF_W_ALIGN(ID); return ibf_dump_write(dump, table, sizeof(ID) * size); } -static ID * +static const ID * ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offset, int size) { if (size > 0) { @@ -11527,7 +13358,14 @@ ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offse for (i=0; i<size; i++) { table[i] = ibf_load_id(load, table[i]); } - return table; + + if (size == 1 && table[0] == idERROR_INFO) { + ruby_xfree_sized(table, sizeof(ID) * size); + return rb_iseq_shared_exc_local_tbl; + } + else { + return table; + } } else { return NULL; @@ -11535,6 +13373,28 @@ ibf_load_local_table(const struct ibf_load *load, ibf_offset_t local_table_offse } static ibf_offset_t +ibf_dump_lvar_states(struct ibf_dump *dump, const rb_iseq_t *iseq) +{ + const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq); + const int size = body->local_table_size; + IBF_W_ALIGN(enum lvar_state); + return ibf_dump_write(dump, body->lvar_states, sizeof(enum lvar_state) * (body->lvar_states ? size : 0)); +} + +static enum lvar_state * +ibf_load_lvar_states(const struct ibf_load *load, ibf_offset_t lvar_states_offset, int size, const ID *local_table) +{ + if (local_table == rb_iseq_shared_exc_local_tbl || + size <= 0) { + return NULL; + } + else { + enum lvar_state *states = IBF_R(lvar_states_offset, enum lvar_state, size); + return states; + } +} + +static ibf_offset_t ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq) { const struct iseq_catch_table *table = ISEQ_BODY(iseq)->catch_table; @@ -11564,12 +13424,13 @@ ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq) } } -static struct iseq_catch_table * -ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size) +static void +ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offset, unsigned int size, const rb_iseq_t *parent_iseq) { if (size) { - struct iseq_catch_table *table = ruby_xmalloc(iseq_catch_table_bytes(size)); + struct iseq_catch_table *table = ruby_xcalloc(1, iseq_catch_table_bytes(size)); table->size = size; + ISEQ_BODY(parent_iseq)->catch_table = table; ibf_offset_t reading_pos = catch_table_offset; @@ -11582,12 +13443,12 @@ ibf_load_catch_table(const struct ibf_load *load, ibf_offset_t catch_table_offse table->entries[i].cont = (unsigned int)ibf_load_small_value(load, &reading_pos); table->entries[i].sp = (unsigned int)ibf_load_small_value(load, &reading_pos); - table->entries[i].iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index); + rb_iseq_t *catch_iseq = (rb_iseq_t *)ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)iseq_index); + RB_OBJ_WRITE(parent_iseq, UNALIGNED_MEMBER_PTR(&table->entries[i], iseq), catch_iseq); } - return table; } else { - return NULL; + ISEQ_BODY(parent_iseq)->catch_table = NULL; } } @@ -11631,15 +13492,44 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq) return offset; } +struct outer_variable_pair { + ID id; + VALUE name; + VALUE val; +}; + +struct outer_variable_list { + size_t num; + struct outer_variable_pair pairs[1]; +}; + static enum rb_id_table_iterator_result -dump_outer_variable(ID id, VALUE val, void *dump) +store_outer_variable(ID id, VALUE val, void *dump) { - ibf_dump_write_small_value(dump, ibf_dump_id(dump, id)); - ibf_dump_write_small_value(dump, val); - + struct outer_variable_list *ovlist = dump; + struct outer_variable_pair *pair = &ovlist->pairs[ovlist->num++]; + pair->id = id; + pair->name = rb_id2str(id); + pair->val = val; return ID_TABLE_CONTINUE; } +static int +outer_variable_cmp(const void *a, const void *b, void *arg) +{ + const struct outer_variable_pair *ap = (const struct outer_variable_pair *)a; + const struct outer_variable_pair *bp = (const struct outer_variable_pair *)b; + + if (!ap->name) { + return -1; + } + else if (!bp->name) { + return 1; + } + + return rb_str_cmp(ap->name, bp->name); +} + static ibf_offset_t ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq) { @@ -11647,12 +13537,24 @@ ibf_dump_outer_variables(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_offset_t offset = ibf_dump_pos(dump); - if (ovs) { - ibf_dump_write_small_value(dump, (VALUE)rb_id_table_size(ovs)); - rb_id_table_foreach(ovs, dump_outer_variable, (void *)dump); - } - else { - ibf_dump_write_small_value(dump, (VALUE)0); + size_t size = ovs ? rb_id_table_size(ovs) : 0; + ibf_dump_write_small_value(dump, (VALUE)size); + if (size > 0) { + VALUE buff; + size_t buffsize = + rb_size_mul_add_or_raise(sizeof(struct outer_variable_pair), size, + offsetof(struct outer_variable_list, pairs), + rb_eArgError); + struct outer_variable_list *ovlist = RB_ALLOCV(buff, buffsize); + ovlist->num = 0; + rb_id_table_foreach(ovs, store_outer_variable, ovlist); + ruby_qsort(ovlist->pairs, size, sizeof(struct outer_variable_pair), outer_variable_cmp, NULL); + for (size_t i = 0; i < size; ++i) { + ID id = ovlist->pairs[i].id; + ID val = ovlist->pairs[i].val; + ibf_dump_write_small_value(dump, ibf_dump_id(dump, id)); + ibf_dump_write_small_value(dump, val); + } } return offset; @@ -11665,6 +13567,11 @@ ibf_load_ci_entries(const struct ibf_load *load, unsigned int ci_size, struct rb_call_data **cd_ptr) { + if (!ci_size) { + *cd_ptr = NULL; + return; + } + ibf_offset_t reading_pos = ci_entries_offset; unsigned int i; @@ -11683,6 +13590,7 @@ ibf_load_ci_entries(const struct ibf_load *load, int kwlen = (int)ibf_load_small_value(load, &reading_pos); if (kwlen > 0) { kwarg = rb_xmalloc_mul_add(kwlen, sizeof(VALUE), sizeof(struct rb_callinfo_kwarg)); + kwarg->references = 0; kwarg->keyword_len = kwlen; for (int j=0; j<kwlen; j++) { VALUE keyword = ibf_load_small_value(load, &reading_pos); @@ -11728,7 +13636,7 @@ ibf_load_outer_variables(const struct ibf_load * load, ibf_offset_t outer_variab static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) { - assert(dump->current_buffer == &dump->global_buffer); + RUBY_ASSERT(dump->current_buffer == &dump->global_buffer); unsigned int *positions; @@ -11756,9 +13664,10 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) positions = rb_iseq_insns_info_decode_positions(ISEQ_BODY(iseq)); const ibf_offset_t insns_info_positions_offset = ibf_dump_insns_info_positions(dump, positions, body->insns_info.size); - ruby_xfree(positions); + SIZED_FREE_N(positions, ISEQ_BODY(iseq)->insns_info.size); const ibf_offset_t local_table_offset = ibf_dump_local_table(dump, iseq); + const ibf_offset_t lvar_states_offset = ibf_dump_lvar_states(dump, iseq); const unsigned int catch_table_size = body->catch_table ? body->catch_table->size : 0; const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq); const int parent_iseq_index = ibf_dump_iseq(dump, ISEQ_BODY(iseq)->parent_iseq); @@ -11787,7 +13696,12 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) (body->param.flags.has_block << 6) | (body->param.flags.ambiguous_param0 << 7) | (body->param.flags.accepts_no_kwarg << 8) | - (body->param.flags.ruby2_keywords << 9); + (body->param.flags.ruby2_keywords << 9) | + (body->param.flags.anon_rest << 10) | + (body->param.flags.anon_kwrest << 11) | + (body->param.flags.use_block << 12) | + (body->param.flags.forwardable << 13) | + (body->param.flags.accepts_no_block << 14); #if IBF_ISEQ_ENABLE_LOCAL_BUFFER # define IBF_BODY_OFFSET(x) (x) @@ -11822,6 +13736,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(insns_info_positions_offset)); ibf_dump_write_small_value(dump, body->insns_info.size); ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(local_table_offset)); + ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(lvar_states_offset)); ibf_dump_write_small_value(dump, catch_table_size); ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset)); ibf_dump_write_small_value(dump, parent_iseq_index); @@ -11837,8 +13752,8 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) ibf_dump_write_small_value(dump, body->ic_size); ibf_dump_write_small_value(dump, body->ci_size); ibf_dump_write_small_value(dump, body->stack_max); - ibf_dump_write_small_value(dump, body->catch_except_p); - ibf_dump_write_small_value(dump, body->builtin_inline_p); + ibf_dump_write_small_value(dump, body->builtin_attrs); + ibf_dump_write_small_value(dump, body->prism ? 1 : 0); #undef IBF_BODY_OFFSET @@ -11923,7 +13838,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) const VALUE location_pathobj_index = ibf_load_small_value(load, &reading_pos); const VALUE location_base_label_index = ibf_load_small_value(load, &reading_pos); const VALUE location_label_index = ibf_load_small_value(load, &reading_pos); - const VALUE location_first_lineno = ibf_load_small_value(load, &reading_pos); + const int location_first_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_node_id = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_lineno = (int)ibf_load_small_value(load, &reading_pos); const int location_code_location_beg_pos_column = (int)ibf_load_small_value(load, &reading_pos); @@ -11933,6 +13848,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) const ibf_offset_t insns_info_positions_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos)); const unsigned int insns_info_size = (unsigned int)ibf_load_small_value(load, &reading_pos); const ibf_offset_t local_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos)); + const ibf_offset_t lvar_states_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos)); const unsigned int catch_table_size = (unsigned int)ibf_load_small_value(load, &reading_pos); const ibf_offset_t catch_table_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos)); const int parent_iseq_index = (int)ibf_load_small_value(load, &reading_pos); @@ -11950,8 +13866,42 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos); const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos); - const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos); - const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos); + const unsigned int builtin_attrs = (unsigned int)ibf_load_small_value(load, &reading_pos); + const bool prism = (bool)ibf_load_small_value(load, &reading_pos); + + // setup fname and dummy frame + VALUE path = ibf_load_object(load, location_pathobj_index); + { + VALUE realpath = Qnil; + + if (RB_TYPE_P(path, T_STRING)) { + realpath = path = rb_fstring(path); + } + else if (RB_TYPE_P(path, T_ARRAY)) { + VALUE pathobj = path; + if (RARRAY_LEN(pathobj) != 2) { + rb_raise(rb_eRuntimeError, "path object size mismatch"); + } + path = rb_fstring(RARRAY_AREF(pathobj, 0)); + realpath = RARRAY_AREF(pathobj, 1); + if (!NIL_P(realpath)) { + if (!RB_TYPE_P(realpath, T_STRING)) { + rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE + "(%x), path=%+"PRIsVALUE, + realpath, TYPE(realpath), path); + } + realpath = rb_fstring(realpath); + } + } + else { + rb_raise(rb_eRuntimeError, "unexpected path object"); + } + rb_iseq_pathobj_set(iseq, path, realpath); + } + + // push dummy frame + rb_execution_context_t *ec = GET_EC(); + VALUE dummy_frame = rb_vm_push_frame_fname(ec, path); #undef IBF_BODY_OFFSET @@ -11967,6 +13917,11 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->param.flags.ambiguous_param0 = (param_flags >> 7) & 1; load_body->param.flags.accepts_no_kwarg = (param_flags >> 8) & 1; load_body->param.flags.ruby2_keywords = (param_flags >> 9) & 1; + load_body->param.flags.anon_rest = (param_flags >> 10) & 1; + load_body->param.flags.anon_kwrest = (param_flags >> 11) & 1; + load_body->param.flags.use_block = (param_flags >> 12) & 1; + load_body->param.flags.forwardable = (param_flags >> 13) & 1; + load_body->param.flags.accepts_no_block = (param_flags >> 14) & 1; load_body->param.size = param_size; load_body->param.lead_num = param_lead_num; load_body->param.opt_num = param_opt_num; @@ -11989,14 +13944,20 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->location.code_location.beg_pos.column = location_code_location_beg_pos_column; load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno; load_body->location.code_location.end_pos.column = location_code_location_end_pos_column; - load_body->catch_except_p = catch_except_p; - load_body->builtin_inline_p = builtin_inline_p; + load_body->builtin_attrs = builtin_attrs; + load_body->prism = prism; load_body->ivc_size = ivc_size; load_body->icvarc_size = icvarc_size; load_body->ise_size = ise_size; load_body->ic_size = ic_size; - load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body)); + + if (ISEQ_IS_SIZE(load_body)) { + load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(load_body)); + } + else { + load_body->is_entries = NULL; + } ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data); load_body->outer_variables = ibf_load_outer_variables(load, outer_variables_offset); load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num); @@ -12005,10 +13966,23 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load_body->insns_info.body = ibf_load_insns_info_body(load, insns_info_body_offset, insns_info_size); load_body->insns_info.positions = ibf_load_insns_info_positions(load, insns_info_positions_offset, insns_info_size); load_body->local_table = ibf_load_local_table(load, local_table_offset, local_table_size); - load_body->catch_table = ibf_load_catch_table(load, catch_table_offset, catch_table_size); - load_body->parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index); - load_body->local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index); - load_body->mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index); + load_body->lvar_states = ibf_load_lvar_states(load, lvar_states_offset, local_table_size, load_body->local_table); + ibf_load_catch_table(load, catch_table_offset, catch_table_size, iseq); + + const rb_iseq_t *parent_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)parent_iseq_index); + const rb_iseq_t *local_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)local_iseq_index); + const rb_iseq_t *mandatory_only_iseq = ibf_load_iseq(load, (const rb_iseq_t *)(VALUE)mandatory_only_iseq_index); + + RB_OBJ_WRITE(iseq, &load_body->parent_iseq, parent_iseq); + RB_OBJ_WRITE(iseq, &load_body->local_iseq, local_iseq); + RB_OBJ_WRITE(iseq, &load_body->mandatory_only_iseq, mandatory_only_iseq); + + // This must be done after the local table is loaded. + if (load_body->param.keyword != NULL) { + RUBY_ASSERT(load_body->local_table); + struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *) load_body->param.keyword; + keyword->table = &load_body->local_table[keyword->bits_start - keyword->num]; + } ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size); #if VM_INSN_INFO_TABLE_IMPL == 2 @@ -12021,33 +13995,6 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load->current_buffer = &load->global_buffer; #endif - { - VALUE realpath = Qnil, path = ibf_load_object(load, location_pathobj_index); - if (RB_TYPE_P(path, T_STRING)) { - realpath = path = rb_fstring(path); - } - else if (RB_TYPE_P(path, T_ARRAY)) { - VALUE pathobj = path; - if (RARRAY_LEN(pathobj) != 2) { - rb_raise(rb_eRuntimeError, "path object size mismatch"); - } - path = rb_fstring(RARRAY_AREF(pathobj, 0)); - realpath = RARRAY_AREF(pathobj, 1); - if (!NIL_P(realpath)) { - if (!RB_TYPE_P(realpath, T_STRING)) { - rb_raise(rb_eArgError, "unexpected realpath %"PRIxVALUE - "(%x), path=%+"PRIsVALUE, - realpath, TYPE(realpath), path); - } - realpath = rb_fstring(realpath); - } - } - else { - rb_raise(rb_eRuntimeError, "unexpected path object"); - } - rb_iseq_pathobj_set(iseq, path, realpath); - } - RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, location_base_label_index)); RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, location_label_index)); @@ -12055,6 +14002,9 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) load->current_buffer = saved_buffer; #endif verify_call_cache(iseq); + + RB_GC_GUARD(dummy_frame); + rb_vm_pop_frame_no_int(ec); } struct ibf_dump_iseq_list_arg @@ -12099,8 +14049,6 @@ ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header) header->iseq_list_size = (unsigned int)size; } -#define IBF_OBJECT_INTERNAL FL_PROMOTED0 - /* * Binary format * - ibf_object_header @@ -12160,7 +14108,11 @@ struct ibf_object_symbol { #define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \ ((((offset) - 1) / (align) + 1) * (align)) -#define IBF_OBJBODY(type, offset) (const type *)\ +/* No cast, since it's UB to create an unaligned pointer. + * Leave as void* for use with memcpy in those cases. + * We align the offset, but the buffer pointer is only VALUE aligned, + * so the returned pointer may be unaligned for `type` .*/ +#define IBF_OBJBODY(type, offset) \ ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset)) static const void * @@ -12255,8 +14207,12 @@ ibf_dump_object_float(struct ibf_dump *dump, VALUE obj) static VALUE ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset) { - const double *dblp = IBF_OBJBODY(double, offset); - return DBL2NUM(*dblp); + double d; + /* Avoid unaligned VFP load on ARMv7; IBF payload may be unaligned (C99 6.3.2.3 p7). */ + memcpy(&d, IBF_OBJBODY(double, offset), sizeof(d)); + VALUE f = DBL2NUM(d); + if (!FLONUM_P(f)) RB_OBJ_SET_SHAREABLE(f); + return f; } static void @@ -12293,7 +14249,7 @@ ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_head VALUE str; if (header->frozen && !header->internal) { - str = rb_enc_interned_str(ptr, len, rb_enc_from_index(encindex)); + str = rb_enc_literal_str(ptr, len, rb_enc_from_index(encindex)); } else { str = rb_enc_str_new(ptr, len, rb_enc_from_index(encindex)); @@ -12327,7 +14283,7 @@ ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_head VALUE reg = rb_reg_compile(srcstr, (int)regexp.option, NULL, 0); if (header->internal) rb_obj_hide(reg); - if (header->frozen) rb_obj_freeze(reg); + if (header->frozen) RB_OBJ_SET_SHAREABLE(rb_obj_freeze(reg)); return reg; } @@ -12358,7 +14314,10 @@ ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_heade rb_ary_push(ary, ibf_load_object(load, index)); } - if (header->frozen) rb_obj_freeze(ary); + if (header->frozen) { + rb_ary_freeze(ary); + rb_ractor_make_shareable(ary); // TODO: check elements + } return ary; } @@ -12389,7 +14348,7 @@ static VALUE ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset) { long len = (long)ibf_load_small_value(load, &offset); - VALUE obj = rb_hash_new_with_size(len); + VALUE obj = header->frozen ? rb_hash_alloc_fixed_size(rb_cHash, len) : rb_hash_new_with_size(len); int i; for (i = 0; i < len; i++) { @@ -12400,10 +14359,59 @@ ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header VALUE val = ibf_load_object(load, val_index); rb_hash_aset(obj, key, val); } - rb_hash_rehash(obj); if (header->internal) rb_obj_hide(obj); - if (header->frozen) rb_obj_freeze(obj); + if (header->frozen) { + RB_OBJ_SET_FROZEN_SHAREABLE(obj); + } + + return obj; +} + +static int +ibf_dump_cdhash_i(st_data_t key, st_data_t val, st_data_t ptr) +{ + struct ibf_dump *dump = (struct ibf_dump *)ptr; + + VALUE key_index = ibf_dump_object(dump, (VALUE)key); + + ibf_dump_write_small_value(dump, key_index); + ibf_dump_write_small_value(dump, val); + return ST_CONTINUE; +} + +static void +ibf_dump_object_imemo(struct ibf_dump *dump, VALUE obj) +{ + switch (imemo_type(obj)) { + case imemo_cdhash: { + st_table *tbl = rb_imemo_cdhash_tbl(obj); + + long len = tbl->num_entries; + ibf_dump_write_small_value(dump, (VALUE)len); + if (len > 0) st_foreach(tbl, ibf_dump_cdhash_i, (VALUE)dump); + break; + } + default: + ibf_dump_object_unsupported(dump, obj); + break; + } +} + +static VALUE +ibf_load_object_imemo(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset) +{ + long len = (long)ibf_load_small_value(load, &offset); + VALUE obj = cdhash_new(len); + + int i; + for (i = 0; i < len; i++) { + VALUE key_index = ibf_load_small_value(load, &offset); + VALUE val = ibf_load_small_value(load, &offset); + + VALUE key = ibf_load_object(load, key_index); + cdhash_aset(obj, key, val); + } return obj; } @@ -12439,7 +14447,7 @@ ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_head VALUE end = ibf_load_object(load, range->end); VALUE obj = rb_range_new(beg, end, range->excl); if (header->internal) rb_obj_hide(obj); - if (header->frozen) rb_obj_freeze(obj); + if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj); return obj; } @@ -12460,10 +14468,14 @@ ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_head const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset); int sign = bignum->slen > 0; ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen; - VALUE obj = rb_integer_unpack(bignum->digits, len * 2, 2, 0, - INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0)); + const int big_unpack_flags = /* c.f. rb_big_unpack() */ + INTEGER_PACK_LSWORD_FIRST | + INTEGER_PACK_NATIVE_BYTE_ORDER; + VALUE obj = rb_integer_unpack(bignum->digits, len, sizeof(BDIGIT), 0, + big_unpack_flags | + (sign == 0 ? INTEGER_PACK_NEGATIVE : 0)); if (header->internal) rb_obj_hide(obj); - if (header->frozen) rb_obj_freeze(obj); + if (header->frozen) RB_OBJ_SET_FROZEN_SHAREABLE(obj); return obj; } @@ -12524,7 +14536,7 @@ ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_o rb_complex_new(a, b) : rb_rational_new(a, b); if (header->internal) rb_obj_hide(obj); - if (header->frozen) rb_obj_freeze(obj); + if (header->frozen) rb_ractor_make_shareable(rb_obj_freeze(obj)); return obj; } @@ -12553,7 +14565,7 @@ ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_head } typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj); -static ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = { +static const ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = { ibf_dump_object_unsupported, /* T_NONE */ ibf_dump_object_unsupported, /* T_OBJECT */ ibf_dump_object_class, /* T_CLASS */ @@ -12580,7 +14592,7 @@ static ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = { ibf_dump_object_unsupported, /* 0x17 */ ibf_dump_object_unsupported, /* 0x18 */ ibf_dump_object_unsupported, /* 0x19 */ - ibf_dump_object_unsupported, /* T_IMEMO 0x1a */ + ibf_dump_object_imemo, /* T_IMEMO 0x1a */ ibf_dump_object_unsupported, /* T_NODE 0x1b */ ibf_dump_object_unsupported, /* T_ICLASS 0x1c */ ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */ @@ -12637,7 +14649,7 @@ ibf_dump_object_object(struct ibf_dump *dump, VALUE obj) else { obj_header.internal = SPECIAL_CONST_P(obj) ? FALSE : (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE; obj_header.special_const = FALSE; - obj_header.frozen = FL_TEST(obj, FL_FREEZE) ? TRUE : FALSE; + obj_header.frozen = OBJ_FROZEN(obj) ? TRUE : FALSE; ibf_dump_object_object_header(dump, obj_header); (*dump_object_functions[obj_header.type])(dump, obj); } @@ -12646,7 +14658,7 @@ ibf_dump_object_object(struct ibf_dump *dump, VALUE obj) } typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset); -static ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = { +static const ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = { ibf_load_object_unsupported, /* T_NONE */ ibf_load_object_unsupported, /* T_OBJECT */ ibf_load_object_class, /* T_CLASS */ @@ -12673,7 +14685,7 @@ static ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = { ibf_load_object_unsupported, /* 0x17 */ ibf_load_object_unsupported, /* 0x18 */ ibf_load_object_unsupported, /* 0x19 */ - ibf_load_object_unsupported, /* T_IMEMO 0x1a */ + ibf_load_object_imemo, /* T_IMEMO 0x1a */ ibf_load_object_unsupported, /* T_NODE 0x1b */ ibf_load_object_unsupported, /* T_ICLASS 0x1c */ ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */ @@ -12789,14 +14801,13 @@ ibf_dump_free(void *ptr) st_free_table(dump->iseq_table); dump->iseq_table = 0; } - ruby_xfree(dump); } static size_t ibf_dump_memsize(const void *ptr) { struct ibf_dump *dump = (struct ibf_dump *)ptr; - size_t size = sizeof(*dump); + size_t size = 0; if (dump->iseq_table) size += st_memsize(dump->iseq_table); if (dump->global_buffer.obj_table) size += st_memsize(dump->global_buffer.obj_table); return size; @@ -12805,7 +14816,7 @@ ibf_dump_memsize(const void *ptr) static const rb_data_type_t ibf_dump_type = { "ibf_dump", {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,}, - 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_EMBEDDABLE }; static void @@ -12841,7 +14852,6 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt) ibf_dump_setup(dump, dump_obj); ibf_dump_write(dump, &header, sizeof(header)); - ibf_dump_write(dump, RUBY_PLATFORM, strlen(RUBY_PLATFORM) + 1); ibf_dump_iseq(dump, iseq); header.magic[0] = 'Y'; /* YARB */ @@ -12850,6 +14860,8 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt) header.magic[3] = 'B'; header.major_version = IBF_MAJOR_VERSION; header.minor_version = IBF_MINOR_VERSION; + header.endian = IBF_ENDIAN_MARK; + header.wordsize = (uint8_t)SIZEOF_VALUE; ibf_dump_iseq_list(dump, &header); ibf_dump_object_list(dump, &header.global_object_list_offset, &header.global_object_list_size); header.size = ibf_dump_pos(dump); @@ -12867,8 +14879,6 @@ rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt) ibf_dump_overwrite(dump, &header, sizeof(header), 0); str = dump->global_buffer.str; - ibf_dump_free(dump); - DATA_PTR(dump_obj) = NULL; RB_GC_GUARD(dump_obj); return str; } @@ -12899,7 +14909,7 @@ rb_ibf_load_iseq_complete(rb_iseq_t *iseq) } #if USE_LAZY_LOAD -MJIT_FUNC_EXPORTED const rb_iseq_t * +const rb_iseq_t * rb_iseq_complete(const rb_iseq_t *iseq) { rb_ibf_load_iseq_complete((rb_iseq_t *)iseq); @@ -12942,16 +14952,12 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq) #endif pinned_list_store(load->iseq_list, iseq_index, (VALUE)iseq); -#if !USE_LAZY_LOAD + if (!USE_LAZY_LOAD || GET_VM()->builtin_function_table) { #if IBF_ISEQ_DEBUG - fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq); + fprintf(stderr, "ibf_load_iseq: loading iseq=%p\n", (void *)iseq); #endif - rb_ibf_load_iseq_complete(iseq); -#else - if (GET_VM()->builtin_function_table) { rb_ibf_load_iseq_complete(iseq); } -#endif /* !USE_LAZY_LOAD */ #if IBF_ISEQ_DEBUG fprintf(stderr, "ibf_load_iseq: iseq=%p loaded %p\n", @@ -12965,35 +14971,39 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq) static void ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, size_t size) { + struct ibf_header *header = (struct ibf_header *)bytes; load->loader_obj = loader_obj; load->global_buffer.buff = bytes; - load->header = (struct ibf_header *)load->global_buffer.buff; - load->global_buffer.size = load->header->size; - load->global_buffer.obj_list_offset = load->header->global_object_list_offset; - load->global_buffer.obj_list_size = load->header->global_object_list_size; - RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(load->header->iseq_list_size)); + load->header = header; + load->global_buffer.size = header->size; + load->global_buffer.obj_list_offset = header->global_object_list_offset; + load->global_buffer.obj_list_size = header->global_object_list_size; + RB_OBJ_WRITE(loader_obj, &load->iseq_list, pinned_list_new(header->iseq_list_size)); RB_OBJ_WRITE(loader_obj, &load->global_buffer.obj_list, pinned_list_new(load->global_buffer.obj_list_size)); load->iseq = NULL; load->current_buffer = &load->global_buffer; - if (size < load->header->size) { + if (size < header->size) { rb_raise(rb_eRuntimeError, "broken binary format"); } - if (strncmp(load->header->magic, "YARB", 4) != 0) { + if (strncmp(header->magic, "YARB", 4) != 0) { rb_raise(rb_eRuntimeError, "unknown binary format"); } - if (load->header->major_version != IBF_MAJOR_VERSION || - load->header->minor_version != IBF_MINOR_VERSION) { + if (header->major_version != IBF_MAJOR_VERSION || + header->minor_version != IBF_MINOR_VERSION) { rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)", - load->header->major_version, load->header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION); + header->major_version, header->minor_version, IBF_MAJOR_VERSION, IBF_MINOR_VERSION); } - if (strcmp(load->global_buffer.buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) { - rb_raise(rb_eRuntimeError, "unmatched platform"); + if (header->endian != IBF_ENDIAN_MARK) { + rb_raise(rb_eRuntimeError, "unmatched endian: %c", header->endian); } - if (load->header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) { + if (header->wordsize != SIZEOF_VALUE) { + rb_raise(rb_eRuntimeError, "unmatched word size: %d", header->wordsize); + } + if (header->iseq_list_offset % RUBY_ALIGNOF(ibf_offset_t)) { rb_raise(rb_eArgError, "unaligned iseq list offset: %u", - load->header->iseq_list_offset); + header->iseq_list_offset); } if (load->global_buffer.obj_list_offset % RUBY_ALIGNOF(ibf_offset_t)) { rb_raise(rb_eArgError, "unaligned object list offset: %u", @@ -13004,15 +15014,17 @@ ibf_load_setup_bytes(struct ibf_load *load, VALUE loader_obj, const char *bytes, static void ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str) { + StringValue(str); + if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) { rb_raise(rb_eRuntimeError, "broken binary format"); } -#if USE_LAZY_LOAD - str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str)); -#endif + if (USE_LAZY_LOAD) { + str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str)); + } - ibf_load_setup_bytes(load, loader_obj, StringValuePtr(str), RSTRING_LEN(str)); + ibf_load_setup_bytes(load, loader_obj, RSTRING_PTR(str), RSTRING_LEN(str)); RB_OBJ_WRITE(loader_obj, &load->str, str); } @@ -13029,7 +15041,7 @@ static void ibf_loader_free(void *ptr) { struct ibf_load *load = (struct ibf_load *)ptr; - ruby_xfree(load); + SIZED_FREE(load); } static size_t @@ -13084,3 +15096,5 @@ rb_iseq_ibf_load_extra_data(VALUE str) RB_GC_GUARD(loader_obj); return extra_str; } + +#include "prism_compile.c" |
