1
0
Fork 0
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:
Koichi Sasada 2019-11-11 16:38:46 +09:00
parent 05a5c69e1a
commit 3141642380
5 changed files with 93 additions and 10 deletions

View file

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

View file

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

View file

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

View file

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

View file

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