Skip to content

Commit d7b0979

Browse files
committed
ZJIT: Bail gen_ccall_variadic when c_call and c_return are traced
1 parent 3b88a1d commit d7b0979

6 files changed

Lines changed: 50 additions & 41 deletions

File tree

jit.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,24 @@ rb_jit_vm_unlock(unsigned int *recursive_lock_level, const char *file, int line)
493493
rb_vm_lock_leave(recursive_lock_level, file, line);
494494
}
495495

496+
// Is anyone listening for :c_call and :c_return event currently?
497+
bool
498+
rb_c_method_tracing_currently_enabled(const rb_execution_context_t *ec)
499+
{
500+
rb_event_flag_t tracing_events;
501+
if (rb_multi_ractor_p()) {
502+
tracing_events = ruby_vm_event_enabled_global_flags;
503+
}
504+
else {
505+
// At the time of writing, events are never removed from
506+
// ruby_vm_event_enabled_global_flags so always checking using it would
507+
// mean we don't compile even after tracing is disabled.
508+
tracing_events = rb_ec_ractor_hooks(ec)->events;
509+
}
510+
511+
return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
512+
}
513+
496514
void
497515
rb_iseq_reset_jit_func(const rb_iseq_t *iseq)
498516
{

vm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ jit_compile(rb_execution_context_t *ec)
445445

446446
// At call-threshold, compile the ISEQ with ZJIT.
447447
if (body->jit_entry_calls == rb_zjit_call_threshold) {
448-
rb_zjit_compile_iseq(iseq, false);
448+
rb_zjit_compile_iseq(iseq, ec, false);
449449
}
450450
}
451451
#endif
@@ -505,7 +505,7 @@ jit_compile_exception(rb_execution_context_t *ec)
505505

506506
// At call-threshold, compile the ISEQ with ZJIT.
507507
if (body->jit_exception_calls == rb_zjit_call_threshold) {
508-
rb_zjit_compile_iseq(iseq, true);
508+
rb_zjit_compile_iseq(iseq, ec, true);
509509
}
510510
}
511511
#endif

yjit.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -169,24 +169,6 @@ rb_yjit_exit_locations_dict(VALUE *yjit_raw_samples, int *yjit_line_samples, int
169169
return result;
170170
}
171171

172-
// Is anyone listening for :c_call and :c_return event currently?
173-
bool
174-
rb_c_method_tracing_currently_enabled(const rb_execution_context_t *ec)
175-
{
176-
rb_event_flag_t tracing_events;
177-
if (rb_multi_ractor_p()) {
178-
tracing_events = ruby_vm_event_enabled_global_flags;
179-
}
180-
else {
181-
// At the time of writing, events are never removed from
182-
// ruby_vm_event_enabled_global_flags so always checking using it would
183-
// mean we don't compile even after tracing is disabled.
184-
tracing_events = rb_ec_ractor_hooks(ec)->events;
185-
}
186-
187-
return tracing_events & (RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
188-
}
189-
190172
// The code we generate in gen_send_cfunc() doesn't fire the c_return TracePoint event
191173
// like the interpreter. When tracing for c_return is enabled, we patch the code after
192174
// the C method return to call into this to fire the event.

zjit.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@
3434
void rb_zjit_profile_disable(const rb_iseq_t *iseq);
3535

3636
void
37-
rb_zjit_compile_iseq(const rb_iseq_t *iseq, bool jit_exception)
37+
rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception)
3838
{
3939
RB_VM_LOCKING() {
4040
rb_vm_barrier();
4141

4242
// Compile a block version starting at the current instruction
43-
uint8_t *rb_zjit_iseq_gen_entry_point(const rb_iseq_t *iseq, bool jit_exception); // defined in Rust
44-
uintptr_t code_ptr = (uintptr_t)rb_zjit_iseq_gen_entry_point(iseq, jit_exception);
43+
uint8_t *rb_zjit_iseq_gen_entry_point(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception); // defined in Rust
44+
uintptr_t code_ptr = (uintptr_t)rb_zjit_iseq_gen_entry_point(iseq, ec, jit_exception);
4545

4646
if (jit_exception) {
4747
iseq->body->jit_exception = (rb_jit_func_t)code_ptr;

zjit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
extern bool rb_zjit_enabled_p;
1414
extern uint64_t rb_zjit_call_threshold;
1515
extern uint64_t rb_zjit_profile_threshold;
16-
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, bool jit_exception);
16+
void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception);
1717
void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec);
1818
void rb_zjit_profile_enable(const rb_iseq_t *iseq);
1919
void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop);
@@ -26,7 +26,7 @@ void rb_zjit_before_ractor_spawn(void);
2626
void rb_zjit_tracing_invalidate_all(void);
2727
#else
2828
#define rb_zjit_enabled_p false
29-
static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, bool jit_exception) {}
29+
static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec, bool jit_exception) {}
3030
static inline void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec) {}
3131
static inline void rb_zjit_profile_enable(const rb_iseq_t *iseq) {}
3232
static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {}

zjit/src/codegen.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@ struct JITState {
3838

3939
/// The number of bytes allocated for basic block arguments spilled onto the C stack
4040
c_stack_slots: usize,
41+
42+
/// Execution context
43+
ec: EcPtr,
4144
}
4245

4346
impl JITState {
4447
/// Create a new JITState instance
45-
fn new(iseq: IseqPtr, num_insns: usize, num_blocks: usize, c_stack_slots: usize) -> Self {
48+
fn new(iseq: IseqPtr, num_insns: usize, num_blocks: usize, c_stack_slots: usize, ec: EcPtr) -> Self {
4649
JITState {
4750
iseq,
4851
opnds: vec![None; num_insns],
4952
labels: vec![None; num_blocks],
5053
iseq_calls: Vec::default(),
5154
c_stack_slots,
55+
ec,
5256
}
5357
}
5458

@@ -74,7 +78,7 @@ impl JITState {
7478
/// If jit_exception is true, compile JIT code for handling exceptions.
7579
/// See jit_compile_exception() for details.
7680
#[unsafe(no_mangle)]
77-
pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, jit_exception: bool) -> *const u8 {
81+
pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> *const u8 {
7882
// Do not test the JIT code in HIR tests
7983
if cfg!(test) {
8084
return std::ptr::null();
@@ -84,7 +88,7 @@ pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, jit_exception: boo
8488
// with_vm_lock() does nothing if the program doesn't use Ractors.
8589
with_vm_lock(src_loc!(), || {
8690
let cb = ZJITState::get_code_block();
87-
let mut code_ptr = with_time_stat(compile_time_ns, || gen_iseq_entry_point(cb, iseq, jit_exception));
91+
let mut code_ptr = with_time_stat(compile_time_ns, || gen_iseq_entry_point(cb, iseq, ec, jit_exception));
8892

8993
if let Err(err) = &code_ptr {
9094
// Assert that the ISEQ compiles if RubyVM::ZJIT.assert_compiles is enabled.
@@ -110,7 +114,7 @@ pub extern "C" fn rb_zjit_iseq_gen_entry_point(iseq: IseqPtr, jit_exception: boo
110114
}
111115

112116
/// Compile an entry point for a given ISEQ
113-
fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool) -> Result<CodePtr, CompileError> {
117+
fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, ec: EcPtr, jit_exception: bool) -> Result<CodePtr, CompileError> {
114118
// We don't support exception handlers yet
115119
if jit_exception {
116120
return Err(CompileError::ExceptionHandler);
@@ -122,7 +126,7 @@ fn gen_iseq_entry_point(cb: &mut CodeBlock, iseq: IseqPtr, jit_exception: bool)
122126
})?;
123127

124128
// Compile the High-level IR
125-
let start_ptr = gen_iseq(cb, iseq, Some(&function)).inspect_err(|err| {
129+
let start_ptr = gen_iseq(cb, iseq, ec, Some(&function)).inspect_err(|err| {
126130
debug!("{err:?}: gen_iseq failed: {}", iseq_get_location(iseq, 0));
127131
})?;
128132

@@ -196,7 +200,7 @@ fn gen_entry(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function, function_pt
196200
}
197201

198202
/// Compile an ISEQ into machine code if not compiled yet
199-
fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>) -> Result<CodePtr, CompileError> {
203+
fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr, ec: EcPtr, function: Option<&Function>) -> Result<CodePtr, CompileError> {
200204
// Return an existing pointer if it's already compiled
201205
let payload = get_or_create_iseq_payload(iseq);
202206
match &payload.status {
@@ -206,7 +210,7 @@ fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>) -> R
206210
}
207211

208212
// Compile the ISEQ
209-
let code_ptr = gen_iseq_body(cb, iseq, function, payload);
213+
let code_ptr = gen_iseq_body(cb, iseq, ec, function, payload);
210214
match &code_ptr {
211215
Ok(start_ptr) => {
212216
payload.status = IseqStatus::Compiled(*start_ptr);
@@ -221,15 +225,15 @@ fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>) -> R
221225
}
222226

223227
/// Compile an ISEQ into machine code
224-
fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>, payload: &mut IseqPayload) -> Result<CodePtr, CompileError> {
228+
fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, ec: EcPtr, function: Option<&Function>, payload: &mut IseqPayload) -> Result<CodePtr, CompileError> {
225229
// Convert ISEQ into optimized High-level IR if not given
226230
let function = match function {
227231
Some(function) => function,
228232
None => &compile_iseq(iseq)?,
229233
};
230234

231235
// Compile the High-level IR
232-
let (start_ptr, gc_offsets, iseq_calls) = gen_function(cb, iseq, function)?;
236+
let (start_ptr, gc_offsets, iseq_calls) = gen_function(cb, iseq, ec, function)?;
233237

234238
// Stub callee ISEQs for JIT-to-JIT calls
235239
for iseq_call in iseq_calls.iter() {
@@ -243,9 +247,9 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>,
243247
}
244248

245249
/// Compile a function
246-
fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Result<(CodePtr, Vec<CodePtr>, Vec<IseqCallRef>), CompileError> {
250+
fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, ec: EcPtr, function: &Function) -> Result<(CodePtr, Vec<CodePtr>, Vec<IseqCallRef>), CompileError> {
247251
let c_stack_slots = max_num_params(function).saturating_sub(ALLOC_REGS.len());
248-
let mut jit = JITState::new(iseq, function.num_insns(), function.num_blocks(), c_stack_slots);
252+
let mut jit = JITState::new(iseq, function.num_insns(), function.num_blocks(), c_stack_slots, ec);
249253
let mut asm = Assembler::new();
250254

251255
// Compile each basic block
@@ -342,6 +346,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
342346
asm_comment!(asm, "Insn: {insn_id} {insn}");
343347
}
344348

349+
unsafe extern "C" {
350+
fn rb_c_method_tracing_currently_enabled(ec: EcPtr) -> bool;
351+
}
352+
345353
let out_opnd = match insn {
346354
Insn::Const { val: Const::Value(val) } => gen_const(*val),
347355
Insn::Const { .. } => panic!("Unexpected Const in gen_insn: {insn}"),
@@ -397,6 +405,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
397405
&Insn::GuardBlockParamProxy { level, state } => no_output!(gen_guard_block_param_proxy(jit, asm, level, &function.frame_state(state))),
398406
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
399407
Insn::CCall { cfun, args, name: _, return_type: _, elidable: _ } => gen_ccall(asm, *cfun, opnds!(args)),
408+
Insn::CCallVariadic { state, .. } if unsafe { rb_c_method_tracing_currently_enabled(jit.ec) } => return Err(*state),
400409
Insn::CCallVariadic { cfun, recv, args, name: _, cme, state } => {
401410
gen_ccall_variadic(jit, asm, *cfun, opnd!(recv), opnds!(args), *cme, &function.frame_state(*state))
402411
}
@@ -1816,7 +1825,7 @@ c_callable! {
18161825
/// This function is expected to be called repeatedly when ZJIT fails to compile the stub.
18171826
/// We should be able to compile most (if not all) function stubs by side-exiting at unsupported
18181827
/// instructions, so this should be used primarily for cb.has_dropped_bytes() situations.
1819-
fn function_stub_hit(iseq_call_ptr: *const c_void, cfp: CfpPtr, sp: *mut VALUE) -> *const u8 {
1828+
fn function_stub_hit(iseq_call_ptr: *const c_void, cfp: CfpPtr, sp: *mut VALUE, ec: EcPtr) -> *const u8 {
18201829
with_vm_lock(src_loc!(), || {
18211830
// gen_push_frame() doesn't set PC, so we need to set them before exit.
18221831
// function_stub_hit_body() may allocate and call gc_validate_pc(), so we always set PC.
@@ -1864,7 +1873,7 @@ c_callable! {
18641873
}
18651874

18661875
// Otherwise, attempt to compile the ISEQ. We have to mark_all_executable() beyond this point.
1867-
let code_ptr = with_time_stat(compile_time_ns, || function_stub_hit_body(cb, &iseq_call));
1876+
let code_ptr = with_time_stat(compile_time_ns, || function_stub_hit_body(cb, &iseq_call, ec));
18681877
let code_ptr = code_ptr.unwrap_or_else(|compile_error| {
18691878
prepare_for_exit(iseq, cfp, sp, &compile_error);
18701879
ZJITState::get_exit_trampoline_with_counter()
@@ -1876,9 +1885,9 @@ c_callable! {
18761885
}
18771886

18781887
/// Compile an ISEQ for a function stub
1879-
fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>) -> Result<CodePtr, CompileError> {
1888+
fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc<RefCell<IseqCall>>, ec: EcPtr) -> Result<CodePtr, CompileError> {
18801889
// Compile the stubbed ISEQ
1881-
let code_ptr = gen_iseq(cb, iseq_call.borrow().iseq, None).inspect_err(|err| {
1890+
let code_ptr = gen_iseq(cb, iseq_call.borrow().iseq, ec, None).inspect_err(|err| {
18821891
debug!("{err:?}: gen_iseq failed: {}", iseq_get_location(iseq_call.borrow().iseq, 0));
18831892
})?;
18841893

@@ -1924,7 +1933,7 @@ pub fn gen_function_stub_hit_trampoline(cb: &mut CodeBlock) -> Result<CodePtr, C
19241933
const { assert!(ALLOC_REGS.len() % 2 == 0, "x86_64 would need to push one more if we push an odd number of regs"); }
19251934

19261935
// Compile the stubbed ISEQ
1927-
let jump_addr = asm_ccall!(asm, function_stub_hit, SCRATCH_OPND, CFP, SP);
1936+
let jump_addr = asm_ccall!(asm, function_stub_hit, SCRATCH_OPND, CFP, SP, EC);
19281937
asm.mov(SCRATCH_OPND, jump_addr);
19291938

19301939
asm_comment!(asm, "restore argument registers");

0 commit comments

Comments
 (0)