[Feature #21116] Extract RJIT as a third-party gem
This commit is contained in:
parent
d35cc0cc77
commit
4a67ef09cc
Notes:
git
2025-02-13 09:51:08 +00:00
@ -19,7 +19,6 @@ hash.rb
|
||||
io.rb
|
||||
kernel.rb
|
||||
marshal.rb
|
||||
rjit.rb
|
||||
numeric.rb
|
||||
nilclass.rb
|
||||
pack.rb
|
||||
|
6
.github/workflows/compilers.yml
vendored
6
.github/workflows/compilers.yml
vendored
@ -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:
|
||||
|
86
.github/workflows/rjit-bindgen.yml
vendored
86
.github/workflows/rjit-bindgen.yml
vendored
@ -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
|
131
.github/workflows/rjit.yml
vendored
131
.github/workflows/rjit.yml
vendored
@ -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
6
.gitignore
vendored
@ -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
|
||||
|
@ -1,5 +1,4 @@
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
|
||||
[](https://ci.appveyor.com/project/ruby/ruby/branch/master)
|
||||
|
@ -1,5 +1,4 @@
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"MinGW")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"RJIT")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"Ubuntu")
|
||||
[](https://github.com/ruby/ruby/actions?query=workflow%3A"Windows")
|
||||
[](https://app.travis-ci.com/ruby/ruby)
|
||||
|
@ -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
|
||||
|
@ -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 = []
|
||||
|
@ -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)}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
537
common.mk
@ -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
|
||||
|
51
configure.ac
51
configure.ac
@ -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
1
cont.c
@ -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"
|
||||
|
@ -8,6 +8,5 @@ optparse
|
||||
date
|
||||
rdoc
|
||||
regexp
|
||||
rjit
|
||||
yjit
|
||||
ruby
|
||||
|
@ -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
1
eval.c
@ -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
1
gc.c
@ -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"
|
||||
|
2
inits.c
2
inits.c
@ -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
|
||||
|
@ -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
8
iseq.c
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
stats.rb
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
6
ractor.c
6
ractor.c
@ -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
501
rjit.c
@ -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
101
rjit.h
@ -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
52
rjit.rb
@ -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
548
rjit_c.c
@ -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
165
rjit_c.h
@ -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 */
|
65
ruby.c
65
ruby.c
@ -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);
|
||||
|
@ -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@
|
||||
|
@ -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/,
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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{}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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/) {
|
||||
|
@ -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;"}")
|
||||
|
@ -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
|
||||
|
@ -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)?
|
||||
|
@ -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__
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
8
thread.c
8
thread.c
@ -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);
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include "internal/gc.h"
|
||||
#include "internal/sanitizers.h"
|
||||
#include "rjit.h"
|
||||
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#include <sys/resource.h>
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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;
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
16
vm.c
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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++;
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user