aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--builtin.c40
-rw-r--r--builtin.h52
-rw-r--r--common.mk56
-rw-r--r--compile.c256
-rw-r--r--inits.c2
-rw-r--r--insns.def48
-rw-r--r--iseq.c11
-rw-r--r--iseq.h2
-rw-r--r--mini_builtin.c89
-rw-r--r--miniinit.c2
-rw-r--r--mjit_compile.c1
-rw-r--r--tool/mk_builtin_binary.rb33
-rw-r--r--tool/mk_builtin_loader.rb76
-rw-r--r--tool/ruby_vm/models/typemap.rb1
-rw-r--r--vm.c2
-rw-r--r--vm_core.h9
-rw-r--r--vm_insnhelper.c186
17 files changed, 838 insertions, 28 deletions
diff --git a/builtin.c b/builtin.c
new file mode 100644
index 0000000000..44471f01e8
--- /dev/null
+++ b/builtin.c
@@ -0,0 +1,40 @@
+#include "internal.h"
+#include "vm_core.h"
+#include "iseq.h"
+#include "builtin.h"
+
+#include "builtin_binary.inc"
+
+static const unsigned char*
+builtin_lookup(const char *feature, size_t *psize)
+{
+ for (int i=0; i<BUILTIN_BINARY_SIZE; i++) {
+ if (strcmp(builtin_binary[i].feature, feature) == 0) {
+ *psize = builtin_binary[i].bin_size;
+ return builtin_binary[i].bin;
+ }
+ }
+ rb_bug("builtin_lookup: can not find %s\n", feature);
+}
+
+void
+rb_load_with_builtin_functions(const char *feature_name, const char *fname, const struct rb_builtin_function *table)
+{
+ // search binary
+ size_t size;
+ const unsigned char *bin = builtin_lookup(feature_name, &size);
+
+ // load binary
+ GET_VM()->builtin_function_table = table;
+ const rb_iseq_t *iseq = rb_iseq_ibf_load_cstr((const char *)bin, size);
+ GET_VM()->builtin_function_table = NULL;
+
+ // exec
+ rb_iseq_eval(iseq);
+}
+
+void
+Init_builtin(void)
+{
+ //
+}
diff --git a/builtin.h b/builtin.h
new file mode 100644
index 0000000000..ef6ff82236
--- /dev/null
+++ b/builtin.h
@@ -0,0 +1,52 @@
+// invoke
+
+struct rb_builtin_function {
+ // for invocation
+ const void * const func_ptr;
+ const int argc;
+
+ // for load
+ const int index;
+ const char * const name;
+};
+
+#define RB_BUILTIN_FUNCTION(_i, _name, _arity) { .name = #_name, .func_ptr = (void *)_name, .argc = _arity, .index = _i }
+
+void rb_load_with_builtin_functions(const char *feature_name, const char *fname, const struct rb_builtin_function *table);
+
+#ifndef VM_CORE_H_EC_DEFINED
+typedef struct rb_execution_context_struct rb_execution_context_t;
+#endif
+
+/* The following code is generated by the following Ruby script:
+
+16.times{|i|
+ args = (i > 0 ? ', ' : '') + (0...i).map{"VALUE"}.join(', ')
+ puts "static inline void rb_builtin_function_check_arity#{i}(VALUE (*f)(rb_execution_context_t *ec, VALUE self#{args})){}"
+}
+*/
+
+static inline void rb_builtin_function_check_arity0(VALUE (*f)(rb_execution_context_t *ec, VALUE self)){}
+static inline void rb_builtin_function_check_arity1(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE)){}
+static inline void rb_builtin_function_check_arity2(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity3(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity4(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity5(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity6(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity7(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity8(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity9(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity10(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity11(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity12(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity13(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity14(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+static inline void rb_builtin_function_check_arity15(VALUE (*f)(rb_execution_context_t *ec, VALUE self, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)){}
+
+// dump/load
+
+struct builtin_binary {
+ const char *feature; // feature name
+ const unsigned char *bin; // binary by ISeq#to_binary
+ size_t bin_size;
+};
diff --git a/common.mk b/common.mk
index 850e3d9760..42d3223e90 100644
--- a/common.mk
+++ b/common.mk
@@ -156,7 +156,7 @@ EXPORTOBJS = $(DLNOBJ) \
loadpath.$(OBJEXT) \
$(COMMONOBJS)
-OBJS = $(EXPORTOBJS) prelude.$(OBJEXT)
+OBJS = $(EXPORTOBJS) prelude.$(OBJEXT) builtin.$(OBJEXT)
ALLOBJS = $(NORMALMAINOBJ) $(MINIOBJS) $(COMMONOBJS) $(INITOBJS)
GOLFOBJS = goruby.$(OBJEXT) golf_prelude.$(OBJEXT)
@@ -1094,6 +1094,11 @@ preludes: {$(VPATH)}prelude.c
preludes: {$(VPATH)}miniprelude.c
preludes: {$(srcdir)}golf_prelude.c
+BUILTIN_RB_SRCS =
+
+builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/tool/mk_builtin_binary.rb
+ $(Q) $(MINIRUBY) $(srcdir)/tool/mk_builtin_binary.rb
+
$(srcdir)/revision.h:
$(Q)$(gnumake:yes=#) $(RM) $(@F)
$(Q)$(gnumake:yes=#) exit > $@ || exit > $(@F)
@@ -1613,6 +1618,33 @@ bignum.$(OBJEXT): {$(VPATH)}st.h
bignum.$(OBJEXT): {$(VPATH)}subst.h
bignum.$(OBJEXT): {$(VPATH)}thread.h
bignum.$(OBJEXT): {$(VPATH)}util.h
+builtin.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+builtin.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+builtin.$(OBJEXT): $(CCAN_DIR)/list/list.h
+builtin.$(OBJEXT): $(CCAN_DIR)/str/str.h
+builtin.$(OBJEXT): $(hdrdir)/ruby.h
+builtin.$(OBJEXT): $(hdrdir)/ruby/ruby.h
+builtin.$(OBJEXT): {$(VPATH)}assert.h
+builtin.$(OBJEXT): {$(VPATH)}builtin.c
+builtin.$(OBJEXT): {$(VPATH)}builtin.h
+builtin.$(OBJEXT): {$(VPATH)}builtin_binary.inc
+builtin.$(OBJEXT): {$(VPATH)}config.h
+builtin.$(OBJEXT): {$(VPATH)}defines.h
+builtin.$(OBJEXT): {$(VPATH)}id.h
+builtin.$(OBJEXT): {$(VPATH)}intern.h
+builtin.$(OBJEXT): {$(VPATH)}internal.h
+builtin.$(OBJEXT): {$(VPATH)}iseq.h
+builtin.$(OBJEXT): {$(VPATH)}method.h
+builtin.$(OBJEXT): {$(VPATH)}missing.h
+builtin.$(OBJEXT): {$(VPATH)}node.h
+builtin.$(OBJEXT): {$(VPATH)}ruby_assert.h
+builtin.$(OBJEXT): {$(VPATH)}ruby_atomic.h
+builtin.$(OBJEXT): {$(VPATH)}st.h
+builtin.$(OBJEXT): {$(VPATH)}subst.h
+builtin.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+builtin.$(OBJEXT): {$(VPATH)}thread_native.h
+builtin.$(OBJEXT): {$(VPATH)}vm_core.h
+builtin.$(OBJEXT): {$(VPATH)}vm_opts.h
class.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
class.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
class.$(OBJEXT): $(CCAN_DIR)/list/list.h
@@ -1663,6 +1695,7 @@ compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
compile.$(OBJEXT): $(hdrdir)/ruby.h
compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
compile.$(OBJEXT): {$(VPATH)}assert.h
+compile.$(OBJEXT): {$(VPATH)}builtin.h
compile.$(OBJEXT): {$(VPATH)}compile.c
compile.$(OBJEXT): {$(VPATH)}config.h
compile.$(OBJEXT): {$(VPATH)}defines.h
@@ -2208,6 +2241,7 @@ iseq.$(OBJEXT): $(CCAN_DIR)/str/str.h
iseq.$(OBJEXT): $(hdrdir)/ruby.h
iseq.$(OBJEXT): $(hdrdir)/ruby/ruby.h
iseq.$(OBJEXT): {$(VPATH)}assert.h
+iseq.$(OBJEXT): {$(VPATH)}builtin.h
iseq.$(OBJEXT): {$(VPATH)}config.h
iseq.$(OBJEXT): {$(VPATH)}debug_counter.h
iseq.$(OBJEXT): {$(VPATH)}defines.h
@@ -2350,18 +2384,36 @@ math.$(OBJEXT): {$(VPATH)}onigmo.h
math.$(OBJEXT): {$(VPATH)}oniguruma.h
math.$(OBJEXT): {$(VPATH)}st.h
math.$(OBJEXT): {$(VPATH)}subst.h
+miniinit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
+miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
+miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h
+miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h
+miniinit.$(OBJEXT): $(hdrdir)/ruby.h
miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
miniinit.$(OBJEXT): {$(VPATH)}assert.h
+miniinit.$(OBJEXT): {$(VPATH)}builtin.h
miniinit.$(OBJEXT): {$(VPATH)}config.h
miniinit.$(OBJEXT): {$(VPATH)}defines.h
miniinit.$(OBJEXT): {$(VPATH)}encoding.h
+miniinit.$(OBJEXT): {$(VPATH)}id.h
miniinit.$(OBJEXT): {$(VPATH)}intern.h
+miniinit.$(OBJEXT): {$(VPATH)}internal.h
+miniinit.$(OBJEXT): {$(VPATH)}iseq.h
+miniinit.$(OBJEXT): {$(VPATH)}method.h
+miniinit.$(OBJEXT): {$(VPATH)}mini_builtin.c
miniinit.$(OBJEXT): {$(VPATH)}miniinit.c
miniinit.$(OBJEXT): {$(VPATH)}missing.h
+miniinit.$(OBJEXT): {$(VPATH)}node.h
miniinit.$(OBJEXT): {$(VPATH)}onigmo.h
miniinit.$(OBJEXT): {$(VPATH)}oniguruma.h
+miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h
+miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
miniinit.$(OBJEXT): {$(VPATH)}st.h
miniinit.$(OBJEXT): {$(VPATH)}subst.h
+miniinit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
+miniinit.$(OBJEXT): {$(VPATH)}thread_native.h
+miniinit.$(OBJEXT): {$(VPATH)}vm_core.h
+miniinit.$(OBJEXT): {$(VPATH)}vm_opts.h
miniprelude.$(OBJEXT): {$(VPATH)}iseq.h
miniprelude.$(OBJEXT): {$(VPATH)}miniprelude.c
mjit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
@@ -2406,6 +2458,7 @@ mjit_compile.$(OBJEXT): $(CCAN_DIR)/str/str.h
mjit_compile.$(OBJEXT): $(hdrdir)/ruby.h
mjit_compile.$(OBJEXT): $(hdrdir)/ruby/ruby.h
mjit_compile.$(OBJEXT): {$(VPATH)}assert.h
+mjit_compile.$(OBJEXT): {$(VPATH)}builtin.h
mjit_compile.$(OBJEXT): {$(VPATH)}config.h
mjit_compile.$(OBJEXT): {$(VPATH)}debug_counter.h
mjit_compile.$(OBJEXT): {$(VPATH)}defines.h
@@ -3217,6 +3270,7 @@ vm.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm.$(OBJEXT): $(hdrdir)/ruby.h
vm.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm.$(OBJEXT): {$(VPATH)}assert.h
+vm.$(OBJEXT): {$(VPATH)}builtin.h
vm.$(OBJEXT): {$(VPATH)}config.h
vm.$(OBJEXT): {$(VPATH)}constant.h
vm.$(OBJEXT): {$(VPATH)}debug_counter.h
diff --git a/compile.c b/compile.c
index 0b90a360a0..e0a3c891d1 100644
--- a/compile.c
+++ b/compile.c
@@ -18,6 +18,7 @@
#include "vm_core.h"
#include "vm_debug.h"
+#include "builtin.h"
#include "iseq.h"
#include "insns.inc"
#include "insns_info.inc"
@@ -2237,6 +2238,9 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
case TS_FUNCPTR:
generated_iseq[code_index + 1 + j] = operands[j];
break;
+ case TS_BUILTIN:
+ generated_iseq[code_index + 1 + j] = operands[j];
+ break;
default:
BADINSN_ERROR(iseq, iobj->insn_info.line_no,
"unknown operand type: %c", type);
@@ -3214,6 +3218,14 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
}
}
+ if (IS_INSN_ID(iobj, opt_invokebuiltin_delegate)) {
+ if (IS_TRACE(iobj->link.next)) {
+ if (IS_NEXT_INSN_ID(iobj->link.next, leave)) {
+ iobj->insn_id = BIN(opt_invokebuiltin_delegate_leave);
+ }
+ }
+ }
+
return COMPILE_OK;
}
@@ -6718,6 +6730,77 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
}
static int
+iseq_has_builtin_function_table(const rb_iseq_t *iseq)
+{
+ return ISEQ_COMPILE_DATA(iseq)->builtin_function_table != NULL;
+}
+
+static const struct rb_builtin_function *
+iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
+{
+ int i;
+ const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
+ for (i=0; table[i].name != NULL; i++) {
+ // fprintf(stderr, "table[%d].name:%s, name:%s\n", i, table[i].name, name);
+ if (strcmp(table[i].name, name) == 0) {
+ return &table[i];
+ }
+ }
+ return NULL;
+}
+
+static const char *
+iseq_builtin_function_name(ID mid)
+{
+ const char *name = rb_id2name(mid);
+ const char prefix[] = "__builtin_";
+ const int prefix_len = strlen(prefix);
+
+ if (UNLIKELY(strncmp("__builtin_", name, prefix_len) == 0)) {
+ return &name[prefix_len];
+ }
+ else {
+ return NULL;
+ }
+}
+
+static int
+delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args)
+{
+ if (argc == 0) {
+ return TRUE;
+ }
+ else if (argc == iseq->body->param.size) {
+ const LINK_ELEMENT *elem = FIRST_ELEMENT(args);
+
+ for (unsigned int i=0; i<argc; i++) {
+ if (elem->type == ISEQ_ELEMENT_INSN &&
+ INSN_OF(elem) == BIN(getlocal)) {
+ int local_index = FIX2INT(OPERAND_AT(elem, 0));
+ int local_level = FIX2INT(OPERAND_AT(elem, 1));
+ if (local_level == 0) {
+ unsigned int index = iseq->body->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1);
+#if 0
+ ID param_id = iseq->body->local_table[i];
+ fprintf(stderr, "param_id:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n",
+ rb_id2name(param_id), i,
+ rb_id2name(iseq->body->local_table[index]), index,
+ local_index, (int)iseq->body->local_table_size);
+#endif
+ if (i == index) {
+ elem = elem->next;
+ continue; /* for */
+ }
+ }
+ }
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int type, int line, int popped)
{
/* call: obj.method(...)
@@ -6802,6 +6885,51 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
}
}
#endif
+ const char *builtin_func;
+ if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
+ (builtin_func = iseq_builtin_function_name(mid)) != NULL) {
+
+ if (parent_block != NULL) {
+ COMPILE_ERROR(iseq, line, "should not call builtins here.");
+ return COMPILE_NG;
+ }
+ else {
+ const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
+
+ if (bf == NULL) {
+ if (1) {
+ rb_bug("can't find builtin function:%s", builtin_func);
+ }
+ else {
+ COMPILE_ERROR(ERROR_ARGS "can't find builtin function:%s", builtin_func);
+ }
+ return COMPILE_NG;
+ }
+
+ // fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
+
+ argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+
+ if (FIX2INT(argc) != bf->argc) {
+ COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
+ builtin_func, bf->argc, FIX2INT(argc));
+ return COMPILE_NG;
+ }
+
+ if (delegate_call_p(iseq, FIX2INT(argc), args)) {
+ ADD_INSN1(ret, line, opt_invokebuiltin_delegate, bf);
+ }
+ else {
+ ADD_SEQ(ret, args);
+ ADD_INSN1(ret,line, invokebuiltin, bf);
+ }
+
+ if (popped) ADD_INSN(ret, line, pop);
+ return COMPILE_OK;
+ }
+ }
+
+
/* receiver */
if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
int idx, level;
@@ -8475,6 +8603,9 @@ insn_data_to_s_detail(INSN *iobj)
rb_str_catf(str, "<%p>", func);
}
break;
+ case TS_BUILTIN:
+ rb_bug("unsupported: TS_BUILTIN");
+ break;
default:{
rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
}
@@ -9395,6 +9526,14 @@ ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long of
memcpy(ptr + offset, buff, size);
}
+static const void *
+ibf_load_ptr(const struct ibf_load *load, ibf_offset_t *offset, int size)
+{
+ ibf_offset_t beg = *offset;
+ *offset += size;
+ return load->current_buffer->buff + beg;
+}
+
static void *
ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, size_t x, size_t y)
{
@@ -9603,6 +9742,42 @@ ibf_load_small_value(const struct ibf_load *load, ibf_offset_t *offset)
return x;
}
+static void
+ibf_dump_builtin(struct ibf_dump *dump, const struct rb_builtin_function *bf)
+{
+ // short: index
+ // short: name.length
+ // bytes: name
+ // // omit argc (only verify with name)
+ ibf_dump_write_small_value(dump, (VALUE)bf->index);
+
+ size_t len = strlen(bf->name);
+ ibf_dump_write_small_value(dump, (VALUE)len);
+ ibf_dump_write(dump, bf->name, len);
+}
+
+static const struct rb_builtin_function *
+ibf_load_builtin(const struct ibf_load *load, ibf_offset_t *offset)
+{
+ int i = (int)ibf_load_small_value(load, offset);
+ int len = (int)ibf_load_small_value(load, offset);
+ const char *name = (char *)ibf_load_ptr(load, offset, len);
+
+ if (0) {
+ for (int i=0; i<len; i++) fprintf(stderr, "%c", name[i]);
+ fprintf(stderr, "!!\n");
+ }
+
+ const struct rb_builtin_function *table = GET_VM()->builtin_function_table;
+ if (table == NULL) rb_bug(__func__);
+ if (strncmp(table[i].name, name, len) != 0) {
+ rb_bug("%s mistach", __func__);
+ }
+ // fprintf(stderr, "load-builtin: name:%s(%d)\n", table[i].name, table[i].argc);
+
+ return &table[i];
+}
+
static ibf_offset_t
ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
{
@@ -9625,16 +9800,15 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
/* operands */
for (op_index=0; types[op_index]; op_index++, code_index++) {
VALUE op = orig_code[code_index];
+ VALUE wv;
+
switch (types[op_index]) {
case TS_CDHASH:
case TS_VALUE:
- ibf_dump_write_small_value(dump, ibf_dump_object(dump, op));
+ wv = ibf_dump_object(dump, op);
break;
case TS_ISEQ:
- {
- VALUE index = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
- ibf_dump_write_small_value(dump, index);
- }
+ wv = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
break;
case TS_IC:
case TS_ISE:
@@ -9645,29 +9819,34 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
break;
}
}
- ibf_dump_write_small_value(dump, (VALUE)i);
+ wv = (VALUE)i;
}
break;
case TS_CALLDATA:
{
- 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);
+ char c = ibf_dump_calldata(dump, (const struct rb_call_data *)op) == Qtrue; // 1 or 0
+ ibf_dump_write_byte(dump, c);
+ goto skip_wv;
}
- break;
case TS_ID:
- ibf_dump_write_small_value(dump, ibf_dump_id(dump, (ID)op));
+ wv = ibf_dump_id(dump, (ID)op);
break;
case TS_GENTRY:
- ibf_dump_write_small_value(dump, ibf_dump_gentry(dump, (const struct rb_global_entry *)op));
+ wv = ibf_dump_gentry(dump, (const struct rb_global_entry *)op);
break;
case TS_FUNCPTR:
rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
- break;
+ goto skip_wv;
+ case TS_BUILTIN:
+ ibf_dump_builtin(dump, (const struct rb_builtin_function *)op);
+ goto skip_wv;
default:
- ibf_dump_write_small_value(dump, op);
+ wv = op;
break;
}
+ ibf_dump_write_small_value(dump, wv);
+ skip_wv:;
}
assert(insn_len(insn) == op_index+1);
}
@@ -9749,6 +9928,9 @@ ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, ibf_offset_t b
case TS_FUNCPTR:
rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
break;
+ case TS_BUILTIN:
+ code[code_index] = (VALUE)ibf_load_builtin(load, &reading_pos);
+ break;
default:
code[code_index] = ibf_load_small_value(load, &reading_pos);
continue;
@@ -11244,21 +11426,10 @@ ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
}
static void
-ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
+ibf_load_setup_cstr(struct ibf_load *load, VALUE loader_obj, const char *cstr, size_t size)
{
- rb_check_safe_obj(str);
-
- if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
- rb_raise(rb_eRuntimeError, "broken binary format");
- }
-
-#if USE_LAZY_LOAD
- str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
-#endif
-
- RB_OBJ_WRITE(loader_obj, &load->str, str);
load->loader_obj = loader_obj;
- load->global_buffer.buff = StringValuePtr(str);
+ load->global_buffer.buff = cstr;
load->header = (struct ibf_header *)load->global_buffer.buff;
load->global_buffer.size = load->header->size;
load->global_buffer.obj_list_offset = load->header->global_object_list_offset;
@@ -11270,7 +11441,7 @@ ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
load->current_buffer = &load->global_buffer;
- if (RSTRING_LENINT(str) < (int)load->header->size) {
+ if (size < load->header->size) {
rb_raise(rb_eRuntimeError, "broken binary format");
}
if (strncmp(load->header->magic, "YARB", 4) != 0) {
@@ -11295,6 +11466,23 @@ ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
}
static void
+ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
+{
+ rb_check_safe_obj(str);
+
+ if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
+ rb_raise(rb_eRuntimeError, "broken binary format");
+ }
+
+#if USE_LAZY_LOAD
+ str = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str));
+#endif
+
+ ibf_load_setup_cstr(load, loader_obj, StringValuePtr(str), RSTRING_LEN(str));
+ RB_OBJ_WRITE(loader_obj, &load->str, str);
+}
+
+static void
ibf_loader_mark(void *ptr)
{
struct ibf_load *load = (struct ibf_load *)ptr;
@@ -11336,6 +11524,20 @@ rb_iseq_ibf_load(VALUE str)
return iseq;
}
+const rb_iseq_t *
+rb_iseq_ibf_load_cstr(const char *cstr, size_t size)
+{
+ struct ibf_load *load;
+ rb_iseq_t *iseq;
+ VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
+
+ ibf_load_setup_cstr(load, loader_obj, cstr, size);
+ iseq = ibf_load_iseq(load, 0);
+
+ RB_GC_GUARD(loader_obj);
+ return iseq;
+}
+
VALUE
rb_iseq_ibf_load_extra_data(VALUE str)
{
diff --git a/inits.c b/inits.c
index 75ee976192..11b5fa5a7e 100644
--- a/inits.c
+++ b/inits.c
@@ -69,5 +69,7 @@ rb_call_inits(void)
CALL(vm_stack_canary);
CALL(ast);
CALL(gc_stress);
+
+ CALL(builtin);
}
#undef CALL
diff --git a/insns.def b/insns.def
index b59ae6a760..993bc047ee 100644
--- a/insns.def
+++ b/insns.def
@@ -1488,6 +1488,54 @@ opt_call_c_function
NEXT_INSN();
}
+/* call specific function with args */
+DEFINE_INSN
+invokebuiltin
+(RB_BUILTIN bf)
+(...)
+(VALUE ret)
+// attr bool leaf = false; /* anything can happen inside */
+// attr rb_snum_t sp_inc = 1 - bf->argc;
+{
+ ret = vm_invoke_builtin(ec, reg_cfp, bf);
+}
+
+/* call specific function with args (same parameters) */
+DEFINE_INSN
+opt_invokebuiltin_delegate
+(RB_BUILTIN bf)
+()
+(VALUE ret)
+// attr bool leaf = false; /* anything can happen inside */
+{
+ ret = vm_invoke_builtin_delegate(ec, reg_cfp, bf);
+}
+
+/* call specific function with args (same parameters) and leave */
+DEFINE_INSN
+opt_invokebuiltin_delegate_leave
+(RB_BUILTIN bf)
+()
+(VALUE val)
+// attr bool leaf = false; /* anything can happen inside */
+{
+ val = vm_invoke_builtin_delegate(ec, reg_cfp, bf);
+
+ /* leave fastpath */
+ /* TracePoint/return should fallback this insn to invokecfuncwparam */
+ if (vm_pop_frame(ec, GET_CFP(), GET_EP())) {
+#if OPT_CALL_THREADED_CODE
+ rb_ec_thread_ptr(ec)->retval = val;
+ return 0;
+#else
+ return val;
+#endif
+ }
+ else {
+ RESTORE_REGS();
+ }
+}
+
/* BLT */
DEFINE_INSN_IF(SUPPORT_JOKE)
bitblt
diff --git a/iseq.c b/iseq.c
index 0ca697ceb8..3cfb7602d0 100644
--- a/iseq.c
+++ b/iseq.c
@@ -12,6 +12,7 @@
#include "internal.h"
#include "ruby/util.h"
#include "eval_intern.h"
+#include "builtin.h"
#ifdef HAVE_DLADDR
# include <dlfcn.h>
@@ -559,6 +560,8 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
+ ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
+
if (option->coverage_enabled) {
VALUE coverages = rb_get_coverages();
if (RTEST(coverages)) {
@@ -1968,6 +1971,14 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
}
break;
+ case TS_BUILTIN:
+ {
+ const struct rb_builtin_function *bf = (const struct rb_builtin_function *)op;
+ ret = rb_sprintf("<builtin!%s/%d>",
+ bf->name, bf->argc);
+ }
+ break;
+
default:
rb_bug("unknown operand type: %c", type);
}
diff --git a/iseq.h b/iseq.h
index ef0dd7176d..5e4f24562e 100644
--- a/iseq.h
+++ b/iseq.h
@@ -116,6 +116,7 @@ struct iseq_compile_data {
unsigned int ci_kw_index;
const rb_compile_option_t *option;
struct rb_id_table *ivar_cache_table;
+ const struct rb_builtin_function *builtin_function_table;
#if OPT_SUPPORT_JOKE
st_table *labels_table;
#endif
@@ -155,6 +156,7 @@ iseq_imemo_alloc(void)
VALUE rb_iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt);
void rb_ibf_load_iseq_complete(rb_iseq_t *iseq);
const rb_iseq_t *rb_iseq_ibf_load(VALUE str);
+const rb_iseq_t *rb_iseq_ibf_load_cstr(const char *cstr, size_t);
VALUE rb_iseq_ibf_load_extra_data(VALUE str);
void rb_iseq_init_trace(rb_iseq_t *iseq);
int rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line);
diff --git a/mini_builtin.c b/mini_builtin.c
new file mode 100644
index 0000000000..d19f9da4c5
--- /dev/null
+++ b/mini_builtin.c
@@ -0,0 +1,89 @@
+#include "internal.h"
+#include "vm_core.h"
+#include "iseq.h"
+#include "builtin.h"
+
+// include from miniinits.c
+
+static const char *
+read_file(const char *fname, size_t *psize)
+{
+ struct stat st;
+ char *code;
+ FILE *fp;
+
+ if (stat(fname, &st) != 0) {
+ rb_bug("stat fails: %s", fname);
+ }
+
+ size_t fsize = st.st_size;
+ if ((code = malloc(fsize + 1)) == NULL) {
+ rb_bug("can't allocate memory: %s (%d)", fname, (int)fsize);
+ }
+
+ if ((fp = fopen(fname, "rb")) == NULL) {
+ rb_bug("can't open file: %s", fname);
+ }
+
+ size_t read_size = fread(code, 1, fsize, fp);
+ if (read_size != fsize) {
+ rb_bug("can't read file enough: %s (expect %d but was %d)", fname, (int)fsize, (int)read_size);
+ }
+
+ code[fsize] = 0;
+ *psize = fsize;
+ return code;
+}
+
+static struct st_table *loaded_builtin_table;
+
+void
+rb_load_with_builtin_functions(const char *feature_name, const char *fname, const struct rb_builtin_function *table)
+{
+ size_t fsize;
+ const char *code = read_file(fname, &fsize);
+ VALUE code_str = rb_utf8_str_new_static(code, fsize);
+ VALUE name_str = rb_sprintf("<internal:%s>", feature_name);
+ rb_obj_hide(code_str);
+
+ rb_ast_t *ast = rb_parser_compile_string_path(rb_parser_new(), name_str, code_str, 1);
+
+ GET_VM()->builtin_function_table = table;
+ const rb_iseq_t *iseq = rb_iseq_new(&ast->body, name_str, name_str, Qnil, NULL, ISEQ_TYPE_TOP);
+ GET_VM()->builtin_function_table = NULL;
+
+ rb_ast_dispose(ast);
+ free((void *)code); // code_str becomes broken.
+
+ // register (loaded iseq will not be freed)
+ st_insert(loaded_builtin_table, (st_data_t)feature_name, (st_data_t)iseq);
+ rb_gc_register_mark_object((VALUE)iseq);
+
+ // eval
+ rb_iseq_eval(iseq);
+}
+
+static int
+each_builtin_i(st_data_t key, st_data_t val, st_data_t dmy)
+{
+ const char *feature = (const char *)key;
+ const rb_iseq_t *iseq = (const rb_iseq_t *)val;
+
+ rb_yield_values(2, rb_str_new2(feature), rb_iseqw_new(iseq));
+
+ return ST_CONTINUE;
+}
+
+static VALUE
+each_builtin(VALUE self)
+{
+ st_foreach(loaded_builtin_table, each_builtin_i, 0);
+ return Qnil;
+}
+
+void
+Init_builtin(void)
+{
+ rb_define_singleton_method(rb_cRubyVM, "each_builtin", each_builtin, 0);
+ loaded_builtin_table = st_init_strtable();
+}
diff --git a/miniinit.c b/miniinit.c
index 7284e2e62d..2a14a0d1c5 100644
--- a/miniinit.c
+++ b/miniinit.c
@@ -47,3 +47,5 @@ Init_enc(void)
rb_encdb_alias("BINARY", "ASCII-8BIT");
rb_encdb_alias("ASCII", "US-ASCII");
}
+
+#include "mini_builtin.c"
diff --git a/mjit_compile.c b/mjit_compile.c
index bf5143f6ed..c31588d13e 100644
--- a/mjit_compile.c
+++ b/mjit_compile.c
@@ -17,6 +17,7 @@
#include "vm_core.h"
#include "vm_exec.h"
#include "mjit.h"
+#include "builtin.h"
#include "insns.inc"
#include "insns_info.inc"
#include "vm_insnhelper.h"
diff --git a/tool/mk_builtin_binary.rb b/tool/mk_builtin_binary.rb
new file mode 100644
index 0000000000..a5c962d5c1
--- /dev/null
+++ b/tool/mk_builtin_binary.rb
@@ -0,0 +1,33 @@
+#
+# make builtin_binary.inc file.
+#
+
+def dump_bin iseq
+ bin = iseq.to_binary
+ bin.each_byte.with_index{|b, index|
+ print "\n " if (index%20) == 0
+ print "0x#{'%02x' % b.ord}, "
+ }
+end
+
+ary = []
+RubyVM::each_builtin{|feature, iseq|
+ ary << [feature, iseq]
+}
+
+$stdout = open('builtin_binary.inc', 'wb')
+
+ary.each{|feature, iseq|
+ puts "static const unsigned char #{feature}_bin[] = {"
+ dump_bin(iseq)
+ puts "};"
+}
+
+puts "static const struct builtin_binary builtin_binary[] = {"
+ary.each{|feature, iseq|
+ puts " {#{feature.dump}, #{feature}_bin, sizeof(#{feature}_bin)},"
+}
+puts " {NULL}," # dummy sentry
+puts "};"
+
+puts "#define BUILTIN_BINARY_SIZE #{ary.size}"
diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
new file mode 100644
index 0000000000..91a66b16b4
--- /dev/null
+++ b/tool/mk_builtin_loader.rb
@@ -0,0 +1,76 @@
+
+def collect_builtin iseq_ary, bs
+ code = iseq_ary[13]
+
+ code.each{|insn|
+ next unless Array === insn
+ case insn[0]
+ when :send
+ ci = insn[1]
+ if /\A__builtin_(.+)/ =~ ci[:mid]
+ func_name = $1
+ argc = ci[:orig_argc]
+
+ if bs[func_name] && bs[func_name] != argc
+ raise
+ end
+ bs[func_name] = argc
+ end
+ else
+ insn[1..-1].each{|op|
+ if op[0] == "YARVInstructionSequence/SimpleDataFormat"
+ collect_builtin op, bs
+ end
+ }
+ end
+ }
+end
+# ruby mk_builtin_loader.rb TARGET_FILE.rb
+# #=> generate load_TARGET_FILE.inc
+#
+
+def mk_builtin_header file
+ base = File.basename(file, '.rb')
+ ofile = File.join("load_#{base}.inc")
+
+ collect_builtin(RubyVM::InstructionSequence.compile_file(file, false).to_a, bs = {})
+
+ open(ofile, 'w'){|f|
+ f.puts "// DO NOT MODIFY THIS FILE DIRECTLY."
+ f.puts "// auto-generated file"
+ f.puts "// by #{__FILE__}"
+ f.puts "// with #{file}"
+ f.puts
+
+ f.puts "static void load_#{base}(void)"
+ f.puts "{"
+
+ table = "#{base}_table"
+ f.puts " // table definition"
+ f.puts " static const struct rb_builtin_function #{table}[] = {"
+ bs.each.with_index{|(func, argc), i|
+ f.puts " RB_BUILTIN_FUNCTION(#{i}, #{func}, #{argc}),"
+ }
+ f.puts " RB_BUILTIN_FUNCTION(-1, NULL, 0),"
+ f.puts " };"
+
+ f.puts
+ f.puts " // arity_check"
+ bs.each{|func, argc|
+ f.puts " if (0) rb_builtin_function_check_arity#{argc}(#{func});"
+ }
+
+ path = File.expand_path(file)
+ f.puts
+ f.puts " // load"
+ f.puts " rb_load_with_builtin_functions(\"#{base}\", \"#{file}\", #{table});"
+
+ f.puts "}"
+ }
+end
+
+ARGV.each{|file|
+ # feature.rb => load_feature.inc
+ path = File.expand_path(file)
+ mk_builtin_header path
+}
diff --git a/tool/ruby_vm/models/typemap.rb b/tool/ruby_vm/models/typemap.rb
index 1125c4bbf6..015aa05632 100644
--- a/tool/ruby_vm/models/typemap.rb
+++ b/tool/ruby_vm/models/typemap.rb
@@ -24,6 +24,7 @@ RubyVM::Typemap = {
"lindex_t" => %w[L TS_LINDEX],
"rb_insn_func_t" => %w[F TS_FUNCPTR],
"rb_num_t" => %w[N TS_NUM],
+ "RB_BUILTIN" => %w[R TS_BUILTIN],
}
# :FIXME: should this method be here?
diff --git a/vm.c b/vm.c
index ed02a71a48..29745143b7 100644
--- a/vm.c
+++ b/vm.c
@@ -19,6 +19,8 @@
#include "vm_debug.h"
#include "iseq.h"
#include "eval_intern.h"
+#include "builtin.h"
+
#ifndef MJIT_HEADER
#include "probes.h"
#else
diff --git a/vm_core.h b/vm_core.h
index fabe8bbc10..9e158df923 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -564,6 +564,10 @@ typedef struct rb_hook_list_struct {
unsigned int running;
} rb_hook_list_t;
+
+// see builtin.h for definition
+typedef const struct rb_builtin_function *RB_BUILTIN;
+
typedef struct rb_vm_struct {
VALUE self;
@@ -647,6 +651,8 @@ typedef struct rb_vm_struct {
VALUE *defined_strings;
st_table *frozen_strings;
+ const struct rb_builtin_function *builtin_function_table;
+
/* params */
struct { /* size in byte */
size_t thread_vm_stack_size;
@@ -880,6 +886,9 @@ typedef struct rb_execution_context_struct {
} machine;
} rb_execution_context_t;
+// for builtin.h
+#define VM_CORE_H_EC_DEFINED 1
+
// Set the vm_stack pointer in the execution context.
void rb_ec_set_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size);
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index f8be5f6f33..89bc94cd8e 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -4725,3 +4725,189 @@ vm_canary_is_found_dead(enum ruby_vminsn_type i, VALUE c)
#else
void Init_vm_stack_canary(void) { /* nothing to do */ }
#endif
+
+
+/* a part of the following code is generated by this ruby script:
+
+16.times{|i|
+ typedef_args = (0...i).map{|j| "VALUE v#{j+1}"}.join(", ")
+ typedef_args.prepend(", ") if i != 0
+ call_args = (0...i).map{|j| "argv[#{j}]"}.join(", ")
+ call_args.prepend(", ") if i != 0
+ puts %Q{
+static VALUE
+builtin_invoker#{i}(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr#{i}_t)(rb_execution_context_t *ec, VALUE self#{typedef_args});
+ return (*(rb_invoke_funcptr#{i}_t)funcptr)(ec, self#{call_args});
+}}
+}
+
+puts
+puts "static VALUE (* const cfunc_invokers[])(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr) = {"
+16.times{|i|
+ puts " builtin_invoker#{i},"
+}
+puts "};"
+*/
+
+static VALUE
+builtin_invoker0(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr0_t)(rb_execution_context_t *ec, VALUE self);
+ return (*(rb_invoke_funcptr0_t)funcptr)(ec, self);
+}
+
+static VALUE
+builtin_invoker1(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr1_t)(rb_execution_context_t *ec, VALUE self, VALUE v1);
+ return (*(rb_invoke_funcptr1_t)funcptr)(ec, self, argv[0]);
+}
+
+static VALUE
+builtin_invoker2(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr2_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2);
+ return (*(rb_invoke_funcptr2_t)funcptr)(ec, self, argv[0], argv[1]);
+}
+
+static VALUE
+builtin_invoker3(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr3_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3);
+ return (*(rb_invoke_funcptr3_t)funcptr)(ec, self, argv[0], argv[1], argv[2]);
+}
+
+static VALUE
+builtin_invoker4(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr4_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4);
+ return (*(rb_invoke_funcptr4_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3]);
+}
+
+static VALUE
+builtin_invoker5(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr5_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5);
+ return (*(rb_invoke_funcptr5_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4]);
+}
+
+static VALUE
+builtin_invoker6(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr6_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6);
+ return (*(rb_invoke_funcptr6_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
+}
+
+static VALUE
+builtin_invoker7(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr7_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7);
+ return (*(rb_invoke_funcptr7_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
+}
+
+static VALUE
+builtin_invoker8(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr8_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8);
+ return (*(rb_invoke_funcptr8_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
+}
+
+static VALUE
+builtin_invoker9(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr9_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9);
+ return (*(rb_invoke_funcptr9_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
+}
+
+static VALUE
+builtin_invoker10(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr10_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10);
+ return (*(rb_invoke_funcptr10_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]);
+}
+
+static VALUE
+builtin_invoker11(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr11_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10, VALUE v11);
+ return (*(rb_invoke_funcptr11_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
+}
+
+static VALUE
+builtin_invoker12(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr12_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10, VALUE v11, VALUE v12);
+ return (*(rb_invoke_funcptr12_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]);
+}
+
+static VALUE
+builtin_invoker13(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr13_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10, VALUE v11, VALUE v12, VALUE v13);
+ return (*(rb_invoke_funcptr13_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]);
+}
+
+static VALUE
+builtin_invoker14(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr14_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10, VALUE v11, VALUE v12, VALUE v13, VALUE v14);
+ return (*(rb_invoke_funcptr14_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]);
+}
+
+static VALUE
+builtin_invoker15(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr)
+{
+ typedef VALUE (*rb_invoke_funcptr15_t)(rb_execution_context_t *ec, VALUE self, VALUE v1, VALUE v2, VALUE v3, VALUE v4, VALUE v5, VALUE v6, VALUE v7, VALUE v8, VALUE v9, VALUE v10, VALUE v11, VALUE v12, VALUE v13, VALUE v14, VALUE v15);
+ return (*(rb_invoke_funcptr15_t)funcptr)(ec, self, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]);
+}
+
+typedef VALUE (*builtin_invoker)(rb_execution_context_t *ec, VALUE self, const VALUE *argv, rb_insn_func_t funcptr);
+
+static builtin_invoker
+lookup_builtin_invoker(int argc)
+{
+ static const builtin_invoker invokers[] = {
+ builtin_invoker0,
+ builtin_invoker1,
+ builtin_invoker2,
+ builtin_invoker3,
+ builtin_invoker4,
+ builtin_invoker5,
+ builtin_invoker6,
+ builtin_invoker7,
+ builtin_invoker8,
+ builtin_invoker9,
+ builtin_invoker10,
+ builtin_invoker11,
+ builtin_invoker12,
+ builtin_invoker13,
+ builtin_invoker14,
+ builtin_invoker15,
+ };
+
+ return invokers[argc];
+}
+
+static inline VALUE
+invoke_bf(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_builtin_function* bf, const VALUE *argv)
+{
+ VALUE self = cfp->self;
+ return (*lookup_builtin_invoker(bf->argc))(ec, self, argv, (rb_insn_func_t)bf->func_ptr);
+}
+
+static VALUE
+vm_invoke_builtin(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_builtin_function* bf)
+{
+ const VALUE *argv = cfp->sp - bf->argc;
+ return invoke_bf(ec, cfp, bf, argv);
+}
+
+static VALUE
+vm_invoke_builtin_delegate(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_builtin_function *bf)
+{
+ const VALUE *argv = cfp->ep - cfp->iseq->body->local_table_size - VM_ENV_DATA_SIZE + 1;
+ // fprintf(stderr, "%s %s(%d):%p\n", __func__, bf->name, bf->argc, bf->func_ptr);
+ return invoke_bf(ec, cfp, bf, argv);
+}