diff --git a/NEWS.md b/NEWS.md index a040aebeae..85f49d4846 100644 --- a/NEWS.md +++ b/NEWS.md @@ -176,6 +176,9 @@ Excluding feature bug fixes. * The JIT compiler no longer skips compilation of methods longer than 1000 instructions. +* `--jit-verbose` and `--jit-warning` output "JIT cancel" when JIT-ed + code is disabled because TracePoint or GC.compact is used. + * `RubyVM::MJIT` is renamed to `RubyVM::JIT`. [[Feature #17490]] ## Static analysis diff --git a/mjit.c b/mjit.c index 5ca4d6849e..57f28476ca 100644 --- a/mjit.c +++ b/mjit.c @@ -83,6 +83,16 @@ mjit_gc_exit_hook(void) CRITICAL_SECTION_FINISH(4, "mjit_gc_exit_hook"); } +// Prohibit calling JIT-ed code and let existing JIT-ed frames exit before the next insn. +void +mjit_cancel_all(const char *reason) +{ + mjit_call_p = false; + if (mjit_opts.warnings || mjit_opts.verbose) { + fprintf(stderr, "JIT cancel: Disabled JIT-ed code because %s\n", reason); + } +} + // Deal with ISeq movement from compactor void mjit_update_references(const rb_iseq_t *iseq) @@ -96,7 +106,7 @@ mjit_update_references(const rb_iseq_t *iseq) // We need to invalidate JIT-ed code for the ISeq because it embeds pointer addresses. // To efficiently do that, we use the same thing as TracePoint and thus everything is cancelled for now. // See mjit.h and tool/ruby_vm/views/_mjit_compile_insn.erb for how `mjit_call_p` is used. - mjit_call_p = false; // TODO: instead of cancelling all, invalidate only this one and recompile it with some threshold. + mjit_cancel_all("GC.compact is used"); // TODO: instead of cancelling all, invalidate only this one and recompile it with some threshold. } // Units in stale_units (list of over-speculated and invalidated code) are not referenced from diff --git a/mjit.h b/mjit.h index 4d27e39e3c..813ac0cf21 100644 --- a/mjit.h +++ b/mjit.h @@ -90,6 +90,7 @@ extern void rb_mjit_recompile_inlining(const rb_iseq_t *iseq); extern void rb_mjit_recompile_const(const rb_iseq_t *iseq); RUBY_SYMBOL_EXPORT_END +extern void mjit_cancel_all(const char *reason); extern bool mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname, int id); extern void mjit_init(const struct mjit_options *opts); extern void mjit_gc_start_hook(void); @@ -181,6 +182,7 @@ void mjit_finish(bool close_handle_p); # else // USE_MJIT +static inline void mjit_cancel_all(const char *reason){} static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;} static inline void mjit_cont_free(struct mjit_cont *cont){} static inline void mjit_gc_start_hook(void){} diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb index 5b79f8c0ea..d6306dcfc9 100644 --- a/test/ruby/test_jit.rb +++ b/test/ruby/test_jit.rb @@ -10,6 +10,7 @@ class TestJIT < Test::Unit::TestCase IGNORABLE_PATTERNS = [ /\AJIT recompile: .+\n\z/, /\AJIT inline: .+\n\z/, + /\AJIT cancel: .+\n\z/, /\ASuccessful MJIT finish\n\z/, ] MAX_CACHE_PATTERNS = [ @@ -1100,6 +1101,14 @@ class TestJIT < Test::Unit::TestCase end; end + def test_cancel_by_tracepoint + assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 0, min_calls: 2) + begin; + TracePoint.new(:line) {}.enable + 2.times {} + end; + end + def test_caller_locations_without_catch_table out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1) begin; diff --git a/vm_trace.c b/vm_trace.c index 398ca97de3..b603293d34 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -81,10 +81,8 @@ update_global_event_hook(rb_event_flag_t vm_events) rb_event_flag_t enabled_iseq_events = ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS; if (new_iseq_events & ~enabled_iseq_events) { - /* Stop calling all JIT-ed code. Compiling trace insns is not supported for now. */ -#if USE_MJIT - mjit_call_p = FALSE; -#endif + // Stop calling all JIT-ed code. We can't rewrite existing JIT-ed code to trace_ insns for now. + mjit_cancel_all("TracePoint is enabled"); /* write all ISeqs if and only if new events are added */ rb_iseq_trace_set_all(new_iseq_events | enabled_iseq_events);