YJIT: Cancel on-stack jit_return on invalidation (#9086)
* YJIT: Cancel on-stack jit_return on invalidation Co-authored-by: Alan Wu <alansi.xingwu@shopify.com> * Use RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P --------- Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
This commit is contained in:
parent
5888a16a12
commit
ba1cdadfc8
35
cont.c
35
cont.c
@ -1290,19 +1290,38 @@ rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data)
|
|||||||
if (cont->ec->vm_stack == NULL)
|
if (cont->ec->vm_stack == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const rb_control_frame_t *cfp;
|
const rb_control_frame_t *cfp = cont->ec->cfp;
|
||||||
for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
|
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
|
||||||
const rb_iseq_t *iseq;
|
if (cfp->pc && cfp->iseq && imemo_type((VALUE)cfp->iseq) == imemo_iseq) {
|
||||||
if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((VALUE)iseq) == imemo_iseq) {
|
callback(cfp->iseq, data);
|
||||||
callback(iseq, data);
|
|
||||||
}
|
}
|
||||||
|
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
||||||
if (cfp == cont->ec->cfp)
|
|
||||||
break; // reached the most recent cfp
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_YJIT
|
||||||
|
// Update the jit_return of all CFPs to leave_exit unless it's leave_exception or not set.
|
||||||
|
// This prevents jit_exec_exception from jumping to the caller after invalidation.
|
||||||
|
void
|
||||||
|
rb_yjit_cancel_jit_return(void *leave_exit, void *leave_exception)
|
||||||
|
{
|
||||||
|
struct rb_jit_cont *cont;
|
||||||
|
for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
|
||||||
|
if (cont->ec->vm_stack == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const rb_control_frame_t *cfp = cont->ec->cfp;
|
||||||
|
while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
|
||||||
|
if (cfp->jit_return && cfp->jit_return != leave_exception) {
|
||||||
|
((rb_control_frame_t *)cfp)->jit_return = leave_exit;
|
||||||
|
}
|
||||||
|
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Finish working with jit_cont.
|
// Finish working with jit_cont.
|
||||||
void
|
void
|
||||||
rb_jit_cont_finish(void)
|
rb_jit_cont_finish(void)
|
||||||
|
@ -1324,6 +1324,36 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_return_to_invalidated_frame
|
||||||
|
assert_compiles(code_gc_helpers + <<~RUBY, exits: :any, result: :ok)
|
||||||
|
def jump
|
||||||
|
[] # something not inlined
|
||||||
|
end
|
||||||
|
|
||||||
|
def entry(code_gc)
|
||||||
|
jit_exception(code_gc)
|
||||||
|
jump # faulty jump after code GC. #jit_exception should not come back.
|
||||||
|
end
|
||||||
|
|
||||||
|
def jit_exception(code_gc)
|
||||||
|
if code_gc
|
||||||
|
tap do
|
||||||
|
RubyVM::YJIT.code_gc
|
||||||
|
break # jit_exec_exception catches TAG_BREAK and re-enters JIT code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_pages(100)
|
||||||
|
jump # Compile #jump in a non-first page
|
||||||
|
add_pages(100)
|
||||||
|
entry(false) # Compile #entry and its call to #jump in another page
|
||||||
|
entry(true) # Free #jump but not #entry
|
||||||
|
|
||||||
|
:ok
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
def test_setivar_on_class
|
def test_setivar_on_class
|
||||||
# Bug in https://github.com/ruby/ruby/pull/8152
|
# Bug in https://github.com/ruby/ruby/pull/8152
|
||||||
assert_compiles(<<~RUBY, result: :ok)
|
assert_compiles(<<~RUBY, result: :ok)
|
||||||
|
@ -11,6 +11,7 @@ use crate::utils::IntoUsize;
|
|||||||
use crate::yjit::yjit_enabled_p;
|
use crate::yjit::yjit_enabled_p;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::os::raw::c_void;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
// Invariants to track:
|
// Invariants to track:
|
||||||
@ -517,6 +518,17 @@ pub extern "C" fn rb_yjit_tracing_invalidate_all() {
|
|||||||
|
|
||||||
let cb = CodegenGlobals::get_inline_cb();
|
let cb = CodegenGlobals::get_inline_cb();
|
||||||
|
|
||||||
|
// Prevent on-stack frames from jumping to the caller on jit_exec_exception
|
||||||
|
extern "C" {
|
||||||
|
fn rb_yjit_cancel_jit_return(leave_exit: *mut c_void, leave_exception: *mut c_void) -> VALUE;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
rb_yjit_cancel_jit_return(
|
||||||
|
CodegenGlobals::get_leave_exit_code().raw_ptr(cb) as _,
|
||||||
|
CodegenGlobals::get_leave_exception_code().raw_ptr(cb) as _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Apply patches
|
// Apply patches
|
||||||
let old_pos = cb.get_write_pos();
|
let old_pos = cb.get_write_pos();
|
||||||
let old_dropped_bytes = cb.has_dropped_bytes();
|
let old_dropped_bytes = cb.has_dropped_bytes();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user