зеркало из https://github.com/github/ruby.git
Combine call info and cache to speed up method invocation
To perform a regular method call, the VM needs two structs, `rb_call_info` and `rb_call_cache`. At the moment, we allocate these two structures in separate buffers. In the worst case, the CPU needs to read 4 cache lines to complete a method call. Putting the two structures together reduces the maximum number of cache line reads to 2. Combining the structures also saves 8 bytes per call site as the current layout uses separate two pointers for the call info and the call cache. This saves about 2 MiB on Discourse. This change improves the Optcarrot benchmark at least 3%. For more details, see attached bugs.ruby-lang.org ticket. Complications: - A new instruction attribute `comptime_sp_inc` is introduced to calculate SP increase at compile time without using call caches. At compile time, a `TS_CALLDATA` operand points to a call info struct, but at runtime, the same operand points to a call data struct. Instruction that explicitly define `sp_inc` also need to define `comptime_sp_inc`. - MJIT code for copying call cache becomes slightly more complicated. - This changes the bytecode format, which might break existing tools. [Misc #16258]
This commit is contained in:
Родитель
38e931fa2c
Коммит
89e7997622
|
@ -974,7 +974,7 @@ $(srcs_vpath)insns.inc: $(srcdir)/tool/ruby_vm/views/insns.inc.erb $(inc_common_
|
|||
$(srcs_vpath)insns_info.inc: $(srcdir)/tool/ruby_vm/views/insns_info.inc.erb $(inc_common_headers) \
|
||||
$(srcdir)/tool/ruby_vm/views/_insn_type_chars.erb $(srcdir)/tool/ruby_vm/views/_insn_name_info.erb \
|
||||
$(srcdir)/tool/ruby_vm/views/_insn_len_info.erb $(srcdir)/tool/ruby_vm/views/_insn_operand_info.erb \
|
||||
$(srcdir)/tool/ruby_vm/views/_attributes.erb $(srcdir)/tool/ruby_vm/views/_insn_stack_increase.erb \
|
||||
$(srcdir)/tool/ruby_vm/views/_attributes.erb $(srcdir)/tool/ruby_vm/views/_comptime_insn_stack_increase.erb \
|
||||
$(srcdir)/tool/ruby_vm/views/_insn_sp_pc_dependency.erb
|
||||
$(srcs_vpath)vmtc.inc: $(srcdir)/tool/ruby_vm/views/vmtc.inc.erb $(inc_common_headers)
|
||||
$(srcs_vpath)vm.inc: $(srcdir)/tool/ruby_vm/views/vm.inc.erb $(inc_common_headers) \
|
||||
|
|
237
compile.c
237
compile.c
|
@ -546,6 +546,31 @@ verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const anchor)
|
|||
#define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
|
||||
#endif
|
||||
|
||||
static void
|
||||
verify_call_cache(rb_iseq_t *iseq)
|
||||
{
|
||||
return; /* comment out to enable */
|
||||
|
||||
VALUE *original = rb_iseq_original_iseq(iseq);
|
||||
size_t i = 0;
|
||||
while (i < iseq->body->iseq_size) {
|
||||
VALUE insn = original[i];
|
||||
const char *types = insn_op_types(insn);
|
||||
|
||||
for (int j=0; types[j]; j++) {
|
||||
if (types[j] == TS_CALLDATA) {
|
||||
struct rb_call_cache cc;
|
||||
struct rb_call_data *cd = (struct rb_call_data *)original[i+j+1];
|
||||
MEMZERO(&cc, cc, 1);
|
||||
if (memcmp(&cc, &cd->cc, sizeof(cc))) {
|
||||
rb_bug("call cache not zero for fresh iseq");
|
||||
}
|
||||
}
|
||||
}
|
||||
i += insn_len(insn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* elem1, elem2 => elem1, elem2, elem
|
||||
*/
|
||||
|
@ -1178,11 +1203,10 @@ 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, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_call_info_kw_arg *keywords)
|
||||
{
|
||||
VALUE *operands = compile_data_alloc2(iseq, sizeof(VALUE), 3);
|
||||
VALUE *operands = compile_data_alloc2(iseq, sizeof(VALUE), 2);
|
||||
operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
|
||||
operands[1] = Qfalse; /* cache */
|
||||
operands[2] = (VALUE)blockiseq;
|
||||
return new_insn_core(iseq, line_no, BIN(send), 3, operands);
|
||||
operands[1] = (VALUE)blockiseq;
|
||||
return new_insn_core(iseq, line_no, BIN(send), 2, operands);
|
||||
}
|
||||
|
||||
static rb_iseq_t *
|
||||
|
@ -1358,6 +1382,7 @@ iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
VALUE str = rb_iseq_disasm(iseq);
|
||||
printf("%s\n", StringValueCStr(str));
|
||||
}
|
||||
verify_call_cache(iseq);
|
||||
debugs("[compile step: finish]\n");
|
||||
|
||||
return COMPILE_OK;
|
||||
|
@ -2090,13 +2115,10 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
|
||||
positions = ALLOC_N(unsigned int, insn_num);
|
||||
body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
|
||||
body->ci_entries =
|
||||
rb_xmalloc_mul_add_mul(
|
||||
sizeof(struct rb_call_info), body->ci_size,
|
||||
sizeof(struct rb_call_info_with_kwarg), body->ci_kw_size);
|
||||
MEMZERO(body->ci_entries + body->ci_size, struct rb_call_info_with_kwarg, body->ci_kw_size); /* need to clear ci_kw entries */
|
||||
body->cc_entries = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size);
|
||||
|
||||
body->call_data =
|
||||
rb_xcalloc_mul_add_mul(
|
||||
sizeof(struct rb_call_data), body->ci_size,
|
||||
sizeof(struct rb_kwarg_call_data), body->ci_kw_size);
|
||||
ISEQ_COMPILE_DATA(iseq)->ci_index = ISEQ_COMPILE_DATA(iseq)->ci_kw_index = 0;
|
||||
|
||||
list = FIRST_ELEMENT(anchor);
|
||||
|
@ -2180,33 +2202,27 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
generated_iseq[code_index + 1 + j] = (VALUE)ic;
|
||||
break;
|
||||
}
|
||||
case TS_CALLINFO: /* call info */
|
||||
{
|
||||
struct rb_call_info *base_ci = (struct rb_call_info *)operands[j];
|
||||
struct rb_call_info *ci;
|
||||
case TS_CALLDATA:
|
||||
{
|
||||
struct rb_call_info *source_ci = (struct rb_call_info *)operands[j];
|
||||
struct rb_call_data *cd;
|
||||
|
||||
if (base_ci->flag & VM_CALL_KWARG) {
|
||||
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&body->ci_entries[body->ci_size];
|
||||
struct rb_call_info_with_kwarg *ci_kw = &ci_kw_entries[ISEQ_COMPILE_DATA(iseq)->ci_kw_index++];
|
||||
*ci_kw = *((struct rb_call_info_with_kwarg *)base_ci);
|
||||
ci = (struct rb_call_info *)ci_kw;
|
||||
assert(ISEQ_COMPILE_DATA(iseq)->ci_kw_index <= body->ci_kw_size);
|
||||
}
|
||||
else {
|
||||
ci = &body->ci_entries[ISEQ_COMPILE_DATA(iseq)->ci_index++];
|
||||
*ci = *base_ci;
|
||||
assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
|
||||
}
|
||||
if (source_ci->flag & VM_CALL_KWARG) {
|
||||
struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
|
||||
struct rb_kwarg_call_data *cd_kw = &kw_calls[ISEQ_COMPILE_DATA(iseq)->ci_kw_index++];
|
||||
cd_kw->ci_kw = *((struct rb_call_info_with_kwarg *)source_ci);
|
||||
cd = (struct rb_call_data *)cd_kw;
|
||||
assert(ISEQ_COMPILE_DATA(iseq)->ci_kw_index <= body->ci_kw_size);
|
||||
}
|
||||
else {
|
||||
cd = &body->call_data[ISEQ_COMPILE_DATA(iseq)->ci_index++];
|
||||
cd->ci = *source_ci;
|
||||
assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= body->ci_size);
|
||||
}
|
||||
|
||||
generated_iseq[code_index + 1 + j] = (VALUE)ci;
|
||||
break;
|
||||
}
|
||||
case TS_CALLCACHE:
|
||||
{
|
||||
struct rb_call_cache *cc = &body->cc_entries[ISEQ_COMPILE_DATA(iseq)->ci_index + ISEQ_COMPILE_DATA(iseq)->ci_kw_index - 1];
|
||||
generated_iseq[code_index + 1 + j] = (VALUE)cc;
|
||||
break;
|
||||
}
|
||||
generated_iseq[code_index + 1 + j] = (VALUE)cd;
|
||||
break;
|
||||
}
|
||||
case TS_ID: /* ID */
|
||||
generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
|
||||
break;
|
||||
|
@ -2530,7 +2546,7 @@ remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
|
|||
case TS_OFFSET:
|
||||
unref_destination((INSN *)i, pos);
|
||||
break;
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
if (((struct rb_call_info *)OPERAND_AT(i, pos))->flag & VM_CALL_KWARG)
|
||||
--(body->ci_kw_size);
|
||||
else
|
||||
|
@ -3147,9 +3163,9 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
|||
}
|
||||
|
||||
if (piobj) {
|
||||
struct rb_call_info *ci = (struct rb_call_info *)piobj->operands[0];
|
||||
struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(piobj, 0);
|
||||
if (IS_INSN_ID(piobj, send) || IS_INSN_ID(piobj, invokesuper)) {
|
||||
if (piobj->operands[2] == 0) { /* no blockiseq */
|
||||
if (OPERAND_AT(piobj, 1) == 0) { /* no blockiseq */
|
||||
ci->flag |= VM_CALL_TAILCALL;
|
||||
}
|
||||
}
|
||||
|
@ -3208,12 +3224,10 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
|
|||
|
||||
if (insn_id == BIN(opt_neq)) {
|
||||
VALUE *old_operands = iobj->operands;
|
||||
iobj->operand_size = 4;
|
||||
iobj->operand_size = 2;
|
||||
iobj->operands = compile_data_alloc2(iseq, iobj->operand_size, sizeof(VALUE));
|
||||
iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
|
||||
iobj->operands[1] = Qfalse; /* CALL_CACHE */
|
||||
iobj->operands[2] = old_operands[0];
|
||||
iobj->operands[3] = Qfalse; /* CALL_CACHE */
|
||||
iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
|
||||
iobj->operands[1] = old_operands[0];
|
||||
}
|
||||
|
||||
return COMPILE_OK;
|
||||
|
@ -3247,7 +3261,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
|||
|
||||
if (IS_INSN_ID(iobj, send)) {
|
||||
struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, 0);
|
||||
const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 2);
|
||||
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 (ci->flag & VM_CALL_ARGS_SIMPLE) {
|
||||
|
@ -4348,7 +4362,7 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const n
|
|||
CHECK(COMPILE_POPPED(ret, "masgn lhs (NODE_ATTRASGN)", node));
|
||||
|
||||
iobj = (INSN *)get_prev_insn((INSN *)LAST_ELEMENT(ret)); /* send insn */
|
||||
ci = (struct rb_call_info *)iobj->operands[0];
|
||||
ci = (struct rb_call_info *)OPERAND_AT(iobj, 0);
|
||||
ci->orig_argc += 1;
|
||||
dupidx = INT2FIX(ci->orig_argc);
|
||||
|
||||
|
@ -6667,14 +6681,12 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
|
|||
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
|
||||
VALUE str = rb_fstring(node->nd_recv->nd_lit);
|
||||
if (node->nd_mid == idUMinus) {
|
||||
ADD_INSN3(ret, line, opt_str_uminus, str,
|
||||
new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE),
|
||||
Qundef /* CALL_CACHE */);
|
||||
ADD_INSN2(ret, line, opt_str_uminus, str,
|
||||
new_callinfo(iseq, idUMinus, 0, 0, NULL, FALSE));
|
||||
}
|
||||
else {
|
||||
ADD_INSN3(ret, line, opt_str_freeze, str,
|
||||
new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE),
|
||||
Qundef /* CALL_CACHE */);
|
||||
ADD_INSN2(ret, line, opt_str_freeze, str,
|
||||
new_callinfo(iseq, idFreeze, 0, 0, NULL, FALSE));
|
||||
}
|
||||
RB_OBJ_WRITTEN(iseq, Qundef, str);
|
||||
if (popped) {
|
||||
|
@ -6693,9 +6705,8 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
|
|||
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_INSN3(ret, line, opt_aref_with, str,
|
||||
new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE),
|
||||
NULL/* CALL_CACHE */);
|
||||
ADD_INSN2(ret, line, opt_aref_with, str,
|
||||
new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE));
|
||||
RB_OBJ_WRITTEN(iseq, Qundef, str);
|
||||
if (popped) {
|
||||
ADD_INSN(ret, line, pop);
|
||||
|
@ -7565,9 +7576,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
|
||||
ADD_INSN(ret, line, putself);
|
||||
ADD_SEQ(ret, args);
|
||||
ADD_INSN3(ret, line, invokesuper,
|
||||
new_callinfo(iseq, 0, argc, flag | VM_CALL_SUPER | (type == NODE_ZSUPER ? VM_CALL_ZSUPER : 0) | VM_CALL_FCALL, keywords, parent_block != NULL),
|
||||
Qnil, /* CALL_CACHE */
|
||||
ADD_INSN2(ret, line, invokesuper,
|
||||
new_callinfo(iseq, 0, argc, flag | VM_CALL_SUPER | (type == NODE_ZSUPER ? VM_CALL_ZSUPER : 0) | VM_CALL_FCALL, keywords, parent_block != NULL),
|
||||
parent_block);
|
||||
|
||||
if (popped) {
|
||||
|
@ -8262,9 +8272,8 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
ADD_INSN(ret, line, swap);
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(1));
|
||||
}
|
||||
ADD_INSN3(ret, line, opt_aset_with, str,
|
||||
new_callinfo(iseq, idASET, 2, 0, NULL, FALSE),
|
||||
NULL/* CALL_CACHE */);
|
||||
ADD_INSN2(ret, line, opt_aset_with, str,
|
||||
new_callinfo(iseq, idASET, 2, 0, NULL, FALSE));
|
||||
RB_OBJ_WRITTEN(iseq, Qundef, str);
|
||||
ADD_INSN(ret, line, pop);
|
||||
break;
|
||||
|
@ -8367,7 +8376,7 @@ insn_data_length(INSN *iobj)
|
|||
static int
|
||||
calc_sp_depth(int depth, INSN *insn)
|
||||
{
|
||||
return insn_stack_increase(depth, insn->insn_id, insn->operands);
|
||||
return comptime_insn_stack_increase(depth, insn->insn_id, insn->operands);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -8441,17 +8450,12 @@ insn_data_to_s_detail(INSN *iobj)
|
|||
case TS_ISE: /* inline storage entry */
|
||||
rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
|
||||
break;
|
||||
case TS_CALLINFO: /* call info */
|
||||
case TS_CALLDATA: /* we store these as call infos at compile time */
|
||||
{
|
||||
struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, j);
|
||||
rb_str_cat2(str, "<callinfo:");
|
||||
if (ci->mid) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(ci->mid));
|
||||
rb_str_catf(str, ", %d>", ci->orig_argc);
|
||||
break;
|
||||
}
|
||||
case TS_CALLCACHE: /* call cache */
|
||||
{
|
||||
rb_str_catf(str, "<call cache>");
|
||||
const struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, j);
|
||||
rb_str_cat2(str, "<calldata:");
|
||||
if (ci->mid) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(ci->mid));
|
||||
rb_str_catf(str, ", %d>", ci->orig_argc);
|
||||
break;
|
||||
}
|
||||
case TS_CDHASH: /* case/when condition cache */
|
||||
|
@ -8832,12 +8836,9 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
|
|||
iseq->body->is_size = NUM2INT(op) + 1;
|
||||
}
|
||||
break;
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
argv[j] = iseq_build_callinfo_from_hash(iseq, op);
|
||||
break;
|
||||
case TS_CALLCACHE:
|
||||
argv[j] = Qfalse;
|
||||
break;
|
||||
case TS_ID:
|
||||
argv[j] = rb_to_symbol_type(op);
|
||||
break;
|
||||
|
@ -9487,9 +9488,9 @@ ibf_load_id(const struct ibf_load *load, const ID id_index)
|
|||
/* dump/load: code */
|
||||
|
||||
static VALUE
|
||||
ibf_dump_callinfo(struct ibf_dump *dump, const struct rb_call_info *ci)
|
||||
ibf_dump_calldata(struct ibf_dump *dump, const struct rb_call_data *cd)
|
||||
{
|
||||
return (ci->flag & VM_CALL_KWARG) ? Qtrue : Qfalse;
|
||||
return (cd->ci.flag & VM_CALL_KWARG) ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
|
||||
|
@ -9646,16 +9647,13 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
|
|||
ibf_dump_write_small_value(dump, (VALUE)i);
|
||||
}
|
||||
break;
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
{
|
||||
VALUE callinfo = ibf_dump_callinfo(dump, (const struct rb_call_info *)op);
|
||||
/* ibf_dump_callinfo() always returns either Qtrue or Qfalse */
|
||||
VALUE callinfo = ibf_dump_calldata(dump, (const struct rb_call_data *)op);
|
||||
/* ibf_dump_calldata() always returns either Qtrue or Qfalse */
|
||||
ibf_dump_write_byte(dump, callinfo == Qtrue);
|
||||
}
|
||||
break;
|
||||
case TS_CALLCACHE:
|
||||
/* do nothing */
|
||||
break;
|
||||
case TS_ID:
|
||||
ibf_dump_write_small_value(dump, ibf_dump_id(dump, (ID)op));
|
||||
break;
|
||||
|
@ -9684,9 +9682,8 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, ibf_offset_t b
|
|||
VALUE *code = ALLOC_N(VALUE, iseq_size);
|
||||
|
||||
struct rb_iseq_constant_body *load_body = iseq->body;
|
||||
struct rb_call_info *ci_entries = load_body->ci_entries;
|
||||
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&load_body->ci_entries[load_body->ci_size];
|
||||
struct rb_call_cache *cc_entries = load_body->cc_entries;
|
||||
struct rb_call_data *cd_entries = load_body->call_data;
|
||||
struct rb_kwarg_call_data *cd_kw_entries = (struct rb_kwarg_call_data *)&load_body->call_data[load_body->ci_size];
|
||||
union iseq_inline_storage_entry *is_entries = load_body->is_entries;
|
||||
|
||||
for (code_index=0; code_index<iseq_size;) {
|
||||
|
@ -9730,15 +9727,12 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, ibf_offset_t b
|
|||
code[code_index] = (VALUE)&is_entries[op];
|
||||
}
|
||||
break;
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
{
|
||||
unsigned char op = ibf_load_byte(load, &reading_pos);
|
||||
code[code_index] = op ? (VALUE)ci_kw_entries++ : (VALUE)ci_entries++; /* op is 1 (kw) or 0 (!kw) */
|
||||
code[code_index] = op ? (VALUE)cd_kw_entries++ : (VALUE)cd_entries++; /* op is 1 (kw) or 0 (!kw) */
|
||||
}
|
||||
break;
|
||||
case TS_CALLCACHE:
|
||||
code[code_index] = (VALUE)cc_entries++;
|
||||
break;
|
||||
case TS_ID:
|
||||
{
|
||||
VALUE op = ibf_load_small_value(load, &reading_pos);
|
||||
|
@ -10006,34 +10000,34 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
|
|||
const struct rb_iseq_constant_body *const body = iseq->body;
|
||||
const unsigned int ci_size = body->ci_size;
|
||||
const unsigned int ci_kw_size = body->ci_kw_size;
|
||||
const struct rb_call_info *ci_entries = body->ci_entries;
|
||||
const struct rb_call_info_with_kwarg *ci_kw_entries = (const struct rb_call_info_with_kwarg *)&body->ci_entries[ci_size];
|
||||
const struct rb_call_data *calls = body->call_data;
|
||||
const struct rb_kwarg_call_data *kw_calls = (const struct rb_kwarg_call_data *)&body->call_data[ci_size];
|
||||
|
||||
ibf_offset_t offset = ibf_dump_pos(dump);
|
||||
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ci_size; i++) {
|
||||
VALUE mid = ibf_dump_id(dump, ci_entries[i].mid);
|
||||
VALUE mid = ibf_dump_id(dump, calls[i].ci.mid);
|
||||
|
||||
ibf_dump_write_small_value(dump, mid);
|
||||
ibf_dump_write_small_value(dump, ci_entries[i].flag);
|
||||
ibf_dump_write_small_value(dump, ci_entries[i].orig_argc);
|
||||
ibf_dump_write_small_value(dump, calls[i].ci.flag);
|
||||
ibf_dump_write_small_value(dump, calls[i].ci.orig_argc);
|
||||
}
|
||||
|
||||
for (i = 0; i < ci_kw_size; i++) {
|
||||
const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
|
||||
const struct rb_call_info_kw_arg *kw_arg = kw_calls[i].ci_kw.kw_arg;
|
||||
|
||||
VALUE mid = ibf_dump_id(dump, ci_kw_entries[i].ci.mid);
|
||||
VALUE mid = ibf_dump_id(dump, kw_calls[i].ci_kw.ci.mid);
|
||||
|
||||
ibf_dump_write_small_value(dump, mid);
|
||||
ibf_dump_write_small_value(dump, ci_kw_entries[i].ci.flag);
|
||||
ibf_dump_write_small_value(dump, ci_kw_entries[i].ci.orig_argc);
|
||||
ibf_dump_write_small_value(dump, kw_calls[i].ci_kw.ci.flag);
|
||||
ibf_dump_write_small_value(dump, kw_calls[i].ci_kw.ci.orig_argc);
|
||||
|
||||
ibf_dump_write_small_value(dump, kw_arg->keyword_len);
|
||||
|
||||
int j;
|
||||
for (j = 0; j < ci_kw_entries[i].kw_arg->keyword_len; j++) {
|
||||
for (j = 0; j < kw_calls[i].ci_kw.kw_arg->keyword_len; j++) {
|
||||
VALUE keyword = ibf_dump_object(dump, kw_arg->keywords[j]); /* kw_arg->keywords[n] is Symbol */
|
||||
|
||||
ibf_dump_write_small_value(dump, keyword);
|
||||
|
@ -10043,7 +10037,8 @@ ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
|
|||
return offset;
|
||||
}
|
||||
|
||||
static struct rb_call_info *
|
||||
/* note that we dump out rb_call_info but load back rb_call_data */
|
||||
static struct rb_call_data *
|
||||
ibf_load_ci_entries(const struct ibf_load *load,
|
||||
ibf_offset_t ci_entries_offset,
|
||||
unsigned int ci_size,
|
||||
|
@ -10053,43 +10048,43 @@ ibf_load_ci_entries(const struct ibf_load *load,
|
|||
|
||||
unsigned int i;
|
||||
|
||||
struct rb_call_info *ci_entries =
|
||||
rb_xmalloc_mul_add_mul(
|
||||
sizeof(struct rb_call_info), ci_size,
|
||||
sizeof(struct rb_call_info_with_kwarg), ci_kw_size);
|
||||
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&ci_entries[ci_size];
|
||||
struct rb_call_data *calls =
|
||||
rb_xcalloc_mul_add_mul(
|
||||
sizeof(struct rb_call_data), ci_size,
|
||||
sizeof(struct rb_kwarg_call_data), ci_kw_size);
|
||||
struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&calls[ci_size];
|
||||
|
||||
for (i = 0; i < ci_size; i++) {
|
||||
VALUE mid_index = ibf_load_small_value(load, &reading_pos);
|
||||
|
||||
ci_entries[i].mid = ibf_load_id(load, mid_index);
|
||||
ci_entries[i].flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
|
||||
ci_entries[i].orig_argc = (int)ibf_load_small_value(load, &reading_pos);
|
||||
calls[i].ci.mid = ibf_load_id(load, mid_index);
|
||||
calls[i].ci.flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
|
||||
calls[i].ci.orig_argc = (int)ibf_load_small_value(load, &reading_pos);
|
||||
}
|
||||
|
||||
for (i = 0; i < ci_kw_size; i++) {
|
||||
VALUE mid_index = ibf_load_small_value(load, &reading_pos);
|
||||
|
||||
ci_kw_entries[i].ci.mid = ibf_load_id(load, mid_index);
|
||||
ci_kw_entries[i].ci.flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
|
||||
ci_kw_entries[i].ci.orig_argc = (int)ibf_load_small_value(load, &reading_pos);
|
||||
kw_calls[i].ci_kw.ci.mid = ibf_load_id(load, mid_index);
|
||||
kw_calls[i].ci_kw.ci.flag = (unsigned int)ibf_load_small_value(load, &reading_pos);
|
||||
kw_calls[i].ci_kw.ci.orig_argc = (int)ibf_load_small_value(load, &reading_pos);
|
||||
|
||||
int keyword_len = (int)ibf_load_small_value(load, &reading_pos);
|
||||
|
||||
ci_kw_entries[i].kw_arg =
|
||||
kw_calls[i].ci_kw.kw_arg =
|
||||
rb_xmalloc_mul_add(keyword_len - 1, sizeof(VALUE), sizeof(struct rb_call_info_kw_arg));
|
||||
|
||||
ci_kw_entries[i].kw_arg->keyword_len = keyword_len;
|
||||
kw_calls[i].ci_kw.kw_arg->keyword_len = keyword_len;
|
||||
|
||||
int j;
|
||||
for (j = 0; j < ci_kw_entries[i].kw_arg->keyword_len; j++) {
|
||||
for (j = 0; j < kw_calls[i].ci_kw.kw_arg->keyword_len; j++) {
|
||||
VALUE keyword = ibf_load_small_value(load, &reading_pos);
|
||||
|
||||
ci_kw_entries[i].kw_arg->keywords[j] = ibf_load_object(load, keyword);
|
||||
kw_calls[i].ci_kw.kw_arg->keywords[j] = ibf_load_object(load, keyword);
|
||||
}
|
||||
}
|
||||
|
||||
return ci_entries;
|
||||
return calls;
|
||||
}
|
||||
|
||||
static ibf_offset_t
|
||||
|
@ -10343,8 +10338,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
|
|||
load_body->catch_except_p = catch_except_p;
|
||||
|
||||
load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, is_size);
|
||||
load_body->ci_entries = ibf_load_ci_entries(load, ci_entries_offset, ci_size, ci_kw_size);
|
||||
load_body->cc_entries = ZALLOC_N(struct rb_call_cache, ci_size + ci_kw_size);
|
||||
load_body->call_data = ibf_load_ci_entries(load, ci_entries_offset, ci_size, ci_kw_size);
|
||||
load_body->param.opt_table = ibf_load_param_opt_table(load, param_opt_table_offset, param_opt_num);
|
||||
load_body->param.keyword = ibf_load_param_keyword(load, param_keyword_offset);
|
||||
load_body->param.flags.has_kw = (param_flags >> 4) & 1;
|
||||
|
@ -10399,6 +10393,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
|
|||
#if IBF_ISEQ_ENABLE_LOCAL_BUFFER
|
||||
load->current_buffer = saved_buffer;
|
||||
#endif
|
||||
verify_call_cache(iseq);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
100
insns.def
100
insns.def
|
@ -783,13 +783,14 @@ definesmethod
|
|||
/* invoke method. */
|
||||
DEFINE_INSN
|
||||
send
|
||||
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
|
||||
(CALL_DATA cd, ISEQ blockiseq)
|
||||
(...)
|
||||
(VALUE val)
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(&cd->ci);
|
||||
// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci);
|
||||
{
|
||||
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), ci, blockiseq, false);
|
||||
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_method_wrap);
|
||||
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), &cd->ci, blockiseq, false);
|
||||
val = vm_sendish(ec, GET_CFP(), cd, bh, vm_search_method_wrap);
|
||||
|
||||
if (val == Qundef) {
|
||||
RESTORE_REGS();
|
||||
|
@ -800,14 +801,15 @@ send
|
|||
/* Invoke method without block */
|
||||
DEFINE_INSN
|
||||
opt_send_without_block
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(...)
|
||||
(VALUE val)
|
||||
// attr bool handles_sp = true;
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(&cd->ci);
|
||||
// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci);
|
||||
{
|
||||
VALUE bh = VM_BLOCK_HANDLER_NONE;
|
||||
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_method_wrap);
|
||||
val = vm_sendish(ec, GET_CFP(), cd, bh, vm_search_method_wrap);
|
||||
|
||||
if (val == Qundef) {
|
||||
RESTORE_REGS();
|
||||
|
@ -817,7 +819,7 @@ opt_send_without_block
|
|||
|
||||
DEFINE_INSN
|
||||
opt_str_freeze
|
||||
(VALUE str, CALL_INFO ci, CALL_CACHE cc)
|
||||
(VALUE str, CALL_DATA cd)
|
||||
()
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -832,11 +834,11 @@ opt_str_freeze
|
|||
/* optimized nil? */
|
||||
DEFINE_INSN
|
||||
opt_nil_p
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
val = vm_opt_nil_p(ci, cc, recv);
|
||||
val = vm_opt_nil_p(cd, recv);
|
||||
|
||||
if (val == Qundef) {
|
||||
CALL_SIMPLE_METHOD();
|
||||
|
@ -845,7 +847,7 @@ opt_nil_p
|
|||
|
||||
DEFINE_INSN
|
||||
opt_str_uminus
|
||||
(VALUE str, CALL_INFO ci, CALL_CACHE cc)
|
||||
(VALUE str, CALL_DATA cd)
|
||||
()
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -887,13 +889,14 @@ opt_newarray_min
|
|||
/* super(args) # args.size => num */
|
||||
DEFINE_INSN
|
||||
invokesuper
|
||||
(CALL_INFO ci, CALL_CACHE cc, ISEQ blockiseq)
|
||||
(CALL_DATA cd, ISEQ blockiseq)
|
||||
(...)
|
||||
(VALUE val)
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(ci);
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_sendish(&cd->ci);
|
||||
// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci);
|
||||
{
|
||||
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), ci, blockiseq, true);
|
||||
val = vm_sendish(ec, GET_CFP(), ci, cc, bh, vm_search_super_method);
|
||||
VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), &cd->ci, blockiseq, true);
|
||||
val = vm_sendish(ec, GET_CFP(), cd, bh, vm_search_super_method);
|
||||
|
||||
if (val == Qundef) {
|
||||
RESTORE_REGS();
|
||||
|
@ -904,18 +907,19 @@ invokesuper
|
|||
/* yield(args) */
|
||||
DEFINE_INSN
|
||||
invokeblock
|
||||
(CALL_INFO ci)
|
||||
(CALL_DATA cd)
|
||||
(...)
|
||||
(VALUE val)
|
||||
// attr bool handles_sp = true;
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci);
|
||||
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(&cd->ci);
|
||||
// attr rb_snum_t comptime_sp_inc = sp_inc_of_invokeblock(ci);
|
||||
{
|
||||
static struct rb_call_cache cc = {
|
||||
0, 0, NULL, NULL, vm_invokeblock_i,
|
||||
};
|
||||
if (UNLIKELY(cd->cc.call != vm_invokeblock_i)) {
|
||||
cd->cc.call = vm_invokeblock_i; // check before setting to avoid CoW
|
||||
}
|
||||
|
||||
VALUE bh = VM_BLOCK_HANDLER_NONE;
|
||||
val = vm_sendish(ec, GET_CFP(), ci, &cc, bh, vm_search_invokeblock);
|
||||
val = vm_sendish(ec, GET_CFP(), cd, bh, vm_search_invokeblock);
|
||||
|
||||
if (val == Qundef) {
|
||||
RESTORE_REGS();
|
||||
|
@ -1098,7 +1102,7 @@ opt_case_dispatch
|
|||
/* optimized X+Y. */
|
||||
DEFINE_INSN
|
||||
opt_plus
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1112,7 +1116,7 @@ opt_plus
|
|||
/* optimized X-Y. */
|
||||
DEFINE_INSN
|
||||
opt_minus
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1126,7 +1130,7 @@ opt_minus
|
|||
/* optimized X*Y. */
|
||||
DEFINE_INSN
|
||||
opt_mult
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1140,7 +1144,7 @@ opt_mult
|
|||
/* optimized X/Y. */
|
||||
DEFINE_INSN
|
||||
opt_div
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
/* In case of division by zero, it raises. Thus
|
||||
|
@ -1157,7 +1161,7 @@ opt_div
|
|||
/* optimized X%Y. */
|
||||
DEFINE_INSN
|
||||
opt_mod
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
/* Same discussion as opt_mod. */
|
||||
|
@ -1173,11 +1177,11 @@ opt_mod
|
|||
/* optimized X==Y. */
|
||||
DEFINE_INSN
|
||||
opt_eq
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
val = opt_eq_func(recv, obj, ci, cc);
|
||||
val = opt_eq_func(recv, obj, cd);
|
||||
|
||||
if (val == Qundef) {
|
||||
CALL_SIMPLE_METHOD();
|
||||
|
@ -1187,11 +1191,11 @@ opt_eq
|
|||
/* optimized X!=Y. */
|
||||
DEFINE_INSN
|
||||
opt_neq
|
||||
(CALL_INFO ci_eq, CALL_CACHE cc_eq, CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd_eq, CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
val = vm_opt_neq(ci, cc, ci_eq, cc_eq, recv, obj);
|
||||
val = vm_opt_neq(cd, cd_eq, recv, obj);
|
||||
|
||||
if (val == Qundef) {
|
||||
CALL_SIMPLE_METHOD();
|
||||
|
@ -1201,7 +1205,7 @@ opt_neq
|
|||
/* optimized X<Y. */
|
||||
DEFINE_INSN
|
||||
opt_lt
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1215,7 +1219,7 @@ opt_lt
|
|||
/* optimized X<=Y. */
|
||||
DEFINE_INSN
|
||||
opt_le
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1229,7 +1233,7 @@ opt_le
|
|||
/* optimized X>Y. */
|
||||
DEFINE_INSN
|
||||
opt_gt
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1243,7 +1247,7 @@ opt_gt
|
|||
/* optimized X>=Y. */
|
||||
DEFINE_INSN
|
||||
opt_ge
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1257,7 +1261,7 @@ opt_ge
|
|||
/* << */
|
||||
DEFINE_INSN
|
||||
opt_ltlt
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
/* This instruction can append an integer, as a codepoint, into a
|
||||
|
@ -1275,7 +1279,7 @@ opt_ltlt
|
|||
/* optimized X&Y. */
|
||||
DEFINE_INSN
|
||||
opt_and
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1289,7 +1293,7 @@ opt_and
|
|||
/* optimized X|Y. */
|
||||
DEFINE_INSN
|
||||
opt_or
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1303,7 +1307,7 @@ opt_or
|
|||
/* [] */
|
||||
DEFINE_INSN
|
||||
opt_aref
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj)
|
||||
(VALUE val)
|
||||
/* This is complicated. In case of hash, vm_opt_aref() resorts to
|
||||
|
@ -1322,7 +1326,7 @@ opt_aref
|
|||
/* recv[obj] = set */
|
||||
DEFINE_INSN
|
||||
opt_aset
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv, VALUE obj, VALUE set)
|
||||
(VALUE val)
|
||||
/* This is another story than opt_aref. When vm_opt_aset() resorts
|
||||
|
@ -1339,7 +1343,7 @@ opt_aset
|
|||
/* recv[str] = set */
|
||||
DEFINE_INSN
|
||||
opt_aset_with
|
||||
(VALUE key, CALL_INFO ci, CALL_CACHE cc)
|
||||
(VALUE key, CALL_DATA cd)
|
||||
(VALUE recv, VALUE val)
|
||||
(VALUE val)
|
||||
/* Same discussion as opt_aset. */
|
||||
|
@ -1362,7 +1366,7 @@ opt_aset_with
|
|||
/* recv[str] */
|
||||
DEFINE_INSN
|
||||
opt_aref_with
|
||||
(VALUE key, CALL_INFO ci, CALL_CACHE cc)
|
||||
(VALUE key, CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
/* Same discussion as opt_aref. */
|
||||
|
@ -1381,7 +1385,7 @@ opt_aref_with
|
|||
/* optimized length */
|
||||
DEFINE_INSN
|
||||
opt_length
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1395,7 +1399,7 @@ opt_length
|
|||
/* optimized size */
|
||||
DEFINE_INSN
|
||||
opt_size
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1409,7 +1413,7 @@ opt_size
|
|||
/* optimized empty? */
|
||||
DEFINE_INSN
|
||||
opt_empty_p
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1423,7 +1427,7 @@ opt_empty_p
|
|||
/* optimized succ */
|
||||
DEFINE_INSN
|
||||
opt_succ
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
|
@ -1437,11 +1441,11 @@ opt_succ
|
|||
/* optimized not */
|
||||
DEFINE_INSN
|
||||
opt_not
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE recv)
|
||||
(VALUE val)
|
||||
{
|
||||
val = vm_opt_not(ci, cc, recv);
|
||||
val = vm_opt_not(cd, recv);
|
||||
|
||||
if (val == Qundef) {
|
||||
CALL_SIMPLE_METHOD();
|
||||
|
@ -1451,7 +1455,7 @@ opt_not
|
|||
/* optimized regexp match 2 */
|
||||
DEFINE_INSN
|
||||
opt_regexpmatch2
|
||||
(CALL_INFO ci, CALL_CACHE cc)
|
||||
(CALL_DATA cd)
|
||||
(VALUE obj2, VALUE obj1)
|
||||
(VALUE val)
|
||||
// attr bool leaf = false; /* match_at() has rb_thread_check_ints() */
|
||||
|
|
45
iseq.c
45
iseq.c
|
@ -102,15 +102,14 @@ rb_iseq_free(const rb_iseq_t *iseq)
|
|||
ruby_xfree((void *)body->local_table);
|
||||
ruby_xfree((void *)body->is_entries);
|
||||
|
||||
if (body->ci_entries) {
|
||||
if (body->call_data) {
|
||||
unsigned int i;
|
||||
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&body->ci_entries[body->ci_size];
|
||||
struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
|
||||
for (i=0; i<body->ci_kw_size; i++) {
|
||||
const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
|
||||
const struct rb_call_info_kw_arg *kw_arg = kw_calls[i].ci_kw.kw_arg;
|
||||
ruby_xfree((void *)kw_arg);
|
||||
}
|
||||
ruby_xfree(body->ci_entries);
|
||||
ruby_xfree(body->cc_entries);
|
||||
ruby_xfree(body->call_data);
|
||||
}
|
||||
ruby_xfree((void *)body->catch_table);
|
||||
ruby_xfree((void *)body->param.opt_table);
|
||||
|
@ -379,7 +378,7 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
|
|||
/* TODO: should we count original_iseq? */
|
||||
|
||||
if (ISEQ_EXECUTABLE_P(iseq) && body) {
|
||||
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&body->ci_entries[body->ci_size];
|
||||
struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
|
||||
|
||||
size += sizeof(struct rb_iseq_constant_body);
|
||||
size += body->iseq_size * sizeof(VALUE);
|
||||
|
@ -394,19 +393,15 @@ rb_iseq_memsize(const rb_iseq_t *iseq)
|
|||
/* body->is_entries */
|
||||
size += body->is_size * sizeof(union iseq_inline_storage_entry);
|
||||
|
||||
/* body->ci_entries */
|
||||
size += body->ci_size * sizeof(struct rb_call_info);
|
||||
size += body->ci_kw_size * sizeof(struct rb_call_info_with_kwarg);
|
||||
/* body->call_data */
|
||||
size += body->ci_size * sizeof(struct rb_call_data);
|
||||
size += body->ci_kw_size * sizeof(struct rb_kwarg_call_data);
|
||||
|
||||
/* body->cc_entries */
|
||||
size += body->ci_size * sizeof(struct rb_call_cache);
|
||||
size += body->ci_kw_size * sizeof(struct rb_call_cache);
|
||||
|
||||
if (ci_kw_entries) {
|
||||
if (kw_calls) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < body->ci_kw_size; i++) {
|
||||
const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
|
||||
const struct rb_call_info_kw_arg *kw_arg = kw_calls[i].ci_kw.kw_arg;
|
||||
|
||||
if (kw_arg) {
|
||||
size += rb_call_info_kw_arg_bytes(kw_arg->keyword_len);
|
||||
|
@ -1916,9 +1911,10 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
|
|||
ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - iseq->body->is_entries);
|
||||
break;
|
||||
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
{
|
||||
struct rb_call_info *ci = (struct rb_call_info *)op;
|
||||
struct rb_call_data *cd = (struct rb_call_data *)op;
|
||||
struct rb_call_info *ci = &cd->ci;
|
||||
VALUE ary = rb_ary_new();
|
||||
|
||||
if (ci->mid) {
|
||||
|
@ -1950,12 +1946,9 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
|
|||
CALL_FLAG(OPT_SEND); /* maybe not reachable */
|
||||
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
|
||||
}
|
||||
ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
|
||||
}
|
||||
break;
|
||||
|
||||
case TS_CALLCACHE:
|
||||
ret = rb_str_new2("<callcache>");
|
||||
ret = rb_sprintf("<calldata!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
|
||||
}
|
||||
break;
|
||||
|
||||
case TS_CDHASH:
|
||||
|
@ -2723,9 +2716,10 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
|
|||
rb_ary_push(ary, INT2FIX(is - iseq_body->is_entries));
|
||||
}
|
||||
break;
|
||||
case TS_CALLINFO:
|
||||
case TS_CALLDATA:
|
||||
{
|
||||
struct rb_call_info *ci = (struct rb_call_info *)*seq;
|
||||
struct rb_call_data *cd = (struct rb_call_data *)*seq;
|
||||
struct rb_call_info *ci = &cd->ci;
|
||||
VALUE e = rb_hash_new();
|
||||
int orig_argc = ci->orig_argc;
|
||||
|
||||
|
@ -2749,9 +2743,6 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
|
|||
rb_ary_push(ary, e);
|
||||
}
|
||||
break;
|
||||
case TS_CALLCACHE:
|
||||
rb_ary_push(ary, Qfalse);
|
||||
break;
|
||||
case TS_ID:
|
||||
rb_ary_push(ary, ID2SYM(*seq));
|
||||
break;
|
||||
|
|
11
mjit.c
11
mjit.c
|
@ -46,7 +46,16 @@ mjit_copy_job_handler(void *data)
|
|||
|
||||
const struct rb_iseq_constant_body *body = job->iseq->body;
|
||||
if (job->cc_entries) {
|
||||
memcpy(job->cc_entries, body->cc_entries, sizeof(struct rb_call_cache) * (body->ci_size + body->ci_kw_size));
|
||||
unsigned int i;
|
||||
struct rb_call_cache *sink = job->cc_entries;
|
||||
const struct rb_call_data *calls = body->call_data;
|
||||
const struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
|
||||
for (i = 0; i < body->ci_size; i++) {
|
||||
*sink++ = calls[i].cc;
|
||||
}
|
||||
for (i = 0; i < body->ci_kw_size; i++) {
|
||||
*sink++ = kw_calls[i].cc;
|
||||
}
|
||||
}
|
||||
if (job->is_entries) {
|
||||
memcpy(job->is_entries, body->is_entries, sizeof(union iseq_inline_storage_entry) * body->is_size);
|
||||
|
|
|
@ -25,6 +25,21 @@
|
|||
#define NOT_COMPILED_STACK_SIZE -1
|
||||
#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
|
||||
|
||||
static size_t
|
||||
call_data_index(CALL_DATA cd, const struct rb_iseq_constant_body *body)
|
||||
{
|
||||
const struct rb_kwarg_call_data *kw_calls = (const struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
|
||||
const struct rb_kwarg_call_data *kw_cd = (const struct rb_kwarg_call_data *)cd;
|
||||
|
||||
VM_ASSERT(cd >= body->call_data && kw_cd < (kw_calls + body->ci_kw_size));
|
||||
if (kw_cd < kw_calls) {
|
||||
return cd - body->call_data;
|
||||
}
|
||||
else {
|
||||
return kw_cd - kw_calls + body->ci_size;
|
||||
}
|
||||
}
|
||||
|
||||
// For propagating information needed for lazily pushing a frame.
|
||||
struct inlined_call_context {
|
||||
int orig_argc; // ci->orig_argc
|
||||
|
@ -383,8 +398,9 @@ precompile_inlinable_iseqs(FILE *f, const rb_iseq_t *iseq, struct compile_status
|
|||
#endif
|
||||
|
||||
if (insn == BIN(opt_send_without_block)) { // `compile_inlined_cancel_handler` supports only `opt_send_without_block`
|
||||
CALL_INFO ci = (CALL_INFO)body->iseq_encoded[pos + 1];
|
||||
CALL_CACHE cc_copy = status->cc_entries + ((CALL_CACHE)body->iseq_encoded[pos + 2] - body->cc_entries); // use copy to avoid race condition
|
||||
CALL_DATA cd = (CALL_DATA)body->iseq_encoded[pos + 1];
|
||||
CALL_INFO ci = &cd->ci;
|
||||
CALL_CACHE cc_copy = status->cc_entries + call_data_index(cd, body); // use copy to avoid race condition
|
||||
|
||||
const rb_iseq_t *child_iseq;
|
||||
if (has_valid_method_type(cc_copy) &&
|
||||
|
|
|
@ -21,6 +21,13 @@ class RubyVM::Attribute
|
|||
@key = opts[:name]
|
||||
@expr = RubyVM::CExpr.new location: opts[:location], expr: opts[:expr]
|
||||
@type = opts[:type]
|
||||
@ope_decls = @insn.opes.map do |operand|
|
||||
decl = operand[:decl]
|
||||
if @key == 'comptime_sp_inc' && operand[:type] == 'CALL_DATA'
|
||||
decl = decl.gsub('CALL_DATA', 'CALL_INFO').gsub('cd', 'ci')
|
||||
end
|
||||
decl
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
|
@ -32,22 +39,20 @@ class RubyVM::Attribute
|
|||
end
|
||||
|
||||
def declaration
|
||||
opes = @insn.opes
|
||||
if opes.empty?
|
||||
if @ope_decls.empty?
|
||||
argv = "void"
|
||||
else
|
||||
argv = opes.map {|o| o[:decl] }.join(', ')
|
||||
argv = @ope_decls.join(', ')
|
||||
end
|
||||
sprintf '%s %s(%s)', @type, name, argv
|
||||
end
|
||||
|
||||
def definition
|
||||
opes = @insn.opes
|
||||
if opes.empty?
|
||||
if @ope_decls.empty?
|
||||
argv = "void"
|
||||
else
|
||||
argv = opes.map {|o| "MAYBE_UNUSED(#{o[:decl]})" }.join(",\n ")
|
||||
argv = "\n #{argv}\n" if opes.size > 1
|
||||
argv = @ope_decls.map {|decl| "MAYBE_UNUSED(#{decl})" }.join(",\n ")
|
||||
argv = "\n #{argv}\n" if @ope_decls.size > 1
|
||||
end
|
||||
sprintf "%s\n%s(%s)", @type, name, argv
|
||||
end
|
||||
|
|
|
@ -33,6 +33,7 @@ class RubyVM::BareInstructions
|
|||
h[a.key] = a
|
||||
}
|
||||
@attrs_orig = @attrs.dup
|
||||
check_attribute_consistency
|
||||
predefine_attributes
|
||||
end
|
||||
|
||||
|
@ -139,8 +140,28 @@ class RubyVM::BareInstructions
|
|||
return @pops.any? {|i| i[:name] == var[:name] }
|
||||
end
|
||||
|
||||
def use_call_data?
|
||||
@use_call_data ||=
|
||||
@variables.find { |_, var_info| var_info[:type] == 'CALL_DATA' }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_attribute_consistency
|
||||
if has_attribute?('sp_inc') \
|
||||
&& use_call_data? \
|
||||
&& !has_attribute?('comptime_sp_inc')
|
||||
# As the call cache caches information that can only be obtained at
|
||||
# runtime, we do not need it when compiling from AST to bytecode. This
|
||||
# attribute defines an expression that computes the stack pointer
|
||||
# increase based on just the call info to avoid reserving space for the
|
||||
# call cache at compile time. In the expression, all call data operands
|
||||
# are mapped to their call info counterpart. Additionally, all mentions
|
||||
# of `cd` in the operand name are replaced with `ci`.
|
||||
raise "Please define attribute `comptime_sp_inc` for `#{@name}`"
|
||||
end
|
||||
end
|
||||
|
||||
def generate_attribute t, k, v
|
||||
@attrs[k] ||= RubyVM::Attribute.new \
|
||||
insn: self, \
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
|
||||
RubyVM::Typemap = {
|
||||
"..." => %w[. TS_VARIABLE],
|
||||
"CALL_CACHE" => %w[E TS_CALLCACHE],
|
||||
"CALL_INFO" => %w[C TS_CALLINFO],
|
||||
"CALL_DATA" => %w[C TS_CALLDATA],
|
||||
"CDHASH" => %w[H TS_CDHASH],
|
||||
"GENTRY" => %w[G TS_GENTRY],
|
||||
"IC" => %w[K TS_IC],
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
%# conditions mentioned in the file COPYING are met. Consult the file for
|
||||
%# details.
|
||||
%#
|
||||
PUREFUNC(MAYBE_UNUSED(static int insn_stack_increase(int depth, int insn, const VALUE *opes)));
|
||||
PUREFUNC(static rb_snum_t insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes));
|
||||
PUREFUNC(MAYBE_UNUSED(static int comptime_insn_stack_increase(int depth, int insn, const VALUE *opes)));
|
||||
PUREFUNC(static rb_snum_t comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes));
|
||||
|
||||
rb_snum_t
|
||||
insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
|
||||
comptime_insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
|
||||
{
|
||||
static const signed char t[] = {
|
||||
% RubyVM::Instructions.each_slice 8 do |a|
|
||||
|
@ -34,9 +34,19 @@ insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
|
|||
UNREACHABLE;
|
||||
% RubyVM::Instructions.each do |i|
|
||||
% next unless i.has_attribute?('sp_inc')
|
||||
% attr_function =
|
||||
% if i.has_attribute?('comptime_sp_inc')
|
||||
% "attr_comptime_sp_inc_#{i.name}"
|
||||
% else
|
||||
% "attr_sp_inc_#{i.name}"
|
||||
% end
|
||||
case <%= i.bin %>:
|
||||
return attr_sp_inc_<%= i.name %>(<%=
|
||||
return <%= attr_function %>(<%=
|
||||
i.opes.map.with_index do |v, j|
|
||||
if v[:type] == 'CALL_DATA' && i.has_attribute?('comptime_sp_inc')
|
||||
v = v.dup
|
||||
v[:type] = 'CALL_INFO'
|
||||
end
|
||||
i.cast_from_VALUE v, "opes[#{j}]"
|
||||
end.join(", ")
|
||||
%>);
|
||||
|
@ -45,8 +55,8 @@ insn_stack_increase_dispatch(enum ruby_vminsn_type insn, const VALUE *opes)
|
|||
}
|
||||
|
||||
int
|
||||
insn_stack_increase(int depth, int insn, const VALUE *opes)
|
||||
comptime_insn_stack_increase(int depth, int insn, const VALUE *opes)
|
||||
{
|
||||
enum ruby_vminsn_type itype = (enum ruby_vminsn_type)insn;
|
||||
return depth + (int)insn_stack_increase_dispatch(itype, opes);
|
||||
return depth + (int)comptime_insn_stack_increase_dispatch(itype, opes);
|
||||
}
|
|
@ -34,8 +34,8 @@
|
|||
% case ope.fetch(:type)
|
||||
% when 'ID'
|
||||
comment_id(f, (ID)operands[<%= i %>]);
|
||||
% when 'CALL_INFO'
|
||||
comment_id(f, ((CALL_INFO)operands[<%= i %>])->mid);
|
||||
% when 'CALL_DATA'
|
||||
comment_id(f, ((CALL_DATA)operands[<%= i %>])->ci.mid);
|
||||
% when 'VALUE'
|
||||
if (SYMBOL_P((VALUE)operands[<%= i %>])) comment_id(f, SYM2ID((VALUE)operands[<%= i %>]));
|
||||
% end
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>];
|
||||
% end
|
||||
% # compiler: Use copied cc to avoid race condition
|
||||
CALL_CACHE cc_copy = status->cc_entries + (cc - body->cc_entries);
|
||||
CALL_CACHE cc_copy = status->cc_entries + call_data_index(cd, body);
|
||||
%
|
||||
if (!status->compile_info->disable_send_cache && has_valid_method_type(cc_copy)) {
|
||||
const rb_iseq_t *iseq;
|
||||
const CALL_INFO ci = &cd->ci;
|
||||
unsigned int argc = ci->orig_argc; // this `argc` variable is for calculating a value's position on stack considering `blockarg`.
|
||||
% if insn.name == 'send'
|
||||
argc += ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0); // simulate `vm_caller_setup_arg_block`'s `--reg_cfp->sp`
|
||||
|
@ -58,7 +59,7 @@
|
|||
fprintf(f, " {\n");
|
||||
fprintf(f, " struct rb_calling_info calling;\n");
|
||||
% if insn.name == 'send'
|
||||
fprintf(f, " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (CALL_INFO)0x%"PRIxVALUE", (rb_iseq_t *)0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]);
|
||||
fprintf(f, " calling.block_handler = vm_caller_setup_arg_block(ec, reg_cfp, (CALL_INFO)0x%"PRIxVALUE", (rb_iseq_t *)0x%"PRIxVALUE", FALSE);\n", (VALUE)ci, (VALUE)blockiseq);
|
||||
% else
|
||||
fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n");
|
||||
% end
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
<%= render 'leaf_helpers' %>
|
||||
<%= render 'sp_inc_helpers' %>
|
||||
<%= render 'attributes' %>
|
||||
<%= render 'insn_stack_increase' %>
|
||||
<%= render 'comptime_insn_stack_increase' %>
|
||||
<%= render 'insn_sp_pc_dependency' %>
|
||||
|
|
|
@ -56,8 +56,8 @@ switch (insn) {
|
|||
<%= render 'mjit_compile_send', locals: { insn: insn } -%>
|
||||
% when *send_compatible_opt_insns
|
||||
% # To avoid cancel, just emit `opt_send_without_block` instead of `opt_*` insn if call cache is populated.
|
||||
% cc_index = insn.opes.index { |o| o.fetch(:type) == 'CALL_CACHE' }
|
||||
if (has_valid_method_type(status->cc_entries + ((CALL_CACHE)operands[<%= cc_index %>] - body->cc_entries))) {
|
||||
% cd_index = insn.opes.index { |o| o.fetch(:type) == 'CALL_DATA' }
|
||||
if (has_valid_method_type(status->cc_entries + call_data_index((CALL_DATA)operands[<%= cd_index %>], body))) {
|
||||
<%= render 'mjit_compile_send', locals: { insn: opt_send_without_block } -%>
|
||||
<%= render 'mjit_compile_insn', locals: { insn: opt_send_without_block } -%>
|
||||
break;
|
||||
|
|
23
vm_core.h
23
vm_core.h
|
@ -258,6 +258,16 @@ struct rb_calling_info {
|
|||
int kw_splat;
|
||||
};
|
||||
|
||||
struct rb_call_data {
|
||||
struct rb_call_cache cc;
|
||||
struct rb_call_info ci;
|
||||
};
|
||||
|
||||
struct rb_kwarg_call_data {
|
||||
struct rb_call_cache cc;
|
||||
struct rb_call_info_with_kwarg ci_kw;
|
||||
};
|
||||
|
||||
struct rb_execution_context_struct;
|
||||
typedef VALUE (*vm_call_handler)(struct rb_execution_context_struct *ec, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
|
||||
|
@ -417,12 +427,12 @@ struct rb_iseq_constant_body {
|
|||
struct rb_iseq_struct *local_iseq; /* local_iseq->flip_cnt can be modified */
|
||||
|
||||
union iseq_inline_storage_entry *is_entries;
|
||||
struct rb_call_info *ci_entries; /* struct rb_call_info ci_entries[ci_size];
|
||||
* struct rb_call_info_with_kwarg cikw_entries[ci_kw_size];
|
||||
* So that:
|
||||
* struct rb_call_info_with_kwarg *cikw_entries = &body->ci_entries[ci_size];
|
||||
*/
|
||||
struct rb_call_cache *cc_entries; /* size is ci_size + ci_kw_size */
|
||||
struct rb_call_data *call_data; /* A buffer for two arrays:
|
||||
* struct rb_call_data calls[ci_size];
|
||||
* struct rb_kwarg_call_data kw_calls[ci_kw_size];
|
||||
* Such that:
|
||||
* struct rb_kwarg_call_data *kw_calls = &body->call_data[ci_size];
|
||||
*/
|
||||
|
||||
struct {
|
||||
rb_snum_t flip_count;
|
||||
|
@ -1121,6 +1131,7 @@ typedef struct iseq_inline_cache_entry *IC;
|
|||
typedef union iseq_inline_storage_entry *ISE;
|
||||
typedef struct rb_call_info *CALL_INFO;
|
||||
typedef struct rb_call_cache *CALL_CACHE;
|
||||
typedef struct rb_call_data *CALL_DATA;
|
||||
|
||||
void rb_vm_change_state(void);
|
||||
|
||||
|
|
|
@ -1463,17 +1463,16 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)())
|
|||
}
|
||||
|
||||
static inline int
|
||||
vm_method_cfunc_is(CALL_INFO ci, CALL_CACHE cc,
|
||||
VALUE recv, VALUE (*func)())
|
||||
vm_method_cfunc_is(CALL_DATA cd, VALUE recv, VALUE (*func)())
|
||||
{
|
||||
vm_search_method(ci, cc, recv);
|
||||
return check_cfunc(cc->me, func);
|
||||
vm_search_method(&cd->ci, &cd->cc, recv);
|
||||
return check_cfunc(cd->cc.me, func);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
opt_equal_fallback(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
||||
opt_equal_fallback(VALUE recv, VALUE obj, CALL_DATA cd)
|
||||
{
|
||||
if (vm_method_cfunc_is(ci, cc, recv, rb_obj_equal)) {
|
||||
if (vm_method_cfunc_is(cd, recv, rb_obj_equal)) {
|
||||
return recv == obj ? Qtrue : Qfalse;
|
||||
}
|
||||
|
||||
|
@ -1533,7 +1532,7 @@ static
|
|||
inline
|
||||
#endif
|
||||
VALUE
|
||||
opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
||||
opt_eq_func(VALUE recv, VALUE obj, CALL_DATA cd)
|
||||
{
|
||||
switch (comparable_by_identity(recv, obj)) {
|
||||
case 1:
|
||||
|
@ -1556,7 +1555,7 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
|||
}
|
||||
|
||||
fallback:
|
||||
return opt_equal_fallback(recv, obj, ci, cc);
|
||||
return opt_equal_fallback(recv, obj, cd);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -1564,7 +1563,7 @@ static
|
|||
inline
|
||||
#endif
|
||||
VALUE
|
||||
opt_eql_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
||||
opt_eql_func(VALUE recv, VALUE obj, CALL_DATA cd)
|
||||
{
|
||||
switch (comparable_by_identity(recv, obj)) {
|
||||
case 1:
|
||||
|
@ -1586,7 +1585,7 @@ opt_eql_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
|||
}
|
||||
|
||||
fallback:
|
||||
return opt_equal_fallback(recv, obj, ci, cc);
|
||||
return opt_equal_fallback(recv, obj, cd);
|
||||
}
|
||||
#undef BUILTIN_CLASS_P
|
||||
#undef EQ_UNREDEFINED_P
|
||||
|
@ -1594,27 +1593,25 @@ opt_eql_func(VALUE recv, VALUE obj, CALL_INFO ci, CALL_CACHE cc)
|
|||
VALUE
|
||||
rb_equal_opt(VALUE obj1, VALUE obj2)
|
||||
{
|
||||
struct rb_call_info ci;
|
||||
struct rb_call_cache cc;
|
||||
struct rb_call_data cd;
|
||||
|
||||
ci.mid = idEq;
|
||||
cc.method_state = 0;
|
||||
cc.class_serial = 0;
|
||||
cc.me = NULL;
|
||||
return opt_eq_func(obj1, obj2, &ci, &cc);
|
||||
cd.ci.mid = idEq;
|
||||
cd.cc.method_state = 0;
|
||||
cd.cc.class_serial = 0;
|
||||
cd.cc.me = NULL;
|
||||
return opt_eq_func(obj1, obj2, &cd);
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_eql_opt(VALUE obj1, VALUE obj2)
|
||||
{
|
||||
struct rb_call_info ci;
|
||||
struct rb_call_cache cc;
|
||||
struct rb_call_data cd;
|
||||
|
||||
ci.mid = idEqlP;
|
||||
cc.method_state = 0;
|
||||
cc.class_serial = 0;
|
||||
cc.me = NULL;
|
||||
return opt_eql_func(obj1, obj2, &ci, &cc);
|
||||
cd.ci.mid = idEqlP;
|
||||
cd.cc.method_state = 0;
|
||||
cd.cc.class_serial = 0;
|
||||
cd.cc.me = NULL;
|
||||
return opt_eql_func(obj1, obj2, &cd);
|
||||
}
|
||||
|
||||
extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat);
|
||||
|
@ -3752,8 +3749,7 @@ static VALUE
|
|||
vm_sendish(
|
||||
struct rb_execution_context_struct *ec,
|
||||
struct rb_control_frame_struct *reg_cfp,
|
||||
struct rb_call_info *ci,
|
||||
struct rb_call_cache *cc,
|
||||
struct rb_call_data *cd,
|
||||
VALUE block_handler,
|
||||
void (*method_explorer)(
|
||||
const struct rb_control_frame_struct *reg_cfp,
|
||||
|
@ -3761,6 +3757,8 @@ vm_sendish(
|
|||
struct rb_call_cache *cc,
|
||||
VALUE recv))
|
||||
{
|
||||
CALL_INFO ci = &cd->ci;
|
||||
CALL_CACHE cc = &cd->cc;
|
||||
VALUE val;
|
||||
int argc = ci->orig_argc;
|
||||
VALUE recv = TOPN(argc);
|
||||
|
@ -4116,12 +4114,10 @@ vm_opt_mod(VALUE recv, VALUE obj)
|
|||
}
|
||||
|
||||
static VALUE
|
||||
vm_opt_neq(CALL_INFO ci, CALL_CACHE cc,
|
||||
CALL_INFO ci_eq, CALL_CACHE cc_eq,
|
||||
VALUE recv, VALUE obj)
|
||||
vm_opt_neq(CALL_DATA cd, CALL_DATA cd_eq, VALUE recv, VALUE obj)
|
||||
{
|
||||
if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not_equal)) {
|
||||
VALUE val = opt_eq_func(recv, obj, ci_eq, cc_eq);
|
||||
if (vm_method_cfunc_is(cd, recv, rb_obj_not_equal)) {
|
||||
VALUE val = opt_eq_func(recv, obj, cd_eq);
|
||||
|
||||
if (val != Qundef) {
|
||||
return RTEST(val) ? Qfalse : Qtrue;
|
||||
|
@ -4392,7 +4388,7 @@ vm_opt_empty_p(VALUE recv)
|
|||
VALUE rb_false(VALUE obj);
|
||||
|
||||
static VALUE
|
||||
vm_opt_nil_p(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
|
||||
vm_opt_nil_p(CALL_DATA cd, VALUE recv)
|
||||
{
|
||||
if (recv == Qnil) {
|
||||
if (BASIC_OP_UNREDEFINED_P(BOP_NIL_P, NIL_REDEFINED_OP_FLAG)) {
|
||||
|
@ -4403,7 +4399,7 @@ vm_opt_nil_p(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
|
|||
}
|
||||
}
|
||||
else {
|
||||
if (vm_method_cfunc_is(ci, cc, recv, rb_false)) {
|
||||
if (vm_method_cfunc_is(cd, recv, rb_false)) {
|
||||
return Qfalse;
|
||||
}
|
||||
else {
|
||||
|
@ -4460,9 +4456,9 @@ vm_opt_succ(VALUE recv)
|
|||
}
|
||||
|
||||
static VALUE
|
||||
vm_opt_not(CALL_INFO ci, CALL_CACHE cc, VALUE recv)
|
||||
vm_opt_not(CALL_DATA cd, VALUE recv)
|
||||
{
|
||||
if (vm_method_cfunc_is(ci, cc, recv, rb_obj_not)) {
|
||||
if (vm_method_cfunc_is(cd, recv, rb_obj_not)) {
|
||||
return RTEST(recv) ? Qfalse : Qtrue;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -166,7 +166,7 @@ enum vm_regan_acttype {
|
|||
#ifndef MJIT_HEADER
|
||||
#define CALL_SIMPLE_METHOD() do { \
|
||||
rb_snum_t x = leaf ? INSN_ATTR(width) : 0; \
|
||||
rb_snum_t y = attr_width_opt_send_without_block(0, 0); \
|
||||
rb_snum_t y = attr_width_opt_send_without_block(0); \
|
||||
rb_snum_t z = x - y; \
|
||||
ADD_PC(z); \
|
||||
DISPATCH_ORIGINAL_INSN(opt_send_without_block); \
|
||||
|
|
Загрузка…
Ссылка в новой задаче