Skip to content

Commit

Permalink
YJIT: Generate Block::entry_exit with block entry PC
Browse files Browse the repository at this point in the history
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]: 27fcab9
[2]: 31461c7
  • Loading branch information
XrXr committed Feb 24, 2023
1 parent fa1eb31 commit 132934b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 2 deletions.
41 changes: 41 additions & 0 deletions test/ruby/test_yjit.rb
Expand Up @@ -1155,6 +1155,47 @@ def test(str)
RUBY
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

def code_gc_helpers
Expand Down
4 changes: 2 additions & 2 deletions yjit/src/codegen.rs
Expand Up @@ -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.
block.entry_exit = Some(get_side_exit(jit, ocb, &block_ctx).unwrap_code_ptr());
} else {
let _pc = unsafe { rb_iseq_pc_at_idx(blockid.iseq, blockid.idx) };
block.entry_exit = Some(gen_outlined_exit(jit.pc, &block_ctx, ocb));
let block_entry_pc = unsafe { rb_iseq_pc_at_idx(blockid.iseq, blockid.idx) };
block.entry_exit = Some(gen_outlined_exit(block_entry_pc, &block_ctx, ocb));
}
}

Expand Down

0 comments on commit 132934b

Please sign in to comment.