зеркало из https://github.com/github/ruby.git
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.
This commit is contained in:
Родитель
7030cae969
Коммит
e4bf905a28
|
@ -20,6 +20,9 @@
|
|||
// 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
|
|||
}
|
||||
|
||||
// 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
|
|||
}
|
||||
|
||||
// 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
|
|||
|
||||
// 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)
|
|||
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)
|
|||
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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче