YJIT: Generate Block::entry_exit with block entry PC
Previously, when Block::entry_exit is requested from any instruction that is not the first one in the block, we generated the exit with an incorrect PC. We should always be using the PC for the entry of the block for Block::entry_exit. It was a simple typo. The bug was [introduced][1] while we were refactoring to use the current backend. Later, we had a chance to spot this issue while [preparing][2] to enable unused variable warnings, but didn't spot the issue. Fixes [Bug #19463] [1]: 27fcab995e6dde19deb91dc6e291bdb72100af68 [2]: 31461c7e0eab4963ccc8649ea8ebf27979132c0c
This commit is contained in:
parent
fa1eb31fca
commit
132934b82b
Notes:
git
2023-02-24 21:19:13 +00:00
Merged: https://github.com/ruby/ruby/pull/7374 Merged-By: XrXr
@ -1155,6 +1155,47 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_return_to_invalidated_block
|
||||||
|
# [Bug #19463]
|
||||||
|
assert_compiles(<<~RUBY, result: [1, 1, :ugokanai])
|
||||||
|
klass = Class.new do
|
||||||
|
def self.lookup(hash, key) = hash[key]
|
||||||
|
|
||||||
|
def self.foo(a, b) = []
|
||||||
|
|
||||||
|
def self.test(hash, key)
|
||||||
|
[lookup(hash, key), key, "".freeze]
|
||||||
|
# 05 opt_send_without_block :lookup
|
||||||
|
# 07 getlocal_WC_0 :hash
|
||||||
|
# 09 opt_str_freeze ""
|
||||||
|
# 12 newarray 3
|
||||||
|
# 14 leave
|
||||||
|
#
|
||||||
|
# YJIT will put instructions (07..14) into a block.
|
||||||
|
# When String#freeze is redefined from within lookup(),
|
||||||
|
# the return address to the block is still on-stack. We rely
|
||||||
|
# on invalidation patching the code at the return address
|
||||||
|
# to service this situation correctly.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# get YJIT to compile test()
|
||||||
|
hash = { 1 => [] }
|
||||||
|
31.times { klass.test(hash, 1) }
|
||||||
|
|
||||||
|
# inject invalidation into lookup()
|
||||||
|
evil_hash = Hash.new do |_, key|
|
||||||
|
class String
|
||||||
|
undef :freeze
|
||||||
|
def freeze = :ugokanai
|
||||||
|
end
|
||||||
|
|
||||||
|
key
|
||||||
|
end
|
||||||
|
klass.test(evil_hash, 1)
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def code_gc_helpers
|
def code_gc_helpers
|
||||||
|
@ -489,8 +489,8 @@ pub fn jit_ensure_block_entry_exit(jit: &mut JITState, ocb: &mut OutlinedCb) {
|
|||||||
// Generate the exit with the cache in jitstate.
|
// Generate the exit with the cache in jitstate.
|
||||||
block.entry_exit = Some(get_side_exit(jit, ocb, &block_ctx).unwrap_code_ptr());
|
block.entry_exit = Some(get_side_exit(jit, ocb, &block_ctx).unwrap_code_ptr());
|
||||||
} else {
|
} else {
|
||||||
let _pc = unsafe { rb_iseq_pc_at_idx(blockid.iseq, blockid.idx) };
|
let block_entry_pc = unsafe { rb_iseq_pc_at_idx(blockid.iseq, blockid.idx) };
|
||||||
block.entry_exit = Some(gen_outlined_exit(jit.pc, &block_ctx, ocb));
|
block.entry_exit = Some(gen_outlined_exit(block_entry_pc, &block_ctx, ocb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user