inline Primitive.cexpr!
author卜部昌平 <[email protected]>
Fri, 10 Jul 2020 02:49:50 +0000 (10 11:49 +0900)
committer卜部昌平 <[email protected]>
Sun, 12 Jul 2020 23:56:18 +0000 (13 08:56 +0900)
We can obtain the verbatim source code of Primitive.cexpr!.  Why not
paste that content into the JITed program.

integer.rb
internal/numeric.h
internal/object.h
numeric.c
object.c
tool/mk_builtin_loader.rb
tool/ruby_vm/helpers/c_escape.rb

index daf11b0..f2f1e79 100644 (file)
@@ -15,7 +15,7 @@ class Integer
   #  Returns +true+ if +int+ is an even number.
   def even?
     Primitive.attr! 'inline'
-    Primitive.cexpr! 'int_even_p(self)'
+    Primitive.cexpr! 'rb_int_even_p(self)'
   end
 
   #  call-seq:
@@ -79,6 +79,6 @@ class Integer
   # Returns +true+ if +int+ has a zero value.
   def zero?
     Primitive.attr! 'inline'
-    Primitive.cexpr! 'int_zero_p(self)'
+    Primitive.cexpr! 'rb_int_zero_p(self)'
   end
 end
index 219e739..87f5559 100644 (file)
@@ -75,7 +75,6 @@ VALUE rb_int_divmod(VALUE x, VALUE y);
 VALUE rb_int_and(VALUE x, VALUE y);
 VALUE rb_int_lshift(VALUE x, VALUE y);
 VALUE rb_int_div(VALUE x, VALUE y);
-VALUE rb_int_abs(VALUE num);
 VALUE rb_int_odd_p(VALUE num);
 int rb_int_positive_p(VALUE num);
 int rb_int_negative_p(VALUE num);
@@ -107,6 +106,9 @@ VALUE rb_float_equal(VALUE x, VALUE y);
 int rb_float_cmp(VALUE x, VALUE y);
 VALUE rb_float_eql(VALUE x, VALUE y);
 VALUE rb_fix_aref(VALUE fix, VALUE idx);
+VALUE rb_int_zero_p(VALUE num);
+VALUE rb_int_even_p(VALUE num);
+VALUE rb_int_abs(VALUE num);
 MJIT_SYMBOL_EXPORT_END
 
 static inline bool
index a05abbf..d34f498 100644 (file)
@@ -35,6 +35,7 @@ VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2);
 void rb_obj_copy_ivar(VALUE dest, VALUE obj);
 VALUE rb_false(VALUE obj);
 VALUE rb_convert_type_with_id(VALUE v, int t, const char* nam, ID mid);
+VALUE rb_obj_size(VALUE self, VALUE args, VALUE obj);
 MJIT_SYMBOL_EXPORT_END
 
 static inline void
index b883b30..b59c319 100644 (file)
--- a/numeric.c
+++ b/numeric.c
@@ -794,6 +794,12 @@ int_zero_p(VALUE num)
     return Qfalse;
 }
 
+VALUE
+rb_int_zero_p(VALUE num)
+{
+    return int_zero_p(num);
+}
+
 /*
  *  call-seq:
  *     num.nonzero?  ->  self or nil
@@ -3250,6 +3256,12 @@ int_even_p(VALUE num)
     }
 }
 
+VALUE
+rb_int_even_p(VALUE num)
+{
+    return int_even_p(num);
+}
+
 /*
  *  call-seq:
  *     int.allbits?(mask)  ->  true or false
index a684927..b47635f 100644 (file)
--- a/object.c
+++ b/object.c
@@ -591,7 +591,7 @@ rb_obj_itself(VALUE obj)
     return obj;
 }
 
-static VALUE
+VALUE
 rb_obj_size(VALUE self, VALUE args, VALUE obj)
 {
     return LONG2FIX(1);
index 4efdde3..8f78b66 100644 (file)
@@ -1,6 +1,8 @@
 # Parse built-in script and make rbinc file
 
 require 'ripper'
+require 'stringio'
+require_relative 'ruby_vm/helpers/c_escape'
 
 def string_literal(lit, str = [])
   while lit
@@ -207,6 +209,29 @@ def collect_iseq iseq_ary
   }
 end
 
+def generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
+  f = StringIO.new
+  f.puts '{'
+  lineno += 1
+  locals.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} \"#{line_file}\""
+  lineno += 1
+
+  f.puts text
+  lineno += text.count("\n") + 1
+
+  f.puts "#line #{lineno + 2} \"#{ofile}\"" # TODO: restore line number.
+  f.puts "}"
+  f.puts
+  lineno += 3
+
+  return lineno, f.string
+end
+
 def mk_builtin_header file
   base = File.basename(file, '.rb')
   ofile = "#{file}inc"
@@ -244,23 +269,10 @@ def mk_builtin_header file
 
     inlines.each{|cfunc_name, (body_lineno, text, locals, func_name)|
       if String === cfunc_name
-        f.puts "static VALUE #{cfunc_name}(struct rb_execution_context_struct *ec, const VALUE self) {"
-        lineno += 1
-
-        locals.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} \"#{line_file}\""
+        f.puts "static VALUE #{cfunc_name}(struct rb_execution_context_struct *ec, const VALUE self)"
         lineno += 1
-
-        f.puts text
-        lineno += text.count("\n") + 1
-
-        f.puts "#line #{lineno + 2} \"#{ofile}\"" # TODO: restore line number.
-        f.puts "}"
-        lineno += 2
+        lineno, str = generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
+        f.write str
       else
         # cinit!
         f.puts "#line #{body_lineno} \"#{line_file}\""
@@ -276,17 +288,32 @@ def mk_builtin_header file
       f.puts %'static void'
       f.puts %'mjit_compile_invokebuiltin_for_#{func}(FILE *f, long index)'
       f.puts %'{'
-      f.puts %'    if (index > 0) {'
-      f.puts %'        fprintf(f, "    const unsigned int lnum = GET_ISEQ()->body->local_table_size;\\n");'
-      f.puts %'        fprintf(f, "    const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\\n", index);'
-      f.puts %'    }'
-      f.puts %'    else if (index == 0) {'
-      f.puts %'        fprintf(f, "    const VALUE *argv = NULL;\\n");'
-      f.puts %'    }'
-      f.puts %'    else {'
-      f.puts %'        fprintf(f, "    const VALUE *argv = STACK_ADDR_FROM_TOP(%d);\\n", #{argc});'
-      f.puts %'    }'
-      f.puts %'    fprintf(f, "    val = builtin_invoker#{argc}(ec, GET_SELF(), argv, %p);\\n", (const void *)#{cfunc_name});'
+      if inlines.has_key? cfunc_name
+        f.puts %'    fprintf(f, "    MAYBE_UNUSED(VALUE) self = GET_SELF();\\n");'
+        body_lineno, text, locals, func_name = inlines[cfunc_name]
+        lineno, str = generate_cexpr(ofile, lineno, line_file, body_lineno, text, locals, func_name)
+        str.each_line {|i|
+          f.printf(%'    fprintf(f, "%%s", %s);\n', RubyVM::CEscape.rstring2cstr(i.sub(/^return\b/ , '    val =')))
+        }
+      else
+        decl = ', VALUE' * argc
+        argv = argc                    \
+             . times                   \
+             . map {|i|", argv[#{i}]"} \
+             . join('')
+        f.puts %'    fprintf(f, "    typedef VALUE (*func)(rb_execution_context_t *, VALUE#{decl});\\n");'
+        if argc > 0
+          f.puts %'    if (index == -1) {'
+          f.puts %'        fprintf(f, "    const VALUE *argv = STACK_ADDR_FROM_TOP(%d);\\n", #{argc});'
+          f.puts %'    }'
+          f.puts %'    else {'
+          f.puts %'        fprintf(f, "    const unsigned int lnum = GET_ISEQ()->body->local_table_size;\\n");'
+          f.puts %'        fprintf(f, "    const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\\n", index);'
+          f.puts %'    }'
+        end
+        f.puts %'    fprintf(f, "    func f = (func)%p\\n;", (const void *)#{cfunc_name});'
+        f.puts %'    fprintf(f, "    val = f(ec, GET_SELF()#{argv});\\n");'
+      end
       f.puts %'}'
       f.puts
     }
index 3e2bf2e..fa3cb8b 100644 (file)
@@ -46,7 +46,7 @@ module RubyVM::CEscape
     # I believe this is the fastest implementation done in pure-ruby.
     # Constants cached, gsub skips block evaluation, string literal optimized.
     buf = str.b
-    buf.gsub! %r/./n, RString2CStr
+    buf.gsub! %r/./nm, RString2CStr
     return %'"#{buf}"'
   end