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

ruby-changes:69829

From: Adam <ko1@a...>
Date: Sat, 20 Nov 2021 06:58:01 +0900 (JST)
Subject: [ruby-changes:69829] 73388aff5e (master): Add YJIT codegen for objtostring (#5149)

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

From 73388aff5e2a6c6f5520d4ccc7843990fb15520e Mon Sep 17 00:00:00 2001
From: Adam Hess <HParker@g...>
Date: Fri, 19 Nov 2021 13:57:09 -0800
Subject: Add YJIT codegen for objtostring (#5149)

This is the minimal correct objtostring implementation in YJIT.
For correctness, it is important that to_string not get called on strings or subclasses of string.
There is a new test for this behavior.

A follow up should implement an optimized version for other types as performed in `vm_objtostring`.

Co-authored-by: John Hawthorn <jhawthorn@g...>

Co-authored-by: John Hawthorn <jhawthorn@g...>
---
 bootstraptest/test_yjit.rb | 17 +++++++++++++++++
 test/ruby/test_yjit.rb     | 12 ++++++------
 yjit_codegen.c             | 25 +++++++++++++++++++++++++
 3 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 33e9a35462c..f1900f9f3f8 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1214,6 +1214,23 @@ assert_equal 'foo123', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L1214
   make_str("foo", 123)
 }
 
+# test string interpolation with overridden to_s
+assert_equal 'foo', %q{
+  class String
+    def to_s
+      "bad"
+    end
+  end
+
+  def make_str(foo)
+    "#{foo}"
+  end
+
+  make_str("foo")
+  make_str("foo")
+}
+
+
 # test invokebuiltin as used in struct assignment
 assert_equal '123', %q{
   def foo(obj)
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
index 2b7d5523dfe..a571432ccdc 100644
--- a/test/ruby/test_yjit.rb
+++ b/test/ruby/test_yjit.rb
@@ -215,7 +215,7 @@ class TestYJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_yjit.rb#L215
 
   def test_compile_tostring
     assert_no_exits('"i am a string #{true}"')
-  end if false # Until objtostring supported
+  end
 
   def test_compile_opt_aset
     assert_compiles('[1,2,3][2] = 4', insns: %i[opt_aset])
@@ -240,7 +240,7 @@ class TestYJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_yjit.rb#L240
 
   def test_compile_regexp
     assert_no_exits('/#{true}/')
-  end if false # Until objtostring supported
+  end
 
   def test_getlocal_with_level
     assert_compiles(<<~RUBY, insns: %i[getlocal opt_plus], result: [[7]])
@@ -377,7 +377,7 @@ class TestYJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_yjit.rb#L377
   end
 
   def test_string_interpolation
-    assert_compiles(<<~'RUBY', insns: %i[checktype concatstrings], result: "foobar", min_calls: 2)
+    assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", min_calls: 2)
       def make_str(foo, bar)
         "#{foo}#{bar}"
       end
@@ -385,17 +385,17 @@ class TestYJIT < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_yjit.rb#L385
       make_str("foo", "bar")
       make_str("foo", "bar")
     RUBY
-  end if false # Until objtostring supported
+  end
 
   def test_string_interpolation_cast
-    assert_compiles(<<~'RUBY', insns: %i[checktype concatstrings tostring], result: "123")
+    assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "123")
       def make_str(foo, bar)
         "#{foo}#{bar}"
       end
 
       make_str(1, 23)
     RUBY
-  end if false # Until objtostring supported
+  end
 
   def test_checkkeyword
     assert_compiles(<<~'RUBY', insns: %i[checkkeyword], result: [2, 5])
diff --git a/yjit_codegen.c b/yjit_codegen.c
index f53aea50dba..60ddf489e10 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -4307,6 +4307,30 @@ gen_anytostring(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L4307
     return YJIT_KEEP_COMPILING;
 }
 
+static codegen_status_t
+gen_objtostring(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb)
+{
+    if (!jit_at_current_insn(jit)) {
+        defer_compilation(jit, ctx);
+        return YJIT_END_BLOCK;
+    }
+
+    x86opnd_t recv = ctx_stack_opnd(ctx, 0);
+    VALUE comptime_recv = jit_peek_at_stack(jit, ctx, 0);
+
+    if (RB_TYPE_P(comptime_recv, T_STRING)) {
+        uint8_t *side_exit = yjit_side_exit(jit, ctx);
+
+        mov(cb, REG0, recv);
+        jit_guard_known_klass(jit, ctx, CLASS_OF(comptime_recv), OPND_STACK(0), comptime_recv, SEND_MAX_DEPTH, side_exit);
+        // No work needed. The string value is already on the top of the stack.
+        return YJIT_KEEP_COMPILING;
+    } else {
+        struct rb_call_data *cd = (struct rb_call_data *)jit_get_arg(jit, 0);
+        return gen_send_general(jit, ctx, cd, NULL);
+    }
+}
+
 static codegen_status_t
 gen_toregexp(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb)
 {
@@ -4832,6 +4856,7 @@ yjit_init_codegen(void) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L4856
     yjit_reg_op(BIN(getglobal), gen_getglobal);
     yjit_reg_op(BIN(setglobal), gen_setglobal);
     yjit_reg_op(BIN(anytostring), gen_anytostring);
+    yjit_reg_op(BIN(objtostring), gen_objtostring);
     yjit_reg_op(BIN(toregexp), gen_toregexp);
     yjit_reg_op(BIN(getspecial), gen_getspecial);
     yjit_reg_op(BIN(getclassvariable), gen_getclassvariable);
-- 
cgit v1.2.1


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

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