diff options
author | Koichi Sasada <[email protected]> | 2019-11-11 16:38:46 +0900 |
---|---|---|
committer | Koichi Sasada <[email protected]> | 2019-11-11 16:47:50 +0900 |
commit | 31416423809f64d4b5ea6b9651cced3179cc5ced (patch) | |
tree | 5cf4b7d9b19019d5f665ca0b0c3902f8100cd597 | |
parent | 05a5c69e1a06a3853afb3744fa9925910b78904f (diff) |
__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.
-rw-r--r-- | builtin.c | 15 | ||||
-rw-r--r-- | builtin.h | 13 | ||||
-rw-r--r-- | compile.c | 16 | ||||
-rw-r--r-- | tool/mk_builtin_loader.rb | 58 | ||||
-rw-r--r-- | vm_core.h | 1 |
5 files changed, 93 insertions, 10 deletions
@@ -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]; +} @@ -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 { @@ -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)", diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 4f140a6f6b..8f4d5df60b 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -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 */ |