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

ruby-changes:68992

From: John <ko1@a...>
Date: Thu, 21 Oct 2021 08:19:36 +0900 (JST)
Subject: [ruby-changes:68992] e4bf905a28 (master): Use an st_table for optimized method codegen

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

From e4bf905a2863a8ff1e3455e462dff0c8657aaa8e Mon Sep 17 00:00:00 2001
From: John Hawthorn <john@h...>
Date: Fri, 18 Jun 2021 13:30:07 -0700
Subject: Use an st_table for optimized method codegen

We recently added the ability to optimize a known cfunc with custom code
generation for it.

Previously we performed this lookup with a switch statement on the
address of the c function being called. This commit swaps that out for a
hash lookup on the method definition. For now I've kept this limited
this to cfuncs, but it wouldn't take significant changes to make this
work for other method types.

This implemenation is similar to how the interpreter keeps track of
which BOPs (basic operations) are redefined

This has a few advantages:
- Doesn't the C function's symbol to be exported (they're often static)
- This could support VM_METHOD_TYPE_OPTIMIZED in the future.
- This could support VM_METHOD_TYPE_ISEQ in the future. Kernel#class
  would be a good candidate for this since to yjit it will just be a
  constant push as we already know the class through BBV.
- Slightly cleaner to declare
- Less tightly coupled to each method's implementation

And a couple minor trade-offs:
- The looser coupling could be seen as a disadvantage (I don't think so,
- If a cfunc is defined multiple times we would need to declare it on
  each definition. ex. BasicObject#== and BasicObject#equal?. This is
  rare compared to using an alias.
---
 yjit_codegen.c | 39 ++++++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/yjit_codegen.c b/yjit_codegen.c
index b7029472be..c4dae13ff9 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -20,6 +20,9 @@ https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L20
 // Map from YARV opcodes to code generation functions
 static codegen_fn gen_fns[VM_INSTRUCTION_SIZE] = { NULL };
 
+// Map from method entries to code generation functions
+static st_table *yjit_method_codegen_table = NULL;
+
 // Code block into which we write machine code
 static codeblock_t block;
 codeblock_t* cb = NULL;
@@ -2272,7 +2275,7 @@ jit_protected_callee_ancestry_guard(jitstate_t *jit, codeblock_t *cb, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2275
 }
 
 // Return true when the codegen function generates code.
-typedef bool (*cfunc_codegen_t)(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);
+typedef bool (*method_codegen_t)(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);
 
 // Codegen for rb_obj_not().
 // Note, caller is responsible for generating all the right guards, including
@@ -2304,11 +2307,12 @@ jit_rb_obj_not(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2307
 }
 
 // Check if we know how to codegen for a particular cfunc method
-static cfunc_codegen_t
-lookup_cfunc_codegen(const rb_method_cfunc_t *cfunc)
+static method_codegen_t
+lookup_cfunc_codegen(const rb_method_definition_t *def)
 {
-    if (cfunc->func == rb_obj_not) {
-        return jit_rb_obj_not;
+    method_codegen_t gen_fn;
+    if (st_lookup(yjit_method_codegen_table, def->method_serial, (st_data_t *)&gen_fn)) {
+        return gen_fn;
     }
     return NULL;
 }
@@ -2338,8 +2342,8 @@ gen_send_cfunc(jitstate_t *jit, ctx_t *ctx, const struct rb_callinfo *ci, const https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2342
 
     // Delegate to codegen for C methods if we have it.
     {
-        cfunc_codegen_t known_cfunc_codegen;
-        if ((known_cfunc_codegen = lookup_cfunc_codegen(cfunc))) {
+        method_codegen_t known_cfunc_codegen;
+        if ((known_cfunc_codegen = lookup_cfunc_codegen(cme->def))) {
             if (known_cfunc_codegen(jit, ctx, ci, cme, block, argc)) {
                 // cfunc codegen generated code. Terminate the block so
                 // there isn't multiple calls in the same block.
@@ -3282,6 +3286,23 @@ gen_opt_invokebuiltin_delegate(jitstate_t *jit, ctx_t *ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L3286
     return YJIT_KEEP_COMPILING;
 }
 
+static void
+yjit_reg_method(VALUE klass, const char *mid_str, method_codegen_t gen_fn)
+{
+    ID mid = rb_intern(mid_str);
+    const rb_method_entry_t *me = rb_method_entry_at(klass, mid);
+
+    if (!me) {
+        rb_bug("undefined optimized method: %s", rb_id2name(mid));
+    }
+
+    // For now, only cfuncs are supported
+    VM_ASSERT(me && me->def);
+    VM_ASSERT(me->def->type == VM_METHOD_TYPE_CFUNC);
+
+    st_insert(yjit_method_codegen_table, (st_data_t)me->def->method_serial, (st_data_t)gen_fn);
+}
+
 static void
 yjit_reg_op(int opcode, codegen_fn gen_fn)
 {
@@ -3362,4 +3383,8 @@ yjit_init_codegen(void) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L3383
     yjit_reg_op(BIN(opt_send_without_block), gen_opt_send_without_block);
     yjit_reg_op(BIN(send), gen_send);
     yjit_reg_op(BIN(leave), gen_leave);
+
+    yjit_method_codegen_table = st_init_numtable();
+
+    yjit_reg_method(rb_cBasicObject, "!", jit_rb_obj_not);
 }
-- 
cgit v1.2.1


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

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