Feature #14489 ยป 14489.patch
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
|
||
}
|