aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compile.c77
-rw-r--r--gc.c14
-rw-r--r--iseq.c4
-rw-r--r--iseq.h1
-rw-r--r--method.h6
-rw-r--r--tool/mk_builtin_loader.rb5
-rw-r--r--vm_callinfo.h19
-rw-r--r--vm_core.h2
-rw-r--r--vm_eval.c2
-rw-r--r--vm_insnhelper.c19
-rw-r--r--vm_method.c44
11 files changed, 177 insertions, 16 deletions
diff --git a/compile.c b/compile.c
index 9d3b91beb7..9f31bcfd1b 100644
--- a/compile.c
+++ b/compile.c
@@ -791,8 +791,10 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
}
case ISEQ_TYPE_METHOD:
{
+ ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
ADD_TRACE(ret, RUBY_EVENT_CALL);
CHECK(COMPILE(ret, "scoped node", node->nd_body));
+ ISEQ_COMPILE_DATA(iseq)->root_node = node->nd_body;
ADD_TRACE(ret, RUBY_EVENT_RETURN);
ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
break;
@@ -7884,6 +7886,65 @@ compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, c
UNKNOWN_NODE("arg!", node, COMPILE_NG);
}
+static NODE *
+mandatory_node(const rb_iseq_t *iseq, const NODE *cond_node)
+{
+ const NODE *node = ISEQ_COMPILE_DATA(iseq)->root_node;
+ if (nd_type(node) == NODE_IF && node->nd_cond == cond_node) {
+ return node->nd_body;
+ }
+ else {
+ rb_bug("mandatory_node: can't find mandatory node");
+ }
+}
+
+static int
+compile_builtin_mandatory_only_method(rb_iseq_t *iseq, const NODE *node, const NODE *line_node)
+{
+ // argumens
+ struct rb_args_info args = {
+ .pre_args_num = iseq->body->param.lead_num,
+ };
+ NODE args_node;
+ rb_node_init(&args_node, NODE_ARGS, 0, 0, (VALUE)&args);
+
+ // local table without non-mandatory parameters
+ const int skip_local_size = iseq->body->param.size - iseq->body->param.lead_num;
+ const int table_size = iseq->body->local_table_size - skip_local_size;
+ ID *tbl = ALLOCA_N(ID, table_size + 1);
+ tbl[0] = table_size;
+ int i;
+
+ // lead parameters
+ for (i=0; i<iseq->body->param.lead_num; i++) {
+ tbl[i+1] = iseq->body->local_table[i];
+ }
+ // local variables
+ for (; i<table_size; i++) {
+ tbl[i+1] = iseq->body->local_table[i + skip_local_size];
+ }
+
+ NODE scope_node;
+ rb_node_init(&scope_node, NODE_SCOPE, (VALUE)tbl, (VALUE)mandatory_node(iseq, node), (VALUE)&args_node);
+
+ rb_ast_body_t ast = {
+ .root = &scope_node,
+ .compile_option = 0,
+ .script_lines = iseq->body->variable.script_lines,
+ };
+
+ int prev_inline_index = GET_VM()->builtin_inline_index;
+
+ iseq->body->mandatory_only_iseq =
+ rb_iseq_new_with_opt(&ast, rb_iseq_base_label(iseq),
+ rb_iseq_path(iseq), rb_iseq_realpath(iseq),
+ INT2FIX(nd_line(line_node)), NULL, 0,
+ ISEQ_TYPE_METHOD, ISEQ_COMPILE_DATA(iseq)->option);
+
+ GET_VM()->builtin_inline_index = prev_inline_index;
+ return COMPILE_OK;
+}
+
static int
compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, const NODE *line_node, int popped,
const rb_iseq_t *parent_block, LINK_ANCHOR *args, const char *builtin_func)
@@ -7922,6 +7983,18 @@ compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NOD
else if (strcmp("arg!", builtin_func) == 0) {
return compile_builtin_arg(iseq, ret, args_node, line_node, popped);
}
+ else if (strcmp("mandatory_only?", builtin_func) == 0) {
+ if (popped) {
+ rb_bug("mandatory_only? should be in if condition");
+ }
+ else if (!LIST_INSN_SIZE_ZERO(ret)) {
+ rb_bug("mandatory_only? should be put on top");
+ }
+
+ ADD_INSN1(ret, line_node, putobject, Qfalse);
+ return compile_builtin_mandatory_only_method(iseq, node, line_node);
+ return COMPILE_OK;
+ }
else if (1) {
rb_bug("can't find builtin function:%s", builtin_func);
}
@@ -11628,6 +11701,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
const ibf_offset_t catch_table_offset = ibf_dump_catch_table(dump, iseq);
const int parent_iseq_index = ibf_dump_iseq(dump, iseq->body->parent_iseq);
const int local_iseq_index = ibf_dump_iseq(dump, iseq->body->local_iseq);
+ const int mandatory_only_iseq_index = ibf_dump_iseq(dump, iseq->body->mandatory_only_iseq);
const ibf_offset_t ci_entries_offset = ibf_dump_ci_entries(dump, iseq);
const ibf_offset_t outer_variables_offset = ibf_dump_outer_variables(dump, iseq);
@@ -11690,6 +11764,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(catch_table_offset));
ibf_dump_write_small_value(dump, parent_iseq_index);
ibf_dump_write_small_value(dump, local_iseq_index);
+ ibf_dump_write_small_value(dump, mandatory_only_iseq_index);
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(ci_entries_offset));
ibf_dump_write_small_value(dump, IBF_BODY_OFFSET(outer_variables_offset));
ibf_dump_write_small_value(dump, body->variable.flip_count);
@@ -11797,6 +11872,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
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);
const int local_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
+ const int mandatory_only_iseq_index = (int)ibf_load_small_value(load, &reading_pos);
const ibf_offset_t ci_entries_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
const ibf_offset_t outer_variables_offset = (ibf_offset_t)IBF_BODY_OFFSET(ibf_load_small_value(load, &reading_pos));
const rb_snum_t variable_flip_count = (rb_snum_t)ibf_load_small_value(load, &reading_pos);
@@ -11859,6 +11935,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
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);
ibf_load_code(load, iseq, bytecode_offset, bytecode_size, iseq_size);
#if VM_INSN_INFO_TABLE_IMPL == 2
diff --git a/gc.c b/gc.c
index 2c64460541..670fb4afd3 100644
--- a/gc.c
+++ b/gc.c
@@ -2960,7 +2960,7 @@ cc_table_mark_i(ID id, VALUE ccs_ptr, void *data_ptr)
for (int i=0; i<ccs->len; i++) {
VM_ASSERT(data->klass == ccs->entries[i].cc->klass);
- VM_ASSERT(ccs->cme == vm_cc_cme(ccs->entries[i].cc));
+ VM_ASSERT(vm_cc_check_cme(ccs->entries[i].cc, ccs->cme));
gc_mark(data->objspace, (VALUE)ccs->entries[i].ci);
gc_mark(data->objspace, (VALUE)ccs->entries[i].cc);
@@ -6443,10 +6443,11 @@ mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me)
if (def) {
switch (def->type) {
- case VM_METHOD_TYPE_ISEQ:
- if (def->body.iseq.iseqptr) gc_mark(objspace, (VALUE)def->body.iseq.iseqptr);
- gc_mark(objspace, (VALUE)def->body.iseq.cref);
- break;
+ case VM_METHOD_TYPE_ISEQ:
+ if (def->body.iseq.iseqptr) gc_mark(objspace, (VALUE)def->body.iseq.iseqptr);
+ gc_mark(objspace, (VALUE)def->body.iseq.cref);
+ if (def->body.iseq.mandatory_only_cme) gc_mark(objspace, (VALUE)def->body.iseq.mandatory_only_cme);
+ break;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
gc_mark(objspace, def->body.attr.location);
@@ -9612,6 +9613,9 @@ gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me)
TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, def->body.iseq.iseqptr);
}
TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, def->body.iseq.cref);
+ if (def->body.iseq.mandatory_only_cme) {
+ TYPED_UPDATE_IF_MOVED(objspace, rb_callable_method_entry_t *, def->body.iseq.mandatory_only_cme);
+ }
break;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
diff --git a/iseq.c b/iseq.c
index a94f1e8d1f..081c746ca6 100644
--- a/iseq.c
+++ b/iseq.c
@@ -272,6 +272,9 @@ rb_iseq_update_references(rb_iseq_t *iseq)
if (body->parent_iseq) {
body->parent_iseq = (struct rb_iseq_struct *)rb_gc_location((VALUE)body->parent_iseq);
}
+ if (body->mandatory_only_iseq) {
+ body->mandatory_only_iseq = (struct rb_iseq_struct *)rb_gc_location((VALUE)body->mandatory_only_iseq);
+ }
if (body->call_data) {
for (unsigned int i=0; i<body->ci_size; i++) {
struct rb_call_data *cds = body->call_data;
@@ -351,6 +354,7 @@ rb_iseq_mark(const rb_iseq_t *iseq)
rb_gc_mark_movable(body->location.label);
rb_gc_mark_movable(body->location.base_label);
rb_gc_mark_movable(body->location.pathobj);
+ RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)body->mandatory_only_iseq);
RUBY_MARK_MOVABLE_UNLESS_NULL((VALUE)body->parent_iseq);
if (body->call_data) {
diff --git a/iseq.h b/iseq.h
index 8387f90e9a..eb46367379 100644
--- a/iseq.h
+++ b/iseq.h
@@ -117,6 +117,7 @@ struct iseq_compile_data {
const rb_compile_option_t *option;
struct rb_id_table *ivar_cache_table;
const struct rb_builtin_function *builtin_function_table;
+ const NODE *root_node;
#if OPT_SUPPORT_JOKE
st_table *labels_table;
#endif
diff --git a/method.h b/method.h
index 5b6fe2d800..a5fa187df1 100644
--- a/method.h
+++ b/method.h
@@ -132,8 +132,9 @@ typedef struct rb_iseq_struct rb_iseq_t;
#endif
typedef struct rb_method_iseq_struct {
- rb_iseq_t * iseqptr; /*!< iseq pointer, should be separated from iseqval */
+ const rb_iseq_t * iseqptr; /*!< iseq pointer, should be separated from iseqval */
rb_cref_t * cref; /*!< class reference, should be marked */
+ const rb_callable_method_entry_t *mandatory_only_cme;
} rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */
typedef struct rb_method_cfunc_struct {
@@ -171,7 +172,8 @@ enum method_optimized_type {
struct rb_method_definition_struct {
BITFIELD(rb_method_type_t, type, VM_METHOD_TYPE_MINIMUM_BITS);
- int alias_count : 28;
+ unsigned int iseq_overload: 1;
+ int alias_count : 27;
int complemented_count : 28;
union {
diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
index 6740ec5c17..02941735f7 100644
--- a/tool/mk_builtin_loader.rb
+++ b/tool/mk_builtin_loader.rb
@@ -100,6 +100,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil
when :call, :command_call # CALL
_, recv, sep, mid, (_, args) = tree
end
+
if mid
raise "unknown sexp: #{mid.inspect}" unless %i[@ident @const].include?(mid.first)
_, mid, (lineno,) = mid
@@ -126,7 +127,7 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil
args.pop unless (args ||= []).last
argc = args.size
- if /(.+)\!\z/ =~ func_name
+ if /(.+)[\!\?]\z/ =~ func_name
case $1
when 'attr'
text = inline_text(argc, args.first)
@@ -156,6 +157,8 @@ def collect_builtin base, tree, name, bs, inlines, locals = nil
func_name = nil # required
inlines[inlines.size] = [lineno, text, nil, nil]
argc -= 1
+ when 'mandatory_only'
+ func_name = nil
when 'arg'
argc == 1 or raise "unexpected argument number #{argc}"
(arg = args.first)[0] == :symbol_literal or raise "symbol literal expected #{args}"
diff --git a/vm_callinfo.h b/vm_callinfo.h
index d5f4388fa8..4f3518c066 100644
--- a/vm_callinfo.h
+++ b/vm_callinfo.h
@@ -446,6 +446,25 @@ vm_ccs_p(const struct rb_class_cc_entries *ccs)
{
return ccs->debug_sig == ~(VALUE)ccs;
}
+
+static inline bool
+vm_cc_check_cme(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme)
+{
+ if (vm_cc_cme(cc) == cme ||
+ (cme->def->iseq_overload && vm_cc_cme(cc) == cme->def->body.iseq.mandatory_only_cme)) {
+ return true;
+ }
+ else {
+#if 1
+ fprintf(stderr, "iseq_overload:%d mandatory_only_cme:%p eq:%d\n",
+ (int)cme->def->iseq_overload,
+ (void *)cme->def->body.iseq.mandatory_only_cme,
+ vm_cc_cme(cc) == cme->def->body.iseq.mandatory_only_cme);
+#endif
+ return false;
+ }
+}
+
#endif
// gc.c
diff --git a/vm_core.h b/vm_core.h
index 2e022a6dd0..bcb0255a00 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -482,6 +482,8 @@ struct rb_iseq_constant_body {
bool builtin_inline_p;
struct rb_id_table *outer_variables;
+ const rb_iseq_t *mandatory_only_iseq;
+
#if USE_MJIT
/* The following fields are MJIT related info. */
VALUE (*jit_func)(struct rb_execution_context_struct *,
diff --git a/vm_eval.c b/vm_eval.c
index fb018c7eeb..59b4ed4945 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -455,7 +455,7 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc)
if (LIKELY(!METHOD_ENTRY_INVALIDATED(cme) &&
cme->called_id == mid)) {
- VM_ASSERT(vm_cc_cme(cc) == rb_callable_method_entry(klass, mid));
+ VM_ASSERT(vm_cc_check_cme(cc, rb_callable_method_entry(klass, mid)));
RB_DEBUG_COUNTER_INC(gccct_hit);
return cc;
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index b9950f4fe2..8e0793e070 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -678,7 +678,7 @@ rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
}
-static rb_iseq_t *
+static const rb_iseq_t *
method_entry_iseqptr(const rb_callable_method_entry_t *me)
{
switch (me->def->type) {
@@ -1754,13 +1754,16 @@ vm_ccs_verify(struct rb_class_cc_entries *ccs, ID mid, VALUE klass)
VM_ASSERT(vm_ci_mid(ci) == mid);
VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache));
VM_ASSERT(vm_cc_class_check(cc, klass));
- VM_ASSERT(vm_cc_cme(cc) == ccs->cme);
+ VM_ASSERT(vm_cc_check_cme(cc, ccs->cme));
}
return TRUE;
}
#endif
#ifndef MJIT_HEADER
+
+static const rb_callable_method_entry_t *overloaded_cme(const rb_callable_method_entry_t *cme);
+
static const struct rb_callcache *
vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
{
@@ -1829,7 +1832,6 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
VM_ASSERT(cme == rb_callable_method_entry(klass, mid));
- const struct rb_callcache *cc = vm_cc_new(klass, cme, vm_call_general);
METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
if (ccs == NULL) {
@@ -1846,6 +1848,14 @@ vm_search_cc(const VALUE klass, const struct rb_callinfo * const ci)
}
}
+ if ((cme->def->iseq_overload &&
+ (int)vm_ci_argc(ci) == method_entry_iseqptr(cme)->body->param.lead_num)) {
+ // use alternative
+ cme = overloaded_cme(cme);
+ METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme);
+ // rp(cme);
+ }
+ const struct rb_callcache *cc = vm_cc_new(klass, cme, vm_call_general);
vm_ccs_push(klass, ccs, ci, cc);
VM_ASSERT(vm_cc_cme(cc) != NULL);
@@ -3529,9 +3539,10 @@ vm_call_method_each_type(rb_execution_context_t *ec, rb_control_frame_t *cfp, st
{
const struct rb_callinfo *ci = calling->ci;
const struct rb_callcache *cc = calling->cc;
+ const rb_callable_method_entry_t *cme = vm_cc_cme(cc);
VALUE v;
- switch (vm_cc_cme(cc)->def->type) {
+ switch (cme->def->type) {
case VM_METHOD_TYPE_ISEQ:
CC_SET_FASTPATH(cc, vm_call_iseq_setup, TRUE);
return vm_call_iseq_setup(ec, cfp, calling);
diff --git a/vm_method.c b/vm_method.c
index 75f38d386d..b41b0ff908 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -208,6 +208,10 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
vm_cme_invalidate((rb_callable_method_entry_t *)cme);
RB_DEBUG_COUNTER_INC(cc_invalidate_tree_cme);
+
+ if (cme->def->iseq_overload) {
+ vm_cme_invalidate((rb_callable_method_entry_t *)cme->def->body.iseq.mandatory_only_cme);
+ }
}
// invalidate complement tbl
@@ -451,10 +455,13 @@ rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *de
case VM_METHOD_TYPE_ISEQ:
{
rb_method_iseq_t *iseq_body = (rb_method_iseq_t *)opts;
+ const rb_iseq_t *iseq = iseq_body->iseqptr;
rb_cref_t *method_cref, *cref = iseq_body->cref;
/* setup iseq first (before invoking GC) */
- RB_OBJ_WRITE(me, &def->body.iseq.iseqptr, iseq_body->iseqptr);
+ RB_OBJ_WRITE(me, &def->body.iseq.iseqptr, iseq);
+
+ if (iseq->body->mandatory_only_iseq) def->iseq_overload = 1;
if (0) vm_cref_dump("rb_method_definition_create", cref);
@@ -531,7 +538,8 @@ method_definition_reset(const rb_method_entry_t *me)
case VM_METHOD_TYPE_ISEQ:
RB_OBJ_WRITTEN(me, Qundef, def->body.iseq.iseqptr);
RB_OBJ_WRITTEN(me, Qundef, def->body.iseq.cref);
- break;
+ RB_OBJ_WRITTEN(me, Qundef, def->body.iseq.mandatory_only_cme);
+ break;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
RB_OBJ_WRITTEN(me, Qundef, def->body.attr.location);
@@ -893,6 +901,35 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
return me;
}
+static const rb_callable_method_entry_t *
+overloaded_cme(const rb_callable_method_entry_t *cme)
+{
+ VM_ASSERT(cme->def->iseq_overload);
+ VM_ASSERT(cme->def->type == VM_METHOD_TYPE_ISEQ);
+ VM_ASSERT(cme->def->body.iseq.iseqptr != NULL);
+
+ const rb_callable_method_entry_t *monly_cme = cme->def->body.iseq.mandatory_only_cme;
+
+ if (monly_cme && !METHOD_ENTRY_INVALIDATED(monly_cme)) {
+ // ok
+ }
+ else {
+ rb_method_definition_t *def = rb_method_definition_create(VM_METHOD_TYPE_ISEQ, cme->def->original_id);
+ def->body.iseq.cref = cme->def->body.iseq.cref;
+ def->body.iseq.iseqptr = cme->def->body.iseq.iseqptr->body->mandatory_only_iseq;
+
+ rb_method_entry_t *me = rb_method_entry_alloc(cme->called_id,
+ cme->owner,
+ cme->defined_class,
+ def);
+ METHOD_ENTRY_VISI_SET(me, METHOD_ENTRY_VISI(cme));
+ RB_OBJ_WRITE(cme, &cme->def->body.iseq.mandatory_only_cme, me);
+ monly_cme = (rb_callable_method_entry_t *)me;
+ }
+
+ return monly_cme;
+}
+
#define CALL_METHOD_HOOK(klass, hook, mid) do { \
const VALUE arg = ID2SYM(mid); \
VALUE recv_class = (klass); \
@@ -932,6 +969,7 @@ rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref,
iseq_body.iseqptr = iseq;
iseq_body.cref = cref;
+
rb_add_method(klass, mid, VM_METHOD_TYPE_ISEQ, &iseq_body, visi);
}
@@ -1876,7 +1914,7 @@ rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_defini
switch (d1->type) {
case VM_METHOD_TYPE_ISEQ:
- return d1->body.iseq.iseqptr == d2->body.iseq.iseqptr;
+ return d1->body.iseq.iseqptr == d2->body.iseq.iseqptr;
case VM_METHOD_TYPE_CFUNC:
return
d1->body.cfunc.func == d2->body.cfunc.func &&