support builtin features with Ruby and C.

Support loading builtin features written in Ruby, which implement
with C builtin functions.
[Feature #16254]

Several features:

(1) Load .rb file at boottime with native binary.

Now, prelude.rb is loaded at boottime. However, this file is contained
into the interpreter as a text format and we need to compile it.
This patch contains a feature to load from binary format.

(2) __builtin_func() in Ruby call func() written in C.

In Ruby file, we can write `__builtin_func()` like method call.
However this is not a method call, but special syntax to call
a function `func()` written in C. C functions should be defined
in a file (same compile unit) which load this .rb file.

Functions (`func` in above example) should be defined with
  (a) 1st parameter: rb_execution_context_t *ec
  (b) rest parameters (0 to 15).
  (c) VALUE return type.
This is very similar requirements for functions used by
rb_define_method(), however `rb_execution_context_t *ec`
is new requirement.

(3) automatic C code generation from .rb files.

tool/mk_builtin_loader.rb creates a C code to load .rb files
needed by miniruby and ruby command. This script is run by
BASERUBY, so *.rb should be written in BASERUBY compatbile
syntax. This script load a .rb file and find all of __builtin_
prefix method calls, and generate a part of C code to export
functions.

tool/mk_builtin_binary.rb creates a C code which contains
binary compiled Ruby files needed by ruby command.
This commit is contained in:
Koichi Sasada 2019-11-07 16:58:00 +09:00
parent dddf5afb79
commit 46acd0075d
Notes: git 2019-11-08 09:10:08 +09:00
17 changed files with 838 additions and 28 deletions

40
builtin.c Normal file
View File

@ -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)
{
//
}

52
builtin.h Normal file
View File

@ -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;
};

View File

@ -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

256
compile.c
View File

@ -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;
}
@ -6717,6 +6729,77 @@ compile_call_precheck_freeze(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE
return FALSE;
}
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)
{
@ -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) {
@ -11294,6 +11465,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)
{
@ -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)
{

View File

@ -69,5 +69,7 @@ rb_call_inits(void)
CALL(vm_stack_canary);
CALL(ast);
CALL(gc_stress);
CALL(builtin);
}
#undef CALL

View File

@ -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

11
iseq.c
View File

@ -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);
}

2
iseq.h
View File

@ -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);

89
mini_builtin.c Normal file
View File

@ -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();
}

View File

@ -47,3 +47,5 @@ Init_enc(void)
rb_encdb_alias("BINARY", "ASCII-8BIT");
rb_encdb_alias("ASCII", "US-ASCII");
}
#include "mini_builtin.c"

View File

@ -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"

33
tool/mk_builtin_binary.rb Normal file
View File

@ -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}"

76
tool/mk_builtin_loader.rb Normal file
View File

@ -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
}

View File

@ -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?

2
vm.c
View File

@ -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

View File

@ -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);

View File

@ -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);
}