зеркало из https://github.com/github/ruby.git
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:
Родитель
dddf5afb79
Коммит
46acd0075d
|
@ -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)
|
||||
{
|
||||
//
|
||||
}
|
|
@ -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;
|
||||
};
|
56
common.mk
56
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
|
||||
|
|
256
compile.c
256
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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
2
inits.c
2
inits.c
|
@ -69,5 +69,7 @@ rb_call_inits(void)
|
|||
CALL(vm_stack_canary);
|
||||
CALL(ast);
|
||||
CALL(gc_stress);
|
||||
|
||||
CALL(builtin);
|
||||
}
|
||||
#undef CALL
|
||||
|
|
48
insns.def
48
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
|
||||
|
|
11
iseq.c
11
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);
|
||||
}
|
||||
|
|
2
iseq.h
2
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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -47,3 +47,5 @@ Init_enc(void)
|
|||
rb_encdb_alias("BINARY", "ASCII-8BIT");
|
||||
rb_encdb_alias("ASCII", "US-ASCII");
|
||||
}
|
||||
|
||||
#include "mini_builtin.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"
|
||||
|
|
|
@ -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}"
|
|
@ -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
|
||||
}
|
|
@ -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
2
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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
186
vm_insnhelper.c
186
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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче