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

ruby-changes:61872

From: Takashi <ko1@a...>
Date: Sun, 21 Jun 2020 09:13:19 +0900 (JST)
Subject: [ruby-changes:61872] 7561db8c00 (master): Introduce Primitive.attr! to annotate 'inline' (#3242)

https://git.ruby-lang.org/ruby.git/commit/?id=7561db8c00

From 7561db8c009bb79a75024fa4ed0350bfb3d0626c Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
Date: Sat, 20 Jun 2020 17:13:03 -0700
Subject: Introduce Primitive.attr! to annotate 'inline' (#3242)

[Feature #15589]

diff --git a/benchmark/mjit_int_zero_p.yml b/benchmark/mjit_int_zero_p.yml
new file mode 100644
index 0000000..91e8ea0
--- /dev/null
+++ b/benchmark/mjit_int_zero_p.yml
@@ -0,0 +1,36 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/mjit_int_zero_p.yml#L1
+prelude: |
+  def mjit_zero?(int)
+    int.zero?
+  end
+
+  def mjit_eq_0(int)
+    int == 0
+  end
+
+  def warmup(sym, int)
+    if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+      jit_min_calls = 10000
+      i = 0
+      while i < jit_min_calls
+        send(sym, int)
+        i += 1
+      end
+      RubyVM::MJIT.pause
+    end
+  end
+
+benchmark:
+  - name: 0.zero?
+    prelude: warmup(:mjit_zero?, 0)
+    script: mjit_zero?(0)
+  - name: 1.zero?
+    prelude: warmup(:mjit_zero?, 1)
+    script: mjit_zero?(1)
+  - name: 0 == 0
+    prelude: warmup(:mjit_eq_0, 0)
+    script: mjit_eq_0(0)
+  - name: 1 == 0
+    prelude: warmup(:mjit_eq_0, 1)
+    script: mjit_eq_0(1)
+
+loop_count: 40000000
diff --git a/compile.c b/compile.c
index 972a299..77af316 100644
--- a/compile.c
+++ b/compile.c
@@ -7274,6 +7274,11 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, co https://github.com/ruby/ruby/blob/trunk/compile.c#L7274
                     GET_VM()->builtin_inline_index++;
                     return COMPILE_OK;
                 }
+                else if (strcmp("attr!", builtin_func) == 0) {
+                    // There's only "inline" attribute for now
+                    iseq->body->builtin_inline_p = true;
+                    return COMPILE_OK;
+                }
 
                 if (1) {
                     rb_bug("can't find builtin function:%s", builtin_func);
@@ -10815,6 +10820,7 @@ ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/compile.c#L10820
     ibf_dump_write_small_value(dump, body->ci_size);
     ibf_dump_write_small_value(dump, body->stack_max);
     ibf_dump_write_small_value(dump, body->catch_except_p);
+    ibf_dump_write_small_value(dump, body->builtin_inline_p);
 
 #undef IBF_BODY_OFFSET
 
@@ -10920,6 +10926,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) https://github.com/ruby/ruby/blob/trunk/compile.c#L10926
     const unsigned int ci_size = (unsigned int)ibf_load_small_value(load, &reading_pos);
     const unsigned int stack_max = (unsigned int)ibf_load_small_value(load, &reading_pos);
     const char catch_except_p = (char)ibf_load_small_value(load, &reading_pos);
+    const bool builtin_inline_p = (bool)ibf_load_small_value(load, &reading_pos);
 
 #undef IBF_BODY_OFFSET
 
@@ -10958,6 +10965,7 @@ ibf_load_iseq_each(struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset) https://github.com/ruby/ruby/blob/trunk/compile.c#L10965
     load_body->location.code_location.end_pos.lineno = location_code_location_end_pos_lineno;
     load_body->location.code_location.end_pos.column = location_code_location_end_pos_column;
     load_body->catch_except_p = catch_except_p;
+    load_body->builtin_inline_p = builtin_inline_p;
 
     load_body->is_entries           = ZALLOC_N(union iseq_inline_storage_entry, is_size);
                                       ibf_load_ci_entries(load, ci_entries_offset, ci_size, &load_body->call_data);
diff --git a/integer.rb b/integer.rb
index b1e288d..cc2e589 100644
--- a/integer.rb
+++ b/integer.rb
@@ -4,6 +4,7 @@ class Integer https://github.com/ruby/ruby/blob/trunk/integer.rb#L4
   #
   # Returns +true+ if +num+ has a zero value.
   def zero?
+    Primitive.attr! 'inline'
     Primitive.cexpr! 'int_zero_p(self)'
   end
 end
diff --git a/mjit_compile.c b/mjit_compile.c
index fe5c241..c4c31aa 100644
--- a/mjit_compile.c
+++ b/mjit_compile.c
@@ -374,7 +374,12 @@ inlinable_iseq_p(const struct rb_iseq_constant_body *body) https://github.com/ruby/ruby/blob/trunk/mjit_compile.c#L374
         //   * Do not require `cfp->sp` motion
         //   * Do not move `cfp->pc`
         //   * Do not read any `cfp->pc`
-        if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1)))
+        if (insn == BIN(invokebuiltin) || insn == BIN(opt_invokebuiltin_delegate) || insn == BIN(opt_invokebuiltin_delegate_leave)) {
+            // builtin insn's inlinability is handled by `Primitive.attr! 'inline'` per iseq
+            if (!body->builtin_inline_p)
+                return false;
+        }
+        else if (insn != BIN(leave) && insn_may_depend_on_sp_or_pc(insn, body->iseq_encoded + (pos + 1)))
             return false;
         // At this moment, `cfp->ep` in an inlined method is not working.
         switch (insn) {
diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
index 0155159..9d4b640 100644
--- a/tool/mk_builtin_loader.rb
+++ b/tool/mk_builtin_loader.rb
@@ -135,6 +135,12 @@ def collect_builtin base, tree, name, bs, inlines, params = nil https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L135
 
         if /(.+)\!\z/ =~ func_name
           case $1
+          when 'attr'
+            text = inline_text(argc, args.first)
+            if text != 'inline'
+              raise "Only 'inline' is allowed to be annotated (but got: '#{text}')"
+            end
+            break
           when 'cstmt'
             text = inline_text argc, args.first
 
diff --git a/tool/ruby_vm/views/mjit_compile.inc.erb b/tool/ruby_vm/views/mjit_compile.inc.erb
index 0d3678d..fa2e52e 100644
--- a/tool/ruby_vm/views/mjit_compile.inc.erb
+++ b/tool/ruby_vm/views/mjit_compile.inc.erb
@@ -63,12 +63,26 @@ switch (insn) { https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/mjit_compile.inc.erb#L63
       }
 %   when 'getinstancevariable', 'setinstancevariable'
 <%=   render 'mjit_compile_ivar', locals: { insn: insn } -%>
-%   when 'leave'
+%   when 'leave', 'opt_invokebuiltin_delegate_leave'
+    {
+%     # opt_invokebuiltin_delegate_leave also implements leave insn. We need to handle it here for inlining.
+%     if insn.name == 'opt_invokebuiltin_delegate_leave'
+      RB_BUILTIN bf = (RB_BUILTIN)operands[0];
+      rb_num_t index = (rb_num_t)operands[0];
+      fprintf(f, "{\n");
+      fprintf(f, "    VALUE val;\n");
+      fprintf(f, "    RB_BUILTIN bf = (RB_BUILTIN)0x%"PRIxVALUE";\n", operands[0]);
+      fprintf(f, "    rb_num_t index = (rb_num_t)0x%"PRIxVALUE";\n", operands[1]);
+      fprintf(f, <%= rstring2cstr(insn.expr.expr.lines.find { |l| l =~ / vm_invoke_builtin_delegate\(/ }).gsub("\n", '\n') %>);
+      fprintf(f, "    stack[0] = val;\n");
+      fprintf(f, "}\n");
+%     else
       if (b->stack_size != 1) {
           if (mjit_opts.warnings || mjit_opts.verbose)
               fprintf(stderr, "MJIT warning: Unexpected JIT stack_size on leave: %d\n", b->stack_size);
           status->success = false;
       }
+%     end
 %     # Skip vm_pop_frame for inlined call
       if (status->inlined_iseqs != NULL) { // the current ISeq is NOT being inlined
 %         # Cancel on interrupts to make leave insn leaf
@@ -84,6 +98,7 @@ switch (insn) { https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/mjit_compile.inc.erb#L98
       b->stack_size += <%= insn.call_attribute('sp_inc') %>;
       b->finish_p = TRUE;
       break;
+    }
 %   end
 %
 %   # Main insn implementation generated by insns.def
diff --git a/vm_core.h b/vm_core.h
index 4f122ca..fd49c24 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -418,6 +418,7 @@ struct rb_iseq_constant_body { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L418
     unsigned int stack_max; /* for stack overflow check */
 
     char catch_except_p; /* If a frame of this ISeq may catch exception, set TRUE */
+    bool builtin_inline_p; // This ISeq's builtin func is safe to be inlined by MJIT
 
 #if USE_MJIT
     /* The following fields are MJIT related info.  */
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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