__builtin_inline!
Add an experimental `__builtin_inline!(c_expression)` special intrinsic which run a C code snippet. In `c_expression`, you can access the following variables: * ec (rb_execution_context_t *) * self (const VALUE) * local variables (const VALUE) Not that you can read these variables, but you can not write them. You need to return from this expression and return value will be a result of __builtin_inline!(). Examples: `def foo(x) __builtin_inline!('return rb_p(x);'); end` calls `p(x)`. `def double(x) __builtin_inline!('return INT2NUM(NUM2INT(x) * 2);')` returns x*2.
This commit is contained in:
parent
05a5c69e1a
commit
3141642380
15
builtin.c
15
builtin.c
@ -25,9 +25,12 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin
|
|||||||
const unsigned char *bin = builtin_lookup(feature_name, &size);
|
const unsigned char *bin = builtin_lookup(feature_name, &size);
|
||||||
|
|
||||||
// load binary
|
// load binary
|
||||||
GET_VM()->builtin_function_table = table;
|
rb_vm_t *vm = GET_VM();
|
||||||
|
if (vm->builtin_function_table != NULL) rb_bug("vm->builtin_function_table should be NULL.");
|
||||||
|
vm->builtin_function_table = table;
|
||||||
|
vm->builtin_inline_index = 0;
|
||||||
const rb_iseq_t *iseq = rb_iseq_ibf_load_bytes((const char *)bin, size);
|
const rb_iseq_t *iseq = rb_iseq_ibf_load_bytes((const char *)bin, size);
|
||||||
GET_VM()->builtin_function_table = NULL;
|
vm->builtin_function_table = NULL;
|
||||||
|
|
||||||
// exec
|
// exec
|
||||||
rb_iseq_eval(iseq);
|
rb_iseq_eval(iseq);
|
||||||
@ -38,3 +41,11 @@ Init_builtin(void)
|
|||||||
{
|
{
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// inline
|
||||||
|
VALUE
|
||||||
|
rb_vm_lvar_exposed(rb_execution_context_t *ec, int index)
|
||||||
|
{
|
||||||
|
const rb_control_frame_t *cfp = ec->cfp;
|
||||||
|
return cfp->ep[index];
|
||||||
|
}
|
||||||
|
13
builtin.h
13
builtin.h
@ -43,6 +43,19 @@ static inline void rb_builtin_function_check_arity13(VALUE (*f)(rb_execution_con
|
|||||||
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_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)){}
|
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)){}
|
||||||
|
|
||||||
|
VALUE rb_vm_lvar_exposed(rb_execution_context_t *ec, int index);
|
||||||
|
|
||||||
|
// inline
|
||||||
|
inline VALUE
|
||||||
|
rb_vm_lvar(rb_execution_context_t *ec, int index)
|
||||||
|
{
|
||||||
|
#if VM_CORE_H_EC_DEFINED
|
||||||
|
return ec->cfp->ep[index];
|
||||||
|
#else
|
||||||
|
return rb_vm_lvar_exposed(ec, index);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// dump/load
|
// dump/load
|
||||||
|
|
||||||
struct builtin_binary {
|
struct builtin_binary {
|
||||||
|
16
compile.c
16
compile.c
@ -6740,8 +6740,7 @@ iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
|
const struct rb_builtin_function *table = ISEQ_COMPILE_DATA(iseq)->builtin_function_table;
|
||||||
for (i=0; table[i].name != NULL; i++) {
|
for (i=0; table[i].index != -1; i++) {
|
||||||
// fprintf(stderr, "table[%d].name:%s, name:%s\n", i, table[i].name, name);
|
|
||||||
if (strcmp(table[i].name, name) == 0) {
|
if (strcmp(table[i].name, name) == 0) {
|
||||||
return &table[i];
|
return &table[i];
|
||||||
}
|
}
|
||||||
@ -6886,6 +6885,8 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
const char *builtin_func;
|
const char *builtin_func;
|
||||||
|
NODE *args_node = node->nd_args;
|
||||||
|
|
||||||
if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
|
if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
|
||||||
(builtin_func = iseq_builtin_function_name(mid)) != NULL) {
|
(builtin_func = iseq_builtin_function_name(mid)) != NULL) {
|
||||||
|
|
||||||
@ -6894,9 +6895,18 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
|||||||
return COMPILE_NG;
|
return COMPILE_NG;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
char inline_func[0x20];
|
||||||
|
retry:;
|
||||||
const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
|
const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
|
||||||
|
|
||||||
if (bf == NULL) {
|
if (bf == NULL) {
|
||||||
|
if (strcmp("inline!", builtin_func) == 0) {
|
||||||
|
int inline_index = GET_VM()->builtin_inline_index++;
|
||||||
|
snprintf(inline_func, 0x20, "__builtin_inline%d", inline_index);
|
||||||
|
builtin_func = inline_func;
|
||||||
|
args_node = NULL;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
if (1) {
|
if (1) {
|
||||||
rb_bug("can't find builtin function:%s", builtin_func);
|
rb_bug("can't find builtin function:%s", builtin_func);
|
||||||
}
|
}
|
||||||
@ -6908,7 +6918,7 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
|||||||
|
|
||||||
// fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
|
// fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
|
||||||
|
|
||||||
argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
|
argc = setup_args(iseq, args, args_node, &flag, &keywords);
|
||||||
|
|
||||||
if (FIX2INT(argc) != bf->argc) {
|
if (FIX2INT(argc) != bf->argc) {
|
||||||
COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
|
COMPILE_ERROR(ERROR_ARGS "argc is not match for builtin function:%s (expect %d but %d)",
|
||||||
|
@ -1,8 +1,21 @@
|
|||||||
|
|
||||||
def collect_builtin iseq_ary, bs
|
def collect_builtin base, iseq_ary, bs, inlines
|
||||||
code = iseq_ary[13]
|
code = iseq_ary[13]
|
||||||
|
params = iseq_ary[10]
|
||||||
|
prev_insn = nil
|
||||||
|
lineno = nil
|
||||||
|
|
||||||
code.each{|insn|
|
code.each{|insn|
|
||||||
|
case insn
|
||||||
|
when Array
|
||||||
|
# ok
|
||||||
|
when Integer
|
||||||
|
lineno = insn
|
||||||
|
next
|
||||||
|
else
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
next unless Array === insn
|
next unless Array === insn
|
||||||
case insn[0]
|
case insn[0]
|
||||||
when :send
|
when :send
|
||||||
@ -11,18 +24,31 @@ def collect_builtin iseq_ary, bs
|
|||||||
func_name = $1
|
func_name = $1
|
||||||
argc = ci[:orig_argc]
|
argc = ci[:orig_argc]
|
||||||
|
|
||||||
if bs[func_name] && bs[func_name] != argc
|
if func_name == 'inline!'
|
||||||
raise
|
raise "argc (#{argc}) of inline! should be 1" unless argc == 1
|
||||||
|
raise "1st argument should be string literal" unless prev_insn[0] == :putstring
|
||||||
|
text = prev_insn[1]
|
||||||
|
|
||||||
|
func_name = "__builtin_inline#{inlines.size}"
|
||||||
|
inlines << [func_name, [lineno, text, params]]
|
||||||
|
argc -= 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if bs[func_name] &&
|
||||||
|
bs[func_name] != argc
|
||||||
|
raise "same builtin function \"#{func_name}\", but different arity (was #{bs[func_name]} but #{argc})"
|
||||||
|
end
|
||||||
|
|
||||||
bs[func_name] = argc
|
bs[func_name] = argc
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
insn[1..-1].each{|op|
|
insn[1..-1].each{|op|
|
||||||
if op.is_a?(Array) && op[0] == "YARVInstructionSequence/SimpleDataFormat"
|
if op.is_a?(Array) && op[0] == "YARVInstructionSequence/SimpleDataFormat"
|
||||||
collect_builtin op, bs
|
collect_builtin base, op, bs, inlines
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
prev_insn = insn
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
# ruby mk_builtin_loader.rb TARGET_FILE.rb
|
# ruby mk_builtin_loader.rb TARGET_FILE.rb
|
||||||
@ -33,7 +59,8 @@ def mk_builtin_header file
|
|||||||
base = File.basename(file, '.rb')
|
base = File.basename(file, '.rb')
|
||||||
ofile = "#{base}.rbinc"
|
ofile = "#{base}.rbinc"
|
||||||
|
|
||||||
collect_builtin(RubyVM::InstructionSequence.compile_file(file, false).to_a, bs = {})
|
# bs = { func_name => argc }
|
||||||
|
collect_builtin(base, RubyVM::InstructionSequence.compile_file(file, false).to_a, bs = {}, inlines = [])
|
||||||
|
|
||||||
open(ofile, 'w'){|f|
|
open(ofile, 'w'){|f|
|
||||||
f.puts "// -*- c -*-"
|
f.puts "// -*- c -*-"
|
||||||
@ -42,6 +69,27 @@ def mk_builtin_header file
|
|||||||
f.puts "// by #{__FILE__}"
|
f.puts "// by #{__FILE__}"
|
||||||
f.puts "// with #{file}"
|
f.puts "// with #{file}"
|
||||||
f.puts
|
f.puts
|
||||||
|
lineno = 6
|
||||||
|
|
||||||
|
inlines.each{|name, (body_lineno, text, params)|
|
||||||
|
f.puts "static VALUE #{name}(rb_execution_context_t *ec, const VALUE self) {"
|
||||||
|
lineno += 1
|
||||||
|
|
||||||
|
params.reverse_each.with_index{|param, i|
|
||||||
|
next unless Symbol === param
|
||||||
|
f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
|
||||||
|
lineno += 1
|
||||||
|
}
|
||||||
|
f.puts "#line #{body_lineno} \"#{file}\""
|
||||||
|
lineno += 1
|
||||||
|
|
||||||
|
f.puts text
|
||||||
|
lineno += text.count("\n") + 1
|
||||||
|
|
||||||
|
f.puts "#line #{lineno + 2} \"#{ofile}\"" # TODO: restore line number.
|
||||||
|
f.puts "}"
|
||||||
|
lineno += 2
|
||||||
|
}
|
||||||
|
|
||||||
f.puts "static void load_#{base}(void)"
|
f.puts "static void load_#{base}(void)"
|
||||||
f.puts "{"
|
f.puts "{"
|
||||||
|
@ -652,6 +652,7 @@ typedef struct rb_vm_struct {
|
|||||||
st_table *frozen_strings;
|
st_table *frozen_strings;
|
||||||
|
|
||||||
const struct rb_builtin_function *builtin_function_table;
|
const struct rb_builtin_function *builtin_function_table;
|
||||||
|
int builtin_inline_index;
|
||||||
|
|
||||||
/* params */
|
/* params */
|
||||||
struct { /* size in byte */
|
struct { /* size in byte */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user