Project

General

Profile

Feature #14489 ยป 14489.patch

wanabe (_ wanabe), 03/07/2018 01:02 PM

View differences:

Makefile.in
update-coverage: update-simplecov update-simplecov-html update-doclie
INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \
vmtc.inc vm.inc mjit_compile.inc
vmtc.inc vm.inc mjit_compile.inc mjit_var.inc
$(INSNS): $(srcdir)/insns.def vm_opts.h \
$(srcdir)/defs/opt_operand.def $(srcdir)/defs/opt_insn_unif.def \
common.mk
$(srcs_vpath)mjit_compile.inc: $(srcdir)/tool/ruby_vm/views/mjit_compile.inc.erb \
$(srcdir)/tool/ruby_vm/views/_mjit_compile_insn.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_send.erb \
$(srcdir)/tool/ruby_vm/views/_mjit_compile_insn_body.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb
$(srcs_vpath)mjit_var.inc: $(srcdir)/tool/ruby_vm/views/mjit_var.inc.erb
common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
srcs-lib srcs-ext incs
......
mjit_compile.$(OBJEXT): {$(VPATH)}mjit.h
mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.c
mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.inc
mjit_compile.$(OBJEXT): {$(VPATH)}mjit_var.inc
mjit_compile.$(OBJEXT): {$(VPATH)}vm_core.h
load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
miniinit.c
#include "ruby/encoding.h"
/* loadpath.c */
const char ruby_exec_prefix[] = "";
const char ruby_exec_prefix[] = "./";
const char ruby_initial_load_paths[] = "";
/* localeinit.c */
mjit.c
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <libgen.h>
extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
extern void rb_native_mutex_unlock(rb_nativethread_lock_t *lock);
......
static int in_gc;
/* True when JIT is working. */
static int in_jit;
/* Object cache directory */
static VALUE o_cache_dir = Qfalse;
/* Defined in the client thread before starting MJIT threads: */
/* Used C compiler path. */
......
return exit_code == 0;
}
/* Compile C file to o. It returns 1 if it succeeds. */
static int
compile_c_to_o(const char *c_file, const char *o_file)
{
int exit_code;
const char *files[] = {
#ifdef __clang__
"-include-pch", NULL,
#endif
"-c",
#ifndef _MSC_VER
"-o",
#endif
NULL, NULL, NULL};
const char *libs[] = {
#ifdef _WIN32
# ifdef _MSC_VER
MJIT_LIBS
"-link",
libruby_installed,
libruby_build,
# else
/* Look for ruby.dll.a in build and install directories. */
libruby_installed,
libruby_build,
MJIT_LIBS
"-lmsvcrt",
"-lgcc",
# endif
#endif
NULL};
char **args;
#ifdef _MSC_VER
char *p;
int olen;
#endif
files[numberof(files)-2] = c_file;
#ifdef _MSC_VER
olen = strlen(o_file);
files[0] = p = xmalloc(rb_strlen_lit("-Fe") + olen + 1);
p = append_lit(p, "-Fe");
p = append_str2(p, o_file, olen);
*p = '\0';
#else
# ifdef __clang__
files[1] = pch_file;
# endif
files[numberof(files)-3] = o_file;
#endif
args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS,
files, libs, CC_DLDFLAGS_ARGS);
if (args == NULL)
return FALSE;
exit_code = exec_process(cc_path, args);
xfree(args);
#ifdef _MSC_VER
xfree((char *)files[0]);
#endif
if (exit_code != 0)
verbose(2, "compile_c_to_o: compile error: %d", exit_code);
return exit_code == 0;
}
/* Compile var C file and o to so. It returns 1 if it succeeds. */
static int
compile_var_and_o_to_so(const char *var_file, const char *o_file, const char *so_file)
{
int exit_code;
const char *files[] = {
#ifdef __clang__
"-include-pch", NULL,
#endif
#ifndef _MSC_VER
"-o",
#endif
NULL, NULL, NULL, NULL};
const char *libs[] = {
#ifdef _WIN32
# ifdef _MSC_VER
MJIT_LIBS
"-link",
libruby_installed,
libruby_build,
# else
/* Look for ruby.dll.a in build and install directories. */
libruby_installed,
libruby_build,
MJIT_LIBS
"-lmsvcrt",
"-lgcc",
# endif
#endif
NULL};
char **args;
#ifdef _MSC_VER
char *p;
int solen;
#endif
files[numberof(files)-3] = var_file;
files[numberof(files)-2] = o_file;
#ifdef _MSC_VER
solen = strlen(so_file);
files[0] = p = xmalloc(rb_strlen_lit("-Fe") + solen + 1);
p = append_lit(p, "-Fe");
p = append_str2(p, so_file, solen);
*p = '\0';
#else
# ifdef __clang__
files[1] = pch_file;
# endif
files[numberof(files)-4] = so_file;
#endif
args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS,
files, libs, CC_DLDFLAGS_ARGS);
if (args == NULL)
return FALSE;
exit_code = exec_process(cc_path, args);
xfree(args);
#ifdef _MSC_VER
xfree((char *)files[0]);
#endif
if (exit_code != 0)
verbose(2, "compile_c_to_so: compile error: %d", exit_code);
return exit_code == 0;
}
static void *
load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
{
......
RSTRING_PTR(rb_iseq_path(unit->iseq)), FIX2INT(unit->iseq->body->location.first_lineno), c_file);
}
static int
mkparentdir(char *path)
{
struct stat sb;
char *parent = dirname(strdupa(path));
if (!strcmp(parent, path)) {
return 1;
}
stat(parent, &sb);
if (S_ISDIR(sb.st_mode)) {
return 0;
} else {
if (mkparentdir(parent)) {
return 1;
}
return mkdir(parent, 0
| S_IRUSR | S_IWUSR | S_IXUSR
| S_IRGRP | S_IWGRP | S_IXGRP
| S_IROTH | S_IXOTH | S_IXOTH
);
}
}
/* Compile ISeq in UNIT and return function pointer of JIT-ed code.
It may return NOT_COMPILABLE_JIT_ISEQ_FUNC if something went wrong. */
static mjit_func_t
convert_unit_to_func(struct rb_mjit_unit *unit)
{
char c_file_buff[70], *c_file = c_file_buff, *so_file, funcname[35];
char c_file_buff[70], *c_file = c_file_buff, *so_file, funcname[35], *var_file;
int success;
int fd;
char create_o_cache = 0;
FILE *f;
void *func;
double start_time, end_time;
int c_file_len = (int)sizeof(c_file_buff);
static const char c_ext[] = ".c";
static const char so_ext[] = DLEXT;
char *o_file = NULL;
const int access_mode =
#ifdef O_BINARY
O_BINARY|
#endif
O_WRONLY|O_EXCL|O_CREAT;
if (mjit_opts.cache_objs) {
struct stat o_st, r_st;
char *rb_file = RSTRING_PTR(rb_iseq_realpath(unit->iseq));
if (stat(rb_file, &r_st)) {
o_file = NULL;
verbose(2, "Can't get stat of '%s'", rb_file);
} else {
o_file = malloc(RSTRING_LEN(o_cache_dir) + strlen(rb_file) + 15);
sprintf(o_file, "%s%s.%d.o", RSTRING_PTR(o_cache_dir), rb_file, FIX2INT(unit->iseq->body->location.first_lineno));
verbose(2, "Check Cache '%s'", o_file);
if (mkparentdir(o_file)) {
verbose(2, "Can't create directory of '%s'", o_file);
o_file = NULL;
} else if (stat(o_file, &o_st)) {
if (errno == ENOENT) {
create_o_cache = 1;
verbose(2, "Create '%s'", o_file);
} else {
o_file = NULL;
verbose(2, "Can't get stat of '%s'", o_file);
}
} else if(o_st.st_mtime > r_st.st_mtime) {
verbose(2, "Use cache '%s'", o_file);
} else {
create_o_cache = 1;
verbose(2, "Update '%s'", o_file);
}
}
}
c_file_len = sprint_uniq_filename(c_file_buff, c_file_len, unit->id, MJIT_TMP_PREFIX, c_ext);
if (c_file_len >= (int)sizeof(c_file_buff)) {
++c_file_len;
......
memcpy(&so_file[c_file_len - sizeof(c_ext)], so_ext, sizeof(so_ext));
sprintf(funcname, "_mjit%d", unit->id);
if (o_file) {
static const char var_suffix[] = "_var";
var_file = alloca(c_file_len + sizeof(var_suffix));
memcpy(var_file, c_file, c_file_len - sizeof(c_ext));
memcpy(&var_file[c_file_len - sizeof(c_ext)], var_suffix, sizeof(var_suffix));
memcpy(&var_file[c_file_len - sizeof(c_ext) + sizeof(var_suffix) - 1], c_ext, sizeof(c_ext));
verbose(2, "Create '%s'", var_file);
fd = rb_cloexec_open(var_file, access_mode, 0600);
if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
int e = errno;
if (fd >= 0) (void)close(fd);
verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", var_file, strerror(e));
return (mjit_func_t)NOT_COMPILABLE_JIT_ISEQ_FUNC;
}
#ifdef __clang__
/* -include-pch is used for Clang */
#else
{
# ifdef __GNUC__
const char *s = pch_file;
# else
const char *s = header_file;
# endif
const char *e = header_name_end(s);
fprintf(f, "#include \"");
/* print pch_file except .gch */
for (; s < e; s++) {
switch(*s) {
case '\\': case '"':
fputc('\\', f);
}
fputc(*s, f);
}
fprintf(f, "\"\n");
}
#endif
mjit_compile_var(f, unit->iseq->body, funcname);
fclose(f);
}
if (!o_file || create_o_cache) {
fd = rb_cloexec_open(c_file, access_mode, 0600);
if (fd < 0 || (f = fdopen(fd, "w")) == NULL) {
int e = errno;
......
verbose(2, "start compile: %s@%s:%d -> %s", label, path, lineno, c_file);
fprintf(f, "/* %s@%s:%d */\n\n", label, path, lineno);
}
success = mjit_compile(f, unit->iseq->body, funcname);
success = mjit_compile(f, unit->iseq->body, funcname, create_o_cache);
fclose(f);
/* release blocking mjit_gc_start_hook */
CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC");
......
rb_native_cond_signal(&mjit_client_wakeup);
CRITICAL_SECTION_FINISH(3, "in worker to wakeup client for GC");
fclose(f);
if (!success) {
if (!mjit_opts.save_temps)
remove(c_file);
print_jit_result("failure", unit, 0, c_file);
return (mjit_func_t)NOT_COMPILABLE_JIT_ISEQ_FUNC;
}
}
start_time = real_ms_time();
success = compile_c_to_so(c_file, so_file);
if (o_file) {
if (create_o_cache) {
success = compile_c_to_o(c_file, o_file);
success = compile_var_and_o_to_so(var_file, o_file, so_file) && success;
} else {
success = compile_var_and_o_to_so(var_file, o_file, so_file);
}
free(o_file);
} else {
success = compile_c_to_so(c_file, so_file);
}
end_time = real_ms_time();
if (!mjit_opts.save_temps)
......
rb_native_cond_destroy(&mjit_gc_wakeup);
verbose(1, "Failure in MJIT thread initialization\n");
}
/* Initialize Object cache directory */
if (mjit_opts.cache_objs) {
o_cache_dir = rb_default_home_dir(rb_str_new(0, 0));
rb_str_concat(o_cache_dir, rb_str_new2("/"));
rb_str_concat(o_cache_dir, rb_str_new2(".cache"));
rb_str_concat(o_cache_dir, rb_str_new2("/"));
rb_str_concat(o_cache_dir, rb_str_new2("ruby-mjit"));
rb_str_concat(o_cache_dir, rb_str_new2("/"));
}
}
/* Finish the threads processing units and creating PCH, finalize
......
if (!mjit_init_p)
return;
RUBY_MARK_ENTER("mjit");
RUBY_MARK_UNLESS_NULL(o_cache_dir);
CRITICAL_SECTION_START(4, "mjit_mark");
for (node = unit_queue.head; node != NULL; node = node->next) {
if (node->unit->iseq) { /* ISeq is still not GCed */
mjit.h
/* Maximal permitted number of iseq JIT codes in a MJIT memory
cache. */
int max_cache_size;
char cache_objs; /* flag of MJIT cache object files */
};
typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *);
......
extern mjit_func_t mjit_get_iseq_func(const struct rb_iseq_constant_body *body);
RUBY_SYMBOL_EXPORT_END
extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname);
extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, const char create_o_cache);
extern void mjit_compile_var(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname);
extern void mjit_init(struct mjit_options *opts);
extern void mjit_finish(void);
extern void mjit_gc_start_hook(void);
mjit_compile.c
}
static void compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
unsigned int pos, struct compile_status *status);
unsigned int pos, struct compile_status *status, const char create_o_cache);
/* Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
b->stack_size and return next position.
......
does not have it can be compiled as usual. */
static unsigned int
compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn, const VALUE *operands,
const unsigned int pos, struct compile_status *status, struct compile_branch *b)
const unsigned int pos, struct compile_status *status, struct compile_branch *b, const char create_o_cache)
{
unsigned int next_pos = pos + insn_len(insn);
......
called multiple times for each branch. */
static void
compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
unsigned int pos, struct compile_status *status)
unsigned int pos, struct compile_status *status, const char create_o_cache)
{
int insn;
struct compile_branch branch;
......
status->compiled_for_pos[pos] = TRUE;
fprintf(f, "\nlabel_%d: /* %s */\n", pos, insn_name(insn));
pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch);
pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch, create_o_cache);
if (status->success && branch.stack_size > body->stack_max) {
if (mjit_opts.warnings || mjit_opts.verbose)
fprintf(stderr, "MJIT warning: JIT stack exceeded its max\n");
......
/* Compile ISeq to C code in F. It returns 1 if it succeeds to compile. */
int
mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname)
mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, const char create_o_cache)
{
struct compile_status status;
status.success = TRUE;
status.compiled_for_pos = ZALLOC_N(int, body->iseq_size);
status.local_stack_p = !body->catch_except_p;
if (create_o_cache) {
unsigned int pos = 0;
int insn;
const VALUE *operands;
fprintf(f, "extern const VALUE *const original_body_iseq;\n");
for (pos = 0; pos < body->iseq_size; pos = pos + insn_len(insn)) {
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]);
#else
insn = (int)body->iseq_encoded[pos];
#endif
operands = body->iseq_encoded + (pos+1);
#define MJIT_EXTERN 1
/*****************/
#include "mjit_var.inc"
/*****************/
#undef MJIT_EXTERN
}
}
#ifdef _WIN32
fprintf(f, "__declspec(dllexport)\n");
#endif
......
else {
fprintf(f, " VALUE *stack = reg_cfp->sp;\n");
}
fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
(VALUE)body->iseq_encoded);
if (!create_o_cache) {
fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
(VALUE)body->iseq_encoded);
}
/* Simulate `opt_pc` in setup_parameters_complex */
if (body->param.flags.has_opt) {
......
fprintf(f, " return Qundef;\n");
fprintf(f, " }\n");
compile_insns(f, body, 0, 0, &status);
compile_insns(f, body, 0, 0, &status, create_o_cache);
compile_cancel_handler(f, body, &status);
fprintf(f, "\n} /* end of %s */\n", funcname);
xfree(status.compiled_for_pos);
return status.success;
}
void
mjit_compile_var(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname)
{
unsigned int pos = 0;
int insn;
const VALUE *operands;
fprintf(f, "VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
(VALUE)body->iseq_encoded);
for (pos = 0; pos < body->iseq_size; pos = pos + insn_len(insn)) {
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]);
#else
insn = (int)body->iseq_encoded[pos];
#endif
operands = body->iseq_encoded + (pos+1);
#define MJIT_EXTERN 0
/*****************/
#include "mjit_var.inc"
/*****************/
#undef MJIT_EXTERN
}
}
ruby.c
M("--jit-debug", "", "Enable MJIT debugging (very slow)"),
M("--jit-wait", "", "Wait until JIT compilation is finished everytime (for testing)"),
M("--jit-save-temps", "", "Save MJIT temporary files in $TMP or /tmp (for testing)"),
M("--jit-cache-objs", "", "Cache object files"),
M("--jit-verbose=num", "", "Print MJIT logs of level num or less to stderr (default: 0)"),
M("--jit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: 1000)"),
M("--jit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: 5)"),
......
else if (strncmp(s, "-min-calls=", 11) == 0) {
mjit_opt->min_calls = atoi(s + 11);
}
else if (strcmp(s, "-cache-objs") == 0) {
mjit_opt->cache_objs = 1;
}
else {
rb_raise(rb_eRuntimeError,
"invalid MJIT option `%s' (--help will show valid MJIT options)", s + 1);
tool/ruby_vm/models/instructions.rb
RubyVM::Instructions = RubyVM::BareInstructions.to_a + \
RubyVM::OperandsUnifications.to_a + \
RubyVM::InstructionsUnifications.to_a
RubyVM::MJIT::UnsupportedInstructions = [
'getblockparamproxy', # TODO: support this
'defineclass', # low priority
'opt_call_c_function', # low priority
]
require_relative 'trace_instructions'
RubyVM::Instructions.freeze
tool/ruby_vm/views/_mjit_compile_insn.erb
%
% # JIT: Initialize operands
% insn.opes.each_with_index do |ope, i|
fprintf(f, " <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]);
% if !%w(... OFFSET lindex_t rb_num_t).include?(ope.fetch(:type))
if (create_o_cache) {
fprintf(f, " <%= ope.fetch(:name) %> = mjit_var_%d_<%= i %>;", pos);
} else {
% else
{
% end
fprintf(f, " <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]);
}
% case ope.fetch(:type)
% when 'ID'
comment_id(f, (ID)operands[<%= i %>]);
......
%
% # compiler: If insn has conditional JUMP, the branch which is not targeted by JUMP should be compiled too.
% if insn.expr.expr =~ /if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/
compile_insns(f, body, b->stack_size, pos + insn_len(insn), status);
compile_insns(f, body, b->stack_size, pos + insn_len(insn), status, create_o_cache);
% end
%
% # compiler: If insn returns (leave) or does longjmp (throw), the branch should no longer be compiled. TODO: create attr for it?
tool/ruby_vm/views/_mjit_compile_send.erb
<%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%>
% # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things.
fprintf(f, " if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != %"PRI_SERIALT_PREFIX"u ||\n", cc->method_state);
fprintf(f, " RCLASS_SERIAL(CLASS_OF(stack[%d])) != %"PRI_SERIALT_PREFIX"u)) {\n", b->stack_size - 1 - argc, cc->class_serial);
if (create_o_cache) {
fprintf(f, " if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != mjit_var_%d_method_state ||\n", pos);
fprintf(f, " RCLASS_SERIAL(CLASS_OF(stack[%d])) != mjit_var_%d_class_serial)) {\n", b->stack_size - 1 - argc, pos);
} else {
fprintf(f, " if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != %"PRI_SERIALT_PREFIX"u ||\n", cc->method_state);
fprintf(f, " RCLASS_SERIAL(CLASS_OF(stack[%d])) != %"PRI_SERIALT_PREFIX"u)) {\n", b->stack_size - 1 - argc, cc->class_serial);
}
fprintf(f, " reg_cfp->pc = original_body_iseq + %d;\n", pos);
fprintf(f, " goto cancel;\n");
fprintf(f, " }\n");
......
fprintf(f, " {\n");
fprintf(f, " struct rb_calling_info calling;\n");
% if insn.name == 'send'
fprintf(f, " vm_caller_setup_arg_block(ec, reg_cfp, &calling, 0x%"PRIxVALUE", 0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]);
if (create_o_cache) {
fprintf(f, " vm_caller_setup_arg_block(ec, reg_cfp, &calling, mjit_var_%d_0, mjit_var_%d_2, FALSE);\n", pos, pos);
} else {
fprintf(f, " vm_caller_setup_arg_block(ec, reg_cfp, &calling, 0x%"PRIxVALUE", 0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]);
}
% else
fprintf(f, " calling.block_handler = VM_BLOCK_HANDLER_NONE;\n");
% end
......
fprintf(f, " VALUE v;\n");
fprintf(f, " VALUE *argv = reg_cfp->sp - calling.argc;\n");
fprintf(f, " reg_cfp->sp = argv - 1;\n"); /* recv */
fprintf(f, " vm_push_frame(ec, (const rb_iseq_t *)0x%"PRIxVALUE", VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, "
"calling.block_handler, 0x%"PRIxVALUE", (const VALUE *)0x%"PRIxVALUE", argv + %d, %d, %d);\n",
(VALUE)iseq, (VALUE)cc->me, (VALUE)iseq->body->iseq_encoded, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max);
if (create_o_cache) {
fprintf(f, " vm_push_frame(ec, (const rb_iseq_t *)mjit_var_%d_iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, "
"calling.block_handler, mjit_var_%d_ccme, (const VALUE *)mjit_var_%d_iseq_encoded, argv + %d, %d, %d);\n",
pos, pos, pos, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max);
} else {
fprintf(f, " vm_push_frame(ec, (const rb_iseq_t *)0x%"PRIxVALUE", VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, "
"calling.block_handler, 0x%"PRIxVALUE", (const VALUE *)0x%"PRIxVALUE", argv + %d, %d, %d);\n",
(VALUE)iseq, (VALUE)cc->me, (VALUE)iseq->body->iseq_encoded, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max);
}
if (iseq->body->catch_except_p) {
fprintf(f, " VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n");
fprintf(f, " v = vm_exec(ec, TRUE);\n");
tool/ruby_vm/views/mjit_compile.inc.erb
edit: __FILE__,
} -%>
%
% unsupported_insns = [
% 'getblockparamproxy', # TODO: support this
% 'defineclass', # low priority
% 'opt_call_c_function', # low priority
% ]
% unsupported_insns = RubyVM::MJIT::UnsupportedInstructions
%
% opt_send_without_block = RubyVM::Instructions.find { |i| i.name == 'opt_send_without_block' }
% if opt_send_without_block.nil?
tool/ruby_vm/views/mjit_var.inc.erb
/* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */
% # Copyright (c) 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.
<%= render 'copyright' %>
%
% # This is an ERB template that generates Ruby code that generates C code that
% # generates JIT-ed C code.
<%= render 'notice', locals: {
this_file: 'is the main part of compile_insn() in mjit_compile.c',
edit: __FILE__,
} -%>
%
% unsupported_insns = RubyVM::MJIT::UnsupportedInstructions
switch (insn) {
% (RubyVM::BareInstructions.to_a + RubyVM::OperandsUnifications.to_a).each do |insn|
% next if unsupported_insns.include?(insn.name)
case BIN(<%= insn.name %>):
% if %w[opt_send_without_block send opt_aref].include?(insn.name)
#if MJIT_EXTERN
fprintf(f, "extern const rb_serial_t mjit_var_%d_method_state;\n", pos);
fprintf(f, "extern const rb_serial_t mjit_var_%d_class_serial;\n", pos);
fprintf(f, "extern const VALUE mjit_var_%d_iseq;\n", pos);
fprintf(f, "extern const VALUE mjit_var_%d_ccme;\n", pos);
fprintf(f, "extern const VALUE mjit_var_%d_iseq_encoded;\n", pos);
#else
{
CALL_INFO ci = (CALL_INFO)operands[0];
CALL_CACHE cc = (CALL_CACHE)operands[1];
const rb_iseq_t *iseq = get_iseq_if_available(cc);
if (inlinable_iseq_p(ci, cc, iseq = get_iseq_if_available(cc))) {
fprintf(f, "const rb_serial_t mjit_var_%d_method_state = %"PRI_SERIALT_PREFIX"u;", pos, cc->method_state);
fprintf(f, "const rb_serial_t mjit_var_%d_class_serial = %"PRI_SERIALT_PREFIX"u;", pos, cc->class_serial);
fprintf(f, "const VALUE mjit_var_%d_iseq = 0x%"PRIxVALUE";\n", pos, (VALUE)iseq);
fprintf(f, "const VALUE mjit_var_%d_ccme = 0x%"PRIxVALUE";\n", pos, (VALUE)cc->me);
fprintf(f, "const VALUE mjit_var_%d_iseq_encoded = 0x%"PRIxVALUE";\n", pos, (VALUE)iseq->body->iseq_encoded);
} else {
fprintf(f, "const rb_serial_t mjit_var_%d_method_state = 0u;", pos);
fprintf(f, "const rb_serial_t mjit_var_%d_class_serial = 0u;", pos);
fprintf(f, "const VALUE mjit_var_%d_iseq = 0x0;\n", pos);
fprintf(f, "const VALUE mjit_var_%d_ccme = 0x0;\n", pos);
fprintf(f, "const VALUE mjit_var_%d_iseq_encoded = 0x0;\n", pos);
}
}
#endif
% end
% insn.opes.each_with_index do |ope, i|
% next if %w(... OFFSET lindex_t rb_num_t).include?(ope.fetch(:type))
fprintf(f, "/* <%= insn.name %> <%= ope.fetch(:type) %> */;\n");
#if MJIT_EXTERN
fprintf(f, "extern const <%= ope.fetch(:type) %> mjit_var_%d_<%= i %>;\n", pos);
#else
fprintf(f, "const <%= ope.fetch(:type) %> mjit_var_%d_<%= i %> = 0x%"PRIxVALUE";\n", pos, operands[<%= i %>]);
#endif
% end
break;
% end
}
    (1-1/1)