2021-06-23 19:38:37 -04:00
|
|
|
# frozen_string_literal: true
|
2022-05-03 13:44:43 -04:00
|
|
|
#
|
|
|
|
# This set of tests can be run with:
|
|
|
|
# make test-all TESTS='test/ruby/test_yjit.rb' RUN_OPTS="--yjit-call-threshold=1"
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
require 'test/unit'
|
|
|
|
require 'envutil'
|
|
|
|
require 'tmpdir'
|
2021-12-28 01:22:09 -05:00
|
|
|
require_relative '../lib/jit_support'
|
2021-06-23 19:38:37 -04:00
|
|
|
|
2021-10-27 16:10:25 -04:00
|
|
|
return unless defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
|
2021-06-23 19:38:37 -04:00
|
|
|
|
|
|
|
# Tests for YJIT with assertions on compilation and side exits
|
2022-05-21 00:31:00 -04:00
|
|
|
# insipired by the MJIT tests in test/ruby/test_mjit.rb
|
2021-06-23 19:38:37 -04:00
|
|
|
class TestYJIT < Test::Unit::TestCase
|
2021-10-07 18:05:10 -04:00
|
|
|
def test_yjit_in_ruby_description
|
|
|
|
assert_includes(RUBY_DESCRIPTION, '+YJIT')
|
|
|
|
end
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
# Check that YJIT is in the version string
|
2021-10-07 18:05:10 -04:00
|
|
|
def test_yjit_in_version
|
|
|
|
[
|
|
|
|
%w(--version --yjit),
|
|
|
|
%w(--version --disable-yjit --yjit),
|
|
|
|
%w(--version --disable-yjit --enable-yjit),
|
|
|
|
%w(--version --disable-yjit --enable=yjit),
|
|
|
|
%w(--version --disable=yjit --yjit),
|
|
|
|
%w(--version --disable=yjit --enable-yjit),
|
|
|
|
%w(--version --disable=yjit --enable=yjit),
|
2021-12-13 19:08:01 -05:00
|
|
|
*([
|
|
|
|
%w(--version --jit),
|
|
|
|
%w(--version --disable-jit --jit),
|
|
|
|
%w(--version --disable-jit --enable-jit),
|
|
|
|
%w(--version --disable-jit --enable=jit),
|
|
|
|
%w(--version --disable=jit --yjit),
|
|
|
|
%w(--version --disable=jit --enable-jit),
|
|
|
|
%w(--version --disable=jit --enable=jit),
|
2021-12-28 01:22:09 -05:00
|
|
|
] if JITSupport.yjit_supported?),
|
2021-10-07 18:05:10 -04:00
|
|
|
].each do |version_args|
|
|
|
|
assert_in_out_err(version_args) do |stdout, stderr|
|
2021-10-25 20:28:21 -04:00
|
|
|
assert_equal(RUBY_DESCRIPTION, stdout.first)
|
2021-10-07 18:05:10 -04:00
|
|
|
assert_equal([], stderr)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-15 16:51:57 -04:00
|
|
|
def test_command_line_switches
|
2021-10-25 20:22:54 -04:00
|
|
|
assert_in_out_err('--yjit-', '', [], /invalid option --yjit-/)
|
|
|
|
assert_in_out_err('--yjithello', '', [], /invalid option --yjithello/)
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
#assert_in_out_err('--yjit-call-threshold', '', [], /--yjit-call-threshold needs an argument/)
|
|
|
|
#assert_in_out_err('--yjit-call-threshold=', '', [], /--yjit-call-threshold needs an argument/)
|
2021-10-15 16:51:57 -04:00
|
|
|
end
|
|
|
|
|
2021-10-26 15:15:46 -04:00
|
|
|
def test_yjit_stats_and_v_no_error
|
|
|
|
_stdout, stderr, _status = EnvUtil.invoke_ruby(%w(-v --yjit-stats), '', true, true)
|
|
|
|
refute_includes(stderr, "NoMethodError")
|
|
|
|
end
|
|
|
|
|
2021-10-07 18:05:10 -04:00
|
|
|
def test_enable_from_env_var
|
|
|
|
yjit_child_env = {'RUBY_YJIT_ENABLE' => '1'}
|
2021-10-25 20:28:21 -04:00
|
|
|
assert_in_out_err([yjit_child_env, '--version'], '') do |stdout, stderr|
|
|
|
|
assert_equal(RUBY_DESCRIPTION, stdout.first)
|
|
|
|
assert_equal([], stderr)
|
|
|
|
end
|
2021-10-07 18:05:10 -04:00
|
|
|
assert_in_out_err([yjit_child_env, '-e puts RUBY_DESCRIPTION'], '', [RUBY_DESCRIPTION])
|
2021-10-27 16:10:25 -04:00
|
|
|
assert_in_out_err([yjit_child_env, '-e p RubyVM::YJIT.enabled?'], '', ['true'])
|
2021-10-07 18:05:10 -04:00
|
|
|
end
|
|
|
|
|
2021-11-23 14:09:24 -05:00
|
|
|
def test_compile_setclassvariable
|
|
|
|
script = 'class Foo; def self.foo; @@foo = 1; end; end; Foo.foo'
|
|
|
|
assert_compiles(script, insns: %i[setclassvariable], result: 1)
|
|
|
|
end
|
|
|
|
|
2021-09-29 16:06:40 -04:00
|
|
|
def test_compile_getclassvariable
|
|
|
|
script = 'class Foo; @@foo = 1; def self.foo; @@foo; end; end; Foo.foo'
|
|
|
|
assert_compiles(script, insns: %i[getclassvariable], result: 1)
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_compile_putnil
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('nil', insns: %i[putnil], result: nil)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_putobject
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('true', insns: %i[putobject], result: true)
|
|
|
|
assert_compiles('123', insns: %i[putobject], result: 123)
|
|
|
|
assert_compiles(':foo', insns: %i[putobject], result: :foo)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
2022-05-19 11:52:52 -04:00
|
|
|
def test_compile_opt_succ
|
|
|
|
assert_compiles('1.succ', insns: %i[opt_succ], result: 2)
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_compile_opt_not
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('!false', insns: %i[opt_not], result: true)
|
|
|
|
assert_compiles('!nil', insns: %i[opt_not], result: true)
|
|
|
|
assert_compiles('!true', insns: %i[opt_not], result: false)
|
|
|
|
assert_compiles('![]', insns: %i[opt_not], result: false)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_newarray
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('[]', insns: %i[newarray], result: [])
|
|
|
|
assert_compiles('[1+1]', insns: %i[newarray opt_plus], result: [2])
|
|
|
|
assert_compiles('[1,1+1,3,4,5,6]', insns: %i[newarray opt_plus], result: [1, 2, 3, 4, 5, 6])
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_duparray
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('[1]', insns: %i[duparray], result: [1])
|
|
|
|
assert_compiles('[1, 2, 3]', insns: %i[duparray], result: [1, 2, 3])
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
2021-07-17 06:26:46 -04:00
|
|
|
def test_compile_newrange
|
|
|
|
assert_compiles('s = 1; (s..5)', insns: %i[newrange], result: 1..5)
|
|
|
|
assert_compiles('s = 1; e = 5; (s..e)', insns: %i[newrange], result: 1..5)
|
|
|
|
assert_compiles('s = 1; (s...5)', insns: %i[newrange], result: 1...5)
|
|
|
|
assert_compiles('s = 1; (s..)', insns: %i[newrange], result: 1..)
|
|
|
|
assert_compiles('e = 5; (..e)', insns: %i[newrange], result: ..5)
|
|
|
|
end
|
|
|
|
|
2021-10-27 10:55:43 -04:00
|
|
|
def test_compile_duphash
|
|
|
|
assert_compiles('{ two: 2 }', insns: %i[duphash], result: { two: 2 })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_newhash
|
|
|
|
assert_compiles('{}', insns: %i[newhash], result: {})
|
|
|
|
assert_compiles('{ two: 1 + 1 }', insns: %i[newhash], result: { two: 2 })
|
|
|
|
assert_compiles('{ 1 + 1 => :two }', insns: %i[newhash], result: { 2 => :two })
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_compile_opt_nil_p
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('nil.nil?', insns: %i[opt_nil_p], result: true)
|
|
|
|
assert_compiles('false.nil?', insns: %i[opt_nil_p], result: false)
|
|
|
|
assert_compiles('true.nil?', insns: %i[opt_nil_p], result: false)
|
|
|
|
assert_compiles('(-"").nil?', insns: %i[opt_nil_p], result: false)
|
|
|
|
assert_compiles('123.nil?', insns: %i[opt_nil_p], result: false)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_eq_fixnum
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('123 == 123', insns: %i[opt_eq], result: true)
|
|
|
|
assert_compiles('123 == 456', insns: %i[opt_eq], result: false)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_eq_string
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles('-"" == -""', insns: %i[opt_eq], result: true)
|
|
|
|
assert_compiles('-"foo" == -"foo"', insns: %i[opt_eq], result: true)
|
|
|
|
assert_compiles('-"foo" == -"bar"', insns: %i[opt_eq], result: false)
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
2021-09-03 20:07:16 -04:00
|
|
|
def test_compile_eq_symbol
|
|
|
|
assert_compiles(':foo == :foo', insns: %i[opt_eq], result: true)
|
|
|
|
assert_compiles(':foo == :bar', insns: %i[opt_eq], result: false)
|
|
|
|
assert_compiles(':foo == "foo".to_sym', insns: %i[opt_eq], result: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_eq_object
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_eq], result: false)
|
|
|
|
def eq(a, b)
|
|
|
|
a == b
|
|
|
|
end
|
|
|
|
|
|
|
|
eq(Object.new, Object.new)
|
|
|
|
RUBY
|
|
|
|
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_eq], result: true)
|
|
|
|
def eq(a, b)
|
|
|
|
a == b
|
|
|
|
end
|
|
|
|
|
|
|
|
obj = Object.new
|
|
|
|
eq(obj, obj)
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_eq_arbitrary_class
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_eq], result: "yes")
|
|
|
|
def eq(a, b)
|
|
|
|
a == b
|
|
|
|
end
|
|
|
|
|
|
|
|
class Foo
|
|
|
|
def ==(other)
|
|
|
|
"yes"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
eq(Foo.new, Foo.new)
|
|
|
|
eq(Foo.new, Foo.new)
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-09-14 12:07:35 -04:00
|
|
|
def test_compile_opt_lt
|
|
|
|
assert_compiles('1 < 2', insns: %i[opt_lt])
|
|
|
|
assert_compiles('"a" < "b"', insns: %i[opt_lt])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_le
|
|
|
|
assert_compiles('1 <= 2', insns: %i[opt_le])
|
|
|
|
assert_compiles('"a" <= "b"', insns: %i[opt_le])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_gt
|
|
|
|
assert_compiles('1 > 2', insns: %i[opt_gt])
|
|
|
|
assert_compiles('"a" > "b"', insns: %i[opt_gt])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_ge
|
|
|
|
assert_compiles('1 >= 2', insns: %i[opt_ge])
|
|
|
|
assert_compiles('"a" >= "b"', insns: %i[opt_ge])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_plus
|
|
|
|
assert_compiles('1 + 2', insns: %i[opt_plus])
|
|
|
|
assert_compiles('"a" + "b"', insns: %i[opt_plus])
|
|
|
|
assert_compiles('[:foo] + [:bar]', insns: %i[opt_plus])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_minus
|
|
|
|
assert_compiles('1 - 2', insns: %i[opt_minus])
|
|
|
|
assert_compiles('[:foo, :bar] - [:bar]', insns: %i[opt_minus])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_or
|
|
|
|
assert_compiles('1 | 2', insns: %i[opt_or])
|
|
|
|
assert_compiles('[:foo] | [:bar]', insns: %i[opt_or])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_compile_opt_and
|
|
|
|
assert_compiles('1 & 2', insns: %i[opt_and])
|
|
|
|
assert_compiles('[:foo, :bar] & [:bar]', insns: %i[opt_and])
|
|
|
|
end
|
|
|
|
|
2021-07-27 14:57:30 -04:00
|
|
|
def test_compile_set_and_get_global
|
|
|
|
assert_compiles('$foo = 123; $foo', insns: %i[setglobal], result: 123)
|
|
|
|
end
|
|
|
|
|
2021-06-19 17:03:06 -04:00
|
|
|
def test_compile_putspecialobject
|
|
|
|
assert_compiles('-> {}', insns: %i[putspecialobject])
|
|
|
|
end
|
|
|
|
|
2021-08-04 13:23:34 -04:00
|
|
|
def test_compile_tostring
|
|
|
|
assert_no_exits('"i am a string #{true}"')
|
2021-11-19 16:57:09 -05:00
|
|
|
end
|
2021-08-04 13:23:34 -04:00
|
|
|
|
2021-09-13 17:31:12 -04:00
|
|
|
def test_compile_opt_aset
|
|
|
|
assert_compiles('[1,2,3][2] = 4', insns: %i[opt_aset])
|
|
|
|
assert_compiles('{}[:foo] = :bar', insns: %i[opt_aset])
|
|
|
|
assert_compiles('[1,2,3][0..-1] = []', insns: %i[opt_aset])
|
|
|
|
assert_compiles('"foo"[3] = "d"', insns: %i[opt_aset])
|
|
|
|
end
|
|
|
|
|
2021-09-03 19:06:32 -04:00
|
|
|
def test_compile_attr_set
|
|
|
|
assert_no_exits(<<~EORB)
|
|
|
|
class Foo
|
|
|
|
attr_accessor :bar
|
|
|
|
end
|
|
|
|
|
|
|
|
foo = Foo.new
|
|
|
|
foo.bar = 3
|
|
|
|
foo.bar = 3
|
|
|
|
foo.bar = 3
|
|
|
|
foo.bar = 3
|
|
|
|
EORB
|
|
|
|
end
|
|
|
|
|
2021-08-10 09:10:34 -04:00
|
|
|
def test_compile_regexp
|
|
|
|
assert_no_exits('/#{true}/')
|
2021-11-19 16:57:09 -05:00
|
|
|
end
|
2021-08-10 09:10:34 -04:00
|
|
|
|
2021-12-16 19:52:44 -05:00
|
|
|
def test_compile_dynamic_symbol
|
|
|
|
assert_compiles(':"#{"foo"}"', insns: %i[intern])
|
|
|
|
assert_compiles('s = "bar"; :"foo#{s}"', insns: %i[intern])
|
|
|
|
end
|
|
|
|
|
2021-07-20 13:22:08 -04:00
|
|
|
def test_getlocal_with_level
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[getlocal opt_plus], result: [[7]])
|
|
|
|
def foo(foo, bar)
|
|
|
|
[1].map do |x|
|
|
|
|
[1].map do |y|
|
|
|
|
foo + bar
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
foo(5, 2)
|
|
|
|
RUBY
|
|
|
|
end
|
2021-06-26 02:50:19 -04:00
|
|
|
|
2021-09-17 11:38:33 -04:00
|
|
|
def test_setlocal_with_level
|
|
|
|
assert_no_exits(<<~RUBY)
|
|
|
|
def sum(arr)
|
|
|
|
sum = 0
|
|
|
|
arr.each do |x|
|
|
|
|
sum += x
|
|
|
|
end
|
|
|
|
sum
|
|
|
|
end
|
|
|
|
|
|
|
|
sum([1,2,3])
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_string_then_nil
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_nil_p], result: true)
|
2021-06-23 19:38:37 -04:00
|
|
|
def foo(val)
|
|
|
|
val.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
foo("foo")
|
|
|
|
foo(nil)
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_nil_then_string
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_nil_p], result: false)
|
2021-06-23 19:38:37 -04:00
|
|
|
def foo(val)
|
|
|
|
val.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
foo(nil)
|
|
|
|
foo("foo")
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-05-11 11:20:21 -04:00
|
|
|
def test_string_concat_utf8
|
|
|
|
assert_compiles(<<~RUBY, frozen_string_literal: true, result: true)
|
|
|
|
def str_cat_utf8
|
|
|
|
s = String.new
|
|
|
|
10.times { s << "✅" }
|
|
|
|
s
|
|
|
|
end
|
|
|
|
|
|
|
|
str_cat_utf8 == "✅" * 10
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_string_concat_ascii
|
|
|
|
# Constant-get for classes (e.g. String, Encoding) can cause a side-exit in getinlinecache. For now, ignore exits.
|
|
|
|
assert_compiles(<<~RUBY, exits: :any)
|
|
|
|
str_arg = "b".encode(Encoding::ASCII)
|
|
|
|
def str_cat_ascii(arg)
|
|
|
|
s = String.new(encoding: Encoding::ASCII)
|
|
|
|
10.times { s << arg }
|
|
|
|
s
|
|
|
|
end
|
|
|
|
|
|
|
|
str_cat_ascii(str_arg) == str_arg * 10
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_opt_length_in_method
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_length], result: 5)
|
2021-06-23 19:38:37 -04:00
|
|
|
def foo(str)
|
|
|
|
str.length
|
|
|
|
end
|
|
|
|
|
|
|
|
foo("hello, ")
|
|
|
|
foo("world")
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-06-18 20:32:13 -04:00
|
|
|
def test_opt_regexpmatch2
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_regexpmatch2], result: 0)
|
|
|
|
def foo(str)
|
|
|
|
str =~ /foo/
|
|
|
|
end
|
|
|
|
|
|
|
|
foo("foobar")
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-09-04 04:50:25 -04:00
|
|
|
def test_expandarray
|
|
|
|
assert_compiles(<<~'RUBY', insns: %i[expandarray], result: [1, 2])
|
|
|
|
a, b = [1, 2]
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_expandarray_nil
|
|
|
|
assert_compiles(<<~'RUBY', insns: %i[expandarray], result: [nil, nil])
|
|
|
|
a, b = nil
|
|
|
|
[a, b]
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-09-08 21:47:09 -04:00
|
|
|
def test_getspecial_backref
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $&", insns: %i[getspecial], result: "oo")
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $`", insns: %i[getspecial], result: "f")
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $'", insns: %i[getspecial], result: "")
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $+", insns: %i[getspecial], result: "o")
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $1", insns: %i[getspecial], result: "o")
|
|
|
|
assert_compiles("'foo' =~ /(o)./; $2", insns: %i[getspecial], result: nil)
|
|
|
|
end
|
|
|
|
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 13:35:48 -04:00
|
|
|
def test_compile_opt_getconstant_path
|
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_getconstant_path], result: 123, call_threshold: 2)
|
2021-06-23 19:38:37 -04:00
|
|
|
def get_foo
|
|
|
|
FOO
|
|
|
|
end
|
|
|
|
|
|
|
|
FOO = 123
|
|
|
|
|
|
|
|
get_foo # warm inline cache
|
|
|
|
get_foo
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 13:35:48 -04:00
|
|
|
def test_opt_getconstant_path_slowpath
|
|
|
|
assert_compiles(<<~RUBY, exits: { opt_getconstant_path: 1 }, result: [42, 42, 1, 1], call_threshold: 2)
|
2021-09-21 18:16:23 -04:00
|
|
|
class A
|
|
|
|
FOO = 42
|
|
|
|
class << self
|
|
|
|
def foo
|
|
|
|
_foo = nil
|
|
|
|
FOO
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
|
|
result << A.foo
|
|
|
|
result << A.foo
|
|
|
|
|
|
|
|
class << A
|
|
|
|
FOO = 1
|
|
|
|
end
|
|
|
|
|
|
|
|
result << A.foo
|
|
|
|
result << A.foo
|
|
|
|
|
|
|
|
result
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
def test_string_interpolation
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", call_threshold: 2)
|
2021-06-23 19:38:37 -04:00
|
|
|
def make_str(foo, bar)
|
|
|
|
"#{foo}#{bar}"
|
|
|
|
end
|
|
|
|
|
|
|
|
make_str("foo", "bar")
|
|
|
|
make_str("foo", "bar")
|
|
|
|
RUBY
|
2021-11-19 16:57:09 -05:00
|
|
|
end
|
2021-06-23 19:38:37 -04:00
|
|
|
|
2021-08-26 14:45:05 -04:00
|
|
|
def test_string_interpolation_cast
|
2021-11-19 16:57:09 -05:00
|
|
|
assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "123")
|
2021-08-26 14:45:05 -04:00
|
|
|
def make_str(foo, bar)
|
|
|
|
"#{foo}#{bar}"
|
|
|
|
end
|
|
|
|
|
|
|
|
make_str(1, 23)
|
|
|
|
RUBY
|
2021-11-19 16:57:09 -05:00
|
|
|
end
|
2021-08-26 14:45:05 -04:00
|
|
|
|
2021-11-05 16:54:23 -04:00
|
|
|
def test_checkkeyword
|
|
|
|
assert_compiles(<<~'RUBY', insns: %i[checkkeyword], result: [2, 5])
|
|
|
|
def foo(foo: 1+1)
|
|
|
|
foo
|
|
|
|
end
|
|
|
|
|
|
|
|
[foo, foo(foo: 5)]
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-11-25 14:56:58 -05:00
|
|
|
def test_struct_aref
|
|
|
|
assert_compiles(<<~RUBY)
|
|
|
|
def foo(obj)
|
|
|
|
obj.foo
|
|
|
|
obj.bar
|
|
|
|
end
|
|
|
|
|
|
|
|
Foo = Struct.new(:foo, :bar)
|
|
|
|
foo(Foo.new(123))
|
|
|
|
foo(Foo.new(123))
|
|
|
|
RUBY
|
|
|
|
end
|
2021-11-17 21:01:31 -05:00
|
|
|
|
2021-11-25 14:56:58 -05:00
|
|
|
def test_struct_aset
|
2021-07-09 02:33:55 -04:00
|
|
|
assert_compiles(<<~RUBY)
|
|
|
|
def foo(obj)
|
|
|
|
obj.foo = 123
|
|
|
|
obj.bar = 123
|
|
|
|
end
|
|
|
|
|
|
|
|
Foo = Struct.new(:foo, :bar)
|
|
|
|
foo(Foo.new(123))
|
|
|
|
foo(Foo.new(123))
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-05-03 18:25:03 -04:00
|
|
|
def test_getblockparam
|
|
|
|
assert_compiles(<<~'RUBY', insns: [:getblockparam])
|
|
|
|
def foo &blk
|
|
|
|
2.times do
|
|
|
|
blk
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
foo {}
|
|
|
|
foo {}
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_getblockparamproxy
|
|
|
|
# Currently two side exits as OPTIMIZED_METHOD_TYPE_CALL is unimplemented
|
|
|
|
assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: { opt_send_without_block: 2 })
|
|
|
|
def foo &blk
|
|
|
|
p blk.call
|
|
|
|
p blk.call
|
|
|
|
end
|
|
|
|
|
|
|
|
foo { 1 }
|
|
|
|
foo { 2 }
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-07-28 11:38:07 -04:00
|
|
|
def test_getblockparamproxy_with_no_block
|
|
|
|
# Currently side exits on the send
|
|
|
|
assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: { send: 2 })
|
|
|
|
def bar
|
|
|
|
end
|
|
|
|
|
|
|
|
def foo &blk
|
|
|
|
bar(&blk)
|
|
|
|
bar(&blk)
|
|
|
|
end
|
|
|
|
|
|
|
|
foo
|
|
|
|
foo
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-07-15 13:52:47 -04:00
|
|
|
def test_send_splat
|
|
|
|
assert_compiles(<<~'RUBY', result: "3#1,2,3/P", exits: {})
|
|
|
|
def internal_method(*args)
|
|
|
|
"#{args.size}##{args.join(",")}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method, *[1, 2, 3]) + "/P"
|
|
|
|
end
|
|
|
|
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_multiarg
|
|
|
|
assert_compiles(<<~'RUBY', result: "3#1,2,3/Q")
|
|
|
|
def internal_method(*args)
|
|
|
|
"#{args.size}##{args.join(",")}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method, 1, 2, 3) + "/Q"
|
|
|
|
end
|
|
|
|
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_kwargs
|
|
|
|
# For now, this side-exits when calls include keyword args
|
|
|
|
assert_compiles(<<~'RUBY', result: "2#a:1,b:2/A", exits: {opt_send_without_block: 1})
|
|
|
|
def internal_method(**kw)
|
|
|
|
"#{kw.size}##{kw.keys.map { |k| "#{k}:#{kw[k]}" }.join(",")}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method, a: 1, b: 2) + "/A"
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_kwargs_in_receiver_only
|
|
|
|
assert_compiles(<<~'RUBY', result: "0/RK", exits: {})
|
|
|
|
def internal_method(**kw)
|
|
|
|
"#{kw.size}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method) + "/RK"
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_with_underscores
|
|
|
|
assert_compiles(<<~'RUBY', result: "0/RK", exits: {})
|
|
|
|
def internal_method(**kw)
|
|
|
|
"#{kw.size}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
__send__(:internal_method) + "/RK"
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_kwargs_splat
|
|
|
|
# For now, this side-exits when calling with a splat
|
|
|
|
assert_compiles(<<~'RUBY', result: "2#a:1,b:2/B", exits: {opt_send_without_block: 1})
|
|
|
|
def internal_method(**kw)
|
|
|
|
"#{kw.size}##{kw.keys.map { |k| "#{k}:#{kw[k]}" }.join(",")}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method, **{ a: 1, b: 2 }) + "/B"
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_block
|
|
|
|
# Setlocal_wc_0 sometimes side-exits on write barrier
|
|
|
|
assert_compiles(<<~'RUBY', result: "b:n/b:y/b:y/b:n", exits: { :setlocal_WC_0 => 0..1 })
|
|
|
|
def internal_method(&b)
|
|
|
|
"b:#{block_given? ? "y" : "n"}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
b7 = proc { 7 }
|
|
|
|
[
|
|
|
|
send(:internal_method),
|
|
|
|
send(:internal_method, &b7),
|
|
|
|
send(:internal_method) { 7 },
|
|
|
|
send(:internal_method, &nil),
|
|
|
|
].join("/")
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_block_calling
|
|
|
|
assert_compiles(<<~'RUBY', result: "1a2", exits: {})
|
|
|
|
def internal_method
|
|
|
|
out = yield
|
|
|
|
"1" + out + "2"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
__send__(:internal_method) { "a" }
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_block_only_receiver
|
|
|
|
assert_compiles(<<~'RUBY', result: "b:n", exits: {})
|
|
|
|
def internal_method(&b)
|
|
|
|
"b:#{block_given? ? "y" : "n"}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:internal_method)
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_send_block_only_sender
|
|
|
|
assert_compiles(<<~'RUBY', result: "Y/Y/Y/Y", exits: {})
|
|
|
|
def internal_method
|
|
|
|
"Y"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
b7 = proc { 7 }
|
|
|
|
[
|
|
|
|
send(:internal_method),
|
|
|
|
send(:internal_method, &b7),
|
|
|
|
send(:internal_method) { 7 },
|
|
|
|
send(:internal_method, &nil),
|
|
|
|
].join("/")
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_multisend
|
|
|
|
assert_compiles(<<~'RUBY', result: "77")
|
|
|
|
def internal_method
|
|
|
|
"7"
|
|
|
|
end
|
|
|
|
|
|
|
|
def jit_method
|
|
|
|
send(:send, :internal_method) + send(:send, :send, :internal_method)
|
|
|
|
end
|
|
|
|
jit_method
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-05-03 13:44:43 -04:00
|
|
|
def test_getivar_opt_plus
|
|
|
|
assert_no_exits(<<~RUBY)
|
|
|
|
class TheClass
|
|
|
|
def initialize
|
|
|
|
@levar = 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_sum
|
|
|
|
sum = 0
|
|
|
|
# The type of levar is unknown,
|
|
|
|
# but this still should not exit
|
|
|
|
sum += @levar
|
|
|
|
sum
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
obj = TheClass.new
|
|
|
|
obj.get_sum
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-08-30 23:58:53 -04:00
|
|
|
def test_super_iseq
|
|
|
|
assert_compiles(<<~'RUBY', insns: %i[invokesuper opt_plus opt_mult], result: 15)
|
|
|
|
class A
|
|
|
|
def foo
|
|
|
|
1 + 2
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class B < A
|
|
|
|
def foo
|
|
|
|
super * 5
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
B.new.foo
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_super_cfunc
|
|
|
|
assert_compiles(<<~'RUBY', insns: %i[invokesuper], result: "Hello")
|
|
|
|
class Gnirts < String
|
|
|
|
def initialize
|
|
|
|
super(-"olleH")
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
super().reverse
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
Gnirts.new.to_s
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-09-04 04:35:22 -04:00
|
|
|
# Tests calling a variadic cfunc with many args
|
|
|
|
def test_build_large_struct
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
assert_compiles(<<~RUBY, insns: %i[opt_send_without_block], call_threshold: 2)
|
2021-09-04 04:35:22 -04:00
|
|
|
::Foo = Struct.new(:a, :b, :c, :d, :e, :f, :g, :h)
|
|
|
|
|
|
|
|
def build_foo
|
|
|
|
::Foo.new(:a, :b, :c, :d, :e, :f, :g, :h)
|
|
|
|
end
|
|
|
|
|
|
|
|
build_foo
|
|
|
|
build_foo
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-06-26 02:50:19 -04:00
|
|
|
def test_fib_recursion
|
2021-07-15 19:49:47 -04:00
|
|
|
assert_compiles(<<~'RUBY', insns: %i[opt_le opt_minus opt_plus opt_send_without_block], result: 34)
|
2021-06-25 17:47:23 -04:00
|
|
|
def fib(n)
|
|
|
|
return n if n <= 1
|
|
|
|
fib(n-1) + fib(n-2)
|
|
|
|
end
|
|
|
|
|
|
|
|
fib(9)
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-12-16 13:19:24 -05:00
|
|
|
def test_optarg_and_kwarg
|
|
|
|
assert_no_exits(<<~'RUBY')
|
|
|
|
def opt_and_kwarg(a, b=nil, c: nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
2.times do
|
|
|
|
opt_and_kwarg(1, 2, c: 3)
|
|
|
|
end
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2022-01-05 19:00:21 -05:00
|
|
|
def test_cfunc_kwarg
|
|
|
|
assert_no_exits('{}.store(:value, foo: 123)')
|
|
|
|
assert_no_exits('{}.store(:value, foo: 123, bar: 456, baz: 789)')
|
|
|
|
assert_no_exits('{}.merge(foo: 123)')
|
|
|
|
assert_no_exits('{}.merge(foo: 123, bar: 456, baz: 789)')
|
|
|
|
end
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
# regression test simplified from URI::Generic#hostname=
|
2021-07-28 23:05:54 -04:00
|
|
|
def test_ctx_different_mappings
|
|
|
|
assert_compiles(<<~'RUBY', frozen_string_literal: true)
|
|
|
|
def foo(v)
|
|
|
|
!(v&.start_with?('[')) && v&.index(':')
|
|
|
|
end
|
|
|
|
|
|
|
|
foo(nil)
|
|
|
|
foo("example.com")
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-09-20 14:55:10 -04:00
|
|
|
def test_no_excessive_opt_getinlinecache_invalidation
|
|
|
|
assert_compiles(<<~'RUBY', exits: :any, result: :ok)
|
|
|
|
objects = [Object.new, Object.new]
|
|
|
|
|
|
|
|
objects.each do |o|
|
|
|
|
class << o
|
|
|
|
def foo
|
|
|
|
Object
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
9000.times {
|
|
|
|
objects[0].foo
|
|
|
|
objects[1].foo
|
|
|
|
}
|
|
|
|
|
2021-10-27 16:10:25 -04:00
|
|
|
stats = RubyVM::YJIT.runtime_stats
|
2021-09-20 14:55:10 -04:00
|
|
|
return :ok unless stats[:all_stats]
|
|
|
|
return :ok if stats[:invalidation_count] < 10
|
|
|
|
|
|
|
|
:fail
|
|
|
|
RUBY
|
|
|
|
end
|
|
|
|
|
2021-07-15 19:38:26 -04:00
|
|
|
def assert_no_exits(script)
|
|
|
|
assert_compiles(script)
|
|
|
|
end
|
|
|
|
|
2021-07-15 19:49:47 -04:00
|
|
|
ANY = Object.new
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
def assert_compiles(test_script, insns: [], call_threshold: 1, stdout: nil, exits: {}, result: ANY, frozen_string_literal: nil)
|
2021-06-23 19:38:37 -04:00
|
|
|
reset_stats = <<~RUBY
|
2021-10-27 16:10:25 -04:00
|
|
|
RubyVM::YJIT.runtime_stats
|
|
|
|
RubyVM::YJIT.reset_stats!
|
2021-06-23 19:38:37 -04:00
|
|
|
RUBY
|
|
|
|
|
2021-07-15 19:49:47 -04:00
|
|
|
write_results = <<~RUBY
|
2021-10-27 16:10:25 -04:00
|
|
|
stats = RubyVM::YJIT.runtime_stats
|
2021-06-23 19:38:37 -04:00
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
def collect_insns(iseq)
|
|
|
|
insns = RubyVM::YJIT.insns_compiled(iseq)
|
|
|
|
iseq.each_child { |c| insns.concat collect_insns(c) }
|
|
|
|
insns
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
iseq = RubyVM::InstructionSequence.of(_test_proc)
|
|
|
|
IO.open(3).write Marshal.dump({
|
2021-06-19 17:03:06 -04:00
|
|
|
result: #{result == ANY ? "nil" : "result"},
|
2021-06-23 19:38:37 -04:00
|
|
|
stats: stats,
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
insns: collect_insns(iseq),
|
2021-06-23 19:38:37 -04:00
|
|
|
disasm: iseq.disasm
|
|
|
|
})
|
|
|
|
RUBY
|
|
|
|
|
|
|
|
script = <<~RUBY
|
2021-07-28 23:05:54 -04:00
|
|
|
#{"# frozen_string_literal: true" if frozen_string_literal}
|
2021-09-20 14:55:10 -04:00
|
|
|
_test_proc = -> {
|
2021-06-23 19:38:37 -04:00
|
|
|
#{test_script}
|
|
|
|
}
|
|
|
|
#{reset_stats}
|
2021-07-15 19:49:47 -04:00
|
|
|
result = _test_proc.call
|
|
|
|
#{write_results}
|
2021-06-23 19:38:37 -04:00
|
|
|
RUBY
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
status, out, err, stats = eval_with_jit(script, call_threshold: call_threshold)
|
2021-06-23 19:38:37 -04:00
|
|
|
|
|
|
|
assert status.success?, "exited with status #{status.to_i}, stderr:\n#{err}"
|
|
|
|
|
|
|
|
assert_equal stdout.chomp, out.chomp if stdout
|
|
|
|
|
2021-07-15 19:49:47 -04:00
|
|
|
unless ANY.equal?(result)
|
|
|
|
assert_equal result, stats[:result]
|
|
|
|
end
|
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
runtime_stats = stats[:stats]
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
insns_compiled = stats[:insns]
|
2021-06-23 19:38:37 -04:00
|
|
|
disasm = stats[:disasm]
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
# Check that exit counts are as expected
|
2022-05-11 11:20:21 -04:00
|
|
|
# Full stats are only available when --enable-yjit=dev
|
2021-07-20 12:34:04 -04:00
|
|
|
if runtime_stats[:all_stats]
|
|
|
|
recorded_exits = runtime_stats.select { |k, v| k.to_s.start_with?("exit_") }
|
2021-06-23 19:38:37 -04:00
|
|
|
recorded_exits = recorded_exits.reject { |k, v| v == 0 }
|
2021-07-20 12:34:04 -04:00
|
|
|
|
2021-06-23 19:38:37 -04:00
|
|
|
recorded_exits.transform_keys! { |k| k.to_s.gsub("exit_", "").to_sym }
|
2022-07-15 13:52:47 -04:00
|
|
|
# Exits can be specified as a hash of stat-name symbol to integer for exact exits.
|
|
|
|
# or stat-name symbol to range if the number of side exits might vary (e.g. write
|
|
|
|
# barriers, cache misses.)
|
|
|
|
if exits != :any &&
|
|
|
|
exits != recorded_exits &&
|
|
|
|
!exits.all? { |k, v| v === recorded_exits[k] } # triple-equal checks range membership or integer equality
|
2021-06-23 19:38:37 -04:00
|
|
|
flunk "Expected #{exits.empty? ? "no" : exits.inspect} exits" \
|
|
|
|
", but got\n#{recorded_exits.inspect}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-11 11:20:21 -04:00
|
|
|
# Only available when --enable-yjit=dev
|
2021-07-20 12:34:04 -04:00
|
|
|
if runtime_stats[:all_stats]
|
2021-06-23 19:38:37 -04:00
|
|
|
missed_insns = insns.dup
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
insns_compiled.each do |op|
|
|
|
|
if missed_insns.include?(op)
|
2021-06-23 19:38:37 -04:00
|
|
|
# This instruction was compiled
|
|
|
|
missed_insns.delete(op)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
unless missed_insns.empty?
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
flunk "Expected to compile instructions #{missed_insns.join(", ")} but didn't.\niseq:\n#{disasm}"
|
2021-06-23 19:38:37 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-05-11 11:20:21 -04:00
|
|
|
def script_shell_encode(s)
|
|
|
|
# We can't pass utf-8-encoded characters directly in a shell arg. But we can use Ruby \u constants.
|
|
|
|
s.chars.map { |c| c.ascii_only? ? c : "\\u%x" % c.codepoints[0] }.join
|
|
|
|
end
|
|
|
|
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
def eval_with_jit(script, call_threshold: 1, timeout: 1000)
|
2021-06-23 19:38:37 -04:00
|
|
|
args = [
|
|
|
|
"--disable-gems",
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 14:40:21 -04:00
|
|
|
"--yjit-call-threshold=#{call_threshold}",
|
2021-06-23 19:38:37 -04:00
|
|
|
"--yjit-stats"
|
|
|
|
]
|
2022-05-11 11:20:21 -04:00
|
|
|
args << "-e" << script_shell_encode(script)
|
2021-06-23 19:38:37 -04:00
|
|
|
stats_r, stats_w = IO.pipe
|
|
|
|
out, err, status = EnvUtil.invoke_ruby(args,
|
|
|
|
'', true, true, timeout: timeout, ios: {3 => stats_w}
|
|
|
|
)
|
|
|
|
stats_w.close
|
|
|
|
stats = stats_r.read
|
|
|
|
stats = Marshal.load(stats) if !stats.empty?
|
|
|
|
stats_r.close
|
|
|
|
[status, out, err, stats]
|
|
|
|
end
|
|
|
|
end
|