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

ruby-changes:69009

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:16:05 +0900 (JST)
Subject: [ruby-changes:69009] 4ea2e753f6 (master): YJIT: implement calls to ivar getter methods

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

From 4ea2e753f65e1e7f4c62330abda256b3b56e72d4 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Thu, 1 Apr 2021 14:31:57 -0400
Subject: YJIT: implement calls to ivar getter methods

---
 bootstraptest/test_yjit.rb |  73 ++++++++++++++++++++++++++++++
 yjit_codegen.c             | 109 +++++++++++++++++++++++++++++----------------
 yjit_iface.h               |   4 +-
 3 files changed, 146 insertions(+), 40 deletions(-)

diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index a209c85290..858e3906d0 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -505,3 +505,76 @@ assert_equal '[Iseq, Cfunc]', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L505
 
   [Iseq.call_itself, Cfunc.call_itself]
 }
+
+# attr_reader method
+assert_equal '[100, 299]', %q{
+  class A
+    attr_reader :foo
+
+    def initialize
+      @foo = 100
+    end
+
+    # Make it extended
+    def fill!
+      @bar = @jojo = @as = @sdfsdf = @foo = 299
+    end
+  end
+
+  def bar(ins)
+    ins.foo
+  end
+
+  ins = A.new
+  oth = A.new
+  oth.fill!
+
+  bar(ins)
+  bar(oth)
+
+  [bar(ins), bar(oth)]
+}
+
+# get ivar on String
+assert_equal '[nil, nil, 42, 42]', %q{
+  # @foo to exercise the getinstancevariable instruction
+  public def get_foo
+    @foo
+  end
+
+  get_foo
+  get_foo # compile it for the top level object
+
+  class String
+    attr_reader :foo
+  end
+
+  def run
+    str = String.new
+
+    getter = str.foo
+    insn = str.get_foo
+
+    str.instance_variable_set(:@foo, 42)
+
+    [getter, insn, str.foo, str.get_foo]
+  end
+
+  run
+  run
+}
+
+# splatting an empty array on a getter
+assert_equal '42', %q{
+  @foo = 42
+  module Kernel
+    attr_reader :foo
+  end
+
+  def run
+    foo(*[])
+  end
+
+  run
+  run
+}
diff --git a/yjit_codegen.c b/yjit_codegen.c
index b91af9bba7..57efe6d3a8 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -111,6 +111,8 @@ jit_peek_at_self(jitstate_t *jit, ctx_t *ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L111
     return jit->ec->cfp->self;
 }
 
+static bool jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, insn_opnd_t insn_opnd, const int max_chain_depth, uint8_t *side_exit);
+
 // Save YJIT registers prior to a C call
 static void
 yjit_save_regs(codeblock_t* cb)
@@ -371,6 +373,9 @@ yjit_gen_block(ctx_t *ctx, block_t *block, rb_execution_context_t *ec) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L373
         // If we can't compile this instruction
         // exit to the interpreter and stop compiling
         if (status == YJIT_CANT_COMPILE) {
+            // TODO: if the codegen funcion makes changes to ctx and then return YJIT_CANT_COMPILE,
+            // the exit this generates would be wrong. We could save a copy of the entry context
+            // and assert that ctx is the same here.
             yjit_gen_exit(&jit, ctx, cb);
             break;
         }
@@ -717,53 +722,42 @@ enum { https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L722
     OSWB_MAX_DEPTH = 5,           // up to 5 different classes
 };
 
+// Codegen for getting an instance variable.
+// Preconditions:
+//   - receiver is in REG0
+//   - receiver has the same class as CLASS_OF(comptime_receiver)
+//   - ctx has not been modified since entry to the codegen of the instruction being compiled
 static codegen_status_t
-gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
+gen_get_ivar(jitstate_t *jit, ctx_t *ctx, const int max_chain_depth, VALUE comptime_receiver, ID ivar_name, bool pop_receiver, uint8_t *side_exit)
 {
-    // Defer compilation so we can specialize a runtime `self`
-    if (!jit_at_current_insn(jit)) {
-        defer_compilation(jit->block, jit->insn_idx, ctx);
-        return YJIT_END_BLOCK;
-    }
-
-    // Specialize base on the compile time self
-    VALUE self_val = jit_peek_at_self(jit, ctx);
-    VALUE self_klass = rb_class_of(self_val);
-
-    // Create a size-exit to fall back to the interpreter
-    uint8_t *side_exit = yjit_side_exit(jit, ctx);
+    VALUE comptime_val_klass = CLASS_OF(comptime_receiver);
+    const ctx_t starting_context = *ctx; // make a copy for use with jit_chain_guard
 
     // If the class uses the default allocator, instances should all be T_OBJECT
     // NOTE: This assumes nobody changes the allocator of the class after allocation.
     //       Eventually, we can encode whether an object is T_OBJECT or not
     //       inside object shapes.
-    if (rb_get_alloc_func(self_klass) != rb_class_allocate_instance) {
+    if (rb_get_alloc_func(comptime_val_klass) != rb_class_allocate_instance) {
+        GEN_COUNTER_INC(cb, getivar_not_object);
         return YJIT_CANT_COMPILE;
     }
-    RUBY_ASSERT(BUILTIN_TYPE(self_val) == T_OBJECT); // because we checked the allocator
+    RUBY_ASSERT(BUILTIN_TYPE(comptime_receiver) == T_OBJECT); // because we checked the allocator
 
-    ID id = (ID)jit_get_arg(jit, 0);
+    // ID for the name of the ivar
+    ID id = ivar_name;
     struct rb_iv_index_tbl_entry *ent;
-    struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(self_val);
+    struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(comptime_receiver);
 
     // Lookup index for the ivar the instruction loads
     if (iv_index_tbl && rb_iv_index_tbl_lookup(iv_index_tbl, id, &ent)) {
         uint32_t ivar_index = ent->index;
 
-        // Load self from CFP
-        mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
-
-        guard_self_is_heap(cb, REG0, COUNTED_EXIT(side_exit, getivar_se_self_not_heap), ctx);
-
-        // Guard that self has a known class
-        x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass));
-        mov(cb, REG1, klass_opnd);
-        x86opnd_t serial_opnd = mem_opnd(64, REG1, offsetof(struct RClass, class_serial));
-        cmp(cb, serial_opnd, imm_opnd(RCLASS_SERIAL(self_klass)));
-        jit_chain_guard(JCC_JNE, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
+        if (pop_receiver) {
+            (void)ctx_stack_pop(ctx, 1);
+        }
 
-        // Compile time self is embedded and the ivar index is within the object
-        if (RB_FL_TEST_RAW(self_val, ROBJECT_EMBED) && ivar_index < ROBJECT_EMBED_LEN_MAX) {
+        // Compile time self is embedded and the ivar index lands within the object
+        if (RB_FL_TEST_RAW(comptime_receiver, ROBJECT_EMBED) && ivar_index < ROBJECT_EMBED_LEN_MAX) {
             // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
 
             // Guard that self is embedded
@@ -771,13 +765,14 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L765
             ADD_COMMENT(cb, "guard embedded getivar");
             x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
             test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
-            jit_chain_guard(JCC_JZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
+            jit_chain_guard(JCC_JZ, jit, &starting_context, max_chain_depth, side_exit);
 
             // Load the variable
             x86opnd_t ivar_opnd = mem_opnd(64, REG0, offsetof(struct RObject, as.ary) + ivar_index * SIZEOF_VALUE);
             mov(cb, REG1, ivar_opnd);
 
             // Guard that the variable is not Qundef
+            // TODO: use cmov to push Qnil in this case
             cmp(cb, REG1, imm_opnd(Qundef));
             je_ptr(cb, COUNTED_EXIT(side_exit, getivar_undef));
 
@@ -786,14 +781,14 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L781
             mov(cb, out_opnd, REG1);
         }
         else {
-            // Compile time self is *not* embeded.
+            // Compile value is *not* embeded.
 
-            // Guard that self is *not* embedded
+            // Guard that value is *not* embedded
             // See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
             ADD_COMMENT(cb, "guard extended getivar");
             x86opnd_t flags_opnd = member_opnd(REG0, struct RBasic, flags);
             test(cb, flags_opnd, imm_opnd(ROBJECT_EMBED));
-            jit_chain_guard(JCC_JNZ, jit, ctx, GETIVAR_MAX_DEPTH, side_exit);
+            jit_chain_guard(JCC_JNZ, jit, &starting_context, max_chain_depth, side_exit);
 
             // check that the extended table is big enough
             if (ivar_index >= ROBJECT_EMBED_LEN_MAX + 1) {
@@ -825,9 +820,36 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L820
         return YJIT_END_BLOCK;
     }
 
+    GEN_COUNTER_INC(cb, getivar_name_not_mapped);
     return YJIT_CANT_COMPILE;
 }
 
+static codegen_status_t
+gen_getinstancevariable(jitstate_t *jit, ctx_t *ctx)
+{
+    // Defer compilation so we can specialize on a runtime `self`
+    if (!jit_at_current_insn(jit)) {
+        defer_compilation(jit->block, jit->insn_idx, ctx);
+        return YJIT_END_BLOCK;
+    }
+
+    ID ivar_name = (ID)jit_get_arg(jit, 0);
+
+    VALUE comptime_val = jit_peek_at_self(jit, ctx);
+    VALUE comptime_val_klass = CLASS_OF(comptime_val);
+
+    // Generate a side exit
+    uint8_t *side_exit = yjit_side_exit(jit, ctx);
+
+    // Guard that the receiver has the same class as the one from compile time.
+    mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, self));
+    guard_self_is_heap(cb, REG0, side_exit, ctx);
+
+    jit_guard_known_klass(jit, ctx, comptime_val_klass, OPND_SELF, GETIVAR_MAX_DEPTH, side_exit);
+
+    return gen_get_ivar(jit, ctx, GETIVAR_MAX_DEPTH, comptime_val, ivar_name, false, side_exit);
+}
+
 static codegen_status_t
 gen_setinstancevariable(jitstate_t* jit, ctx_t* ctx)
 {
@@ -1413,8 +1435,8 @@ jit_guard_known_klass(jitstate_t *jit, ctx_t* ctx, VALUE known_klass, insn_opnd_ https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1435
     // Pointer to the klass field of the receiver &(recv->klass)
     x86opnd_t klass_opn (... truncated)

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

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