[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]