ruby-changes:68997
From: John <ko1@a...>
Date: Thu, 21 Oct 2021 08:19:37 +0900 (JST)
Subject: [ruby-changes:68997] 7d252186fe (master): Simplify known class check for singletons
https://git.ruby-lang.org/ruby.git/commit/?id=7d252186fe From 7d252186fe803aa5e1fa37c953609266a2d8ba1d Mon Sep 17 00:00:00 2001 From: John Hawthorn <john@h...> Date: Thu, 17 Jun 2021 11:29:28 -0700 Subject: Simplify known class check for singletons Singleton classes should only ever be attached to one object. This means that checking for the object should be the same as checking for the class. This should be slightly faster by avoiding one memory acccess as well as allowing us to skip checking if the receiver is a heap object. This will be most common for calling class methods. --- bootstraptest/test_yjit.rb | 31 +++++++++++++++++++++++++++++++ yjit_codegen.c | 35 +++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 129903bf9c..e15728fd98 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -1153,3 +1153,34 @@ assert_equal '7', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L1153 foo(5,2) foo(5,2) } + +# Call to object with singleton +assert_equal '123', %q{ + obj = Object.new + def obj.foo + 123 + end + + def foo(obj) + obj.foo() + end + + foo(obj) + foo(obj) +} + +# Call to singleton class +assert_equal '123', %q{ + class Foo + def self.foo + 123 + end + end + + def foo(obj) + obj.foo() + end + + foo(Foo) + foo(Foo) +} diff --git a/yjit_codegen.c b/yjit_codegen.c index 3a6f6c5cdc..60d2338115 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -2172,6 +2172,7 @@ static bool https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2172 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) { val_type_t val_type = ctx_get_opnd_type(ctx, insn_opnd); + bool singleton_klass = FL_TEST(known_klass, FL_SINGLETON); if (known_klass == rb_cNilClass) { if (val_type.type != ETYPE_NIL) { @@ -2190,7 +2191,6 @@ 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#L2191 ctx_set_opnd_type(ctx, insn_opnd, TYPE_TRUE); } - } else if (known_klass == rb_cFalseClass) { if (val_type.type != ETYPE_FALSE) { @@ -2202,15 +2202,26 @@ 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#L2202 ctx_set_opnd_type(ctx, insn_opnd, TYPE_FALSE); } } - else { - // Can't guard for for these classes because some of they are sometimes immediate (special const). - // Can remove this by adding appropriate dynamic checks. - if (known_klass == rb_cInteger || + else if (known_klass == rb_cInteger || known_klass == rb_cSymbol || known_klass == rb_cFloat) { - return false; - } - + // Can't guard for for these classes because some of they are sometimes + // immediate (special const). Can remove this by adding appropriate + // dynamic checks. + return false; + } + else if (singleton_klass) { + // Singleton classes are attached to one specific object, so we can + // avoid one memory access (and potentially the is_heap check) by + // looking for the expected object directly. + ADD_COMMENT(cb, "guard known object with singleton class"); + VALUE known_obj = rb_attr_get(known_klass, id__attached__); + // TODO: jit_mov_gc_ptr keeps a strong reference, which leaks the object. + jit_mov_gc_ptr(jit, cb, REG1, known_obj); + cmp(cb, REG0, REG1); + jit_chain_guard(JCC_JNE, jit, ctx, max_chain_depth, side_exit); + } + else { // Check that the receiver is a heap object // Note: if we get here, the class doesn't have immediate instances. if (!val_type.is_heap) { @@ -2234,14 +2245,6 @@ 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#L2245 jit_chain_guard(JCC_JNE, jit, ctx, max_chain_depth, side_exit); } - // Pointer to the klass field of the receiver &(recv->klass) - x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass)); - - // Bail if receiver class is different from known_klass - // TODO: jit_mov_gc_ptr keeps a strong reference, which leaks the class. - jit_mov_gc_ptr(jit, cb, REG1, known_klass); - cmp(cb, klass_opnd, REG1); - jit_chain_guard(JCC_JNE, jit, ctx, max_chain_depth, side_exit); return true; } -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/