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

ruby-changes:68864

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:14:58 +0900 (JST)
Subject: [ruby-changes:68864] a8f7eb2f35 (master): Polymorphic opt_send_without_block

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

From a8f7eb2f3566194ead1d5aa541cec32b968855cf Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Mon, 22 Mar 2021 20:12:34 -0400
Subject: Polymorphic opt_send_without_block

---
 bootstraptest/test_yjit.rb |  57 ++++++++++++++++
 yjit_codegen.c             | 159 +++++++++++++++++++++++----------------------
 yjit_core.c                |  12 ++++
 yjit_iface.c               |   2 +
 4 files changed, 151 insertions(+), 79 deletions(-)

diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 44817313b4..a209c85290 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -448,3 +448,60 @@ assert_equal '[42, :default]', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L448
     index(h, 1)
   ]
 }
+
+# A regression test for making sure cfp->sp is proper when
+# hitting stubs. See :stub-sp-flush:
+assert_equal 'ok', %q{
+  class D
+    def foo
+      Object.new
+    end
+  end
+
+  GC.stress = true
+  10.times do
+    D.new.foo
+    #    ^
+    #  This hits a stub with sp_offset > 0
+  end
+
+  :ok
+}
+
+# Test polymorphic callsite, cfunc -> iseq
+assert_equal '[Cfunc, Iseq]', %q{
+  public def call_itself
+    itself # the polymorphic callsite
+  end
+
+  class Cfunc; end
+
+  class Iseq
+    def itself
+      self
+    end
+  end
+
+  call_itself # cross threshold
+
+  [Cfunc.call_itself, Iseq.call_itself]
+}
+
+# Test polymorphic callsite, iseq -> cfunc
+assert_equal '[Iseq, Cfunc]', %q{
+  public def call_itself
+    itself # the polymorphic callsite
+  end
+
+  class Cfunc; end
+
+  class Iseq
+    def itself
+      self
+    end
+  end
+
+  call_itself # cross threshold
+
+  [Iseq.call_itself, Cfunc.call_itself]
+}
diff --git a/yjit_codegen.c b/yjit_codegen.c
index bd3ab5543c..b9d84a7570 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -96,7 +96,11 @@ jit_peek_at_stack(jitstate_t* jit, ctx_t* ctx, int n) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L96
 {
     RUBY_ASSERT(jit_at_current_insn(jit));
 
-    VALUE *sp = jit->ec->cfp->sp + ctx->sp_offset;
+    // Note: this does not account for ctx->sp_offset because
+    // this is only available when hitting a stub, and while
+    // hitting a stub, cfp->sp needs to be up to date in case
+    // codegen functions trigger GC. See :stub-sp-flush:.
+    VALUE *sp = jit->ec->cfp->sp;
 
     return *(sp - 1 - n);
 }
@@ -664,6 +668,7 @@ bool rb_iv_index_tbl_lookup(struct st_table *iv_index_tbl, ID id, struct rb_iv_i https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L668
 enum {
     GETIVAR_MAX_DEPTH = 10,       // up to 5 different classes, and embedded or not for each
     OPT_AREF_MAX_CHAIN_DEPTH = 2, // hashes and arrays
+    OSWB_MAX_DEPTH = 5,           // up to 5 different classes
 };
 
 static codegen_status_t
@@ -1346,15 +1351,13 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1351
     const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
 
     // If the function expects a Ruby array of arguments
-    if (cfunc->argc < 0 && cfunc->argc != -1)
-    {
+    if (cfunc->argc < 0 && cfunc->argc != -1) {
         GEN_COUNTER_INC(cb, oswb_cfunc_ruby_array_varg);
         return YJIT_CANT_COMPILE;
     }
 
     // If the argument count doesn't match
-    if (cfunc->argc >= 0 && cfunc->argc != argc)
-    {
+    if (cfunc->argc >= 0 && cfunc->argc != argc) {
         GEN_COUNTER_INC(cb, oswb_cfunc_argc_mismatch);
         return YJIT_CANT_COMPILE;
     }
@@ -1373,38 +1376,6 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1376
 
     // Points to the receiver operand on the stack
     x86opnd_t recv = ctx_stack_opnd(ctx, argc);
-    mov(cb, REG0, recv);
-
-    // Callee method ID
-    //ID mid = vm_ci_mid(cd->ci);
-    //printf("JITting call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc);
-    //print_str(cb, "");
-    //print_str(cb, "calling CFUNC:");
-    //print_str(cb, rb_id2name(mid));
-    //print_str(cb, "recv");
-    //print_ptr(cb, recv);
-
-    // Check that the receiver is a heap object
-    {
-        uint8_t *receiver_not_heap = COUNTED_EXIT(side_exit, oswb_se_receiver_not_heap);
-        test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
-        jnz_ptr(cb, receiver_not_heap);
-        cmp(cb, REG0, imm_opnd(Qfalse));
-        je_ptr(cb, receiver_not_heap);
-        cmp(cb, REG0, imm_opnd(Qnil));
-        je_ptr(cb, receiver_not_heap);
-    }
-
-    // Pointer to the klass field of the receiver &(recv->klass)
-    x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
-
-    // FIXME: This leaks when st_insert raises NoMemoryError
-    assume_method_lookup_stable(cd->cc->klass, cme, jit->block);
-
-    // Bail if receiver class is different from compile-time call cache class
-    jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
-    cmp(cb, klass_opnd, REG1);
-    jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
 
     // Store incremented PC into current control frame in case callee raises.
     mov(cb, REG0, const_ptr_opnd(jit->pc + insn_len(BIN(opt_send_without_block))));
@@ -1545,9 +1516,14 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1516
         );
     }
 
+    // TODO: gen_oswb_iseq() jumps to the next instruction with ctx->sp_offset == 0
+    // after the call, while this does not. This difference prevents
+    // the two call types from sharing the same successor.
+
     // Jump (fall through) to the call continuation block
     // We do this to end the current block after the call
-    blockid_t cont_block = { jit->iseq, jit_next_idx(jit) };
+    blockid_t cont_block = { jit->iseq, jit_next_insn_idx(jit) };
+    ctx->chain_depth = 0;
     gen_direct_jump(
         ctx,
         cont_block
@@ -1609,35 +1585,6 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1585
 
     // Points to the receiver operand on the stack
     x86opnd_t recv = ctx_stack_opnd(ctx, argc);
-    mov(cb, REG0, recv);
-
-    // Callee method ID
-    //ID mid = vm_ci_mid(cd->ci);
-    //printf("JITting call to Ruby function \"%s\", argc: %d\n", rb_id2name(mid), argc);
-    //print_str(cb, "");
-    //print_str(cb, "recv");
-    //print_ptr(cb, recv);
-
-    // Check that the receiver is a heap object
-    {
-        uint8_t *receiver_not_heap = COUNTED_EXIT(side_exit, oswb_se_receiver_not_heap);
-        test(cb, REG0, imm_opnd(RUBY_IMMEDIATE_MASK));
-        jnz_ptr(cb, receiver_not_heap);
-        cmp(cb, REG0, imm_opnd(Qfalse));
-        je_ptr(cb, receiver_not_heap);
-        cmp(cb, REG0, imm_opnd(Qnil));
-        je_ptr(cb, receiver_not_heap);
-    }
-
-    // Pointer to the klass field of the receiver &(recv->klass)
-    x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
-
-    assume_method_lookup_stable(cd->cc->klass, cme, jit->block);
-
-    // Bail if receiver class is different from compile-time call cache class
-    jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
-    cmp(cb, klass_opnd, REG1);
-    jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
 
     if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
         // Generate ancestry guard for protected callee.
@@ -1717,6 +1664,7 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1664
     ctx_stack_pop(&return_ctx, argc + 1);
     ctx_stack_push(&return_ctx, T_NONE);
     return_ctx.sp_offset = 0;
+    return_ctx.chain_depth = 0;
 
     // Write the JIT return address on the callee frame
     gen_branch(
@@ -1749,39 +1697,92 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1697
     // Relevant definitions:
     // rb_execution_context_t       : vm_core.h
     // invoker, cfunc logic         : method.h, vm_method.c
+    // rb_callinfo                  : vm_callinfo.h
     // rb_callable_method_entry_t   : method.h
     // vm_call_cfunc_with_frame     : vm_insnhelper.c
-    // rb_callcache                 : vm_callinfo.h
 
-    struct rb_call_data * cd = (struct rb_call_data *)jit_get_arg(jit, 0);
+    struct rb_call_data *cd = (struct rb_call_data *)jit_get_arg(jit, 0);
+    const struct rb_callinfo *ci = cd->ci; // info about the call site
+
     int32_t argc = (int32_t)vm_ci_argc(cd->ci);
+    ID mid = vm_ci_mid(cd->ci);
 
     // Don't JIT calls with keyword splat
-    if (vm_ci_flag(cd->ci) & VM_CALL_KW_SPLAT) {
+    if (vm_ci_flag(ci) & VM_CALL_KW_SPLAT) {
         GEN_COUNTER_INC(cb, oswb_kw_splat);
         return YJIT_CANT_COMPILE;
     }
 
     // Don't JIT calls that aren't simple
-    if (!(vm_ci_flag(cd->ci) & VM_CALL_ARGS_SIMPLE)) {
+    if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)) {
         GEN_COUNTER_INC(cb, oswb_callsite_not_simple);
         return YJIT_CANT_COMPILE;
     }
 
-    // Don't JIT if the inline cache is not set
-    if (!cd->cc || !cd->cc->klass) {
-        GEN_COUNTER_INC(cb, oswb_ic_empty);
-        return YJIT_CANT_COMPILE;
+    // Defer compilation so we can specialize on class of receiver
+    if (!jit_at_current_insn(jit)) {
+        defer_compilation(jit->block, jit->insn_idx, ctx);
+        return YJIT_END_BLOCK;
     }
 
-    const rb_callable_method_entry_t *cme = vm_cc_cme(cd->cc);
+    VALUE comptime_recv = jit_peek_at_stack(jit, ctx, argc);
+    //rp(comptime_recv);
+    //fprintf(stderr, "offset=%d\n", (int)ctx->sp_offset);
+    VALUE comptime_recv_klass = CLASS_OF(comptime_recv);
+
+    // Can't guard for for these classes because some of they are sometimes
+    // immediate (special const). Can remove this once jit_guard_known_cla (... truncated)

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

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