Project

General

Profile

Actions

Bug #22427

open

`--enable=zjit` does not enable ZJIT

Bug #22427: `--enable=zjit` does not enable ZJIT

Added by znz (Kazuhiro NISHIYAMA) about 4 hours ago.

Status:
Open
Assignee:
Target version:
-
ruby -v:
ruby 4.1.0dev (2026-10-11T13:56:36Z v4.1.0-preview1 2d08b5f42f) +PRISM [x86_64-linux]
[ruby-core:127073]

Description

ruby --help lists zjit under "Features" (zjit Method-based JIT compiler (default: disabled).), but --enable=zjit alone does not enable ZJIT, while --enable=yjit does enable YJIT.

$ ruby -v
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [x86_64-linux]

$ ruby --enable=yjit -v
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +YJIT +PRISM [x86_64-linux]
$ ruby --enable=yjit -e 'p RubyVM::YJIT.enabled?'
true

$ ruby --enable=zjit -v
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [x86_64-linux]
$ ruby --enable=zjit -e 'p RubyVM::ZJIT.enabled?'
false

$ ruby --zjit -v
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +ZJIT +PRISM [x86_64-linux]
$ ruby --zjit -e 'p RubyVM::ZJIT.enabled?'
true

The same happens with --enable-zjit and with RUBYOPT=--enable=zjit. ZJIT is enabled as soon as any --zjit-* option is added (for example --enable=zjit --zjit-call-threshold=30), and also with RUBY_ZJIT_ENABLE=1.

The cause is in ruby.c. --enable=zjit only sets the feature bit. Later, opt->zjit is computed as

#if USE_ZJIT
    if (FEATURE_SET_P(opt->features, zjit)) {
        bool rb_zjit_option_enable(void);
        opt->zjit = rb_zjit_option_enable(); // set opt->zjit for Init_ruby_description() and calling rb_zjit_init()
    }
#endif

and rb_zjit_option_enable() (zjit/src/options.rs) returns true only when the Rust-side OPTIONS has been initialized (OPTIONS.as_ref().is_some_and(|opts| !opts.disable)). OPTIONS is initialized by rb_zjit_prepare_options(), which is called for --zjit* (through setup_zjit_options()) and for RUBY_ZJIT_ENABLE, but not for --enable=zjit. So opt->zjit stays false and rb_zjit_init(false) is called. The YJIT counterpart, rb_yjit_option_disable(), has no such dependency, which is why --enable=yjit works.

Judging from the diff, this was introduced by f84bbb4238 ("ZJIT: add support for lazy RubyVM::ZJIT.enable", 2025-11-17, included in 4.0.0). Before it, the code was

    if (FEATURE_SET_P(opt->features, zjit) && !opt->zjit) {
        extern void rb_zjit_prepare_options(void);
        rb_zjit_prepare_options();
        opt->zjit = true;
    }

A possible fix is to prepare the options when the feature bit is set, before asking whether ZJIT should be enabled:

#if USE_ZJIT
    if (FEATURE_SET_P(opt->features, zjit)) {
        // --enable=zjit sets the feature bit without going through setup_zjit_options()
        extern void rb_zjit_prepare_options(void);
        rb_zjit_prepare_options();
        bool rb_zjit_option_enable(void);
        opt->zjit = rb_zjit_option_enable();
    }
#endif

rb_zjit_prepare_options() already handles the already-initialized case, so --zjit-* options and --zjit-disable keep working. test/ruby/test_rubyoptions.rb does not exercise --enable=yjit or --enable=zjit, so a test for the --enable= form would also help.

Reproduced in the ghcr.io/ruby/ruby:4.0 (4.0.7) and ghcr.io/ruby/ruby:master (4.1.0dev 2d08b5f42f) Docker images (x86_64-linux).

No data to display

Actions

Also available in: PDF Atom