[Feature #21116] Extract RJIT as a third-party gem

This commit is contained in:
Nobuyoshi Nakada 2025-02-13 15:59:16 +09:00
parent d35cc0cc77
commit 4a67ef09cc
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2025-02-13 09:51:08 +00:00
83 changed files with 50 additions and 14787 deletions

View File

@ -19,7 +19,6 @@ hash.rb
io.rb
kernel.rb
marshal.rb
rjit.rb
numeric.rb
nilclass.rb
pack.rb

View File

@ -193,7 +193,7 @@ jobs:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with: { sparse-checkout-cone-mode: false, sparse-checkout: /.github }
- { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } }
- { uses: './.github/actions/compilers', name: 'disable-jit', with: { append_configure: '--disable-yjit --disable-rjit' } }
- { uses: './.github/actions/compilers', name: 'disable-jit', with: { append_configure: '--disable-yjit' } }
- { uses: './.github/actions/compilers', name: 'disable-dln', with: { append_configure: '--disable-dln' } }
- { uses: './.github/actions/compilers', name: 'enable-mkmf-verbose', with: { append_configure: '--enable-mkmf-verbose' } }
- { uses: './.github/actions/compilers', name: 'disable-rubygems', with: { append_configure: '--disable-rubygems' } }
@ -295,10 +295,8 @@ jobs:
- { uses: './.github/actions/setup/directories', with: { srcdir: 'src', builddir: 'build', makeup: true, fetch-depth: 10 } }
- { uses: './.github/actions/compilers', name: 'VM_DEBUG_BP_CHECK', with: { cppflags: '-DVM_DEBUG_BP_CHECK' } }
- { uses: './.github/actions/compilers', name: 'VM_DEBUG_VERIFY_METHOD_CACHE', with: { cppflags: '-DVM_DEBUG_VERIFY_METHOD_CACHE' } }
- { uses: './.github/actions/compilers', name: 'enable-yjit', with: { append_configure: '--enable-yjit --disable-rjit' } }
- { uses: './.github/actions/compilers', name: 'enable-rjit', with: { append_configure: '--enable-rjit --disable-yjit' } }
- { uses: './.github/actions/compilers', name: 'enable-yjit', with: { append_configure: '--enable-yjit' } }
- { uses: './.github/actions/compilers', name: 'YJIT_FORCE_ENABLE', with: { cppflags: '-DYJIT_FORCE_ENABLE' } }
- { uses: './.github/actions/compilers', name: 'RJIT_FORCE_ENABLE', with: { cppflags: '-DRJIT_FORCE_ENABLE' } }
- { uses: './.github/actions/compilers', name: 'UNIVERSAL_PARSER', with: { cppflags: '-DUNIVERSAL_PARSER' } }
compilemax:

View File

@ -1,86 +0,0 @@
name: RJIT bindgen
on:
push:
paths-ignore:
- 'doc/**'
- '**/man/*'
- '**.md'
- '**.rdoc'
- '**/.document'
- '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
- '**/man/*'
- '**.md'
- '**.rdoc'
- '**/.document'
- '.*.yml'
merge_group:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
permissions:
contents: read
jobs:
make:
strategy:
matrix:
include:
- task: rjit-bindgen
fail-fast: false
runs-on: ubuntu-22.04
if: >-
${{!(false
|| contains(github.event.head_commit.message, '[DOC]')
|| contains(github.event.head_commit.message, 'Document')
|| contains(github.event.pull_request.title, '[DOC]')
|| contains(github.event.pull_request.title, 'Document')
|| contains(github.event.pull_request.labels.*.name, 'Documentation')
|| (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]')
)}}
steps:
- name: Set up Ruby
uses: ruby/setup-ruby@a6e6f86333f0a2523ece813039b8b4be04560854 # v1.190.0
with:
ruby-version: '3.1'
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
- uses: ./.github/actions/setup/ubuntu
- uses: ./.github/actions/setup/directories
with:
srcdir: src
builddir: build
makeup: true
- name: Run configure
run: ../src/configure -C --disable-install-doc --prefix=$(pwd)/install --enable-yjit=dev_nodebug
- run: make
- run: make install
- run: make ${{ matrix.task }}
- run: git diff --exit-code
working-directory: src
- uses: ./.github/actions/slack
with:
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
if: ${{ failure() }}
defaults:
run:
working-directory: build

View File

@ -1,131 +0,0 @@
name: RJIT
on:
push:
paths-ignore:
- 'doc/**'
- '**.md'
- '**.rdoc'
- '**/.document'
- '**.[1-8]'
- '**.ronn'
- '.*.yml'
pull_request:
paths-ignore:
- 'doc/**'
- '**.md'
- '**.rdoc'
- '**/.document'
- '**.[1-8]'
- '**.ronn'
- '.*.yml'
merge_group:
concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}
permissions:
contents: read
jobs:
make:
strategy:
matrix:
# main variables included in the job name
test_task: [check]
run_opts: ['--rjit-call-threshold=1']
arch: ['']
fail-fast: false
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
RUBY_DEBUG: ci
SETARCH: ${{ matrix.arch && format('setarch {0}', matrix.arch) }}
runs-on: ubuntu-22.04
if: >-
${{!(false
|| contains(github.event.head_commit.message, '[DOC]')
|| contains(github.event.head_commit.message, 'Document')
|| contains(github.event.pull_request.title, '[DOC]')
|| contains(github.event.pull_request.title, 'Document')
|| contains(github.event.pull_request.labels.*.name, 'Documentation')
|| (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]')
)}}
steps:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
sparse-checkout-cone-mode: false
sparse-checkout: /.github
- uses: ./.github/actions/setup/ubuntu
- uses: ./.github/actions/setup/directories
with:
srcdir: src
builddir: build
makeup: true
# Set fetch-depth: 10 so that Launchable can receive commits information.
fetch-depth: 10
- name: Run configure
env:
arch: ${{ matrix.arch }}
run: >-
$SETARCH ../src/configure -C --disable-install-doc cppflags=-DRUBY_DEBUG
${arch:+--target=$arch-$OSTYPE --host=$arch-$OSTYPE}
- run: $SETARCH make
- name: Set up Launchable
uses: ./.github/actions/launchable/setup
with:
os: ubuntu-22.04
launchable-token: ${{ secrets.LAUNCHABLE_TOKEN }}
builddir: build
srcdir: src
test-opts: ${{ matrix.run_opts }}
test-tasks: '["test", "test-all", "test-spec"]'
continue-on-error: true
- name: make test
run: |
$SETARCH make -s test RUN_OPTS="$RUN_OPTS"
timeout-minutes: 30
env:
GNUMAKEFLAGS: ''
RUBY_TESTOPTS: >-
${{ env.TESTS }}
--tty=no
RUN_OPTS: ${{ matrix.run_opts }}
- name: make test-all
run: >-
$SETARCH make -s test-all
RUN_OPTS="$RUN_OPTS"
${TESTS:+TESTS="$TESTS"}
timeout-minutes: 60
env:
GNUMAKEFLAGS: ''
RUBY_TESTOPTS: '-q --tty=no'
RUN_OPTS: ${{ matrix.run_opts }}
- name: make test-spec
run: |
$SETARCH make -s test-spec RUN_OPTS="$RUN_OPTS"
timeout-minutes: 10
env:
GNUMAKEFLAGS: ''
RUN_OPTS: ${{ matrix.run_opts }}
- uses: ./.github/actions/slack
with:
label: ${{ matrix.run_opts }}
SLACK_WEBHOOK_URL: ${{ secrets.SIMPLER_ALERTS_URL }} # ruby-lang slack: ruby/simpler-alerts-bot
if: ${{ failure() }}
defaults:
run:
working-directory: build

6
.gitignore vendored
View File

@ -241,12 +241,6 @@ lcov*.info
# /win32/
/win32/*.ico
# RJIT
/include/ruby-*/*/rb_rjit_min_header-*.h
/lib/ruby_vm/rjit/instruction.rb
/lib/ruby_vm/rjit/instruction.rb
/rjit_config.h
/rb_rjit_header.h*
# YJIT
/yjit-bench

View File

@ -1,5 +1,4 @@
[![Actions Status: MinGW](https://github.com/ruby/ruby/workflows/MinGW/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
[![Actions Status: RJIT](https://github.com/ruby/ruby/workflows/RJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/0sy8rrxut4o0k960/branch/master?svg=true)](https://ci.appveyor.com/project/ruby/ruby/branch/master)

View File

@ -1,5 +1,4 @@
[![Actions Status: MinGW](https://github.com/ruby/ruby/workflows/MinGW/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
[![Actions Status: RJIT](https://github.com/ruby/ruby/workflows/RJIT/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
[![Actions Status: Ubuntu](https://github.com/ruby/ruby/workflows/Ubuntu/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
[![Actions Status: Windows](https://github.com/ruby/ruby/workflows/Windows/badge.svg)](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
[![Travis Status](https://app.travis-ci.com/ruby/ruby.svg?branch=master)](https://app.travis-ci.com/ruby/ruby)

View File

@ -163,7 +163,7 @@ def main
BT.tty = nil
BT.quiet = false
BT.timeout = 180
BT.timeout_scale = (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 3 : 1) # for --jit-wait
BT.timeout_scale = 1
if (ts = (ENV["RUBY_TEST_TIMEOUT_SCALE"] || ENV["RUBY_TEST_SUBPROCESS_TIMEOUT_SCALE"]).to_i) > 1
BT.timeout_scale *= ts
end
@ -891,9 +891,4 @@ def yjit_enabled?
ENV.key?('RUBY_YJIT_ENABLE') || ENV.fetch('RUN_OPTS', '').include?('yjit') || BT.ruby.include?('yjit')
end
def rjit_enabled?
# Don't check `RubyVM::RJIT.enabled?`. On btest-bruby, target Ruby != runner Ruby.
ENV.fetch('RUN_OPTS', '').include?('rjit')
end
exit main

View File

@ -14,7 +14,7 @@ ms = "a".."k"
o.send(meth)
end
end
}, '[ruby-dev:39453]' unless rjit_enabled? # speed up RJIT CI
}, '[ruby-dev:39453]'
assert_normal_exit %q{
a = []

View File

@ -12,7 +12,7 @@ assert_equal 'ok', %q{
}
}.map {|t| t.value }
vs[0] == M && vs[1] == M ? :ok : :ng
}, '[ruby-dev:32048]' unless ENV.fetch('RUN_OPTS', '').include?('rjit') # Thread seems to be switching during JIT. To be fixed later.
}, '[ruby-dev:32048]'
assert_equal 'ok', %q{
%w[a a/foo b].each {|d| Dir.mkdir(d)}

View File

@ -1599,7 +1599,7 @@ assert_equal "ok", %q{
1_000.times { idle_worker, tmp_reporter = Ractor.select(*workers) }
"ok"
} unless yjit_enabled? || rjit_enabled? # flaky
} unless yjit_enabled? # flaky
assert_equal "ok", %q{
def foo(*); ->{ super }; end

View File

@ -848,7 +848,7 @@ assert_normal_exit %q{
def x(a=1, b, *rest); nil end
end
end
}, bug2415 unless rjit_enabled? # flaky
}, bug2415
assert_normal_exit %q{
0.times do
@ -880,7 +880,7 @@ assert_normal_exit %q{
end
end
end
}, bug2415 unless rjit_enabled? # flaky
}, bug2415
assert_normal_exit %q{
a {

View File

@ -301,7 +301,7 @@ assert_normal_exit %q{
}.each {|t|
t.join
}
} unless rjit_enabled? # flaky
}
assert_equal 'ok', %q{
def m
@ -493,7 +493,8 @@ assert_equal 'foo', %q{
[th1, th2].each {|t| t.join }
GC.start
f.call.source
} unless rjit_enabled? # flaky
}
assert_normal_exit %q{
class C
def inspect

View File

@ -114,7 +114,7 @@ assert_equal '[:ae, :ae]', %q{
end
[test(Array.new 5), test([])]
} unless rjit_enabled? # Not yet working on RJIT
}
# regression test for arity check with splat and send
assert_equal '[:ae, :ae]', %q{
@ -174,7 +174,7 @@ assert_equal 'ok', %q{
GC.compact
end
:ok
} unless rjit_enabled? # Not yet working on RJIT
}
# regression test for overly generous guard elision
assert_equal '[0, :sum, 0, :sum]', %q{
@ -311,7 +311,7 @@ assert_equal '[:ok]', %q{
# Used to crash due to GC run in rb_ensure_iv_list_size()
# not marking the newly allocated [:ok].
RegressionTest.new.extender.itself
} unless rjit_enabled? # Skip on RJIT since this uncovers a crash
}
assert_equal 'true', %q{
# regression test for tracking type of locals for too long
@ -2065,7 +2065,7 @@ assert_equal '[97, :nil, 97, :nil, :raised]', %q{
getbyte("a", 0)
[getbyte("a", 0), getbyte("a", 1), getbyte("a", -1), getbyte("a", -2), getbyte("a", "a")]
} unless rjit_enabled? # Not yet working on RJIT
}
# Basic test for String#setbyte
assert_equal 'AoZ', %q{
@ -2763,7 +2763,7 @@ assert_equal '[1, 2]', %q{
expandarray_redefined_nilclass
expandarray_redefined_nilclass
} unless rjit_enabled?
}
assert_equal '[1, 2, nil]', %q{
def expandarray_rhs_too_small
@ -2875,7 +2875,7 @@ assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
events.compiled(events)
events
} unless rjit_enabled? # RJIT calls extra Ruby methods
}
# test enabling a TracePoint that targets a particular line in a C method call
assert_equal '[true]', %q{
@ -2957,7 +2957,7 @@ assert_equal '[[:c_call, :itself]]', %q{
tp.enable { shouldnt_compile }
events
} unless rjit_enabled? # RJIT calls extra Ruby methods
}
# test enabling c_return tracing before compiling
assert_equal '[[:c_return, :itself, main]]', %q{
@ -2972,7 +2972,7 @@ assert_equal '[[:c_return, :itself, main]]', %q{
tp.enable { shouldnt_compile }
events
} unless rjit_enabled? # RJIT calls extra Ruby methods
}
# test c_call invalidation
assert_equal '[[:c_call, :itself]]', %q{
@ -4494,7 +4494,7 @@ assert_equal 'true', %q{
rescue ArgumentError
true
end
} unless rjit_enabled? # Not yet working on RJIT
}
# Regression test: register allocator on expandarray
assert_equal '[]', %q{
@ -4909,7 +4909,7 @@ assert_equal '0', %q{
end
foo # try again
} unless rjit_enabled? # doesn't work on RJIT
}
# test integer left shift with constant rhs
assert_equal [0x80000000000, 'a+', :ok].inspect, %q{
@ -5017,7 +5017,7 @@ assert_equal '[[true, false, false], [true, true, false], [true, :error, :error]
end
results << test
} unless rjit_enabled? # Not yet working on RJIT
}
# test FalseClass#=== before and after redefining FalseClass#==
assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{
@ -5052,7 +5052,7 @@ assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]
end
results << test
} unless rjit_enabled? # Not yet working on RJIT
}
# test NilClass#=== before and after redefining NilClass#==
assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]]', %q{
@ -5087,7 +5087,7 @@ assert_equal '[[true, false, false], [true, false, true], [true, :error, :error]
end
results << test
} unless rjit_enabled? # Not yet working on RJIT
}
# test struct accessors fire c_call events
assert_equal '[[:c_call, :x=], [:c_call, :x]]', %q{

537
common.mk
View File

@ -139,8 +139,6 @@ COMMONOBJS = array.$(OBJEXT) \
marshal.$(OBJEXT) \
math.$(OBJEXT) \
memory_view.$(OBJEXT) \
rjit.$(OBJEXT) \
rjit_c.$(OBJEXT) \
node.$(OBJEXT) \
node_dump.$(OBJEXT) \
numeric.$(OBJEXT) \
@ -349,16 +347,6 @@ all: $(SHOWFLAGS) main
main: $(SHOWFLAGS) exts $(ENCSTATIC:static=lib)encs
@$(NULLCMD)
main: $(srcdir)/lib/ruby_vm/rjit/instruction.rb
srcs: $(srcdir)/lib/ruby_vm/rjit/instruction.rb
$(srcdir)/lib/ruby_vm/rjit/instruction.rb: $(tooldir)/insns2vm.rb $(tooldir)/ruby_vm/views/lib/ruby_vm/rjit/instruction.rb.erb $(srcdir)/insns.def
$(ECHO) generating $@
$(Q) $(BASERUBY) -Ku $(tooldir)/insns2vm.rb --basedir="$(srcdir)" $(INSNS2VMOPT) $@
.PHONY: rjit-bindgen
rjit-bindgen:
$(Q) $(BASERUBY) -rrubygems -C $(srcdir)/tool/rjit bindgen.rb $(CURDIR)
.PHONY: showflags
exts enc trans: $(SHOWFLAGS)
showflags:
@ -1206,8 +1194,6 @@ BUILTIN_RB_SRCS = \
$(srcdir)/numeric.rb \
$(srcdir)/io.rb \
$(srcdir)/marshal.rb \
$(srcdir)/rjit.rb \
$(srcdir)/rjit_c.rb \
$(srcdir)/pack.rb \
$(srcdir)/trace_point.rb \
$(srcdir)/warning.rb \
@ -4066,7 +4052,6 @@ cont.$(OBJEXT): {$(VPATH)}prism/version.h
cont.$(OBJEXT): {$(VPATH)}prism_compile.h
cont.$(OBJEXT): {$(VPATH)}ractor.h
cont.$(OBJEXT): {$(VPATH)}ractor_core.h
cont.$(OBJEXT): {$(VPATH)}rjit.h
cont.$(OBJEXT): {$(VPATH)}ruby_assert.h
cont.$(OBJEXT): {$(VPATH)}ruby_atomic.h
cont.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -7055,7 +7040,6 @@ eval.$(OBJEXT): {$(VPATH)}probes.h
eval.$(OBJEXT): {$(VPATH)}probes_helper.h
eval.$(OBJEXT): {$(VPATH)}ractor.h
eval.$(OBJEXT): {$(VPATH)}ractor_core.h
eval.$(OBJEXT): {$(VPATH)}rjit.h
eval.$(OBJEXT): {$(VPATH)}ruby_assert.h
eval.$(OBJEXT): {$(VPATH)}ruby_atomic.h
eval.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -7544,7 +7528,6 @@ gc.$(OBJEXT): {$(VPATH)}re.h
gc.$(OBJEXT): {$(VPATH)}regenc.h
gc.$(OBJEXT): {$(VPATH)}regex.h
gc.$(OBJEXT): {$(VPATH)}regint.h
gc.$(OBJEXT): {$(VPATH)}rjit.h
gc.$(OBJEXT): {$(VPATH)}ruby_assert.h
gc.$(OBJEXT): {$(VPATH)}ruby_atomic.h
gc.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -9101,7 +9084,6 @@ iseq.$(OBJEXT): {$(VPATH)}prism/prism.h
iseq.$(OBJEXT): {$(VPATH)}prism/version.h
iseq.$(OBJEXT): {$(VPATH)}prism_compile.h
iseq.$(OBJEXT): {$(VPATH)}ractor.h
iseq.$(OBJEXT): {$(VPATH)}rjit.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
iseq.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -10469,7 +10451,6 @@ miniinit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
miniinit.$(OBJEXT): $(CCAN_DIR)/list/list.h
miniinit.$(OBJEXT): $(CCAN_DIR)/str/str.h
miniinit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
miniinit.$(OBJEXT): $(srcdir)/rjit_c.rb
miniinit.$(OBJEXT): $(top_srcdir)/internal/array.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
miniinit.$(OBJEXT): $(top_srcdir)/internal/bignum.h
@ -10704,8 +10685,6 @@ miniinit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
miniinit.$(OBJEXT): {$(VPATH)}prism/version.h
miniinit.$(OBJEXT): {$(VPATH)}prism_compile.h
miniinit.$(OBJEXT): {$(VPATH)}ractor.rb
miniinit.$(OBJEXT): {$(VPATH)}rjit.rb
miniinit.$(OBJEXT): {$(VPATH)}rjit_c.rb
miniinit.$(OBJEXT): {$(VPATH)}ruby_assert.h
miniinit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
miniinit.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -13520,7 +13499,6 @@ process.$(OBJEXT): {$(VPATH)}onigmo.h
process.$(OBJEXT): {$(VPATH)}oniguruma.h
process.$(OBJEXT): {$(VPATH)}process.c
process.$(OBJEXT): {$(VPATH)}ractor.h
process.$(OBJEXT): {$(VPATH)}rjit.h
process.$(OBJEXT): {$(VPATH)}ruby_assert.h
process.$(OBJEXT): {$(VPATH)}ruby_atomic.h
process.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -13745,7 +13723,6 @@ ractor.$(OBJEXT): {$(VPATH)}ractor.c
ractor.$(OBJEXT): {$(VPATH)}ractor.h
ractor.$(OBJEXT): {$(VPATH)}ractor.rbinc
ractor.$(OBJEXT): {$(VPATH)}ractor_core.h
ractor.$(OBJEXT): {$(VPATH)}rjit.h
ractor.$(OBJEXT): {$(VPATH)}ruby_assert.h
ractor.$(OBJEXT): {$(VPATH)}ruby_atomic.h
ractor.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -15562,515 +15539,6 @@ regsyntax.$(OBJEXT): {$(VPATH)}regint.h
regsyntax.$(OBJEXT): {$(VPATH)}regsyntax.c
regsyntax.$(OBJEXT): {$(VPATH)}st.h
regsyntax.$(OBJEXT): {$(VPATH)}subst.h
rjit.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
rjit.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
rjit.$(OBJEXT): $(CCAN_DIR)/list/list.h
rjit.$(OBJEXT): $(CCAN_DIR)/str/str.h
rjit.$(OBJEXT): $(hdrdir)/ruby.h
rjit.$(OBJEXT): $(hdrdir)/ruby/ruby.h
rjit.$(OBJEXT): $(hdrdir)/ruby/version.h
rjit.$(OBJEXT): $(top_srcdir)/internal/array.h
rjit.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
rjit.$(OBJEXT): $(top_srcdir)/internal/class.h
rjit.$(OBJEXT): $(top_srcdir)/internal/cmdlineopt.h
rjit.$(OBJEXT): $(top_srcdir)/internal/compile.h
rjit.$(OBJEXT): $(top_srcdir)/internal/compilers.h
rjit.$(OBJEXT): $(top_srcdir)/internal/cont.h
rjit.$(OBJEXT): $(top_srcdir)/internal/file.h
rjit.$(OBJEXT): $(top_srcdir)/internal/gc.h
rjit.$(OBJEXT): $(top_srcdir)/internal/hash.h
rjit.$(OBJEXT): $(top_srcdir)/internal/imemo.h
rjit.$(OBJEXT): $(top_srcdir)/internal/process.h
rjit.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
rjit.$(OBJEXT): $(top_srcdir)/internal/serial.h
rjit.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
rjit.$(OBJEXT): $(top_srcdir)/internal/string.h
rjit.$(OBJEXT): $(top_srcdir)/internal/struct.h
rjit.$(OBJEXT): $(top_srcdir)/internal/variable.h
rjit.$(OBJEXT): $(top_srcdir)/internal/vm.h
rjit.$(OBJEXT): $(top_srcdir)/internal/warnings.h
rjit.$(OBJEXT): $(top_srcdir)/prism/defines.h
rjit.$(OBJEXT): $(top_srcdir)/prism/encoding.h
rjit.$(OBJEXT): $(top_srcdir)/prism/node.h
rjit.$(OBJEXT): $(top_srcdir)/prism/options.h
rjit.$(OBJEXT): $(top_srcdir)/prism/pack.h
rjit.$(OBJEXT): $(top_srcdir)/prism/parser.h
rjit.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
rjit.$(OBJEXT): $(top_srcdir)/prism/prism.h
rjit.$(OBJEXT): $(top_srcdir)/prism/regexp.h
rjit.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
rjit.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
rjit.$(OBJEXT): {$(VPATH)}assert.h
rjit.$(OBJEXT): {$(VPATH)}atomic.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/assume.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/bool.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/limits.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
rjit.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rjit.$(OBJEXT): {$(VPATH)}builtin.h
rjit.$(OBJEXT): {$(VPATH)}config.h
rjit.$(OBJEXT): {$(VPATH)}constant.h
rjit.$(OBJEXT): {$(VPATH)}debug.h
rjit.$(OBJEXT): {$(VPATH)}debug_counter.h
rjit.$(OBJEXT): {$(VPATH)}defines.h
rjit.$(OBJEXT): {$(VPATH)}dln.h
rjit.$(OBJEXT): {$(VPATH)}encoding.h
rjit.$(OBJEXT): {$(VPATH)}id.h
rjit.$(OBJEXT): {$(VPATH)}id_table.h
rjit.$(OBJEXT): {$(VPATH)}insns.def
rjit.$(OBJEXT): {$(VPATH)}insns.inc
rjit.$(OBJEXT): {$(VPATH)}insns_info.inc
rjit.$(OBJEXT): {$(VPATH)}intern.h
rjit.$(OBJEXT): {$(VPATH)}internal.h
rjit.$(OBJEXT): {$(VPATH)}internal/abi.h
rjit.$(OBJEXT): {$(VPATH)}internal/anyargs.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
rjit.$(OBJEXT): {$(VPATH)}internal/assume.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/const.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/error.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/format.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
rjit.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
rjit.$(OBJEXT): {$(VPATH)}internal/cast.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
rjit.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
rjit.$(OBJEXT): {$(VPATH)}internal/config.h
rjit.$(OBJEXT): {$(VPATH)}internal/constant_p.h
rjit.$(OBJEXT): {$(VPATH)}internal/core.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/robject.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
rjit.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
rjit.$(OBJEXT): {$(VPATH)}internal/ctype.h
rjit.$(OBJEXT): {$(VPATH)}internal/dllexport.h
rjit.$(OBJEXT): {$(VPATH)}internal/dosish.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
rjit.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
rjit.$(OBJEXT): {$(VPATH)}internal/error.h
rjit.$(OBJEXT): {$(VPATH)}internal/eval.h
rjit.$(OBJEXT): {$(VPATH)}internal/event.h
rjit.$(OBJEXT): {$(VPATH)}internal/fl_type.h
rjit.$(OBJEXT): {$(VPATH)}internal/gc.h
rjit.$(OBJEXT): {$(VPATH)}internal/glob.h
rjit.$(OBJEXT): {$(VPATH)}internal/globals.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/extension.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/feature.h
rjit.$(OBJEXT): {$(VPATH)}internal/has/warning.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/array.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/class.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/error.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/file.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/io.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/load.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/object.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/process.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/random.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/range.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/re.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/select.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/string.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/time.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
rjit.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
rjit.$(OBJEXT): {$(VPATH)}internal/interpreter.h
rjit.$(OBJEXT): {$(VPATH)}internal/iterator.h
rjit.$(OBJEXT): {$(VPATH)}internal/memory.h
rjit.$(OBJEXT): {$(VPATH)}internal/method.h
rjit.$(OBJEXT): {$(VPATH)}internal/module.h
rjit.$(OBJEXT): {$(VPATH)}internal/newobj.h
rjit.$(OBJEXT): {$(VPATH)}internal/scan_args.h
rjit.$(OBJEXT): {$(VPATH)}internal/special_consts.h
rjit.$(OBJEXT): {$(VPATH)}internal/static_assert.h
rjit.$(OBJEXT): {$(VPATH)}internal/stdalign.h
rjit.$(OBJEXT): {$(VPATH)}internal/stdbool.h
rjit.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
rjit.$(OBJEXT): {$(VPATH)}internal/symbol.h
rjit.$(OBJEXT): {$(VPATH)}internal/value.h
rjit.$(OBJEXT): {$(VPATH)}internal/value_type.h
rjit.$(OBJEXT): {$(VPATH)}internal/variable.h
rjit.$(OBJEXT): {$(VPATH)}internal/warning_push.h
rjit.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
rjit.$(OBJEXT): {$(VPATH)}iseq.h
rjit.$(OBJEXT): {$(VPATH)}method.h
rjit.$(OBJEXT): {$(VPATH)}missing.h
rjit.$(OBJEXT): {$(VPATH)}node.h
rjit.$(OBJEXT): {$(VPATH)}onigmo.h
rjit.$(OBJEXT): {$(VPATH)}oniguruma.h
rjit.$(OBJEXT): {$(VPATH)}prism/ast.h
rjit.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
rjit.$(OBJEXT): {$(VPATH)}prism/version.h
rjit.$(OBJEXT): {$(VPATH)}prism_compile.h
rjit.$(OBJEXT): {$(VPATH)}ractor.h
rjit.$(OBJEXT): {$(VPATH)}ractor_core.h
rjit.$(OBJEXT): {$(VPATH)}rjit.c
rjit.$(OBJEXT): {$(VPATH)}rjit.h
rjit.$(OBJEXT): {$(VPATH)}rjit.rbinc
rjit.$(OBJEXT): {$(VPATH)}rjit_c.h
rjit.$(OBJEXT): {$(VPATH)}ruby_assert.h
rjit.$(OBJEXT): {$(VPATH)}ruby_atomic.h
rjit.$(OBJEXT): {$(VPATH)}rubyparser.h
rjit.$(OBJEXT): {$(VPATH)}shape.h
rjit.$(OBJEXT): {$(VPATH)}st.h
rjit.$(OBJEXT): {$(VPATH)}subst.h
rjit.$(OBJEXT): {$(VPATH)}thread.h
rjit.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
rjit.$(OBJEXT): {$(VPATH)}thread_native.h
rjit.$(OBJEXT): {$(VPATH)}util.h
rjit.$(OBJEXT): {$(VPATH)}vm_callinfo.h
rjit.$(OBJEXT): {$(VPATH)}vm_core.h
rjit.$(OBJEXT): {$(VPATH)}vm_debug.h
rjit.$(OBJEXT): {$(VPATH)}vm_opts.h
rjit.$(OBJEXT): {$(VPATH)}vm_sync.h
rjit.$(OBJEXT): {$(VPATH)}yjit.h
rjit_c.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
rjit_c.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
rjit_c.$(OBJEXT): $(CCAN_DIR)/list/list.h
rjit_c.$(OBJEXT): $(CCAN_DIR)/str/str.h
rjit_c.$(OBJEXT): $(hdrdir)/ruby.h
rjit_c.$(OBJEXT): $(hdrdir)/ruby/ruby.h
rjit_c.$(OBJEXT): $(srcdir)/rjit_c.rb
rjit_c.$(OBJEXT): $(top_srcdir)/internal/array.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/bits.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/class.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/compile.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/compilers.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/gc.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/hash.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/imemo.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/object.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/proc.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/serial.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/string.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/struct.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/variable.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/vm.h
rjit_c.$(OBJEXT): $(top_srcdir)/internal/warnings.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/defines.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/encoding.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/node.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/options.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/pack.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/parser.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/prettyprint.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/prism.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/regexp.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/static_literals.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_buffer.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_char.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_constant_pool.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_integer.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_list.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_memchr.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_newline_list.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_string.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_strncasecmp.h
rjit_c.$(OBJEXT): $(top_srcdir)/prism/util/pm_strpbrk.h
rjit_c.$(OBJEXT): {$(VPATH)}assert.h
rjit_c.$(OBJEXT): {$(VPATH)}atomic.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/assume.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/bool.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/limits.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
rjit_c.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
rjit_c.$(OBJEXT): {$(VPATH)}builtin.h
rjit_c.$(OBJEXT): {$(VPATH)}config.h
rjit_c.$(OBJEXT): {$(VPATH)}constant.h
rjit_c.$(OBJEXT): {$(VPATH)}debug.h
rjit_c.$(OBJEXT): {$(VPATH)}debug_counter.h
rjit_c.$(OBJEXT): {$(VPATH)}defines.h
rjit_c.$(OBJEXT): {$(VPATH)}encoding.h
rjit_c.$(OBJEXT): {$(VPATH)}id.h
rjit_c.$(OBJEXT): {$(VPATH)}id_table.h
rjit_c.$(OBJEXT): {$(VPATH)}insns.def
rjit_c.$(OBJEXT): {$(VPATH)}insns.inc
rjit_c.$(OBJEXT): {$(VPATH)}insns_info.inc
rjit_c.$(OBJEXT): {$(VPATH)}intern.h
rjit_c.$(OBJEXT): {$(VPATH)}internal.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/abi.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/anyargs.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/assume.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/const.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/error.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/format.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/cast.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/config.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/constant_p.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/robject.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/ctype.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/dllexport.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/dosish.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/error.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/eval.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/event.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/fl_type.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/gc.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/glob.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/globals.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/extension.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/feature.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/has/warning.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/array.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/class.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/error.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/file.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/io.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/load.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/object.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/process.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/random.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/range.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/re.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/string.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/time.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/interpreter.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/iterator.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/memory.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/method.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/module.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/newobj.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/scan_args.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/special_consts.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/static_assert.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/stdalign.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/stdbool.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/symbol.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/value.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/value_type.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/variable.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/warning_push.h
rjit_c.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
rjit_c.$(OBJEXT): {$(VPATH)}iseq.h
rjit_c.$(OBJEXT): {$(VPATH)}method.h
rjit_c.$(OBJEXT): {$(VPATH)}missing.h
rjit_c.$(OBJEXT): {$(VPATH)}node.h
rjit_c.$(OBJEXT): {$(VPATH)}onigmo.h
rjit_c.$(OBJEXT): {$(VPATH)}oniguruma.h
rjit_c.$(OBJEXT): {$(VPATH)}prism/ast.h
rjit_c.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
rjit_c.$(OBJEXT): {$(VPATH)}prism/version.h
rjit_c.$(OBJEXT): {$(VPATH)}prism_compile.h
rjit_c.$(OBJEXT): {$(VPATH)}probes.dmyh
rjit_c.$(OBJEXT): {$(VPATH)}probes.h
rjit_c.$(OBJEXT): {$(VPATH)}probes_helper.h
rjit_c.$(OBJEXT): {$(VPATH)}rjit.h
rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.c
rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.h
rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.rb
rjit_c.$(OBJEXT): {$(VPATH)}rjit_c.rbinc
rjit_c.$(OBJEXT): {$(VPATH)}ruby_assert.h
rjit_c.$(OBJEXT): {$(VPATH)}ruby_atomic.h
rjit_c.$(OBJEXT): {$(VPATH)}rubyparser.h
rjit_c.$(OBJEXT): {$(VPATH)}shape.h
rjit_c.$(OBJEXT): {$(VPATH)}st.h
rjit_c.$(OBJEXT): {$(VPATH)}subst.h
rjit_c.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h
rjit_c.$(OBJEXT): {$(VPATH)}thread_native.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_callinfo.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_core.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_debug.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_exec.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_insnhelper.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_opts.h
rjit_c.$(OBJEXT): {$(VPATH)}vm_sync.h
rjit_c.$(OBJEXT): {$(VPATH)}yjit.h
ruby-runner.$(OBJEXT): {$(VPATH)}config.h
ruby-runner.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
ruby-runner.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
@ -16324,7 +15792,6 @@ ruby.$(OBJEXT): {$(VPATH)}prism/ast.h
ruby.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
ruby.$(OBJEXT): {$(VPATH)}prism/version.h
ruby.$(OBJEXT): {$(VPATH)}prism_compile.h
ruby.$(OBJEXT): {$(VPATH)}rjit.h
ruby.$(OBJEXT): {$(VPATH)}ruby.c
ruby.$(OBJEXT): {$(VPATH)}ruby_assert.h
ruby.$(OBJEXT): {$(VPATH)}ruby_atomic.h
@ -18785,7 +18252,6 @@ thread.$(OBJEXT): {$(VPATH)}prism/version.h
thread.$(OBJEXT): {$(VPATH)}prism_compile.h
thread.$(OBJEXT): {$(VPATH)}ractor.h
thread.$(OBJEXT): {$(VPATH)}ractor_core.h
thread.$(OBJEXT): {$(VPATH)}rjit.h
thread.$(OBJEXT): {$(VPATH)}ruby_assert.h
thread.$(OBJEXT): {$(VPATH)}ruby_atomic.h
thread.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -19790,7 +19256,6 @@ version.$(OBJEXT): {$(VPATH)}node.h
version.$(OBJEXT): {$(VPATH)}onigmo.h
version.$(OBJEXT): {$(VPATH)}oniguruma.h
version.$(OBJEXT): {$(VPATH)}revision.h
version.$(OBJEXT): {$(VPATH)}rjit.h
version.$(OBJEXT): {$(VPATH)}ruby_assert.h
version.$(OBJEXT): {$(VPATH)}ruby_atomic.h
version.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -20058,7 +19523,6 @@ vm.$(OBJEXT): {$(VPATH)}probes.h
vm.$(OBJEXT): {$(VPATH)}probes_helper.h
vm.$(OBJEXT): {$(VPATH)}ractor.h
vm.$(OBJEXT): {$(VPATH)}ractor_core.h
vm.$(OBJEXT): {$(VPATH)}rjit.h
vm.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h
vm.$(OBJEXT): {$(VPATH)}rubyparser.h
@ -20984,7 +20448,6 @@ vm_trace.$(OBJEXT): {$(VPATH)}prism/diagnostic.h
vm_trace.$(OBJEXT): {$(VPATH)}prism/version.h
vm_trace.$(OBJEXT): {$(VPATH)}prism_compile.h
vm_trace.$(OBJEXT): {$(VPATH)}ractor.h
vm_trace.$(OBJEXT): {$(VPATH)}rjit.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_assert.h
vm_trace.$(OBJEXT): {$(VPATH)}ruby_atomic.h
vm_trace.$(OBJEXT): {$(VPATH)}rubyparser.h

View File

@ -1297,11 +1297,6 @@ main()
AS_CASE(["$target_cpu"], [powerpc64*|arm64|aarch64], [
ac_cv_func___builtin_setjmp=no
])
# With gcc-8's -fcf-protection, RJIT's __builtin_longjmp fails.
AS_CASE(["$CC $CFLAGS "], [*" -fcf-protection "*], [cf_protection=yes], [cf_protection=no])
AS_IF([test "$cf_protection" = yes], [
ac_cv_func___builtin_setjmp=no
])
],
[emscripten*], [LIBS="-lm -lc $LIBS"
RUBY_APPEND_OPTIONS(LDFLAGS, "-sALLOW_MEMORY_GROWTH=1")
@ -3971,51 +3966,6 @@ AC_SUBST(CARGO)dnl Cargo command for Rust builds
AC_SUBST(CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
AC_SUBST(YJIT_LIBS)dnl for optionally building the Rust parts of YJIT
AC_SUBST(YJIT_OBJ)dnl for optionally building the C parts of YJIT
dnl RJIT supports only x86_64 platforms, but allows arm64/aarch64 for custom JITs.
RJIT_TARGET_OK=no
AS_IF([test "$cross_compiling" = no],
AS_CASE(["$target_cpu-$target_os"],
[*android*], [
RJIT_TARGET_OK=no
],
[arm64-darwin*|aarch64-darwin*|x86_64-darwin*], [
RJIT_TARGET_OK=yes
],
[arm64-*linux*|aarch64-*linux*|x86_64-*linux*], [
RJIT_TARGET_OK=yes
],
[arm64-*bsd*|aarch64-*bsd*|x86_64-*bsd*], [
RJIT_TARGET_OK=yes
]
)
)
dnl Build RJIT on supported platforms or if --enable-rjit is specified.
AC_ARG_ENABLE(rjit,
AS_HELP_STRING([--enable-rjit],
[enable pure-Ruby JIT compiler. enabled by default on Unix x86_64 platforms]),
[RJIT_SUPPORT=$enableval],
[AS_CASE(["$RJIT_TARGET_OK"],
[yes], [RJIT_SUPPORT=yes],
[RJIT_SUPPORT=no]
)]
)
AS_CASE(["$RJIT_SUPPORT"],
[yes|dev], [
AS_CASE(["$RJIT_SUPPORT"],
[dev], [
# Link libcapstone for --rjit-dump-disasm
AC_CHECK_LIB([capstone], [cs_disasm])
])
AC_DEFINE(USE_RJIT, 1)
], [
AC_DEFINE(USE_RJIT, 0)
])
AC_SUBST(RJIT_SUPPORT)
}
[begin]_group "build section" && {
@ -4738,7 +4688,6 @@ config_summary "hardenflags" "$hardenflags"
config_summary "strip command" "$STRIP"
config_summary "install doc" "$DOCTARGETS"
config_summary "YJIT support" "$YJIT_SUPPORT"
config_summary "RJIT support" "$RJIT_SUPPORT"
config_summary "man page type" "$MANTYPE"
config_summary "search path" "$search_path"
config_summary "static-linked-ext" ${EXTSTATIC:+"yes"}

1
cont.c
View File

@ -35,7 +35,6 @@ extern int madvise(caddr_t, size_t, int);
#include "internal/sanitizers.h"
#include "internal/warnings.h"
#include "ruby/fiber/scheduler.h"
#include "rjit.h"
#include "yjit.h"
#include "vm_core.h"
#include "vm_sync.h"

View File

@ -8,6 +8,5 @@ optparse
date
rdoc
regexp
rjit
yjit
ruby

View File

@ -1,45 +0,0 @@
# RJIT: Ruby JIT
This document has some tips that might be useful when you work on RJIT.
## Project purpose
This project is for experimental purposes.
For production deployment, consider using YJIT instead.
## Supported platforms
The following platforms are assumed to work. `linux-x86_64` is tested on CI.
* OS: Linux, macOS, BSD
* Arch: x86\_64
## configure
### --enable-rjit
On supported platforms, `--enable-rjit` is set by default. You usually don't need to specify this.
You may still manually pass `--enable-rjit` to try RJIT on unsupported platforms.
### --enable-rjit=dev
It enables `--rjit-dump-disasm` if libcapstone is available.
## make
### rjit-bindgen
If you see an "RJIT bindgen" GitHub Actions failure, please commit the `git diff` shown on the failed job.
For doing the same thing locally, run `make rjit-bindgen` after installing libclang.
macOS seems to have libclang by default. On Ubuntu, you can install it with `apt install libclang1`.
## ruby
### --rjit-stats
This prints RJIT stats at exit.
### --rjit-dump-disasm
This dumps all JIT code. You need to install libcapstone before configure and use `--enable-rjit=dev` on configure.
* Ubuntu: `sudo apt-get install -y libcapstone-dev`
* macOS: `brew install capstone`

1
eval.c
View File

@ -32,7 +32,6 @@
#include "internal/variable.h"
#include "ruby/fiber/scheduler.h"
#include "iseq.h"
#include "rjit.h"
#include "probes.h"
#include "probes_helper.h"
#include "ruby/vm.h"

1
gc.c
View File

@ -108,7 +108,6 @@
#include "internal/thread.h"
#include "internal/variable.h"
#include "internal/warnings.h"
#include "rjit.h"
#include "probes.h"
#include "regint.h"
#include "ruby/debug.h"

View File

@ -103,8 +103,6 @@ rb_call_builtin_inits(void)
BUILTIN(thread_sync);
BUILTIN(nilclass);
BUILTIN(marshal);
BUILTIN(rjit_c);
BUILTIN(rjit);
Init_builtin_prelude();
}
#undef CALL

View File

@ -1,7 +1,6 @@
#ifndef INTERNAL_CMDLINEOPT_H /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_CMDLINEOPT_H
#include "rjit.h"
#include "yjit.h"
typedef struct {
@ -24,9 +23,6 @@ typedef struct ruby_cmdline_options {
ruby_features_t warn;
unsigned int dump;
long backtrace_length_limit;
#if USE_RJIT
struct rb_rjit_options rjit;
#endif
const char *crash_report;

8
iseq.c
View File

@ -35,7 +35,6 @@
#include "internal/thread.h"
#include "internal/variable.h"
#include "iseq.h"
#include "rjit.h"
#include "ruby/util.h"
#include "vm_core.h"
#include "vm_callinfo.h"
@ -167,7 +166,6 @@ rb_iseq_free(const rb_iseq_t *iseq)
if (iseq && ISEQ_BODY(iseq)) {
iseq_clear_ic_references(iseq);
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
rb_rjit_free_iseq(iseq); /* Notify RJIT */
#if USE_YJIT
rb_yjit_iseq_free(iseq);
if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) {
@ -379,17 +377,11 @@ rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
}
if (reference_updating) {
#if USE_RJIT
rb_rjit_iseq_update_references(body);
#endif
#if USE_YJIT
rb_yjit_iseq_update_references(iseq);
#endif
}
else {
#if USE_RJIT
rb_rjit_iseq_mark(body->rjit_blocks);
#endif
#if USE_YJIT
rb_yjit_iseq_mark(body->yjit_payload);
#endif

View File

@ -1 +0,0 @@
stats.rb

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
class RubyVM::RJIT::Block < Struct.new(
:iseq, # @param ``
:pc, # @param [Integer] Starting PC
:ctx, # @param [RubyVM::RJIT::Context] **Starting** Context (TODO: freeze?)
:start_addr, # @param [Integer] Starting address of this block's JIT code
:entry_exit, # @param [Integer] Address of entry exit (optional)
:incoming, # @param [Array<RubyVM::RJIT::BranchStub>] Incoming branches
:invalidated, # @param [TrueClass,FalseClass] true if already invalidated
)
def initialize(incoming: [], invalidated: false, **) = super
end

View File

@ -1,24 +0,0 @@
module RubyVM::RJIT
# Branch shapes
Next0 = :Next0 # target0 is a fallthrough
Next1 = :Next1 # target1 is a fallthrough
Default = :Default # neither targets is a fallthrough
class BranchStub < Struct.new(
:iseq, # @param [RubyVM::RJIT::CPointer::Struct_rb_iseq_struct] Branch target ISEQ
:shape, # @param [Symbol] Next0, Next1, or Default
:target0, # @param [RubyVM::RJIT::BranchTarget] First branch target
:target1, # @param [RubyVM::RJIT::BranchTarget,NilClass] Second branch target (optional)
:compile, # @param [Proc] A callback to (re-)generate this branch stub
:start_addr, # @param [Integer] Stub source start address to be re-generated
:end_addr, # @param [Integer] Stub source end address to be re-generated
)
end
class BranchTarget < Struct.new(
:pc,
:ctx,
:address,
)
end
end

View File

@ -1,394 +0,0 @@
module RubyVM::RJIT
# Every class under this namespace is a pointer. Even if the type is
# immediate, it shouldn't be dereferenced until `*` is called.
module CPointer
# Note: We'd like to avoid alphabetic method names to avoid a conflict
# with member methods. to_i and to_s are considered an exception.
class Struct
# @param name [String]
# @param sizeof [Integer]
# @param members [Hash{ Symbol => [RubyVM::RJIT::CType::*, Integer, TrueClass] }]
def initialize(addr, sizeof, members)
@addr = addr
@sizeof = sizeof
@members = members
end
# Get a raw address
def to_i
@addr
end
# Serialized address for generated code
def to_s
"0x#{@addr.to_s(16)}"
end
# Pointer diff
def -(struct)
raise ArgumentError if self.class != struct.class
(@addr - struct.to_i) / @sizeof
end
# Primitive API that does no automatic dereference
# TODO: remove this?
# @param member [Symbol]
def [](member)
type, offset = @members.fetch(member)
type.new(@addr + offset / 8)
end
private
# @param member [Symbol]
# @param value [Object]
def []=(member, value)
type, offset = @members.fetch(member)
type[@addr + offset / 8] = value
end
# @param size [Integer]
# @param members [Hash{ Symbol => [Integer, RubyVM::RJIT::CType::*] }]
def self.define(size, members)
Class.new(self) do
# Return the size of this type
define_singleton_method(:size) { size }
# Return the offset to a field
define_singleton_method(:offsetof) do |field, *fields|
member, offset = members.fetch(field)
offset /= 8
unless fields.empty?
offset += member.offsetof(*fields)
end
offset
end
# Return member names
define_singleton_method(:members) { members.keys }
define_method(:initialize) do |addr = nil|
if addr.nil? # TODO: get rid of this feature later
addr = Fiddle.malloc(size)
end
super(addr, size, members)
end
members.each do |member, (type, offset, to_ruby)|
# Intelligent API that does automatic dereference
define_method(member) do
value = self[member]
if value.respond_to?(:*)
value = value.*
end
if to_ruby
value = C.to_ruby(value)
end
value
end
define_method("#{member}=") do |value|
if to_ruby
value = C.to_value(value)
end
self[member] = value
end
end
end
end
end
# Note: We'd like to avoid alphabetic method names to avoid a conflict
# with member methods. to_i is considered an exception.
class Union
# @param _name [String] To be used when it starts defining a union pointer class
# @param sizeof [Integer]
# @param members [Hash{ Symbol => RubyVM::RJIT::CType::* }]
def initialize(addr, sizeof, members)
@addr = addr
@sizeof = sizeof
@members = members
end
# Get a raw address
def to_i
@addr
end
# Move addr to access this pointer like an array
def +(index)
raise ArgumentError unless index.is_a?(Integer)
self.class.new(@addr + index * @sizeof)
end
# Pointer diff
def -(union)
raise ArgumentError if self.class != union.class
(@addr - union.instance_variable_get(:@addr)) / @sizeof
end
# @param sizeof [Integer]
# @param members [Hash{ Symbol => RubyVM::RJIT::CType::* }]
def self.define(sizeof, members)
Class.new(self) do
# Return the size of this type
define_singleton_method(:sizeof) { sizeof }
# Part of Struct's offsetof implementation
define_singleton_method(:offsetof) do |field, *fields|
member = members.fetch(field)
offset = 0
unless fields.empty?
offset += member.offsetof(*fields)
end
offset
end
define_method(:initialize) do |addr|
super(addr, sizeof, members)
end
members.each do |member, type|
# Intelligent API that does automatic dereference
define_method(member) do
value = type.new(@addr)
if value.respond_to?(:*)
value = value.*
end
value
end
end
end
end
end
class Immediate
# @param addr [Integer]
# @param size [Integer]
# @param pack [String]
def initialize(addr, size, pack)
@addr = addr
@size = size
@pack = pack
end
# Get a raw address
def to_i
@addr
end
# Move addr to addess this pointer like an array
def +(index)
Immediate.new(@addr + index * @size, @size, @pack)
end
# Dereference
def *
self[0]
end
# Array access
def [](index)
return nil if @addr == 0
Fiddle::Pointer.new(@addr + index * @size)[0, @size].unpack1(@pack)
end
# Array set
def []=(index, value)
Fiddle::Pointer.new(@addr + index * @size)[0, @size] = [value].pack(@pack)
end
# Serialized address for generated code. Used for embedding things like body->iseq_encoded.
def to_s
"0x#{Integer(@addr).to_s(16)}"
end
# @param fiddle_type [Integer] Fiddle::TYPE_*
def self.define(fiddle_type)
size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type)
pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type)
Class.new(self) do
define_method(:initialize) do |addr|
super(addr, size, pack)
end
define_singleton_method(:size) do
size
end
# Type-level []=: Used by struct fields
define_singleton_method(:[]=) do |addr, value|
Fiddle::Pointer.new(addr)[0, size] = [value].pack(pack)
end
end
end
end
# -Fiddle::TYPE_CHAR Immediate with special handling of true/false
class Bool < Immediate.define(-Fiddle::TYPE_CHAR)
# Dereference
def *
return nil if @addr == 0
super != 0
end
def self.[]=(addr, value)
super(addr, value ? 1 : 0)
end
end
# Basically Immediate but without #* to skip auto-dereference of structs.
class Array
attr_reader :type
# @param addr [Integer]
# @param type [Class] RubyVM::RJIT::CType::*
def initialize(addr, type)
@addr = addr
@type = type
end
# Array access
def [](index)
@type.new(@addr)[index]
end
# Array set
# @param index [Integer]
# @param value [Integer, RubyVM::RJIT::CPointer::Struct] an address itself or an object that return an address with to_i
def []=(index, value)
@type.new(@addr)[index] = value
end
private
def self.define(block)
Class.new(self) do
define_method(:initialize) do |addr|
super(addr, block.call)
end
end
end
end
class Pointer
attr_reader :type
# @param addr [Integer]
# @param type [Class] RubyVM::RJIT::CType::*
def initialize(addr, type)
@addr = addr
@type = type
end
# Move addr to addess this pointer like an array
def +(index)
raise ArgumentError unless index.is_a?(Integer)
Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP, @type)
end
# Dereference
def *
return nil if dest_addr == 0
@type.new(dest_addr)
end
# Array access
def [](index)
(self + index).*
end
# Array set
# @param index [Integer]
# @param value [Integer, RubyVM::RJIT::CPointer::Struct] an address itself or an object that return an address with to_i
def []=(index, value)
Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] =
[value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
end
# Get a raw address
def to_i
@addr
end
private
def dest_addr
Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
end
def self.define(block)
Class.new(self) do
define_method(:initialize) do |addr|
super(addr, block.call)
end
# Type-level []=: Used by struct fields
# @param addr [Integer]
# @param value [Integer, RubyVM::RJIT::CPointer::Struct] an address itself, or an object that return an address with to_i
define_singleton_method(:[]=) do |addr, value|
value = value.to_i
Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
end
end
end
end
class BitField
# @param addr [Integer]
# @param width [Integer]
# @param offset [Integer]
def initialize(addr, width, offset)
@addr = addr
@width = width
@offset = offset
end
# Dereference
def *
byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first
if @width == 1
bit = (1 & (byte >> @offset))
bit == 1
elsif @width <= 8 && @offset == 0
bitmask = @width.times.map { |i| 1 << i }.sum
byte & bitmask
else
raise NotImplementedError.new("not-implemented bit field access: width=#{@width} offset=#{@offset}")
end
end
# @param width [Integer]
# @param offset [Integer]
def self.define(width, offset)
Class.new(self) do
define_method(:initialize) do |addr|
super(addr, width, offset)
end
end
end
end
# Give a name to a dynamic CPointer class to see it on inspect
def self.with_class_name(prefix, name, cache: false, &block)
return block.call if !name.nil? && name.empty?
# Use a cached result only if cache: true
class_name = "#{prefix}_#{name}"
klass =
if cache && self.const_defined?(class_name)
self.const_get(class_name)
else
block.call
end
# Give it a name unless it's already defined
unless self.const_defined?(class_name)
self.const_set(class_name, klass)
end
klass
end
end
end

View File

@ -1,99 +0,0 @@
require 'fiddle'
require 'fiddle/pack'
require_relative 'c_pointer'
module RubyVM::RJIT
module CType
module Struct
# @param name [String]
# @param members [Hash{ Symbol => [Integer, RubyVM::RJIT::CType::*] }]
def self.new(name, sizeof, **members)
name = members.keys.join('_') if name.empty?
CPointer.with_class_name('Struct', name) do
CPointer::Struct.define(sizeof, members)
end
end
end
module Union
# @param name [String]
# @param members [Hash{ Symbol => RubyVM::RJIT::CType::* }]
def self.new(name, sizeof, **members)
name = members.keys.join('_') if name.empty?
CPointer.with_class_name('Union', name) do
CPointer::Union.define(sizeof, members)
end
end
end
module Immediate
# @param fiddle_type [Integer]
def self.new(fiddle_type)
name = Fiddle.constants.find do |const|
const.start_with?('TYPE_') && Fiddle.const_get(const) == fiddle_type.abs
end&.name
name = name.delete_prefix('TYPE_')
if fiddle_type.negative?
name.prepend('U')
end
CPointer.with_class_name('Immediate', name, cache: true) do
CPointer::Immediate.define(fiddle_type)
end
end
# @param type [String]
def self.parse(ctype)
new(Fiddle::Importer.parse_ctype(ctype))
end
def self.find(size, signed)
fiddle_type = TYPE_MAP.fetch(size)
fiddle_type = -fiddle_type unless signed
new(fiddle_type)
end
TYPE_MAP = Fiddle::PackInfo::SIZE_MAP.map { |type, size| [size, type.abs] }.to_h
private_constant :TYPE_MAP
end
module Bool
def self.new
CPointer::Bool
end
end
class Array
def self.new(&block)
CPointer.with_class_name('Array', block.object_id.to_s) do
CPointer::Array.define(block)
end
end
end
class Pointer
# This takes a block to avoid "stack level too deep" on a cyclic reference
# @param block [Proc]
def self.new(&block)
CPointer.with_class_name('Pointer', block.object_id.to_s) do
CPointer::Pointer.define(block)
end
end
end
module BitField
# @param width [Integer]
# @param offset [Integer]
def self.new(width, offset)
CPointer.with_class_name('BitField', "#{offset}_#{width}") do
CPointer::BitField.define(width, offset)
end
end
end
# Types that are referenced but not part of code generation targets
Stub = ::Struct.new(:name)
# Types that it failed to figure out from the header
Unknown = Module.new
end
end

View File

@ -1,91 +0,0 @@
module RubyVM::RJIT
class CodeBlock
# @param mem_block [Integer] JIT buffer address
# @param mem_size [Integer] JIT buffer size
# @param outliend [TrueClass,FalseClass] true for outlined CodeBlock
def initialize(mem_block:, mem_size:, outlined: false)
@comments = Hash.new { |h, k| h[k] = [] } if dump_disasm?
@mem_block = mem_block
@mem_size = mem_size
@write_pos = 0
@outlined = outlined
end
# @param asm [RubyVM::RJIT::Assembler]
def write(asm)
return 0 if @write_pos + asm.size >= @mem_size
start_addr = write_addr
# Write machine code
C.mprotect_write(@mem_block, @mem_size)
@write_pos += asm.assemble(start_addr)
C.mprotect_exec(@mem_block, @mem_size)
end_addr = write_addr
# Convert comment indexes to addresses
asm.comments.each do |index, comments|
@comments[start_addr + index] += comments if dump_disasm?
end
asm.comments.clear
# Dump disasm if --rjit-dump-disasm
if C.rjit_opts.dump_disasm && start_addr < end_addr
dump_disasm(start_addr, end_addr)
end
start_addr
end
def set_write_addr(addr)
@write_pos = addr - @mem_block
@comments.delete(addr) if dump_disasm?
end
def with_write_addr(addr)
old_write_pos = @write_pos
set_write_addr(addr)
yield
ensure
@write_pos = old_write_pos
end
def write_addr
@mem_block + @write_pos
end
def include?(addr)
(@mem_block...(@mem_block + @mem_size)).include?(addr)
end
def dump_disasm(from, to, io: STDOUT, color: true, test: false)
C.dump_disasm(from, to, test:).each do |address, mnemonic, op_str|
@comments.fetch(address, []).each do |comment|
io.puts colorize(" # #{comment}", bold: true, color:)
end
io.puts colorize(" 0x#{format("%x", address)}: #{mnemonic} #{op_str}", color:)
end
io.puts
end
private
def colorize(text, bold: false, color:)
return text unless color
buf = +''
buf << "\e[1m" if bold
buf << "\e[34m" if @outlined
buf << text
buf << "\e[0m"
buf
end
def bold(text)
"\e[1m#{text}\e[0m"
end
def dump_disasm?
C.rjit_opts.dump_disasm
end
end
end

View File

@ -1,518 +0,0 @@
require 'ruby_vm/rjit/assembler'
require 'ruby_vm/rjit/block'
require 'ruby_vm/rjit/branch_stub'
require 'ruby_vm/rjit/code_block'
require 'ruby_vm/rjit/context'
require 'ruby_vm/rjit/entry_stub'
require 'ruby_vm/rjit/exit_compiler'
require 'ruby_vm/rjit/insn_compiler'
require 'ruby_vm/rjit/instruction'
require 'ruby_vm/rjit/invariants'
require 'ruby_vm/rjit/jit_state'
require 'ruby_vm/rjit/type'
module RubyVM::RJIT
# Compilation status
KeepCompiling = :KeepCompiling
CantCompile = :CantCompile
EndBlock = :EndBlock
# Ruby constants
Qtrue = Fiddle::Qtrue
Qfalse = Fiddle::Qfalse
Qnil = Fiddle::Qnil
Qundef = Fiddle::Qundef
# Callee-saved registers
# TODO: support using r12/r13 here
EC = :r14
CFP = :r15
SP = :rbx
# Scratch registers: rax, rcx, rdx
# Mark objects in this Array during GC
GC_REFS = []
# Maximum number of versions per block
# 1 means always create generic versions
MAX_VERSIONS = 4
class Compiler
attr_accessor :write_pos
def self.decode_insn(encoded)
INSNS.fetch(C.rb_vm_insn_decode(encoded))
end
def initialize
mem_size = C.rjit_opts.exec_mem_size * 1024 * 1024
mem_block = C.mmap(mem_size)
@cb = CodeBlock.new(mem_block: mem_block, mem_size: mem_size / 2)
@ocb = CodeBlock.new(mem_block: mem_block + mem_size / 2, mem_size: mem_size / 2, outlined: true)
@exit_compiler = ExitCompiler.new
@insn_compiler = InsnCompiler.new(@cb, @ocb, @exit_compiler)
Invariants.initialize(@cb, @ocb, self, @exit_compiler)
end
# Compile an ISEQ from its entry point.
# @param iseq `RubyVM::RJIT::CPointer::Struct_rb_iseq_t`
# @param cfp `RubyVM::RJIT::CPointer::Struct_rb_control_frame_t`
def compile(iseq, cfp)
return unless supported_platform?
pc = cfp.pc.to_i
jit = JITState.new(iseq:, cfp:)
asm = Assembler.new
compile_prologue(asm, iseq, pc)
compile_block(asm, jit:, pc:)
iseq.body.jit_entry = @cb.write(asm)
rescue Exception => e
STDERR.puts "#{e.class}: #{e.message}"
STDERR.puts e.backtrace
exit 1
end
# Compile an entry.
# @param entry [RubyVM::RJIT::EntryStub]
def entry_stub_hit(entry_stub, cfp)
# Compile a new entry guard as a next entry
pc = cfp.pc.to_i
next_entry = Assembler.new.then do |asm|
compile_entry_chain_guard(asm, cfp.iseq, pc)
@cb.write(asm)
end
# Try to find an existing compiled version of this block
ctx = Context.new
block = find_block(cfp.iseq, pc, ctx)
if block
# If an existing block is found, generate a jump to the block.
asm = Assembler.new
asm.jmp(block.start_addr)
@cb.write(asm)
else
# If this block hasn't yet been compiled, generate blocks after the entry guard.
asm = Assembler.new
jit = JITState.new(iseq: cfp.iseq, cfp:)
compile_block(asm, jit:, pc:, ctx:)
@cb.write(asm)
block = jit.block
end
# Regenerate the previous entry
@cb.with_write_addr(entry_stub.start_addr) do
# The last instruction of compile_entry_chain_guard is jne
asm = Assembler.new
asm.jne(next_entry)
@cb.write(asm)
end
return block.start_addr
rescue Exception => e
STDERR.puts e.full_message
exit 1
end
# Compile a branch stub.
# @param branch_stub [RubyVM::RJIT::BranchStub]
# @param cfp `RubyVM::RJIT::CPointer::Struct_rb_control_frame_t`
# @param target0_p [TrueClass,FalseClass]
# @return [Integer] The starting address of the compiled branch stub
def branch_stub_hit(branch_stub, cfp, target0_p)
# Update cfp->pc for `jit.at_current_insn?`
target = target0_p ? branch_stub.target0 : branch_stub.target1
cfp.pc = target.pc
# Reuse an existing block if it already exists
block = find_block(branch_stub.iseq, target.pc, target.ctx)
# If the branch stub's jump is the last code, allow overwriting part of
# the old branch code with the new block code.
fallthrough = block.nil? && @cb.write_addr == branch_stub.end_addr
if fallthrough
# If the branch stub's jump is the last code, allow overwriting part of
# the old branch code with the new block code.
@cb.set_write_addr(branch_stub.start_addr)
branch_stub.shape = target0_p ? Next0 : Next1
Assembler.new.tap do |branch_asm|
branch_stub.compile.call(branch_asm)
@cb.write(branch_asm)
end
end
# Reuse or generate a block
if block
target.address = block.start_addr
else
jit = JITState.new(iseq: branch_stub.iseq, cfp:)
target.address = Assembler.new.then do |asm|
compile_block(asm, jit:, pc: target.pc, ctx: target.ctx.dup)
@cb.write(asm)
end
block = jit.block
end
block.incoming << branch_stub # prepare for invalidate_block
# Re-generate the branch code for non-fallthrough cases
unless fallthrough
@cb.with_write_addr(branch_stub.start_addr) do
branch_asm = Assembler.new
branch_stub.compile.call(branch_asm)
@cb.write(branch_asm)
end
end
return target.address
rescue Exception => e
STDERR.puts e.full_message
exit 1
end
# @param iseq `RubyVM::RJIT::CPointer::Struct_rb_iseq_t`
# @param pc [Integer]
def invalidate_blocks(iseq, pc)
list_blocks(iseq, pc).each do |block|
invalidate_block(block)
end
# If they were the ISEQ's first blocks, re-compile RJIT entry as well
if iseq.body.iseq_encoded.to_i == pc
iseq.body.jit_entry = 0
iseq.body.jit_entry_calls = 0
end
end
def invalidate_block(block)
iseq = block.iseq
# Avoid touching GCed ISEQs. We assume it won't be re-entered.
return unless C.imemo_type_p(iseq, C.imemo_iseq)
# Remove this block from the version array
remove_block(iseq, block)
# Invalidate the block with entry exit
unless block.invalidated
@cb.with_write_addr(block.start_addr) do
asm = Assembler.new
asm.comment('invalidate_block')
asm.jmp(block.entry_exit)
@cb.write(asm)
end
block.invalidated = true
end
# Re-stub incoming branches
block.incoming.each do |branch_stub|
target = [branch_stub.target0, branch_stub.target1].compact.find do |target|
target.pc == block.pc && target.ctx == block.ctx
end
next if target.nil?
# TODO: Could target.address be a stub address? Is invalidation not needed in that case?
# If the target being re-generated is currently a fallthrough block,
# the fallthrough code must be rewritten with a jump to the stub.
if target.address == branch_stub.end_addr
branch_stub.shape = Default
end
target.address = Assembler.new.then do |ocb_asm|
@exit_compiler.compile_branch_stub(block.ctx, ocb_asm, branch_stub, target == branch_stub.target0)
@ocb.write(ocb_asm)
end
@cb.with_write_addr(branch_stub.start_addr) do
branch_asm = Assembler.new
branch_stub.compile.call(branch_asm)
@cb.write(branch_asm)
end
end
end
private
# Callee-saved: rbx, rsp, rbp, r12, r13, r14, r15
# Caller-saved: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11
#
# @param asm [RubyVM::RJIT::Assembler]
def compile_prologue(asm, iseq, pc)
asm.comment('RJIT entry point')
# Save callee-saved registers used by JITed code
asm.push(CFP)
asm.push(EC)
asm.push(SP)
# Move arguments EC and CFP to dedicated registers
asm.mov(EC, :rdi)
asm.mov(CFP, :rsi)
# Load sp to a dedicated register
asm.mov(SP, [CFP, C.rb_control_frame_t.offsetof(:sp)]) # rbx = cfp->sp
# Setup cfp->jit_return
asm.mov(:rax, leave_exit)
asm.mov([CFP, C.rb_control_frame_t.offsetof(:jit_return)], :rax)
# We're compiling iseqs that we *expect* to start at `insn_idx`. But in
# the case of optional parameters, the interpreter can set the pc to a
# different location depending on the optional parameters. If an iseq
# has optional parameters, we'll add a runtime check that the PC we've
# compiled for is the same PC that the interpreter wants us to run with.
# If they don't match, then we'll take a side exit.
if iseq.body.param.flags.has_opt
compile_entry_chain_guard(asm, iseq, pc)
end
end
def compile_entry_chain_guard(asm, iseq, pc)
entry_stub = EntryStub.new
stub_addr = Assembler.new.then do |ocb_asm|
@exit_compiler.compile_entry_stub(ocb_asm, entry_stub)
@ocb.write(ocb_asm)
end
asm.comment('guard expected PC')
asm.mov(:rax, pc)
asm.cmp([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax)
asm.stub(entry_stub) do
asm.jne(stub_addr)
end
end
# @param asm [RubyVM::RJIT::Assembler]
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
def compile_block(asm, jit:, pc:, ctx: Context.new)
# Mark the block start address and prepare an exit code storage
ctx = limit_block_versions(jit.iseq, pc, ctx)
block = Block.new(iseq: jit.iseq, pc:, ctx: ctx.dup)
jit.block = block
asm.block(block)
iseq = jit.iseq
asm.comment("Block: #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{iseq_lineno(iseq, pc)}")
# Compile each insn
index = (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size
while index < iseq.body.iseq_size
# Set the current instruction
insn = self.class.decode_insn(iseq.body.iseq_encoded[index])
jit.pc = (iseq.body.iseq_encoded + index).to_i
jit.stack_size_for_pc = ctx.stack_size
jit.side_exit_for_pc.clear
# If previous instruction requested to record the boundary
if jit.record_boundary_patch_point
# Generate an exit to this instruction and record it
exit_pos = Assembler.new.then do |ocb_asm|
@exit_compiler.compile_side_exit(jit.pc, ctx, ocb_asm)
@ocb.write(ocb_asm)
end
Invariants.record_global_inval_patch(asm, exit_pos)
jit.record_boundary_patch_point = false
end
# In debug mode, verify our existing assumption
if C.rjit_opts.verify_ctx && jit.at_current_insn?
verify_ctx(jit, ctx)
end
case status = @insn_compiler.compile(jit, ctx, asm, insn)
when KeepCompiling
# For now, reset the chain depth after each instruction as only the
# first instruction in the block can concern itself with the depth.
ctx.chain_depth = 0
index += insn.len
when EndBlock
# TODO: pad nops if entry exit exists (not needed for x86_64?)
break
when CantCompile
# Rewind stack_size using ctx.with_stack_size to allow stack_size changes
# before you return CantCompile.
@exit_compiler.compile_side_exit(jit.pc, ctx.with_stack_size(jit.stack_size_for_pc), asm)
# If this is the first instruction, this block never needs to be invalidated.
if block.pc == iseq.body.iseq_encoded.to_i + index * C.VALUE.size
block.invalidated = true
end
break
else
raise "compiling #{insn.name} returned unexpected status: #{status.inspect}"
end
end
incr_counter(:compiled_block_count)
add_block(iseq, block)
end
def leave_exit
@leave_exit ||= Assembler.new.then do |asm|
@exit_compiler.compile_leave_exit(asm)
@ocb.write(asm)
end
end
def incr_counter(name)
if C.rjit_opts.stats
C.rb_rjit_counters[name][0] += 1
end
end
# Produce a generic context when the block version limit is hit for the block
def limit_block_versions(iseq, pc, ctx)
# Guard chains implement limits separately, do nothing
if ctx.chain_depth > 0
return ctx.dup
end
# If this block version we're about to add will hit the version limit
if list_blocks(iseq, pc).size + 1 >= MAX_VERSIONS
# Produce a generic context that stores no type information,
# but still respects the stack_size and sp_offset constraints.
# This new context will then match all future requests.
generic_ctx = Context.new
generic_ctx.stack_size = ctx.stack_size
generic_ctx.sp_offset = ctx.sp_offset
if ctx.diff(generic_ctx) == TypeDiff::Incompatible
raise 'should substitute a compatible context'
end
return generic_ctx
end
return ctx.dup
end
def list_blocks(iseq, pc)
rjit_blocks(iseq)[pc]
end
# @param [Integer] pc
# @param [RubyVM::RJIT::Context] ctx
# @return [RubyVM::RJIT::Block,NilClass]
def find_block(iseq, pc, ctx)
versions = rjit_blocks(iseq)[pc]
best_version = nil
best_diff = Float::INFINITY
versions.each do |block|
# Note that we always prefer the first matching
# version found because of inline-cache chains
case ctx.diff(block.ctx)
in TypeDiff::Compatible[diff] if diff < best_diff
best_version = block
best_diff = diff
else
end
end
return best_version
end
# @param [RubyVM::RJIT::Block] block
def add_block(iseq, block)
rjit_blocks(iseq)[block.pc] << block
end
# @param [RubyVM::RJIT::Block] block
def remove_block(iseq, block)
rjit_blocks(iseq)[block.pc].delete(block)
end
def rjit_blocks(iseq)
# Guard against ISEQ GC at random moments
unless C.imemo_type_p(iseq, C.imemo_iseq)
return Hash.new { |h, k| h[k] = [] }
end
unless iseq.body.rjit_blocks
iseq.body.rjit_blocks = Hash.new { |blocks, pc| blocks[pc] = [] }
# For some reason, rb_rjit_iseq_mark didn't protect this Hash
# from being freed. So we rely on GC_REFS to keep the Hash.
GC_REFS << iseq.body.rjit_blocks
end
iseq.body.rjit_blocks
end
def iseq_lineno(iseq, pc)
C.rb_iseq_line_no(iseq, (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size)
rescue RangeError # bignum too big to convert into `unsigned long long' (RangeError)
-1
end
# Verify the ctx's types and mappings against the compile-time stack, self, and locals.
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
def verify_ctx(jit, ctx)
# Only able to check types when at current insn
assert(jit.at_current_insn?)
self_val = jit.peek_at_self
self_val_type = Type.from(self_val)
# Verify self operand type
assert_compatible(self_val_type, ctx.get_opnd_type(SelfOpnd))
# Verify stack operand types
[ctx.stack_size, MAX_TEMP_TYPES].min.times do |i|
learned_mapping, learned_type = ctx.get_opnd_mapping(StackOpnd[i])
stack_val = jit.peek_at_stack(i)
val_type = Type.from(stack_val)
case learned_mapping
in MapToSelf
if C.to_value(self_val) != C.to_value(stack_val)
raise "verify_ctx: stack value was mapped to self, but values did not match:\n"\
"stack: #{stack_val.inspect}, self: #{self_val.inspect}"
end
in MapToLocal[local_idx]
local_val = jit.peek_at_local(local_idx)
if C.to_value(local_val) != C.to_value(stack_val)
raise "verify_ctx: stack value was mapped to local, but values did not match:\n"\
"stack: #{stack_val.inspect}, local: #{local_val.inspect}"
end
in MapToStack
# noop
end
# If the actual type differs from the learned type
assert_compatible(val_type, learned_type)
end
# Verify local variable types
local_table_size = jit.iseq.body.local_table_size
[local_table_size, MAX_TEMP_TYPES].min.times do |i|
learned_type = ctx.get_local_type(i)
local_val = jit.peek_at_local(i)
local_type = Type.from(local_val)
assert_compatible(local_type, learned_type)
end
end
def assert_compatible(actual_type, ctx_type)
if actual_type.diff(ctx_type) == TypeDiff::Incompatible
raise "verify_ctx: ctx type (#{ctx_type.type.inspect}) is incompatible with actual type (#{actual_type.type.inspect})"
end
end
def assert(cond)
unless cond
raise "'#{cond.inspect}' was not true"
end
end
def supported_platform?
return @supported_platform if defined?(@supported_platform)
@supported_platform = RUBY_PLATFORM.match?(/x86_64/).tap do |supported|
warn "warning: RJIT does not support #{RUBY_PLATFORM} yet" unless supported
end
end
end
end

View File

@ -1,377 +0,0 @@
module RubyVM::RJIT
# Maximum number of temp value types we keep track of
MAX_TEMP_TYPES = 8
# Maximum number of local variable types we keep track of
MAX_LOCAL_TYPES = 8
# Operand to a YARV bytecode instruction
SelfOpnd = :SelfOpnd # The value is self
StackOpnd = Data.define(:index) # Temporary stack operand with stack index
# Potential mapping of a value on the temporary stack to self,
# a local variable, or constant so that we can track its type
MapToStack = :MapToStack # Normal stack value
MapToSelf = :MapToSelf # Temp maps to the self operand
MapToLocal = Data.define(:local_index) # Temp maps to a local variable with index
class Context < Struct.new(
:stack_size, # @param [Integer] The number of values on the stack
:sp_offset, # @param [Integer] JIT sp offset relative to the interpreter's sp
:chain_depth, # @param [Integer] jit_chain_guard depth
:local_types, # @param [Array<RubyVM::RJIT::Type>] Local variable types we keep track of
:temp_types, # @param [Array<RubyVM::RJIT::Type>] Temporary variable types we keep track of
:self_type, # @param [RubyVM::RJIT::Type] Type we track for self
:temp_mapping, # @param [Array<Symbol>] Mapping of temp stack entries to types we track
)
def initialize(
stack_size: 0,
sp_offset: 0,
chain_depth: 0,
local_types: [Type::Unknown] * MAX_LOCAL_TYPES,
temp_types: [Type::Unknown] * MAX_TEMP_TYPES,
self_type: Type::Unknown,
temp_mapping: [MapToStack] * MAX_TEMP_TYPES
) = super
# Deep dup by default for safety
def dup
ctx = super
ctx.local_types = ctx.local_types.dup
ctx.temp_types = ctx.temp_types.dup
ctx.temp_mapping = ctx.temp_mapping.dup
ctx
end
# Create a new Context instance with a given stack_size and sp_offset adjusted
# accordingly. This is useful when you want to virtually rewind a stack_size for
# generating a side exit while considering past sp_offset changes on gen_save_sp.
def with_stack_size(stack_size)
ctx = self.dup
ctx.sp_offset -= ctx.stack_size - stack_size
ctx.stack_size = stack_size
ctx
end
def stack_opnd(depth_from_top)
[SP, C.VALUE.size * (self.sp_offset - 1 - depth_from_top)]
end
def sp_opnd(offset_bytes = 0)
[SP, (C.VALUE.size * self.sp_offset) + offset_bytes]
end
# Push one new value on the temp stack with an explicit mapping
# Return a pointer to the new stack top
def stack_push_mapping(mapping_temp_type)
stack_size = self.stack_size
# Keep track of the type and mapping of the value
if stack_size < MAX_TEMP_TYPES
mapping, temp_type = mapping_temp_type
self.temp_mapping[stack_size] = mapping
self.temp_types[stack_size] = temp_type
case mapping
in MapToLocal[idx]
assert(idx < MAX_LOCAL_TYPES)
else
end
end
self.stack_size += 1
self.sp_offset += 1
return self.stack_opnd(0)
end
# Push one new value on the temp stack
# Return a pointer to the new stack top
def stack_push(val_type)
return self.stack_push_mapping([MapToStack, val_type])
end
# Push the self value on the stack
def stack_push_self
return self.stack_push_mapping([MapToStack, Type::Unknown])
end
# Push a local variable on the stack
def stack_push_local(local_idx)
if local_idx >= MAX_LOCAL_TYPES
return self.stack_push(Type::Unknown)
end
return self.stack_push_mapping([MapToLocal[local_idx], Type::Unknown])
end
# Pop N values off the stack
# Return a pointer to the stack top before the pop operation
def stack_pop(n = 1)
assert(n <= self.stack_size)
top = self.stack_opnd(0)
# Clear the types of the popped values
n.times do |i|
idx = self.stack_size - i - 1
if idx < MAX_TEMP_TYPES
self.temp_types[idx] = Type::Unknown
self.temp_mapping[idx] = MapToStack
end
end
self.stack_size -= n
self.sp_offset -= n
return top
end
def shift_stack(argc)
assert(argc < self.stack_size)
method_name_index = self.stack_size - argc - 1
(method_name_index...(self.stack_size - 1)).each do |i|
if i + 1 < MAX_TEMP_TYPES
self.temp_types[i] = self.temp_types[i + 1]
self.temp_mapping[i] = self.temp_mapping[i + 1]
end
end
self.stack_pop(1)
end
# Get the type of an instruction operand
def get_opnd_type(opnd)
case opnd
in SelfOpnd
self.self_type
in StackOpnd[idx]
assert(idx < self.stack_size)
stack_idx = self.stack_size - 1 - idx
# If outside of tracked range, do nothing
if stack_idx >= MAX_TEMP_TYPES
return Type::Unknown
end
mapping = self.temp_mapping[stack_idx]
case mapping
in MapToSelf
self.self_type
in MapToStack
self.temp_types[self.stack_size - 1 - idx]
in MapToLocal[idx]
assert(idx < MAX_LOCAL_TYPES)
self.local_types[idx]
end
end
end
# Get the currently tracked type for a local variable
def get_local_type(idx)
self.local_types[idx] || Type::Unknown
end
# Upgrade (or "learn") the type of an instruction operand
# This value must be compatible and at least as specific as the previously known type.
# If this value originated from self, or an lvar, the learned type will be
# propagated back to its source.
def upgrade_opnd_type(opnd, opnd_type)
case opnd
in SelfOpnd
self.self_type = self.self_type.upgrade(opnd_type)
in StackOpnd[idx]
assert(idx < self.stack_size)
stack_idx = self.stack_size - 1 - idx
# If outside of tracked range, do nothing
if stack_idx >= MAX_TEMP_TYPES
return
end
mapping = self.temp_mapping[stack_idx]
case mapping
in MapToSelf
self.self_type = self.self_type.upgrade(opnd_type)
in MapToStack
self.temp_types[stack_idx] = self.temp_types[stack_idx].upgrade(opnd_type)
in MapToLocal[idx]
assert(idx < MAX_LOCAL_TYPES)
self.local_types[idx] = self.local_types[idx].upgrade(opnd_type)
end
end
end
# Get both the type and mapping (where the value originates) of an operand.
# This is can be used with stack_push_mapping or set_opnd_mapping to copy
# a stack value's type while maintaining the mapping.
def get_opnd_mapping(opnd)
opnd_type = self.get_opnd_type(opnd)
case opnd
in SelfOpnd
return [MapToSelf, opnd_type]
in StackOpnd[idx]
assert(idx < self.stack_size)
stack_idx = self.stack_size - 1 - idx
if stack_idx < MAX_TEMP_TYPES
return [self.temp_mapping[stack_idx], opnd_type]
else
# We can't know the source of this stack operand, so we assume it is
# a stack-only temporary. type will be UNKNOWN
assert(opnd_type == Type::Unknown)
return [MapToStack, opnd_type]
end
end
end
# Overwrite both the type and mapping of a stack operand.
def set_opnd_mapping(opnd, mapping_opnd_type)
case opnd
in SelfOpnd
raise 'self always maps to self'
in StackOpnd[idx]
assert(idx < self.stack_size)
stack_idx = self.stack_size - 1 - idx
# If outside of tracked range, do nothing
if stack_idx >= MAX_TEMP_TYPES
return
end
mapping, opnd_type = mapping_opnd_type
self.temp_mapping[stack_idx] = mapping
# Only used when mapping == MAP_STACK
self.temp_types[stack_idx] = opnd_type
end
end
# Set the type of a local variable
def set_local_type(local_idx, local_type)
if local_idx >= MAX_LOCAL_TYPES
return
end
# If any values on the stack map to this local we must detach them
MAX_TEMP_TYPES.times do |stack_idx|
case self.temp_mapping[stack_idx]
in MapToStack
# noop
in MapToSelf
# noop
in MapToLocal[idx]
if idx == local_idx
self.temp_types[stack_idx] = self.local_types[idx]
self.temp_mapping[stack_idx] = MapToStack
else
# noop
end
end
end
self.local_types[local_idx] = local_type
end
# Erase local variable type information
# eg: because of a call we can't track
def clear_local_types
# When clearing local types we must detach any stack mappings to those
# locals. Even if local values may have changed, stack values will not.
MAX_TEMP_TYPES.times do |stack_idx|
case self.temp_mapping[stack_idx]
in MapToStack
# noop
in MapToSelf
# noop
in MapToLocal[local_idx]
self.temp_types[stack_idx] = self.local_types[local_idx]
self.temp_mapping[stack_idx] = MapToStack
end
end
# Clear the local types
self.local_types = [Type::Unknown] * MAX_LOCAL_TYPES
end
# Compute a difference score for two context objects
def diff(dst)
# Self is the source context (at the end of the predecessor)
src = self
# Can only lookup the first version in the chain
if dst.chain_depth != 0
return TypeDiff::Incompatible
end
# Blocks with depth > 0 always produce new versions
# Sidechains cannot overlap
if src.chain_depth != 0
return TypeDiff::Incompatible
end
if dst.stack_size != src.stack_size
return TypeDiff::Incompatible
end
if dst.sp_offset != src.sp_offset
return TypeDiff::Incompatible
end
# Difference sum
diff = 0
# Check the type of self
diff += case src.self_type.diff(dst.self_type)
in TypeDiff::Compatible[diff] then diff
in TypeDiff::Incompatible then return TypeDiff::Incompatible
end
# For each local type we track
src.local_types.size.times do |i|
t_src = src.local_types[i]
t_dst = dst.local_types[i]
diff += case t_src.diff(t_dst)
in TypeDiff::Compatible[diff] then diff
in TypeDiff::Incompatible then return TypeDiff::Incompatible
end
end
# For each value on the temp stack
src.stack_size.times do |i|
src_mapping, src_type = src.get_opnd_mapping(StackOpnd[i])
dst_mapping, dst_type = dst.get_opnd_mapping(StackOpnd[i])
# If the two mappings aren't the same
if src_mapping != dst_mapping
if dst_mapping == MapToStack
# We can safely drop information about the source of the temp
# stack operand.
diff += 1
else
return TypeDiff::Incompatible
end
end
diff += case src_type.diff(dst_type)
in TypeDiff::Compatible[diff] then diff
in TypeDiff::Incompatible then return TypeDiff::Incompatible
end
end
return TypeDiff::Compatible[diff]
end
private
def assert(cond)
unless cond
raise "'#{cond.inspect}' was not true"
end
end
end
end

View File

@ -1,7 +0,0 @@
module RubyVM::RJIT
class EntryStub < Struct.new(
:start_addr, # @param [Integer] Stub source start address to be re-generated
:end_addr, # @param [Integer] Stub source end address to be re-generated
)
end
end

View File

@ -1,164 +0,0 @@
module RubyVM::RJIT
class ExitCompiler
def initialize = freeze
# Used for invalidating a block on entry.
# @param pc [Integer]
# @param asm [RubyVM::RJIT::Assembler]
def compile_entry_exit(pc, ctx, asm, cause:)
# Fix pc/sp offsets for the interpreter
save_pc_and_sp(pc, ctx, asm, reset_sp_offset: false)
# Increment per-insn exit counter
count_insn_exit(pc, asm)
# Restore callee-saved registers
asm.comment("#{cause}: entry exit")
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# Set to cfp->jit_return by default for leave insn
# @param asm [RubyVM::RJIT::Assembler]
def compile_leave_exit(asm)
asm.comment('default cfp->jit_return')
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
# :rax is written by #leave
asm.ret
end
# Fire cfunc events on invalidation by TracePoint
# @param asm [RubyVM::RJIT::Assembler]
def compile_full_cfunc_return(asm)
# This chunk of code expects REG_EC to be filled properly and
# RAX to contain the return value of the C method.
asm.comment('full cfunc return')
asm.mov(C_ARGS[0], EC)
asm.mov(C_ARGS[1], :rax)
asm.call(C.rjit_full_cfunc_return)
# TODO: count the exit
# Restore callee-saved registers
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def compile_side_exit(pc, ctx, asm)
# Fix pc/sp offsets for the interpreter
save_pc_and_sp(pc, ctx.dup, asm) # dup to avoid sp_offset update
# Increment per-insn exit counter
count_insn_exit(pc, asm)
# Restore callee-saved registers
asm.comment("exit to interpreter on #{pc_to_insn(pc).name}")
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
asm.mov(C_RET, Qundef)
asm.ret
end
# @param asm [RubyVM::RJIT::Assembler]
# @param entry_stub [RubyVM::RJIT::EntryStub]
def compile_entry_stub(asm, entry_stub)
# Call rb_rjit_entry_stub_hit
asm.comment('entry stub hit')
asm.mov(C_ARGS[0], to_value(entry_stub))
asm.call(C.rb_rjit_entry_stub_hit)
# Jump to the address returned by rb_rjit_entry_stub_hit
asm.jmp(:rax)
end
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
# @param branch_stub [RubyVM::RJIT::BranchStub]
# @param target0_p [TrueClass,FalseClass]
def compile_branch_stub(ctx, asm, branch_stub, target0_p)
# Call rb_rjit_branch_stub_hit
iseq = branch_stub.iseq
if C.rjit_opts.dump_disasm && C.imemo_type_p(iseq, C.imemo_iseq) # Guard against ISEQ GC at random moments
asm.comment("branch stub hit: #{iseq.body.location.label}@#{C.rb_iseq_path(iseq)}:#{iseq_lineno(iseq, target0_p ? branch_stub.target0.pc : branch_stub.target1.pc)}")
end
asm.mov(:rdi, to_value(branch_stub))
asm.mov(:esi, ctx.sp_offset)
asm.mov(:edx, target0_p ? 1 : 0)
asm.call(C.rb_rjit_branch_stub_hit)
# Jump to the address returned by rb_rjit_branch_stub_hit
asm.jmp(:rax)
end
private
def pc_to_insn(pc)
Compiler.decode_insn(C.VALUE.new(pc).*)
end
# @param pc [Integer]
# @param asm [RubyVM::RJIT::Assembler]
def count_insn_exit(pc, asm)
if C.rjit_opts.stats
insn = Compiler.decode_insn(C.VALUE.new(pc).*)
asm.comment("increment insn exit: #{insn.name}")
asm.mov(:rax, (C.rjit_insn_exits + insn.bin).to_i)
asm.add([:rax], 1) # TODO: lock
end
if C.rjit_opts.trace_exits
asm.comment('rjit_record_exit_stack')
asm.mov(C_ARGS[0], pc)
asm.call(C.rjit_record_exit_stack)
end
end
# @param jit [RubyVM::RJIT::JITState]
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def save_pc_and_sp(pc, ctx, asm, reset_sp_offset: true)
# Update pc (TODO: manage PC offset?)
asm.comment("save PC#{' and SP' if ctx.sp_offset != 0} to CFP")
asm.mov(:rax, pc) # rax = jit.pc
asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
# Update sp
if ctx.sp_offset != 0
asm.add(SP, C.VALUE.size * ctx.sp_offset) # sp += stack_size
asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], SP) # cfp->sp = sp
if reset_sp_offset
ctx.sp_offset = 0
end
end
end
def to_value(obj)
GC_REFS << obj
C.to_value(obj)
end
def iseq_lineno(iseq, pc)
C.rb_iseq_line_no(iseq, (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size)
rescue RangeError # bignum too big to convert into `unsigned long long' (RangeError)
-1
end
end
end

View File

@ -1,36 +0,0 @@
module RubyVM::RJIT
module Hooks # :nodoc: all
def self.on_bop_redefined(_redefined_flag, _bop)
# C.rjit_cancel_all("BOP is redefined")
end
def self.on_cme_invalidate(cme)
cme = C.rb_callable_method_entry_struct.new(cme)
Invariants.on_cme_invalidate(cme)
end
def self.on_ractor_spawn
# C.rjit_cancel_all("Ractor is spawned")
end
# Global constant changes like const_set
def self.on_constant_state_changed(id)
Invariants.on_constant_state_changed(id)
end
# ISEQ-specific constant invalidation
def self.on_constant_ic_update(iseq, ic, insn_idx)
iseq = C.rb_iseq_t.new(iseq)
ic = C.IC.new(ic)
Invariants.on_constant_ic_update(iseq, ic, insn_idx)
end
def self.on_tracing_invalidate_all(_new_iseq_events)
Invariants.on_tracing_invalidate_all
end
def self.on_update_references
Invariants.on_update_references
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -1,155 +0,0 @@
require 'set'
module RubyVM::RJIT
class Invariants
class << self
# Called by RubyVM::RJIT::Compiler to lazily initialize this
# @param cb [CodeBlock]
# @param ocb [CodeBlock]
# @param compiler [RubyVM::RJIT::Compiler]
# @param exit_compiler [RubyVM::RJIT::ExitCompiler]
def initialize(cb, ocb, compiler, exit_compiler)
@cb = cb
@ocb = ocb
@compiler = compiler
@exit_compiler = exit_compiler
@bop_blocks = Set.new # TODO: actually invalidate this
@cme_blocks = Hash.new { |h, k| h[k] = Set.new }
@const_blocks = Hash.new { |h, k| h[k] = Set.new }
@patches = {}
# freeze # workaround a binding.irb issue. TODO: resurrect this
end
# @param jit [RubyVM::RJIT::JITState]
# @param klass [Integer]
# @param op [Integer]
def assume_bop_not_redefined(jit, klass, op)
return false unless C.BASIC_OP_UNREDEFINED_P(klass, op)
ensure_block_entry_exit(jit, cause: 'assume_bop_not_redefined')
@bop_blocks << jit.block
true
end
# @param jit [RubyVM::RJIT::JITState]
def assume_method_lookup_stable(jit, cme)
ensure_block_entry_exit(jit, cause: 'assume_method_lookup_stable')
@cme_blocks[cme.to_i] << jit.block
end
# @param jit [RubyVM::RJIT::JITState]
def assume_method_basic_definition(jit, klass, mid)
if C.rb_method_basic_definition_p(klass, mid)
cme = C.rb_callable_method_entry(klass, mid)
assume_method_lookup_stable(jit, cme)
true
else
false
end
end
def assume_stable_constant_names(jit, idlist)
(0..).each do |i|
break if (id = idlist[i]) == 0
@const_blocks[id] << jit.block
end
end
# @param asm [RubyVM::RJIT::Assembler]
def record_global_inval_patch(asm, target)
asm.pos_marker do |address|
if @patches.key?(address)
raise 'multiple patches in the same address'
end
@patches[address] = target
end
end
def on_cme_invalidate(cme)
@cme_blocks.fetch(cme.to_i, []).each do |block|
@cb.with_write_addr(block.start_addr) do
asm = Assembler.new
asm.comment('on_cme_invalidate')
asm.jmp(block.entry_exit)
@cb.write(asm)
end
# TODO: re-generate branches that refer to this block
end
@cme_blocks.delete(cme.to_i)
end
def on_constant_ic_update(iseq, ic, insn_idx)
# TODO: check multi ractor as well
if ic.entry.ic_cref
# No need to recompile the slowpath
return
end
pc = iseq.body.iseq_encoded + insn_idx
insn_name = Compiler.decode_insn(pc.*).name
if insn_name != :opt_getconstant_path && insn_name != :trace_opt_getconstant_path
raise 'insn_idx was not at opt_getconstant_path'
end
if ic.to_i != pc[1]
raise 'insn_idx + 1 was not at the updated IC'
end
@compiler.invalidate_blocks(iseq, pc.to_i)
end
def on_constant_state_changed(id)
@const_blocks.fetch(id, []).each do |block|
@compiler.invalidate_block(block)
end
end
def on_tracing_invalidate_all
invalidate_all
end
def on_update_references
# Give up. In order to support GC.compact, you'd have to update ISEQ
# addresses in BranchStub, etc. Ideally, we'd need to update moved
# pointers in JITed code here, but we just invalidate all for now.
invalidate_all
end
# @param jit [RubyVM::RJIT::JITState]
# @param block [RubyVM::RJIT::Block]
def ensure_block_entry_exit(jit, cause:)
block = jit.block
if block.entry_exit.nil?
block.entry_exit = Assembler.new.then do |asm|
@exit_compiler.compile_entry_exit(block.pc, block.ctx, asm, cause:)
@ocb.write(asm)
end
end
end
private
def invalidate_all
# On-Stack Replacement
@patches.each do |address, target|
# TODO: assert patches don't overlap each other
@cb.with_write_addr(address) do
asm = Assembler.new
asm.comment('on_tracing_invalidate_all')
asm.jmp(target)
@cb.write(asm)
end
end
@patches.clear
C.rjit_for_each_iseq do |iseq|
# Avoid entering past code
iseq.body.jit_entry = 0
# Avoid reusing past code
iseq.body.rjit_blocks.clear if iseq.body.rjit_blocks
# Compile this again if not converted to trace_* insns
iseq.body.jit_entry_calls = 0
end
end
end
end
end

View File

@ -1,65 +0,0 @@
module RubyVM::RJIT
class JITState < Struct.new(
:iseq, # @param `RubyVM::RJIT::CPointer::Struct_rb_iseq_t`
:pc, # @param [Integer] The JIT target PC
:cfp, # @param `RubyVM::RJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before RJIT is called)
:block, # @param [RubyVM::RJIT::Block]
:stack_size_for_pc, # @param [Integer]
:side_exit_for_pc, # @param [Hash{ Integer => Integer }] { sp_offset => address }
:record_boundary_patch_point, # @param [TrueClass,FalseClass]
)
def initialize(side_exit_for_pc: {}, record_boundary_patch_point: false, **) = super
def insn
Compiler.decode_insn(C.VALUE.new(pc).*)
end
def operand(index, signed: false, ruby: false)
addr = pc + (index + 1) * Fiddle::SIZEOF_VOIDP
value = Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP].unpack(signed ? 'q' : 'Q')[0]
if ruby
value = C.to_ruby(value)
end
value
end
def at_current_insn?
pc == cfp.pc.to_i
end
def peek_at_local(n)
local_table_size = iseq.body.local_table_size
offset = -C::VM_ENV_DATA_SIZE - local_table_size + n + 1
value = (cfp.ep + offset).*
C.to_ruby(value)
end
def peek_at_stack(depth_from_top)
raise 'not at current insn' unless at_current_insn?
offset = -(1 + depth_from_top)
# rb_rjit_branch_stub_hit updates SP, so you don't need to worry about sp_offset
value = (cfp.sp + offset).*
C.to_ruby(value)
end
def peek_at_self
C.to_ruby(cfp.self)
end
def peek_at_block_handler(level)
ep = ep_at_level(cfp, level:)
ep[C::VM_ENV_DATA_INDEX_SPECVAL]
end
private
def ep_at_level(cfp, level:)
ep = cfp.ep
level.times do
# VM_ENV_PREV_EP
ep = C.VALUE.new(ep[C::VM_ENV_DATA_INDEX_SPECVAL] & ~0x03)
end
ep
end
end
end

View File

@ -1,191 +0,0 @@
# frozen_string_literal: true
module RubyVM::RJIT
# Return a Hash for \RJIT statistics. \--rjit-stats makes more information available.
def self.runtime_stats
stats = {}
# Insn exits
INSNS.each_value do |insn|
exits = C.rjit_insn_exits[insn.bin]
if exits > 0
stats[:"exit_#{insn.name}"] = exits
end
end
# Runtime stats
C.rb_rjit_runtime_counters.members.each do |member|
stats[member] = C.rb_rjit_counters.public_send(member)
end
stats[:vm_insns_count] = C.rb_vm_insns_count
# Other stats are calculated here
stats[:side_exit_count] = stats.select { |name, _count| name.start_with?('exit_') }.sum(&:last)
if stats[:vm_insns_count] > 0
retired_in_rjit = stats[:rjit_insns_count] - stats[:side_exit_count]
stats[:total_insns_count] = retired_in_rjit + stats[:vm_insns_count]
stats[:ratio_in_rjit] = 100.0 * retired_in_rjit / stats[:total_insns_count]
else
stats.delete(:vm_insns_count)
end
stats
end
# :nodoc: all
class << self
private
# --yjit-stats at_exit
def print_stats
stats = runtime_stats
$stderr.puts("***RJIT: Printing RJIT statistics on exit***")
print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons')
print_counters(stats, prefix: 'invokeblock_', prompt: 'invokeblock exit reasons')
print_counters(stats, prefix: 'invokesuper_', prompt: 'invokesuper exit reasons')
print_counters(stats, prefix: 'getblockpp_', prompt: 'getblockparamproxy exit reasons')
print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons')
print_counters(stats, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons')
print_counters(stats, prefix: 'optaref_', prompt: 'opt_aref exit reasons')
print_counters(stats, prefix: 'optgetconst_', prompt: 'opt_getconstant_path exit reasons')
print_counters(stats, prefix: 'expandarray_', prompt: 'expandarray exit reasons')
$stderr.puts "compiled_block_count: #{format_number(13, stats[:compiled_block_count])}"
$stderr.puts "side_exit_count: #{format_number(13, stats[:side_exit_count])}"
$stderr.puts "total_insns_count: #{format_number(13, stats[:total_insns_count])}" if stats.key?(:total_insns_count)
$stderr.puts "vm_insns_count: #{format_number(13, stats[:vm_insns_count])}" if stats.key?(:vm_insns_count)
$stderr.puts "rjit_insns_count: #{format_number(13, stats[:rjit_insns_count])}"
$stderr.puts "ratio_in_rjit: #{format('%12.1f', stats[:ratio_in_rjit])}%" if stats.key?(:ratio_in_rjit)
print_exit_counts(stats)
end
def print_counters(stats, prefix:, prompt:)
$stderr.puts("#{prompt}: ")
counters = stats.filter { |key, _| key.start_with?(prefix) }
counters.filter! { |_, value| value != 0 }
counters.transform_keys! { |key| key.to_s.delete_prefix(prefix) }
if counters.empty?
$stderr.puts(" (all relevant counters are zero)")
return
end
counters = counters.to_a
counters.sort_by! { |(_, counter_value)| counter_value }
longest_name_length = counters.max_by { |(name, _)| name.length }.first.length
total = counters.sum { |(_, counter_value)| counter_value }
counters.reverse_each do |(name, value)|
percentage = value.fdiv(total) * 100
$stderr.printf(" %*s %s (%4.1f%%)\n", longest_name_length, name, format_number(10, value), percentage)
end
end
def print_exit_counts(stats, how_many: 20, padding: 2)
exits = stats.filter_map { |name, count| [name.to_s.delete_prefix('exit_'), count] if name.start_with?('exit_') }.to_h
return if exits.empty?
top_exits = exits.sort_by { |_name, count| -count }.first(how_many).to_h
total_exits = exits.values.sum
$stderr.puts "Top-#{top_exits.size} most frequent exit ops (#{format("%.1f", 100.0 * top_exits.values.sum / total_exits)}% of exits):"
name_width = top_exits.map { |name, _count| name.length }.max + padding
count_width = top_exits.map { |_name, count| format_number(10, count).length }.max + padding
top_exits.each do |name, count|
ratio = 100.0 * count / total_exits
$stderr.puts "#{format("%#{name_width}s", name)}: #{format_number(count_width, count)} (#{format('%4.1f', ratio)}%)"
end
end
# Format large numbers with comma separators for readability
def format_number(pad, number)
integer, decimal = number.to_s.split('.')
d_groups = integer.chars.reverse.each_slice(3)
with_commas = d_groups.map(&:join).join(',').reverse
[with_commas, decimal].compact.join('.').rjust(pad, ' ')
end
# --yjit-trace-exits at_exit
def dump_trace_exits
filename = "#{Dir.pwd}/rjit_exit_locations.dump"
File.binwrite(filename, Marshal.dump(exit_traces))
$stderr.puts("RJIT exit locations dumped to:\n#{filename}")
end
# Convert rb_rjit_raw_samples and rb_rjit_line_samples into a StackProf format.
def exit_traces
results = C.rjit_exit_traces
raw_samples = results[:raw].dup
line_samples = results[:lines].dup
frames = results[:frames].dup
samples_count = 0
# Loop through the instructions and set the frame hash with the data.
# We use nonexistent.def for the file name, otherwise insns.def will be displayed
# and that information isn't useful in this context.
RubyVM::INSTRUCTION_NAMES.each_with_index do |name, frame_id|
frame_hash = { samples: 0, total_samples: 0, edges: {}, name: name, file: "nonexistent.def", line: nil, lines: {} }
results[:frames][frame_id] = frame_hash
frames[frame_id] = frame_hash
end
# Loop through the raw_samples and build the hashes for StackProf.
# The loop is based off an example in the StackProf documentation and therefore
# this functionality can only work with that library.
#
# Raw Samples:
# [ length, frame1, frame2, frameN, ..., instruction, count
#
# Line Samples
# [ length, line_1, line_2, line_n, ..., dummy value, count
i = 0
while i < raw_samples.length
stack_length = raw_samples[i] + 1
i += 1 # consume the stack length
prev_frame_id = nil
stack_length.times do |idx|
idx += i
frame_id = raw_samples[idx]
if prev_frame_id
prev_frame = frames[prev_frame_id]
prev_frame[:edges][frame_id] ||= 0
prev_frame[:edges][frame_id] += 1
end
frame_info = frames[frame_id]
frame_info[:total_samples] += 1
frame_info[:lines][line_samples[idx]] ||= [0, 0]
frame_info[:lines][line_samples[idx]][0] += 1
prev_frame_id = frame_id
end
i += stack_length # consume the stack
top_frame_id = prev_frame_id
top_frame_line = 1
sample_count = raw_samples[i]
frames[top_frame_id][:samples] += sample_count
frames[top_frame_id][:lines] ||= {}
frames[top_frame_id][:lines][top_frame_line] ||= [0, 0]
frames[top_frame_id][:lines][top_frame_line][1] += sample_count
samples_count += sample_count
i += 1
end
results[:samples] = samples_count
# Set missed_samples and gc_samples to 0 as their values
# don't matter to us in this context.
results[:missed_samples] = 0
results[:gc_samples] = 0
results
end
end
end

View File

@ -1,221 +0,0 @@
module RubyVM::RJIT
# Represent the type of a value (local/stack/self) in RJIT
Type = Data.define(:type) do
# Check if the type is an immediate
def imm?
case self
in Type::UnknownImm then true
in Type::Nil then true
in Type::True then true
in Type::False then true
in Type::Fixnum then true
in Type::Flonum then true
in Type::ImmSymbol then true
else false
end
end
# Returns true when the type is not specific.
def unknown?
case self
in Type::Unknown | Type::UnknownImm | Type::UnknownHeap then true
else false
end
end
# Returns true when we know the VALUE is a specific handle type,
# such as a static symbol ([Type::ImmSymbol], i.e. true from RB_STATIC_SYM_P()).
# Opposite of [Self::is_unknown].
def specific?
!self.unknown?
end
# Check if the type is a heap object
def heap?
case self
in Type::UnknownHeap then true
in Type::TArray then true
in Type::Hash then true
in Type::HeapSymbol then true
in Type::TString then true
in Type::CString then true
in Type::BlockParamProxy then true
else false
end
end
# Check if it's a T_ARRAY object
def array?
case self
in Type::TArray then true
else false
end
end
# Check if it's a T_STRING object (both TString and CString are T_STRING)
def string?
case self
in Type::TString then true
in Type::CString then true
else false
end
end
# Returns the class if it is known, otherwise nil
def known_class
case self
in Type::Nil then C.rb_cNilClass
in Type::True then C.rb_cTrueClass
in Type::False then C.rb_cFalseClass
in Type::Fixnum then C.rb_cInteger
in Type::Flonum then C.rb_cFloat
in Type::ImmSymbol | Type::HeapSymbol then C.rb_cSymbol
in Type::CString then C.rb_cString
else nil
end
end
# Returns a boolean representing whether the value is truthy if known, otherwise nil
def known_truthy
case self
in Type::Nil then false
in Type::False then false
in Type::UnknownHeap then false
in Type::Unknown | Type::UnknownImm then nil
else true
end
end
# Returns a boolean representing whether the value is equal to nil if known, otherwise nil
def known_nil
case [self, self.known_truthy]
in Type::Nil, _ then true
in Type::False, _ then false # Qfalse is not nil
in _, true then false # if truthy, can't be nil
in _, _ then nil # otherwise unknown
end
end
def diff(dst)
# Perfect match, difference is zero
if self == dst
return TypeDiff::Compatible[0]
end
# Any type can flow into an unknown type
if dst == Type::Unknown
return TypeDiff::Compatible[1]
end
# A CString is also a TString.
if self == Type::CString && dst == Type::TString
return TypeDiff::Compatible[1]
end
# Specific heap type into unknown heap type is imperfect but valid
if self.heap? && dst == Type::UnknownHeap
return TypeDiff::Compatible[1]
end
# Specific immediate type into unknown immediate type is imperfect but valid
if self.imm? && dst == Type::UnknownImm
return TypeDiff::Compatible[1]
end
# Incompatible types
return TypeDiff::Incompatible
end
def upgrade(new_type)
assert(new_type.diff(self) != TypeDiff::Incompatible)
new_type
end
private
def assert(cond)
unless cond
raise "'#{cond.inspect}' was not true"
end
end
end
# This returns an appropriate Type based on a known value
class << Type
def from(val)
if C::SPECIAL_CONST_P(val)
if fixnum?(val)
Type::Fixnum
elsif val.nil?
Type::Nil
elsif val == true
Type::True
elsif val == false
Type::False
elsif static_symbol?(val)
Type::ImmSymbol
elsif flonum?(val)
Type::Flonum
else
raise "Illegal value: #{val.inspect}"
end
else
val_class = C.to_value(C.rb_class_of(val))
if val_class == C.rb_cString && C.rb_obj_frozen_p(val)
return Type::CString
end
if C.to_value(val) == C.rb_block_param_proxy
return Type::BlockParamProxy
end
case C::BUILTIN_TYPE(val)
in C::RUBY_T_ARRAY
Type::TArray
in C::RUBY_T_HASH
Type::Hash
in C::RUBY_T_STRING
Type::TString
else
Type::UnknownHeap
end
end
end
private
def fixnum?(obj)
(C.to_value(obj) & C::RUBY_FIXNUM_FLAG) == C::RUBY_FIXNUM_FLAG
end
def flonum?(obj)
(C.to_value(obj) & C::RUBY_FLONUM_MASK) == C::RUBY_FLONUM_FLAG
end
def static_symbol?(obj)
(C.to_value(obj) & 0xff) == C::RUBY_SYMBOL_FLAG
end
end
# List of types
Type::Unknown = Type[:Unknown]
Type::UnknownImm = Type[:UnknownImm]
Type::UnknownHeap = Type[:UnknownHeap]
Type::Nil = Type[:Nil]
Type::True = Type[:True]
Type::False = Type[:False]
Type::Fixnum = Type[:Fixnum]
Type::Flonum = Type[:Flonum]
Type::Hash = Type[:Hash]
Type::ImmSymbol = Type[:ImmSymbol]
Type::HeapSymbol = Type[:HeapSymbol]
Type::TString = Type[:TString] # An object with the T_STRING flag set, possibly an rb_cString
Type::CString = Type[:CString] # An un-subclassed string of type rb_cString (can have instance vars in some cases)
Type::TArray = Type[:TArray] # An object with the T_ARRAY flag set, possibly an rb_cArray
Type::BlockParamProxy = Type[:BlockParamProxy] # A special sentinel value indicating the block parameter should be read from
module TypeDiff
Compatible = Data.define(:diversion) # The smaller, the more compatible.
Incompatible = :Incompatible
end
end

View File

@ -32,5 +32,5 @@ module Enumerable
# Makes a set from the enumerable object with given arguments.
def to_set(klass = Set, *args, &block)
klass.new(self, *args, &block)
end unless instance_methods.include?(:to_set) # RJIT could already load this from builtin prelude
end
end

View File

@ -110,7 +110,6 @@ int initgroups(const char *, rb_gid_t);
#include "internal/thread.h"
#include "internal/variable.h"
#include "internal/warnings.h"
#include "rjit.h"
#include "ruby/io.h"
#include "ruby/st.h"
#include "ruby/thread.h"
@ -4099,7 +4098,7 @@ fork_check_err(struct rb_process_status *status, int (*chfunc)(void*, char *, si
* The "async_signal_safe" name is a lie, but it is used by pty.c and
* maybe other exts. fork() is not async-signal-safe due to pthread_atfork
* and future POSIX revisions will remove it from a list of signal-safe
* functions. rb_waitpid is not async-signal-safe since RJIT, either.
* functions. rb_waitpid is not async-signal-safe.
* For our purposes, we do not need async-signal-safety, here
*/
rb_pid_t

View File

@ -18,7 +18,6 @@
#include "internal/thread.h"
#include "variable.h"
#include "yjit.h"
#include "rjit.h"
VALUE rb_cRactor;
static VALUE rb_cRactorSelector;
@ -39,7 +38,6 @@ static void
ASSERT_ractor_unlocking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
// GET_EC is NULL in an RJIT worker
if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
rb_bug("recursive ractor locking");
}
@ -50,7 +48,6 @@ static void
ASSERT_ractor_locking(rb_ractor_t *r)
{
#if RACTOR_CHECK_MODE > 0
// GET_EC is NULL in an RJIT worker
if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
rp(r->sync.locked_by);
rb_bug("ractor lock is not acquired.");
@ -67,7 +64,7 @@ ractor_lock(rb_ractor_t *r, const char *file, int line)
rb_native_mutex_lock(&r->sync.lock);
#if RACTOR_CHECK_MODE > 0
if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an RJIT worker
if (rb_current_execution_context(false) != NULL) {
rb_ractor_t *cr = rb_current_ractor_raw(false);
r->sync.locked_by = cr ? rb_ractor_self(cr) : Qundef;
}
@ -2165,7 +2162,6 @@ ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VAL
r->debug = cr->debug;
rb_yjit_before_ractor_spawn();
rb_rjit_before_ractor_spawn();
rb_thread_create_ractor(r, args, block);
RB_GC_GUARD(rv);

501
rjit.c
View File

@ -1,501 +0,0 @@
/**********************************************************************
rjit.c - Ruby JIT compiler functions
Copyright (C) 2023 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
#include "rjit.h" // defines USE_RJIT
#if USE_RJIT
#include "constant.h"
#include "id_table.h"
#include "internal.h"
#include "internal/class.h"
#include "internal/cmdlineopt.h"
#include "internal/cont.h"
#include "internal/file.h"
#include "internal/hash.h"
#include "internal/process.h"
#include "internal/warnings.h"
#include "vm_sync.h"
#include "ractor_core.h"
#ifdef __sun
#define __EXTENSIONS__ 1
#endif
#include "vm_core.h"
#include "vm_callinfo.h"
#include "rjit_c.h"
#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"
#include "internal/gc.h"
#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"
// For mmapp(), sysconf()
#ifndef _WIN32
#include <unistd.h>
#include <sys/mman.h>
#endif
#include "ruby/util.h"
// 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
// freed.
struct rb_rjit_options rb_rjit_opts;
// true if RJIT is enabled.
bool rb_rjit_enabled = false;
// true if --rjit-stats (used before rb_rjit_opts is set)
bool rb_rjit_stats_enabled = false;
// true if --rjit-trace-exits (used before rb_rjit_opts is set)
bool rb_rjit_trace_exits_enabled = false;
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
// 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.
static bool rjit_cancel_p = false;
// `rb_ec_ractor_hooks(ec)->events` is moved to this variable during compilation.
rb_event_flag_t rb_rjit_global_events = 0;
// Basically rb_rjit_opts.stats, but this becomes false during RJIT compilation.
static bool rjit_stats_p = false;
// 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;
// Frames for --rjit-trace-exits
VALUE rb_rjit_raw_samples = 0;
// Line numbers for --rjit-trace-exits
VALUE rb_rjit_line_samples = 0;
// Postponed job handle for triggering rjit_iseq_update_references
static rb_postponed_job_handle_t rjit_iseq_update_references_pjob;
// A default threshold used to add iseq to JIT.
#define DEFAULT_CALL_THRESHOLD 10
// Size of executable memory block in MiB.
#define DEFAULT_EXEC_MEM_SIZE 64
#define opt_match_noarg(s, l, name) \
opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --rjit-" name " is ignored"), 1) : 1)
#define opt_match_arg(s, l, name) \
opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--rjit-" name " needs an argument"), 0))
void
rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt)
{
const size_t l = strlen(s);
if (l == 0) {
return;
}
else if (opt_match_arg(s, l, "exec-mem-size")) {
rjit_opt->exec_mem_size = atoi(s + 1);
}
else if (opt_match_arg(s, l, "call-threshold")) {
rjit_opt->call_threshold = atoi(s + 1);
}
else if (opt_match_noarg(s, l, "stats")) {
rjit_opt->stats = true;
}
else if (opt_match_noarg(s, l, "disable")) {
rjit_opt->disable = true;
}
else if (opt_match_noarg(s, l, "trace")) {
rjit_opt->trace = true;
}
else if (opt_match_noarg(s, l, "trace-exits")) {
rjit_opt->trace_exits = true;
}
else if (opt_match_noarg(s, l, "dump-disasm")) {
rjit_opt->dump_disasm = true;
}
else if (opt_match_noarg(s, l, "verify-ctx")) {
rjit_opt->verify_ctx = true;
}
else {
rb_raise(rb_eRuntimeError,
"invalid RJIT option '%s' (--help will show valid RJIT options)", s);
}
}
#define M(shortopt, longopt, desc) RUBY_OPT_MESSAGE(shortopt, longopt, desc)
const struct ruby_opt_message rb_rjit_option_messages[] = {
M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")."),
M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")."),
M("--rjit-stats", "", "Enable collecting RJIT statistics."),
M("--rjit-disable", "", "Disable RJIT for lazily enabling it with RubyVM::RJIT.enable."),
M("--rjit-trace", "", "Allow TracePoint during JIT compilation."),
M("--rjit-trace-exits", "", "Trace side exit locations."),
#ifdef HAVE_LIBCAPSTONE
M("--rjit-dump-disasm", "", "Dump all JIT code"),
#endif
{0}
};
#undef M
struct rb_rjit_runtime_counters rb_rjit_counters = { 0 };
extern VALUE rb_gc_enable(void);
extern VALUE rb_gc_disable(void);
extern RB_THREAD_LOCAL_SPECIFIER uint64_t rb_vm_insns_count;
// Disable GC, TracePoint, JIT, stats, and $!
#define WITH_RJIT_ISOLATED_USING_PC(using_pc, stmt) do { \
VALUE was_disabled = rb_gc_disable(); \
\
rb_hook_list_t *global_hooks = rb_ec_ractor_hooks(GET_EC()); \
rb_rjit_global_events = global_hooks->events; \
\
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; \
\
bool original_call_p = rb_rjit_call_p; \
rb_rjit_call_p = false; \
\
rjit_stats_p = false; \
uint64_t insns_count = rb_vm_insns_count; \
\
VALUE err = rb_errinfo(); \
\
stmt; \
\
rb_set_errinfo(err); \
\
rb_vm_insns_count = insns_count; \
rjit_stats_p = rb_rjit_opts.stats; \
\
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; \
\
if (!was_disabled) rb_gc_enable(); \
} while (0);
#define WITH_RJIT_ISOLATED(stmt) WITH_RJIT_ISOLATED_USING_PC(false, stmt)
void
rb_rjit_cancel_all(const char *reason)
{
if (!rb_rjit_enabled)
return;
rb_rjit_call_p = false;
rjit_cancel_p = true;
}
void
rb_rjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop)
{
if (!rb_rjit_call_p) return;
rb_rjit_call_p = false;
}
static void
rjit_cme_invalidate(void *data)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
WITH_RJIT_ISOLATED({
rb_funcall(rb_mRJITHooks, rb_intern("on_cme_invalidate"), 1, SIZET2NUM((size_t)data));
});
}
extern int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t func, void *data);
void
rb_rjit_cme_invalidate(rb_callable_method_entry_t *cme)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
// Asynchronously hook the Ruby code since running Ruby in the middle of cme invalidation is dangerous.
rb_workqueue_register(0, rjit_cme_invalidate, (void *)cme);
}
void
rb_rjit_before_ractor_spawn(void)
{
if (!rb_rjit_call_p) return;
rb_rjit_call_p = false;
}
static void
rjit_constant_state_changed(void *data)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
RB_VM_LOCK_ENTER();
rb_vm_barrier();
WITH_RJIT_ISOLATED({
rb_funcall(rb_mRJITHooks, rb_intern("on_constant_state_changed"), 1, SIZET2NUM((size_t)data));
});
RB_VM_LOCK_LEAVE();
}
void
rb_rjit_constant_state_changed(ID id)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
// Asynchronously hook the Ruby code since this is hooked during a "Ruby critical section".
rb_workqueue_register(0, rjit_constant_state_changed, (void *)id);
}
void
rb_rjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
RB_VM_LOCK_ENTER();
rb_vm_barrier();
WITH_RJIT_ISOLATED({
rb_funcall(rb_mRJITHooks, rb_intern("on_constant_ic_update"), 3,
SIZET2NUM((size_t)iseq), SIZET2NUM((size_t)ic), UINT2NUM(insn_idx));
});
RB_VM_LOCK_LEAVE();
}
void
rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
WITH_RJIT_ISOLATED({
rb_funcall(rb_mRJITHooks, rb_intern("on_tracing_invalidate_all"), 1, UINT2NUM(new_iseq_events));
});
}
static void
rjit_iseq_update_references(void *data)
{
if (!rb_rjit_enabled || !rb_rjit_call_p || !rb_mRJITHooks) return;
WITH_RJIT_ISOLATED({
rb_funcall(rb_mRJITHooks, rb_intern("on_update_references"), 0);
});
}
void
rb_rjit_iseq_update_references(struct rb_iseq_constant_body *const body)
{
if (!rb_rjit_enabled) return;
if (body->rjit_blocks) {
body->rjit_blocks = rb_gc_location(body->rjit_blocks);
}
// 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.
rb_postponed_job_trigger(rjit_iseq_update_references_pjob);
}
void
rb_rjit_iseq_mark(VALUE rjit_blocks)
{
if (!rb_rjit_enabled) return;
// Note: This wasn't enough for some reason.
// We actually rely on RubyVM::RJIT::GC_REFS to mark this.
if (rjit_blocks) {
rb_gc_mark_movable(rjit_blocks);
}
}
// 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);
rb_gc_mark(rb_rjit_raw_samples);
rb_gc_mark(rb_rjit_line_samples);
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
}
// TODO: Use this in more places
VALUE
rb_rjit_iseq_new(rb_iseq_t *iseq)
{
return rb_funcall(rb_cRJITIseqPtr, rb_intern("new"), 1, SIZET2NUM((size_t)iseq));
}
void
rb_rjit_compile(const rb_iseq_t *iseq)
{
RB_VM_LOCK_ENTER();
rb_vm_barrier();
WITH_RJIT_ISOLATED_USING_PC(true, {
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);
});
RB_VM_LOCK_LEAVE();
}
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;
WITH_RJIT_ISOLATED_USING_PC(true, {
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);
}
void *
rb_rjit_branch_stub_hit(VALUE branch_stub, int sp_offset, int target0_p)
{
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
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));
});
cfp->sp -= sp_offset; // reset for consistency with the code without the stub
RB_VM_LOCK_LEAVE();
return (void *)NUM2SIZET(result);
}
void
rb_rjit_init(const struct rb_rjit_options *opts)
{
VM_ASSERT(rb_rjit_enabled);
// 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
// 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"))) {
rb_warn("Disabling RJIT because RubyVM::RJIT::Compiler is not defined");
rb_rjit_enabled = false;
return;
}
rjit_iseq_update_references_pjob = rb_postponed_job_preregister(0, rjit_iseq_update_references, NULL);
if (rjit_iseq_update_references_pjob == POSTPONED_JOB_HANDLE_INVALID) {
rb_bug("Could not preregister postponed job for RJIT");
}
rb_mRJITC = rb_const_get(rb_mRJIT, rb_intern("C"));
VALUE rb_cRJITCompiler = rb_const_get(rb_mRJIT, rb_intern("Compiler"));
rb_RJITCompiler = rb_funcall(rb_cRJITCompiler, rb_intern("new"), 0);
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"));
if (rb_rjit_opts.trace_exits) {
rb_rjit_raw_samples = rb_ary_new();
rb_rjit_line_samples = rb_ary_new();
}
// Enable RJIT and stats from here
rb_rjit_call_p = !rb_rjit_opts.disable;
rjit_stats_p = rb_rjit_opts.stats;
}
//
// Primitive for rjit.rb
//
// Same as `rb_rjit_opts.stats`, but this is used before rb_rjit_opts is set.
static VALUE
rjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self)
{
return RBOOL(rb_rjit_stats_enabled);
}
// 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);
}
// Disable anything that could impact stats. It ends up disabling JIT calls as well.
static VALUE
rjit_stop_stats(rb_execution_context_t *ec, VALUE self)
{
rb_rjit_call_p = false;
rjit_stats_p = false;
return Qnil;
}
#include "rjit.rbinc"
#endif // USE_RJIT

101
rjit.h
View File

@ -1,101 +0,0 @@
#ifndef RUBY_RJIT_H
#define RUBY_RJIT_H 1
/**********************************************************************
rjit.h - Interface to RJIT
Copyright (C) 2023 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
#include "ruby/internal/config.h" // defines USE_RJIT
#include "ruby/internal/stdbool.h"
#include "vm_core.h"
# if USE_RJIT
#include "ruby.h"
#include "vm_core.h"
// RJIT options which can be defined on the MRI command line.
struct rb_rjit_options {
// Converted from "rjit" feature flag to tell the enablement
// information to ruby_show_version().
bool on;
// Size of executable memory block in MiB
unsigned int exec_mem_size;
// Number of calls to trigger JIT compilation
unsigned int call_threshold;
// Collect RJIT statistics
bool stats;
// Do not start RJIT until RJIT.enable is called
bool disable;
// Allow TracePoint during JIT compilation
bool trace;
// Trace side exit locations
bool trace_exits;
// Enable disasm of all JIT code
bool dump_disasm;
// Verify context objects
bool verify_ctx;
};
RUBY_SYMBOL_EXPORT_BEGIN
RUBY_EXTERN struct rb_rjit_options rb_rjit_opts;
RUBY_EXTERN bool rb_rjit_call_p;
#define rb_rjit_call_threshold() rb_rjit_opts.call_threshold
extern void rb_rjit_compile(const rb_iseq_t *iseq);
RUBY_SYMBOL_EXPORT_END
extern void rb_rjit_cancel_all(const char *reason);
extern void rb_rjit_init(const struct rb_rjit_options *opts);
extern void rb_rjit_free_iseq(const rb_iseq_t *iseq);
extern void rb_rjit_iseq_update_references(struct rb_iseq_constant_body *const body);
extern void rb_rjit_mark(void);
extern void rb_rjit_iseq_mark(VALUE rjit_blocks);
extern void rjit_notify_waitpid(int exit_code);
extern void rb_rjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
extern void rb_rjit_cme_invalidate(rb_callable_method_entry_t *cme);
extern void rb_rjit_before_ractor_spawn(void);
extern void rb_rjit_constant_state_changed(ID id);
extern void rb_rjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx);
extern void rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events);
extern void rb_rjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
extern void rb_rjit_before_ractor_spawn(void);
extern void rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events);
extern void rb_rjit_collect_vm_usage_insn(int insn);
extern bool rb_rjit_enabled;
extern bool rb_rjit_stats_enabled;
extern bool rb_rjit_trace_exits_enabled;
# else // USE_RJIT
static inline void rb_rjit_compile(const rb_iseq_t *iseq){}
static inline void rb_rjit_cancel_all(const char *reason){}
static inline void rb_rjit_free_iseq(const rb_iseq_t *iseq){}
static inline void rb_rjit_mark(void){}
static inline void rb_rjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}
static inline void rb_rjit_cme_invalidate(rb_callable_method_entry_t *cme) {}
static inline void rb_rjit_before_ractor_spawn(void) {}
static inline void rb_rjit_constant_state_changed(ID id) {}
static inline void rb_rjit_constant_ic_update(const rb_iseq_t *const iseq, IC ic, unsigned insn_idx) {}
static inline void rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_events) {}
#define rb_rjit_enabled false
#define rb_rjit_call_p false
#define rb_rjit_stats_enabled false
#define rb_rjit_trace_exits_enabled false
#define rb_rjit_call_threshold() UINT_MAX
static inline void rb_rjit_collect_vm_usage_insn(int insn) {}
# endif // USE_RJIT
#endif // RUBY_RJIT_H

52
rjit.rb
View File

@ -1,52 +0,0 @@
module RubyVM::RJIT
# Return true if \RJIT is enabled.
def self.enabled?
Primitive.cexpr! 'RBOOL(rb_rjit_enabled)'
end
# Start JIT compilation after \--rjit-disable.
def self.enable
Primitive.cstmt! %{
rb_rjit_call_p = true;
return Qnil;
}
end
if Primitive.rjit_stats_enabled_p
at_exit do
Primitive.rjit_stop_stats
print_stats
end
end
if Primitive.rjit_trace_exits_enabled_p
at_exit do
Primitive.rjit_stop_stats
dump_trace_exits
end
end
end
if RubyVM::RJIT.enabled?
fiddle_paths = nil
begin
require 'fiddle'
require 'fiddle/import'
rescue LoadError
return if fiddle_paths
# Find fiddle from artifacts of bundled gems for make test-all
fiddle_paths = %W[#{__dir__}/.bundle/gems/fiddle-*/lib .bundle/extensions/*/*/fiddle-*].map do |dir|
Dir.glob(dir).first
end.compact
if fiddle_paths.empty?
return # miniruby doesn't support RJIT
else
$LOAD_PATH.unshift(*fiddle_paths)
retry
end
end
require 'ruby_vm/rjit/c_type'
require 'ruby_vm/rjit/compiler'
require 'ruby_vm/rjit/hooks'
require 'ruby_vm/rjit/stats'
end

548
rjit_c.c
View File

@ -1,548 +0,0 @@
/**********************************************************************
rjit_c.c - C helpers for RJIT
Copyright (C) 2017 Takashi Kokubun <k0kubun@ruby-lang.org>.
**********************************************************************/
#include "rjit.h" // defines USE_RJIT
#if USE_RJIT
#include "rjit_c.h"
#include "include/ruby/assert.h"
#include "include/ruby/debug.h"
#include "internal.h"
#include "internal/compile.h"
#include "internal/fixnum.h"
#include "internal/hash.h"
#include "internal/sanitizers.h"
#include "internal/gc.h"
#include "internal/proc.h"
#include "yjit.h"
#include "vm_insnhelper.h"
#include "probes.h"
#include "probes_helper.h"
#include "insns.inc"
#include "insns_info.inc"
// For mmapp(), sysconf()
#ifndef _WIN32
#include <unistd.h>
#include <sys/mman.h>
#endif
#include <errno.h>
#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
// Align the current write position to a multiple of bytes
static uint8_t *
align_ptr(uint8_t *ptr, uint32_t multiple)
{
// Compute the pointer modulo the given alignment boundary
uint32_t rem = ((uint32_t)(uintptr_t)ptr) % multiple;
// If the pointer is already aligned, stop
if (rem == 0)
return ptr;
// Pad the pointer by the necessary amount to align it
uint32_t pad = multiple - rem;
return ptr + pad;
}
#endif
// Address space reservation. Memory pages are mapped on an as needed basis.
// See the Rust mm module for details.
static uint8_t *
rjit_reserve_addr_space(uint32_t mem_size)
{
#ifndef _WIN32
uint8_t *mem_block;
// On Linux
#if defined(MAP_FIXED_NOREPLACE) && defined(_SC_PAGESIZE)
uint32_t const page_size = (uint32_t)sysconf(_SC_PAGESIZE);
uint8_t *const cfunc_sample_addr = (void *)(uintptr_t)&rjit_reserve_addr_space;
uint8_t *const probe_region_end = cfunc_sample_addr + INT32_MAX;
// Align the requested address to page size
uint8_t *req_addr = align_ptr(cfunc_sample_addr, page_size);
// Probe for addresses close to this function using MAP_FIXED_NOREPLACE
// to improve odds of being in range for 32-bit relative call instructions.
do {
mem_block = mmap(
req_addr,
mem_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE,
-1,
0
);
// If we succeeded, stop
if (mem_block != MAP_FAILED) {
ruby_annotate_mmap(mem_block, mem_size, "Ruby:rjit_reserve_addr_space");
break;
}
// +4MB
req_addr += 4 * 1024 * 1024;
} while (req_addr < probe_region_end);
// On MacOS and other platforms
#else
// Try to map a chunk of memory as executable
mem_block = mmap(
(void *)rjit_reserve_addr_space,
mem_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0
);
#endif
// Fallback
if (mem_block == MAP_FAILED) {
// Try again without the address hint (e.g., valgrind)
mem_block = mmap(
NULL,
mem_size,
PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
0
);
if (mem_block != MAP_FAILED) {
ruby_annotate_mmap(mem_block, mem_size, "Ruby:rjit_reserve_addr_space:fallback");
}
}
// Check that the memory mapping was successful
if (mem_block == MAP_FAILED) {
perror("ruby: yjit: mmap:");
if(errno == ENOMEM) {
// No crash report if it's only insufficient memory
exit(EXIT_FAILURE);
}
rb_bug("mmap failed");
}
return mem_block;
#else
// Windows not supported for now
return NULL;
#endif
}
static VALUE
mprotect_write(rb_execution_context_t *ec, VALUE self, VALUE rb_mem_block, VALUE rb_mem_size)
{
void *mem_block = (void *)NUM2SIZET(rb_mem_block);
uint32_t mem_size = NUM2UINT(rb_mem_size);
return RBOOL(mprotect(mem_block, mem_size, PROT_READ | PROT_WRITE) == 0);
}
static VALUE
mprotect_exec(rb_execution_context_t *ec, VALUE self, VALUE rb_mem_block, VALUE rb_mem_size)
{
void *mem_block = (void *)NUM2SIZET(rb_mem_block);
uint32_t mem_size = NUM2UINT(rb_mem_size);
if (mem_size == 0) return Qfalse; // Some platforms return an error for mem_size 0.
if (mprotect(mem_block, mem_size, PROT_READ | PROT_EXEC)) {
rb_bug("Couldn't make JIT page (%p, %lu bytes) executable, errno: %s",
mem_block, (unsigned long)mem_size, strerror(errno));
}
return Qtrue;
}
static VALUE
rjit_optimized_call(VALUE *recv, rb_execution_context_t *ec, int argc, VALUE *argv, int kw_splat, VALUE block_handler)
{
rb_proc_t *proc;
GetProcPtr(recv, proc);
return rb_vm_invoke_proc(ec, proc, argc, argv, kw_splat, block_handler);
}
static VALUE
rjit_str_neq_internal(VALUE str1, VALUE str2)
{
return rb_str_eql_internal(str1, str2) == Qtrue ? Qfalse : Qtrue;
}
static VALUE
rjit_str_simple_append(VALUE str1, VALUE str2)
{
return rb_str_cat(str1, RSTRING_PTR(str2), RSTRING_LEN(str2));
}
static VALUE
rjit_rb_ary_subseq_length(VALUE ary, long beg)
{
long len = RARRAY_LEN(ary);
return rb_ary_subseq(ary, beg, len);
}
static VALUE
rjit_build_kwhash(const struct rb_callinfo *ci, VALUE *sp)
{
const struct rb_callinfo_kwarg *kw_arg = vm_ci_kwarg(ci);
int kw_len = kw_arg->keyword_len;
VALUE hash = rb_hash_new_with_size(kw_len);
for (int i = 0; i < kw_len; i++) {
VALUE key = kw_arg->keywords[i];
VALUE val = *(sp - kw_len + i);
rb_hash_aset(hash, key, val);
}
return hash;
}
// The code we generate in gen_send_cfunc() doesn't fire the c_return TracePoint event
// like the interpreter. When tracing for c_return is enabled, we patch the code after
// the C method return to call into this to fire the event.
static void
rjit_full_cfunc_return(rb_execution_context_t *ec, VALUE return_value)
{
rb_control_frame_t *cfp = ec->cfp;
RUBY_ASSERT_ALWAYS(cfp == GET_EC()->cfp);
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
RUBY_ASSERT_ALWAYS(RUBYVM_CFUNC_FRAME_P(cfp));
RUBY_ASSERT_ALWAYS(me->def->type == VM_METHOD_TYPE_CFUNC);
// CHECK_CFP_CONSISTENCY("full_cfunc_return"); TODO revive this
// Pop the C func's frame and fire the c_return TracePoint event
// Note that this is the same order as vm_call_cfunc_with_frame().
rb_vm_pop_frame(ec);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, cfp->self, me->def->original_id, me->called_id, me->owner, return_value);
// Note, this deviates from the interpreter in that users need to enable
// a c_return TracePoint for this DTrace hook to work. A reasonable change
// since the Ruby return event works this way as well.
RUBY_DTRACE_CMETHOD_RETURN_HOOK(ec, me->owner, me->def->original_id);
// Push return value into the caller's stack. We know that it's a frame that
// uses cfp->sp because we are patching a call done with gen_send_cfunc().
ec->cfp->sp[0] = return_value;
ec->cfp->sp++;
}
static rb_proc_t *
rjit_get_proc_ptr(VALUE procv)
{
rb_proc_t *proc;
GetProcPtr(procv, proc);
return proc;
}
// Use the same buffer size as Stackprof.
#define BUFF_LEN 2048
extern VALUE rb_rjit_raw_samples;
extern VALUE rb_rjit_line_samples;
static void
rjit_record_exit_stack(const VALUE *exit_pc)
{
// Let Primitive.rjit_stop_stats stop this
if (!rb_rjit_call_p) return;
// Get the opcode from the encoded insn handler at this PC
int insn = rb_vm_insn_addr2opcode((void *)*exit_pc);
// Create 2 array buffers to be used to collect frames and lines.
VALUE frames_buffer[BUFF_LEN] = { 0 };
int lines_buffer[BUFF_LEN] = { 0 };
// Records call frame and line information for each method entry into two
// temporary buffers. Returns the number of times we added to the buffer (ie
// the length of the stack).
//
// Call frame info is stored in the frames_buffer, line number information
// in the lines_buffer. The first argument is the start point and the second
// argument is the buffer limit, set at 2048.
int stack_length = rb_profile_frames(0, BUFF_LEN, frames_buffer, lines_buffer);
int samples_length = stack_length + 3; // 3: length, insn, count
// If yjit_raw_samples is less than or equal to the current length of the samples
// we might have seen this stack trace previously.
int prev_stack_len_index = (int)RARRAY_LEN(rb_rjit_raw_samples) - samples_length;
VALUE prev_stack_len_obj;
if (RARRAY_LEN(rb_rjit_raw_samples) >= samples_length && FIXNUM_P(prev_stack_len_obj = RARRAY_AREF(rb_rjit_raw_samples, prev_stack_len_index))) {
int prev_stack_len = NUM2INT(prev_stack_len_obj);
int idx = stack_length - 1;
int prev_frame_idx = 0;
bool seen_already = true;
// If the previous stack length and current stack length are equal,
// loop and compare the current frame to the previous frame. If they are
// not equal, set seen_already to false and break out of the loop.
if (prev_stack_len == stack_length) {
while (idx >= 0) {
VALUE current_frame = frames_buffer[idx];
VALUE prev_frame = RARRAY_AREF(rb_rjit_raw_samples, prev_stack_len_index + prev_frame_idx + 1);
// If the current frame and previous frame are not equal, set
// seen_already to false and break out of the loop.
if (current_frame != prev_frame) {
seen_already = false;
break;
}
idx--;
prev_frame_idx++;
}
// If we know we've seen this stack before, increment the counter by 1.
if (seen_already) {
int prev_idx = (int)RARRAY_LEN(rb_rjit_raw_samples) - 1;
int prev_count = NUM2INT(RARRAY_AREF(rb_rjit_raw_samples, prev_idx));
int new_count = prev_count + 1;
rb_ary_store(rb_rjit_raw_samples, prev_idx, INT2NUM(new_count));
rb_ary_store(rb_rjit_line_samples, prev_idx, INT2NUM(new_count));
return;
}
}
}
rb_ary_push(rb_rjit_raw_samples, INT2NUM(stack_length));
rb_ary_push(rb_rjit_line_samples, INT2NUM(stack_length));
int idx = stack_length - 1;
while (idx >= 0) {
VALUE frame = frames_buffer[idx];
int line = lines_buffer[idx];
rb_ary_push(rb_rjit_raw_samples, frame);
rb_ary_push(rb_rjit_line_samples, INT2NUM(line));
idx--;
}
// Push the insn value into the yjit_raw_samples Vec.
rb_ary_push(rb_rjit_raw_samples, INT2NUM(insn));
// Push the current line onto the yjit_line_samples Vec. This
// points to the line in insns.def.
int line = (int)RARRAY_LEN(rb_rjit_line_samples) - 1;
rb_ary_push(rb_rjit_line_samples, INT2NUM(line));
// Push number of times seen onto the stack, which is 1
// because it's the first time we've seen it.
rb_ary_push(rb_rjit_raw_samples, INT2NUM(1));
rb_ary_push(rb_rjit_line_samples, INT2NUM(1));
}
// For a given raw_sample (frame), set the hash with the caller's
// name, file, and line number. Return the hash with collected frame_info.
static void
rjit_add_frame(VALUE hash, VALUE frame)
{
VALUE frame_id = SIZET2NUM(frame);
if (RTEST(rb_hash_aref(hash, frame_id))) {
return;
}
else {
VALUE frame_info = rb_hash_new();
// Full label for the frame
VALUE name = rb_profile_frame_full_label(frame);
// Absolute path of the frame from rb_iseq_realpath
VALUE file = rb_profile_frame_absolute_path(frame);
// Line number of the frame
VALUE line = rb_profile_frame_first_lineno(frame);
// If absolute path isn't available use the rb_iseq_path
if (NIL_P(file)) {
file = rb_profile_frame_path(frame);
}
rb_hash_aset(frame_info, ID2SYM(rb_intern("name")), name);
rb_hash_aset(frame_info, ID2SYM(rb_intern("file")), file);
rb_hash_aset(frame_info, ID2SYM(rb_intern("samples")), INT2NUM(0));
rb_hash_aset(frame_info, ID2SYM(rb_intern("total_samples")), INT2NUM(0));
rb_hash_aset(frame_info, ID2SYM(rb_intern("edges")), rb_hash_new());
rb_hash_aset(frame_info, ID2SYM(rb_intern("lines")), rb_hash_new());
if (line != INT2FIX(0)) {
rb_hash_aset(frame_info, ID2SYM(rb_intern("line")), line);
}
rb_hash_aset(hash, frame_id, frame_info);
}
}
static VALUE
rjit_exit_traces(void)
{
int samples_len = (int)RARRAY_LEN(rb_rjit_raw_samples);
RUBY_ASSERT(samples_len == RARRAY_LEN(rb_rjit_line_samples));
VALUE result = rb_hash_new();
VALUE raw_samples = rb_ary_new_capa(samples_len);
VALUE line_samples = rb_ary_new_capa(samples_len);
VALUE frames = rb_hash_new();
int idx = 0;
// While the index is less than samples_len, parse yjit_raw_samples and
// yjit_line_samples, then add casted values to raw_samples and line_samples array.
while (idx < samples_len) {
int num = NUM2INT(RARRAY_AREF(rb_rjit_raw_samples, idx));
int line_num = NUM2INT(RARRAY_AREF(rb_rjit_line_samples, idx));
idx++;
rb_ary_push(raw_samples, SIZET2NUM(num));
rb_ary_push(line_samples, INT2NUM(line_num));
// Loop through the length of samples_len and add data to the
// frames hash. Also push the current value onto the raw_samples
// and line_samples array respectively.
for (int o = 0; o < num; o++) {
rjit_add_frame(frames, RARRAY_AREF(rb_rjit_raw_samples, idx));
rb_ary_push(raw_samples, SIZET2NUM(RARRAY_AREF(rb_rjit_raw_samples, idx)));
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
idx++;
}
// insn BIN and lineno
rb_ary_push(raw_samples, RARRAY_AREF(rb_rjit_raw_samples, idx));
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
idx++;
// Number of times seen
rb_ary_push(raw_samples, RARRAY_AREF(rb_rjit_raw_samples, idx));
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
idx++;
}
// Set add the raw_samples, line_samples, and frames to the results
// hash.
rb_hash_aset(result, ID2SYM(rb_intern("raw")), raw_samples);
rb_hash_aset(result, ID2SYM(rb_intern("lines")), line_samples);
rb_hash_aset(result, ID2SYM(rb_intern("frames")), frames);
return result;
}
// An offsetof implementation that works for unnamed struct and union.
// Multiplying 8 for compatibility with libclang's offsetof.
#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
#define SIZEOF(type) RB_SIZE2NUM(sizeof(type))
#define SIGNED_TYPE_P(type) RBOOL((type)(-1) < (type)(1))
// Insn side exit counters
static size_t rjit_insn_exits[VM_INSTRUCTION_SIZE] = { 0 };
// macOS: brew install capstone
// Ubuntu/Debian: apt-get install libcapstone-dev
// Fedora: dnf -y install capstone-devel
#ifdef HAVE_LIBCAPSTONE
#include <capstone/capstone.h>
#endif
// Return an array of [address, mnemonic, op_str]
static VALUE
dump_disasm(rb_execution_context_t *ec, VALUE self, VALUE from, VALUE to, VALUE test)
{
VALUE result = rb_ary_new();
#ifdef HAVE_LIBCAPSTONE
// Prepare for calling cs_disasm
static csh handle;
if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) {
rb_raise(rb_eRuntimeError, "failed to make Capstone handle");
}
size_t from_addr = NUM2SIZET(from);
size_t to_addr = NUM2SIZET(to);
// Call cs_disasm and convert results to a Ruby array
cs_insn *insns;
size_t base_addr = RTEST(test) ? 0 : from_addr; // On tests, start from 0 for output stability.
size_t count = cs_disasm(handle, (const uint8_t *)from_addr, to_addr - from_addr, base_addr, 0, &insns);
for (size_t i = 0; i < count; i++) {
VALUE vals = rb_ary_new_from_args(3, LONG2NUM(insns[i].address), rb_str_new2(insns[i].mnemonic), rb_str_new2(insns[i].op_str));
rb_ary_push(result, vals);
}
// Free memory used by capstone
cs_free(insns, count);
cs_close(&handle);
#endif
return result;
}
// Same as `RubyVM::RJIT.enabled?`, but this is used before it's defined.
static VALUE
rjit_enabled_p(rb_execution_context_t *ec, VALUE self)
{
return RBOOL(rb_rjit_enabled);
}
static int
for_each_iseq_i(void *vstart, void *vend, size_t stride, void *data)
{
VALUE block = (VALUE)data;
VALUE v = (VALUE)vstart;
for (; v != (VALUE)vend; v += stride) {
void *ptr = rb_asan_poisoned_object_p(v);
rb_asan_unpoison_object(v, false);
if (rb_obj_is_iseq(v)) {
extern VALUE rb_rjit_iseq_new(rb_iseq_t *iseq);
rb_iseq_t *iseq = (rb_iseq_t *)v;
rb_funcall(block, rb_intern("call"), 1, rb_rjit_iseq_new(iseq));
}
asan_poison_object_if(ptr, v);
}
return 0;
}
static VALUE
rjit_for_each_iseq(rb_execution_context_t *ec, VALUE self, VALUE block)
{
rb_objspace_each_objects(for_each_iseq_i, (void *)block);
return Qnil;
}
// bindgen references
extern ID rb_get_symbol_id(VALUE name);
extern VALUE rb_fix_aref(VALUE fix, VALUE idx);
extern VALUE rb_str_getbyte(VALUE str, VALUE index);
extern VALUE rb_vm_concat_array(VALUE ary1, VALUE ary2st);
extern VALUE rb_vm_get_ev_const(rb_execution_context_t *ec, VALUE orig_klass, ID id, VALUE allow_nil);
extern VALUE rb_vm_getclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *cfp, ID id, ICVARC ic);
extern VALUE rb_vm_opt_newarray_min(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr);
extern VALUE rb_vm_opt_newarray_max(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr);
extern VALUE rb_vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr);
extern VALUE rb_vm_opt_newarray_pack(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr, VALUE fmt);
extern VALUE rb_vm_splat_array(VALUE flag, VALUE array);
extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
extern bool rb_vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE obj, VALUE v);
extern bool rb_vm_ic_hit_p(IC ic, const VALUE *reg_ep);
extern rb_event_flag_t rb_rjit_global_events;
extern void rb_vm_setinstancevariable(const rb_iseq_t *iseq, VALUE obj, ID id, VALUE val, IVC ic);
extern VALUE rb_vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t throw_state, VALUE throwobj);
extern VALUE rb_reg_new_ary(VALUE ary, int opt);
extern void rb_vm_setclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *cfp, ID id, VALUE val, ICVARC ic);
extern VALUE rb_str_bytesize(VALUE str);
extern const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID mid);
extern VALUE rb_vm_yield_with_cfunc(rb_execution_context_t *ec, const struct rb_captured_block *captured, int argc, const VALUE *argv);
extern VALUE rb_vm_set_ivar_id(VALUE obj, ID id, VALUE val);
extern VALUE rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary);
extern void* rb_rjit_entry_stub_hit(VALUE branch_stub);
extern void* rb_rjit_branch_stub_hit(VALUE branch_stub, int sp_offset, int target0_p);
extern RB_THREAD_LOCAL_SPECIFIER uint64_t rb_vm_insns_count;
#include "rjit_c.rbinc"
#endif // USE_RJIT

165
rjit_c.h
View File

@ -1,165 +0,0 @@
// This file is parsed by tool/rjit/generate.rb to generate rjit_c.rb
#ifndef RJIT_C_H
#define RJIT_C_H
#include "ruby/internal/config.h"
#include "internal/string.h"
#include "internal/struct.h"
#include "internal/variable.h"
#include "vm_core.h"
#include "vm_callinfo.h"
#include "builtin.h"
#include "ccan/list/list.h"
#include "rjit.h"
#include "shape.h"
extern uint8_t *rb_rjit_mem_block;
#define RJIT_RUNTIME_COUNTERS(...) struct rb_rjit_runtime_counters { size_t __VA_ARGS__; };
RJIT_RUNTIME_COUNTERS(
rjit_insns_count,
send_args_splat_kw_splat,
send_args_splat,
send_args_splat_not_array,
send_args_splat_length_not_equal,
send_args_splat_cfunc_var_args,
send_args_splat_arity_error,
send_args_splat_ruby2_hash,
send_args_splat_cfunc_zuper,
send_args_splat_cfunc_ruby2_keywords,
send_kw_splat,
send_kwarg,
send_klass_megamorphic,
send_missing_cme,
send_private,
send_protected_check_failed,
send_tailcall,
send_notimplemented,
send_missing,
send_bmethod,
send_alias,
send_undef,
send_zsuper,
send_refined,
send_stackoverflow,
send_arity,
send_c_tracing,
send_is_a_class_mismatch,
send_instance_of_class_mismatch,
send_keywords,
send_blockiseq,
send_block_handler,
send_block_setup,
send_block_not_nil,
send_block_not_proxy,
send_block_arg,
send_iseq_kwparam,
send_iseq_accepts_no_kwarg,
send_iseq_has_opt,
send_iseq_has_kwrest,
send_iseq_ruby2_keywords,
send_iseq_has_rest_and_captured,
send_iseq_has_rest_and_kw_supplied,
send_iseq_has_no_kw,
send_iseq_zsuper,
send_iseq_materialized_block,
send_iseq_has_rest,
send_iseq_block_arg0_splat,
send_iseq_kw_call,
send_iseq_kw_splat,
send_iseq_splat,
send_iseq_has_rest_and_optional,
send_iseq_arity_error,
send_iseq_missing_optional_kw,
send_iseq_too_many_kwargs,
send_iseq_kwargs_mismatch,
send_iseq_splat_with_kw,
send_iseq_splat_arity_error,
send_iseq_has_rest_and_splat_not_equal,
send_cfunc_variadic,
send_cfunc_too_many_args,
send_cfunc_ruby_array_varg,
send_cfunc_splat_with_kw,
send_cfunc_tracing,
send_cfunc_argc_mismatch,
send_cfunc_toomany_args,
send_attrset_splat,
send_attrset_kwarg,
send_attrset_method,
send_ivar_splat,
send_ivar_opt_send,
send_optimized_send_no_args,
send_optimized_send_not_sym_or_str,
send_optimized_send_mid_class_changed,
send_optimized_send_mid_id_changed,
send_optimized_send_null_mid,
send_optimized_send_send,
send_optimized_call_block,
send_optimized_call_kwarg,
send_optimized_call_splat,
send_optimized_struct_aref_error,
send_optimized_block_call,
send_optimized_struct_aset,
send_bmethod_not_iseq,
send_bmethod_blockarg,
invokesuper_me_changed,
invokesuper_block,
invokeblock_none,
invokeblock_symbol,
invokeblock_proc,
invokeblock_tag_changed,
invokeblock_iseq_block_changed,
invokeblock_iseq_arity,
invokeblock_iseq_arg0_splat,
invokeblock_ifunc_args_splat,
invokeblock_ifunc_kw_splat,
invokeblock_iseq_arg0_args_splat,
invokeblock_iseq_arg0_has_kw,
getivar_megamorphic,
getivar_not_heap,
getivar_special_const,
getivar_too_complex,
optaref_arg_not_fixnum,
optaref_argc_not_one,
optaref_recv_not_array,
optaref_recv_not_hash,
optaref_send,
optgetconst_not_cached,
optgetconst_cref,
optgetconst_cache_miss,
setivar_frozen,
setivar_not_heap,
setivar_megamorphic,
setivar_too_complex,
expandarray_splat,
expandarray_postarg,
expandarray_not_array,
expandarray_rhs_too_small,
getblockpp_block_param_modified,
getblockpp_block_handler_none,
getblockpp_not_gc_guarded,
getblockpp_not_iseq_block,
compiled_block_count
)
#undef RJIT_RUNTIME_COUNTERS
extern struct rb_rjit_runtime_counters rb_rjit_counters;
#endif /* RJIT_C_H */

1671
rjit_c.rb

File diff suppressed because it is too large Load Diff

65
ruby.c
View File

@ -105,8 +105,6 @@ void rb_warning_category_update(unsigned int mask, unsigned int bits);
SEP \
X(frozen_string_literal) \
SEP \
X(rjit) \
SEP \
X(yjit) \
/* END OF FEATURES */
#define EACH_DEBUG_FEATURES(X, SEP) \
@ -119,12 +117,8 @@ enum feature_flag_bits {
EACH_FEATURES(DEFINE_FEATURE, COMMA),
DEFINE_FEATURE(frozen_string_literal_set),
feature_debug_flag_first,
#if defined(RJIT_FORCE_ENABLE) || !USE_YJIT
DEFINE_FEATURE(jit) = feature_rjit,
#else
DEFINE_FEATURE(jit) = feature_yjit,
#endif
feature_jit_mask = FEATURE_BIT(rjit) | FEATURE_BIT(yjit),
feature_jit_mask = FEATURE_BIT(yjit),
feature_debug_flag_begin = feature_debug_flag_first - 1,
EACH_DEBUG_FEATURES(DEFINE_DEBUG_FEATURE, COMMA),
@ -217,9 +211,7 @@ cmdline_options_init(ruby_cmdline_options_t *opt)
opt->ext.enc.index = -1;
opt->intern.enc.index = -1;
opt->features.set = DEFAULT_FEATURES;
#ifdef RJIT_FORCE_ENABLE /* to use with: ./configure cppflags="-DRJIT_FORCE_ENABLE" */
opt->features.set |= FEATURE_BIT(rjit);
#elif defined(YJIT_FORCE_ENABLE)
#if defined(YJIT_FORCE_ENABLE)
opt->features.set |= FEATURE_BIT(yjit);
#endif
opt->dump |= DUMP_BIT(opt_optimize);
@ -314,8 +306,6 @@ usage(const char *name, int help, int highlight, int columns)
#if USE_YJIT
# define PLATFORM_JIT_OPTION "--yjit"
#else
# define PLATFORM_JIT_OPTION "--rjit (experimental)"
#endif
/* This message really ought to be max 23 lines.
@ -346,12 +336,11 @@ usage(const char *name, int help, int highlight, int columns)
M("-W[level=2|:category]", "", "Set warning flag ($-W):\n"
"0 for silent; 1 for moderate; 2 for verbose."),
M("-x[dirpath]", "", "Execute Ruby code starting from a #!ruby line."),
#if USE_YJIT
M("--jit", "", "Enable JIT for the platform; same as " PLATFORM_JIT_OPTION "."),
#endif
#if USE_YJIT
M("--yjit", "", "Enable in-process JIT compiler."),
#endif
#if USE_RJIT
M("--rjit", "", "Enable pure-Ruby JIT compiler (experimental)."),
#endif
M("-h", "", "Print this help message; use --help for longer message."),
};
@ -389,9 +378,6 @@ usage(const char *name, int help, int highlight, int columns)
M("frozen-string-literal", "", "Freeze all string literals (default: disabled)."),
#if USE_YJIT
M("yjit", "", "In-process JIT compiler (default: disabled)."),
#endif
#if USE_RJIT
M("rjit", "", "Pure-Ruby JIT compiler (experimental, default: disabled)."),
#endif
};
static const struct ruby_opt_message warn_categories[] = {
@ -400,9 +386,6 @@ usage(const char *name, int help, int highlight, int columns)
M("performance", "", "Performance issues."),
M("strict_unused_block", "", "Warning unused block strictly"),
};
#if USE_RJIT
extern const struct ruby_opt_message rb_rjit_option_messages[];
#endif
int i;
const char *sb = highlight ? esc_standout+1 : esc_none;
const char *se = highlight ? esc_reset : esc_none;
@ -433,11 +416,6 @@ usage(const char *name, int help, int highlight, int columns)
printf("%s""YJIT options:%s\n", sb, se);
rb_yjit_show_usage(help, highlight, w, columns);
#endif
#if USE_RJIT
printf("%s""RJIT options (experimental):%s\n", sb, se);
for (i = 0; rb_rjit_option_messages[i].str; ++i)
SHOW(rb_rjit_option_messages[i]);
#endif
}
#define rubylib_path_new rb_str_new
@ -1011,7 +989,7 @@ feature_option(const char *str, int len, void *arg, const unsigned int enable)
goto found;
}
if (NAME_MATCH_P("all", str, len)) {
// YJIT and RJIT cannot be enabled at the same time. We enable only one for --enable=all.
// We enable only one JIT for --enable=all.
mask &= ~feature_jit_mask | FEATURE_BIT(jit);
goto found;
}
@ -1457,19 +1435,10 @@ proc_long_options(ruby_cmdline_options_t *opt, const char *s, long argc, char **
ruby_verbose = Qtrue;
}
else if (strcmp("jit", s) == 0) {
#if USE_YJIT || USE_RJIT
#if USE_YJIT
FEATURE_SET(opt->features, FEATURE_BIT(jit));
#else
rb_warn("Ruby was built without JIT support");
#endif
}
else if (is_option_with_optarg("rjit", '-', true, false, false)) {
#if USE_RJIT
extern void rb_rjit_setup_options(const char *s, struct rb_rjit_options *rjit_opt);
FEATURE_SET(opt->features, FEATURE_BIT(rjit));
rb_rjit_setup_options(s, &opt->rjit);
#else
rb_warn("RJIT support is disabled.");
#endif
}
else if (is_option_with_optarg("yjit", '-', true, false, false)) {
@ -1806,16 +1775,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
"environment variables RUBY_GC_HEAP_%d_INIT_SLOTS");
}
#if USE_RJIT
// rb_call_builtin_inits depends on RubyVM::RJIT.enabled?
if (opt->rjit.on)
rb_rjit_enabled = true;
if (opt->rjit.stats)
rb_rjit_stats_enabled = true;
if (opt->rjit.trace_exits)
rb_rjit_trace_exits_enabled = true;
#endif
Init_ext(); /* load statically linked extensions before rubygems */
Init_extra_exts();
@ -1827,11 +1786,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
ruby_init_prelude();
// Initialize JITs after prelude because JITing prelude is typically not optimal.
#if USE_RJIT
// Also, rb_rjit_init is safe only after rb_call_builtin_inits() defines RubyVM::RJIT::Compiler.
if (opt->rjit.on)
rb_rjit_init(&opt->rjit);
#endif
#if USE_YJIT
rb_yjit_init(opt->yjit);
#endif
@ -2359,15 +2313,10 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#endif
}
if (MULTI_BITS_P(FEATURE_SET_BITS(opt->features) & feature_jit_mask)) {
rb_warn("RJIT and YJIT cannot both be enabled at the same time. Exiting");
rb_warn("Only one JIT can be enabled at the same time. Exiting");
return Qfalse;
}
#if USE_RJIT
if (FEATURE_SET_P(opt->features, rjit)) {
opt->rjit.on = true; // set opt->rjit.on for Init_ruby_description() and calling rb_rjit_init()
}
#endif
#if USE_YJIT
if (FEATURE_SET_P(opt->features, yjit)) {
bool rb_yjit_option_disable(void);

View File

@ -101,7 +101,6 @@ XCFLAGS = @XCFLAGS@ $(INCFLAGS) $(_RUBY_DEVEL_enabled:yes=-DRUBY_DEVEL=1) -Dmodu
USE_RUBYGEMS = @USE_RUBYGEMS@
USE_RUBYGEMS_ = $(USE_RUBYGEMS:yes=)
CPPFLAGS = @CPPFLAGS@ $(USE_RUBYGEMS_:no=-DDISABLE_RUBYGEMS=1)
RJIT_SUPPORT = @RJIT_SUPPORT@
YJIT_SUPPORT=@YJIT_SUPPORT@
YJIT_LIBS=@YJIT_LIBS@
YJIT_OBJ=@YJIT_OBJ@

View File

@ -8,10 +8,8 @@ class TestBugReporter < Test::Unit::TestCase
def test_bug_reporter_add
pend "macOS 15 is not working with this test" if macos?(15)
omit "flaky with RJIT" if JITSupport.rjit_enabled?
description = RUBY_DESCRIPTION
description = description.sub(/\+PRISM /, '') unless ParserSupport.prism_enabled_in_subprocess?
description = description.sub(/\+RJIT /, '') unless JITSupport.rjit_force_enabled?
expected_stderr = [
:*,
/\[BUG\]\sSegmentation\sfault.*\n/,

View File

@ -16,18 +16,4 @@ module JITSupport
def yjit_force_enabled?
"#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?YJIT_FORCE_ENABLE\b/)
end
def rjit_supported?
return @rjit_supported if defined?(@rjit_supported)
# nil in mswin
@rjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['RJIT_SUPPORT'])
end
def rjit_enabled?
defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
end
def rjit_force_enabled?
"#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?RJIT_FORCE_ENABLE\b/)
end
end

View File

@ -1,368 +0,0 @@
require 'test/unit'
require_relative '../../lib/jit_support'
return unless JITSupport.rjit_supported?
return unless RubyVM::RJIT.enabled?
return unless RubyVM::RJIT::C.HAVE_LIBCAPSTONE
require 'stringio'
require 'ruby_vm/rjit/assembler'
module RubyVM::RJIT
class TestAssembler < Test::Unit::TestCase
MEM_SIZE = 16 * 1024
def setup
@mem_block ||= C.mmap(MEM_SIZE)
@cb = CodeBlock.new(mem_block: @mem_block, mem_size: MEM_SIZE)
end
def test_add
asm = Assembler.new
asm.add([:rcx], 1) # ADD r/m64, imm8 (Mod 00: [reg])
asm.add(:rax, 0x7f) # ADD r/m64, imm8 (Mod 11: reg)
asm.add(:rbx, 0x7fffffff) # ADD r/m64 imm32 (Mod 11: reg)
asm.add(:rsi, :rdi) # ADD r/m64, r64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: add qword ptr [rcx], 1
0x4: add rax, 0x7f
0x8: add rbx, 0x7fffffff
0xf: add rsi, rdi
EOS
end
def test_and
asm = Assembler.new
asm.and(:rax, 0) # AND r/m64, imm8 (Mod 11: reg)
asm.and(:rbx, 0x7fffffff) # AND r/m64, imm32 (Mod 11: reg)
asm.and(:rcx, [:rdi, 8]) # AND r64, r/m64 (Mod 01: [reg]+disp8)
assert_compile(asm, <<~EOS)
0x0: and rax, 0
0x4: and rbx, 0x7fffffff
0xb: and rcx, qword ptr [rdi + 8]
EOS
end
def test_call
asm = Assembler.new
asm.call(rel32(0xff)) # CALL rel32
asm.call(:rax) # CALL r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: call 0xff
0x5: call rax
EOS
end
def test_cmove
asm = Assembler.new
asm.cmove(:rax, :rcx) # CMOVE r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmove rax, rcx
EOS
end
def test_cmovg
asm = Assembler.new
asm.cmovg(:rbx, :rdi) # CMOVG r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmovg rbx, rdi
EOS
end
def test_cmovge
asm = Assembler.new
asm.cmovge(:rsp, :rbp) # CMOVGE r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmovge rsp, rbp
EOS
end
def test_cmovl
asm = Assembler.new
asm.cmovl(:rdx, :rsp) # CMOVL r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmovl rdx, rsp
EOS
end
def test_cmovle
asm = Assembler.new
asm.cmovle(:rax, :rax) # CMOVLE r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmovle rax, rax
EOS
end
def test_cmovne
asm = Assembler.new
asm.cmovne(:rax, :rbx) # CMOVNE r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS) # cmovne == cmovnz
0x0: cmovne rax, rbx
EOS
end
def test_cmovnz
asm = Assembler.new
asm.cmovnz(:rax, :rbx) # CMOVNZ r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS) # cmovne == cmovnz
0x0: cmovne rax, rbx
EOS
end
def test_cmovz
asm = Assembler.new
asm.cmovz(:rax, :rbx) # CMOVZ r64, r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS) # cmove == cmovz
0x0: cmove rax, rbx
EOS
end
def test_cmp
asm = Assembler.new
asm.cmp(BytePtr[:rax, 8], 8) # CMP r/m8, imm8 (Mod 01: [reg]+disp8)
asm.cmp(DwordPtr[:rax, 8], 0x100) # CMP r/m32, imm32 (Mod 01: [reg]+disp8)
asm.cmp([:rax, 8], 8) # CMP r/m64, imm8 (Mod 01: [reg]+disp8)
asm.cmp([:rbx, 8], 0x100) # CMP r/m64, imm32 (Mod 01: [reg]+disp8)
asm.cmp([:rax, 0x100], 8) # CMP r/m64, imm8 (Mod 10: [reg]+disp32)
asm.cmp(:rax, 8) # CMP r/m64, imm8 (Mod 11: reg)
asm.cmp(:rax, 0x100) # CMP r/m64, imm32 (Mod 11: reg)
asm.cmp([:rax, 8], :rbx) # CMP r/m64, r64 (Mod 01: [reg]+disp8)
asm.cmp([:rax, -0x100], :rbx) # CMP r/m64, r64 (Mod 10: [reg]+disp32)
asm.cmp(:rax, :rbx) # CMP r/m64, r64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: cmp byte ptr [rax + 8], 8
0x4: cmp dword ptr [rax + 8], 0x100
0xb: cmp qword ptr [rax + 8], 8
0x10: cmp qword ptr [rbx + 8], 0x100
0x18: cmp qword ptr [rax + 0x100], 8
0x20: cmp rax, 8
0x24: cmp rax, 0x100
0x2b: cmp qword ptr [rax + 8], rbx
0x2f: cmp qword ptr [rax - 0x100], rbx
0x36: cmp rax, rbx
EOS
end
def test_jbe
asm = Assembler.new
asm.jbe(rel32(0xff)) # JBE rel32
assert_compile(asm, <<~EOS)
0x0: jbe 0xff
EOS
end
def test_je
asm = Assembler.new
asm.je(rel32(0xff)) # JE rel32
assert_compile(asm, <<~EOS)
0x0: je 0xff
EOS
end
def test_jl
asm = Assembler.new
asm.jl(rel32(0xff)) # JL rel32
assert_compile(asm, <<~EOS)
0x0: jl 0xff
EOS
end
def test_jmp
asm = Assembler.new
label = asm.new_label('label')
asm.jmp(label) # JZ rel8
asm.write_label(label)
asm.jmp(rel32(0xff)) # JMP rel32
asm.jmp([:rax, 8]) # JMP r/m64 (Mod 01: [reg]+disp8)
asm.jmp(:rax) # JMP r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: jmp 2
0x2: jmp 0xff
0x7: jmp qword ptr [rax + 8]
0xa: jmp rax
EOS
end
def test_jne
asm = Assembler.new
asm.jne(rel32(0xff)) # JNE rel32
assert_compile(asm, <<~EOS)
0x0: jne 0xff
EOS
end
def test_jnz
asm = Assembler.new
asm.jnz(rel32(0xff)) # JNZ rel32
assert_compile(asm, <<~EOS)
0x0: jne 0xff
EOS
end
def test_jo
asm = Assembler.new
asm.jo(rel32(0xff)) # JO rel32
assert_compile(asm, <<~EOS)
0x0: jo 0xff
EOS
end
def test_jz
asm = Assembler.new
asm.jz(rel32(0xff)) # JZ rel32
assert_compile(asm, <<~EOS)
0x0: je 0xff
EOS
end
def test_lea
asm = Assembler.new
asm.lea(:rax, [:rax, 8]) # LEA r64,m (Mod 01: [reg]+disp8)
asm.lea(:rax, [:rax, 0xffff]) # LEA r64,m (Mod 10: [reg]+disp32)
assert_compile(asm, <<~EOS)
0x0: lea rax, [rax + 8]
0x4: lea rax, [rax + 0xffff]
EOS
end
def test_mov
asm = Assembler.new
asm.mov(:eax, DwordPtr[:rbx, 8]) # MOV r32 r/m32 (Mod 01: [reg]+disp8)
asm.mov(:eax, 0x100) # MOV r32, imm32 (Mod 11: reg)
asm.mov(:rax, [:rbx]) # MOV r64, r/m64 (Mod 00: [reg])
asm.mov(:rax, [:rbx, 8]) # MOV r64, r/m64 (Mod 01: [reg]+disp8)
asm.mov(:rax, [:rbx, 0x100]) # MOV r64, r/m64 (Mod 10: [reg]+disp32)
asm.mov(:rax, :rbx) # MOV r64, r/m64 (Mod 11: reg)
asm.mov(:rax, 0x100) # MOV r/m64, imm32 (Mod 11: reg)
asm.mov(:rax, 0x100000000) # MOV r64, imm64
asm.mov(DwordPtr[:rax, 8], 0x100) # MOV r/m32, imm32 (Mod 01: [reg]+disp8)
asm.mov([:rax], 0x100) # MOV r/m64, imm32 (Mod 00: [reg])
asm.mov([:rax], :rbx) # MOV r/m64, r64 (Mod 00: [reg])
asm.mov([:rax, 8], 0x100) # MOV r/m64, imm32 (Mod 01: [reg]+disp8)
asm.mov([:rax, 8], :rbx) # MOV r/m64, r64 (Mod 01: [reg]+disp8)
asm.mov([:rax, 0x100], 0x100) # MOV r/m64, imm32 (Mod 10: [reg]+disp32)
asm.mov([:rax, 0x100], :rbx) # MOV r/m64, r64 (Mod 10: [reg]+disp32)
assert_compile(asm, <<~EOS)
0x0: mov eax, dword ptr [rbx + 8]
0x3: mov eax, 0x100
0x8: mov rax, qword ptr [rbx]
0xb: mov rax, qword ptr [rbx + 8]
0xf: mov rax, qword ptr [rbx + 0x100]
0x16: mov rax, rbx
0x19: mov rax, 0x100
0x20: movabs rax, 0x100000000
0x2a: mov dword ptr [rax + 8], 0x100
0x31: mov qword ptr [rax], 0x100
0x38: mov qword ptr [rax], rbx
0x3b: mov qword ptr [rax + 8], 0x100
0x43: mov qword ptr [rax + 8], rbx
0x47: mov qword ptr [rax + 0x100], 0x100
0x52: mov qword ptr [rax + 0x100], rbx
EOS
end
def test_or
asm = Assembler.new
asm.or(:rax, 0) # OR r/m64, imm8 (Mod 11: reg)
asm.or(:rax, 0xffff) # OR r/m64, imm32 (Mod 11: reg)
asm.or(:rax, [:rbx, 8]) # OR r64, r/m64 (Mod 01: [reg]+disp8)
assert_compile(asm, <<~EOS)
0x0: or rax, 0
0x4: or rax, 0xffff
0xb: or rax, qword ptr [rbx + 8]
EOS
end
def test_push
asm = Assembler.new
asm.push(:rax) # PUSH r64
assert_compile(asm, <<~EOS)
0x0: push rax
EOS
end
def test_pop
asm = Assembler.new
asm.pop(:rax) # POP r64
assert_compile(asm, <<~EOS)
0x0: pop rax
EOS
end
def test_ret
asm = Assembler.new
asm.ret # RET
assert_compile(asm, "0x0: ret \n")
end
def test_sar
asm = Assembler.new
asm.sar(:rax, 0) # SAR r/m64, imm8 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: sar rax, 0
EOS
end
def test_sub
asm = Assembler.new
asm.sub(:rax, 8) # SUB r/m64, imm8 (Mod 11: reg)
asm.sub(:rax, :rbx) # SUB r/m64, r64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: sub rax, 8
0x4: sub rax, rbx
EOS
end
def test_test
asm = Assembler.new
asm.test(BytePtr[:rax, 8], 16) # TEST r/m8*, imm8 (Mod 01: [reg]+disp8)
asm.test([:rax, 8], 8) # TEST r/m64, imm32 (Mod 01: [reg]+disp8)
asm.test([:rax, 0xffff], 0xffff) # TEST r/m64, imm32 (Mod 10: [reg]+disp32)
asm.test(:rax, 0xffff) # TEST r/m64, imm32 (Mod 11: reg)
asm.test(:eax, :ebx) # TEST r/m32, r32 (Mod 11: reg)
asm.test(:rax, :rbx) # TEST r/m64, r64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: test byte ptr [rax + 8], 0x10
0x4: test qword ptr [rax + 8], 8
0xc: test qword ptr [rax + 0xffff], 0xffff
0x17: test rax, 0xffff
0x1e: test eax, ebx
0x20: test rax, rbx
EOS
end
def test_xor
asm = Assembler.new
asm.xor(:rax, :rbx)
assert_compile(asm, <<~EOS)
0x0: xor rax, rbx
EOS
end
private
def rel32(offset)
@cb.write_addr + 0xff
end
def assert_compile(asm, expected)
actual = compile(asm)
assert_equal expected, actual, "---\n#{actual}---"
end
def compile(asm)
start_addr = @cb.write_addr
@cb.write(asm)
end_addr = @cb.write_addr
io = StringIO.new
@cb.dump_disasm(start_addr, end_addr, io:, color: false, test: true)
io.seek(0)
disasm = io.read
disasm.gsub!(/^ /, '')
disasm.sub!(/\n\z/, '')
disasm
end
end
end

View File

@ -34,7 +34,6 @@ class TestFiber < Test::Unit::TestCase
end
def test_many_fibers
omit 'This is unstable on GitHub Actions --jit-wait. TODO: debug it' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
max = 1000
assert_equal(max, max.times{
Fiber.new{}

View File

@ -253,7 +253,6 @@ class TestGc < Test::Unit::TestCase
end
def test_stat_heap_all
omit "flaky with RJIT, which allocates objects itself" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
stat_heap_all = {}
stat_heap = {}
# Initialize to prevent GC in future calls

View File

@ -681,7 +681,6 @@ class TestIO < Test::Unit::TestCase
if have_nonblock?
def test_copy_stream_no_busy_wait
omit "RJIT has busy wait on GC. This sometimes fails with --jit." if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
omit "multiple threads already active" if Thread.list.size > 1
msg = 'r58534 [ruby-core:80969] [Backport #13533]'
@ -1705,7 +1704,6 @@ class TestIO < Test::Unit::TestCase
end if have_nonblock?
def test_read_nonblock_no_exceptions
omit '[ruby-core:90895] RJIT worker may leave fd open in a forked child' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # TODO: consider acquiring GVL from RJIT worker.
with_pipe {|r, w|
assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
w.puts "HI!"
@ -2515,10 +2513,6 @@ class TestIO < Test::Unit::TestCase
end
def test_autoclose_true_closed_by_finalizer
# http://ci.rvm.jp/results/trunk-rjit@silicon-docker/1465760
# http://ci.rvm.jp/results/trunk-rjit@silicon-docker/1469765
omit 'this randomly fails with RJIT' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
t = Tempfile.new(pre)

View File

@ -3207,7 +3207,6 @@ class TestModule < Test::Unit::TestCase
end
def test_redefinition_mismatch
omit "Investigating trunk-rjit failure on ci.rvm.jp" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
m = Module.new
m.module_eval "A = 1", __FILE__, line = __LINE__
e = assert_raise_with_message(TypeError, /is not a module/) {

View File

@ -591,7 +591,6 @@ class TestRubyOptimization < Test::Unit::TestCase
end
def test_tailcall_not_to_grow_stack
omit 'currently JIT-ed code always creates a new stack frame' if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
bug16161 = '[ruby-core:94881]'
tailcall("#{<<-"begin;"}\n#{<<~"end;"}")

View File

@ -275,7 +275,7 @@ class TestProcess < Test::Unit::TestCase
end;
end
MANDATORY_ENVS = %w[RUBYLIB GEM_HOME GEM_PATH RJIT_SEARCH_BUILD_DIR]
MANDATORY_ENVS = %w[RUBYLIB GEM_HOME GEM_PATH]
case RbConfig::CONFIG['target_os']
when /linux/
MANDATORY_ENVS << 'LD_PRELOAD'
@ -1748,11 +1748,7 @@ class TestProcess < Test::Unit::TestCase
end
assert_send [sig_r, :wait_readable, 5], 'self-pipe not readable'
end
if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # checking -DRJIT_FORCE_ENABLE. It may trigger extra SIGCHLD.
assert_equal [true], signal_received.uniq, "[ruby-core:19744]"
else
assert_equal [true], signal_received, "[ruby-core:19744]"
end
assert_equal [true], signal_received, "[ruby-core:19744]"
rescue NotImplementedError, ArgumentError
ensure
begin

View File

@ -8,7 +8,6 @@ require_relative '../lib/jit_support'
require_relative '../lib/parser_support'
class TestRubyOptions < Test::Unit::TestCase
def self.rjit_enabled? = defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
# Here we're defining our own RUBY_DESCRIPTION without "+PRISM". We do this
@ -22,9 +21,8 @@ class TestRubyOptions < Test::Unit::TestCase
end
NO_JIT_DESCRIPTION =
if rjit_enabled?
RUBY_DESCRIPTION.sub(/\+RJIT /, '')
elsif yjit_enabled?
case
when yjit_enabled?
RUBY_DESCRIPTION.sub(/\+YJIT( (dev|dev_nodebug|stats))? /, '')
else
RUBY_DESCRIPTION
@ -173,21 +171,10 @@ class TestRubyOptions < Test::Unit::TestCase
end
private_constant :VERSION_PATTERN
VERSION_PATTERN_WITH_RJIT =
case RUBY_ENGINE
when 'ruby'
/^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+RJIT (\+MN )?(\+PRISM )?(\+GC)?(\[\w+\]\s|\s)?\[#{q[RUBY_PLATFORM]}\]$/
else
VERSION_PATTERN
end
private_constant :VERSION_PATTERN_WITH_RJIT
def test_verbose
assert_in_out_err([{'RUBY_YJIT_ENABLE' => nil}, "-vve", ""]) do |r, e|
assert_match(VERSION_PATTERN, r[0])
if self.class.rjit_enabled? && !JITSupport.rjit_force_enabled?
assert_equal(NO_JIT_DESCRIPTION, r[0])
elsif self.class.yjit_enabled? && !JITSupport.yjit_force_enabled?
if self.class.yjit_enabled? && !JITSupport.yjit_force_enabled?
assert_equal(NO_JIT_DESCRIPTION, r[0])
else
assert_equal(RUBY_DESCRIPTION, r[0])
@ -212,11 +199,6 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
elsif JITSupport.rjit_supported?
# Avoid failing tests by RJIT warnings
assert_in_out_err(%w(--enable all --disable rjit -e) + [""], "", [], [])
assert_in_out_err(%w(--enable-all --disable-rjit -e) + [""], "", [], [])
assert_in_out_err(%w(--enable=all --disable=rjit -e) + [""], "", [], [])
end
assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [],
/unknown argument for --enable: 'foobarbazqux'/)
@ -258,7 +240,7 @@ class TestRubyOptions < Test::Unit::TestCase
assert_match(VERSION_PATTERN, r[0])
if ENV['RUBY_YJIT_ENABLE'] == '1'
assert_equal(NO_JIT_DESCRIPTION, r[0])
elsif self.class.rjit_enabled? || self.class.yjit_enabled? # checking -D(M|Y)JIT_FORCE_ENABLE
elsif self.class.yjit_enabled? # checking -DYJIT_FORCE_ENABLE
assert_equal(EnvUtil.invoke_ruby(['-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
else
assert_equal(RUBY_DESCRIPTION, r[0])
@ -267,46 +249,6 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
def test_rjit_disabled_version
return unless JITSupport.rjit_supported?
return if JITSupport.yjit_force_enabled?
env = { 'RUBY_YJIT_ENABLE' => nil } # unset in children
[
%w(--version --rjit --disable=rjit),
%w(--version --enable=rjit --disable=rjit),
%w(--version --enable-rjit --disable-rjit),
].each do |args|
assert_in_out_err([env] + args) do |r, e|
assert_match(VERSION_PATTERN, r[0])
assert_match(NO_JIT_DESCRIPTION, r[0])
assert_equal([], e)
end
end
end
def test_rjit_version
return unless JITSupport.rjit_supported?
return if JITSupport.yjit_force_enabled?
env = { 'RUBY_YJIT_ENABLE' => nil } # unset in children
[
%w(--version --rjit),
%w(--version --enable=rjit),
%w(--version --enable-rjit),
].each do |args|
assert_in_out_err([env] + args) do |r, e|
assert_match(VERSION_PATTERN_WITH_RJIT, r[0])
if JITSupport.rjit_force_enabled?
assert_equal(RUBY_DESCRIPTION, r[0])
else
assert_equal(EnvUtil.invoke_ruby([env, '--rjit', '-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
end
assert_equal([], e)
end
end
end
def test_enabled_gc
omit unless /linux|darwin/ =~ RUBY_PLATFORM
@ -850,7 +792,7 @@ class TestRubyOptions < Test::Unit::TestCase
-e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
)x,
%r(
#{ Regexp.quote((TestRubyOptions.rjit_enabled? && !JITSupport.rjit_force_enabled?) ? NO_JIT_DESCRIPTION : RUBY_DESCRIPTION) }\n\n
#{ Regexp.quote(RUBY_DESCRIPTION) }\n\n
)x,
%r(
(?:--\s(?:.+\n)*\n)?

View File

@ -2262,9 +2262,6 @@ CODE
}
# it is dirty hack. usually we shouldn't use such technique
Thread.pass until t.status == 'sleep'
# When RJIT thread exists, t.status becomes 'sleep' even if it does not reach m2t_q.pop.
# This sleep forces it to reach m2t_q.pop for --jit-wait.
sleep 1 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
t.add_trace_func proc{|ev, file, line, *args|
if file == __FILE__

View File

@ -913,7 +913,6 @@ class TestShapes < Test::Unit::TestCase
end
def test_basic_shape_transition
omit "Failing with RJIT for some reason" if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
obj = Example.new
shape = RubyVM::Shape.of(obj)
refute_equal(RubyVM::Shape.root_shape, shape)

View File

@ -323,7 +323,6 @@ class TestThread < Test::Unit::TestCase
s += 1
end
Thread.pass until t.stop?
sleep 1 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
assert_equal(1, s)
t.wakeup
Thread.pass while t.alive?
@ -1479,9 +1478,6 @@ q.pop
def test_thread_interrupt_for_killed_thread
opts = { timeout: 5, timeout_error: nil }
# prevent SIGABRT from slow shutdown with RJIT
opts[:reprieve] = 3 if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
assert_normal_exit(<<-_end, '[Bug #8996]', **opts)
Thread.report_on_exception = false
trap(:TERM){exit}

View File

@ -489,7 +489,7 @@ class TestSocket < Test::Unit::TestCase
end while IO.select([r], nil, nil, 0.1).nil?
n
end
timeout = (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 120 : 30) # for --jit-wait
timeout = 30
assert_equal([[s1],[],[]], IO.select([s1], nil, nil, timeout))
msg, _, _, stamp = s1.recvmsg
assert_equal("a", msg)

View File

@ -89,7 +89,6 @@
#include "internal/time.h"
#include "internal/warnings.h"
#include "iseq.h"
#include "rjit.h"
#include "ruby/debug.h"
#include "ruby/io.h"
#include "ruby/thread.h"
@ -100,10 +99,6 @@
#include "vm_debug.h"
#include "vm_sync.h"
#if USE_RJIT && defined(HAVE_SYS_WAIT_H)
#include <sys/wait.h>
#endif
#ifndef USE_NATIVE_THREAD_PRIORITY
#define USE_NATIVE_THREAD_PRIORITY 0
#define RUBY_THREAD_PRIORITY_MAX 3
@ -4741,9 +4736,6 @@ rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const r
rb_ractor_atfork(vm, th);
rb_vm_postponed_job_atfork();
/* may be held by RJIT threads in parent */
rb_native_mutex_initialize(&vm->workqueue_lock);
/* may be held by any thread in parent */
rb_native_mutex_initialize(&th->interrupt_lock);
ccan_list_head_init(&th->interrupt_exec_tasks);

View File

@ -13,7 +13,6 @@
#include "internal/gc.h"
#include "internal/sanitizers.h"
#include "rjit.h"
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>

View File

@ -64,8 +64,6 @@ File.foreach "config.status" do |line|
when /^(?:X|(?:MINI|RUN|(?:HAVE_)?BASE|BOOTSTRAP|BTEST)RUBY(?:_COMMAND)?$)/; next
when /^INSTALLDOC|TARGET$/; next
when /^DTRACE/; next
when /^RJIT_(CC|SUPPORT)$/; # pass
when /^RJIT_/; next
when /^(?:MAJOR|MINOR|TEENY)$/; vars[name] = val; next
when /^LIBRUBY_D?LD/; next
when /^RUBY_INSTALL_NAME$/; next vars[name] = (install_name = val).dup if $install_name

View File

@ -1,666 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
ENV['GEM_HOME'] = File.expand_path('./.bundle', __dir__)
require 'rubygems/source'
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'ffi-clang', '0.7.0', require: false
end
# Help ffi-clang find libclang
# Hint: apt install libclang1
ENV['LIBCLANG'] ||= Dir.glob("/usr/lib/llvm-*/lib/libclang.so.1").grep_v(/-cpp/).sort.last
require 'ffi/clang'
require 'etc'
require 'fiddle/import'
require 'set'
unless build_dir = ARGV.first
abort "Usage: #{$0} BUILD_DIR"
end
class Node < Struct.new(
:kind,
:spelling,
:type,
:typedef_type,
:bitwidth,
:sizeof_type,
:offsetof,
:enum_value,
:children,
keyword_init: true,
)
end
# Parse a C header with ffi-clang and return Node objects.
# To ease the maintenance, ffi-clang should be used only inside this class.
class HeaderParser
def initialize(header, cflags:)
@translation_unit = FFI::Clang::Index.new.parse_translation_unit(header, cflags, [], {})
end
def parse
parse_children(@translation_unit.cursor)
end
private
def parse_children(cursor)
children = []
cursor.visit_children do |cursor, _parent|
children << parse_cursor(cursor)
next :continue
end
children
end
def parse_cursor(cursor)
unless cursor.kind.start_with?('cursor_')
raise "unexpected cursor kind: #{cursor.kind}"
end
kind = cursor.kind.to_s.delete_prefix('cursor_').to_sym
children = parse_children(cursor)
offsetof = {}
if kind == :struct
children.select { |c| c.kind == :field_decl }.each do |child|
offsetof[child.spelling] = cursor.type.offsetof(child.spelling)
end
end
sizeof_type = nil
if %i[struct union].include?(kind)
sizeof_type = cursor.type.sizeof
end
enum_value = nil
if kind == :enum_constant_decl
enum_value = cursor.enum_value
end
Node.new(
kind: kind,
spelling: cursor.spelling,
type: cursor.type.spelling,
typedef_type: cursor.typedef_type.spelling,
bitwidth: cursor.bitwidth,
sizeof_type: sizeof_type,
offsetof: offsetof,
enum_value: enum_value,
children: children,
)
end
end
# Convert Node objects to a Ruby binding source.
class BindingGenerator
BINDGEN_BEG = '### RJIT bindgen begin ###'
BINDGEN_END = '### RJIT bindgen end ###'
DEFAULTS = { '_Bool' => 'CType::Bool.new' }
DEFAULTS.default_proc = proc { |_h, k| "CType::Stub.new(:#{k})" }
attr_reader :src
# @param src_path [String]
# @param consts [Hash{ Symbol => Array<String> }]
# @param values [Hash{ Symbol => Array<String> }]
# @param funcs [Array<String>]
# @param types [Array<String>]
# @param dynamic_types [Array<String>] #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection
# @param skip_fields [Hash{ Symbol => Array<String> }] Struct fields that are skipped from bindgen
# @param ruby_fields [Hash{ Symbol => Array<String> }] Struct VALUE fields that are considered Ruby objects
def initialize(src_path:, consts:, values:, funcs:, types:, dynamic_types:, skip_fields:, ruby_fields:)
@preamble, @postamble = split_ambles(src_path)
@src = String.new
@consts = consts.transform_values(&:sort)
@values = values.transform_values(&:sort)
@funcs = funcs.sort
@types = types.sort
@dynamic_types = dynamic_types.sort
@skip_fields = skip_fields.transform_keys(&:to_s)
@ruby_fields = ruby_fields.transform_keys(&:to_s)
@references = Set.new
end
def generate(nodes)
println @preamble
# Define macros/enums
@consts.each do |type, values|
values.each do |value|
raise "#{value} isn't a valid constant name" unless ('A'..'Z').include?(value[0])
println " C::#{value} = Primitive.cexpr! %q{ #{type}2NUM(#{value}) }"
end
end
println
# Define variables
@values.each do |type, values|
values.each do |value|
println " def C.#{value} = Primitive.cexpr!(%q{ #{type}2NUM(#{value}) })"
end
end
println
# Define function pointers
@funcs.each do |func|
println " def C.#{func}"
println " Primitive.cexpr! %q{ SIZET2NUM((size_t)#{func}) }"
println " end"
println
end
# Build a hash table for type lookup by name
nodes_index = flatten_nodes(nodes).group_by(&:spelling).transform_values do |values|
# Try to search a declaration with definitions
node_with_children = values.find { |v| !v.children.empty? }
next node_with_children if node_with_children
# Otherwise, assume the last one is the main declaration
values.last
end
# Define types
@types.each do |type|
unless definition = generate_node(nodes_index[type])
raise "Failed to find or generate type: #{type}"
end
println " def C.#{type}"
println "@#{type} ||= #{definition}".gsub(/^/, " ").chomp
println " end"
println
end
# Define dynamic types
@dynamic_types.each do |type|
unless generate_node(nodes_index[type])&.start_with?('CType::Immediate')
raise "Non-immediate type is given to dynamic_types: #{type}"
end
# Only one Primitive.cexpr! is allowed for each line: https://github.com/ruby/ruby/pull/9612
println " def C.#{type}"
println " @#{type} ||= CType::Immediate.find("
println " Primitive.cexpr!(\"SIZEOF(#{type})\"),"
println " Primitive.cexpr!(\"SIGNED_TYPE_P(#{type})\"),"
println " )"
println " end"
println
end
# Leave a stub for types that are referenced but not targeted
(@references - @types - @dynamic_types).each do |type|
println " def C.#{type}"
println " #{DEFAULTS[type]}"
println " end"
println
end
print @postamble
end
private
# Make an array that includes all top-level and nested nodes
def flatten_nodes(nodes)
result = []
nodes.each do |node|
unless node.children.empty?
result.concat(flatten_nodes(node.children))
end
end
result.concat(nodes) # prioritize top-level nodes
result
end
# Return code before BINDGEN_BEG and code after BINDGEN_END
def split_ambles(src_path)
lines = File.read(src_path).lines
preamble_end = lines.index { |l| l.include?(BINDGEN_BEG) }
raise "`#{BINDGEN_BEG}` was not found in '#{src_path}'" if preamble_end.nil?
postamble_beg = lines.index { |l| l.include?(BINDGEN_END) }
raise "`#{BINDGEN_END}` was not found in '#{src_path}'" if postamble_beg.nil?
raise "`#{BINDGEN_BEG}` was found after `#{BINDGEN_END}`" if preamble_end >= postamble_beg
return lines[0..preamble_end].join, lines[postamble_beg..-1].join
end
# Generate code from a node. Used for constructing a complex nested node.
# @param node [Node]
def generate_node(node, sizeof_type: nil)
case node&.kind
when :struct, :union
# node.spelling is often empty for union, but we'd like to give it a name when it has one.
buf = +"CType::#{node.kind.to_s.sub(/\A[a-z]/, &:upcase)}.new(\n"
buf << " \"#{node.spelling}\", Primitive.cexpr!(\"SIZEOF(#{sizeof_type || node.type})\"),\n"
bit_fields_end = node.children.index { |c| c.bitwidth == -1 } || node.children.size # first non-bit field index
node.children.each_with_index do |child, i|
skip_type = sizeof_type&.gsub(/\(\(struct ([^\)]+) \*\)NULL\)->/, '\1.') || node.spelling
next if @skip_fields.fetch(skip_type, []).include?(child.spelling)
field_builder = proc do |field, type|
if node.kind == :struct
to_ruby = @ruby_fields.fetch(node.spelling, []).include?(field)
if child.bitwidth > 0
if bit_fields_end <= i # give up offsetof calculation for non-leading bit fields
raise "non-leading bit fields are not supported. consider including '#{field}' in skip_fields."
end
offsetof = node.offsetof.fetch(field)
else
off_type = sizeof_type || "(*((#{node.type} *)NULL))"
offsetof = "Primitive.cexpr!(\"OFFSETOF(#{off_type}, #{field})\")"
end
" #{field}: [#{type}, #{offsetof}#{', true' if to_ruby}],\n"
else
" #{field}: #{type},\n"
end
end
case child
# BitField is struct-specific. So it must be handled here.
in Node[kind: :field_decl, spelling:, bitwidth:, children: [_grandchild, *]] if bitwidth > 0
buf << field_builder.call(spelling, "CType::BitField.new(#{bitwidth}, #{node.offsetof.fetch(spelling) % 8})")
# "(unnamed ...)" struct and union are handled here, which are also struct-specific.
in Node[kind: :field_decl, spelling:, type:, children: [grandchild]] if type.match?(/\((unnamed|anonymous) [^)]+\)\z/)
if sizeof_type
child_type = "#{sizeof_type}.#{child.spelling}"
else
child_type = "((#{node.type} *)NULL)->#{child.spelling}"
end
buf << field_builder.call(spelling, generate_node(grandchild, sizeof_type: child_type).gsub(/^/, ' ').sub(/\A +/, ''))
# In most cases, we'd like to let generate_type handle the type unless it's "(unnamed ...)".
in Node[kind: :field_decl, spelling:, type:] if !type.empty?
buf << field_builder.call(spelling, generate_type(type))
else # forward declarations are ignored
end
end
buf << ")"
when :typedef_decl
case node.children
in [child]
generate_node(child)
in [child, Node[kind: :integer_literal]]
generate_node(child)
in _ unless node.typedef_type.empty?
generate_type(node.typedef_type)
end
when :enum_decl
generate_type('int')
when :type_ref
generate_type(node.spelling)
end
end
# Generate code from a type name. Used for resolving the name of a simple leaf node.
# @param type [String]
def generate_type(type)
if type.match?(/\[\d+\]\z/)
return "CType::Array.new { #{generate_type(type.sub!(/\[\d+\]\z/, ''))} }"
end
type = type.delete_suffix('const')
if type.end_with?('*')
if type == 'const void *'
# `CType::Pointer.new { CType::Immediate.parse("void") }` is never useful,
# so specially handle that case here.
return 'CType::Immediate.parse("void *")'
end
return "CType::Pointer.new { #{generate_type(type.delete_suffix('*').rstrip)} }"
end
type = type.gsub(/((const|volatile) )+/, '').rstrip
if type.start_with?(/(struct|union|enum) /)
target = type.split(' ', 2).last
push_target(target)
"self.#{target}"
else
begin
ctype = Fiddle::Importer.parse_ctype(type)
rescue Fiddle::DLError
push_target(type)
"self.#{type}"
else
# Convert any function pointers to void* to workaround FILE* vs int*
if ctype == Fiddle::TYPE_VOIDP
"CType::Immediate.parse(\"void *\")"
else
"CType::Immediate.parse(#{type.dump})"
end
end
end
end
def print(str)
@src << str
end
def println(str = "")
@src << str << "\n"
end
def chomp
@src.delete_suffix!("\n")
end
def rstrip!
@src.rstrip!
end
def push_target(target)
unless target.match?(/\A\w+\z/)
raise "invalid target: #{target}"
end
@references << target
end
end
src_dir = File.expand_path('../..', __dir__)
src_path = File.join(src_dir, 'rjit_c.rb')
build_dir = File.expand_path(build_dir)
cflags = [
src_dir,
build_dir,
File.join(src_dir, 'include'),
File.join(build_dir, ".ext/include/#{RUBY_PLATFORM}"),
].map { |dir| "-I#{dir}" }
# Clear .cache/clangd created by the language server, which could break this bindgen
clangd_cache = File.join(src_dir, '.cache/clangd')
if Dir.exist?(clangd_cache)
system('rm', '-rf', clangd_cache, exception: true)
end
# Parse rjit_c.h and generate rjit_c.rb
nodes = HeaderParser.new(File.join(src_dir, 'rjit_c.h'), cflags: cflags).parse
generator = BindingGenerator.new(
src_path: src_path,
consts: {
LONG: %w[
UNLIMITED_ARGUMENTS
VM_ENV_DATA_INDEX_ME_CREF
VM_ENV_DATA_INDEX_SPECVAL
],
SIZET: %w[
ARRAY_REDEFINED_OP_FLAG
BOP_AND
BOP_AREF
BOP_EQ
BOP_EQQ
BOP_FREEZE
BOP_GE
BOP_GT
BOP_LE
BOP_LT
BOP_MINUS
BOP_MOD
BOP_OR
BOP_PLUS
BUILTIN_ATTR_LEAF
HASH_REDEFINED_OP_FLAG
INTEGER_REDEFINED_OP_FLAG
INVALID_SHAPE_ID
METHOD_VISI_PRIVATE
METHOD_VISI_PROTECTED
METHOD_VISI_PUBLIC
METHOD_VISI_UNDEF
OBJ_TOO_COMPLEX_SHAPE_ID
OPTIMIZED_METHOD_TYPE_BLOCK_CALL
OPTIMIZED_METHOD_TYPE_CALL
OPTIMIZED_METHOD_TYPE_SEND
OPTIMIZED_METHOD_TYPE_STRUCT_AREF
OPTIMIZED_METHOD_TYPE_STRUCT_ASET
RARRAY_EMBED_FLAG
RARRAY_EMBED_LEN_MASK
RARRAY_EMBED_LEN_SHIFT
RMODULE_IS_REFINEMENT
ROBJECT_EMBED
RSTRUCT_EMBED_LEN_MASK
RUBY_EVENT_CLASS
RUBY_EVENT_C_CALL
RUBY_EVENT_C_RETURN
RUBY_FIXNUM_FLAG
RUBY_FLONUM_FLAG
RUBY_FLONUM_MASK
RUBY_IMMEDIATE_MASK
RUBY_SPECIAL_SHIFT
RUBY_SYMBOL_FLAG
RUBY_T_ARRAY
RUBY_T_CLASS
RUBY_T_ICLASS
RUBY_T_HASH
RUBY_T_MASK
RUBY_T_MODULE
RUBY_T_STRING
RUBY_T_SYMBOL
RUBY_T_OBJECT
SHAPE_FLAG_SHIFT
SHAPE_FROZEN
SHAPE_ID_NUM_BITS
SHAPE_IVAR
SHAPE_MASK
SHAPE_ROOT
STRING_REDEFINED_OP_FLAG
T_OBJECT
VM_BLOCK_HANDLER_NONE
VM_CALL_ARGS_BLOCKARG
VM_CALL_ARGS_SPLAT
VM_CALL_FCALL
VM_CALL_FORWARDING
VM_CALL_KWARG
VM_CALL_KW_SPLAT
VM_CALL_KW_SPLAT_MUT
VM_CALL_KW_SPLAT_bit
VM_CALL_OPT_SEND
VM_CALL_TAILCALL
VM_CALL_TAILCALL_bit
VM_CALL_ZSUPER
VM_ENV_DATA_INDEX_FLAGS
VM_ENV_DATA_SIZE
VM_ENV_FLAG_LOCAL
VM_ENV_FLAG_WB_REQUIRED
VM_FRAME_FLAG_BMETHOD
VM_FRAME_FLAG_CFRAME
VM_FRAME_FLAG_CFRAME_KW
VM_FRAME_FLAG_LAMBDA
VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM
VM_FRAME_MAGIC_BLOCK
VM_FRAME_MAGIC_CFUNC
VM_FRAME_MAGIC_METHOD
VM_METHOD_TYPE_ALIAS
VM_METHOD_TYPE_ATTRSET
VM_METHOD_TYPE_BMETHOD
VM_METHOD_TYPE_CFUNC
VM_METHOD_TYPE_ISEQ
VM_METHOD_TYPE_IVAR
VM_METHOD_TYPE_MISSING
VM_METHOD_TYPE_NOTIMPLEMENTED
VM_METHOD_TYPE_OPTIMIZED
VM_METHOD_TYPE_REFINED
VM_METHOD_TYPE_UNDEF
VM_METHOD_TYPE_ZSUPER
VM_SPECIAL_OBJECT_VMCORE
RUBY_ENCODING_MASK
RUBY_FL_FREEZE
RHASH_PASS_AS_KEYWORDS
],
},
values: {
SIZET: %w[
block_type_iseq
imemo_iseq
imemo_callinfo
rb_block_param_proxy
rb_cArray
rb_cFalseClass
rb_cFloat
rb_cInteger
rb_cNilClass
rb_cString
rb_cSymbol
rb_cTrueClass
rb_rjit_global_events
rb_mRubyVMFrozenCore
rb_vm_insns_count
idRespond_to_missing
],
},
funcs: %w[
rb_ary_entry_internal
rb_ary_push
rb_ary_resurrect
rb_ary_store
rb_ec_ary_new_from_values
rb_ec_str_resurrect
rb_ensure_iv_list_size
rb_fix_aref
rb_fix_div_fix
rb_fix_mod_fix
rb_fix_mul_fix
rb_gc_writebarrier
rb_get_symbol_id
rb_hash_aref
rb_hash_aset
rb_hash_bulk_insert
rb_hash_new
rb_hash_new_with_size
rb_hash_resurrect
rb_ivar_get
rb_obj_as_string_result
rb_obj_is_kind_of
rb_str_concat_literals
rb_str_eql_internal
rb_str_getbyte
rb_vm_bh_to_procval
rb_vm_concat_array
rb_vm_defined
rb_vm_get_ev_const
rb_vm_getclassvariable
rb_vm_ic_hit_p
rb_vm_opt_newarray_min
rb_vm_opt_newarray_max
rb_vm_opt_newarray_hash
rb_vm_opt_newarray_pack
rb_vm_setinstancevariable
rb_vm_splat_array
rjit_full_cfunc_return
rjit_optimized_call
rjit_str_neq_internal
rjit_record_exit_stack
rb_ivar_defined
rb_vm_throw
rb_backref_get
rb_reg_last_match
rb_reg_match_pre
rb_reg_match_post
rb_reg_match_last
rb_reg_nth_match
rb_gvar_get
rb_range_new
rb_ary_tmp_new_from_values
rb_reg_new_ary
rb_ary_clear
rb_str_intern
rb_vm_setclassvariable
rb_str_bytesize
rjit_str_simple_append
rb_str_buf_append
rb_str_dup
rb_vm_yield_with_cfunc
rb_vm_set_ivar_id
rb_ary_dup
rjit_rb_ary_subseq_length
rb_ary_unshift_m
rjit_build_kwhash
rb_rjit_entry_stub_hit
rb_rjit_branch_stub_hit
rb_sym_to_proc
],
types: %w[
CALL_DATA
IC
ID
IVC
RArray
RB_BUILTIN
RBasic
RObject
RStruct
RString
attr_index_t
iseq_inline_constant_cache
iseq_inline_constant_cache_entry
iseq_inline_iv_cache_entry
iseq_inline_storage_entry
method_optimized_type
rb_block
rb_block_type
rb_builtin_function
rb_call_data
rb_callable_method_entry_struct
rb_callable_method_entry_t
rb_callcache
rb_callinfo
rb_captured_block
rb_cfunc_t
rb_control_frame_t
rb_cref_t
rb_execution_context_struct
rb_execution_context_t
rb_iseq_constant_body
rb_iseq_location_t
rb_iseq_struct
rb_iseq_t
rb_method_attr_t
rb_method_bmethod_t
rb_method_cfunc_t
rb_method_definition_struct
rb_method_entry_t
rb_method_iseq_t
rb_method_optimized_t
rb_method_type_t
rb_proc_t
rb_rjit_runtime_counters
rb_serial_t
rb_shape
rb_shape_t
rb_thread_struct
rb_jit_func_t
rb_iseq_param_keyword
rb_rjit_options
rb_callinfo_kwarg
],
# #ifdef-dependent immediate types, which need Primitive.cexpr! for type detection
dynamic_types: %w[
VALUE
shape_id_t
],
skip_fields: {
'rb_execution_context_struct.machine': %w[regs], # differs between macOS and Linux
rb_execution_context_struct: %w[method_missing_reason], # non-leading bit fields not supported
rb_iseq_constant_body: %w[jit_exception jit_exception_calls yjit_payload yjit_calls_at_interv], # conditionally defined
rb_thread_struct: %w[status has_dedicated_nt to_kill abort_on_exception report_on_exception pending_interrupt_queue_checked],
:'' => %w[is_from_method is_lambda is_isolated], # rb_proc_t
},
ruby_fields: {
rb_iseq_constant_body: %w[
rjit_blocks
],
rb_iseq_location_struct: %w[
base_label
label
pathobj
],
rb_callable_method_entry_t: %w[
defined_class
],
rb_callable_method_entry_struct: %w[
defined_class
],
},
)
generator.generate(nodes)
# Write rjit_c.rb
File.write(src_path, generator.src)

View File

@ -10,7 +10,7 @@
#include "iseq.h"
// This is used to tell RJIT that this insn would be leaf if CHECK_INTS didn't exist.
// This is used to tell JIT that this insn would be leaf if CHECK_INTS didn't exist.
// It should be used only when RUBY_VM_CHECK_INTS is directly written in insns.def.
static bool leafness_of_check_ints = false;

View File

@ -1,14 +0,0 @@
module RubyVM::RJIT # :nodoc: all
Instruction = Data.define(:name, :bin, :len, :operands)
INSNS = {
% RubyVM::Instructions.each_with_index do |insn, i|
<%= i %> => Instruction.new(
name: :<%= insn.name %>,
bin: <%= i %>, # BIN(<%= insn.name %>)
len: <%= insn.width %>, # insn_len
operands: <%= (insn.operands unless insn.name.start_with?('trace_')).inspect %>,
),
% end
}
end

View File

@ -6,7 +6,6 @@ class TestHideSkip < Test::Unit::TestCase
assert_not_match(/^ *1\) Skipped/, hideskip)
assert_match(/^ *1\) Skipped.*^ *2\) Skipped/m, hideskip("--show-skip"))
output = hideskip("--hide-skip")
output.gsub!(/Successful RJIT finish\n/, '') if defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?
assert_match(/assertions\/s.\n+2 tests, 0 assertions, 0 failures, 0 errors, 2 skips/, output)
end

View File

@ -6,7 +6,7 @@ module TestParallel
PARALLEL_RB = "#{__dir__}/../../lib/test/unit/parallel.rb"
TESTS = "#{__dir__}/tests_for_parallel"
# use large timeout for --jit-wait
TIMEOUT = EnvUtil.apply_timeout_scale(defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled? ? 100 : 30)
TIMEOUT = EnvUtil.apply_timeout_scale(30)
class TestParallelWorker < Test::Unit::TestCase
def setup

View File

@ -15,7 +15,6 @@
#include "ruby/ruby.h"
#include "version.h"
#include "vm_core.h"
#include "rjit.h"
#include "yjit.h"
#include <stdio.h>
@ -157,12 +156,6 @@ Init_version(void)
rb_provide("ruby2_keywords.rb");
}
#if USE_RJIT
#define RJIT_OPTS_ON opt->rjit.on
#else
#define RJIT_OPTS_ON 0
#endif
#if USE_YJIT
#define YJIT_OPTS_ON opt->yjit
#else
@ -238,7 +231,6 @@ void
Init_ruby_description(ruby_cmdline_options_t *opt)
{
const char *const jit_opt =
RJIT_OPTS_ON ? " +RJIT" :
YJIT_OPTS_ON ? YJIT_DESCRIPTION :
"";
define_ruby_description(jit_opt);

16
vm.c
View File

@ -32,7 +32,6 @@
#include "internal/sanitizers.h"
#include "internal/variable.h"
#include "iseq.h"
#include "rjit.h"
#include "symbol.h" // This includes a macro for a more performant rb_id2sym.
#include "yjit.h"
#include "ruby/st.h"
@ -420,7 +419,7 @@ rb_yjit_threshold_hit(const rb_iseq_t *iseq, uint64_t entry_calls)
#define rb_yjit_threshold_hit(iseq, entry_calls) false
#endif
#if USE_RJIT || USE_YJIT
#if USE_YJIT
// Generate JIT code that supports the following kinds of ISEQ entries:
// * The first ISEQ on vm_exec (e.g. <main>, or Ruby methods/blocks
// called by a C method). The current frame has VM_FRAME_FLAG_FINISH.
@ -437,16 +436,13 @@ jit_compile(rb_execution_context_t *ec)
bool yjit_enabled = rb_yjit_enabled_p;
// Increment the ISEQ's call counter and trigger JIT compilation if not compiled
if (body->jit_entry == NULL && (yjit_enabled || rb_rjit_call_p)) {
if (body->jit_entry == NULL && yjit_enabled) {
body->jit_entry_calls++;
if (yjit_enabled) {
if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
rb_yjit_compile_iseq(iseq, ec, false);
}
}
else if (body->jit_entry_calls == rb_rjit_call_threshold()) {
rb_rjit_compile(iseq);
}
}
return body->jit_entry;
}
@ -2171,7 +2167,6 @@ rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
rb_id2name(me->called_id)
);
rb_yjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
rb_rjit_bop_redefined(flag, (enum ruby_basic_operators)bop);
ruby_vm_redefined_flag[bop] |= flag;
}
}
@ -3036,7 +3031,6 @@ rb_vm_mark(void *ptr)
}
rb_thread_sched_mark_zombies(vm);
rb_rjit_mark();
}
RUBY_MARK_LEAVE("vm");
@ -4455,12 +4449,6 @@ void Init_builtin_yjit(void) {}
// Whether YJIT is enabled or not, we load yjit_hook.rb to remove Kernel#with_yjit.
#include "yjit_hook.rbinc"
// Stub for builtin function when not building RJIT units
#if !USE_RJIT
void Init_builtin_rjit(void) {}
void Init_builtin_rjit_c(void) {}
#endif
/* top self */
static VALUE

View File

@ -365,8 +365,6 @@ pathobj_realpath(VALUE pathobj)
}
/* Forward declarations */
struct rb_rjit_unit;
typedef uintptr_t iseq_bits_t;
#define ISEQ_IS_SIZE(body) (body->ic_size + body->ivc_size + body->ise_size + body->icvarc_size)
@ -536,7 +534,7 @@ struct rb_iseq_constant_body {
const rb_iseq_t *mandatory_only_iseq;
#if USE_RJIT || USE_YJIT
#if USE_YJIT
// Function pointer for JIT code on jit_exec()
rb_jit_func_t jit_entry;
// Number of calls on jit_exec()
@ -550,11 +548,6 @@ struct rb_iseq_constant_body {
long unsigned jit_exception_calls;
#endif
#if USE_RJIT
// RJIT stores some data on each iseq.
VALUE rjit_blocks;
#endif
#if USE_YJIT
// YJIT stores some data on each iseq.
void *yjit_payload;

View File

@ -11,7 +11,7 @@
#include <math.h>
#if USE_YJIT || USE_RJIT
#if USE_YJIT
// The number of instructions executed on vm_exec_core. --yjit-stats uses this.
RB_THREAD_LOCAL_SPECIFIER uint64_t rb_vm_insns_count = 0;
#endif

View File

@ -6372,7 +6372,7 @@ vm_ic_track_const_chain(rb_control_frame_t *cfp, IC ic, const ID *segments)
RB_VM_LOCK_LEAVE();
}
// For RJIT inlining
// For JIT inlining
static inline bool
vm_inlined_ic_hit_p(VALUE flags, VALUE value, const rb_cref_t *ic_cref, const VALUE *reg_ep)
{
@ -6417,7 +6417,6 @@ vm_ic_update(const rb_iseq_t *iseq, IC ic, VALUE val, const VALUE *reg_ep, const
RUBY_ASSERT(pc >= ISEQ_BODY(iseq)->iseq_encoded);
unsigned pos = (unsigned)(pc - ISEQ_BODY(iseq)->iseq_encoded);
rb_yjit_constant_ic_update(iseq, ic, pos);
rb_rjit_constant_ic_update(iseq, ic, pos);
}
VALUE

View File

@ -16,7 +16,7 @@ RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_invalidations;
RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_misses;
RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state;
#if USE_YJIT || USE_RJIT // We want vm_insns_count on any JIT-enabled build.
#if USE_YJIT // We want vm_insns_count on any JIT-enabled build.
// Increment vm_insns_count for --yjit-stats. We increment this even when
// --yjit or --yjit-stats is not used because branching to skip it is slower.
// We also don't use ATOMIC_INC for performance, allowing inaccuracy on Ractors.

View File

@ -4,7 +4,6 @@
#include "id_table.h"
#include "yjit.h"
#include "rjit.h"
#define METHOD_DEBUG 0
@ -123,7 +122,6 @@ vm_cme_invalidate(rb_callable_method_entry_t *cme)
RB_DEBUG_COUNTER_INC(cc_cme_invalidate);
rb_yjit_cme_invalidate(cme);
rb_rjit_cme_invalidate(cme);
}
static int
@ -149,7 +147,6 @@ rb_clear_constant_cache_for_id(ID id)
}
rb_yjit_constant_state_changed(id);
rb_rjit_constant_state_changed(id);
}
static void
@ -188,7 +185,6 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
if (cc_tbl && rb_id_table_lookup(cc_tbl, mid, &ccs_data)) {
struct rb_class_cc_entries *ccs = (struct rb_class_cc_entries *)ccs_data;
rb_yjit_cme_invalidate((rb_callable_method_entry_t *)ccs->cme);
rb_rjit_cme_invalidate((rb_callable_method_entry_t *)ccs->cme);
if (NIL_P(ccs->cme->owner)) invalidate_negative_cache(mid);
rb_vm_ccs_free(ccs);
rb_id_table_delete(cc_tbl, mid);
@ -202,9 +198,6 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
if (rb_yjit_enabled_p && rb_id_table_lookup(cm_tbl, mid, &cme)) {
rb_yjit_cme_invalidate((rb_callable_method_entry_t *)cme);
}
if (rb_rjit_enabled && rb_id_table_lookup(cm_tbl, mid, &cme)) {
rb_rjit_cme_invalidate((rb_callable_method_entry_t *)cme);
}
rb_id_table_delete(cm_tbl, mid);
RB_DEBUG_COUNTER_INC(cc_invalidate_leaf_callable);
}

View File

@ -30,7 +30,6 @@
#include "internal/symbol.h"
#include "internal/thread.h"
#include "iseq.h"
#include "rjit.h"
#include "ruby/atomic.h"
#include "ruby/debug.h"
#include "vm_core.h"
@ -136,7 +135,6 @@ update_global_event_hook(rb_event_flag_t prev_events, rb_event_flag_t new_events
// Do this after event flags updates so other ractors see updated vm events
// when they wake up.
rb_yjit_tracing_invalidate_all();
rb_rjit_tracing_invalidate_all(new_iseq_events);
}
}
@ -1286,7 +1284,6 @@ rb_tracepoint_enable_for_target(VALUE tpval, VALUE target, VALUE target_line)
}
rb_yjit_tracing_invalidate_all();
rb_rjit_tracing_invalidate_all(tp->events);
ruby_vm_event_local_num++;

View File

@ -363,9 +363,6 @@ CPPFLAGS = $(DEFS) $(ARCHDEFS) $(CPPFLAGS)
!if "$(USE_RUBYGEMS)" == "no"
CPPFLAGS = -DDISABLE_RUBYGEMS $(CPPFLAGS)
!endif
!ifndef RJIT_SUPPORT
RJIT_SUPPORT = no
!endif
POSTLINK =
DLDFLAGS = $(LDFLAGS) -dll
@ -966,7 +963,6 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub
#define RUBY_COREDLL "$(RT)"
#define RUBY_PLATFORM "$(arch)"
#define RUBY_SITEARCH "$(sitearch)"
#define USE_RJIT 0
#endif /* $(guard) */
<<
@ -1166,7 +1162,6 @@ s,@srcdir@,$(srcdir),;t t
s,@top_srcdir@,$(srcdir),;t t
s,@try_header@,try_compile,;t t
s,@ruby_pc@,$(ruby_pc),;t t
s,@RJIT_SUPPORT@,$(RJIT_SUPPORT),;t t
s,@PKG_CONFIG@,$(PKG_CONFIG),;t t
<<KEEP