diff --git a/.github/workflows/ubuntu-ibm.yml b/.github/workflows/ubuntu-ibm.yml
index c58934007020a6..566bca8d3b6b1a 100644
--- a/.github/workflows/ubuntu-ibm.yml
+++ b/.github/workflows/ubuntu-ibm.yml
@@ -86,7 +86,7 @@ jobs:
# https://github.com/IBM/actionspz/issues/30
- name: Set HOME env
run: |
- echo "HOME: #{HOME}"
+ echo "HOME: ${HOME}"
echo "HOME=$(ls -d ~)" >> $GITHUB_ENV
working-directory:
if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }}
@@ -134,15 +134,12 @@ jobs:
continue-on-error: true
timeout-minutes: 3
- # A temporary workaround: Skip user ground id test
- # There is a mismatch between the group IDs of "id -g" and C function
- # getpwuid(uid_t uid) pw_gid.
+ # A temporary workaround: Set the user's primary group to avoid a mismatch
+ # between the group IDs of "id -g" and C function getpwuid(uid_t uid)
+ # pw_gid.
# https://github.com/IBM/actionspz/issues/31
- - name: Skip user group id test
- run: |
- sed -i.orig '/^ it "returns user group id" do/a\ skip' \
- ../src/spec/ruby/library/etc/struct_passwd_spec.rb
- diff -u ../src/spec/ruby/library/etc/struct_passwd_spec.rb{.orig,} || :
+ - name: Set user's group id
+ run: sudo usermod -g "$(id -g)" runner
if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }}
- name: make ${{ matrix.test_task }}
diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml
index b2b6f5e285561b..bb244dd1b28bcd 100644
--- a/.github/workflows/zjit-macos.yml
+++ b/.github/workflows/zjit-macos.yml
@@ -32,24 +32,22 @@ jobs:
fail-fast: false
matrix:
include:
- - test_task: 'zjit-check'
+ - test_task: 'check'
+ configure: '--enable-zjit=dev'
+ run_opts: '--zjit-call-threshold=1'
+ specopts: '-T --zjit-call-threshold=1'
+
+ - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb
configure: '--enable-yjit=dev --enable-zjit'
rust_version: "1.85.0"
- - test_task: 'ruby' # build test for combo build
+ - test_task: 'ruby'
+ hint: 'combo build test'
configure: '--enable-yjit --enable-zjit'
- - test_task: 'zjit-test-all'
- configure: '--enable-zjit=dev'
- testopts: '--seed=11831'
-
- - test_task: 'test'
- configure: '--enable-zjit=dev'
- zjit_opts: '--zjit-call-threshold=1'
-
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
- RUN_OPTS: ${{ matrix.zjit_opts }}
+ RUN_OPTS: ${{ matrix.run_opts }}
SPECOPTS: ${{ matrix.specopts }}
TESTOPTS: ${{ matrix.testopts }}
@@ -113,6 +111,7 @@ jobs:
RUN_OPTS="$RUN_OPTS"
SPECOPTS="$SPECOPTS"
TESTOPTS="$TESTOPTS"
+ TEST_EXCLUDES="$TEST_EXCLUDES"
timeout-minutes: 60
env:
RUBY_TESTOPTS: '-q --tty=no'
@@ -120,6 +119,7 @@ jobs:
SYNTAX_SUGGEST_TIMEOUT: '5'
PRECHECK_BUNDLED_GEMS: 'no'
TESTS: ${{ matrix.tests }}
+ TEST_EXCLUDES: '--excludes-dir=../src/test/.excludes-zjit --name=!/memory_leak/'
continue-on-error: ${{ matrix.continue-on-test_task || false }}
result:
diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml
index 46f4f08b43bf0f..3861e2481fc8d9 100644
--- a/.github/workflows/zjit-ubuntu.yml
+++ b/.github/workflows/zjit-ubuntu.yml
@@ -51,26 +51,23 @@ jobs:
fail-fast: false
matrix:
include:
- - test_task: 'zjit-bindgen'
- hint: 'To fix: use patch in logs'
- configure: '--enable-zjit=dev --with-gcc=clang-14'
- libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
+ - test_task: 'check'
+ configure: '--enable-zjit=dev'
+ run_opts: '--zjit-call-threshold=1'
+ specopts: '-T --zjit-call-threshold=1'
- - test_task: 'zjit-check'
+ - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb
configure: '--enable-yjit --enable-zjit=dev'
rust_version: '1.85.0'
- - test_task: 'zjit-test-all'
- configure: '--enable-zjit=dev'
- testopts: '--seed=18140'
-
- - test_task: 'test'
- configure: '--enable-zjit=dev'
- zjit_opts: '--zjit-call-threshold=1'
+ - test_task: 'zjit-bindgen'
+ hint: 'To fix: use patch in logs'
+ configure: '--enable-zjit=dev --with-gcc=clang-14'
+ libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1'
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}
- RUN_OPTS: ${{ matrix.zjit_opts }}
+ RUN_OPTS: ${{ matrix.run_opts }}
YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }}
SPECOPTS: ${{ matrix.specopts }}
TESTOPTS: ${{ matrix.testopts }}
@@ -152,12 +149,13 @@ jobs:
run: >-
make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"}
RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS"
- TESTOPTS="$TESTOPTS"
+ TESTOPTS="$TESTOPTS" TEST_EXCLUDES="$TEST_EXCLUDES"
ZJIT_BINDGEN_DIFF_OPTS="$ZJIT_BINDGEN_DIFF_OPTS"
timeout-minutes: 90
env:
RUBY_TESTOPTS: '-q --tty=no'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
+ TEST_EXCLUDES: '--excludes-dir=../src/test/.excludes-zjit --name=!/memory_leak/'
PRECHECK_BUNDLED_GEMS: 'no'
SYNTAX_SUGGEST_TIMEOUT: '5'
ZJIT_BINDGEN_DIFF_OPTS: '--exit-code'
diff --git a/doc/_regexp.rdoc b/doc/_regexp.rdoc
index a544aab089197e..b862c9a49e0594 100644
--- a/doc/_regexp.rdoc
+++ b/doc/_regexp.rdoc
@@ -1272,13 +1272,13 @@ because the optimization uses memoization (which may invoke large memory consump
== References
-Read (online PDF books):
+Read:
-- {Mastering Regular Expressions}[https://ia902508.us.archive.org/10/items/allitebooks-02/Mastering%20Regular%20Expressions%2C%203rd%20Edition.pdf]
+- Mastering Regular Expressions
by Jeffrey E.F. Friedl.
-- {Regular Expressions Cookbook}[https://doc.lagout.org/programmation/Regular%20Expressions/Regular%20Expressions%20Cookbook_%20Detailed%20Solutions%20in%20Eight%20Programming%20Languages%20%282nd%20ed.%29%20%5BGoyvaerts%20%26%20Levithan%202012-09-06%5D.pdf]
+- Regular Expressions Cookbook
by Jan Goyvaerts & Steven Levithan.
-Explore, test (interactive online editor):
+Explore, test:
-- {Rubular}[https://rubular.com/].
+- {Rubular}[https://rubular.com/]: interactive online editor.
diff --git a/math.c b/math.c
index 9e96f3666a3bdd..852620da20be6c 100644
--- a/math.c
+++ b/math.c
@@ -1054,7 +1054,7 @@ math_gamma(VALUE unused_obj, VALUE x)
*
* [Math.log(Math.gamma(x).abs), Math.gamma(x) < 0 ? -1 : 1]
*
- * See {logarithmic gamma function}[https://en.wikipedia.org/wiki/Gamma_function#The_log-gamma_function].
+ * See {log gamma function}[https://en.wikipedia.org/wiki/Gamma_function#Log-gamma_function].
*
* - Domain: (-INFINITY, INFINITY].
* - Range of first element: (-INFINITY, INFINITY].
diff --git a/ruby.c b/ruby.c
index 5a78a7c7649f90..f64412a9cf0fd2 100644
--- a/ruby.c
+++ b/ruby.c
@@ -119,7 +119,11 @@ enum feature_flag_bits {
EACH_FEATURES(DEFINE_FEATURE, COMMA),
DEFINE_FEATURE(frozen_string_literal_set),
feature_debug_flag_first,
+#if !USE_YJIT && USE_ZJIT
+ DEFINE_FEATURE(jit) = feature_zjit,
+#else
DEFINE_FEATURE(jit) = feature_yjit,
+#endif
feature_jit_mask = FEATURE_BIT(yjit) | FEATURE_BIT(zjit),
feature_debug_flag_begin = feature_debug_flag_first - 1,
diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb
index d402ab13825315..1350675a48d3ed 100644
--- a/test/-ext-/bug_reporter/test_bug_reporter.rb
+++ b/test/-ext-/bug_reporter/test_bug_reporter.rb
@@ -20,7 +20,9 @@ def test_bug_reporter_add
no_core = "Process.setrlimit(Process::RLIMIT_CORE, 0); " if defined?(Process.setrlimit) && defined?(Process::RLIMIT_CORE)
args = ["-r-test-/bug_reporter", "-C", tmpdir]
- args.push("--yjit") if JITSupport.yjit_enabled? # We want the printed description to match this process's RUBY_DESCRIPTION
+ # We want the printed description to match this process's RUBY_DESCRIPTION
+ args.push("--yjit") if JITSupport.yjit_enabled?
+ args.push("--zjit") if JITSupport.zjit_enabled?
args.unshift({"RUBY_ON_BUG" => nil})
stdin = "#{no_core}register_sample_bug_reporter(12345); Process.kill :SEGV, $$"
assert_in_out_err(args, stdin, [], expected_stderr, encoding: "ASCII-8BIT")
diff --git a/test/.excludes-zjit/TestBugReporter.rb b/test/.excludes-zjit/TestBugReporter.rb
deleted file mode 100644
index 57d3166d87aa54..00000000000000
--- a/test/.excludes-zjit/TestBugReporter.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_bug_reporter_add, 'Test fails with ZJIT')
diff --git a/test/.excludes-zjit/TestGc.rb b/test/.excludes-zjit/TestGc.rb
deleted file mode 100644
index 7591da79bc37b1..00000000000000
--- a/test/.excludes-zjit/TestGc.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_interrupt_in_finalizer, "Interrupt checks are still being developed")
diff --git a/test/.excludes-zjit/TestProc.rb b/test/.excludes-zjit/TestProc.rb
deleted file mode 100644
index 2961b686b6e3e7..00000000000000
--- a/test/.excludes-zjit/TestProc.rb
+++ /dev/null
@@ -1 +0,0 @@
-exclude(:test_binding_receiver, 'Test fails with ZJIT')
diff --git a/test/.excludes-zjit/TestRubyOptions.rb b/test/.excludes-zjit/TestRubyOptions.rb
deleted file mode 100644
index 966d0b75511936..00000000000000
--- a/test/.excludes-zjit/TestRubyOptions.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-exclude(:test_verbose, 'Test crashes with ZJIT')
-exclude(:test_segv_setproctitle, 'Test crashes with ZJIT')
-exclude(:test_crash_report, 'Test crashes with ZJIT')
-exclude(:test_segv_loaded_features, 'Test crashes with ZJIT')
-exclude(:test_crash_report_executable_path, 'Test crashes with ZJIT')
-exclude(:test_segv_test, 'Test crashes with ZJIT')
-exclude(:test_crash_report_script, 'Test crashes with ZJIT')
-exclude(:test_crash_report_script_path, 'Test crashes with ZJIT')
-
-exclude(:test_version, 'Test fails with ZJIT')
diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb
index 79fdbcce48f410..386a5a6f1efd53 100644
--- a/test/lib/jit_support.rb
+++ b/test/lib/jit_support.rb
@@ -10,7 +10,7 @@ def yjit_supported?
end
def yjit_enabled?
- defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+ defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
end
def yjit_force_enabled?
@@ -22,4 +22,8 @@ def zjit_supported?
# nil in mswin
@zjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['ZJIT_SUPPORT'])
end
+
+ def zjit_enabled?
+ defined?(RubyVM::ZJIT) && RubyVM::ZJIT.enabled?
+ end
end
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index b6c76ac73a662e..2c9f8534b19aad 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -8,8 +8,6 @@
require_relative '../lib/parser_support'
class TestRubyOptions < Test::Unit::TestCase
- def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
-
# Here we're defining our own RUBY_DESCRIPTION without "+PRISM". We do this
# here so that the various tests that reference RUBY_DESCRIPTION don't have to
# worry about it. The flag itself is tested in its own test.
@@ -22,8 +20,10 @@ def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
NO_JIT_DESCRIPTION =
case
- when yjit_enabled?
- RUBY_DESCRIPTION.sub(/\+YJIT( (dev|dev_nodebug|stats))? /, '')
+ when JITSupport.yjit_enabled?
+ RUBY_DESCRIPTION.sub(/\+YJIT( \w+)? /, '')
+ when JITSupport.zjit_enabled?
+ RUBY_DESCRIPTION.sub(/\+ZJIT( \w+)? /, '')
else
RUBY_DESCRIPTION
end
@@ -181,7 +181,7 @@ def test_debug
def test_verbose
assert_in_out_err([{'RUBY_YJIT_ENABLE' => nil}, "-vve", ""]) do |r, e|
assert_match(VERSION_PATTERN, r[0])
- if self.class.yjit_enabled? && !JITSupport.yjit_force_enabled?
+ if (JITSupport.yjit_enabled? && !JITSupport.yjit_force_enabled?) || JITSupport.zjit_enabled?
assert_equal(NO_JIT_DESCRIPTION, r[0])
else
assert_equal(RUBY_DESCRIPTION, r[0])
@@ -247,7 +247,7 @@ def test_version
assert_match(VERSION_PATTERN, r[0])
if ENV['RUBY_YJIT_ENABLE'] == '1'
assert_equal(NO_JIT_DESCRIPTION, r[0])
- elsif self.class.yjit_enabled? # checking -DYJIT_FORCE_ENABLE
+ elsif JITSupport.yjit_enabled? || JITSupport.zjit_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])
@@ -852,7 +852,8 @@ def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &b
# We want YJIT to be enabled in the subprocess if it's enabled for us
# so that the Ruby description matches.
env = Hash === args.first ? args.shift : {}
- args.unshift("--yjit") if self.class.yjit_enabled?
+ args.unshift("--yjit") if JITSupport.yjit_enabled?
+ args.unshift("--zjit") if JITSupport.zjit_enabled?
env.update({'RUBY_ON_BUG' => nil})
# ASAN registers a segv handler which prints out "AddressSanitizer: DEADLYSIGNAL" when
# catching sigsegv; we don't expect that output, so suppress it.
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb
index 2f197084664f03..b751d482e2b41d 100644
--- a/test/ruby/test_zjit.rb
+++ b/test/ruby/test_zjit.rb
@@ -196,6 +196,32 @@ def test
}, insns: [:setglobal]
end
+ def test_toplevel_binding
+ # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`.
+ out, err, status = eval_with_jit(%q{
+ a = 1
+ b = 2
+ TOPLEVEL_BINDING.local_variable_set(:b, 3)
+ c = 4
+ print [a, b, c]
+ })
+ assert_success(out, err, status)
+ assert_equal "[1, 3, 4]", out
+ end
+
+ def test_toplevel_local_after_eval
+ # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`.
+ out, err, status = eval_with_jit(%q{
+ a = 1
+ b = 2
+ eval('b = 3')
+ c = 4
+ print [a, b, c]
+ })
+ assert_success(out, err, status)
+ assert_equal "[1, 3, 4]", out
+ end
+
def test_getlocal_after_eval
assert_compiles '2', %q{
def test
@@ -2428,12 +2454,8 @@ def assert_runs(expected, test_script, insns: [], assert_compiles: false, **opts
IO.open(#{pipe_fd}).write(Marshal.dump(result))
RUBY
- status, out, err, result = eval_with_jit(script, pipe_fd:, **opts)
-
- message = "exited with status #{status.to_i}"
- message << "\nstdout:\n```\n#{out}```\n" unless out.empty?
- message << "\nstderr:\n```\n#{err}```\n" unless err.empty?
- assert status.success?, message
+ out, err, status, result = eval_with_jit(script, pipe_fd:, **opts)
+ assert_success(out, err, status)
result = Marshal.load(result)
assert_equal(expected, result.fetch(:ret_val).inspect)
@@ -2462,7 +2484,7 @@ def eval_with_jit(
debug: true,
allowed_iseqs: nil,
timeout: 1000,
- pipe_fd:
+ pipe_fd: nil
)
args = ["--disable-gems"]
if zjit
@@ -2478,18 +2500,25 @@ def eval_with_jit(
end
end
args << "-e" << script_shell_encode(script)
- pipe_r, pipe_w = IO.pipe
- # Separate thread so we don't deadlock when
- # the child ruby blocks writing the output to pipe_fd
- pipe_out = nil
- pipe_reader = Thread.new do
- pipe_out = pipe_r.read
- pipe_r.close
+ ios = {}
+ if pipe_fd
+ pipe_r, pipe_w = IO.pipe
+ # Separate thread so we don't deadlock when
+ # the child ruby blocks writing the output to pipe_fd
+ pipe_out = nil
+ pipe_reader = Thread.new do
+ pipe_out = pipe_r.read
+ pipe_r.close
+ end
+ ios[pipe_fd] = pipe_w
end
- out, err, status = EnvUtil.invoke_ruby(args, '', true, true, rubybin: RbConfig.ruby, timeout: timeout, ios: { pipe_fd => pipe_w })
- pipe_w.close
- pipe_reader.join(timeout)
- [status, out, err, pipe_out]
+ result = EnvUtil.invoke_ruby(args, '', true, true, rubybin: RbConfig.ruby, timeout: timeout, ios:)
+ if pipe_fd
+ pipe_w.close
+ pipe_reader.join(timeout)
+ result << pipe_out
+ end
+ result
ensure
pipe_reader&.kill
pipe_reader&.join(timeout)
@@ -2498,6 +2527,13 @@ def eval_with_jit(
jitlist&.unlink
end
+ def assert_success(out, err, status)
+ message = "exited with status #{status.to_i}"
+ message << "\nstdout:\n```\n#{out}```\n" unless out.empty?
+ message << "\nstderr:\n```\n#{err}```\n" unless err.empty?
+ assert status.success?, message
+ end
+
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
diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb
index 4c31eb3bc136fa..eeda4d94cff1c5 100644
--- a/test/rubygems/test_gem_command_manager.rb
+++ b/test/rubygems/test_gem_command_manager.rb
@@ -72,6 +72,7 @@ def test_find_command_unknown
end
def test_find_command_unknown_suggestions
+ omit "randomly fails on ruby/ruby CI" if ENV["GITHUB_REPOSITORY"] == "ruby/ruby"
e = assert_raise Gem::UnknownCommandError do
@command_manager.find_command "pish"
end
diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs
index 0ef402f073754c..374faa6d972543 100644
--- a/zjit/src/cruby.rs
+++ b/zjit/src/cruby.rs
@@ -294,6 +294,17 @@ pub fn iseq_opcode_at_idx(iseq: IseqPtr, insn_idx: u32) -> u32 {
unsafe { rb_iseq_opcode_at_pc(iseq, pc) as u32 }
}
+/// Return true if the ISEQ always uses a frame with escaped EP.
+pub fn iseq_escapes_ep(iseq: IseqPtr) -> bool {
+ match unsafe { get_iseq_body_type(iseq) } {
+ // The EP of the frame points to TOPLEVEL_BINDING
+ ISEQ_TYPE_MAIN |
+ // eval frames point to the EP of another frame or scope
+ ISEQ_TYPE_EVAL => true,
+ _ => false,
+ }
+}
+
/// Iterate over all existing ISEQs
pub fn for_each_iseq(mut callback: F) {
unsafe extern "C" fn callback_wrapper(iseq: IseqPtr, data: *mut c_void) {
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs
index 98381fa0907e14..af44c86cbe93b3 100644
--- a/zjit/src/hir.rs
+++ b/zjit/src/hir.rs
@@ -3019,6 +3019,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
let payload = get_or_create_iseq_payload(iseq);
let mut profiles = ProfileOracle::new(payload);
let mut fun = Function::new(iseq);
+
// Compute a map of PC->Block by finding jump targets
let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq);
let mut insn_idx_to_block = HashMap::new();
@@ -3029,6 +3030,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
insn_idx_to_block.insert(insn_idx, fun.new_block(insn_idx));
}
+ // Check if the EP is escaped for the ISEQ from the beginning. We give up
+ // optimizing locals in that case because they're shared with other frames.
+ let ep_escaped = iseq_escapes_ep(iseq);
+
// Iteratively fill out basic blocks using a queue
// TODO(max): Basic block arguments at edges
let mut queue = std::collections::VecDeque::new();
@@ -3066,7 +3071,6 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
let mut visited = HashSet::new();
let iseq_size = unsafe { get_iseq_encoded_size(iseq) };
- let iseq_type = unsafe { get_iseq_body_type(iseq) };
while let Some((incoming_state, block, mut insn_idx, mut local_inval)) = queue.pop_front() {
if visited.contains(&block) { continue; }
visited.insert(block);
@@ -3361,8 +3365,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
}
YARVINSN_getlocal_WC_0 => {
let ep_offset = get_arg(pc, 0).as_u32();
- if iseq_type == ISEQ_TYPE_EVAL || has_blockiseq {
- // On eval, the locals are always on the heap, so read the local using EP.
+ if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here
+ // Read the local using EP
let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0 });
state.setlocal(ep_offset, val); // remember the result to spill on side-exits
state.stack_push(val);
@@ -3374,6 +3378,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoEPEscape(iseq), state: exit_id });
local_inval = false;
}
+ // Read the local from FrameState
let val = state.getlocal(ep_offset);
state.stack_push(val);
}
@@ -3381,8 +3386,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
YARVINSN_setlocal_WC_0 => {
let ep_offset = get_arg(pc, 0).as_u32();
let val = state.stack_pop()?;
- if iseq_type == ISEQ_TYPE_EVAL || has_blockiseq {
- // On eval, the locals are always on the heap, so write the local using EP.
+ if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here
+ // Write the local using EP
fun.push_insn(block, Insn::SetLocal { val, ep_offset, level: 0 });
} else if local_inval {
// If there has been any non-leaf call since JIT entry or the last patch point,
@@ -3391,6 +3396,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result {
fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoEPEscape(iseq), state: exit_id });
local_inval = false;
}
+ // Write the local into FrameState
state.setlocal(ep_offset, val);
}
YARVINSN_getlocal_WC_1 => {