diff options
Diffstat (limited to 'compile.c')
| -rw-r--r-- | compile.c | 924 |
1 files changed, 592 insertions, 332 deletions
@@ -494,6 +494,7 @@ static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const 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, bool ignore); static int compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int method_call_keywords, int popped); @@ -609,8 +610,6 @@ branch_coverage_valid_p(rb_iseq_t *iseq, int first_line) return 1; } -#define PTR2NUM(x) (rb_int2inum((intptr_t)(void *)(x))) - static VALUE setup_branch(const rb_code_location_t *loc, const char *type, VALUE structure, VALUE key) { @@ -837,9 +836,9 @@ get_string_value(const NODE *node) { switch (nd_type(node)) { case NODE_STR: - return rb_node_str_string_val(node); + return RB_OBJ_SET_SHAREABLE(rb_node_str_string_val(node)); case NODE_FILE: - return rb_node_file_path_val(node); + return RB_OBJ_SET_SHAREABLE(rb_node_file_path_val(node)); default: rb_bug("unexpected node: %s", ruby_node_name(nd_type(node))); } @@ -876,6 +875,7 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node) /* iseq type of top, method, class, block */ 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: @@ -1029,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; + padding = align - 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; - } -#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) { @@ -1113,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; @@ -1128,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); } /* @@ -1398,6 +1372,9 @@ 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 * @@ -1430,7 +1407,7 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type i 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; @@ -1440,6 +1417,30 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type i 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 * new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq) { @@ -1464,7 +1465,7 @@ 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, 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; @@ -1665,7 +1666,7 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) debugs("[compile step 6.1 (remove unused catch tables)] \n"); RUBY_ASSERT(ISEQ_COMPILE_DATA(iseq)); if (!ISEQ_COMPILE_DATA(iseq)->catch_except_p && ISEQ_BODY(iseq)->catch_table) { - xfree(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; } @@ -1691,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; } @@ -1838,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)) { @@ -1858,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); } @@ -1995,6 +2038,7 @@ iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, 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_ractor_make_shareable(dv); RB_OBJ_WRITE(iseq, &dvs[i], dv); } @@ -2014,7 +2058,7 @@ iseq_set_use_block(rb_iseq_t *iseq) 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 - st_insert(vm->unused_block_warning_table, key, 1); + set_insert(&vm->unused_block_warning_table, key); } } } @@ -2026,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 = &RNODE_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; @@ -2034,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); @@ -2124,7 +2167,10 @@ iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *cons 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); @@ -2178,15 +2224,24 @@ iseq_set_local_table(rb_iseq_t *iseq, const rb_ast_id_table_t *tbl, const NODE * // 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); + 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; @@ -2275,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; @@ -2282,15 +2364,20 @@ 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) { @@ -2320,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) @@ -2481,7 +2568,7 @@ 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 (int i = 0; i < size; i++) { + for (long i = 0; i < size; i++) { VALUE sym = RARRAY_AREF(arr, i); ids[i] = SYM2ID(sym); } @@ -2587,8 +2674,13 @@ 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); if (ISEQ_IS_SIZE(body)) { body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, ISEQ_IS_SIZE(body)); @@ -2615,12 +2707,13 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) bool needs_bitmap = false; - if (ISEQ_MBITS_BUFLEN(code_index) == 1) { + 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; } @@ -2668,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); @@ -2720,12 +2811,12 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) 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); } - - vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID); } case TS_ISE: /* inline storage entry: `once` insn */ case TS_ICVARC: /* inline cvar cache */ @@ -2812,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, @@ -2848,7 +2939,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) else { body->mark_bits.list = NULL; ISEQ_COMPILE_DATA(iseq)->mark_bits.list = NULL; - ruby_xfree(mark_offset_bits); + SIZED_FREE_N(mark_offset_bits, mark_offset_bits_size); } } @@ -2856,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; @@ -3134,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) || IS_INSN_ID(insn, putchilledstring)) { + if (IS_INSN_ID(insn, dupstring) || IS_INSN_ID(insn, dupchilledstring)) { *op = OPERAND_AT(insn, 0); return 1; } @@ -3146,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) { /* @@ -3175,8 +3285,8 @@ optimize_checktype(rb_iseq_t *iseq, INSN *iobj) VALUE type; switch (INSN_OF(iobj)) { - case BIN(putstring): - case BIN(putchilledstring): + case BIN(dupstring): + case BIN(dupchilledstring): type = INT2FIX(T_STRING); break; case BIN(putnil): @@ -3374,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 * * ==> @@ -3390,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); @@ -3438,11 +3549,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal VALUE ary = iobj->operands[0]; rb_obj_reveal(ary, rb_cArray); - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = ary; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3463,12 +3570,9 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal 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); - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = hash; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3487,11 +3591,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal 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) { - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = rb_cArray_empty_frozen; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3510,11 +3610,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal 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) { - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = rb_cHash_empty_frozen; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3604,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 * */ @@ -3628,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); @@ -3666,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 @@ -3675,8 +3771,8 @@ 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(putchilledstring) || + previ == BIN(putself) || previ == BIN(dupstring) || + previ == BIN(dupchilledstring) || previ == BIN(dup) || previ == BIN(getlocal) || previ == BIN(getblockparam) || @@ -3818,10 +3914,10 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } - if (IS_INSN_ID(iobj, putstring) || IS_INSN_ID(iobj, putchilledstring) || + 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 @@ -3848,6 +3944,9 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal 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); } @@ -3889,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 ... @@ -4091,7 +4188,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal unsigned int flags = vm_ci_flag(ci); if ((flags & set_flags) == set_flags && !(flags & unset_flags)) { ((INSN*)niobj)->insn_id = BIN(putobject); - OPERAND_AT(niobj, 0) = rb_hash_freeze(rb_hash_resurrect(OPERAND_AT(niobj, 0))); + 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)); @@ -4108,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; } @@ -4150,38 +4246,28 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (method != INT2FIX(0)) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = num; - iobj->operands[1] = method; - iobj->operand_size = operand_len; + 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, putstring) || IS_INSN_ID(niobj, putchilledstring) || + 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]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 1); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK); - iobj->operand_size = operand_len; + 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, putchilledstring "E", getlocal b, send :pack with {buffer: b} - // -> putchilledstring "E", getlocal b, opt_newarray_send n+2, :pack, :buffer - else if ((IS_INSN_ID(niobj, putstring) || IS_INSN_ID(niobj, putchilledstring) || + // 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))) { @@ -4190,12 +4276,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) 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]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 2); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER); - iobj->operand_size = operand_len; + 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... @@ -4209,7 +4290,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) // 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, putstring) || IS_INSN_ID(niobj, putchilledstring) || + 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) || @@ -4229,11 +4310,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) { VALUE num = iobj->operands[0]; INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_newarray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = FIXNUM_INC(num, 1); - sendins->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P); + 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; @@ -4271,12 +4348,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) rb_obj_reveal(ary, rb_cArray); INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_duparray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1;; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = ary; - sendins->operands[1] = rb_id2sym(idIncludeP); - sendins->operands[2] = INT2FIX(1); + 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); @@ -4367,6 +4439,7 @@ 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; @@ -4399,6 +4472,22 @@ 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; // Give up if there is a throw @@ -4466,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 */ @@ -4670,7 +4759,8 @@ compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node) int cnt; if (!RNODE_DSTR(node)->nd_next) { VALUE lit = rb_node_dstr_string_val(node); - ADD_INSN1(ret, node, putstring, lit); + ADD_INSN1(ret, node, dupstring, lit); + RB_OBJ_SET_SHAREABLE(lit); RB_OBJ_WRITTEN(iseq, Qundef, lit); } else { @@ -4690,6 +4780,7 @@ compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i 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); } @@ -4782,6 +4873,7 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond, 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)); @@ -4791,6 +4883,7 @@ compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *cond, 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)); @@ -5034,13 +5127,21 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq) { switch (nd_type(node)) { case NODE_INTEGER: - return rb_node_integer_literal_val(node); + { + VALUE lit = rb_node_integer_literal_val(node); + if (!SPECIAL_CONST_P(lit)) RB_OBJ_SET_SHAREABLE(lit); + return lit; + } case NODE_FLOAT: - return rb_node_float_literal_val(node); + { + 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_node_rational_literal_val(node); + return rb_ractor_make_shareable(rb_node_rational_literal_val(node)); case NODE_IMAGINARY: - return rb_node_imaginary_literal_val(node); + return rb_ractor_make_shareable(rb_node_imaginary_literal_val(node)); case NODE_NIL: return Qnil; case NODE_TRUE: @@ -5050,7 +5151,7 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq) case NODE_SYM: return rb_node_sym_string_val(node); case NODE_REGX: - return rb_node_regx_string_val(node); + return RB_OBJ_SET_SHAREABLE(rb_node_regx_string_val(node)); case NODE_LINE: return rb_node_line_lineno_val(node); case NODE_ENCODING: @@ -5059,7 +5160,9 @@ static_literal_value(const NODE *node, rb_iseq_t *iseq) case NODE_STR: if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) { VALUE lit = get_string_value(node); - return rb_str_with_debug_created_info(lit, rb_iseq_path(iseq), (int)nd_line(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 get_string_value(node); @@ -5157,7 +5260,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop /* Create a hidden array */ for (; count; count--, node = RNODE_LIST(node)->nd_next) rb_ary_push(ary, static_literal_value(RNODE_LIST(node)->nd_head, iseq)); - OBJ_FREEZE(ary); + RB_OBJ_SET_FROZEN_SHAREABLE(ary); /* Emit optimized code */ FLUSH_CHUNK; @@ -5169,6 +5272,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop ADD_INSN1(ret, line_node, putobject, ary); ADD_INSN(ret, line_node, concattoarray); } + RB_OBJ_SET_SHAREABLE(ary); RB_OBJ_WRITTEN(iseq, Qundef, ary); } } @@ -5295,13 +5399,15 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth for (; count; count--, node = RNODE_LIST(RNODE_LIST(node)->nd_next)->nd_next) { VALUE elem[2]; 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); + VALUE hash = rb_hash_alloc_fixed_size(Qfalse, RARRAY_LEN(ary) / 2); rb_hash_bulk_insert(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary), hash); - hash = rb_obj_hide(hash); - OBJ_FREEZE(hash); + RB_GC_GUARD(ary); + hash = RB_OBJ_SET_FROZEN_SHAREABLE(hash); /* Emit optimized code */ FLUSH_CHUNK(); @@ -5450,8 +5556,8 @@ when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals, 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) || nd_type_p(val, NODE_FILE)) { @@ -5968,10 +6074,12 @@ collect_const_segments(rb_iseq_t *iseq, const NODE *node) 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)); @@ -6014,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)) { @@ -6022,9 +6147,13 @@ compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath) return VM_DEFINECLASS_FLAG_SCOPED; } else if (nd_type_p(cpath, NODE_COLON2) && RNODE_COLON2(cpath)->nd_head) { - /* Bar::Foo */ + /* Bar::Foo or expr::Foo */ NO_CHECK(COMPILE(ret, "nd_else->nd_head", RNODE_COLON2(cpath)->nd_head)); - return VM_DEFINECLASS_FLAG_SCOPED; + 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 */ @@ -6381,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; @@ -6640,6 +6769,14 @@ setup_args_dup_rest_p(const NODE *argn) 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; } @@ -6962,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; @@ -6973,8 +7110,6 @@ 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", RNODE_CASE(node)->nd_head)); branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case"); @@ -7595,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)); } @@ -7632,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) */)); @@ -8973,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 (get_node_call_nd_mid(node) == idAREF && !private_recv_p(node) && get_nd_args(node) && - nd_type_p(get_nd_args(node), NODE_LIST) && RNODE_LIST(get_nd_args(node))->as.nd_alen == 1 && - (nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(get_nd_args(node))->nd_head, NODE_FILE)) && - ISEQ_COMPILE_DATA(iseq)->current_block == NULL && - !frozen_string_literal_p(iseq) && - ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) { - VALUE str = get_string_value(RNODE_LIST(get_nd_args(node))->nd_head); - CHECK(COMPILE(ret, "recv", get_nd_recv(node))); - 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; } @@ -9149,6 +9267,9 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node) // 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; } @@ -9250,16 +9371,18 @@ compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const N 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; VALUE ast_value = rb_ruby_ast_new(RNODE(&scope_node)); - ISEQ_BODY(iseq)->mandatory_only_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), 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); ALLOCV_END(idtmp); return COMPILE_OK; @@ -9380,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; @@ -9475,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); @@ -9491,7 +9626,37 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co if ((flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT) && !(flag & VM_CALL_KW_SPLAT_MUT)) { ADD_INSN(ret, line_node, splatkw); } - ADD_SEND_R(ret, line_node, mid, argc, parent_block, INT2FIX(flag), keywords); + + 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) { @@ -10076,9 +10241,13 @@ 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, rb_node_regx_string_val(node)); - 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", RNODE_MATCH2(node)->nd_recv)); @@ -10155,6 +10324,7 @@ compile_colon3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { 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); } @@ -10182,6 +10352,7 @@ compile_dots(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in 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); } @@ -10276,31 +10447,6 @@ compile_attrasgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node LABEL *else_label = NULL; VALUE branches = Qfalse; - /* optimization shortcut - * obj["literal"] = value -> opt_aset_with(obj, "literal", value) - */ - if (!ISEQ_COMPILE_DATA(iseq)->in_masgn && - mid == idASET && !private_recv_p(node) && RNODE_ATTRASGN(node)->nd_args && - nd_type_p(RNODE_ATTRASGN(node)->nd_args, NODE_LIST) && RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->as.nd_alen == 2 && - (nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_STR) || nd_type_p(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head, NODE_FILE)) && - ISEQ_COMPILE_DATA(iseq)->current_block == NULL && - !frozen_string_literal_p(iseq) && - ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) - { - VALUE str = get_string_value(RNODE_LIST(RNODE_ATTRASGN(node)->nd_args)->nd_head); - CHECK(COMPILE(ret, "recv", RNODE_ATTRASGN(node)->nd_recv)); - CHECK(COMPILE(ret, "value", RNODE_LIST(RNODE_LIST(RNODE_ATTRASGN(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, RNODE_ATTRASGN(node)->nd_args, &flag, NULL); @@ -10643,8 +10789,10 @@ compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_pa ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); } else if (nd_type(node) == NODE_HASH) { - int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; - ADD_INSN1(anchor, node, newhash, INT2FIX(len)); + 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; @@ -10658,8 +10806,10 @@ compile_shareable_literal_constant(rb_iseq_t *iseq, LINK_ANCHOR *ret, enum rb_pa ADD_INSN1(anchor, node, newarray, INT2FIX(RNODE_LIST(node)->as.nd_alen)); } else if (nd_type(node) == NODE_HASH) { - int len = (int)RNODE_LIST(RNODE_HASH(node)->nd_head)->as.nd_alen; - ADD_INSN1(anchor, node, newhash, INT2FIX(len)); + 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; @@ -10833,10 +10983,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } case NODE_MASGN:{ - bool prev_in_masgn = ISEQ_COMPILE_DATA(iseq)->in_masgn; - ISEQ_COMPILE_DATA(iseq)->in_masgn = true; compile_massign(iseq, ret, node, popped); - ISEQ_COMPILE_DATA(iseq)->in_masgn = prev_in_masgn; break; } @@ -11018,6 +11165,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) { 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); } @@ -11083,6 +11231,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } 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); @@ -11092,6 +11241,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } 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); @@ -11101,6 +11251,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } 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); @@ -11110,6 +11261,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no } 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); @@ -11126,13 +11278,14 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no 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, putchilledstring, lit); + ADD_INSN1(ret, node, dupchilledstring, lit); break; case ISEQ_FROZEN_STRING_LITERAL_DISABLED: - ADD_INSN1(ret, node, putstring, lit); + ADD_INSN1(ret, node, dupstring, lit); break; case ISEQ_FROZEN_STRING_LITERAL_ENABLED: ADD_INSN1(ret, node, putobject, lit); @@ -11180,6 +11333,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no 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); } @@ -11372,9 +11526,20 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const no 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) { @@ -11951,7 +12116,7 @@ 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 ADD_ELEM(anchor, @@ -12031,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); } @@ -12177,23 +12342,18 @@ 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]; @@ -12389,7 +12549,7 @@ typedef uint32_t ibf_offset_t; #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 @@ -12548,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; } @@ -12787,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); @@ -12873,21 +13039,23 @@ 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; @@ -12919,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. @@ -12973,12 +13137,12 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod 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); } - - vm_ic_attr_index_initialize(cache, INVALID_SHAPE_ID); } } @@ -13010,20 +13174,9 @@ 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; } RUBY_ASSERT(code_index == iseq_size); @@ -13195,7 +13348,7 @@ ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq) 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) { @@ -13205,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; @@ -13213,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; @@ -13242,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; @@ -13260,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; } } @@ -13336,6 +13519,14 @@ 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); } @@ -13473,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); @@ -13508,7 +13700,8 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) (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.forwardable << 13) | + (body->param.flags.accepts_no_block << 14); #if IBF_ISEQ_ENABLE_LOCAL_BUFFER # define IBF_BODY_OFFSET(x) (x) @@ -13543,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); @@ -13654,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); @@ -13726,6 +13921,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) 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; @@ -13770,10 +13966,16 @@ 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) { @@ -13847,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 @@ -13908,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 * @@ -14003,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 @@ -14075,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; } @@ -14106,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_ary_freeze(ary); + if (header->frozen) { + rb_ary_freeze(ary); + rb_ractor_make_shareable(ary); // TODO: check elements + } return ary; } @@ -14137,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++) { @@ -14148,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; } @@ -14187,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; } @@ -14215,7 +14475,7 @@ ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_head 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; } @@ -14276,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; } @@ -14332,7 +14592,7 @@ static const 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 */ @@ -14389,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); } @@ -14425,7 +14685,7 @@ static const 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 */ @@ -14781,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 |
