8000 YJIT: Add RubyVM::YJIT.enable (#8705) · Shopify/ruby@3342181 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3342181

Browse files
committed
YJIT: Add RubyVM::YJIT.enable (ruby#8705)
1 parent 7745ad0 commit 3342181

File tree

11 files changed

+111
-103
lines changed

11 files changed

+111
-103
lines changed

cont.c

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ static VALUE rb_cFiberPool;
7171
#define FIBER_POOL_ALLOCATION_FREE
7272
#endif
7373

74-
#define jit_cont_enabled (rb_rjit_enabled || rb_yjit_enabled_p())
75-
7674
enum context_type {
7775
CONTINUATION_CONTEXT = 0,
7876
FIBER_CONTEXT = 1
@@ -1062,10 +1060,8 @@ cont_free(void *ptr)
10621060

10631061
RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
10641062

1065-
if (jit_cont_enabled) {
1066-
VM_ASSERT(cont->jit_cont != NULL);
1067-
jit_cont_free(cont->jit_cont);
1068-
}
1063+
VM_ASSERT(cont->jit_cont != NULL);
1064+
jit_cont_free(cont->jit_cont);
10691065
/* free rb_cont_t or rb_fiber_t */
10701066
ruby_xfree(ptr);
10711067
RUBY_FREE_LEAVE("cont");
@@ -1311,9 +1307,6 @@ rb_jit_cont_each_iseq(rb_iseq_callback callback, void *data)
13111307
void
13121308
rb_jit_cont_finish(void)
13131309
{
1314-
if (!jit_cont_enabled)
1315-
return;
1316-
13171310
struct rb_jit_cont *cont, *next;
13181311
for (cont = first_jit_cont; cont != NULL; cont = next) {
13191312
next = cont->next;
@@ -1326,9 +1319,8 @@ static void
13261319
cont_init_jit_cont(rb_context_t *cont)
13271320
{
13281321
VM_ASSERT(cont->jit_cont == NULL);
1329-
if (jit_cont_enabled) {
1330-
cont->jit_cont = jit_cont_new(&(cont->saved_ec));
1331-
}
1322+
// We always allocate this since YJIT may be enabled later
1323+
cont->jit_cont = jit_cont_new(&(cont->saved_ec));
13321324
}
13331325

13341326
struct rb_execution_context_struct *
@@ -1375,15 +1367,11 @@ rb_fiberptr_blocking(struct rb_fiber_struct *fiber)
13751367
return fiber->blocking;
13761368
}
13771369

1378-
// Start working with jit_cont.
1370+
// Initialize the jit_cont_lock
13791371
void
13801372
rb_jit_cont_init(void)
13811373
{
1382-
if (!jit_cont_enabled)
1383-
return;
1384-
13851374
rb_native_mutex_initialize(&jit_cont_lock);
1386-
cont_init_jit_cont(&GET_EC()->fiber_ptr->cont);
13871375
}
13881376

13891377
#if 0
@@ -2564,10 +2552,9 @@ rb_threadptr_root_fiber_setup(rb_thread_t *th)
25642552
fiber->killed = 0;
25652553
fiber_status_set(fiber, FIBER_RESUMED); /* skip CREATED */
25662554
th->ec = &fiber->cont.saved_ec;
2567-
// When rb_threadptr_root_fiber_setup is called for the first time, rb_rjit_enabled and
2568-
// rb_yjit_enabled_p() are still false. So this does nothing and rb_jit_cont_init() that is
2569-
// called later will take care of it. However, you still have to call cont_init_jit_cont()
2570-
// here for other Ractors, which are not initialized by rb_jit_cont_init().
2555+
// This is the first fiber. Hence it's the first jit_cont_new() as well.
2556+
// Initialize the mutex for jit_cont_new() in cont_init_jit_cont().
2557+
rb_jit_cont_init();
25712558
cont_init_jit_cont(&fiber->cont);
25722559
}
25732560

ruby.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,10 +1778,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
17781778
if (opt->yjit)
17791779
rb_yjit_init();
17801780
#endif
1781-
// rb_threadptr_root_fiber_setup for the initial thread is called before rb_yjit_enabled_p()
1782-
// or rjit_enabled becomes true, meaning jit_cont_new is skipped for the initial root fiber.
1783-
// Therefore we need to call this again here to set the initial root fiber's jit_cont.
1784-
rb_jit_cont_init(); // must be after rjit_enabled = true and rb_yjit_init()
17851781

17861782
ruby_set_script_name(opt->script_name);
17871783
require_libraries(&opt->req_list);

test/ruby/test_yjit.rb

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,27 +51,36 @@ def test_command_line_switches
5151
#assert_in_out_err('--yjit-call-threshold=', '', [], /--yjit-call-threshold needs an argument/)
5252
end
5353

54-
def test_starting_paused
55-
program = <<~RUBY
54+
def test_yjit_enable
55+
args = []
56+
args << "--disable=yjit" if RubyVM::YJIT.enabled?
57+
assert_separately(args, <<~RUBY)
58+
assert_false RubyVM::YJIT.enabled?
59+
assert_false RUBY_DESCRIPTION.include?("+YJIT")
60+
61+
RubyVM::YJIT.enable
62+
63+
assert_true RubyVM::YJIT.enabled?
64+
assert_true RUBY_DESCRIPTION.include?("+YJIT")
65+
RUBY
66+
end
67+
68+
def test_yjit_enable_with_call_threshold
69+
assert_separately(%w[--yjit-disable --yjit-call-threshold=1], <<~RUBY)
5670
def not_compiled = nil
5771
def will_compile = nil
58-
def compiled_counts = RubyVM::YJIT.runtime_stats[:compiled_iseq_count]
59-
counts = []
72+
def compiled_counts = RubyVM::YJIT.runtime_stats&.dig(:compiled_iseq_count)
73+
6074
not_compiled
61-
counts << compiled_counts
75+
assert_nil compiled_counts
76+
assert_false RubyVM::YJIT.enabled?
6277
63-
RubyVM::YJIT.resume
78+
RubyVM::YJIT.enable
6479
6580
will_compile
66-
counts << compiled_counts
67-
68-
if counts[0] == 0 && counts[1] > 0
69-
p :ok
70-
end
81+
assert compiled_counts > 0
82+
assert_true RubyVM::YJIT.enabled?
7183
RUBY
72-
assert_in_out_err(%w[--yjit-pause --yjit-stats --yjit-call-threshold=1], program, success: true) do |stdout, stderr|
73-
assert_equal([":ok"], stdout)
74-
end
7584
end
7685

7786
def test_yjit_stats_and_v_no_error

version.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,16 @@ Init_version(void)
140140
#define YJIT_OPTS_ON 0
141141
#endif
142142

143-
void
144-
Init_ruby_description(ruby_cmdline_options_t *opt)
143+
static void
144+
define_ruby_description(bool rjit_enabled, bool yjit_enabled)
145145
{
146146
VALUE description;
147147

148-
if (RJIT_OPTS_ON) {
148+
if (rjit_enabled) {
149149
rb_dynamic_description = ruby_description_with_rjit;
150150
description = MKSTR(description_with_rjit);
151151
}
152-
else if (YJIT_OPTS_ON) {
152+
else if (yjit_enabled) {
153153
rb_dynamic_description = ruby_description_with_yjit;
154154
description = MKSTR(description_with_yjit);
155155
}
@@ -163,6 +163,19 @@ Init_ruby_description(ruby_cmdline_options_t *opt)
163163
rb_define_global_const("RUBY_DESCRIPTION", /* MKSTR(description) */ description);
164164
}
165165

166+
void
167+
Init_ruby_description(ruby_cmdline_options_t *opt)
168+
{
169+
define_ruby_description(RJIT_OPTS_ON, YJIT_OPTS_ON);
170+
}
171+
172+
void
173+
ruby_ F42D set_yjit_description(void)
174+
{
175+
rb_const_remove(rb_cObject, rb_intern("RUBY_DESCRIPTION"));
176+
define_ruby_description(false, true);
177+
}
178+
166179
void
167180
ruby_show_version(void)
168181
{

vm.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,14 @@ jit_compile(rb_execution_context_t *ec)
383383
{
384384
const rb_iseq_t *iseq = ec->cfp->iseq;
385385
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
386-
bool yjit_enabled = rb_yjit_compile_new_iseqs();
387-
if (!(yjit_enabled || rb_rjit_call_p)) {
386+
if (!(rb_yjit_enabled_p || rb_rjit_call_p)) {
388387
return NULL;
389388
}
390389

391390
// Increment the ISEQ's call counter and trigger JIT compilation if not compiled
392391
if (body->jit_entry == NULL) {
393392
body->jit_entry_calls++;
394-
if (yjit_enabled) {
393+
if (rb_yjit_enabled_p) {
395394
if (rb_yjit_threshold_hit(iseq, body->jit_entry_calls)) {
396395
rb_yjit_compile_iseq(iseq, ec, false);
397396
}
@@ -435,7 +434,7 @@ jit_compile_exception(rb_execution_context_t *ec)
435434
{
436435
const rb_iseq_t *iseq = ec->cfp->iseq;
437436
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
438-
if (!rb_yjit_compile_new_iseqs()) {
437+
if (!rb_yjit_enabled_p) {
439438
return NULL;
440439
}
441440

vm_method.c

Expand all lines: vm_method.c
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
201201
struct rb_id_table *cm_tbl;
202202
if ((cm_tbl = RCLASS_CALLABLE_M_TBL(klass)) != NULL) {
203203
VALUE cme;
204-
if (rb_yjit_enabled_p() && rb_id_table_lookup(cm_tbl, mid, &cme)) {
204+
if (rb_yjit_enabled_p && rb_id_table_lookup(cm_tbl, mid, &cme)) {
205205
rb_yjit_cme_invalidate((rb_callable_method_entry_t *)cme);
206206
}
207207
if (rb_rjit_enabled && rb_id_table_lookup(cm_tbl, mid, &cme)) {

yjit.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,20 +1178,15 @@ VALUE rb_yjit_insns_compiled(rb_execution_context_t *ec, VALUE self, VALUE iseq)
11781178
VALUE rb_yjit_code_gc(rb_execution_context_t *ec, VALUE self);
11791179
VALUE rb_yjit_simulate_oom_bang(rb_execution_context_t *ec, VALUE self);
11801180
VALUE rb_yjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);
1181-
VALUE rb_yjit_resume(rb_execution_context_t *ec, VALUE self);
1181+
VALUE rb_yjit_enable(rb_execution_context_t *ec, VALUE self);
11821182

11831183
// Preprocessed yjit.rb generated during build
11841184
#include "yjit.rbinc"
11851185

1186-
// Can raise RuntimeError
1186+
// Initialize the GC hooks
11871187
void
1188-
rb_yjit_init(void)
1188+
rb_yjit_init_gc_hooks(void)
11891189
{
1190-
// Call the Rust initialization code
1191-
void rb_yjit_init_rust(void);
1192-
rb_yjit_init_rust();
1193-
1194-
// Initialize the GC hooks. Do this second as some code depend on Rust initialization.
11951190
struct yjit_root_struct *root;
11961191
VALUE yjit_root = TypedData_Make_Struct(0, struct yjit_root_struct, &yjit_root_type, root);
11971192
rb_gc_register_mark_object(yjit_root);

yjit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
#endif
2626

2727
// Expose these as declarations since we are building YJIT.
28-
bool rb_yjit_enabled_p(void);
28+
extern bool rb_yjit_enabled_p;
2929
bool rb_yjit_compile_new_iseqs(void);
3030
bool rb_yjit_threshold_hit(const rb_iseq_t *const iseq, unsigned long total_calls);
3131
void rb_yjit_invalidate_all_method_lookup_assumptions(void);
@@ -47,7 +47,7 @@ void rb_yjit_tracing_invalidate_all(void);
4747
// !USE_YJIT
4848
// In these builds, YJIT could never be turned on. Provide dummy implementations.
4949

50-
static inline bool rb_yjit_enabled_p(void) { return false; }
50+
#define rb_yjit_enabled_p false
5151
static inline bool rb_yjit_compile_new_iseqs(void) { return false; }
5252
static inline bool rb_yjit_threshold_hit(const rb_iseq_t *const iseq, unsigned long total_calls) { return false; }
5353
static inline void rb_yjit_invalidate_all_method_lookup_assumptions(void) {}

yjit.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
module RubyVM::YJIT
1212
# Check if YJIT is enabled
1313
def self.enabled?
14-
Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p())'
14+
Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p)'
1515
end
1616

1717
# Check if --yjit-stats is used.
@@ -29,9 +29,9 @@ def self.reset_stats!
2929
Primitive.rb_yjit_reset_stats_bang
3030
end
3131

32-
# Resume YJIT compilation after paused on startup with --yjit-pause
33-
def self.resume
34-
Primitive.rb_yjit_resume
32+
# Enable YJIT compilation.
33+
def self.enable
34+
Primitive.rb_yjit_enable
3535
end
3636

3737
# If --yjit-trace-exits is enabled parse the hashes from
@@ -234,6 +234,8 @@ def self.simulate_oom! # :nodoc:
234234
end
235235

236236
class << self
237+
alias :resume :enable # for migration
238+
237239
private
238240

239241
def _dump_locations # :nodoc:

yjit/src/options.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ pub struct Options {
3838
// how often to sample exit trace data
3939
pub trace_exits_sample_rate: usize,
4040

41-
// Whether to start YJIT in paused state (initialize YJIT but don't
42-
// compile anything)
43-
pub pause: bool,
41+
// Whether to enable YJIT at boot. This option prevents other
42+
// YJIT tuning options from enabling YJIT at boot.
43+
pub disable: bool,
4444

4545
/// Dump compiled and executed instructions for debugging
4646
pub dump_insns: bool,
@@ -67,7 +67,7 @@ pub static mut OPTIONS: Options = Options {
6767
gen_trace_exits: false,
6868
print_stats: true,
6969
trace_exits_sample_rate: 0,
70-
pause: false,
70+
disable: false,
7171
dump_insns: false,
7272
dump_disasm: None,
7373
verify_ctx: false,
@@ -150,8 +150,9 @@ pub fn parse_option(str_ptr: *const std::os::raw::c_char) -> Option<()> {
150150
}
151151
},
152152

153-
("pause", "") => unsafe {
154-
OPTIONS.pause = true;
153+
("pause", "") | // for migration
154+
("disable", "") => unsafe {
155+
OPTIONS.disable = true;
155156
},
156157

157158
("temp-regs", _) => match opt_val.parse() {

0 commit comments

Comments
 (0)
0