precalc invokebuiltin destinations

Noticed that struct rb_builtin_function is a purely compile-time
constant.  MJIT can eliminate some runtime calculations by statically
generate dedicated C code generator for each builtin functions.
This commit is contained in:
卜部昌平 2020-07-09 21:43:42 +09:00
Родитель 5d02c1dd14
Коммит f66e0212ef
7 изменённых файлов: 77 добавлений и 15 удалений

Просмотреть файл

@ -11,13 +11,17 @@ struct rb_builtin_function {
// for load
const int index;
const char * const name;
// for jit
void (*compiler)(FILE *, long);
};
#define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity) { \
#define RB_BUILTIN_FUNCTION(_i, _name, _fname, _arity, _compiler) {\
.name = #_name, \
.func_ptr = (void *)_fname, \
.argc = _arity, \
.index = _i \
.index = _i, \
.compiler = _compiler, \
}
void rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin_function *table);
@ -76,4 +80,20 @@ struct builtin_binary {
size_t bin_size;
};
// mjit
RBIMPL_ATTR_MAYBE_UNUSED()
static void
mjit_invokebuiltin_default_compiler(FILE *f, const struct rb_builtin_function *bf, long index)
{
if (index >= 0) {
fprintf(f, "val = vm_invoke_builtin(ec, GET_CFP(), %p, STACK_ADDR_FROM_TOP(%d));\n",
(const void *)bf, bf->argc);
}
else {
fprintf(f, "val = vm_invoke_builtin_delegate(ec, GET_CFP(), %p, %ld);\n",
(const void *)bf, index);
}
}
#endif // BUILTIN_H_INCLUDED

Просмотреть файл

@ -1478,11 +1478,11 @@ DEFINE_INSN
invokebuiltin
(RB_BUILTIN bf)
(...)
(VALUE ret)
(VALUE val)
// attr bool leaf = false; /* anything can happen inside */
// attr rb_snum_t sp_inc = 1 - bf->argc;
{
ret = vm_invoke_builtin(ec, reg_cfp, bf, STACK_ADDR_FROM_TOP(bf->argc));
val = vm_invoke_builtin(ec, reg_cfp, bf, STACK_ADDR_FROM_TOP(bf->argc));
}
/* call specific function with args (same parameters) */
@ -1490,10 +1490,10 @@ DEFINE_INSN
opt_invokebuiltin_delegate
(RB_BUILTIN bf, rb_num_t index)
()
(VALUE ret)
(VALUE val)
// attr bool leaf = false; /* anything can happen inside */
{
ret = vm_invoke_builtin_delegate(ec, reg_cfp, bf, (unsigned int)index);
val = vm_invoke_builtin_delegate(ec, reg_cfp, bf, (unsigned int)index);
}
/* call specific function with args (same parameters) and leave */

Просмотреть файл

@ -316,9 +316,9 @@ opt_struct_aset(rb_execution_context_t *ec, VALUE self, VALUE val, VALUE idx)
}
static const struct rb_builtin_function struct_aref_builtin =
RB_BUILTIN_FUNCTION(0, struct_aref, opt_struct_aref, 1);
RB_BUILTIN_FUNCTION(0, struct_aref, opt_struct_aref, 1, 0);
static const struct rb_builtin_function struct_aset_builtin =
RB_BUILTIN_FUNCTION(1, struct_aref, opt_struct_aset, 2);
RB_BUILTIN_FUNCTION(1, struct_aref, opt_struct_aset, 2, 0);
static void
define_aref_method(VALUE nstr, VALUE name, VALUE off)

Просмотреть файл

@ -272,6 +272,25 @@ def mk_builtin_header file
end
}
bs.each_pair{|func, (argc, cfunc_name)|
f.puts %'static void'
f.puts %'mjit_compile_invokebuiltin_for_#{func}(FILE *f, long index)'
f.puts %'{'
f.puts %' if (index > 0) {'
f.puts %' fprintf(f, " const unsigned int lnum = GET_ISEQ()->body->local_table_size;\\n");'
f.puts %' fprintf(f, " const VALUE *argv = GET_EP() - lnum - VM_ENV_DATA_SIZE + 1 + %ld;\\n", index);'
f.puts %' }'
f.puts %' else if (index == 0) {'
f.puts %' fprintf(f, " const VALUE *argv = NULL;\\n");'
f.puts %' }'
f.puts %' else {'
f.puts %' fprintf(f, " const VALUE *argv = STACK_ADDR_FROM_TOP(%d);\\n", #{argc});'
f.puts %' }'
f.puts %' fprintf(f, " val = builtin_invoker#{argc}(ec, GET_SELF(), argv, %p);\\n", (const void *)#{cfunc_name});'
f.puts %'}'
f.puts
}
f.puts "void Init_builtin_#{base}(void)"
f.puts "{"
@ -279,9 +298,9 @@ def mk_builtin_header file
f.puts " // table definition"
f.puts " static const struct rb_builtin_function #{table}[] = {"
bs.each.with_index{|(func, (argc, cfunc_name)), i|
f.puts " RB_BUILTIN_FUNCTION(#{i}, #{func}, #{cfunc_name}, #{argc}),"
f.puts " RB_BUILTIN_FUNCTION(#{i}, #{func}, #{cfunc_name}, #{argc}, mjit_compile_invokebuiltin_for_#{func}),"
}
f.puts " RB_BUILTIN_FUNCTION(-1, NULL, NULL, 0),"
f.puts " RB_BUILTIN_FUNCTION(-1, NULL, NULL, 0, 0),"
f.puts " };"
f.puts

Просмотреть файл

@ -63,6 +63,10 @@
fprintf(f, " goto label_%lu;\n", arg.base_pos + else_offset);
fprintf(f, " }\n");
}
% elsif insn.name == 'invokebuiltin' || insn.name == 'opt_invokebuiltin_delegate'
{
<%= render 'mjit_compile_invokebuiltin', locals: { insn: insn } -%>
}
% else
% # Before we `goto` next insn, we need to set return values, especially for getinlinecache
% insn.rets.reverse_each.with_index do |ret, i|

Просмотреть файл

@ -0,0 +1,23 @@
% # -*- C -*-
% # Copyright (c) 2020 Urabe, Shyouhei. All rights reserved.
% #
% # This file is a part of the programming language Ruby. Permission is hereby
% # granted, to either redistribute and/or modify this file, provided that the
% # conditions mentioned in the file COPYING are met. Consult the file for
% # details.
%
/* <%= insn.name %> */
const struct rb_builtin_function *bf = (const void *)operands[0];
%
% if insn.name == 'invokebuiltin' then
const rb_num_t index = -1;
% else
const rb_num_t index = (rb_num_t)operands[1];
% end
%
if (bf->compiler) {
bf->compiler(f, index);
}
else {
mjit_invokebuiltin_default_compiler(f, bf, index);
}

Просмотреть файл

@ -67,13 +67,9 @@ switch (insn) {
{
% # opt_invokebuiltin_delegate_leave also implements leave insn. We need to handle it here for inlining.
% if insn.name == 'opt_invokebuiltin_delegate_leave'
RB_BUILTIN bf = (RB_BUILTIN)operands[0];
rb_num_t index = (rb_num_t)operands[0];
fprintf(f, "{\n");
fprintf(f, " VALUE val;\n");
fprintf(f, " RB_BUILTIN bf = (RB_BUILTIN)0x%"PRIxVALUE";\n", operands[0]);
fprintf(f, " rb_num_t index = (rb_num_t)0x%"PRIxVALUE";\n", operands[1]);
fprintf(f, <%= rstring2cstr(insn.expr.expr.lines.find { |l| l =~ / vm_invoke_builtin_delegate\(/ }).gsub("\n", '\n') %>);
<%= render 'mjit_compile_invokebuiltin', locals: { insn: insn } -%>
fprintf(f, " stack[0] = val;\n");
fprintf(f, "}\n");
% else