ZJIT: prepare options whenever the zjit feature is enabled

--enable=zjit sets the zjit feature bit without parsing a --zjit option,
so OPTIONS is never initialized and rb_zjit_option_enable() returns
false.

ZJIT then stays disabled at boot. The same happens with --enable-zjit
and RUBYOPT=--enable=zjit, and with --jit, --enable=jit and --enable=all
on ZJIT-only builds.

In this commit we call rb_zjit_prepare_options() whenever the zjit
feature is set, regardless of where the bit came from. It's a no-op
when the --zjit option parser has already initialized OPTIONS, so
--zjit-disable and the other ZJIT options still apply.

[Bug #22427]
This commit is contained in:
Matt Valentine-House authored and eightbitraptor committed 2026-10-14 10:25:31 +01:00
1 parent 44f7c6b00d
commit d71692d49f
2 files changed
+26 -6
Notes: git 2026-10-14 09:26:02 +00:00

No files matched your search

+3 -6
View File
@@ -2425,12 +2425,6 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#if USE_ZJIT
if (!FEATURE_USED_P(opt->features, zjit) && env_var_truthy("RUBY_ZJIT_ENABLE")) {
FEATURE_SET(opt->features, FEATURE_BIT(zjit));
// When the --zjit flag is specified, we would have call setup_zjit_options(""),
// which would have called rb_zjit_prepare_options() internally. This ensures we
// go through the same set up but with less overhead than setup_zjit_options("").
extern void rb_zjit_prepare_options();
rb_zjit_prepare_options();
}
#endif
}
@@ -2447,6 +2441,9 @@ process_options(int argc, char **argv, ruby_cmdline_options_t *opt)
#endif
#if USE_ZJIT
if (FEATURE_SET_P(opt->features, zjit)) {
// --enable=zjit, RUBY_ZJIT_ENABLE and --jit in ZJIT-only builds set the bit without parsing a --zjit* option.
extern void rb_zjit_prepare_options(void);
rb_zjit_prepare_options();
bool rb_zjit_option_enable(void);
opt->zjit = rb_zjit_option_enable(); // set opt->zjit for Init_ruby_description() and calling rb_zjit_init()
}
+23
View File
@@ -110,6 +110,29 @@ class TestZJITCLI < Test::Unit::TestCase
end
end
def test_enable_through_feature_flag
child_env = {'RUBY_YJIT_ENABLE' => nil, 'RUBY_ZJIT_ENABLE' => nil, 'RUBYOPT' => nil}
args_list = [
%w[--enable=zjit],
%w[--enable-zjit],
%w[--disable=zjit --enable=zjit],
%w[--zjit --zjit-disable --enable=zjit],
]
args_list += [%w[--jit], %w[--enable=jit]] unless JITSupport.yjit_supported?
args_list.each do |args|
assert_in_out_err([child_env, '--disable-all', *args, '-e', 'p RubyVM::ZJIT.enabled?']) do |stdout, stderr|
expected = args.include?('--zjit-disable') ? 'false' : 'true'
assert_equal([expected], stdout, args.inspect)
assert_equal([], stderr, args.inspect)
end
end
assert_in_out_err([child_env.merge('RUBYOPT' => '--enable=zjit'), '--disable=gems,yjit', '-v']) do |stdout, stderr|
assert_include(stdout.first, '+ZJIT')
assert_equal([], stderr)
end
end
def test_zjit_enable
# --disable-all is important in case the build/environment has YJIT enabled by
# default through e.g. -DYJIT_FORCE_ENABLE. Can't enable ZJIT when YJIT is on.