ruby-changes:58710
From: Koichi <ko1@a...>
Date: Mon, 11 Nov 2019 16:48:20 +0900 (JST)
Subject: [ruby-changes:58710] 3141642380 (master): __builtin_inline!
https://git.ruby-lang.org/ruby.git/commit/?id=3141642380 From 31416423809f64d4b5ea6b9651cced3179cc5ced Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Mon, 11 Nov 2019 16:38:46 +0900 Subject: __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. diff --git a/builtin.c b/builtin.c index d936d1c..d0d998d 100644 --- a/builtin.c +++ b/builtin.c @@ -25,9 +25,12 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin https://github.com/ruby/ruby/blob/trunk/builtin.c#L25 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) https://github.com/ruby/ruby/blob/trunk/builtin.c#L41 { // } + +// 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]; +} diff --git a/builtin.h b/builtin.h index ee99e21..236dfd9 100644 --- a/builtin.h +++ b/builtin.h @@ -43,6 +43,19 @@ static inline void rb_builtin_function_check_arity13(VALUE (*f)(rb_execution_con https://github.com/ruby/ruby/blob/trunk/builtin.h#L43 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 { diff --git a/compile.c b/compile.c index 131f82a..e0b8505 100644 --- a/compile.c +++ b/compile.c @@ -6740,8 +6740,7 @@ iseq_builtin_function_lookup(const rb_iseq_t *iseq, const char *name) https://github.com/ruby/ruby/blob/trunk/compile.c#L6740 { 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6885 } #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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6895 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6918 // 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 4f140a6..8f4d5df 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -1,8 +1,21 @@ https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L1 -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 https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L24 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 https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L59 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 https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L69 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 "{" diff --git a/vm_core.h b/vm_core.h index 9e158df..9c5b723 100644 --- a/vm_core.h +++ b/vm_core.h @@ -652,6 +652,7 @@ typedef struct rb_vm_struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L652 st_table *frozen_strings; const struct rb_builtin_function *builtin_function_table; + int builtin_inline_index; /* params */ struct { /* size in byte */ -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/