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

ruby-changes:69018

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:20:16 +0900 (JST)
Subject: [ruby-changes:69018] 0758115d11 (master): Implement send with blocks

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

From 0758115d112a1ff452876d3689d20e84d5ff1e37 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Tue, 4 May 2021 12:35:51 -0400
Subject: Implement send with blocks

* Implement send with blocks

Not that much extra work compared to `opt_send_without_block`.
Moved the stack over flow check because it could've exited after changes
are made to cfp.

* rename oswb counters

* Might as well implement sending block to cfuncs

* Disable sending blocks to cfuncs for now

* Reconstruct interpreter sp before calling into cfuncs

In case the callee cfunc calls a method or delegates to a block.
This also has the side benefit of letting call sites that sometimes are
iseq calls and sometimes cfunc call share the same successor.

* only sync with interpreter sp when passing a block


Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...>
Co-authored-by: Aaron Patterson <aaron.patterson@s...>
---
 bootstraptest/test_yjit.rb |  24 ++++++
 yjit.rb                    |   2 +-
 yjit_codegen.c             | 199 +++++++++++++++++++++++++++++----------------
 yjit_codegen.h             |   3 +
 yjit_iface.c               |   1 +
 yjit_iface.h               |  52 ++++++------
 6 files changed, 184 insertions(+), 97 deletions(-)

diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 6ac31f8134..b8ae5a989c 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -799,3 +799,27 @@ assert_equal 'raised', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L799
     :raised
   end
 }
+
+# test calling Ruby method with a block
+assert_equal '[1, 2, 42]', %q{
+def thing(a, b)
+  [a, b, yield]
+end
+
+def use
+  thing(1,2) { 42 }
+end
+
+use
+use
+}
+
+# test calling C method with a block
+assert_equal '[42, 42]', %q{
+def use(array, initial)
+  array.reduce(initial) { |a, b| a + b }
+end
+
+use([], 0)
+[use([2, 2], 38), use([14, 14, 14], 0)]
+}
diff --git a/yjit.rb b/yjit.rb
index 8203051b81..cd3ad4d652 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -82,7 +82,7 @@ module YJIT https://github.com/ruby/ruby/blob/trunk/yjit.rb#L82
       $stderr.puts("Number of bindings allocated: %d\n" % counters[:binding_allocations])
       $stderr.puts("Number of locals modified through binding: %d\n" % counters[:binding_set])
 
-      print_counters(counters, prefix: 'oswb_', prompt: 'opt_send_without_block exit reasons: ')
+      print_counters(counters, prefix: 'send_', prompt: 'method call exit reasons: ')
       print_counters(counters, prefix: 'leave_', prompt: 'leave exit reasons: ')
       print_counters(counters, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons:')
       print_counters(counters, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:')
diff --git a/yjit_codegen.c b/yjit_codegen.c
index 62be5e3d2e..dfcf198978 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -46,7 +46,7 @@ jit_print_loc(jitstate_t* jit, const char* msg) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L46
 static int
 jit_get_opcode(jitstate_t* jit)
 {
-    return yjit_opcode_at_pc(jit->iseq, jit->pc);
+    return jit->opcode;
 }
 
 // Get the index of the next instruction
@@ -362,11 +362,9 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L362
 
     // Initialize a JIT state object
     jitstate_t jit = {
-        block,
-        iseq,
-        0,
-        0,
-        ec
+        .block = block,
+        .iseq = iseq,
+        .ec = ec
     };
 
     // Mark the start position of the block
@@ -389,6 +387,7 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L387
         // Set the current instruction
         jit.insn_idx = insn_idx;
         jit.pc = pc;
+        jit.opcode = opcode;
 
         // Lookup the codegen function for this instruction
         codegen_fn gen_fn = gen_fns[opcode];
@@ -831,7 +830,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#L830
 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
+    SEND_MAX_DEPTH = 5,           // up to 5 different classes
 };
 
 // Codegen for setting an instance variable.
@@ -1664,29 +1663,29 @@ jit_protected_callee_ancestry_guard(jitstate_t *jit, codeblock_t *cb, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1663
     call_ptr(cb, REG0, (void *)&rb_obj_is_kind_of);
     yjit_load_regs(cb);
     test(cb, RAX, RAX);
-    jz_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_protected_check_failed));
+    jz_ptr(cb, COUNTED_EXIT(side_exit, send_se_protected_check_failed));
 }
 
 static codegen_status_t
-gen_oswb_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const rb_callable_method_entry_t *cme, int32_t argc)
+gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const rb_callable_method_entry_t *cme, rb_iseq_t *block, const int32_t argc)
 {
     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) {
-        GEN_COUNTER_INC(cb, oswb_cfunc_ruby_array_varg);
+        GEN_COUNTER_INC(cb, send_cfunc_ruby_array_varg);
         return YJIT_CANT_COMPILE;
     }
 
     // If the argument count doesn't match
     if (cfunc->argc >= 0 && cfunc->argc != argc) {
-        GEN_COUNTER_INC(cb, oswb_cfunc_argc_mismatch);
+        GEN_COUNTER_INC(cb, send_cfunc_argc_mismatch);
         return YJIT_CANT_COMPILE;
     }
 
     // Don't JIT functions that need C stack arguments for now
     if (argc + 1 > NUM_C_ARG_REGS) {
-        GEN_COUNTER_INC(cb, oswb_cfunc_toomany_args);
+        GEN_COUNTER_INC(cb, send_cfunc_toomany_args);
         return YJIT_CANT_COMPILE;
     }
 
@@ -1699,42 +1698,62 @@ gen_oswb_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1698
     //print_str(cb, "recv");
     //print_ptr(cb, recv);
 
+    // If this function needs a Ruby stack frame
+    const bool push_frame = cfunc_needs_frame(cfunc);
+
     // Create a size-exit to fall back to the interpreter
     uint8_t *side_exit = yjit_side_exit(jit, ctx);
 
     // Check for interrupts
     yjit_check_ints(cb, side_exit);
 
+    if (push_frame) {
+        // Stack overflow check
+        // #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
+        // REG_CFP <= REG_SP + 4 * sizeof(VALUE) + sizeof(rb_control_frame_t)
+        lea(cb, REG0, ctx_sp_opnd(ctx, sizeof(VALUE) * 4 + sizeof(rb_control_frame_t)));
+        cmp(cb, REG_CFP, REG0);
+        jle_ptr(cb, COUNTED_EXIT(side_exit, send_se_cf_overflow));
+    }
+
     // Points to the receiver operand on the stack
     x86opnd_t recv = ctx_stack_opnd(ctx, argc);
 
     // 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))));
+    mov(cb, REG0, const_ptr_opnd(jit->pc + insn_len(jit->opcode)));
     mov(cb, mem_opnd(64, REG_CFP, offsetof(rb_control_frame_t, pc)), REG0);
 
-    // If this function needs a Ruby stack frame
-    if (cfunc_needs_frame(cfunc)) {
-        // Stack overflow check
-        // #define CHECK_VM_STACK_OVERFLOW0(cfp, sp, margin)
-        // REG_CFP <= REG_SP + 4 * sizeof(VALUE) + sizeof(rb_control_frame_t)
-        lea(cb, REG0, ctx_sp_opnd(ctx, sizeof(VALUE) * 4 + sizeof(rb_control_frame_t)));
-        cmp(cb, REG_CFP, REG0);
-        jle_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cf_overflow));
+    if (push_frame) {
+        if (block) {
+            // Change cfp->block_code in the current frame. See vm_caller_setup_arg_block().
+            // VM_CFP_TO_CAPTURED_BLCOK does &cfp->self, rb_captured_block->code.iseq aliases
+            // with cfp->block_code.
+            jit_mov_gc_ptr(jit, cb, REG0, (VALUE)block);
+            mov(cb, member_opnd(REG_CFP, rb_control_frame_t, block_code), REG0);
+        }
 
         // Increment the stack pointer by 3 (in the callee)
         // sp += 3
         lea(cb, REG0, ctx_sp_opnd(ctx, sizeof(VALUE) * 3));
 
+        // Write method entry at sp[-3]
+        // sp[-3] = me;
         // Put compile time cme into REG1. It's assumed to be valid because we are notified when
         // any cme we depend on become outdated. See rb_yjit_method_lookup_change().
         jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cme);
-        // Write method entry at sp[-3]
-        // sp[-3] = me;
         mov(cb, mem_opnd(64, REG0, 8 * -3), REG1);
 
         // Write block handler at sp[-2]
         // sp[-2] = block_handler;
-        mov(cb, mem_opnd(64, REG0, 8 * -2), imm_opnd(VM_BLOCK_HANDLER_NONE));
+        if (block) {
+            // reg1 = VM_BH_FROM_ISEQ_BLOCK(VM_CFP_TO_CAPTURED_BLOCK(reg_cfp));
+            lea(cb, REG1, member_opnd(REG_CFP, rb_control_frame_t, self));
+            or(cb, REG1, imm_opnd(1));
+            mov(cb, mem_opnd(64, REG0, 8 * -2), REG1);
+        }
+        else {
+            mov(cb, mem_opnd(64, REG0, 8 * -2), imm_opnd(VM_BLOCK_HANDLER_NONE));
+        }
 
         // Write env flags at sp[-1]
         // sp[-1] = frame_type;
@@ -1776,22 +1795,33 @@ gen_oswb_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1795
         yjit_save_regs(cb);
 
         // Call check_cfunc_dispatch
-        mov(cb, RDI, recv);
-        jit_mov_gc_ptr(jit, cb, RSI, (VALUE)ci);
-        mov(cb, RDX, const_ptr_opnd((void *)cfunc->func));
-        jit_mov_gc_ptr(jit, cb, RCX, (VALUE)cme);
+        mov(cb, C_ARG_REGS[0], recv);
+        jit_mov_gc_ptr(jit, cb, C_ARG_REGS[1], (VALUE)ci);
+        mov(cb, C_ARG_REGS[2 (... truncated)

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

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