mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit.c - Ruby JIT compiler functions
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
|
2023-03-07 10:17:25 +03:00
|
|
|
Copyright (C) 2023 Takashi Kokubun <k0kubun@ruby-lang.org>.
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2023-03-09 10:07:30 +03:00
|
|
|
#include "rjit.h" // defines USE_RJIT
|
2023-03-06 01:15:19 +03:00
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
#if USE_RJIT
|
2018-10-20 09:53:00 +03:00
|
|
|
|
mjit.c: try changing the order of includes
Hoping to fix the AIX's build failure like:
In file included from ./include/ruby/defines.h:139:0,
from ./include/ruby/ruby.h:29,
from ./include/ruby.h:33,
from internal.h:15,
from mjit.c:81:
/opt/freeware/lib/gcc/powerpc-ibm-aix7.1.0.0/4.8.1/include-fixed/unistd.h:939:14: error: conflicting types for 'ftruncate64'
extern int ftruncate64(int, off64_t);
^
In file included from ./include/ruby/defines.h:139:0,
from ./include/ruby/ruby.h:29,
from ./include/ruby.h:33,
from internal.h:15,
from mjit.c:81:
/opt/freeware/lib/gcc/powerpc-ibm-aix7.1.0.0/4.8.1/include-fixed/unistd.h:937:14: note: previous declaration of 'ftruncate64' was here
extern int ftruncate(int, off_t);
^
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62227 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-05 15:33:36 +03:00
|
|
|
#include "constant.h"
|
|
|
|
#include "id_table.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal.h"
|
|
|
|
#include "internal/class.h"
|
2022-01-10 17:38:16 +03:00
|
|
|
#include "internal/cmdlineopt.h"
|
2020-02-29 10:58:33 +03:00
|
|
|
#include "internal/cont.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal/file.h"
|
|
|
|
#include "internal/hash.h"
|
2022-08-21 08:04:52 +03:00
|
|
|
#include "internal/process.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal/warnings.h"
|
2021-06-10 10:32:15 +03:00
|
|
|
#include "vm_sync.h"
|
2022-06-15 19:40:54 +03:00
|
|
|
#include "ractor_core.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
|
2022-07-15 06:34:46 +03:00
|
|
|
#ifdef __sun
|
|
|
|
#define __EXTENSIONS__ 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "vm_core.h"
|
|
|
|
#include "vm_callinfo.h"
|
2023-03-07 10:17:25 +03:00
|
|
|
#include "rjit_c.h"
|
2022-07-15 06:34:46 +03:00
|
|
|
#include "ruby_assert.h"
|
|
|
|
#include "ruby/debug.h"
|
|
|
|
#include "ruby/thread.h"
|
|
|
|
#include "ruby/version.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "insns.inc"
|
|
|
|
#include "insns_info.inc"
|
|
|
|
#include "internal/compile.h"
|
2023-02-08 14:56:53 +03:00
|
|
|
#include "internal/gc.h"
|
2022-07-15 06:34:46 +03:00
|
|
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
#include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
|
|
# include <sys/param.h>
|
|
|
|
#endif
|
|
|
|
#include "dln.h"
|
|
|
|
|
2023-03-08 09:43:37 +03:00
|
|
|
// For mmapp(), sysconf()
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#endif
|
|
|
|
|
2022-07-15 06:34:46 +03:00
|
|
|
#include "ruby/util.h"
|
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
// A copy of RJIT portion of MRI options since RJIT initialization. We
|
|
|
|
// need them as RJIT threads still can work when the most MRI data were
|
2022-07-15 06:34:46 +03:00
|
|
|
// freed.
|
2023-03-19 07:27:07 +03:00
|
|
|
struct rb_rjit_options rb_rjit_opts;
|
2022-07-15 06:34:46 +03:00
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
// true if RJIT is enabled.
|
2023-03-09 10:14:33 +03:00
|
|
|
bool rb_rjit_enabled = false;
|
2023-03-12 23:55:39 +03:00
|
|
|
// true if --rjit-stats (used before rb_rjit_opts is set)
|
2023-03-09 10:14:33 +03:00
|
|
|
bool rb_rjit_stats_enabled = false;
|
2023-03-12 23:55:39 +03:00
|
|
|
// true if --rjit-trace-exits (used before rb_rjit_opts is set)
|
|
|
|
bool rb_rjit_trace_exits_enabled = false;
|
2022-07-15 06:34:46 +03:00
|
|
|
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
|
2023-03-09 10:14:33 +03:00
|
|
|
// and `rb_rjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
|
|
|
|
bool rb_rjit_call_p = false;
|
|
|
|
// A flag to communicate that rb_rjit_call_p should be disabled while it's temporarily false.
|
2023-03-09 10:25:36 +03:00
|
|
|
static bool rjit_cancel_p = false;
|
2022-07-15 06:34:46 +03:00
|
|
|
|
2023-03-09 10:25:36 +03:00
|
|
|
// `rb_ec_ractor_hooks(ec)->events` is moved to this variable during compilation.
|
|
|
|
rb_event_flag_t rb_rjit_global_events = 0;
|
2021-08-13 08:54:40 +03:00
|
|
|
|
2023-03-09 10:25:36 +03:00
|
|
|
// Basically rb_rjit_opts.stats, but this becomes false during RJIT compilation.
|
|
|
|
static bool rjit_stats_p = false;
|
2021-01-04 11:16:40 +03:00
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
// RubyVM::RJIT
|
|
|
|
static VALUE rb_mRJIT = 0;
|
|
|
|
// RubyVM::RJIT::C
|
|
|
|
static VALUE rb_mRJITC = 0;
|
|
|
|
// RubyVM::RJIT::Compiler
|
|
|
|
static VALUE rb_RJITCompiler = 0;
|
|
|
|
// RubyVM::RJIT::CPointer::Struct_rb_iseq_t
|
|
|
|
static VALUE rb_cRJITIseqPtr = 0;
|
|
|
|
// RubyVM::RJIT::CPointer::Struct_rb_control_frame_t
|
|
|
|
static VALUE rb_cRJITCfpPtr = 0;
|
|
|
|
// RubyVM::RJIT::Hooks
|
|
|
|
static VALUE rb_mRJITHooks = 0;
|
2022-09-06 07:31:07 +03:00
|
|
|
|
2023-03-12 23:55:39 +03:00
|
|
|
// Frames for --rjit-trace-exits
|
|
|
|
VALUE rb_rjit_raw_samples = 0;
|
|
|
|
// Line numbers for --rjit-trace-exits
|
|
|
|
VALUE rb_rjit_line_samples = 0;
|
|
|
|
|
Change the semantics of rb_postponed_job_register
Our current implementation of rb_postponed_job_register suffers from
some safety issues that can lead to interpreter crashes (see bug #1991).
Essentially, the issue is that jobs can be called with the wrong
arguments.
We made two attempts to fix this whilst keeping the promised semantics,
but:
* The first one involved masking/unmasking when flushing jobs, which
was believed to be too expensive
* The second one involved a lock-free, multi-producer, single-consumer
ringbuffer, which was too complex
The critical insight behind this third solution is that essentially the
only user of these APIs are a) internal, or b) profiling gems.
For a), none of the usages actually require variable data; they will
work just fine with the preregistration interface.
For b), generally profiling gems only call a single callback with a
single piece of data (which is actually usually just zero) for the life
of the program. The ringbuffer is complex because it needs to support
multi-word inserts of job & data (which can't be atomic); but nobody
actually even needs that functionality, really.
So, this comit:
* Introduces a pre-registration API for jobs, with a GVL-requiring
rb_postponed_job_prereigster, which returns a handle which can be
used with an async-signal-safe rb_postponed_job_trigger.
* Deprecates rb_postponed_job_register (and re-implements it on top of
the preregister function for compatability)
* Moves all the internal usages of postponed job register
pre-registration
2023-11-19 14:54:57 +03:00
|
|
|
// Postponed job handle for triggering rjit_iseq_update_references
|
|
|
|
static rb_postponed_job_handle_t rjit_iseq_update_references_pjob;
|
|
|
|
|
Change defaults of --jit options
* --jit-min-calls: 5 -> 10000
--jit-min-calls=5 obviously can compile non hotspot. This was not a
problem for MJIT-benchmarks and Optcarrot because the former has very
few hot optimiziable methods and the latter is likely to trigger
compilation of hotspot by its intensive calls to optimizable hotspot
methods and has a very short window to allow limited compilations.
In real-world applications, it has more time to compile more methods and
it pressures computer's limited resources like icache. We should avoid
compiling too many methods. Also compiling many methods exhausts time
budget for compilation in one ruby process lifetime and delays the "JIT
compaction" of Ruby 2.6.
JVM is known to use 1,500 for C1 (client) compiler and 10,000 for C2
(server) compiler for -XX:CompileThreshold by default.
https://docs.oracle.com/javase/8/embedded/develop-apps-platforms/codecache.htm
When things are called X,000 times, requiring 10,000 can eliminate
compilation of methods which are called only once in these X,000
iterations and obviously not hotspot. And in fact things like
unicorn-worker-killer restarts unicorn process every 4096 requests.
So I felt 10,000 is good for such an environment.
* --jit-max-cache: 1000 -> 100
By the same reason stated above, we should not allow compiling many
methods especially on MJIT which has a larger overhead due to poor code
locality by dlopen and whose code is also likely to be bigger by just
inlining many VM instructions with -O3 rather than directly generating
low-level code.
In JVM -XX:ReservedCodeCacheSize is 32M for reserved and 48M for maximum.
--jit-max-cache=1,000 could be closer to it, but in this case MJIT's
compilation is slow due to data synchronization between threads (to be
improved in Ruby 2.7 though) and we do not want to delay the "JIT
compaction" for a long time.
So I chose a really conservative number for this, but by having method
inlining in the future, wider range could be optimized even with this
value.
* Optcarrot
--disable-gems, --benchmark Lan_Master.nes 12 attempts.
No significant impact.
| r67276 | r67276 --jit | after --jit |
|:-------------------|:------------------|:------------------|
| 50.44369263063978 | 72.87390680773056 | 73.47873485047297 |
| 50.58788746124193 | 78.06820808947026 | 78.29723420171945 |
| 50.77509250801378 | 80.29010348842613 | 78.94689404460769 |
| 50.935361702064405 | 80.42796829926374 | 80.39539527351525 |
| 51.27352672981195 | 81.98758158033202 | 81.6754198664817 |
| 51.720715743242124 | 82.00118535811626 | 82.22960569251283 |
| 51.89643169822524 | 82.2290091613556 | 82.5013636146388 |
| 51.95895898113868 | 82.37318990939565 | 82.74002377794454 |
| 52.10124886807968 | 82.48796686037502 | 83.23354941183932 |
| 52.292280637519376 | 83.0265226541066 | 84.01552618012572 |
| 52.51856237784144 | 83.8797360318052 | 84.8588319093393 |
| 52.65076845986818 | 84.80037351256634 | 85.13577756273656 |
* Railsbench
`WARMUP=20000 BENCHMARK=1000 bin/bench` of https://github.com/k0kubun/railsbench.
It gets closer to --disable=jit.
| | r67276 | r67276 | after |
| | | --jit | --jit |
|:----------|:-------|:-------|:-------|
| req/s | 891.3 | 742.2 | 841.5 |
|:----------|:-------|:-------|:-------|
| 50%ile ms | 1.00 | 1.21 | 1.08 |
| 66%ile ms | 1.02 | 1.24 | 1.09 |
| 75%ile ms | 1.03 | 1.28 | 1.10 |
| 80%ile ms | 1.03 | 1.30 | 1.11 |
| 90%ile ms | 1.09 | 1.42 | 1.15 |
| 95%ile ms | 1.32 | 1.65 | 1.27 |
| 98%ile ms | 4.79 | 2.23 | 1.81 |
| 99%ile ms | 5.68 | 7.52 | 6.64 |
|100%ile ms | 6.52 | 9.69 | 8.59 |
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67277 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2019-03-17 08:13:38 +03:00
|
|
|
// A default threshold used to add iseq to JIT.
|
2023-04-27 04:19:44 +03:00
|
|
|
#define DEFAULT_CALL_THRESHOLD 10
|
2023-03-10 22:55:48 +03:00
|
|
|
// Size of executable memory block in MiB.
|
|
|
|
#define DEFAULT_EXEC_MEM_SIZE 64
|
mjit.c: merge MJIT infrastructure
that allows to JIT-compile Ruby methods by generating C code and
using C compiler. See the first comment of mjit.c to know what this
file does.
mjit.c is authored by Vladimir Makarov <vmakarov@redhat.com>.
After he invented great method JIT infrastructure for MRI as MJIT,
Lars Kanis <lars@greiz-reinsdorf.de> sent the patch to support MinGW
in MJIT. In addition to merging it, I ported pthread to Windows native
threads. Now this MJIT infrastructure can be compiled on Visual Studio.
This commit simplifies mjit.c to decrease code at initial merge. For
example, this commit does not provide multiple JIT threads support.
We can resurrect them later if we really want them, but I wanted to minimize
diff to make it easier to review this patch.
`/tmp/_mjitXXX` file is renamed to `/tmp/_ruby_mjitXXX` because non-Ruby
developers may not know the name "mjit" and the file name should make
sure it's from Ruby and not from some harmful programs. TODO: it may be
better to store this to some temporary directory which Ruby is already using
by Tempfile, if it's not bad for performance.
mjit.h: New. It has `mjit_exec` interface similar to `vm_exec`, which is
for triggering MJIT. This drops interface for AOT compared to the original
MJIT.
Makefile.in: define macros to let MJIT know the path of MJIT header.
Probably we can refactor this to reduce the number of macros (TODO).
win32/Makefile.sub: ditto.
common.mk: compile mjit.o and mjit_compile.o. Unlike original MJIT, this
commit separates MJIT infrastructure and JIT compiler code as independent
object files. As initial patch is NOT going to have ultra-fast JIT compiler,
it's likely to replace JIT compiler, e.g. original MJIT's compiler or some
future JIT impelementations which are not public now.
inits.c: define MJIT module. This is added because `MJIT.enabled?` was
necessary for testing.
test/lib/zombie_hunter.rb: skip if `MJIT.enabled?`. Obviously this
wouldn't work with current code when JIT is enabled.
test/ruby/test_io.rb: skip this too. This would make no sense with MJIT.
ruby.c: define MJIT CLI options. As major difference from original MJIT,
"-j:l"/"--jit:llvm" are renamed to "--jit-cc" because I want to support
not only gcc/clang but also cl.exe (Visual Studio) in the future. But it
takes only "--jit-cc=gcc", "--jit-cc=clang" for now. And only long "--jit"
options are allowed since some Ruby committers preferred it at Ruby
developers Meeting on January, and some of options are renamed.
This file also triggers to initialize MJIT thread and variables.
eval.c: finalize MJIT worker thread and variables.
test/ruby/test_rubyoptions.rb: fix number of CLI options for --jit.
thread_pthread.c: change for pthread abstraction in MJIT. Prefix rb_ for
functions which are used by other files.
thread_win32.c: ditto, for Windows. Those pthread porting is one of major
works that YARV-MJIT created, which is my fork of MJIT, in Feature 14235.
thread.c: follow rb_ prefix changes
vm.c: trigger MJIT call on VM invocation. Also trigger `mjit_mark` to avoid
SEGV by race between JIT and GC of ISeq. The improvement was provided by
wanabe <s.wanabe@gmail.com>.
In JIT compiler I created and am going to add in my next commit, I found
that having `mjit_exec` after `vm_loop_start:` is harmful because the
JIT-ed function doesn't proceed other ISeqs on RESTORE_REGS of leave insn.
Executing non-FINISH frame is unexpected for my JIT compiler and
`exception_handler` triggers executions of such ISeqs. So `mjit_exec`
here should be executed only when it directly comes from `vm_exec` call.
`RubyVM::MJIT` module and `.enabled?` method is added so that we can skip
some tests which don't expect JIT threads or compiler file descriptors.
vm_insnhelper.h: trigger MJIT on method calls during VM execution.
vm_core.h: add fields required for mjit.c. `bp` must be `cfp[6]` because
rb_control_frame_struct is likely to be casted to another struct. The
last position is the safest place to add the new field.
vm_insnhelper.c: save initial value of cfp->ep as cfp->bp. This is an
optimization which are done in both MJIT and YARV-MJIT. So this change
is added in this commit. Calculating bp from ep is a little heavy work,
so bp is kind of cache for it.
iseq.c: notify ISeq GC to MJIT. We should know which iseq in MJIT queue
is GCed to avoid SEGV. TODO: unload some GCed units in some safe way.
gc.c: add hooks so that MJIT can wait GC, and vice versa. Simultaneous
JIT and GC executions may cause SEGV and so we should synchronize them.
cont.c: save continuation information in MJIT worker. As MJIT shouldn't
unload JIT-ed code which is being used, MJIT wants to know full list of
saved execution contexts for continuation and detect ISeqs in use.
mjit_compile.c: added empty JIT compiler so that you can reuse this commit
to build your own JIT compiler. This commit tries to compile ISeqs but
all of them are considered as not supported in this commit. So you can't
use JIT compiler in this commit yet while we added --jit option now.
Patch author: Vladimir Makarov <vmakarov@redhat.com>.
Contributors:
Takashi Kokubun <takashikkbn@gmail.com>.
wanabe <s.wanabe@gmail.com>.
Lars Kanis <lars@greiz-reinsdorf.de>.
Part of Feature 12589 and 14235.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62189 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 09:58:09 +03:00
|
|
|
|
2022-01-10 17:38:16 +03:00
|
|
|
#define opt_match_noarg(s, l, name) \
|
2023-03-07 10:17:25 +03:00
|
|
|
opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --rjit-" name " is ignored"), 1) : 1)
|
2022-01-10 17:38:16 +03:00
|
|
|
#define opt_match_arg(s, l, name) \
|
2023-03-07 10:17:25 +03:00
|
|
|
opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--rjit-" name " needs an argument"), 0))
|
2022-01-10 17:38:16 +03:00
|
|
|
|
|
|
|
void
|
2023-03-19 07:27:07 +03:00
|
|
|
rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt)
|
2022-01-10 17:38:16 +03:00
|
|
|
{
|
|
|
|
const size_t l = strlen(s);
|
|
|
|
if (l == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2023-04-04 08:52:38 +03:00
|
|
|
else if (opt_match_arg(s, l, "exec-mem-size")) {
|
|
|
|
rjit_opt->exec_mem_size = atoi(s + 1);
|
|
|
|
}
|
2023-12-22 07:57:34 +03:00
|
|
|
else if (opt_match_arg(s, l, "call-threshold")) {
|
|
|
|
rjit_opt->call_threshold = atoi(s + 1);
|
|
|
|
}
|
2023-03-08 08:21:19 +03:00
|
|
|
else if (opt_match_noarg(s, l, "stats")) {
|
|
|
|
rjit_opt->stats = true;
|
2022-01-10 17:38:16 +03:00
|
|
|
}
|
2023-12-22 07:57:34 +03:00
|
|
|
else if (opt_match_noarg(s, l, "disable")) {
|
|
|
|
rjit_opt->disable = true;
|
|
|
|
}
|
|
|
|
else if (opt_match_noarg(s, l, "trace")) {
|
2023-12-22 08:25:08 +03:00
|
|
|
rjit_opt->trace = true;
|
2023-12-22 07:57:34 +03:00
|
|
|
}
|
2023-03-12 23:55:39 +03:00
|
|
|
else if (opt_match_noarg(s, l, "trace-exits")) {
|
|
|
|
rjit_opt->trace_exits = true;
|
|
|
|
}
|
2023-04-04 08:52:38 +03:00
|
|
|
else if (opt_match_noarg(s, l, "dump-disasm")) {
|
|
|
|
rjit_opt->dump_disasm = true;
|
2022-01-10 17:38:16 +03:00
|
|
|
}
|
2023-04-04 08:52:38 +03:00
|
|
|
else if (opt_match_noarg(s, l, "verify-ctx")) {
|
|
|
|
rjit_opt->verify_ctx = true;
|
2023-03-10 22:55:48 +03:00
|
|
|
}
|
2022-01-10 17:38:16 +03:00
|
|
|
else {
|
|
|
|
rb_raise(rb_eRuntimeError,
|
2023-03-07 10:15:30 +03:00
|
|
|
"invalid RJIT option `%s' (--help will show valid RJIT options)", s);
|
2022-01-10 17:38:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
|
2023-03-09 10:14:33 +03:00
|
|
|
const struct ruby_opt_message rb_rjit_option_messages[] = {
|
2023-03-10 22:55:48 +03:00
|
|
|
M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")"),
|
2023-03-08 08:16:05 +03:00
|
|
|
M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
|
2023-12-19 10:49:54 +03:00
|
|
|
M("--rjit-stats", "", "Enable collecting RJIT statistics"),
|
2023-12-22 07:57:34 +03:00
|
|
|
M("--rjit-disable", "", "Disable RJIT for lazily enabling it with RubyVM::RJIT.enable"),
|
|
|
|
M("--rjit-trace", "", "Allow TracePoint during JIT compilation"),
|
2023-12-19 10:49:54 +03:00
|
|
|
M("--rjit-trace-exits", "", "Trace side exit locations"),
|
2023-03-08 10:30:49 +03:00
|
|
|
#ifdef HAVE_LIBCAPSTONE
|
2023-03-08 08:16:05 +03:00
|
|
|
M("--rjit-dump-disasm", "", "Dump all JIT code"),
|
|
|
|
#endif
|
2022-01-10 17:38:16 +03:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
#undef M
|
|
|
|
|
2023-03-07 10:17:25 +03:00
|
|
|
struct rb_rjit_runtime_counters rb_rjit_counters = { 0 };
|
2022-12-27 09:46:40 +03:00
|
|
|
|
2023-03-06 09:09:57 +03:00
|
|
|
extern VALUE rb_gc_enable(void);
|
|
|
|
extern VALUE rb_gc_disable(void);
|
2023-12-22 04:12:40 +03:00
|
|
|
extern uint64_t rb_vm_insns_count;
|
2023-03-06 09:09:57 +03:00
|
|
|
|
2023-12-23 09:17:58 +03:00
|
|
|
// Disable GC, TracePoint, JIT, stats, and $!
|
2023-12-22 08:25:08 +03:00
|
|
|
#define WITH_RJIT_ISOLATED_USING_PC(using_pc, stmt) do { \
|
2023-03-06 09:09:57 +03:00
|
|
|
VALUE was_disabled = rb_gc_disable(); \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
2023-02-11 04:22:12 +03:00
|
|
|
rb_hook_list_t *global_hooks = rb_ec_ractor_hooks(GET_EC()); \
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_global_events = global_hooks->events; \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
|
|
|
const VALUE *pc = NULL; \
|
|
|
|
if (rb_rjit_opts.trace) { \
|
|
|
|
pc = GET_EC()->cfp->pc; \
|
|
|
|
if (!using_pc) GET_EC()->cfp->pc = 0; /* avoid crashing on calc_lineno */ \
|
|
|
|
} \
|
|
|
|
else global_hooks->events = 0; \
|
|
|
|
\
|
2023-03-09 10:14:33 +03:00
|
|
|
bool original_call_p = rb_rjit_call_p; \
|
|
|
|
rb_rjit_call_p = false; \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
|
|
|
rjit_stats_p = false; \
|
2023-12-22 04:12:40 +03:00
|
|
|
uint64_t insns_count = rb_vm_insns_count; \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
2023-12-23 09:17:58 +03:00
|
|
|
VALUE err = rb_errinfo(); \
|
|
|
|
\
|
2023-02-04 09:42:13 +03:00
|
|
|
stmt; \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
2023-12-23 09:17:58 +03:00
|
|
|
rb_set_errinfo(err); \
|
|
|
|
\
|
2023-12-22 04:12:40 +03:00
|
|
|
rb_vm_insns_count = insns_count; \
|
2023-03-09 10:14:33 +03:00
|
|
|
rjit_stats_p = rb_rjit_opts.stats; \
|
2023-12-22 08:25:08 +03:00
|
|
|
\
|
|
|
|
rb_rjit_call_p = (rjit_cancel_p ? false : original_call_p); \
|
|
|
|
\
|
|
|
|
if (rb_rjit_opts.trace) GET_EC()->cfp->pc = pc; \
|
|
|
|
else global_hooks->events = rb_rjit_global_events; \
|
|
|
|
\
|
2023-03-06 09:09:57 +03:00
|
|
|
if (!was_disabled) rb_gc_enable(); \
|
2023-02-04 09:42:13 +03:00
|
|
|
} while (0);
|
2023-12-22 08:25:08 +03:00
|
|
|
#define WITH_RJIT_ISOLATED(stmt) WITH_RJIT_ISOLATED_USING_PC(false, stmt)
|
2023-02-04 09:42:13 +03:00
|
|
|
|
2023-03-09 10:25:36 +03:00
|
|
|
void
|
|
|
|
rb_rjit_cancel_all(const char *reason)
|
|
|
|
{
|
|
|
|
if (!rb_rjit_enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rb_rjit_call_p = false;
|
|
|
|
rjit_cancel_p = true;
|
|
|
|
}
|
|
|
|
|
2022-12-27 08:37:11 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop)
|
2022-12-24 11:11:59 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_call_p) return;
|
|
|
|
rb_rjit_call_p = false;
|
2022-12-24 11:11:59 +03:00
|
|
|
}
|
|
|
|
|
2023-02-11 03:04:04 +03:00
|
|
|
static void
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_cme_invalidate(void *data)
|
2023-02-04 09:42:13 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
rb_funcall(rb_mRJITHooks, rb_intern("on_cme_invalidate"), 1, SIZET2NUM((size_t)data));
|
2023-02-04 09:42:13 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-25 00:19:42 +03:00
|
|
|
extern int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data);
|
|
|
|
|
2023-02-11 03:04:04 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_cme_invalidate(rb_callable_method_entry_t *cme)
|
2023-02-11 03:04:04 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-02-11 03:04:04 +03:00
|
|
|
// Asynchronously hook the Ruby code since running Ruby in the middle of cme invalidation is dangerous.
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_workqueue_register(0, rjit_cme_invalidate, (void *)cme);
|
2023-02-11 03:04:04 +03:00
|
|
|
}
|
|
|
|
|
2022-12-24 11:11:59 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_before_ractor_spawn(void)
|
2022-12-27 08:37:11 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_call_p) return;
|
|
|
|
rb_rjit_call_p = false;
|
2022-12-27 08:37:11 +03:00
|
|
|
}
|
|
|
|
|
2023-03-02 08:32:36 +03:00
|
|
|
static void
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_constant_state_changed(void *data)
|
2023-03-02 08:32:36 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-03-02 08:32:36 +03:00
|
|
|
RB_VM_LOCK_ENTER();
|
|
|
|
rb_vm_barrier();
|
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
rb_funcall(rb_mRJITHooks, rb_intern("on_constant_state_changed"), 1, SIZET2NUM((size_t)data));
|
2023-03-02 08:32:36 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
RB_VM_LOCK_LEAVE();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_constant_state_changed(ID id)
|
2023-03-02 08:32:36 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-03-02 08:32:36 +03:00
|
|
|
// Asynchronously hook the Ruby code since this is hooked during a "Ruby critical section".
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_workqueue_register(0, rjit_constant_state_changed, (void *)id);
|
2023-03-02 08:32:36 +03:00
|
|
|
}
|
|
|
|
|
2023-02-19 01:45:17 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx)
|
2023-02-19 01:45:17 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-02-19 01:45:17 +03:00
|
|
|
|
|
|
|
RB_VM_LOCK_ENTER();
|
|
|
|
rb_vm_barrier();
|
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
rb_funcall(rb_mRJITHooks, rb_intern("on_constant_ic_update"), 3,
|
2023-02-19 01:45:17 +03:00
|
|
|
SIZET2NUM((size_t)iseq), SIZET2NUM((size_t)ic), UINT2NUM(insn_idx));
|
|
|
|
});
|
|
|
|
|
|
|
|
RB_VM_LOCK_LEAVE();
|
|
|
|
}
|
|
|
|
|
2022-12-27 08:37:11 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events)
|
2022-12-24 11:11:59 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
rb_funcall(rb_mRJITHooks, rb_intern("on_tracing_invalidate_all"), 1, UINT2NUM(new_iseq_events));
|
2023-02-10 22:43:53 +03:00
|
|
|
});
|
2023-02-11 01:41:45 +03:00
|
|
|
}
|
|
|
|
|
2023-02-25 00:19:42 +03:00
|
|
|
static void
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_iseq_update_references(void *data)
|
2023-02-25 00:19:42 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
rb_funcall(rb_mRJITHooks, rb_intern("on_update_references"), 0);
|
2023-02-25 00:19:42 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_iseq_update_references(struct rb_iseq_constant_body *const body)
|
2023-02-25 00:19:42 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled) return;
|
2023-02-25 00:19:42 +03:00
|
|
|
|
2023-03-07 10:17:25 +03:00
|
|
|
if (body->rjit_blocks) {
|
|
|
|
body->rjit_blocks = rb_gc_location(body->rjit_blocks);
|
2023-02-25 00:52:43 +03:00
|
|
|
}
|
2023-02-25 00:19:42 +03:00
|
|
|
|
|
|
|
// Asynchronously hook the Ruby code to avoid allocation during GC.compact.
|
|
|
|
// Using _one because it's too slow to invalidate all for each ISEQ. Thus
|
|
|
|
// not giving an ISEQ pointer.
|
Change the semantics of rb_postponed_job_register
Our current implementation of rb_postponed_job_register suffers from
some safety issues that can lead to interpreter crashes (see bug #1991).
Essentially, the issue is that jobs can be called with the wrong
arguments.
We made two attempts to fix this whilst keeping the promised semantics,
but:
* The first one involved masking/unmasking when flushing jobs, which
was believed to be too expensive
* The second one involved a lock-free, multi-producer, single-consumer
ringbuffer, which was too complex
The critical insight behind this third solution is that essentially the
only user of these APIs are a) internal, or b) profiling gems.
For a), none of the usages actually require variable data; they will
work just fine with the preregistration interface.
For b), generally profiling gems only call a single callback with a
single piece of data (which is actually usually just zero) for the life
of the program. The ringbuffer is complex because it needs to support
multi-word inserts of job & data (which can't be atomic); but nobody
actually even needs that functionality, really.
So, this comit:
* Introduces a pre-registration API for jobs, with a GVL-requiring
rb_postponed_job_prereigster, which returns a handle which can be
used with an async-signal-safe rb_postponed_job_trigger.
* Deprecates rb_postponed_job_register (and re-implements it on top of
the preregister function for compatability)
* Moves all the internal usages of postponed job register
pre-registration
2023-11-19 14:54:57 +03:00
|
|
|
rb_postponed_job_trigger(rjit_iseq_update_references_pjob);
|
2023-02-25 00:19:42 +03:00
|
|
|
}
|
|
|
|
|
2023-02-25 00:52:43 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_iseq_mark(VALUE rjit_blocks)
|
2023-02-25 00:52:43 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
if (!rb_rjit_enabled) return;
|
2023-02-25 00:52:43 +03:00
|
|
|
|
|
|
|
// Note: This wasn't enough for some reason.
|
2023-03-07 10:15:30 +03:00
|
|
|
// We actually rely on RubyVM::RJIT::GC_REFS to mark this.
|
2023-03-07 10:17:25 +03:00
|
|
|
if (rjit_blocks) {
|
|
|
|
rb_gc_mark_movable(rjit_blocks);
|
2023-02-25 00:52:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-09 10:25:36 +03:00
|
|
|
// Called by rb_vm_mark()
|
|
|
|
void
|
|
|
|
rb_rjit_mark(void)
|
|
|
|
{
|
|
|
|
if (!rb_rjit_enabled)
|
|
|
|
return;
|
|
|
|
RUBY_MARK_ENTER("rjit");
|
|
|
|
|
|
|
|
// Pin object pointers used in this file
|
|
|
|
rb_gc_mark(rb_RJITCompiler);
|
|
|
|
rb_gc_mark(rb_cRJITIseqPtr);
|
|
|
|
rb_gc_mark(rb_cRJITCfpPtr);
|
|
|
|
rb_gc_mark(rb_mRJITHooks);
|
2023-03-12 23:55:39 +03:00
|
|
|
rb_gc_mark(rb_rjit_raw_samples);
|
|
|
|
rb_gc_mark(rb_rjit_line_samples);
|
2023-03-09 10:25:36 +03:00
|
|
|
|
|
|
|
RUBY_MARK_LEAVE("rjit");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_rjit_free_iseq(const rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
// TODO: implement this. GC_REFS should remove this iseq's mjit_blocks
|
|
|
|
}
|
|
|
|
|
2023-02-11 01:41:45 +03:00
|
|
|
// TODO: Use this in more places
|
|
|
|
VALUE
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_iseq_new(rb_iseq_t *iseq)
|
2023-02-11 01:41:45 +03:00
|
|
|
{
|
2023-03-07 10:15:30 +03:00
|
|
|
return rb_funcall(rb_cRJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
|
2022-12-24 11:11:59 +03:00
|
|
|
}
|
|
|
|
|
2022-12-11 10:55:33 +03:00
|
|
|
void
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_compile(const rb_iseq_t *iseq)
|
2022-12-11 10:55:33 +03:00
|
|
|
{
|
2022-12-24 02:56:05 +03:00
|
|
|
RB_VM_LOCK_ENTER();
|
|
|
|
rb_vm_barrier();
|
2022-12-12 08:16:33 +03:00
|
|
|
|
2023-12-22 08:25:08 +03:00
|
|
|
WITH_RJIT_ISOLATED_USING_PC(true, {
|
2023-03-07 10:15:30 +03:00
|
|
|
VALUE iseq_ptr = rb_funcall(rb_cRJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
|
|
|
|
VALUE cfp_ptr = rb_funcall(rb_cRJITCfpPtr, rb_intern("new"), 1, SIZET2NUM((size_t)GET_EC()->cfp));
|
|
|
|
rb_funcall(rb_RJITCompiler, rb_intern("compile"), 2, iseq_ptr, cfp_ptr);
|
2023-02-11 04:22:12 +03:00
|
|
|
});
|
2022-12-12 08:16:33 +03:00
|
|
|
|
2022-12-24 02:56:05 +03:00
|
|
|
RB_VM_LOCK_LEAVE();
|
2022-12-11 10:55:33 +03:00
|
|
|
}
|
|
|
|
|
2023-04-03 01:26:46 +03:00
|
|
|
void *
|
|
|
|
rb_rjit_entry_stub_hit(VALUE branch_stub)
|
|
|
|
{
|
|
|
|
VALUE result;
|
|
|
|
|
|
|
|
RB_VM_LOCK_ENTER();
|
|
|
|
rb_vm_barrier();
|
|
|
|
|
|
|
|
rb_control_frame_t *cfp = GET_EC()->cfp;
|
|
|
|
|
2023-12-22 08:25:08 +03:00
|
|
|
WITH_RJIT_ISOLATED_USING_PC(true, {
|
2023-04-03 01:26:46 +03:00
|
|
|
VALUE cfp_ptr = rb_funcall(rb_cRJITCfpPtr, rb_intern("new"), 1, SIZET2NUM((size_t)cfp));
|
|
|
|
result = rb_funcall(rb_RJITCompiler, rb_intern("entry_stub_hit"), 2, branch_stub, cfp_ptr);
|
|
|
|
});
|
|
|
|
|
|
|
|
RB_VM_LOCK_LEAVE();
|
|
|
|
|
|
|
|
return (void *)NUM2SIZET(result);
|
|
|
|
}
|
|
|
|
|
2023-01-08 00:21:14 +03:00
|
|
|
void *
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_branch_stub_hit(VALUE branch_stub, int sp_offset, int target0_p)
|
2023-01-08 00:21:14 +03:00
|
|
|
{
|
|
|
|
VALUE result;
|
|
|
|
|
|
|
|
RB_VM_LOCK_ENTER();
|
|
|
|
rb_vm_barrier();
|
|
|
|
|
|
|
|
rb_control_frame_t *cfp = GET_EC()->cfp;
|
|
|
|
cfp->sp += sp_offset; // preserve stack values, also using the actual sp_offset to make jit.peek_at_stack work
|
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
WITH_RJIT_ISOLATED({
|
|
|
|
VALUE cfp_ptr = rb_funcall(rb_cRJITCfpPtr, rb_intern("new"), 1, SIZET2NUM((size_t)cfp));
|
|
|
|
result = rb_funcall(rb_RJITCompiler, rb_intern("branch_stub_hit"), 3, branch_stub, cfp_ptr, RBOOL(target0_p));
|
2023-02-11 04:22:12 +03:00
|
|
|
});
|
2023-01-01 00:41:32 +03:00
|
|
|
|
2023-01-04 11:12:16 +03:00
|
|
|
cfp->sp -= sp_offset; // reset for consistency with the code without the stub
|
2023-01-01 00:41:32 +03:00
|
|
|
|
|
|
|
RB_VM_LOCK_LEAVE();
|
|
|
|
|
|
|
|
return (void *)NUM2SIZET(result);
|
|
|
|
}
|
|
|
|
|
2022-12-11 10:39:21 +03:00
|
|
|
void
|
2023-03-19 07:27:07 +03:00
|
|
|
rb_rjit_init(const struct rb_rjit_options *opts)
|
2022-12-11 10:39:21 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
VM_ASSERT(rb_rjit_enabled);
|
2022-12-11 10:39:21 +03:00
|
|
|
|
2023-03-10 22:55:48 +03:00
|
|
|
// Normalize options
|
|
|
|
rb_rjit_opts = *opts;
|
|
|
|
if (rb_rjit_opts.exec_mem_size == 0)
|
|
|
|
rb_rjit_opts.exec_mem_size = DEFAULT_EXEC_MEM_SIZE;
|
|
|
|
if (rb_rjit_opts.call_threshold == 0)
|
|
|
|
rb_rjit_opts.call_threshold = DEFAULT_CALL_THRESHOLD;
|
|
|
|
#ifndef HAVE_LIBCAPSTONE
|
|
|
|
if (rb_rjit_opts.dump_disasm)
|
|
|
|
rb_warn("libcapstone has not been linked. Ignoring --rjit-dump-disasm.");
|
|
|
|
#endif
|
2022-12-12 08:32:24 +03:00
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
// RJIT doesn't support miniruby, but it might reach here by RJIT_FORCE_ENABLE.
|
|
|
|
rb_mRJIT = rb_const_get(rb_cRubyVM, rb_intern("RJIT"));
|
|
|
|
if (!rb_const_defined(rb_mRJIT, rb_intern("Compiler"))) {
|
2023-03-09 10:12:49 +03:00
|
|
|
rb_warn("Disabling RJIT because RubyVM::RJIT::Compiler is not defined");
|
2023-03-09 10:14:33 +03:00
|
|
|
rb_rjit_enabled = false;
|
2022-12-11 10:39:21 +03:00
|
|
|
return;
|
|
|
|
}
|
2023-12-09 19:47:14 +03:00
|
|
|
rjit_iseq_update_references_pjob = rb_postponed_job_preregister(0, rjit_iseq_update_references, NULL);
|
Change the semantics of rb_postponed_job_register
Our current implementation of rb_postponed_job_register suffers from
some safety issues that can lead to interpreter crashes (see bug #1991).
Essentially, the issue is that jobs can be called with the wrong
arguments.
We made two attempts to fix this whilst keeping the promised semantics,
but:
* The first one involved masking/unmasking when flushing jobs, which
was believed to be too expensive
* The second one involved a lock-free, multi-producer, single-consumer
ringbuffer, which was too complex
The critical insight behind this third solution is that essentially the
only user of these APIs are a) internal, or b) profiling gems.
For a), none of the usages actually require variable data; they will
work just fine with the preregistration interface.
For b), generally profiling gems only call a single callback with a
single piece of data (which is actually usually just zero) for the life
of the program. The ringbuffer is complex because it needs to support
multi-word inserts of job & data (which can't be atomic); but nobody
actually even needs that functionality, really.
So, this comit:
* Introduces a pre-registration API for jobs, with a GVL-requiring
rb_postponed_job_prereigster, which returns a handle which can be
used with an async-signal-safe rb_postponed_job_trigger.
* Deprecates rb_postponed_job_register (and re-implements it on top of
the preregister function for compatability)
* Moves all the internal usages of postponed job register
pre-registration
2023-11-19 14:54:57 +03:00
|
|
|
if (rjit_iseq_update_references_pjob == POSTPONED_JOB_HANDLE_INVALID) {
|
|
|
|
rb_bug("Could not preregister postponed job for RJIT");
|
|
|
|
}
|
2023-03-07 10:15:30 +03:00
|
|
|
rb_mRJITC = rb_const_get(rb_mRJIT, rb_intern("C"));
|
|
|
|
VALUE rb_cRJITCompiler = rb_const_get(rb_mRJIT, rb_intern("Compiler"));
|
2023-03-10 22:55:48 +03:00
|
|
|
rb_RJITCompiler = rb_funcall(rb_cRJITCompiler, rb_intern("new"), 0);
|
2023-03-07 10:15:30 +03:00
|
|
|
rb_cRJITIseqPtr = rb_funcall(rb_mRJITC, rb_intern("rb_iseq_t"), 0);
|
|
|
|
rb_cRJITCfpPtr = rb_funcall(rb_mRJITC, rb_intern("rb_control_frame_t"), 0);
|
|
|
|
rb_mRJITHooks = rb_const_get(rb_mRJIT, rb_intern("Hooks"));
|
2023-03-12 23:55:39 +03:00
|
|
|
if (rb_rjit_opts.trace_exits) {
|
|
|
|
rb_rjit_raw_samples = rb_ary_new();
|
|
|
|
rb_rjit_line_samples = rb_ary_new();
|
|
|
|
}
|
2022-12-11 10:39:21 +03:00
|
|
|
|
2023-03-10 22:55:48 +03:00
|
|
|
// Enable RJIT and stats from here
|
2023-12-22 01:24:10 +03:00
|
|
|
rb_rjit_call_p = !rb_rjit_opts.disable;
|
2023-03-09 10:14:33 +03:00
|
|
|
rjit_stats_p = rb_rjit_opts.stats;
|
2022-12-11 10:39:21 +03:00
|
|
|
}
|
|
|
|
|
2023-03-09 10:37:58 +03:00
|
|
|
//
|
|
|
|
// Primitive for rjit.rb
|
|
|
|
//
|
|
|
|
|
2023-03-12 23:55:39 +03:00
|
|
|
// Same as `rb_rjit_opts.stats`, but this is used before rb_rjit_opts is set.
|
2022-12-28 10:57:16 +03:00
|
|
|
static VALUE
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self)
|
2022-12-28 10:57:16 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
return RBOOL(rb_rjit_stats_enabled);
|
2022-12-28 10:57:16 +03:00
|
|
|
}
|
|
|
|
|
2023-03-12 23:55:39 +03:00
|
|
|
// Same as `rb_rjit_opts.trace_exits`, but this is used before rb_rjit_opts is set.
|
|
|
|
static VALUE
|
|
|
|
rjit_trace_exits_enabled_p(rb_execution_context_t *ec, VALUE self)
|
|
|
|
{
|
|
|
|
return RBOOL(rb_rjit_trace_exits_enabled);
|
|
|
|
}
|
|
|
|
|
2023-02-07 11:00:09 +03:00
|
|
|
// Disable anything that could impact stats. It ends up disabling JIT calls as well.
|
|
|
|
static VALUE
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_stop_stats(rb_execution_context_t *ec, VALUE self)
|
2023-02-07 11:00:09 +03:00
|
|
|
{
|
2023-03-09 10:14:33 +03:00
|
|
|
rb_rjit_call_p = false;
|
2023-03-07 10:17:25 +03:00
|
|
|
rjit_stats_p = false;
|
2023-02-07 11:00:09 +03:00
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2023-03-07 10:17:25 +03:00
|
|
|
#include "rjit.rbinc"
|
2022-06-15 20:19:33 +03:00
|
|
|
|
2023-03-07 10:15:30 +03:00
|
|
|
#endif // USE_RJIT
|