mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
__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
5 changed files with 93 additions and 10 deletions
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);
|
||||
|
||||
// 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);
|
||||
GET_VM()->builtin_function_table = NULL;
|
||||
vm->builtin_function_table = NULL;
|
||||
|
||||
// exec
|
||||
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_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
|
||||
|
||||
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;
|
||||
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);
|
||||
for (i=0; table[i].index != -1; i++) {
|
||||
if (strcmp(table[i].name, name) == 0) {
|
||||
return &table[i];
|
||||
}
|
||||
|
@ -6886,6 +6885,8 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
|||
}
|
||||
#endif
|
||||
const char *builtin_func;
|
||||
NODE *args_node = node->nd_args;
|
||||
|
||||
if (UNLIKELY(iseq_has_builtin_function_table(iseq)) &&
|
||||
(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;
|
||||
}
|
||||
else {
|
||||
char inline_func[0x20];
|
||||
retry:;
|
||||
const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
|
||||
argc = setup_args(iseq, args, args_node, &flag, &keywords);
|
||||
|
||||
if (FIX2INT(argc) != bf->argc) {
|
||||
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]
|
||||
params = iseq_ary[10]
|
||||
prev_insn = nil
|
||||
lineno = nil
|
||||
|
||||
code.each{|insn|
|
||||
case insn
|
||||
when Array
|
||||
# ok
|
||||
when Integer
|
||||
lineno = insn
|
||||
next
|
||||
else
|
||||
next
|
||||
end
|
||||
|
||||
next unless Array === insn
|
||||
case insn[0]
|
||||
when :send
|
||||
|
@ -11,18 +24,31 @@ def collect_builtin iseq_ary, bs
|
|||
func_name = $1
|
||||
argc = ci[:orig_argc]
|
||||
|
||||
if bs[func_name] && bs[func_name] != argc
|
||||
raise
|
||||
if func_name == 'inline!'
|
||||
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
|
||||
|
||||
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
|
||||
end
|
||||
else
|
||||
insn[1..-1].each{|op|
|
||||
if op.is_a?(Array) && op[0] == "YARVInstructionSequence/SimpleDataFormat"
|
||||
collect_builtin op, bs
|
||||
collect_builtin base, op, bs, inlines
|
||||
end
|
||||
}
|
||||
end
|
||||
prev_insn = insn
|
||||
}
|
||||
end
|
||||
# ruby mk_builtin_loader.rb TARGET_FILE.rb
|
||||
|
@ -33,7 +59,8 @@ def mk_builtin_header file
|
|||
base = File.basename(file, '.rb')
|
||||
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|
|
||||
f.puts "// -*- c -*-"
|
||||
|
@ -42,6 +69,27 @@ def mk_builtin_header file
|
|||
f.puts "// by #{__FILE__}"
|
||||
f.puts "// with #{file}"
|
||||
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 "{"
|
||||
|
|
|
@ -652,6 +652,7 @@ typedef struct rb_vm_struct {
|
|||
st_table *frozen_strings;
|
||||
|
||||
const struct rb_builtin_function *builtin_function_table;
|
||||
int builtin_inline_index;
|
||||
|
||||
/* params */
|
||||
struct { /* size in byte */
|
||||
|
|
Loading…
Reference in a new issue